OddBeck
OddBeck

Reputation: 845

make_shared in a try catch - scoping issue

I need to allocate memory, but I would like to do this in a try/catch, but that introduces a new scope, in which the variable is then not available once I'm out of the try-scope. What's the best way to solve this?

try {
    auto something = std::make_unique<SomeClass>;
} 
catch (std::bad_alloc) {
    ...
}
// ... lots of code I don't want to include in the try/catch-scope.
something.callSomeMethod();

How to I go about solving this?

Upvotes: 1

Views: 1997

Answers (4)

Richard Hodges
Richard Hodges

Reputation: 69884

If you really must use something outside the try/catch block (and no, you really mustn't, but if you insist...) then it's a good idea to make it impossible to give yourself a segfault.

calling a method on a null unique_ptr is a sure way to disaster, plus the initialisation of the unique_ptr requires an un-necessary memory allocation.

Another way is to use boost::optional :

#include <iostream>

#include <boost/optional.hpp>

using namespace std;

int main()
{
    class SomeClass { public: int a; void callSomeMethod() const {} };

    boost::optional<SomeClass> something{};

    try {
        something = SomeClass();

        // operation that might throw other_exception
    }
    catch (const std::exception& e) {
        std::cout << "bad" << std::endl;
    }
    // ... lots of code I don't want to include in the try/catch-scope.
    if (something) {
        something.get().callSomeMethod();
    }
    return 0;
}

Upvotes: 0

Jason
Jason

Reputation: 3917

As other answers have mentioned, your code should generally be within the try/catch block, since it doesn't have any meaning if the exception is caught. I'm not sure what your reason is for not wanting to include the code in the try/catch, since you're implying something.callSomeMethod() depends on lots of code, and lots of code depends on std::make_unique. If lots of code isn't dependent, you can delay std::make_unique until after lots of code.

I think it might be worth clarifying, the purpose of exceptions is to abort and handle an exceptional situation. The exceptional situation is that execution of following code can be impacted. So, any code that would be impacted, or transitively code that depends on it, should be included in the try/catch block. This is the minimal scope, by definition, and it's the scope you should use. Nothing more, nothing less.

Sometimes the code to handle an exception can be shared across functions that throw exceptions, but it's still generally best to narrow the scope and write the necessary specific handling code for each one.

It might be worth noting that almost nothing can be done for std::bad_alloc, so it's generally not an exception worth catching. Also, you should generally catch exceptions by reference unless you have a reason to do otherwise. So, your code should look something like this...

try {

    auto something = std::make_unique<SomeClass>;    

    // ... lots of code I don't want to include in the try/catch-scope.

    something.callSomeMethod();
}

catch (std::exception& e) {

    // you dun goofed...
}

Upvotes: 1

R2-Dequeue
R2-Dequeue

Reputation: 638

For posterity, and in case you needed to do something else in your try block that would not result in an invalid pointer (because, as has been mentioned there is a reason what you're trying to do doesn't work). If you're trying to do something else and still want to execute your operations on something afterward, the following code will do what you want:

#include <iostream>

#include <memory>
#include <utility>

using namespace std;

int main()
{
    class SomeClass { public: int a; void callSomeMethod() const {} };

    std::unique_ptr<SomeClass> something{};

    try {
        something = std::move(std::make_unique<SomeClass>());

        // operation that might throw other_exception
    }
    catch (const other_exception& e) {
        std::cout << "bad" << std::endl;
    }
    // ... lots of code I don't want to include in the try/catch-scope.
    something->callSomeMethod();

    return 0;
}

Upvotes: 2

dillonh2
dillonh2

Reputation: 155

There is a specific reason why what you are doing shouldn't work. If the code you wrote worked the way you wrote it, then you would be calling callSomeMethod() on a null object. The right way to do it as Benjamin Lindley said, is to put that code in your try block. That way the variable is in-scope but the method call will only happen if there was not a bad alloc that threw an exception.

Upvotes: 2

Related Questions