RAII: why is it unique to C++?

Many programmers (in particular aspiring game developers), despite endless advice to the contrary, learn C++.  There seems to be a widespread belief that all commercial games are written in C++, along with an idea that it is the ‘only real programming language’ – apparently they missed the earlier, identical memo about C 😉

Now, I am not saying that C++ is a bad language – on the contrary, it is as powerful and flexible as you typically need, and for its intended purpose as a systems language, it does very well. But C++ is a painful language to use, with myriad design flaws, all plastered over by the slow creep of feature bloat. The archaic preproccessor and complicated template mechanism in no way match their counterparts in dynamic languages, and the compilation model is simply heinous.

The language does have a couple of high points – in particular the standard library is excellent, with a wide selection of containers and algorithms, a generally very high implementation quality, and a simple, uniform interface. But to my mind, the most useful (and often undervalued) feature is RAII, “Resource Acquisition Is Initialisation”.

With RAII, we create an object (sometimes known as a ‘handle’) to manage the lifetime of some resource (a file, socket, block of memory, …). The object is designed so that the resource is allocated and/or initialised in the object’s constructor, and destroyed in the object’s destructor.

This means that we no longer have to explicitly manage the lifetime of the resource – just create a handle on the stack, and the resource is automatically created. As soon as the handle falls out of scope, the resource will be destroyed. We can even wrap the handle with a reference-counted smart point (such as boost::smart_ptr), and the resource will now stay alive as long as any client object is using it, and be destroyed as soon as the last client finishes. And the benefits don’t end there either – destructors are called during exception unwinding, which means that our handle will correctly dispose of its resource even in the event of a failure or crash (very important for resources such as global semaphores, which will other wise hang around forever).

So why is this incredibly useful technique not fully present in newer languages, at least a few of which have been billed as ‘C++ killers’? Many of these newer languages offer a limited form of RAII, but at least Java and Objective C lack RAII entirely.

D’s scope keyword, Python’s with statement and C#’s using declaration all provide limited RAII, by allowing resources to have a scoped lifetime, but none of them readily or cleanly support the clever tricks allowed by C++’s combination of smart pointers and RAII, such as returning handles from functions, multiple handles in the same scope, or handles held by multiple clients.

The answer seems to be garbage collection: all of these newer languages are garbage collected, while C++ (at least in its current form) is not. C++ ties RAII to object destruction, which becomes an issue with garbage collection, as most garbage collectors do not guarantee when they will destroy objects, or even if they will destroy objects at all.

But this lack is more than a little annoying to someone who learned to program in C++: take as an example OpenGL vertex buffers or textures. These buffers need to be released as soon as possible (to free up precious video memory), but unfortunately the same vertex buffer may be shared by many copies of a model.

Since we can’t afford to give each model it’s own copy of the buffer, we need to implement reference counting. In C++ this would be as simple as wrapping the buffer object in a smart_ptr, but in any of these languages we have to explicitly implement reference counting in the buffer object, and explicitly call these functions from each client – which isn’t bad for a single type of resource, and a single type of client, but quickly becomes tedious when there are 10, or 20, or even more, and reference counting has to be invoked from 100’s of locations in the code.

Now I finally come to the point behind all this: I have moved away from C++ for the majority of my programming, but I haven’t really come up with a satisfactory solution to this (apart from manual reference counting). So I am fishing for your thoughts and suggestions here – please comment, and let me know how you deal with replacing RAII in modern languages?

Advertisements

