Remove boost feature during use

I have a situation where the function boost :: function and boost :: bind (in fact, std :: tr1 :: function and bind) are deleted during use. It is safe? I would usually avoid this, but the violation code is a little rooted, and my only other option is to add a new thread.

typedef function<int(int)> foo_type; foo_type* global_foo = NULL; int actual_foo( int i, Magic* m ) { delete global_foo; return m->magic(i); } int main() { Magic m; global_foo = new foo_type( bind( &actual_foo, _1, &m ); return (*global_foo)(10) } 

Bound parameters are always simple integral types (int and pointer in real code), not links.

+4
source share
2 answers

boost::function or std::tr1::functions are objects to copy. Thus, as a rule, there is absolutely no reason to highlight them - just pass them by value.

They are well optimized for most real-world cases ... So just pass them by value:

 typedef function<int(int)> foo_type; foo_type global_foo; int actual_foo( int i, Magic* m ) { delete global_foo; return m->magic(i); } int main() { Magic m; global_foo = bind( &actual_foo, _1, &m ); return global_foo(10) } 

The code you suspected is dangerous, run this code:

  #include <boost/function.hpp> #include <boost/bind.hpp> #include <iostream> using namespace std; boost::function<void()> *glb; struct Data { int x; Data(int _x = 0) : x(_x) { cout<<"ctor:"<<this<<endl; } ~Data() { cout<<"dtor:"<<this<<endl; } Data(Data const &p) {x=px; cout<<"ctor:"<<this<<endl; } Data const &operator=(Data const &p) { x=px; cout<<this<<"="<<&p<<endl; return *this; } }; void func(Data const &x) { delete glb; cout<<&x<<endl; } int main() { glb=new boost::function<void()>(boost::bind(func,Data(3))); (*glb)(); return 0; } 

You will find that you are trying to access func on the destroyed object (dtor with the same pointer value), which is shown in cout<<&x<<endl , has already been called.

Because when you destroy function objects, you also destroy the bound parameters that you have and Data const &x become inaccessible because it was destroyed with global_function

Edit: Cleanup for comment:

If you have something like

 map<string,function<void()> > calls; void delete_key(){ calls.erase("key"); } main() { calls["key"]=delete_key; // Wrong and dangerous // You delete function while in use calls["key"](); // Correct and safe // You create a copy of function so it would not // be deleted while in use. function<void()> f=calls["key"]; f(); } 
+5
source

Edit: After discussing this answer a bit (with Charles Bailey), I find this unsafe as it depends on the implementation of the boost :: function.

The table of calls when entering actual_foo will look something like this:

 actual_foo boost::function::operator() main 

So, when actual_foo has finished executing, we will return to the operator() boost::function code, and this object has been deleted. This is not a problem - it is a bit like calling delete this , but you have to be very careful what you do in the member function of the remote object. You cannot name any virtual functions or use any data items.

The problem is that I don't know that boost::function makes any guarantees as to what it does in operator() after the function that it wraps has been called. It seems that on my platform this does nothing dangerous (therefore valgrind does not complain), but it is quite possible that on another platform, with a different implementation or with different compilation flags, you might want to do something that is not allowed only after uninstallation object - for example, it can write out some debugging information using a member variable.

Thus, I believe this is a dangerous thing that can lead to undefined behavior under certain circumstances.

Further note:

I quickly looked at what the promotion actually does after calling the function. Looking here: http://boost.cvs.sourceforge.net/viewvc/boost/boost/boost/function/function_template.hpp?view=markup in the operator () function on line 687, my interpretation is that it immediately returns the return value and does not do anything else, so in practice, with this implementation, you should be fine, but comments about it are dangerous. Please note that I could very well find the wrong bit of code and / or misunderstood it ...

Original answer below:

I believe that everything is in order. By the time actual_foo boost::bind and boost::function objects have completed their tasks, and you are executing the actual actual_foo function.

I tested this on my platform (gcc 4.2.4, Linux) by running the program through valgrind .

Here is the program that I ran:

 #include <boost/bind.hpp> #include <boost/function.hpp> class Magic { public: int magic( int i ) { return 5; } }; typedef boost::function<int(int)> foo_type; foo_type* global_foo = NULL; int actual_foo( int i, Magic* m ) { delete global_foo; return m->magic(i); } int main() { Magic m; global_foo = new foo_type( boost::bind( &actual_foo, _1, &m ) ); return (*global_foo)(10); } 

and here is the output of valgrind:

 $ valgrind ./boost_bind ==17606== Memcheck, a memory error detector. ==17606== Copyright (C) 2002-2007, and GNU GPL'd, by Julian Seward et al. ==17606== Using LibVEX rev 1804, a library for dynamic binary translation. ==17606== Copyright (C) 2004-2007, and GNU GPL'd, by OpenWorks LLP. ==17606== Using valgrind-3.3.0-Debian, a dynamic binary instrumentation framework. ==17606== Copyright (C) 2000-2007, and GNU GPL'd, by Julian Seward et al. ==17606== For more details, rerun with: -v ==17606== ==17606== ==17606== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 17 from 1) ==17606== malloc/free: in use at exit: 0 bytes in 0 blocks. ==17606== malloc/free: 1 allocs, 1 frees, 16 bytes allocated. ==17606== For counts of detected errors, rerun with: -v ==17606== All heap blocks were freed -- no leaks are possible. 

I have to say that this seems like a strange thing. I would prefer, if possible, to use automatic deletion of this functional object by making it a stack variable or by deleting it in the stack variable destructor (as in RAII ). It would be more neat, safer, less scary and safer. But I am sure that you already know all this, and there are good reasons for living with the code as it is.

+2
source

All Articles