Suppose I construct a RAII object, and that object may fail to construct. How do I handle this?
try {
std::vector<int> v(LOTS);
// try scope ends here because that's what the catch is for
} catch( const std::bad_alloc& ) {
// ...
}
// v? what v?
Granted, the default constructor of std::vector
won't throw and that can help, but this is not the general case. A constructor may very well throw. If I want to handle any resource acquisition failure, how do I do that while still being able to proceed if it doesn't throw?
Edit: To clarify, my issue is that if a resource fails to acquire then I might want to try again, and so on. Maybe I can try acquiring an alternative resource.
Resource Acquisition Is Initialization or RAII, is a C++ programming technique which binds the life cycle of a resource that must be acquired before use (allocated heap memory, thread of execution, open socket, open file, locked mutex, disk space, database connection—anything that exists in limited supply) to the ...
The principle that objects own resources is also known as "resource acquisition is initialization," or RAII. When a resource-owning stack object goes out of scope, its destructor is automatically invoked. In this way, garbage collection in C++ is closely related to object lifetime, and is deterministic.
Resource acquisition is initialization (RAII) is a programming idiom used in several object-oriented, statically-typed programming languages to describe a particular language behavior.
RAII is an important C++ idiom for resource management. Notably, RAII provides a structural idiom for proper resource management with exceptions. The power of the idiom is in the guarantees it provides. Properly used, the destructor for your RAII-object is guaranteed to be called to allow you to free resources.
Depends what you mean by "proceed". Whatever operation requires the resource will fail: that's what "requires" means. So when you want to continue after an error, you might end up writing code like this:
void something_using_RAII(thingummy &t) {
vector<int> v(t.size_required);
// do something using v
}
...
for each thingummy {
try {
something_using_RAII(this_thingummy);
} catch(const std::bad_alloc &) {
std::cerr << "can't manage that one, sorry\n";
}
}
That's why you should only catch exceptions when there's something worthwhile you can do with them (in this case, report failure and move on to the next thingummy).
If you want to try again on failure, but only if the constructor fails, not if anything else fails:
while(not bored of trying) {
bool constructor_failed = true;
try {
vector<int> v(LOTS);
constructor_failed = false;
// use v
} catch(...) {
if (!constructor_failed) throw;
}
}
This is more-or-less how std::new_handler
works - the handler is called in the catch clause of a similar loop, although with no need for a flag.
If you want to try a different resource on failure:
try {
vector<int> v(LOTS);
// use v
} catch(...) try {
otherthing<int> w(LOTS);
// use w
} catch(...) {
// failed
}
If "use v" and "use w" are basically the same code, then refactor into a function and call it from both places. Your function is doing quite a lot at this point.
If an RAII constructor throws, all resources bound to RAII objects prior to the throwing point will be cleaned up properly. The C++ rules are sensibly designed to guarantee that.
If your v
construction throws because of a bad_alloc
then any RAII object created prior to v
in the try
block will be properly cleaned up.
So if you consequently use RAII, you don't need a manual try
/ catch
like that, because the RAII objects handle cleanup for you. If you do need it for some reason, in the case above you could use swap like the following.
std::vector<int> v;
try {
std::vector<int> vtry(LOTS);
v.swap(vtry); // no-throw
} catch( const std::bad_alloc& ) {
// ...
}
// v!
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