【问题标题】:Binding boost::asio::steady_timer::async_wait绑定 boost::asio::steady_timer::async_wait
【发布时间】:2015-10-27 15:21:38
【问题描述】:

我想转接这个电话:

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

【问题讨论】:

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


【解决方案1】:

从初始化函数 (async_*) 返回的对象类型取决于提供给初始化函数的处理程序类型。文档用它的void-or-deduced 类型指出了这个变体。

这里尝试使用functionbind会有多个问题:

  • timer.async_wait() 的函数签名必须首先指定将提供的确切处理程序类型,以便推导出返回类型
  • 将处理程序类型更改为std::function&lt;...&gt; 可能会产生意想不到的后果,因为将调用默认的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 模板的特化来自定义返回类型,该模板用于确定返回类型并从处理程序中提取返回值。

例如,Boost.Asio 将boost::asio::async_result 专门用于boost::asio::use_future。当boost::asio::use_future 被提供为初始化函数的处理程序时,返回类型为std::future

【讨论】:

  • 哇,没想到兔子洞会那么深。感谢您的详细解释!
【解决方案2】:

你没有。改用 lambda:

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

bind 一开始并不是最方便使用的东西,但在这里处理一个重载 成员函数时尤其痛苦。也就是说,如果你只是说&amp;steady_timer::async_wait,那么你是模棱两可的,编译器无法确定你指的是哪个重载。

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

【讨论】:

  • 在文档中它只列出了async_wait 的一个签名,所以在我看来它不应该是重载(因为重载意味着多个签名)。是 void-or-deduced 返回类型的魔法让它过载吗?那么在文档中列出多个签名不是更正确吗?
  • 更改处理程序类型可能会产生意想不到的后果,因为将调用默认的 asio_handler_* 挂钩而不是自定义挂钩。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-12-19
  • 1970-01-01
  • 1970-01-01
  • 2013-04-08
  • 1970-01-01
  • 2022-10-21
相关资源
最近更新 更多