Open In App

How to detect Stack Unwinding in a Destructor in C++?

Last Updated : 06 Dec, 2022
Improve
Improve
Like Article
Like
Save
Share
Report

What is Stack Unwinding?

Stack unwinding is the process of executing destructors for all local objects when an exception propagates out of a function. It happens when an exception is thrown and not caught within the same function. When this occurs, the destructors for all objects with automatic storage duration declared in that function are called in reverse order of their declaration before the control is transferred to a handler (if any) or returned to the caller.

Stack unwinding is usually transparent to the programmer and happens automatically. Stack Unwinding is commonly associated with Exception Handling. When an exception occurs in C++, the function call stack is linearly searched for the exception handler, and any entries before the function with the exception handler are eliminated. If an exception is not handled in the same code, stack unwinding is required (where it is thrown). Stack unwinding is essentially the process of invoking the destructors for all automated objects created at run time (whenever an exception is thrown).

Different ways to approach Stack Unwinding:

There are different ways of approaching the topic of stack unwinding in a destructor.

  • One way is to look at it from the perspective of what happens when an exception is thrown.
  • Another way is to look at it from the perspective of how a destructor is called when an object goes out of scope.

Looking at stack unwinding from the perspective of an exception being thrown can help us understand why it is important to have a destructor that can clean up after itself. 

When an exception is thrown:

The program execution jumps to the nearest catch block. But before that happens, all the objects that were created in the try block are destroyed. This includes any local objects as well as any objects that were created by dynamic memory allocation. If the destructors for these objects do not properly clean up, then it can lead to memory leaks or other problems.

From the perspective of how a destructor is called: 

It can help to understand the importance of a destructor. A destructor is called whenever an object goes out of scope. When an object goes out of scope, its destructor is called and any resources that it was using are freed up. If the destructor does not properly clean up, then it can lead to resource leaks or other problems.

How to Detect Stack Unwinding?

In a destructor, stack unwinding can be detected by looking for signs of cleanup activity, such as the following:

  • Invoking functions that release resources (e.g. close files or free memory)
  • Logging messages
  • Setting flags

If any of these activities are observed in a destructor, it is likely that stack unwinding is taking place.

Using the std::uncaught_exception()

You can use the std::uncaught_exception() function which returns true if an exception is currently being handled (i.e. thrown but not yet caught). So, in your destructor, you would check if std::uncaught_exception() returns true or false and take appropriate action accordingly.

Example:

C++




#include <bits/stdc++.h>
using namespace std;
  
class MyClass {
public:
    MyClass()
    {
        // allocate some resource 
    }
  
    ~MyClass() 
    {
        if (!std::uncaught_exception()) {
            // release resource
        }
        Â        
    };
};


Overriding std::terminate() function

In order to detect when stack unwinding is occurring, you can override the std::terminate() function. The runtime calls this function when it cannot find a suitable exception handler. By overriding std::terminate(), you can set a breakpoint or log a message to help debug your program.

Here is an example of how you would override std::terminate():

C++




void my_terminate()
{
    // Set breakpoint here or log message
    std::cerr << "Stack unwinding detected!" << std::endl;
  
    // Call original terminate function
    std::terminate();
}
  
int main()
{
    // Install our custom terminate handler
    std::set_terminate(my_terminate);
  
    try {
        // Code that might throw an exception goes here...
    }
    catch (...) {
        // Exception handlers go here...
    }
    return 0;
}


By setting a flag in the constructor: 

Set a flag in the constructor and check that flag in the destructor. If the flag is set, then you know that the destructor was called because of an exception. Here is an example:

C++




class MyClass {
public:
    MyClass()
        : m_isUnwinding(false)
    {
        // Constructor
    }
  
    ~MyClass()
    {
        if (m_isUnwinding) {
            // The stack is unwinding because of an
            // exception
        }
        else {
            // No exception is being handled
        }
    };
};


Related Articles:



Like Article
Suggest improvement
Previous
Next
Share your thoughts in the comments

Similar Reads