【问题标题】:param of asio timer async_wait difference lambda, bind, function pointerasio 定时器的参数 async_wait 差异 lambda、bind、函数指针
【发布时间】:2019-02-23 09:11:38
【问题描述】:

当我使用boost::asio::steady_timer 时,我发现lambdabindfunction pointer 之间有些不同。

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

void print() { std::cout << "Hello, world!" << std::endl; }

int main()
{
    boost::asio::io_context io;

    boost::asio::steady_timer t(io, boost::asio::chrono::seconds(5));
    t.async_wait(&print); // Error
    t.async_wait([]{print();}) // Error
    t.async_wait(std::bind(print)); // Done

    io.run();

    return 0;
}

我阅读了 asio 手册,async_wait 处理程序需要 const boost::system::error_code&amp; error 参数。因此,如果我将print 更改为void print(const boost::system::error_code &amp; /*e*/),则一切正常。但是timer4/timer.cc && timeouts/server.cc 的 asio 示例使用了通过绑定创建的处理程序,而没有 void print(const boost::system::error_code &amp; /*e*/)。当我更改为 lambda 时,编译错误。那么,bind && lambda 之间的签名有何不同。

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

class printer
{
public:
    printer(boost::asio::io_context &io)
        : timer_(io, boost::asio::chrono::seconds(1)), count_(0)
    {
        timer_.async_wait(std::bind(&printer::print, this));
    }

    ~printer() { std::cout << "Final count is " << count_ << std::endl; }

    void print()
    {
        if (count_ < 5) {
            std::cout << count_ << std::endl;
            ++count_;

            timer_.expires_at(timer_.expiry() +
                      boost::asio::chrono::seconds(1));
            timer_.async_wait(boost::bind(&printer::print, this));
            // timer_.async_wait([this]{print();}); Error
        }
    }

private:
    boost::asio::steady_timer timer_;
    int count_;
};

int main()
{
    boost::asio::io_context io;
    printer p(io);
    io.run();

    return 0;
}

【问题讨论】:

  • 我已经找到答案了,bind会忽略过多的参数。比如void print() { cout &lt;&lt; 1 &lt;&lt; endl; } int main() { auto f = bind(print); f(2); }是对的。

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


【解决方案1】:

std::bind 生成的“部分”检测并忽略调用点提供的参数,并且未明确连接到绑定代码。

一个简约的例子 (godbolted):

#include <functional>
#include <iostream>

void callme(std::function<void(int, float)> arg) {
    arg(42, 4.2);
}

// or like this
// template <typename F> void callme(F&& arg) {
//     arg(42, 4.2);
// }

int main()
{
    auto fn = std::bind([](){std::cout << "hi there" << std::endl; });
    // auto fn = std::bind([](auto&& x){std::cout << "x=" << x << std::endl; }, std::placeholders::_1);  <-- this works too and prints 42
    // auto fn = std::bind([](auto&& x){std::cout << "x=" << x << std::endl; }, std::placeholders::_2);  <-- and works too and prints 4.2

    callme(fn);
    return 0;
}

【讨论】:

  • 我看了asio代码,发现BOOST_ASIO_WAIT_HANDLER_CHECK(WaitHandler, handler) type_check;。这不起作用,如何检查是否绑定匹配所有参数并且什么都不忽略。
  • 查看你的代码后,我有一个问题,function &lt;void()&gt; 可以转换为function &lt;void (int, float)&gt; 没有任何编译器错误。
  • @bobah,我今天阅读了 llvm-5 仿函数实现代码。我发现函数 ctor template &lt;class _Rp, class... _ArgTypes&gt; template &lt;class _Fp, class&gt; function&lt;_Rp(_ArgTypes...)&gt;::function(_Fp __f) 使用可调用的 static const bool value = is_same&lt;void, _Rp&gt;::value || is_convertible&lt;typename __invoke_of&lt;_Fp&amp;, _ArgTypes...&gt;::type, _Rp&gt;::value; 进行保护。
  • @bobah,在 llvm 中,__invoke_ofinvoke_result 的基类。通过 clang,std::bind([](){std::cout &lt;&lt; "hi there" &lt;&lt; std::endl; }); 可以用 (int, float) 调用,因为 bind 忽略了过多的参数。所以std::bind([](){std::cout &lt;&lt; "hi there" &lt;&lt; std::endl; }); 可以转换为std::function&lt;void(int, float)&gt;。它只检查 arg,而不是 return_result。所以function&lt;void(int)&gt; a = bind(function&lt;string()&gt;()); 是对的。但是运行导致std::__1::bad_function_call: std::exception。这是个问题。我们需要自己强烈检查这段代码。
  • @bobah @sehe,template &lt;class _Fp, class... _BoundArgs&gt; class bind 使用 typedef typename __make_tuple_indices&lt;sizeof...(_BoundArgs)&gt;::type __indices;。当调用operator(),最终调用return _VSTD::__apply_functor(__f_, __bound_args_, __indices(), tuple&lt;_Args &amp;&amp;...&gt;(_VSTD::forward&lt;_Args&gt;(__args)...));。因为sizeof...(_BoundArgs) == 0,所以apply_functor没有展开_args tuple,所有参数都被忽略了。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2022-07-25
  • 1970-01-01
  • 1970-01-01
  • 2013-10-06
  • 1970-01-01
  • 1970-01-01
  • 2016-09-19
相关资源
最近更新 更多