Remove the original pointer argument to boost :: bind

Suppose I have a bunch of A* allocated that I want to pass as an argument to boost::bind . boost::bind stored for later processing in some container of type STL boost::functions .

I want A* be destroyed when the STL container is destroyed.

To demonstrate:

 A* pA = new A(); // some time later container.push_back(boost::bind(&SomeClass::HandleA, this, pA); // some time later container is destroyed => pA is destroyed too 

How can I do that?

EDIT

Perhaps what I want is not so realistic.

I have a raw pointer and a function that receives a raw pointer. The call is delayed using boost :: bind . At this point, I want automatic memory management in case boost :: bind wants to execute. I am lazy, so I want to use a “turnkey” solution for smart pointers.

std :: auto_ptr looks like a good candidate, however ...

 auto_ptr<A> pAutoA(pA); container.push_back(boost::bind(&SomeClass::HandleA, this, pAutoA); 

not compiled (see here )

 auto_ptr<A> pAutoA(pA); container.push_back(boost::bind(&SomeClass::HandleA, this, boost::ref(pAutoA)); 

pAutoA is destroyed by removing the underlying pA.

EDIT 02

In the specified container, I will need to store different "callbacks" with different arguments. Some of them are raw pointers to an object. Since the code is old, I cannot always change it.

Writing your own wrapper to store callbacks in the container is the last resort (although perhaps the only one), therefore, the reward.

+6
source share
4 answers

The idea of ​​@pmjordan has already gone in the right direction. You replied that you cannot use shared_ptr because you cannot return it back after creating it. But this is not entirely correct: with shared_ptr you can use a custom deleter mechanism. Here's how to do it:

Suppose these toy defects are for your A and f(A*) :

 struct A { ~A() { std::cout << "~A()" << std::endl; } }; void f( A * a ) { std::cout << "in f(A*)" << std::endl; delete a; } 
  • Write a deletion that can be disabled:

     struct opt_delete { bool m_delete; opt_delete() : m_delete( true ) {} template <typename T> void operator()( T * t ) { if ( m_delete ) delete t; } }; 
  • Then you can write the take() function, which again captures the shared_ptr payload:

     template <typename T> T * take( const boost::shared_ptr<T> & sp ) { opt_delete * d = boost::get_deleter<opt_delete>( sp ); assert( d ); assert( d->m_delete == true ); d->m_delete = false; return sp.get(); } 

    (this will leave a payload in the remaining instances of shared_ptr , but for your case this is normal, and assert() covers cases where this is not the case).

  • Now you can manually wrap f(A*) as follows:

     void f_sp( const boost::shared_ptr<A> & a ) { f( take( a ) ); } 
  • And finally, check out two scenarios:

     int main( int argc, char * argv[] ) { const boost::shared_ptr<A> a( new A, opt_delete() ); const boost::function<void()> func = boost::bind( &f_sp, a ); if ( argc >= 2 && *argv[1] == '1' ) // call 'func' func(); else ; // don't return 0; } 

Running a test program using argument 1 will print

in f (A *)
~ A ()

and without (or any other argument), it will print

~ A ()

You can extend the test harness to put func in the container first, but it will still be safe. The only thing that is unsafe in the case is calling func copies more than once (but then you will call the second statement in take() ).

EDIT . Please note: this mechanism is not thread safe. To make it thread safe, you need to put opt_delete mutex to synchronize operator() with take() .

+8
source

I assume that you mean that you have some kind of function, let it be called f() , which takes A* , which you then proxy with boost::bind ? Can you change this function to accept Boost / TR1 shared_ptr<A> instead? Using shared_ptr (or, less likely, C ++ 98 std::auto_ptr ) should solve your life cycle problem.

Alternatively, if you cannot change f yourself, you can create a wrapper that accepts shared_ptr<A> , pull out the raw pointer and call f with it. If you find that you are writing a lot of these wrappers, you can create a template to create them, assuming the function signatures are similar.

+3
source

NB! This is UGLY!

Just missed some proof of concept. Well, he does what he asked, as far as I can see, but this stuff is based on the const_cast assumption. If you decide to use something like this in your program, be prepared to double-check all the copy constructs that occur in your program, and use valgrind to verify that nothing is leaking / damaged.

Trick defines its own wrapper class that ignores const qualifiers and allows the transfer of aut_ptr rights from const auto_ptr. This can go crazy if, for example, you try to copy the vector itself.

Therefore, do not forget to carefully familiarize yourself with the semantics of the vector copy, the semantics of transferring aut_ptr rights and, best of all, just use shared_ptr :)

 #include <iostream> #include <boost/bind.hpp> #include <algorithm> #include <vector> #include <boost/function.hpp> class parameter_data { public: ~parameter_data() { std::cout << "~parameter_data()" << std::endl; } parameter_data() { std::cout << "parameter_data()" << std::endl; } }; void f( parameter_data* data ) { std::cout << "Processing data..." << std::endl; }; class storage_wrapper { private: boost::function<void()> callable; std::auto_ptr<parameter_data> data; public: storage_wrapper( const storage_wrapper& copy ) { callable = const_cast< storage_wrapper&>(copy).callable; data = const_cast< storage_wrapper&>(copy).data; } storage_wrapper( parameter_data *adata ) : data( adata ) { callable = boost::bind( &f, adata ); } storage_wrapper& operator=( const storage_wrapper& copy) { callable = const_cast< storage_wrapper&>(copy).callable; data = const_cast< storage_wrapper&>(copy).data; } void operator()() { callable(); } }; int main() { std::cout << "Start of program" << std::endl; { std::vector<storage_wrapper> container; for ( int i = 0; i < 100; i++ ) container.push_back( storage_wrapper( new parameter_data() ) ); for ( int i = 0; i < 100; i++ ) container[i](); } std::cout << "End of program" << std::endl; return 0; } 
+1
source

This does not have to be very complicated:

 class MyContainer : public std::vector<boost::function<void ()> > { public: void push_back(boost::function<void ()> f, A *pA) { push_back(f); vec.push_back(pA); } ~MyContainer() { int s=vec.size; for(int i=0;i<s;i++) delete vec[i]; } private: std::vector<A*> vec; }; 

It has one problem that you need to pass it to other functions through MyContainer and instead of the std :: vector link, otherwise you can call the original push_back and resolve cases where you can push_back without specifying an A * pointer. Also, it does not check the binding parameters as the same A * object than pA. You can fix this by changing the push_back prototype:

 template<class T> void push_back(T *object, void (T::*fptr)(), A *pA) { push_back(boost::bind(fptr, object, pA)); vec.push_back(pA); } 
+1
source

Source: https://habr.com/ru/post/887820/


All Articles