CodeGuru Home VC++ / MFC / C++ .NET / C# Visual Basic VB Forums Developer.com
Results 1 to 12 of 12
  1. #1
    Join Date
    Sep 2010
    Posts
    5

    some problem with boost::bind.

    The sample code is like this:

    #include <boost/bind.hpp>
    #include <boost/system/error_code.hpp>
    #include <string>

    template<typename Handler1, typename Handler2>
    void two_handler_test_func(const char* p1, const char* p2, Handler1 handler1, Handler2 handler2)
    {
    handler1(boost::system::error_code());
    handler2(boost::system::error_code());
    }

    class MyTestClass
    {
    public:
    template<typename Handler>
    void test_func(const std::string& p1, const std::string& p2, Handler handler)
    {
    two_handler_test_func(p1.c_str(), p2.c_str(),
    boost::bind(&MyTestClass::handle_test_func1<Handler>, this, _1, handler),
    boost::bind(&MyTestClass::handle_test_func2, this, _1, 2));
    }

    private:
    template<typename Handler>
    void handle_test_func1(const boost::system::error_code& ec, Handler handler)
    {
    handler(ec);
    }

    void handle_test_func2(const boost::system::error_code& ec, int b)
    {}

    };

    void command_listen_finished(const boost::system::error_code& ec, int i)
    {}

    void command_listen_finished2(const boost::system::error_code& ec)
    {}

    class TestCallback
    {
    public:
    TestCallback(int i)
    :m_i(i)
    {}

    void operator()(const boost::system::error_code& ec)
    {}
    private:
    int m_i;
    };

    int main()
    {
    MyTestClass testClass;
    testClass.test_func("abc", "def", boost::bind(command_listen_finished, _1, 1));
    //testClass.test_func("abc", "def", command_listen_finished2);
    //testClass.test_func("abc", "def", TestCallback(1));
    return 0;
    }



    The last three "testClass.test_func" call.
    The first one can not pass the compile, but the second and thrid can.
    I thought the first one bind is build a same functor as the third one, but it just can not pass compile.
    I use boost 1.4.3

    The compile I use is VC9, the detail version is below:
    Microsoft Visual Studio 2008
    Version 9.0.30729.1 SP
    Microsoft .NET Framework
    Version 3.5 SP1
    Installed Edition: Enterprise
    Microsoft Visual C++ 2008 91899-153-0000007-60183

    Thanks for help...

  2. #2
    Lindley is offline Elite Member Power Poster
    Join Date
    Oct 2007
    Location
    Seattle, WA
    Posts
    10,895

    Re: some problem with boost::bind.

    What is the error message?

  3. #3
    Join Date
    Apr 2004
    Location
    Canada
    Posts
    1,342

    Re: some problem with boost::bind.

    The problem is that in test_func(), "handler", which is being used in a bind-expression, can itself be a bind-expression (and indeed, it is in the first test_func() call, but not in the second and third).

    Boost.Bind treats nested bind-expressions specially to facilitate function composition.

    Let's simplify the example a bit and see what's happening:

    Code:
    #include <boost/bind.hpp>
    using boost::bind;
    
    template<typename Handler>
    void invoker(Handler handler)
    {
        handler();
    }
    
    template<typename Handler>
    void f(Handler handler)
    {
        handler();
    }
    
    template<typename Handler>
    void test_func(Handler handler)
    {
        invoker(bind(&f<Handler>, handler));
    }
    
    void g(int i)
    {
    }
    
    int main()
    {
        test_func(bind(g, 1));
        return 0;
    }
    I've changed a few names for brevity: invoker is your two_handler_test_func, f is your handle_test_func1 template, and g is your command_listen_finished. I've also removed all the extra parameters so we can focus on the issue.

    Normally, if f is a unary function, bind(f, handler) is a nullary function such that bind(f, handler)() is equivalent to f(handler).

    Likewise, if g is a unary function, bind(g, 1) is a nullary function such that bind(g, 1)() is equivalent to g(1).

    We would thus expect that bind(f, bind(g, 1)) is a nullary function such that bind(f, bind(g, 1))() is equivalent f(bind(g, 1)). If inside f we call handler(), which we do, we would thus expect this to be equivalent to calling bind(g, 1)(), i.e. g(1).

    However, boost treats nested bind-expressions specially and actually evaluates nested bind-expressions - so rather than bind(f, bind(g, 1))() being equivalent to f(bind(g, 1)), it is equivalent to f(bind(g, 1)())!

    This makes all the difference in the world, since bind(g, 1) was a function, but bind(g, 1)() is the result of that function, in this case void. While it made perfect sense to invoke bind(g, 1) - a function - in invoker(), it makes no sense at all to invoke bind(g, 1)() - a void expression. Hence the compiler error.

    Now there is a good reason why Boost.Bind behaves the way it does. f(bind(g, 1)()) is equivalent to f(g(1)), so this special treatment of nested bind-expressions allows us to create a binding that acts as a function composition, something that would be impossible otherwise (try it!). However, in some cases, such as yours, you don't want this behaviour - you don't want Boost.Bind to evaluate the nested bind expression. The solution is "guard" the inner bind expression by wrapping it in a call to boost:: protect(). This tells boost that the inner bind expression should be evaluated, and we get the desired behaviour (you can read all the details here.):

    Code:
    #include <boost/bind/protect.hpp>
    template<typename Handler>
    void test_func(Handler handler)
    {
        invoker(bind(&f<Handler>, boost::protect(handler)));
    }
    There is however one blemish in this solution. This code still does not compile, because the type of boost:: protect(handler) is different from the type of handler (which is Handler), so there is a mismatch between the type you're instantiating f with (Handler), and the type you are passing to it (boost:: protect(handler)).

    One solution is to move the boost:: protect() call to test_func's call site:

    Code:
    test_func(boost::protect(bind(g, 1)));
    Now the Handler template parameter in test_func() is deduced to be the type of boost:: protect(bind(g, 1)) and all is well.

    However, this solution doesn't feel right. The fact that test_func() will use the bind-expression we pass to it inside another bind-expression is an implementation detail of test_func(). The caller of test_func() should not have to know to wrap every bind-expression being passed to test_func() in boost:: protect() - this should be test_func()'s responsibility.

    So if we want to keep the boost:: protect call in test_func(), we have two options. One is to write a helper function to deduce the type of boost:: protect(handler), like this:

    Code:
    #include <boost/bind/protect.hpp>
    template <typename Handler>
    void test_func_helper(Handler handler)
    {
        invoker(bind(&f<Handler>, handler));
    }
    template<typename Handler>
    void test_func(Handler handler)
    {
        test_func_helper(boost::protect(handler));
    }
    The other option is to use BOOST_TYPEOF (or, if you have access to it, the proper C++0x equivalent decltype) to deduce the type of boost:: protect(handler) in test_func:

    Code:
    #include <boost/bind/protect.hpp>
    #include <boost/typeof/typeof.hpp>
    template<typename Handler>
    void test_func(Handler handler)
    {
        invoker(bind(&f<BOOST_TYPEOF(boost::protect(handler))>, boost::protect(handler)));
    }
    Last edited by HighCommander4; September 5th, 2010 at 04:56 PM.
    Old Unix programmers never die, they just mv to /dev/null

  4. #4
    Join Date
    Sep 2010
    Posts
    5

    Thumbs up Re: some problem with boost::bind.

    This is my first time to hear boost:: protect.
    I am really leak of careful to read the boost::bind document.
    Thank you very much help me to deal with this issue.

  5. #5
    Join Date
    Sep 2010
    Posts
    5

    Re: some problem with boost::bind.

    But it seems that the boost:rotect can not work with noraml functor.
    So if it write in test_func, test_func can not work with normal functor any more.

  6. #6
    Lindley is offline Elite Member Power Poster
    Join Date
    Oct 2007
    Location
    Seattle, WA
    Posts
    10,895

    Re: some problem with boost::bind.

    Looking at this protect implementation:
    http://www.boost.org/doc/libs/1_43_0...nd/protect.hpp

    I don't see any obvious reason why it wouldn't work with an arbitrary functor. It appears to be a simple wrapper.

    If there really is trouble, you could write your own functor wrapper; it would work the same way.

  7. #7
    Join Date
    Apr 2004
    Location
    Canada
    Posts
    1,342

    Re: some problem with boost::bind.

    Quote Originally Posted by Lindley View Post
    Looking at this protect implementation:
    http://www.boost.org/doc/libs/1_43_0...nd/protect.hpp

    I don't see any obvious reason why it wouldn't work with an arbitrary functor. It appears to be a simple wrapper.

    If there really is trouble, you could write your own functor wrapper; it would work the same way.
    If you look at the first typedef in protect.hpp, it requires the F template parameter to have nested "result_type". For this reason an arbitrary function will not work, nor will an arbitrary functor unless it's derived from std::unary_function, std::binary_function etc.

    I can't think of any workaround right now except calling boost:: protect() when you call test_func(), not inside test_func(), but this still feels wrong to me...
    Old Unix programmers never die, they just mv to /dev/null

  8. #8
    Join Date
    Sep 2010
    Posts
    5

    Re: some problem with boost::bind.

    Well, I use boost:rotect to wrap every boost::bind where I want to pass a callback handler.
    For a rule I think it could be safety, just a bit grammer trouble.

  9. #9
    Lindley is offline Elite Member Power Poster
    Join Date
    Oct 2007
    Location
    Seattle, WA
    Posts
    10,895

    Re: some problem with boost::bind.

    Quote Originally Posted by HighCommander4 View Post
    If you look at the first typedef in protect.hpp, it requires the F template parameter to have nested "result_type". For this reason an arbitrary function will not work, nor will an arbitrary functor unless it's derived from std::unary_function, std::binary_function etc.

    I can't think of any workaround right now except calling boost:: protect() when you call test_func(), not inside test_func(), but this still feels wrong to me...
    I suppose you could just require functors to be derived from unary_function......that's totally reasonable as a requirement.

    However, there's a catch: C++0x lambda functions won't have result_type defined. On the other hand, if you have C++0x support, it might be possible to leverage decltype to work out the result using a wrapper class.

  10. #10
    Join Date
    Apr 2004
    Location
    Canada
    Posts
    1,342

    Re: some problem with boost::bind.

    Quote Originally Posted by Lindley View Post
    I suppose you could just require functors to be derived from unary_function......that's totally reasonable as a requirement.
    Functors, yes. But it is nice to be able to pass plain functions as callbacks as well.

    However, there's a catch: C++0x lambda functions won't have result_type defined. On the other hand, if you have C++0x support, it might be possible to leverage decltype to work out the result using a wrapper class.
    Well, if you have C++0x lambdas, you probably don't need to use bind() at all...
    Old Unix programmers never die, they just mv to /dev/null

  11. #11
    Lindley is offline Elite Member Power Poster
    Join Date
    Oct 2007
    Location
    Seattle, WA
    Posts
    10,895

    Re: some problem with boost::bind.

    Quote Originally Posted by HighCommander4 View Post
    Functors, yes. But it is nice to be able to pass plain functions as callbacks as well.
    Well, that's what std:tr_fun is for.

  12. #12
    Join Date
    Apr 2004
    Location
    Canada
    Posts
    1,342

    Re: some problem with boost::bind.

    Quote Originally Posted by Lindley View Post
    Well, that's what std:: ptr_fun is for.
    Interesting, I didn't know about std:: ptr_fun.

    However, since it only transforms pointers to functions into functors (as opposed to anything function-like into a functor), it must still be used at the call site.

    I just realized, though, that boost::bind itself can be used as something that transforms anything function-like into a functor. For example, if you know that f is a callback function (whether a plain function, functor, bind-expression, etc.) with 2 parameters, then boost::bind(f, _1, _2) is an equivalent functor.

    Using this technique, you can move all the wrappers to where the callback function is used inside a bind-expression:

    Code:
    template <typename Callback>
    void function_that_uses_callback(Callback callback)
    {
        ...
        boost::bind(..., boost::protect(boost::bind(callback, _1, _2)));
        ...
    }
    and at the call site you are now free to pass in anything you like, including plain funcions without having to wrap them in anything: boost::bind transforms them into a functor, and boost:: protect ensures they are not evaluated.
    Last edited by HighCommander4; September 7th, 2010 at 11:54 AM.
    Old Unix programmers never die, they just mv to /dev/null

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  





Click Here to Expand Forum to Full Width

Featured