Jump to content

  • Log In with Google      Sign In   
  • Create Account

We're offering banner ads on our site from just $5!

1. Details HERE. 2. GDNet+ Subscriptions HERE. 3. Ad upload HERE.


Don't forget to read Tuesday's email newsletter for your chance to win a free copy of Construct 2!


Critique my approach to writing RAII wrappers


Old topic!
Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.

  • You cannot reply to this topic
19 replies to this topic

#1 kloffy   Members   -  Reputation: 929

Like
0Likes
Like

Posted 04 September 2014 - 04:21 AM

Recently, I have played around with wrapping up a couple of handy C libraries in order to have a nicer more modern C++11 programming interface. I am aware that there can be associated problems, however I still find RAII wrappers handy in many cases.

So, the straight forward approach would be something like this (untested):
class Texture
{
public:
	Texture():
		_id{0}
	{
		glGenTextures(1, &_id);
		
		if (!*this)
		{
			throw std::runtime_error("Failed to acquire texture.");
		}
	}
	
	~Texture()
	{
		if (*this)
		{
			glDeleteTextures(1, &_id);
		}
	}
	
	explicit operator bool() const
	{
		return _id != 0;
	}
	
	
	// Non-Copyable
	Texture(Texture const& other) = delete;
	Texture& operator=(Texture const& other) = delete;
	
	// Moveable
	Texture(Texture&& other):
		_id{0}
	{
		swap(*this, other);
	}
	Texture& operator=(Texture&& other)
	{
		swap(*this, other);
		return *this;
	}
	
	// Wrapper Methods
	//void bind();
	//void image(...);
	//voud subImage(...);
	
private:
	GLuint _id;
	
	friend void swap(Texture& lhs, Texture& rhs)
	{
		using std::swap;
		swap(lhs._id, rhs._id);
	}
};

Initially this works fine, but it tries to do things at the same time: provide wrapper methods and manage lifetime. This becomes a problem when you receive a handle from the C api, whose lifetime is already managed in some other way. In that case, you cannot use the wrapper methods. For example, this isn't possible:
Texture getBoundTexture()
{
	GLuint boundTexture = 0;
	glGetIntegerv(GL_TEXTURE_BINDING_2D, (GLint*) &boundTexture);
	return {boundTexture};
}

Even if there was a matching constructor, this code would be buggy since now there would be two RAII objects managing one lifetime. When one of them is destroyed, the resource gets freed and the second destructor results in double deletion. Really, it should be possible to distinguish between objects that manage lifetimes and those that don't. Well, you might say there already is such a thing: smart pointers! I could create my Texture object on the heap and use smart pointers to manage its lifetime. However, there is really no need for creating objects on the heap, if instead we generalize smart pointers. In fact, this has been proposed. However, it is not part of the standard library yet and - what I think is even worse - it's not easily possible to associate wrapped methods with the handle.

