2

Short version of my question:

When I tried to boost::bind io_service::post like this:

boost::bind(&boost::asio_io_service::post, &ios,
        boost::bind(&MyClass::func, this, arg1, arg2));

I get errors like this:

error: no matching function for call to ‘bind(<unresolved overloaded function type>,
boost::asio::io_service*, boost::_bi::bind_t<void, boost::_mfi::mf2<void, MyClass,
const char*, const char*>, boost::_bi::list3<boost::_bi::value<MyClass*>,
boost::_bi::value<const char*>, boost::_bi::value<const char*> > >)’

How can I fix this?

Very simple test code: http://pastebin.com/V0uyLywC

Long version of my question:

I'm trying to write a generic event queue class, which you can add different types of events to the queue. You can subscribe to events by types, when that type of events is added, the subscribed callback function will be called.

An event queue object can potentially be subscribed by a different thread group from a different io_services. The internal queue of this class will be thread-safe using boost::lockfree:queue or boost::interprocess::message_queue (if this will be inter-process in the future). And the subscribed callback function will need to be called by its corresponding io_service's post, hence the attempted nested boost::bind above.

Is this a good and workable design?

Assuming this approach will work, I figured then an alternative would be also passing the io_service when you subscribe, but I was thinking 1) perhaps this class can be used when it does not involve io_services, and 2) this class shouldn't need to know about io_service.

Thanks all.

P.S. I have read boost::bind composition inside io_service::post function but it feels a bit different to my problem.

Community
  • 1
  • 1
TDL
  • 103
  • 6
  • What's the signature of `MyClass::func`? Try `boost::bind(&boost::asio_io_service::post, &ios, boost::protect(boost::bind(&MyClass::func, this, arg1, arg2)));` – Igor R. Jun 11 '14 at 12:08
  • As a test, it was `MyClass::func(const char *arg1, const char *arg2);` I got similar `error: no matching function for call to ‘bind(, boost::asio::io_service*, boost::_bi::protected_bind_t, boost::_bi::list3, boost::_bi::value, boost::_bi::value > > >)’` – TDL Jun 11 '14 at 12:21
  • It might work by seperating the 2 statements: `boost::function f = boost::bind(&MyClass::func, this, arg1, arg2);` `boost::bind(&boost::asio::io_service::post, &ios, f);` – Matthias247 Jun 11 '14 at 12:23
  • Error becomes: `no matching function for call to ‘bind(, boost::asio::io_service*, boost::function&)’` Should I be worried with the ``? Maybe it's something about io_service::post()? – TDL Jun 11 '14 at 12:28
  • Added a very simple test code: http://pastebin.com/V0uyLywC Also changed the arg1 and arg2 to just ints. – TDL Jun 11 '14 at 12:38
  • If I change the ::post into a ::run, it builds. So it is only related to the post? – TDL Jun 11 '14 at 12:53
  • `` means `&boost::asio_io_service::post` does not uniquely identify a function, as that's an overloaded function. You need to tell the compiler which overload you want, usually with a cast, e.g. http://stackoverflow.com/a/15240314/981959 and http://stackoverflow.com/a/4364610/981959 – Jonathan Wakely Jun 11 '14 at 14:28
  • just out of interest, do you not have access to c++11? (and lambdas?) – Nim Jun 12 '14 at 07:48

2 Answers2

2

Update All solutions Live On Coliru

The problem

The first problem is that post has overloads, so you need to disambiguate. That's pretty ugly:

boost::bind(
    static_cast<void (boost::asio::io_service::*)
        (
            boost::_bi::protected_bind_t<
                boost::_bi::bind_t<
                    void, 
                    boost::_mfi::mf2<void, MyClass, int, int>,
                    boost::_bi::list3<boost::_bi::value<MyClass *>,
                    boost::_bi::value<int>,
                    boost::_bi::value<int>
                >
            > > const&
        )>(&boost::asio::io_service::post), 
    &ios_, 
    boost::protect(boost::bind(&MyClass::func, this, 7, 42)));

Of course, you could try to use decltype and some typedefs:

auto nested = boost::protect(boost::bind(&MyClass::func, this, 7, 42));
typedef decltype(nested) actual_t; // _bi::protected_bind_t<bind_t<void, mf2<void, MyClass, int, int>, list3<value<MyClass *>, value<int>, value<int> > > >
typedef void (boost::asio::io_service::*pmf)(actual_t const&);
boost::bind(static_cast<pmf>(&boost::asio::io_service::post), &ios_, nested);

But that would more or less refute the purpose of inline bind expressions in the first place.


Solution

Or you can use a helper functor, that hides the overload set behind a suitable polymorphic operator():

boost::bind(poster(ios_),
            boost::protect(boost::bind(&MyClass::func, this, 7, 42)));

Where the poster helper looks like

struct poster {
    typedef void result_type;

    poster(boost::asio::io_service& ios) : ios_(ios) {}
    boost::asio::io_service& ios_;

    template<typename F> void operator()(F const& f) const {
        ios_.post(f);
    }

    template<typename F> void operator()(F&& f) const {
        ios_.post(std::move(f));
    }
};
sehe
  • 374,641
  • 47
  • 450
  • 633
  • oh, in case you wondered, the "second problem" was nested binds, but you already correctly applied `protect` to the inner bind expression :) – sehe Jun 11 '14 at 15:46
  • It turns out that `io_service::post()` is a template, so `&boost::asio::io_service::post` should not compile at all. Does it? – Igor R. Jun 11 '14 at 18:01
  • @IgorR No it doesn't, indeed. (I didn't deem it interesting to make the distinction, since logically funtion templates simply generate an overload set. And of course my awful demonstruction of the relevant cast clearly takes the address of the explicit template instantiation for the reason you stated). – sehe Jun 11 '14 at 18:02
  • So, `static_cast(&io_service::post)` is equivalent to `&io_service::post`? I'm wondering what the standard states about it... – Igor R. Jun 11 '14 at 18:10
  • Ah. That would have been simpler. No it would have been equivalent to `&io_service::post` (likely what you meant). Pretty sure the standard specifies it, or different compilers would likely differ in opinion. Also, `pmf x = &ioservice::post;` works. – sehe Jun 11 '14 at 18:12
0

Surely what you are attempting to do is as simple as:

std::function<void()> f = [this, arg1, arg2]() {
  ios_.post([this, arg1, arg2]() {
    this->func(arg1, arg2);
  });
};

now your thread can call f(), which will do the right thing...

Or am I missing something...

Nim
  • 33,299
  • 2
  • 62
  • 101