【问题标题】:asio_handler_invoke const overloadsasio_handler_invoke 常量重载
【发布时间】:2017-06-29 17:27:24
【问题描述】:

给定以下实现异步 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();
}

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

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

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

【问题讨论】:

  • 你不能制作内部 lambda mutable
  • 是的,正如我所写的,使两个 lambdas 都可变是另一个“解决方案”。然而,这很容易出错。如果你这样做了,就没有错误,但是你的程序有一个错误,有时并不那么容易检测到。问题是这种情况是否可以通过某种方式得到改善。

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


【解决方案1】:

非常有趣的问题。

首先,前提看起来很奇怪。 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 之类的事情会悄悄出错,这将导致可预见的问题。

【讨论】:

  • 你试过运行我的例子吗?如果我省略了asio_handler_invoke,协程将不再在链中运行,它会在一段时间后崩溃。可以给async_wait一个更好的版本吗?
  • 另外,我在这里报告了这个错误:github.com/chriskohlhoff/asio/issues/183
  • 我在很大程度上确认了您的错误,使用的包装器可以更容易地查看是否调用了正确的版本。确实,这似乎是一个问题。当然,您可以在 boost::asio::detail 命名空间中添加相关的重载,但我认为这应该是库的一部分
  • 我明白这一点。显然这应该进入 asio 本身。但是,我指的是您答案的第一部分,您说使用asio_handler_invoke“在概念上不会增加​​任何东西,并且可能会降低性能(例如,通过两次缠绕)。”并且“它很容易导致不良结果”。我是说除了使用asio_handler_invoke 之外,我没有找到其他方法可以使我的示例正常工作。所以我只是想知道,你将如何编写我的 async_wait 函数。
  • 我从来没有说过 asio_handler_invoke 什么都不添加(那将是非常愚蠢的)。我不确定以那种方式调用它会增加什么(即asio_handler_invoke(f, &amp;f)而不是asio_handler_invoke(g, &amp;f)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-10-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-07-24
  • 1970-01-01
相关资源
最近更新 更多