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

如何将需可拷贝回调的异步函数适配为Boost.Asio CompletionToken?

问题:将仅接受std::function回调的第三方异步函数适配为Boost.Asio CompletionToken

现有不可修改的第三方异步函数std_function_cb,其回调参数类型固定为std::function<void()>。需要将该函数适配为Boost.Asio的CompletionToken,使其同时支持普通回调、future以及协程调用。

自行编写的适配代码出现编译错误,根源在于async_compose生成的self对象仅支持移动操作,不可拷贝,但std::function要求其存储的可调用对象必须具备拷贝能力。已知move_only_function等替代方案,但无法修改std_function_cb的参数类型,求可行解决办法。

第三方不可修改代码

namespace as = boost::asio;

// BEGIN 3rd party code (不可修改)
inline
void std_function_cb(as::io_context& ioc, std::function<void()> cb) {
    // 伪实现:将回调投递到io_context执行
    as::post(ioc, std::move(cb));
}
// END 3rd party code (不可修改)

原适配代码(编译报错)

struct async_impl {
    as::io_context& ioc_;
    enum { first, second } state_ = first;

    template <typename Self>
    void operator()(Self& self) {
        switch (state_) {
        case first:
            state_ = second;
            std_function_cb(ioc_, std::move(self));
            break;
        case second:
            self.complete();
            break;
        }
    }
};

template <typename CompletionToken>
auto async_call(
    as::io_context& ioc,
    CompletionToken&& token
)
-> typename as::async_result<
    typename std::decay<CompletionToken>::type,
    void()
>::return_type {
    return as::async_compose<
        CompletionToken,
        void()
    >(async_impl{ioc}, token);
}

期望支持的用法

int main() {
    std::cout << BOOST_VERSION << std::endl;
    {
        std::cout << "使用普通回调作为token" << std::endl;
        as::io_context ioc;
        async_call(
            ioc,
            [] {
                std::cout << "回调被调用" << std::endl;
            }
        );
        ioc.run();
    }
    {
        std::cout << "使用future作为token" << std::endl;
        as::io_context ioc;
        std::future<void> f = async_call(ioc, as::use_future);
        std::thread th {[&] { ioc.run(); }};
        f.get();
        std::cout << "future已解除阻塞" << std::endl;
        th.join();
    }
}

解决方案

核心思路是借助std::shared_ptr的拷贝语义,包装async_compose生成的移动-only self对象,让包装后的对象满足std::function的拷贝要求。具体实现如下:

修改后的适配代码

#include <memory>
namespace as = boost::asio;

// 第三方不可修改代码(同上)
inline
void std_function_cb(as::io_context& ioc, std::function<void()> cb) {
    as::post(ioc, std::move(cb));
}

struct async_impl {
    as::io_context& ioc_;
    enum { first, second } state_ = first;

    template <typename Self>
    void operator()(Self& self) {
        switch (state_) {
        case first:
            state_ = second;
            // 用shared_ptr持有self的所有权,将拷贝语义传递给std::function
            auto self_ptr = std::make_shared<Self>(std::move(self));
            std_function_cb(ioc_, [self_ptr]() mutable {
                // 触发self的后续逻辑
                (*self_ptr)();
            });
            break;
        case second:
            self.complete();
            break;
        }
    }
};

template <typename CompletionToken>
auto async_call(
    as::io_context& ioc,
    CompletionToken&& token
)
-> typename as::async_result<
    typename std::decay<CompletionToken>::type,
    void()
>::return_type {
    return as::async_compose<
        CompletionToken,
        void()
    >(async_impl{ioc}, token);
}

原理说明

  • std::shared_ptr本身是可拷贝的,将移动-only的self对象通过std::move存入shared_ptr后,shared_ptr可以安全地被拷贝到std::function中。
  • 当第三方函数触发回调时,通过shared_ptr访问内部的self对象,调用其operator()进入second状态,最终完成CompletionToken的回调逻辑。
  • 这种方式既满足了std::function的拷贝要求,又没有违反self对象的移动-only特性,因为self仅被移动一次到shared_ptr中,后续操作都是通过智能指针访问。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.17 07:20:35