Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Binding boost::asio::steady_timer::async_wait

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?


1 Answers

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.

like image 68
Tanner Sansbury Avatar answered Nov 07 '25 06:11

Tanner Sansbury



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!