In OpenGL, one often writes code like this:
glPushMatrix(); // modify the current matrix and use it glPopMatrix();
Essentially, the state is changed, then some actions are performed that use the new state, and finally the state is restored.
Now there are two problems here:
In true object-based programming style, I have written some utility classes to overcome these problems, like so:
struct WithPushedMatrix { WithPushedMatrix() { glPushMatrix(); } ~WithPushedMatrix() { glPopMatrix(); } };
Now I can simply write my previous example like this:
WithPushedMatrix p; // modify the current matrix and use it
The exact moment of restoring is determined by the lifetime of p
. If an exception is thrown, p
's destructor gets called, the state is restored, and life is good.
Still, I'm not entirely happy. Especially if the constructor takes some arguments (e.g. flags for glEnable
), it's easy to forget to assign the object to a variable:
WithEnabledFlags(GL_BLEND); // whoops!
The temporary gets destroyed immediately, and the state change is reversed prematurely.
Another issue is that anyone else reading my code might get confused: "Why is there a variable declared here that is never used? Let's get rid of it!"
So, my questions: Is this a good pattern? Does it maybe even have a name? Are there any problems with this approach that I'm overlooking? Last but not least: are there any good alternatives?
Update: Yes, I guess it's a form of RAII. But not in the way RAII is normally used, because it involves a seemingly useless variable; the "resource" in question is never accessed explicitly. I just didn't realize that this particular usage was so common.
Constructors and destructors are special member functions of classes that are used to construct and destroy class objects. Construction may involve memory allocation and initialization for objects. Destruction may involve cleanup and deallocation of memory for objects.
The following restrictions apply to constructors and destructors: Constructors and destructors do not have return types nor can they return values. References and pointers cannot be used on constructors and destructors because their addresses cannot be taken. Constructors cannot be declared with the keyword virtual .
It is a good practice to make the base class's destructor as virtual as this ensures that the object of the derived class is destroyed properly. Whenever a virtual class is used, a virtual destructor should be added immediately to prevent any future unexpected results.
Constructor helps to initialize the object of a class. Whereas destructor is used to destroy the instances.
I like the idea of using RAII to control OpenGL state, but I'd actually take it one step further: have your WithFoo
class constructor take a function pointer as a parameter, which contains the code you want to execute in that context. Then don't create named variables, and just work with temporaries, passing in the action you want to execute in that context as a lambda. (needs C++0x, of course - can work with regular function pointers too but it's not nearly as pretty.)
Something like this: (edited to restore exception-safety)
class WithPushedMatrix { public: WithPushedMatrix() { glPushMatrix(); } ~WithPushedMatrix() { glPopMatrix(); } template <typename Func> void Execute(Func action) { action(); } };
And use it like so:
WithPushedMatrix().Execute([] { glBegin(GL_LINES); //etc. etc. });
The temporary object will set up your state, execute the action and then tear it down automatically; you don't have "loose" state variables floating around, and the actions executing under the context become strongly associated with it. You can even nest multiple contextual actions without worrying about destructor order.
You can even take this further and make a generic WithContext
class that takes additional setup and teardown function parameters.
edit: Had to move the action()
call into a separate Execute
function to restore exception-safety - if it's called in the constructor and throws, the destructor won't get called.
So I fiddled around with this idea some more, and came up with something better:
I'll define a With
class, that creates the context variable and stuffs it into a std::auto_ptr
in it's initializer, then calls the action
:
template <typename T> class With { public: template <typename Func> With(Func action) : context(new T()) { action(); } template <typename Func, typename Arg> With(Arg arg, Func action) : context(new T(arg)) { action(); } private: const std::auto_ptr<T> context; };
Now you can combine it with context type that you defined originally:
struct PushedMatrix { PushedMatrix() { glPushMatrix(); } ~PushedMatrix() { glPopMatrix(); } };
And use it like this:
With<PushedMatrix>([] { glBegin(GL_LINES); //etc. etc. });
or
With<EnabledFlag>(GL_BLEND, [] { //... });
Benefits:
auto_ptr
now, so if action
throws, the context will still get destroyed properly.Execute
method, so it looks clean again! :)With
class so you just need to define a simple ctor/dtor for each new type of context.One niggle: As I've written it above, you need to declare manual overloads for the ctor for as many parameters as you need; although even just one should cover most OpenGL use cases, this isn't really nice. This should be neatly fixed with variadic templates - just replace typename Arg
in the ctor with typename ...Args
- but it'll depend on compiler support for that (MSVC2010 doesn't have them yet).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With