You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何优雅结合asio::co_composed与std::variant解决异步调用问题

问题

我需要处理拥有相同签名成员函数的connection类,比如tcp和tls都属于connection类,二者都提供支持CompletionToken的send()成员函数模板。我用std::variant来管理connection类型。

在应用的某部分,我定义了支持CompletionToken的non_awaitable_func函数模板,希望通过co_await实现它,这种场景可以用asio::experimental::co_composed。

到目前为止一切正常。

但当我用std::visit调用connection的send()成员函数模板(传入boost::asio::deferred)时,出现断言失败。原因是std::visit的访问器函数返回类型不同:boost::asio::deferred会生成可转换为指定签名的不同返回类型。

通常这种场景我会用boost::asio::use_awaitable替代boost::asio::deferred,因为boost::asio::use_awaitable会生成统一的返回类型,但在co_composed的实现里没法用它。

有没有更好的解决方法?

演示代码

#include <iostream>
#include <chrono>

#include <boost/asio.hpp>
#include <boost/asio/experimental/co_composed.hpp>

struct tcp {
    template <typename CompletionToken> // void(boost::system::error_code)
    auto send(CompletionToken&& token) {
        // 伪实现
        auto tim = std::make_shared<boost::asio::steady_timer>(exe_, std::chrono::seconds(1));
        return tim->async_wait(
            boost::asio::consign(
                std::forward<CompletionToken>(token),
                tim
            )
        );
    }
    boost::asio::any_io_executor exe_;
};

struct tls {
    template <typename CompletionToken> // void(boost::system::error_code)
    auto send(CompletionToken&& token) {
        // 伪实现
        return boost::asio::dispatch(
            boost::asio::append(
                std::forward<CompletionToken>(token),
                boost::system::errc::make_error_code(boost::system::errc::bad_message)
            )
        );
    }
    boost::asio::any_io_executor exe_;
};

#if 0 // 设为0时无错误,因为所有visit返回类型相同
using connection = std::variant<tcp, tls>;
#else
using connection = std::variant<tcp>;
#endif

template <typename CompletionToken>
auto non_awaitable_func(
    connection& con,
    CompletionToken&& token
) {
    return boost::asio::async_initiate<
        CompletionToken,
        void(boost::system::error_code)
    >(
        boost::asio::experimental::co_composed<
            void(boost::system::error_code)
        >(
            [](auto /*state*/, connection& con) -> void {
                auto [ec] = co_await std::visit(
                    [](auto& c) {
                        // co_composed中无法使用use_awaitable
                        return c.send(boost::asio::as_tuple(boost::asio::deferred));
                    },
                    con
                );
                // 用户自定义实现
                co_return {ec};
            }
        ),
        token,
        con
    );
}

int main() {
    boost::asio::io_context ioc;
    connection con = tcp{ioc.get_executor()};
    non_awaitable_func(
        con,
        [&] (boost::system::error_code ec) {
            std::cout << "cb called:" << ec << std::endl;
        }
    );
    ioc.run();
}

临时解决方案

我用std::get_if替代了std::visit。

boost::asio::experimental::co_composed<
            void(boost::system::error_code)
        >(
            [](auto /*state*/, connection& con) -> void {
                // 不使用std::visit的方案
                // 但不够优雅,存在重复代码...
                if (auto* p = std::get_if<0>(&con)) {
                    auto [ec] = co_await p->send(boost::asio::as_tuple(boost::asio::deferred));
                    // 用户自定义实现
                    co_return {ec};
                }
                else {
                    // p冲突
                    auto* q  = std::get_if<1>(&con);
                    BOOST_ASSERT(q);
                    auto [ec] = co_await q->send(boost::asio::as_tuple(boost::asio::deferred));
                    // 用户自定义实现
                    co_return {ec};
                }
            }
        ),

该方案能正常工作,但不够优雅。我尝试用预处理器宏避免代码重复,但目前还没找到合适的方法。

环境
  • Boost 1.84.0
  • clang++ 17.0.1
  • 编译选项:-std=c++20
更新说明

我注意到non_awaitable_func()的名称容易引起误解,特此澄清:它指返回类型非boost::asio::awaitable<T>的异步函数,该函数带有CompletionToken参数,支持各类Boost.Asio的CompletionToken类型,例如use_future、use_awaitable和deferred。


内容的提问来源于stack exchange,提问作者Takatoshi Kondo

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.02 11:34:56