Critique my approach to writing RAII wrappers

Started by
18 comments, last by jms bc 9 years, 7 months ago

if you're not acquiring the resource on object instantiation, you're not implementing "Resource Acquisition Is [Object] Instantiation".


and if RAII was "resource acquisition is instantiation", you'd be 100% correct.

But it's not; it's "resource acquisition is initialization".

if you think programming is like sex, you probably haven't done much of either.-------------- - capn_midnight
Advertisement

Perhaps something is missing in translation but I'm having an hard time understanding how someone could initialize without instancing something first.

Honestly I found RAII, as well as standard OOP-inspired ownership semantics just inconvenient for code mangling hardware resources.

What I do: specify who holds ownership and ensure this.

RAII was just inconvenient in my last project, too many dependancies on the underlying APIs, too much code, enums, special cases... The rule of five? Are you kidding?

Previously "Krohm"

Perhaps something is missing in translation but I'm having an hard time understanding how someone could initialize without instancing something first.

Honestly I found RAII, as well as standard OOP-inspired ownership semantics just inconvenient for code mangling hardware resources.
What I do: specify who holds ownership and ensure this.
RAII was just inconvenient in my last project, too many dependancies on the underlying APIs, too much code, enums, special cases... The rule of five? Are you kidding?


RAII lets you write something once that enforces ownership which then means you don't have to do it again. With RAII you let the computer enforce ownership and, to be honest, the computer is far better at enforcing (clear) rules then people are.

Sounds like you just had a bad experience with a bad API, as "too much code, enums, special cases" are all things that are not a part of RAII - or at least shouldn't be, aside from specifying ownership (who owns what, can ownership be shared, etc).

Also, the "rule of five" really is the Rule of Zero once you've written your RAII wrappers (which you've templated so you don't have to keep rewriting them, right?)

Let the compiler help you. It's what it's there for!

I want to add something here - there's no particularly strong reason to generate or use exceptions in a game engine. That serves no productive purpose. Either assert on stuff that is fatal, or smoothly handle the problem in a way that can be detected and addressed.

On another note, I don't feel that the bool casts are a good idea either. Create a proper IsValid function.


In your case, any instance of a Texture object MUST allocate a graphics resource. If I were to attempt to create an array of texture objects for any purpose whatsoever I am obligated to also allocate OpenGL resources. If i were to, for example, make an array of 50 Texture objects so I could do something interesting, there is absolutely no way I can create these texture objects without also having GenTextures() run.

To be fair, this is not quite how glGenTextures works; it creates a name reservation, which is effectively a variable increment inside the driver. Spooling up a bunch of names and not using them is not an impacting thing to do.

SlimDX | Ventspace Blog | Twitter | Diverse teams make better games. I am currently hiring capable C++ engine developers in Baltimore, MD.


it's "resource acquisition is initialization".

What is the class invariant of an object that can be in a valid state but not hold a resource?

What kind of aid to logic is "may or may not hold a resource"?

What kind of aid to reasoning is "the resource may or may not be held by this object"?

How should I distinguish between this version of RAII and any other object that gets initialized in its constructor and destroyed in its destructor?

How is late resource acquisition (or resource transfer) different from open/close semantics?

Is there another name for the idiom in which the invariant of the object is "owns the resource"?

Stephen M. Webb
Professional Free Software Developer

I think we're getting just a tiny bit too nitpicky on the language (yes, I know, I helped start it tongue.png )

Stroustrup was the one who came up with the name, so I think it's best to let him explain it in his own words.

It's resource management, pure and simple. C++ uses constructors and destructors and the scope mechanism to make sure things that are initialized are cleaned up, and things that aren't initialized aren't (i.e. constructor threw an exception - which is the only way to get an error out of a constructor and maintain class invariants).

If I allocate/open/acquire a resource, I must deallocate/close/release said resource when I'm done with it. By allocating/opening/acquiring said resource in the constructor and saying that object "owns" that resource, then the destructor naturally deallocates/closes/releases that resource in all cases - normal or exceptional.

Does it solve all resource management issues? No. You still need to figure out your ownership. Some objects own uniquely (unique_ptr) some share ownership (shared_ptr). Use the right one for the job. Sometimes closing/releasing a resource can cause an error, in which case you want to have an explicit close/release function on the object because you cannot throw from a destructor. But the destructor is still there as a safety net (and should have a try/catch that eats any exceptions should the release have a chance to throw)

