Passing values to atexit
-
24-02-2021 - |
Question
I want to push a series of clean up functions as they are needed. I was using atexit to do this for one cleanup function without any parameters, but I am not sure how to expand this approach to more then one clean up function. I am not very familiar with boost::bind, but assumed it would be a good idea as that is how I binded my functions to threads...
In c++ I am trying to get the following to work:
Function Definition
static void closeAnimation(string prefix="");// static member of fileWriter
Code:
atexit(boost::bind(fileWriter::closeAnimation, "0")); // I want to first prefix to be "0"
The error:
cannot convert ‘boost::_bi::bind_t<void, void (*)(std::basic_string<char>), boost::_bi::list1<boost::_bi::value<const char*> > >’ to ‘void (*)()’ for argument
Thanks in advance!
Solution
There is no "1 line solution without complicating your code".
The worst solution is to store that parameter in a global variable, and retrieve it in the atexit handler
Since you're using C++, the destructor of a static variable could also serve as an atexit
handler. Then you can pass parameter at the constructor of that static variable for parametrization, e.g.
struct AtExitAnimationCloser
{
const char* _which_param;
AtExitAnimationCloser(const char* which_param) : _which_param(which_param) {}
~AtExitAnimationCloser() { FileWriter::closeAnimation(_which_param); }
};
void f()
{
printf("entering f\n");
static AtExitAnimationCloser s0 ("0"); // registers closeAnimation("0") at exit
static AtExitAnimationCloser s1 ("1"); // registers closeAnimation("0") at exit
printf("leaving f\n");
}
Demonstration: http://www.ideone.com/bfYnY
Note that static variables are bound to their name, so you cannot say
for (it = vecs.begin(); it != vecs.end(); ++ it)
{
static AtExitAnimationCloser s (*it);
}
to call atexit for all the content. But you could make the static variable itself take whole range
static AnotherAtExitAnimationCloser s (vecs.begin(), vecs.end())
Finally, with idiomatic C++ I don't think you need to use these tricks... You could store a vector of types T, which on destruction ~T calls fileWriter::closeAnimation
.
OTHER TIPS
atexit
is a legacy C function, and not very adapted to C++. You can
register more than one function with atexit
, but all of them must be
void (*)()
, no boost::function
and no arguments.
In C++, most, if not all of atexit
's functionality has been subsumed
by the destructors of static objects. In your case, I'd write something
like:
#include <vector>
class CallInDestructor
{
class Registry
{
std::vector<CallInDestructor*> myInstances;
public:
register_caller(CallInDestructor* caller)
{
myInstances.push_back(caller);
}
~Registry()
{
while ( !myInstances.empty() ) {
delete myInstances.back();
myInstances.pop_back();
}
}
};
static Registry& registry()
{
static Registry theOneAndOnly;
return theOneAndOnly;
}
protected:
CallInDestructor() { registry().register_caller( this ); }
public:
virtual ~CallInDestructor() {}
};
template<typename Fnc> void callAtExit( Fnc fnc );
template<typename Fnc>
class ConcreteCallInDestructor : public CallInDestructor
{
Fnc myFnc;
ConcreteCallInDestructor( Fnc fnc = Fnc() ) : myFnc( fnc ) {}
virtual ~ConcreteCallInDestructor() { myFnc(); }
friend void callAtExit<Fnc>( Fnc fnc );
};
template<typename Fnc>
void
callAtExit( Fnc fnc )
{
new ConcreteCallInDestructor<Fnc>( fnc );
}
Use callAtExit
as you would atexit
, but it should work for anything
which can be called with no arguments (including a boost::function
).
Or you can write your own classes deriving from CallInDestructor
, as
long as you take steps to ensure that all instances are dynamically
allocated (since the constructor registers the object so that it will be
deleted); these classes can contain any additional data you want.
The problem is that bind returns a function object, and atexit takes a pointer to a function that returns void and takes no parameter.
You can try this:
void fun() {
fileWriter::closeAnimation("0");
}
atexit(fun);