patterncppMinor
Automatic function call on scope exit
Viewed 0 times
functionexitcallscopeautomatic
Problem
I need to add logging functionality for an existing code, based on a result of an operation. I was thinking of creating a class that, when constructed, receives a condition function and a function for execution. Based on the result of the condition, the code will be executed.
Example:
I will use this class like this:
I would be happy to get some comments on this code. Are there any problem I am not seeing in this solution? Are there any better solutions?
Example:
class auto_logger{
public:
auto_logger(const std::function& cond,
const std::function& func): _cond(cond), _func(func) {}
virtual ~auto_logger()
{
try {
if (_cond()) {
_func();
}
} catch (...) {
}
}
private:
std::function _cond;
std::function _func;
};I will use this class like this:
int existing_func()
{
int res = FAILURE;
// this is the only additional code
auto_logger logger([&](){
return res == SUCCESS;
},
[](){
print_to_log();
});
do_some_stuff_that_may_throw();
res = perform_operation();
return res;
}I would be happy to get some comments on this code. Are there any problem I am not seeing in this solution? Are there any better solutions?
Solution
Echoing the comments from the question, the usual design for these sort of things, often called a scope guard, is to have a function that is called when it no longer needs to be run (frequenlty called
Is the expectation that there will be derived classes destroyed via base pointers or via base references? If not, a non-virtual destructor is a common way to signal that a class is not designed to be inherited from.
Note that Visual C++ (at least) can be configured via the /EHa switch (this is usually a misguided configuration, IMHO) to catch structured exceptions (things like access violations/segmentation faults) in a
A common technique to hedge against this configuration is to catch
This assumption may or may not be valid. Say this code needs to work in an environment where it is normal and okay to throw and catch things that don't derive from
dismiss or release.int existing_func()
{
int res = FAILURE;
auto_logger logger([](){
print_to_log();
});
do_some_stuff_that_may_throw();
res = perform_operation();
dismiss_logger(logger, res);
return res;
}
void dismiss_logger(auto_logger& logger, int result) {
if (res != SUCCESS) {
logger.dont_log();
}
}virtual ~auto_logger()Is the expectation that there will be derived classes destroyed via base pointers or via base references? If not, a non-virtual destructor is a common way to signal that a class is not designed to be inherited from.
} catch (...) {Note that Visual C++ (at least) can be configured via the /EHa switch (this is usually a misguided configuration, IMHO) to catch structured exceptions (things like access violations/segmentation faults) in a
catch (...) handler.A common technique to hedge against this configuration is to catch
std::exception& under the assumption that all sensible exceptions will derive from it. Anything else being caught (e.g, an int, a structured exception) is likely indicative of a bug in the program so egregious that termination is the only safe recourse, as there's likely some corrupt state somewhere that prevents any further cleanup.This assumption may or may not be valid. Say this code needs to work in an environment where it is normal and okay to throw and catch things that don't derive from
std::exception. (E.g., a framework that has its own base exception class is being used.) If Visual C++ isn't being used of /EHa will never be used, this recommendation may come off as paranoid. :-)Code Snippets
int existing_func()
{
int res = FAILURE;
auto_logger logger([](){
print_to_log();
});
do_some_stuff_that_may_throw();
res = perform_operation();
dismiss_logger(logger, res);
return res;
}
void dismiss_logger(auto_logger& logger, int result) {
if (res != SUCCESS) {
logger.dont_log();
}
}Context
StackExchange Code Review Q#35482, answer score: 8
Revisions (0)
No revisions yet.