Let's have a template class like below:
template< typename T >
class finalGuard {
T m_func;
public:
finalGuard(T func) : m_func(func) {}
~finalGuard() { m_func(); }
};
What is the requirement? I need to execute lambda when a template instance of the above class goes out of scope or any exception is caused during the execution of code. We can think of this as more or like a final pattern. When a class object's lifetime ends, a piece of code will start executing to wrap up things.
Now I have a factory-like function defined as below to resolve type dependency which lambda is expecting:
template< typename T >
finalGuard< T > finalCall(T obj) { return obj; }
The below piece of code is used to bind the lambda with the class instance and it gets executed as soon as a class object destructor gets invoked.
int main()
{
try {
auto guard = finalCall([]() noexcept {
cout << "Good bye via exception!\n";
});
cout << "This is followed by a throw statement\n";
throw runtime_error("Unknown Error"); // This is a throw which calls finalCall
cout << "This line will never execute\n";
}
catch (exception& ex) { cout << "This is caused by an " << ex.what() << "\n\n"; }
{
auto guard = finalCall([]() noexcept {
cout << "Good bye via normal execution!\n";
});
cout << "This is a simple execution\n";
cout << "Normal execution ends here...\n";
}
return 0;
}
So, as mentioned above code, irrespective of the execution path lambda will be executed to do some work.
The above code works with Microsoft VS 2019, and GNU C++ [C++ 11/14/17].
If we are targeting only the GNU C++ compiler then code can be stripped down a bit. In that case, we don't need a factory-like function explicitly. (Used GCC 11.1.0)
#include <iostream>
#include <stdexcept>
using std::cout;
using std::exception;
using std::runtime_error;
template< typename T >
class finalGuard {
T m_func;
public:
finalGuard(T func) : m_func(func) {}
~finalGuard() { m_func(); }
};
/*
Resolving type dependency for lambda is not needed on GCC C++ 17 onwards.
//template< typename T >
//finalGuard< T > finalCall(T obj) { return obj; }
*/
int main()
{
try {
auto guard = finalGuard([]() noexcept {
cout << "Good bye via exception!\n";
});
cout << "This is followed by a throw statement\n";
throw runtime_error("Unknown Error"); // This is a throw which calls finalCall
cout << "This line will never execute\n";
}
catch (const exception& ex) { cout << "This is caused by an " << ex.what() << "\n\n"; }
{
auto guard = finalGuard([]() noexcept {
cout << "Goodbye via normal execution!\n";
});
cout << "This is a simple execution\n";
cout << "Normal execution ends here...\n";
}
return 0;
}
Comments