asio_handler_invoke const重载

时间:2017-02-11 11:00:03

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

给定以下函数实现异步future.wait()(完整示例here):

template <typename Token, typename T>
typename boost::asio::async_result<
    typename boost::asio::handler_type<Token, void()>::type
>::type
async_wait(boost::asio::io_service& ios, std::future<T>& future, Token&& token)
{
    using handler_type = typename boost::asio::handler_type<Token, void()>::type;
    handler_type handler(std::forward<Token>(token));
    boost::asio::async_result<handler_type> result(handler);

    std::async([&ios, &future, handler]() {
        future.wait();
        ios.dispatch([handler]() {
            using boost::asio::asio_handler_invoke;
            asio_handler_invoke(handler, const_cast<handler_type*>(&handler));
        });
    });

    return result.get();
}

仅当第二个参数为非const时才调用正确的asio_handler_invoke。如果handler是lambda捕获,则它是const并且调用默认的asio_handler_invoke。因此,lambda必须是可变的,或者需要const_cast才能使其正常工作。

这一切看起来都很丑陋,更重要的是我也非常脆弱。所以现在我的问题是:

  • 有更好(即更安全)的方式来实现这个吗?
  • 这是asio中的实际设计缺陷吗const和朋友应该yield_context重载,至少会给出错误消息,而不是只调用默认的asio_handler_invoke?或者是否存在这些不存在的正当理由?
  • 或者甚至更好,是否只有针对默认asio_handler_invoke的const重载会产生错误消息?

1 个答案:

答案 0 :(得分:0)

非常有趣的问题。

首先,前提看起来很奇怪。 asio_handler_invoke用于在与某些给定处理程序相同的执行上下文中调用另一个处理程序时(您无法知道有关的详细信息,因为它可能被包装)在某些(用户定义的)类型中。

所以我认为

F f;
using boost::asio::asio_handler_invoke;
asio_handler_invoke(f, &f);

在概念上不添加任何内容,并可能降低性能(例如,通过两次包装)。

它也很容易导致不良后果。想象一下F是Tanner的样本计数包装器的包装器,它只会导致重复计算调用。

分析问题

所以剩下的问题是处理程序调用是否为内置处理程序包装类型做了正确的事情,让我们使用Tanner的计数包装器,实际上我们看到了

{
    operation_counter oc;

    auto handler = oc.wrap([](boost::system::error_code ec) {
            std::cout << "Inner (" << ec.message() << ")\n";
        });

    asio_handler_invoke([]() {
            std::cout << "Outer\n";
        }, &handler);

    std::cout << "mutable: " << oc.count() << "\n";
}

打印

Outer
mutable: 1

但改为auto const handler打印:

Outer
const: 0

查看两种情况 Live On Coliru

确实,在

的数量上添加了一个重载
  template <typename Function>
      friend void asio_handler_invoke(Function function, counting_handler const *context);

消除差异:

<强> Live On Coliru

所以奇怪的不是重载不适用于const处理程序上下文,而是因为遗漏会回退到默认行为静默且没有警告

事实上,我建议在图书馆开发人员报告这个问题,因为我可以看到boost::asio::strand之类的内容会出现明显错误,这会导致可预测的问题。