Detecting when destructor running due to exception being thrown?
Asked Answered
L

4

23

What is a good way in C++ to detect in a destructor that it is being run during unwind of the stack due to an exception being thrown as opposed to a normal exit of scope triggering the destructor? I'd like to know so that I can create a class that has some cleanup code that is always run on normal exit but skipped when an exception occurs.

Lovins answered 22/8, 2011 at 1:16 Comment(6)
Interesting question! I'm tempted to say that it can't be done, because the objects don't really know that an exception is active, they just go out of scope as the exception unwinds the stack, just like if they go out of any other scope. If anything you'd need some platform-specific "hack"...Choragus
Curious why you want avoid cleanup during stack unwinding?Aucoin
@Eric Z: I'd thought of using this for automatic stack logging. Something along the lines of Log xxx("funcA - ", arg1, arg2, arg3);, but instrumenting the functions is tedious.Natality
@EricZ To not commit pending database update in destructor when exception is thrown.Lovins
@WilliamKF, Ok, it's a database transaction rather than a resource cleanup I thought. Thanks for letting me know.Aucoin
I think it would be more straightforward to just call inst.CleanupCode() on normal exit than fiddle with the destructor. I think this is a case where trying to use RAII to solve error handling and normal program execution is just going to cause a headache.Salford
V
25

std::uncaught_exception() (defined in <exception>) will tell you in your destructor if it was called because of an exception:

class A
{
public:
    ~A()
    {
        if (std::uncaught_exception()) {
            // Called because of an exception
        } else {
            // No exception
        }
    }
};
Vadavaden answered 22/8, 2011 at 1:47 Comment(3)
Nice, I never knew this existed! :-)Choragus
Not as nice as you might think - see the linked article on my post.Ricci
In C++17 it is std::uncaught_exceptions() (plural)Portecochere
R
6

Probably this article will help you. The article will show you the problems with std::uncaught_exception() and contains an advice how to deal with exceptions in destructors.

Ricci answered 22/8, 2011 at 6:36 Comment(1)
TL;DR: You don't want different semantics in your destructor. And also uncaught_exceptions is not robust.Salford
A
2

Don't do that unless you have a good reason. The stack unwinding is such a language feature that all automatic objects inside try block will be enforced to deallocate, so that the resources inside them have a chance to release.

You want skip cleanup in dtor during stack unwinding, which bypasses the original intention of it. And you'll run the risk of leaking resources.

Example

class CDBConnection
{
  public:
    CDBConnection()
    {
      m_db.open();
    }
    ~CDBConnection()
    {
      if (!std::uncaught_exception())
        m_db.close();

      // if this is called during a stack unwinding,
      // your DB connection will not be closed for sure.
      // That's a resource leakage.
    }
    //..
  private:
    DB m_db;
};

void main()
{
  //..
  try
  {
    // code that may throw
    CDBConnection db;
    //..        
  }
  catch(const CDBException& exp)
  {
    // properly handle the exception
  }
}
Aucoin answered 22/8, 2011 at 2:12 Comment(0)
L
0

Here is one way I can think of, but it seems clumsy:

{
  myCleanupClass unwindAction;
  try {
    // do some work which may throw exception.
  } catch (...) {
    unwindAction.disableDestructorWork();
    throw;
  }
}
Lovins answered 22/8, 2011 at 1:25 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.