Вопрос

Given a situation where I want to have a stack allocated object that may throw during construction, but want to handle the exception at the calling site, how do I make the object accessible from outside the try block where it is constructed?

Eg

class MyThrowingClass {

MyThrowingClass() {throw exception();}

doSomethingImportant() {
//...
}

};

int main() {

//Need to catch the exception:
try {
MyThrowingClass myObj;
} catch() {
//actually handle the error
//...
}

//Also need to use myObj later on
myObj.doSomethingImportant();//but we can't use it here because it was scoped to the try block...
}

If I have myObj encased in a try then nothing outside the scope of the try can see it, but I don't want to have everything else inside there because then the code becomes 30 levels of nested try blocks, which is what the exception handling is supposed to remove with using the alternative of init function error codes.

I can't handle the exception inside the constructor as the reaction to the exception depends on the context of the use of MyThrowingClass.

Obviously the problem could be circumvented by having a

MyThrowingClass* pMyObj;

and then being able to wrapper the

pMyObj = new MyThrowingClass();

but surely this should be achievable with a stack allocated object too?

Is the only solution to do something like

MyThrowingClass myObj;

try {
    myObj.init();
} catch(...) {
//...
}

at which point we're back to basically as bad as error codes and having an uninitialised or partially initialised object.

Note that this is not intended to be a global object, I want to have something that will be instantiated in many places.

Is it really the ideal solution to have a try block that wraps the entire scope (here everything that would be inside main) and catches that handle every possible exception at the end of that one try block as opposed to being able to handle exceptions vaguely near to their site?

int main() {
try {

//absoultely everything

}
catch (exceptionTypeA &a) {
//...
}
catch exceptionTypeB &b) {

}

}
Это было полезно?

Решение

how do I make the object accessible from outside the try block where it is constructed?

If construction fails, then the object doesn't exist; so there's nothing to access.

surely this should be achievable with a stack allocated object too?

Automatic (i.e. stack-allocated) objects are only initialised once, so even if you handle the exception, there's no way to go back and try to re-initialise it. If you do want to be able to retry, then you'll have to use something more complicated, like the dynamic allocation or two-stage initialisation you propose. Another alternative is something boost::optional (or, from next year, std::optional), which allows you to create and destroy objects at will within a lump of automatic storage.

Is it really the ideal solution to have a try block that wraps the entire scope?

In the typical case, where exceptions aren't handled locally and initialisation failure indicates an unrecoverable error, yes. In your special case, where you can handle it locally and recover, no.

Другие советы

try is designed to scope objects that can throw for a reason. By working around it, you are circumventing that which it's trying to protect you from (using a poorly defined object.) Consider using a function to generate the object. By using a noexcept move constructor, you can guarantee that the move out of the object is safe:

class MyThrowingClass {
  public:
    MyThrowingClass() {
        throw exception();
    }

    // throw() is *okay* if you don't have noexcept
    MyThrowingClass(const MyThrowingClass && other) noexcept { 
    }

};


MyThrowingClass GetObj() {
    try {
        return std::move(MyThrowingClass());
    } catch(...) {
        // return some well defined default or terminate program
    }
}

int main() {
    MyThrowingClass myObj(std::move(GetObj()));
}

Given a situation where I want to have a stack allocated object that may throw during construction, but want to handle the exception at the calling site, how do I make the object accessible from outside the try block where it is constructed?

Basically, you can't. As for wrapping ALL the code in a try block being a good or bad idea, that depends on the size of "all the code" - a dozen lines or so lines is no big deal.

Do you really want to call MyThrowingClass::doSomethingImportant() if the initialiser throws? Unless you somehow guarantee to fix the broken initialisation in the catch you're then calling methods on a partially initialised object.

Including the call to doSomethingImportant() in the same try block as the construction of the object would give you exactly what exceptions are designed to do: in the event of a problem skip past the following code (which is dependent on the preceding code) to an error handler.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top