4

I want to turn this call:

timer.async_wait(handler);

into this call:

func(handler);

So I tried using std::bind:

#include <functional>

#include <boost/asio.hpp>
#include <boost/asio/steady_timer.hpp>
#include <boost/system/error_code.hpp>

using std::bind;
using std::function;

using boost::asio::io_service;
using boost::asio::steady_timer;
using boost::system::error_code;

int main(int, char**) {
  using namespace std::placeholders;

  io_service io_s;
  steady_timer timer (io_s);

  // error                                                                                                                                                                                                                                    
  function<void(function<void(const error_code&)>)> bound =
    bind(&steady_timer::async_wait, timer, _1);

  return 0;
}

But it wont compile! When compiled like this:

g++-4.9 -std=c++11 -Wall -I/usr/local/include -L/usr/local/lib -lboost_system -o bin main.cpp

The error message is this:

main.cpp:22:46: error: no matching function for call to 'bind(<unresolved overloaded function type>, boost::asio::steady_timer&, const std::_Placeholder<1>&)'
 bind(&steady_timer::async_wait, timer, _1);

I've tried various things (like casting, template arguments, function objects) but I just can't get it to compile.

I'm using Boost ASIO 1.58 and the documentation says the following:

template<
    typename WaitHandler>
void-or-deduced async_wait(
    WaitHandler handler);

And here you can see what a WaitHandler is. I don't understand what void-or-deduced mean but I'm thinking it might be what's giving me a tough time? I've stared at this problem for so long now that I think I've gotten tunnel vision.

How do I properly use std::bind in this case?

  • Possible duplicate of [C++ How to Reference Templated Functions using std::bind / std::function](http://stackoverflow.com/questions/14727313/c-how-to-reference-templated-functions-using-stdbind-stdfunction) – m.s. Oct 27 '15 at 15:46

2 Answers2

4

The type of object returned from an initiating function (async_*) is dependent on the handler type provided to the initiating function. The documentation indicates this variant with its void-or-deduced type.

Trying to use function and bind here will have multiple problems:

  • the function signature for timer.async_wait() cannot be resolved without first specifying the exact handler type that will be provided so that the return-type can be deduced
  • changing the handler type to std::function<...> can have the unintended consequences, as the default asio_handler_* hooks will be invoked rather than custom hooks. See this answer for more details about handler hooks.

Instead, consider using a custom functor to accomplish the binding, while allowing handler types to be properly passed through to Asio:

/// @brief Custom functor used to initiate async_wait operations.
template <typename Timer>
class async_wait_functor
{
public:
  async_wait_functor(Timer& timer): timer_(timer) {}

  template <typename WaitHandler>
  auto operator()(WaitHandler handler)
  {
    return timer_.async_wait(handler);
  }

private:
  Timer& timer_;
};

/// @brief Auxiliary factory function for binding a timer.
template <typename Timer>
async_wait_functor<Timer> bind_async_wait(Timer& timer)
{
  return async_wait_functor<Timer>(timer);
}

...

auto func = bind_async_wait(timer);
func(handler);

Here is a complete example demonstrating using a custom functor:

#include <iostream>
#include <functional>    
#include <boost/asio.hpp>
#include <boost/asio/steady_timer.hpp>

/// @brief Custom functor used to initiate async_wait operations.
template <typename Timer>
class async_wait_functor
{
public:
  async_wait_functor(Timer& timer): timer_(timer) {}

  template <typename WaitHandler>
  auto operator()(WaitHandler handler)
  {
    return timer_.async_wait(handler);
  }

private:
  Timer& timer_;
};

/// @brief Auxiliary factory function for binding a timer.
template <typename Timer>
async_wait_functor<Timer> bind_async_wait(Timer& timer)
{
  return async_wait_functor<Timer>(timer);
}

int main()
{
  boost::asio::io_service io_service;
  boost::asio::steady_timer timer(io_service);
  auto handler = [](const boost::system::error_code&) {
    std::cout << "in handler" << std::endl;
  };

  auto func = bind_async_wait(timer);
  func(handler);

  timer.cancel();
  io_service.run();
}

When ran, it outputs:

in handler

The documentation details how the return type is determined:

By default, initiating functions return void. This is always the case when the handler is a function pointer, C++11 lambda, or a function object produced by boost::bind or std::bind.

For other types, the return type may be customised via [...] a specialisation of the async_result template, which is used both to determine the return type and to extract the return value from the handler.

For example, Boost.Asio specializes boost::asio::async_result for boost::asio::use_future. When boost::asio::use_future is provided as the handler to an iniating function, the return type is std::future.

Community
  • 1
  • 1
Tanner Sansbury
  • 51,153
  • 9
  • 112
  • 169
1

You don't. Use a lambda instead:

auto func = [&timer](const function<void(const error_code&)>& cb) {
    timer.async_wait(cb);
};
func(handler);

bind is not the most convenient thing to use in the first place, but it is especially painful here as you are dealing with an overloaded member function. That is, if you just say &steady_timer::async_wait, you are being ambiguous and the compiler cannot determine which of the overloads you are referring to.

You can fix this by explicitly casting to the correct type, but that will be quite a pain for a complex type like steady_timer. The lambda elegantly works around the problem and also offers the compiler additional opportunities for optimizations, as it's less opaque than the type created by bind.

Community
  • 1
  • 1
ComicSansMS
  • 51,484
  • 14
  • 155
  • 166
  • In the documentation it only lists one signature for `async_wait` so in my head it _shouldn't_ be overload (since overloading implies multiple signatures). Is it the `void-or-deduced` return type magic that makes it overloaded? Wouldn't it be more correct then to list multiple signatures in the documentation? –  Oct 27 '15 at 16:27
  • 1
    Changing the handler type can have unintended consequences, as the default `asio_handler_*` hooks will be invoked rather than custom hooks. – Tanner Sansbury Oct 27 '15 at 16:29