The standard library pointers and containers are RAII because they manage the lifetimes of their contents through new/delete/malloc/free in constructors and destructors. Just because they might be empty doesn't mean they don't implement RAII - just like how a null pointer is still a pointer.

TLDR: I know how to manage the lifetime of one object. But I will screw up the lifetime of ten thousand objects. Let the compiler handle that after I tell it how to manage the lifetime of a single one and my code will be much more stable, bug free, and more secure.

So this thread went from "How is my RAII" to "What the hell is RAII". Interesting comments.

Barney says:

"The name of the game here is to get allocation out of the way so you don't see it"

"the main tool for resource management is constructors and destructors"

A standard container that, say, resizes itself during its lifetime (allocating/deallocating memory outside of constructor/destructor) would therefore not be RAII, right? Or is that being too literal?

To me, if what Bregma says isn't true, then RAII doesn't mean anything new, it is just a reminder to program properly. Sussing out ownership is just sane programming.

A practical application could do wonders for this debate. I approach from the perspective of someone with a resource intensive program -- once I've instantiated the RAII-abiding objects I need without issue, I can count on having the resources ready, ie, the program won't fail on a resource allocation after that point. Could be memory, a serial port, an electron gun, drone...they are all ready to go. My initial interpretation of RAII was along these lines.

The Four Horsemen of Happiness have left.

So this thread went from "How is my RAII" to "What the hell is RAII". Interesting comments.

Yes, I contemplated whether I should step in, but I figured I will just let the discussion go. I don't really see the point of trying to (re-)define what RAII is. As SmkViper pointed out, the man who invented it has written extensively about it. Sure, there are nuances and different interpretations, but what I am interested in is a workable solution for wrapping C libraries like SDL, OpenGL, etc.

You'd be hard pressed to argue in most C++ workplaces that shared pointer isn't a "RAII class".

I can just imagine that workplace conversation when someone name-drops the RAII idiom and some smug bastard tries to get picky...
A: So, this is here just a RAII class, like a shared pointer,
B: Shared pointer isn't RAII! happy.png
A: ... huh.png ... uhhh.... whatever. So anyway, it's like a shared pointer, but...

To me, if what Bregma says isn't true, then RAII doesn't mean anything new

It was new 30 years ago when they were originally writing C++, coming from C programming. The whole point was to avoid the horrible mess that is resource cleanup in plain C.
RAII isn't a new term, so it shouldn't be news to anyone who's written C++ code ever.

don't really see the point of trying to redefine what RAII is

^^That.

it's "resource acquisition is initialization".

  • What is the class invariant of an object that can be in a valid state but not hold a resource?
  • What kind of aid to logic is "may or may not hold a resource"?
  • What kind of aid to reasoning is "the resource may or may not be held by this object"?
  • How should I distinguish between this version of RAII and any other object that gets initialized in its constructor and destroyed in its destructor?
  • How is late resource acquisition (or resource transfer) different from open/close semantics?
  • Is there another name for the idiom in which the invariant of the object is "owns the resource"?

Seriously, or just begging the question? Aren't the answers obvious if you don't start by assuming that your assertion is correct?

  • When the object goes out of scope, if it has a resource, the resource will be released.
  • When you want to use RAII but it's expected that acquisition may fail or be optional.
  • ^
  • Why do you need to?
  • You can't opt out of 'closing' a RAII object; it will always release any resources it has when it goes out of scope. BTW, unique_ptr, which you say is TrueRAII(tm) can act in this same "contravening" way -- you can manually 'close' it / release ownership and retreive the raw pointer. You can also initialize or swap them with a nullptr at any time... so even this class that you say is TrueRAII(tm) fails the same tests that you used to say that shared_ptr isn't TrueRAII(tm)... And yes, decrementing a reference counter is releasing a resource.
  • Bjarne also called it "General Resource Management".


RAII isn't a new term

yeah, I should have said "new to me"...just didn't really pay much attention before C11 and have been confused by the literature...lazy maybe...backing out...

The Four Horsemen of Happiness have left.

This topic is closed to new replies.

Advertisement