安全设置的一个特别棘手的事情是GLX。问题是必须分配相当多的资源,并在初始化期间的任何时候出现错误时以正确的顺序解除分配。
这是我在C中写的(只有相关部分)。
int gfx_init(struct gfx *g)
{
int vis_attriblist[] = { GLX_RGBA, GLX_DOUBLEBUFFER, None };
XSetWindowAttributes wa;
XVisualInfo *vis_info;
int r = 0;
g->xdpy = XOpenDisplay(NULL);
if (g->xdpy == NULL) {
r = -1;
LOG_ERROR("Could not open X Display");
goto xopendisplay_failed;
}
vis_info = glXChooseVisual(g->xdpy, DefaultScreen(g->xdpy),
vis_attriblist);
if (vis_info == NULL) {
r = -1;
LOG_ERROR("Couldn't get an RGBA, double-buffered visual"
" (GLX available?)\n");
goto glxchoosevisual_failed;
}
g->xcolormap = XCreateColormap(g->xdpy, DefaultRootWindow(g->xdpy),
vis_info->visual, AllocNone);
if (gfx_has_xerror(g) /* Checks if there are errors
by flushing Xlib's protocol buffer
with a custom error handler set.
Not included here */) {
r = -1;
LOG_ERROR("Failed to create colormap");
goto xcreatecolormap_failed;
}
wa.colormap = g->xcolormap;
wa.event_mask = StructureNotifyMask | VisibilityChangeMask;
g->xwindow = XCreateWindow(g->xdpy, DefaultRootWindow(g->xdpy),
0,0,1280,1024, 0, vis_info->depth,
InputOutput, vis_info->visual, CWColormap |
CWEventMask, &wa);
if (gfx_has_xerror(g)) {
r = -1;
LOG_ERROR("Failed to create X11 Window");
goto xcreatewindow_failed;
}
g->glxctx = glXCreateContext(g->xdpy, vis_info, NULL, True);
if (g->glxctx == NULL) {
r = -1;
LOG_ERROR("Failed to create GLX context");
goto glxcreatecontext_failed;
}
if (glXMakeCurrent(g->xdpy, g->xwindow, g->glxctx) == False) {
r = -1;
LOG_ERROR("Failed to make context current");
goto glxmakecurrent_failed;
}
g->xa_wmdeletewindow = XInternAtom(g->xdpy, "WM_DELETE_WINDOW", False);
if (gfx_has_xerror(g)) {
r = -1;
LOG_ERROR("XInternAtom failed");
goto xinternatom_failed;
}
XSetWMProtocols(g->xdpy, g->xwindow, &g->xa_wmdeletewindow, 1);
if (gfx_has_xerror(g)) {
r = -1;
LOG_ERROR("XSetWMProtocols failed");
goto xsetwmprotocols_failed;
}
glClearColor(1,1,1,1);
glColor4f(0,0,0,1);
glEnable(GL_TEXTURE_2D);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
if (glGetError() != GL_NO_ERROR) {
r = -1;
LOG_ERROR("There have been GL errors");
goto gotglerror;
}
XMapWindow(g->xdpy, g->xwindow);
XFlush(g->xdpy);
if (r < 0) {
gotglerror:
xsetwmprotocols_failed:
xinternatom_failed:
glXMakeCurrent(g->xdpy, None, NULL);
glxmakecurrent_failed:
glXDestroyContext(g->xdpy, g->glxctx);
glxcreatecontext_failed:
XDestroyWindow(g->xdpy, g->xwindow);
xcreatewindow_failed:
XFreeColormap(g->xdpy, g->xcolormap);
}
xcreatecolormap_failed:
/* This is a local resource which must be destroyed
in case of success as well */
XFree(vis_info);
if (r < 0) {
glxchoosevisual_failed:
XCloseDisplay(g->xdpy);
}
xopendisplay_failed:
return r;
}
其实我对它很满意。我认为这是很好的C风格。唯一的问题是对于gfx_destroy
函数,gfx_init
的释放部分的代码必须是重复的,但这非常简单。
我想知道的是如何以良好的RAII C ++风格进行初始化。特别是,成员分配之间存在依赖关系,假想的RAII class Gfx
的构造函数应该以正确的顺序初始化,或抛出异常并保证初始构造的部分再次被拆除。
因此,自然进展是为分配的类型编写短包装器。 E.g。
struct MyDisplay {
Display *dpy;
MyDisplay() : dpy(XOpenDisplay(NULL)) { if (!dpy) throw "XOpenDisplay()"; }
~MyDisplay() { XCloseDisplay(dpy); }
};
struct MyXVisualInfo {
XVisualInfo *info;
static int vis_attriblist[] = { GLX_RGBA, GLX_DOUBLEBUFFER, None };
MyXVisualInfo(Display *dpy)
: info(glXChooseVisual(dpy, DefaultScreen(dpy), vis_attriblist) {
if (!info)
throw "glXChooseVisual()";
}
~MyXVisualInfo() {
XFree(info);
}
};
Gfx
类:
class Gfx {
MyDisplay mydpy_;
MyXVisualInfo myinfo_;
/* ... */
public:
Gfx::Gfx() : mydpy_(), myinfo_(mydpy_.dpy) /* , ... */ {}
};
但此时我们遇到了一个问题: (这不是真的,请参阅@MarkB的回答)myinfo_(mydpy.dpy)
实际上是hands an undefined value to the MyXVisualInfo constructor。对于RAII班级成员来说,这是一个showstopper吗?
另外,如果构造函数需要分配临时资源,实际情况就是myinfo_
我不希望存储在类中的情况,我认为没有办法避免进入构造函数体,< em>并使用构造函数体的本地资源从那里分配给成员。这意味着会员将接受额外的建设和解构,这是不可行的,因为涉及副作用。
我唯一能想到的就是使用unique_ptr
:
class Gfx {
unique_ptr<MyDisplay> mydpy_;
unique_ptr<MyColormap> mycolormap_;
/* ... */
public:
Gfx() {
mydpy_.reset(new MyDisplay());
MyXVisualInfo myinfo(dpy);
mycolormap_.reset(new MyXColormap(mydpy_->dpy,
DefaultRootWindow(mydpy_->dpy),
myinfo->info, AllocNone));
}
};
现在这显然已经过度工程化,并且已成为维持生计的重要因素。另外引入不必要的开销的unique_ptr并不好。
是否有一种干净的方法能够以干净的RAII方式完成C版本的工作?
答案 0 :(得分:2)
是什么导致您得出myinfo(mydpy.dpy)
将有未定义行为的结论?您链接的SO问题不是与示例代码中的情景相同。请注意,在您的情况下,您按照您希望它们初始化的顺序列出类定义中的成员,因此没有未定义的行为。
通常,如果您觉得在初始化列表中无法执行某些操作,并且需要使用构造函数体,则可以将代码分解为函数并使用初始化列表中的返回值,但是我我很难理解你在Also, if the constructor needs to allocate temporary resources
之后的意思,所以我不能给你一个比这更好的答案。
答案 1 :(得分:1)
std::shared_ptr
和std::unique_ptr
可与自定义删除器一起使用。例如:
std::shared_ptr<int> mem (static_cast<int*>(malloc(sizeof(int))), free);
或使用lambda:
std::shared_ptr<int> mem (new int(), [](int* foobar) {
std::cout << "I am a deleter" << std::endl;
delete foobar;
});