绑定boost :: asio :: steady_timer :: async_wait

时间:2015-10-27 15:21:38

标签: c++ c++11 boost boost-asio

我想打开这个电话:

timer.async_wait(handler);

进入这个电话:

func(handler);

所以我尝试使用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;
}

但它不会编译!编译时如下:

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

错误信息是:

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);

我尝试了各种各样的东西(比如转换,模板参数,函数对象),但我无法编译它。

我使用Boost ASIO 1.58和the documentation说明如下:

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

here你可以看到WaitHandler是什么。我不明白void-or-deduced的意思,但我认为这可能是什么给了我一个艰难的时刻?我已经盯着这个问题很长时间了,因为我认为我已经获得了隧道视野。

在这种情况下如何正确使用std::bind

2 个答案:

答案 0 :(得分:3)

从启动函数(async_*)返回的对象类型在提供给启动函数的处理程序类型上是依赖。文档以void-or-deduced类型表示此变体。

尝试在此使用functionbind会遇到多个问题:

  • 如果没有先指定将提供的确切处理程序类型以便可以推断出返回类型,则无法解析timer.async_wait()的函数签名
  • 将处理程序类型更改为std::function<...>可能会产生意想不到的后果,因为将调用默认的asio_handler_*挂钩而不是自定义挂钩。有关处理程序挂钩的更多详细信息,请参阅this答案。

相反,考虑使用自定义仿函数来完成绑定,同时允许处理程序类型正确传递给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);

以下是使用自定义仿函数的完整示例demonstrating

#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();
}

跑步时输出:

in handler

documentation详细说明了如何确定返回类型:

  

默认情况下,启动函数返回void。当处理程序是函数指针,C ++ 11 lambda或由boost::bindstd::bind生成的函数对象时,情况总是如此。

     

对于其他类型,可以通过async_result模板的特化来定制返回类型,该模板既用于确定返回类型,也用于从处理程序中提取返回值。 / p>

例如,Boost.Asio专门针对boost::asio::async_result boost::asio::use_future。当boost::asio::use_future作为iniating函数的处理程序提供时,返回类型为std::future

答案 1 :(得分:1)

你不是。改为使用lambda:

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

bind首先不是最方便的用法,但在处理重载成员函数时,这一点尤其痛苦。也就是说,如果您只是说&steady_timer::async_wait,那么您就会模糊不清,并且编译器无法确定您指的是哪个重载。

您可以通过explicitly casting to the correct type解决此问题,但对于像steady_timer这样的复杂类型来说,这将非常痛苦。 lambda优雅地解决问题,并为编译器提供了额外的优化机会,因为它不像bind创建的类型那样不透明。