0

Suppose we have a constructor which may throw exceptions.

class A{
public:
    A(); // may throw exceptions
};

And we can use this way to catch the exceptions:

try{
    A a;
    // do somethings to a, and I have to put everything about a here
}catch(...){
    // handle exceptions
}

So my question is how to avoid putting everything in the try-catch block, without using a pointer.

4
  • 1
    You generally catch exceptions only at points where you can respond meaningfully. Commented Jul 15, 2016 at 12:15
  • 3
    This gotw has a detailed discussion on the topic Commented Jul 15, 2016 at 12:21
  • 1
    What would be the problem with putting all manipulations of a in the try block? Commented Jul 15, 2016 at 12:26
  • 1
    The link to the gotw is the right answer. Commented Jul 15, 2016 at 12:49

3 Answers 3

1

You could define a creation function that handles the exception for you, with a sensible default value to return in case of an exception, for example:

struct A
{    
    A() : data("Default")
    {
        std::cout << "In A()" << std::endl;
    }

    A(const A& other) : data(other.data)
    {
        std::cout << "In A(A)" << std::endl;
    }

    A(bool param) 
    {
        std::cout << "In A(bool)" << std::endl;
        if(param)
            throw std::runtime_error("Failed");

        data = "Hello";
    }

    std::string data;
};

A createA(bool param, A& def_a)
try
{
    return A(param);
}
catch (...) {
    //...
    return def_a;
}

Then you can initiate your actual A with the return value of this function. Due to RVO, no copy will be performed if the creation is successful, only if it fails (as it will have to then copy the default value):

int main(int argc, char**args) 
{
    A defA;

    A a1 = createA(true, defA);
    A a2 = createA(false, defA);

    std::cout << "A1: " << a1.data << std::endl;
    std::cout << "A2: " << a2.data << std::endl;

    return 0;
}

The output of this is:

In A()
In A(bool)
In A(A)
In A(bool)
A1: Default
A2: Hello

The first constructor is the default value. Then you can see after the first A(bool) call (which throws), a copy constructor call is made to return the default value. The second call, which doesn't fail, has no copy constructor call (due to RVO). After this you end up with a default A and a successfully created A which you can use thereafter.

Of course, if the copy constructor can also throw, then you might have an exception escape createA - if this is the case you would have to modify this design somewhat.

Sign up to request clarification or add additional context in comments.

Comments

0

One possible approach is to use a sentry.

class sentry {

public:

    bool exception_thrown=true;

    void constructed()
    {
        exception_thrown=false;
    }

    ~sentry()
    {
        if (exception_thrown)
        {
           // Whatever you want to do
        }
    }
};

Then:

sentry a_sentry;
A a;

a_sentry.constructed();

In the sentry's destructor, if exception_thrown is set, it could only be because an exception was thrown in A's constructor, because the flag gets cleared immediately after A gets fully constructed. So, put the cleanup code in the destructor. One thing you have to be careful with this approach is that the destructor itself cannot throw an exception of its own.

You can then also do things like:

class A_with_sentry : public sentry, public A {

public:

      A_with_sentry()
      {
           constructed();
      }
};

Then just declare:

A_with_sentry a;

Then, go a step further, and make this a template function, have the constructor perfect-forward its variadic parameters to the sentried object's constructor, etc...

1 Comment

This is interesting.. but you cannot catch the actual exception nor will know the type this way. That said, it's not necessary in many cases.
0

There is other way but not recommended because you need to deal with pointers.

A* a=nullptr;
try {
    a = new A();
}
catch(...){
}
if(a){
    //do somethings to a, and I have to put everything about a here
    delete a;
}

1 Comment

Change the pointer to std::optional<A> and then it's somewhat better.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.