RAII and try-catch scoping irritation

Started by
9 comments, last by Washu 18 years, 8 months ago
There's a little something about RAII that's bothering me. Let's take an RaiiObject whose constructor can throw an exception. Consider the following code

void Foo()
{
    try
    {
        RaiiObject ro( "FileName.file" );

        //do stuff with ro, maybe
    }
    catch( std::runtime_error ex )
    {
        std::cout << "An exception occurred: " << ex.what() << std::endl;
        throw ex;
    }

    //now suppose I want to do things with ro here?
    //I can't, because ro is out of scope!
}

So my options are either to make ro a pointer and use new, or put the rest of the function in the try {} section. Suppose I don't want to catch exceptions in the rest of the code though. Then I have to make ro a pointer. These don't seem like great options to me. Right now I can deal with things fairly well by inheriting from std::runtime_error and throwing and catching more specific exceptions. Then the entire body of the function resides within the try block, but I don't necessarily have to catch every exception it causes. Am I missing something, or is this just The Way Things Are?
SlimDX | Ventspace Blog | Twitter | Diverse teams make better games. I am currently hiring capable C++ engine developers in Baltimore, MD.
Advertisement
I'm confused why you think using new and getting a pointer means you don't have to catch the exception.
Quote:Original post by SiCrane
I'm confused why you think using new and getting a pointer means you don't have to catch the exception.

I think his point was that you could define the pointer outside the try-catch block, then initialize it inside, thus allowing you to use different error handling procedures for the creation of the object and the use of the object.
I'm still confused. If you declare it as an auto variable and the constructor throws, you generate an exception. If you declare at as pointer, new it and the constructor throws you generate the exact same exception. Now you have to delete it at the end *and* do all the same exception handling.
Quote:Original post by SiCrane
If you declare at as pointer, new it and the constructor throws you generate the exact same exception. Now you have to delete it at the end *and* do all the same exception handling.


Exactly. Making it a pointer practically defeats the purpose of RAII to begin with. But that's the only way to scope it outside the try-catch block. That's what's bothering me. I can't access the object outside of the try-catch, so I can't use it after exiting the try.

Here, this is what I mean by using a pointer:
void Foo(){    RaiiObj* ro;    try    {        ro = new RaiiObj( "FileName.File" );        ...    }    catch( std::runtime_error ex )    {        ...    }    //do more with ro here    delete ro; // this sucks}
SlimDX | Ventspace Blog | Twitter | Diverse teams make better games. I am currently hiring capable C++ engine developers in Baltimore, MD.
I think what he's getting at is something like this:
{    Foo f;    try    {        f.Bar();    }    catch(FooException e)    {        // handle things one way    }    try    {        f.Baz();    }    catch(FooException e)    {        // handle things another way    }

...which works until your first try-block is trying to construct the variable.
Quote:Original post by Promit
There's a little something about RAII that's bothering me. Let's take an RaiiObject whose constructor can throw an exception. Consider the following code
*** Source Snippet Removed ***
So my options are either to make ro a pointer and use new, or put the rest of the function in the try {} section. Suppose I don't want to catch exceptions in the rest of the code though. Then I have to make ro a pointer. These don't seem like great options to me. Right now I can deal with things fairly well by inheriting from std::runtime_error and throwing and catching more specific exceptions. Then the entire body of the function resides within the try block, but I don't necessarily have to catch every exception it causes. Am I missing something, or is this just The Way Things Are?

That's why you put the code that deals with the object also inside the try block rather than outside.
So in short, there's no particularly good way to scope the RAII object outside of the try?
SlimDX | Ventspace Blog | Twitter | Diverse teams make better games. I am currently hiring capable C++ engine developers in Baltimore, MD.
Generally, I wouldn't catch at that level, but at the next highest level:
try {  Foo();} catch(FileNotFoundException& ex) {  ...}

Assuming that's not an option, use a copy:
void Foo() {  RaiiObject ro1;  try {    RaiiObject ro2(...);    ro1 = ro2;  }  catch(FileNotFoundException& ex) {    ...  }}

Of course, even that's not a perfect solution.

In time the project grows, the ignorance of its devs it shows, with many a convoluted function, it plunges into deep compunction, the price of failure is high, Washu's mirth is nigh.

Oh god, it just hit me like a ton of bricks.

Accessing an RAII object outside of the try block only makes sense if the catch block always causes execution to leave the function, either by returning or by throwing. If the catch allowed execution to continue in the function, and the rest of the function attempted to use that RAII object...


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

This topic is closed to new replies.

Advertisement