[boost-users][bind, lambda, ...] binding unused actual parameter
Hello, Is it possible somehow to make a binder that stores "unexpected" argument? Motivation: I have to create & pass a callback handler from within intusively ref-counted object (MS-ATL) to an outer asynchronous subsystem. If the object were shared-ptr enabled, I'd do this as usually: ThisObj::handler() { //... } ThisObj::doSomething() { asyncSubsystem_->doSomething(&ThisObj::handler, shared_from_this()); } However, in my case I have to do something like this: asyncSubsystem_->doSomething(&ThisObj::handler, this, mySmartPtr); // store someOtherSmartPtr in the functor, just to ensure "this" won't die before the handler is invoked Is it possible with bind, lambda or some other library? Thanks.
2009/4/16 Igor R
Hello,
Is it possible somehow to make a binder that stores "unexpected" argument?
Motivation: I have to create & pass a callback handler from within intusively ref-counted object (MS-ATL) to an outer asynchronous subsystem. If the object were shared-ptr enabled, I'd do this as usually:
ThisObj::handler() { //... } ThisObj::doSomething() { asyncSubsystem_->doSomething(&ThisObj::handler, shared_from_this()); }
However, in my case I have to do something like this:
asyncSubsystem_->doSomething(&ThisObj::handler, this, mySmartPtr); // store someOtherSmartPtr in the functor, just to ensure "this" won't die before the handler is invoked
Is it possible with bind, lambda or some other library?
Thanks.
You can overload boost::get_pointer for your smart pointer. namespace boost { template <class T> T * get_pointer(CComPtr<T> const& p) { return p; } } CComPtr<ThisObj> self(this); asyncSubsystem_->doSomething(bind(&ThisObj::handler, self)); If you don't want or can't overload get_pointer, you can use double bind trick. CComPtr<ThisObj> self(this); asyncSubsystem_->doSomething(bind(bind(&ThisObj::handler, this), self)); First bind creates a function object that accepts any number of arguments, and second bind creates a function object that stores smart pointer inside of it. Roman Perepelitsa.
2009/4/16 Roman Perepelitsa
If you don't want or can't overload get_pointer, you can use double bind trick.
CComPtr<ThisObj> self(this); asyncSubsystem_->doSomething(bind(bind(&ThisObj::handler, this), self));
Sorry, you'll also need a protect. asyncSubsystem_->doSomething(bind(protect(bind(&ThisObj::handler, this)), self)); Roman Perepelitsa.
Great, thank you! BTW, is it legal to overload get_pointer inside boost namespace or it's considered a "dirty patch"?
2009/4/16 Igor R
Great, thank you! BTW, is it legal to overload get_pointer inside boost namespace or it's considered a "dirty patch"?
Implementation of mem_fn makes unqualified calls to get_pointer, hence you can define get_pointer in the namespace of your smart pointer (function will be found by ADL). I think it's also OK to overload boost::get_pointer, because it's a deliberate point of extension. Roman Perepelitsa.
Implementation of mem_fn makes unqualified calls to get_pointer, hence you can define get_pointer in the namespace of your smart pointer (function will be found by ADL). I think it's also OK to overload boost::get_pointer, because it's a deliberate point of extension.
Well, it turns out that defining get_pointer() for ATL::CComPtr won't help, because CComPtrBase defines its own operator&(), doing this in a pretty weird way (quote from atlcomcli.h): //The assert on operator& usually indicates a bug. If this is really //what is needed, however, take the address of the p member explicitly. T** operator&() throw() { ATLASSERT(p==NULL); return &p; } So the "double bind" is the only short way to do this.
2009/4/16 Igor R
Well, it turns out that defining get_pointer() for ATL::CComPtr won't help, because CComPtrBase defines its own operator&(), doing this in a pretty weird way (quote from atlcomcli.h):
//The assert on operator& usually indicates a bug. If this is really //what is needed, however, take the address of the p member explicitly. T** operator&() throw() { ATLASSERT(p==NULL); return &p; }
So the "double bind" is the only short way to do this.
Ah, I see why it happens. To distinguish between regular objects and smart
pointers, mem_fn does the following trick:
void call(const T*) { /* it's a regular object */ }
void call(const void*) { /* it might be a smart pointer */ }
void test(U& t) { call(&t); }
If U is CComPtr<T>, taking its address triggers an assert. It can be easily
fixed though.
void test(U& t) { call(false ? &t : 0); }
Now type of the expression &t is used for choosing the right overload of
call, but operator& is not applied in runtime.
Here's the patch against trunk:
--- mem_fn_template.hpp (revision 52422)
+++ mem_fn_template.hpp (working copy)
@@ -51,14 +51,14 @@
template<class U> R operator()(U & u) const
{
- BOOST_MEM_FN_RETURN call(u, &u);
+ BOOST_MEM_FN_RETURN call(u, false ? &u : 0);
}
#ifdef BOOST_MEM_FN_ENABLE_CONST_OVERLOADS
template<class U> R operator()(U const & u) const
{
- BOOST_MEM_FN_RETURN call(u, &u);
+ BOOST_MEM_FN_RETURN call(u, false ? &u : 0);
}
#endif
And here is the test:
#include <cstdlib>
#include
It might be nice if the new code included a comment explaining the reasoning.
On Thursday, April 16, 2009, Roman Perepelitsa
2009/4/16 Igor R
Well, it turns out that defining get_pointer() for ATL::CComPtr won't help, because CComPtrBase defines its own operator&(), doing this in a pretty weird way (quote from atlcomcli.h):
//The assert on operator& usually indicates a bug. If this is really //what is needed, however, take the address of the p member explicitly. T** operator&() throw() { ATLASSERT(p==NULL); return &p; }
So the "double bind" is the only short way to do this.
Ah, I see why it happens. To distinguish between regular objects and smart pointers, mem_fn does the following trick: void call(const T*) { /* it's a regular object */ } void call(const void*) { /* it might be a smart pointer */ } void test(U& t) { call(&t); }
If U is CComPtr<T>, taking its address triggers an assert. It can be easily fixed though. void test(U& t) { call(false ? &t : 0); }
Now type of the expression &t is used for choosing the right overload of call, but operator& is not applied in runtime.
Here's the patch against trunk: --- mem_fn_template.hpp (revision 52422)+++ mem_fn_template.hpp (working copy) @@ -51,14 +51,14 @@ template<class U> R operator()(U & u) const {- BOOST_MEM_FN_RETURN call(u, &u); + BOOST_MEM_FN_RETURN call(u, false ? &u : 0); } #ifdef BOOST_MEM_FN_ENABLE_CONST_OVERLOADS template<class U> R operator()(U const & u) const { - BOOST_MEM_FN_RETURN call(u, &u);+ BOOST_MEM_FN_RETURN call(u, false ? &u : 0); } #endif
And here is the test: #include <cstdlib>#include
struct foo{ void bar() {}} f; struct ptr{ void* operator&() { std::abort(); }}; foo* get_pointer(const ptr& p) { return &f; } int main() { boost::bind(&foo::bar, ptr())(); } Roman Perepelitsa.
template<class U> R operator()(U & u) const { - BOOST_MEM_FN_RETURN call(u, &u); + BOOST_MEM_FN_RETURN call(u, false ? &u : 0); }
#ifdef BOOST_MEM_FN_ENABLE_CONST_OVERLOADS
template<class U> R operator()(U const & u) const { - BOOST_MEM_FN_RETURN call(u, &u); + BOOST_MEM_FN_RETURN call(u, false ? &u : 0); }
#endif
Wow, very nice idea! But if you intend to commit this patch to the trunk, please note that there're 25 more places in this file where the above change should be done :).
participants (3)
-
Gottlob Frege
-
Igor R
-
Roman Perepelitsa