[Boost-users] [boost-users][bind, lambda, ...] binding unused actual parameter

19 views
Skip to first unread message

Igor R

unread,
Apr 16, 2009, 7:21:02 AM4/16/09
to boost...@lists.boost.org
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.
_______________________________________________
Boost-users mailing list
Boost...@lists.boost.org
http://lists.boost.org/mailman/listinfo.cgi/boost-users

Roman Perepelitsa

unread,
Apr 16, 2009, 7:37:53 AM4/16/09
to boost...@lists.boost.org
2009/4/16 Igor R <boost...@gmail.com>

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.

Roman Perepelitsa

unread,
Apr 16, 2009, 7:39:55 AM4/16/09
to boost...@lists.boost.org
2009/4/16 Roman Perepelitsa <roman.pe...@gmail.com>

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.

Igor R

unread,
Apr 16, 2009, 8:12:42 AM4/16/09
to boost...@lists.boost.org
Great, thank you!
BTW, is it legal to overload get_pointer inside boost namespace or
it's considered a "dirty patch"?

Roman Perepelitsa

unread,
Apr 16, 2009, 8:31:16 AM4/16/09
to boost...@lists.boost.org
2009/4/16 Igor R <boost...@gmail.com>

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.

Igor R

unread,
Apr 16, 2009, 11:40:11 AM4/16/09
to boost...@lists.boost.org
> 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.

Roman Perepelitsa

unread,
Apr 16, 2009, 12:18:20 PM4/16/09
to boost...@lists.boost.org
2009/4/16 Igor R <boost...@gmail.com>

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 <boost/bind.hpp>

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.

Gottlob Frege

unread,
Apr 16, 2009, 1:12:51 PM4/16/09
to boost...@lists.boost.org
It might be nice if the new code included a comment explaining the reasoning.

On Thursday, April 16, 2009, Roman Perepelitsa


<roman.pe...@gmail.com> wrote:
> 2009/4/16 Igor R <boost...@gmail.com>
>
>
> 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 <boost/bind.hpp>
> 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.
>

Igor R

unread,
Apr 16, 2009, 4:04:49 PM4/16/09
to boost...@lists.boost.org
>      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 :).

Reply all
Reply to author
Forward
0 new messages