Question

I recently found about RAII in c++ and most examples of RAII talk about exception safety. How you can always release resources even if an exception were to be thrown.

The question I have is, if RAII is worth it if you do not have exceptions turned on. In our firm we work on embedded projects for arm and exceptions are turned off by default and we don't really see any need for them.

Thanks for all the answers!

Was it helpful?

Solution

RAII with exceptions is basically a requirement.

RAII without exceptions means that you can couple the allocation of resources with the code to dispose the resources.

This lets you have functions with multiple exit points, simplifies the writing of destructors (often destructors in an RAII heavy environment are empty or default), can simplify object assignment and moving (once again, often empty or default with sufficient RAII work).

A classic example for embedded environments would be locking and unlocking some mutex. You want to guarantee that you don't lock a mutex and forget to unlock it. To do this, code discipline means you have to have basically one exit point from your function, and you have to engage in gymnastics sometimes to ensure that this happens.

With RAII, you just create a RAII resource holder that owns the lock. Now you can return whenever you want, and the code to unlock the resource is automatically injected at the return site. Code flow is simplified, and resource leaks are far less common.

RAII is also amazing documentation. A structure or class with a Foo* could mean anything: how and when you are supposed to deal with that resource is unclear. A structure or class with a std::unique_ptr<Foo> is clearly owning that pointer. Functions that take std::unique_ptr<Foo> are clearly taking ownership over the passed in pointer. Functions that return std::unique_ptr<Foo> are clearly giving you ownership of that pointer.

OTHER TIPS

RAII in C++ is a much more wide notion. It is an idiom which allows you to write safer code. Resource acquisition part of RAII is where you begin something that must be ended later, such as opening a file and closing it later, allocating some memory and deallocating it, acquiring and releasing a lock. RAII relates to such importants notions as: smart pointers, thread-safety (controlling mutex locks in multi-threaded programs - http://www.boost.org/doc/libs/1_49_0/doc/html/boost/interprocess/scoped_lock.html here is an example of RAII), interaction with files , object ownership (when you use smart pointers like unique_ptr you already use RAII) and so on.

So RAII is always worth to use in good C++ code regardless of exceptions.

The question I have is, if RAII is worth it if you do not have exceptions turned on.

Of course it's worth it! Exception safety is only one aspect of RAII. Another one for example is to avoid leaking dynamically created instances.

I actually think it depends and maybe I'll compete for the weirdest answer here among my C++ peers. That said, if you are already using C++ and C++'s rich type system, I think it'd be crazy not to use RAII for the most part even without exceptions.

The Nuisance of Lacking RAII When Designing and Using Interfaces

You generally don't wanna have to write icky functions that return resources you allocate inside the function that clients have to free/close/destroy manually and externally. At the same time it can be time-consuming to invert the design and have the client allocate the resources and pass them by parameter for a function to use (fill out a buffer allocated by the client, e..g), and still with the client's responsibility to free/close/destroy (just arguably a bit cleaner since the client at least created/opened/allocated the resource himself). Just designing a function that returns a variable-length string whose contents are determined inside the function when it is called is time-consuming and always a little icky either to use or to implement or both lacking RAII.

These are daily nuisances designing and using interfaces in C that you don't have to deal with in C++ if you have resources that clean up after themselves when they go out of scope. The time it takes to just give something a few constructors and a destructors often far outweighs the extra nuisance of having to deal with the above.

Dangers Lacking RAII

And similar thing for just basic things like a scoped mutex. It definitely safeguards you against some potential future bugs to make a mutex explicitly unlock itself when it goes out of scope. It might be easy enough to personally avoid bugs in such cases without exceptions involved when writing the code the first time around and testing it, but some colleague might hastily introduce some early return statement in the future during crunch time into that function and forget to unlock the mutex if that's explicitly required. However probable or improbable that is, it's nice to have that scoped mutex around.

The Benefit of a Dumb Type System

But I'm a weird type that loves both C and C++ and bounces back and forth between both, and there's one thing I find a lot easier to do in C and that's implementing low-level data structures that can work with just about any data type. It's because in C you don't have a rich type system with objects that can have vtables and dtors and ctors and you don't have exceptions. You can generally treat data types as just bits and bytes to memcpy here and memmove there, malloc memory for anything here and realloc there. And the reason we can do that confidently in C is because the type system is so dumb and lacks all these features.

Of course the data structures I design in C are not convenient to use. They lack type safety and often deal with void* pointers, they have to be manually destroyed, etc. They're just really convenient to implement and make cache-friendly while minimizing heap allocations since they let me really focus hardcore on memory layouts and representations when I can just look at data types as bits and bytes to be shuffled around. Meanwhile back in C++, it's not easy even to implement a growable array container like std::vector properly while providing exception-safety and using placement new and manual invocations of destructors and so forth while allocating and freeing through std::allocator. However, std::vector is way, way more convenient and safe to use than any data structure I've designed in C.

So there's a particular kind of convenience to having a type system which lacks destructors and constructors and so forth, and C wouldn't necessarily be made better for what it excels at if it gained destructors and constructors to allow RAII-conforming resources, since suddenly all sorts of existing daily C functions like memcpy would no longer be sane to use anymore. They'd suddenly become the most deadly, error-prone functions under such a type system, as they are in C++.

Therefore I actually think there's an argument to be made that in some low-level domains, RAII could actually be a hindrance, though only for these very low-level domains. Meanwhile it's extremely beneficial for everything else, with or without exceptions. Exceptions turn RAII almost into a requirement, but excluding these very low-level domains, they are still extremely useful regardless.

Trivially Constructible/Destructible UDTs

Sometimes I wish the struct keyword in C++ reduced a structure to a plain old data type that is trivially-constructible and destructible (the type of thing we can safely memcpy around, e.g.), with compiler safeguards that prevent it from storing any data members which aren't. I would find less reasons to use C if so, since at that point we might write generic containers which can only work with such structs, and not classes, without relying on type-traits to determine whether a generic type is trivially constructible/destructible. Because the appeal to C was never so much about the lack of RAII so much as being able to easily assume that many data types we store in our data structures and work with daily don't need it. It's nice when implementing data structures to know you can, say, free a contiguous block of memory for N elements without having to loop through them and invoke destructors. In C++ you often have to err on the safer side and assume that just about everything does need it (or will need it in the future) to the point where, say, memcpy is strongly, strongly discouraged from being used anywhere by anybody.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top