So, instead I have come up with the following approach (which I am sure has its own problems, that's why I am looking for feedback):

First, lifetime management is encapsulated in a separate class (similar to the proposed unique_resource):
template<typename T>
class Unique
{
public:
	template<typename... Args>
	Unique(Args&&... args):
		_value{}
	{
		T::New(_value, std::forward<Args>(args)...);
		
		if (!_value)
		{
			throw std::runtime_error("Failed to acquire resource.");
		}
	}
	
	~Unique()
	{
		if (_value)
		{
			T::Delete(_value);
		}
	}
	
	Unique(Unique const& other) = delete;
	Unique& operator=(Unique const& other) = delete;
	
	Unique(Unique&& other):
		_value{}
	{
		swap(*this, other);
	}
	Unique& operator=(Unique&& other)
	{
		swap(*this, other);
		return *this;
	}
	
	T& 			operator*() 		{ return _value; }
	T const& 	operator*() const 	{ return _value; }
	
	T* 			operator->() 		{ return &_value; }
	T const* 	operator->() const 	{ return &_value; }
	
private:
	T _value;
	
	friend void swap(Unique& lhs, Unique& rhs)
	{
		using std::swap;
		swap(lhs._value, rhs._value);
	}
};

And the wrappers look like this:
class Texture
{
public:
	typedef GLuint Handle;
	
	static void New(Texture& object)
	{
		glGenTextures(1, &object._handle);
	}
	
	static void Delete(Texture& object)
	{
		glDeleteTextures(1, &object._handle);
	}
	
	Texture(): _handle{0} {}
	Texture(Handle const& handle): _handle{handle} {}
	
	Handle const& get() const { return _handle; }
	
	explicit operator bool() const
	{
		return _handle != 0;
	}
	
	// Wrapper Methods
	//void bind();
	//void image(...);
	//voud subImage(...);
	
private:
	Handle _handle;
	
	friend void swap(Texture& lhs, Texture& rhs)
	{
		using std::swap;
		swap(lhs._handle, rhs._handle);
	}
};

The usage could be as follows (artificial example):
{
	Texture bound = getBoundTexture(); // Imlementation as before, now works.

	Unique<Texture> managed;
	
	//Setup texture etc.
	//managed->image(...);
	
	managed->bind();
	
	// Draw Something

	bound.bind();
}

So, the wrappers are like a plain pointer, whereas if you want their lifetime managed, you should use Unique<Texture>. At the end of the block, the manage texture is destroyed, whereas the plain one is untouched. Of course, it would also be possible to implement a Shared<Texture> in the future.

Anyway, I am curious to hear your thoughts. Please critique away...

Edited by kloffy, 08 September 2014 - 10:36 PM.


Sponsor:

#2 frob   Moderators   -  Reputation: 22242

Like
5Likes
Like

Posted 04 September 2014 - 01:40 PM

RAII does not mean "allocate resources".

 

RAII means "initialized".

 

It looks like you are starting to understand that in your original vs updated post, but don't make the final leap.

 

 

 

RAII typically means doing the minimum possible work to get the object in a known state. When working in C, RAII means calling bzero() on new structures. In C++ that means making sure all the member variables that matter are initialized to zero, null, or some other known state during construction, or to intentionally leave them in an indeterminate state because their value has no meaning at that point.

 

If you have a bunch of member variables and never initialize them to a value you do not follow RAII.  When someone creates an object the variables will point to uninitialized data. The values will simply be whatever happened to be in the memory spot before it. If the memory spot already happened to contain "32", it will have the value "32". If the memory region happened to already have a value for a password, the new object value will be that password.

 

In your case NOT following RAII would mean leaving _id as an uninitialized value in the first example. In the second case _handle is an object so the language will automatically call the constructor behind your back even if you don't invoke it during an initializer list; hopefully it also initializes all values to a known state. Better to use an initializer list or use the new syntax for default values.

 

 

 

You can provide alternate constructors that connect to resources or do other work, but those should not be your default constructors.

 

When in doubt, look at what the Standard Library does. When you create a vector with a default constructor it is empty. When you create a string with a default constructor it is empty. When you default-construct an fstream it is not connected to a file. There are alternate constructors to fill a vector, point to an existing string, and spend time to fire up the disk, search the file system, and connect to a file, but these are not the default constructors.

 

 

 

In your original version and your modified version you are requiring resources beyond those needed for RAII. 

 

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. 

 

Even if I'm not going to actually render the textures GenTextures() is run. 

 

Even if I'm pre-allocating an array during game startup before OpenGL is even configured, GenTextures() is run.

 

 

 

In your updated version you still are equating RAII with resource allocation.  

 

Initializtion does not mean resource allocation.  Initialization means a minimal empty valid state.


Check out my book, Game Development with Unity, aimed at beginners who want to build fun games fast.

Also check out my personal website at bryanwagstaff.com, where I write about assorted stuff.


#3 Bregma   Crossbones+   -  Reputation: 5248

Like
5Likes
Like

Posted 04 September 2014 - 02:26 PM

RAII does not mean "allocate resources".

Whaaaat?
 
"Resource allocation is instantiation" does not mean resource allocation is instantiation?  What kind of wacky newspeak are we dealing with here?
 
Perhaps the simple answer is that if you can not control the lifetime of a resource, you can not use RAII to wrap it.  It's just not the appropriate tool for the job.


Stephen M. Webb
Professional Free Software Developer

#4 SmkViper   Members   -  Reputation: 1019

Like
4Likes
Like

Posted 04 September 2014 - 03:05 PM

RAII does not mean "allocate resources".

Whaaaat?
 
"Resource allocation is instantiation" does not mean resource allocation is instantiation?  What kind of wacky newspeak are we dealing with here?
 
Perhaps the simple answer is that if you can not control the lifetime of a resource, you can not use RAII to wrap it.  It's just not the appropriate tool for the job.


RAII stands for Resource Acquisition Is Initialization. Allocation doesn't come into the picture.

As an example of something that follows the RAII pattern but doesn't allocate, I offer the simple lock_guard class:
 
void MyFunc()
{
  std::lock_guard<std::mutex> myLock(MyMutex); // lock_guard locks the mutex in the constructor
  // Do stuff
  // lock_guard calls unlock in destructor when the scope is exited - either normally or via exception
}
Ref-counted smart pointers are another example of something that doesn't allocate or deallocate. They add a ref to an object in the constructor, and then decrement the ref count in the destructor. Granted, de-allocation can happen if the ref count goes to 0, but the important part of RAII is to make sure that things that do cleanup get a chance to do cleanup, no matter how scope is exited.

Heck, I've written tiny structs with function calls in constructors and the corresponding cleanup calls in the destructors just so that I don't have to remember to put the cleanup code every place the function can exit (an impossible task with exceptions).

Edited by SmkViper, 04 September 2014 - 03:08 PM.


#5 kloffy   Members   -  Reputation: 929

Like
0Likes
Like

Posted 04 September 2014 - 07:09 PM

Initializtion does not mean resource allocation.  Initialization means a minimal empty valid state.


First, I wanted to highlight that I intentionally did not implement RAII in the Texture class. It is merely wraps a plain handle and adds convenience wrappers. I might as well have called it TextureHandle or something like that. The RAII comes into play when you use this class in conjunction with Unique, which puts the object into a valid initialised state on construction. To sum it up: int* is not RAII, but std::unique_ptr<int> is RAII. Similarly, Texture is not RAII, but Unique<Texture> is RAII.

Edit: Upon re-reading your post, I may have misinterpreted it the first time. Are you suggesting that it should be possible to create a Unique<Texture> without creating an OpenGL texture object? I must say I tend to agree. I suppose creating it in an empty state and having a makeUnique method which calls Texture::New similar to those in the standard library would be preferable.

Edited by kloffy, 04 September 2014 - 07:19 PM.


#6 ChaosEngine   Crossbones+   -  Reputation: 2449

Like
1Likes
Like

Posted 04 September 2014 - 09:52 PM

 

RAII does not mean "allocate resources".

Whaaaat?
 
"Resource allocation is instantiation" does not mean resource allocation is instantiation?  What kind of wacky newspeak are we dealing with here?
 
Perhaps the simple answer is that if you can not control the lifetime of a resource, you can not use RAII to wrap it.  It's just not the appropriate tool for the job.

 

RAII is actually a total misnomer. The reason it's used has almost nothing to do with resource allocation (or indeed initialisation or instantiation) and everything to do with resource cleanup.

 

I remember reading in the C++ FAQ that a more accurate acronym would be DIRR (Destruction Is Resource Reclamation), but we're kinda stuck with RAII now.


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

#7 Ryan_001   Prime Members   -  Reputation: 1435

Like
0Likes
Like

Posted 06 September 2014 - 12:55 AM

 

 

RAII does not mean "allocate resources".

Whaaaat?
 
"Resource allocation is instantiation" does not mean resource allocation is instantiation?  What kind of wacky newspeak are we dealing with here?
 
Perhaps the simple answer is that if you can not control the lifetime of a resource, you can not use RAII to wrap it.  It's just not the appropriate tool for the job.

 

RAII is actually a total misnomer. The reason it's used has almost nothing to do with resource allocation (or indeed initialisation or instantiation) and everything to do with resource cleanup.

 

I remember reading in the C++ FAQ that a more accurate acronym would be DIRR (Destruction Is Resource Reclamation), but we're kinda stuck with RAII now.

 

 

I vote this up 10 more times if I could.  RAII is about cleanup far more than it is about initialization.



#8 Bregma   Crossbones+   -  Reputation: 5248

Like
1Likes
Like

Posted 06 September 2014 - 07:13 AM


AII is actually a total misnomer. The reason it's used has almost nothing to do with resource allocation (or indeed initialisation or instantiation) and everything to do with resource cleanup.

That's not correct either, although you're right that it's a bit of a misnomer, and resource cleanup is the mandatory sequelae to resource acquisition in RAII.

 

RAII means the object invariant is ownership of a resource.  Once the object has been constructed, you have the resource, and once the object has been destroyed you no longer have the resource.  If at any point you do not have the resource and you have the object, the invariant has been contravened and you do not have RAII.  Similarly, if you have the resource but not the object, it's not RAII.

 

The point of the invariant is that it's a guarantee that the object == the resource.  Both ways. Always and forever.

 

If you are using deferred construction (as frob was discussing), you are not using RAII because it breaks the object invariant.

 

If you have singular objects or nullable objects (eg. smart pointers) you are not implementing RAII (although you can use std::unique_ptr to implement RAII if you remove the move operations).  It breaks the invariant.

 

Things that do the above are valid and useful, but they're not implementing RAII.  You're not using RAII just because you clean things up in your destructor: that's just basic object oriented programming.

 

So yes, RAII is very very much about resource acquisition on instantiation, and also about resource release on destruction.  Like NVI, the tag for the idea is not a complete description of what it means but if you're not acquiring the resource on object instantiation, you're not implementing "Resource Acquisition Is [Object] Instantiation".


Edited by Bregma, 06 September 2014 - 07:20 AM.

Stephen M. Webb
Professional Free Software Developer

#9 frob   Moderators   -  Reputation: 22242

Like
1Likes
Like

Posted 06 September 2014 - 03:50 PM

Bregma: While true for some things, it is not universal.

There are many resources -- containers being the most obvious -- where "initialized" correctly means no allocations.

If you look back before RAII was a term that people thought about, the pattern was this:

// Acquisition. Gain a resource but the initial contents are whatever happened to previously be in memory
myObject = malloc(sizeof(sometype));
// Initialization. Put the resource in a known state
bzero(myObject, sizeof(sometype));

or this

// Acquisition. Gain a chunk of memory with unknown contents
myObject = malloc(sizeof(sometype));
// Initialization. Put the resource in a known state
memcpy(myObject, templateObject, sizeof(sometype), 1);

Object oriented languages improve on this with constructors that perform initialization for you. But even they are not perfect as some types do not initialize their values.

class Foo {
int x;
int y;
public:
Foo() { } // Both x and y are acquired but not intialized. Their contents could be anything.
...
}

RAII can be followed by initializing the object during construction:

Foo() : x(0), y(0) {} // RAII is followed, the object is initialized to a known state.


Yes, there are some classes where initialization absolutely requires additional allocations. But those situations in practice are extremely rare and are usually easily avoidable.

If a default constructor requires trips out for heap allocations you are probably doing something wrong. If a default constructor requires trips out to disk or across a network, you are absolutely doing something wrong. You can provide parameters for non-default constructors, but a default constructor should be instant and not consume additional resources beyond those to initialize the object to a known state.

As an example of a horrible case of default constructors doing this, I have seen beginner code where a default constructor would look in a known resource to look up the next item to load (in this case, an already open file). After reading the next line (potential round trip to disk could be quite long) it would open a file (which means the OS looks up a file name on potentially one or two disk reads, followed by another disk read to get the file). Then it would parse the file for names of sub-models and sub-textures, which in turn spawned off another series of reads and loads. All total using this default constructor required something on the order of 150 disk reads and 500 memory allocations. They were confused why allocating an array of these objects was slow.

Default constructors are used frequently in the language, and many default-constructed objects are allocated but never used. Requiring the default constructor to perform more work than simple initialization to a known state is often a serious implementation flaw.

Check out my book, Game Development with Unity, aimed at beginners who want to build fun games fast.

Also check out my personal website at bryanwagstaff.com, where I write about assorted stuff.


#10 Bregma   Crossbones+   -  Reputation: 5248

Like
0Likes
Like

Posted 06 September 2014 - 08:20 PM

If you're not acquiring a resource in your constructor, you're not implementing RAII.  You're not (necessarily) writing bad code, you're just not implementing RAII.

 

If you have instantiated an object that does not acquire a resource, it is not implementing "resource acquisition is instantiation".  It might be wonderful, perfect code that does exactly what you want in the best way possible.  It's just not RAII.  You might initialize an object to a known state, and that's grand, but if the purpose of the object is to acquire a resource, and the object can exist without acquiring that resource, it's not implementing RAII.  Simply initializing an object to a known state and freeing any resources on object destruction isn't RAII, it's just general object-oriented programming with deterministic destruction.

 

If you use RAII, the statement "if the object exists, the resource is allocated" is always true.  Also, the statement "if the resource is allocated, the object exists" is true.  It is a boon to reasoning about program logic.  But the point of RAII is that you never have to reason something like "the object exists, and it may or may not have acquired resource X, so...."

 

Standard containers are not an example of RAII.  The standard smart pointers are not an example of RAII.


Stephen M. Webb
Professional Free Software Developer

#11 ChaosEngine   Crossbones+   -  Reputation: 2449

Like
0Likes
Like

Posted 07 September 2014 - 05:24 PM

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

#12 Krohm   Crossbones+   -  Reputation: 3171

Like
0Likes
Like

Posted 08 September 2014 - 11:03 AM

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?



#13 SmkViper   Members   -  Reputation: 1019

Like
0Likes
Like

Posted 08 September 2014 - 12:18 PM

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!

#14 Promit   Moderators   -  Reputation: 7341

Like
0Likes
Like

Posted 08 September 2014 - 12:48 PM

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.


Edited by Promit, 08 September 2014 - 12:52 PM.


#15 Bregma   Crossbones+   -  Reputation: 5248

Like
0Likes
Like

Posted 08 September 2014 - 12:57 PM


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

#16 SmkViper   Members   -  Reputation: 1019

Like
0Likes
Like

Posted 08 September 2014 - 02:39 PM

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.

#17 jms bc   Members   -  Reputation: 439

Like
0Likes
Like

Posted 08 September 2014 - 08:35 PM

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.


#18 kloffy   Members   -  Reputation: 929

Like
0Likes
Like

Posted 08 September 2014 - 09:14 PM

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.

#19 Hodgman   Moderators   -  Reputation: 31056

Like
4Likes
Like

Posted 08 September 2014 - 09:35 PM

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™ 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™ fails the same tests that you used to say that shared_ptr isn't TrueRAII™... And yes, decrementing a reference counter is releasing a resource.
  • Bjarne also called it "General Resource Management".

Edited by Hodgman, 09 September 2014 - 12:38 AM.


#20 jms bc   Members   -  Reputation: 439

Like
0Likes
Like

Posted 09 September 2014 - 12:25 AM


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.





Old topic!
Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.



PARTNERS