32 comments

  1. Hi,
    I read your post, understood your point but to be honest I am fine with C++, garbage collector is just not my thing. However I sometimes use other languages and I think that a good conception (in java programs by example) allows the garbage collector to make its job correctly. As for the example about OpenGL, the garbage collector will never delete a ressource if one of the object is still active, so determining the state of your object during program execution should do the trick no ?

    • I wasn’t knocking C++’s lack of a garbage collector – it fits fine with the philosophy of the language. The issue with garbage collected languages is that the garbage collector performs collection eventually, if at all. If I have a resource that needs to be freed as soon as possible, the garbage collector is useless, as it may never collect the object in question. This lazy collection approach is fine for memory, which is plentiful, and shortages can be detected and used to force a collection cycle, but doesn’t work for any other resource.

  2. In XNA/C#, I ended up implementing a reference counted system that used a trampoline reference/handle class that was IDisposable. Thus, the underlying resource gets reference counted by each of these handles, and the handles are treated as RAII (with a finalizer that asserts that the object has been disposed, to catch leaks).

    However, I think you give C++ templates way too little credit. C++ templates are really powerful; much more powerful than generics in Java or C#. There are things I can do in a few lines of C++ templates that require lots of tedious repetition in C#.

    For example, I can define a “property” that supports both backing store and change notification, and then declare those properties inside my class, and it just works. In C#, there’s no way to overload the operator=() to make that happen, so I have to write one property declaration, and an event, and an OnEvent() trigger function (that checks whether the event is NULL or not), for EACH property I want to support change notification for.

    The main thing really missing from C++, which C# totally aces, is the reflection system. There’s no reason C++ couldn’t have a static reflection system that uses the RTTI typeinfo() (annotate with names, types, offsets and sizes of members for each compound data type, etc). But it doesn’t, and it makes life much harder than it has to be.

    • Garbage collection is a useful tool for managing memory – but it is not a useful tool for managing scarce resources like locks, threads, file handles etc. The problem is that many language that supply garbage collection don’t supply a decent way of managing scarce resources.

      IDisposable/using isn’t a good solution – it’s an error prone hack to compensate for C#’s lack of RAII. We’re back to programmers explicitly freeing resources – exactly the problem garbage collection was invented to solve.

      • “Garbage collection is a useful tool for managing memory” – Unless memory itself is scarce… then it is not so great. A prime example is on mobile devices.

  3. I know that Python __del__ is not *guaranteed* to ever be called, but in practice, isn’t it typically called as soon as the internal Python reference count drops to 0? If so, you get everything you need for free.

    • In practice, yes, 90% of the time __del__ will be called in a reasonably timely fashion. Unfortunately, for that other 10% it won’t be called at all, and for critical resources such as threads, sockets and large GPU buffers, this isn’t sufficient.

  4. You’re missing the most important advantage of RAII: safety.

    The resource is freed when it goes out of scope, always, be it in the face of exceptions or normal execution flow.

    This is what makes C++ programs way safer than those in other programming languages.

    As for GC finalizers, their problem is ordering and cycles.
    You cannot rely and a resource not be have been already released in a finalizer, since order of finalization is not specified.
    Even if it was specified, there would be no solution if there was a cycle.
    This comes to the conclusion GC cannot be reliably used for cycles, even though cycle support is often the advertised killer feature of GC.

    In truth, GC is only good in the case where you can use it as a drop-in replacement of RAII. In which case its advantage are only memory pooling and compaction, the first of which is already heavily done in C++ without GC.

    • Arguing that C++ is somehow ‘safer’ than dynamic languages is a very, very lost cause 😉

      In any case, as I mentioned in the post, all recent languages offer some variety of support for scoped objects. What they don’t allow is the sort of monkey tricks we regularly use in C++ with scoped smart pointers (i.e boost::shared_ptr), which combine RAII with reference counting – and this is what I find lacking.

      And the problem isn’t with memory – garbage collectors work perfectly for that. In fact, benchmarks have shown that languages with GC often perform far better than a typical C++ implementation of the same problem set, largely due to their much better ability to pool objects.

        • A naive solution to a given problem will often be faster in a garbage collected language than the matching naive solution in a low-level language.

          It is true that one can always micro-optimise C++ better than one can (for example) Java. However, few people have the requisite knowledge and skill to do it well, and it isn’t always worth it.

          The big gains performance gains are generally to be had in parallelism (not well supported in most native languages), and memory hierarchy optimisations (many of which a garbage collector helps with).

  5. all i can say is, i feel yer pain. apparently there are some patterns people use in Scala, worth googling up a search on that. but since correct resource handling is so fundamental, i wish it were all more built in to the core language(s). seems to me w/out thinking very hard admittedly that we could be able to have an annotation / empty marker interface to tell the GC system “hey when this object goes out of scope, immediately run the finalizer”.

  6. I agree with the sentiment of this post entirely. I hardly touch C++ anymore, but I still find myself lacking a suitable replacement for RAII.

    My understanding is that Python is guaranteed to call __del__ as soon as the reference count goes to 0. The GC is, to the best of my knowledge, only used in situations where the reference count does not go to 0 (cycles). If you can guarantee that your program never encounters those GC-needing situations, then RAII is fine. In practice, you may be unable to guarantee that. (The weakref module may help.)

    Regardless of my correctness, the stigma against using __del__ in Python is strong, pervasive, and understandable.

  7. Looks like everyone covered Python already. Just wanted to note that AFAIK reference-counting isn’t mandated by the Python language spec; it’s simply what the C implementation does. I haven’t checked, but I imagine Jython just relies on the JVM GC, for example.

    Anyway, as you note, memory “is plentiful”, so naturally, allocating memory is far and away the most common case of allocating “resources”. GC is optimizing for the common case. Having to explicitly close files is not a big deal, especially with syntactic sugar, escape analysis (only ever having one reference to an object is also the common case), etc.

    The real problem with other languages is in the details of implementation. Having a close method for a file that can throw an exception, that the language forces you to handle, where noone can give you a good explanation of how it could happen or what you should do if it ever did… ugh.

    @sirpalee: GC languages may tend to be slow in general, but it’s not because of the GC so much as all the other design decisions that just make sense once you’re committed to using GC (i.e. default reference semantics for objects, which leads to calls being dispatched at runtime throughout… of course, new VMs are getting better at optimizing these kinds of things 🙂 )

    @Raoul: I think what you’re thinking about is escape analysis, and I think Java 7 is supposed to finally do it properly, or something like that. The idea is to have it work automatically: local variables can be statically proven not to outlive their scope if you don’t let anything from outside reference them, so you special-case the GC and finalize them right away. No need for annotations (and indeed, expecting them would be a horrible idea, as it’d make at least as much work for the programmer as there is now, and probably bloat objects and/or .class files due to the extra interface).

  8. Ezbez :
    I agree with the sentiment of this post entirely. I hardly touch C++ anymore, but I still find myself lacking a suitable replacement for RAII.
    My understanding is that Python is guaranteed to call __del__ as soon as the reference count goes to 0. The GC is, to the best of my knowledge, only used in situations where the reference count does not go to 0 (cycles). If you can guarantee that your program never encounters those GC-needing situations, then RAII is fine. In practice, you may be unable to guarantee that. (The weakref module may help.)
    Regardless of my correctness, the stigma against using __del__ in Python is strong, pervasive, and understandable.

    In Python you’d use the with statement for RAII. You never need to worry about __del__.

  9. Looks like I’m more than a bit late to the party replying to this, but just thought I’d mention my own RAII withdrawl. I’ve been working in objective-C lately and find myself missing boost::scoped_lock and boost::shared_ptr greatly. For objective-c, I’ve found one insightful blog post with one way to try to get scoped operations (http://kickingbear.com/blog/archives/13), but I’m finding that it’s still not perfect with the exception handling mechanism in objective-C (setjmp / longjmp). Works sometimes, but not all the time. So…I guess I’m just trying to say I feel your pain. 😛

    With regards to garbage collection, I’ve never understood why languages like Java don’t allow stack allocated (auto) variables like C++. Why are primitives the only “special” objects that can be allocated on the stack? They could be completely ignored by the garbage collector and would still give you the ability to implement scoped semantics. Wouldn’t fully satisfy me for RAII (e.g., scoped_ptr wouldn’t work), but would at least help with things like scoped locking.

  10. I think it’s unique because it’s uniquely useless. C++ has RAII because it doesn’t have any better way to deal with resources. RAII doesn’t work in general for memory allocation, or any other resources, that have to outlive the scope of the function they are in.

    “Smart pointers” may be used to alleviate this, but they are a form of reference counting, which doesn’t work in the general case, because reference counting cannot reclaim cyclical or recursive references. So you’ll need a garbage collector anyway if you don’t want to manage allocation by hand.

    For deterministic finalization, which is a different matter altogether, lambda’s (or blocks) are much more useful and generic. For example, here’s a common Ruby idiom:

    File.open(“foo.txt”, “rt”) do |handle|
    # process file handle here
    end

    What this does is, In essence, the open method of the File class is called with a block (a lambda) that gets the file handle if the file could be opened. After the lambda is called (if possible), File.open ensures the file it opened is also closed again.

    Lambda’s allow “turning the problem inside out”. The function that can fail, and that allocates a volatile resource, is now also responsible itself for handling errors and cleaning up. Which is a big win compared to RAII or raising exceptions.

    • See, I would take almost the opposite stance here. Garbage collection is a very blunt instrument: it assumes that one doesn’t actually care when/if the resource is collected, as it doesn’t guarantee to collect the resource in a timely fashion (or at all), and if one wishes to operate in strict memory constraints, will require a significant amount of processing be expended in making sure all memory is reclaimed quickly.

      For modern Desktop application development this isn’t really much of an issue. Your average laptop has 2-4 GB of memory to burn, plus another 5-10 GB of virtual memory to pick up the slack. But on all these little mobile devices, where both memory and processing power are at a premium, one would like a little more control…

      And lambda/blocks are quite effective for this purpose (although no more so than a scoped_ptr in C++), but they only cover the first case I mentioned, when the resource has a finite lifetime in the program. If you need to pass around and share ownership of this resource, another solution is needed.

      • Dear Tristam, I see, and we probably do have an “opposed stance”. So before I start my reply, I’d like to say it’s all in meant to be in friendly spirit. 🙂

        My intent is essentially to point out that there are good reasons why RAII is only used in C++… Maybe I should have better said that RAII is “uniquely useful” to C++ because I feel that it was invented to deal with the problems that C++ causes itself with it’s exception handling and memory allocation, etc.

        I agree that a garbage collector can be a “blunt” tool, depending on how it’s actually implemented. I admit that a classical “stop-the-world” mark-and-sweep garbage collector may have the problems you mentioned. But with a good incremental and generational garbage collector, those problems will be mostly alleviated.

        Speaking of a small, tight device, it depends on how “small” we’re talking about. If we’re talking about, say, an Android phone, that works fine with Java and the Dalvik VM, which has a great garbage collector. Or look at Lua, which is used in many embedded devices, and in many games with soft-realtime requirements.

        If we’re talking a really tiny device, then yes, you’d like more control, but even more you’d like to use less resources. But C++ RAII is far from being “lightweight”. You’re allocating C++ objects with their data and huge vtable on the stack, which on truly small devices, may be very tiny. For example , on a 32 bits Linux platform, a FILE * pointer is only 4 bytes, where a ofstream is 276 bytes. Good luck with RAII if your stack is only 8k… And don’t get me started on the code that C++ hides in your methods to do exception handling… On such really tiny devices, you’d better use plain C for full control, unit test the heck out of it to ensure all resources are closed properly, and if needed, use Lua for scripting the application.

        And if you need to pass around the resource, with shared ownership RAII isn’t going to help. Contrary to what you think, lambda’s (closures) can also be used to manage shared resources. You wrap all users of the resources in a an individual lambda and call those closures from the function that opens the resource, which then closes it. Though I admit that then you may also need coroutines or threading to schedule access to the resource.

        And sorry to say this, but comparing the usefulness of a lambda/closure to a scoped_ptr reeks of “horseless carriage” thinking to me. 🙂 Closures can be used as an alternative to RAII, but RAII isn’t an alternative to closures. So, closures are the more generic feature, and their inclusion in a programming language is far more warranted. Closures are so useful that they are widely available in many programming languages. Even C now has a closure extension in the clang compiler. And Java and C++ are planning to add closures real soon now. 🙂

        • 1 : C++11 already support closure
          2 : “You’re allocating C++ objects with their data and huge vtable on the stack”
          looks like you don’t know c++ very well, you could design a light weight smart pointer for c++, which don’t need any vtable, the size could be as small as raw pointer, and the construct and destruct speed also as fast as raw pointer, that is the beauty of template, the world which c and those pure oop world could not achieve.

        • “Don’t pay for anything you don’t need” is the philosophy of c++, the codes of c++ never need to use more memory or become slower than c, it all depends on the skills of programmers(if the platform have a decent c++ compiler, many old devices do not have), don’t mix the old idea of c or other programming language which do not have an extremely powerful template like c++.If you don’t know the basic of template, you are impossible to understand how could c++ implement those infrastructures(like some smart pointer, but not all of them) without any runtime penalty(not all of them are, you could select what kind of smart pointer you need).Besides, the recycle problem could be solved by weak_ptr, they are not a big deal in c++11.

  11. Heap allocation is generally faster in gc languages. Deallocation may be slower, but is usually performed when the processor isnt busy.. Therefore the allocation and deallocation profile of objects is different in RAII and gc languages.. I’m not a fan of gc but I think that this is probably the rational behind garbage collection.. I

  12. RAII is unique to C++ (well, not quite, Delphi had it) because C++ provides deterministic destructors. I’ve been pondering the issue of how to provide good RAII destruction semantics for my own language for a while now, but trying to locate destructors via type-class resolution gets us into a really bad place for modularity.

  13. I read all this and maybe I see things though different glass – to me, RAII versus GC is a pointless argument. RAII is more about best-approach in vanilla C++. GC is about best-approach in a managed environment. You can implement GC in C++ if you really want to, but that doesn’t mean it makes sense to.

    Philosphically speaking, I prefer C++ because I have that latitude to use the approach I see fit, whereas I cannot un-GC a GC-oriented environment. Same with determinism. Using C++ I could write a garbage collector and use it in part of a project and use RAII in a different part. Of course this creates workload on me as a programmer to create or procure a GC. A lack of vast libraries is certainly a C++ weakness (though for me C++Builder fixes most of this). C++ has its pros/cons and isn’t necessarily the best solution for a given problem, but IMO it is the best single solution if you broaden scope to a mountain of problems. This is the core reason I choose C++ and thusly by default, RAII.

    I do not consider RAII the result of a “design fault” of C++, I consider it the proper application of C++. I consider GC proper in a managed, VMed environment. It’s like arguing that a car sucks for fuel efficiency and a bike sucks for cargo – separate things with separate philosophies that are intended to be fundamentally different. Yet, they are needlessly compared due to overlap of application. You can’t expect to have your cake and eat it too. RAII/C++ and GC/Java/.NET are both fine for their intended uses. Just use the tool(s) that you feel makes sense.

    • I’m not sure quite who you are replying to, because I definitely do not consider C++’s RAII to be a ‘Design fault’. It might be the one overwhelmingly powerful feature in an otherwise unpleasant language.

      Contrary to your position, I’d argue that RAII and Garbage collection are *not* equal, and that garbage collection suffers dramatically when required to deal with shared, non-memory resources. GC centric languages such as Java and C# provide no way to guarantee that a shared heavy-weight resource (such as a thread, file handle or GPU texture) will be deallocated in a timely fashion.

      It might be argued that the problem is actually one of ownership, and that shared ownership of scarce resources is a non-starter. And I’d agree, for the most part, but if we disallow shared ownership of native resources, why not do the same for memory, and remove the need for a GC altogether?

    • Ja, linear types definitely have their appeal. However, I’ve yet to see a programmer-friendly implementation of linear types (or indeed, one that doesn’t cause my eyeballs to bleed).

      That said, I’m quite enamoured of the approach Mozilla’s Rust is taking (despite the horrific syntax) with unique pointer ownership, ‘borrowed’ reference types, and transparent region management. It’s a simple and effective system to allow the programmer to maintain strict ownership of their data (and hence avoid the need for garbage collection entirely), and the language designers have been pragmatic enough to provide an optional garbage collector for those who need/prefer it. Now if only they can fix the damn syntax…

      • Rust actually has affine types, with destructors to guarantee resource cleanup when they go out of scope. But, you see, I meant actual linear types, where failing to explicitly discharge a resource actually causes a compilation error, not C++/Rust-style stack unwinding. However, linear types do not interact well with any form of exception throwing – not even Rust’s notion of task failure.

        That being said, I do like Rust myself. Or, more accurately, I have a love/hate relationship with it. You mention Rust’s syntax being “horrorifying”. It is certainly ugly, but “horrorifying” is an adjective I would reserve for C++’s syntax. Rust has much more fundamental issues, like the absence of owning closures that can be called more than once, and the lack of support for true data parallelism (e.g., if you want to spawn a child task for every element in an array, without having to remove the element from the array so that you can send it to the child task).

  14. Bah: sorry more folks didn’t have useful suggestions for RAII in other languages. Have you had any more ideas/suggestions in the intervening year(s)? I’m particularly interested in the specific application you describe – namely attempting to manage graphical and other resources in games.

    • Some thoughts, definitely. Mostly: don’t attempt to manage the lifetime of resources explicitly. That’s a system that fits fine with the deterministic disposal provided C++/RAII, but attempting to map that philosophy into a garbage collected language is bound to fail.

      Instead you end up building an immediate-mode interface on top of the underlying API, providing caches for the native resource types, and don’t dispose of them until the caches start to overflow. If you look at, for example, the Android drawing APIs, they work exactly like this. The resources provided up in Java (i.e. Bitmap) are just wrappers for the client-side memory, and the matching GPU object (in this case, textures) are not allocated until you attempt to draw the bitmap (via a Canvas). The GPU object will be unloaded either when the texture cache fills up, or when memory compaction is required, and can be recreated from the client-side object if needed.


Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s