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

如何在编译时获取Boost.Asio自定义Handler所需的内存分配大小?

解决方案

你遇到的问题本质是:Boost.Asio会对传入的handler做多层包装(比如添加执行器上下文、异常安全包装、操作跟踪等),这些包装后的类型大小和Asio版本、编译选项强绑定,手动枚举size必然会在版本升级时失效。C++17下可以通过编译期类型收集+自动去重生成大小列表的方式完全解决,不需要手动指定任何固定大小。

第一步:实现编译期大小去重工具

先写一个标准库原生的编译期工具,用来对收集到的所有需要分配的大小做去重,避免BucketPool生成冗余的模板实例:

#include <utility>
#include <type_traits>

// 编译期对size列表去重
template <size_t... Sizes>
struct unique_sizes {
    using type = std::index_sequence<Sizes...>;
};

template <size_t S, size_t... Rest>
struct unique_sizes<S, S, Rest...> : unique_sizes<S, Rest...> {};

template <size_t S, size_t S2, size_t... Rest>
struct unique_sizes<S, S2, Rest...> {
    using rest_type = typename unique_sizes<S2, Rest...>::type;
    template <size_t... Rs>
    static unique_sizes<S, Rs...> impl(unique_sizes<Rs...>);
    using type = decltype(impl(std::declval<rest_type>()));
};

template <size_t... Sizes>
using unique_sizes_t = typename unique_sizes<Sizes...>::type;

第二步:自动收集所有需要分配的类型大小

Boost.Asio提供了handler_type专用trait,可以直接获取异步操作最终使用的handler类型,不需要手动猜包装后的大小:

// 先定义你业务逻辑的原始handler类型
using RawHandler = decltype(boost::bind(
    &HandlerWrapper::handleTimerCallback, 
    std::declval<boost::shared_ptr<HandlerWrapper>>(),
    boost::asio::placeholders::error
));

// 用Asio官方trait获取异步操作最终包装后的handler类型
using FinalHandler = boost::asio::handler_type<
    decltype(std::declval<boost::asio::steady_timer&>().async_wait),
    void(boost::system::error_code)
>::type;

// 收集所有需要分配的类型大小,有其他异步操作可以继续往下加
constexpr size_t final_handler_size = sizeof(FinalHandler);
constexpr size_t raw_handler_size = sizeof(RawHandler);
constexpr size_t wrapper_size = sizeof(HandlerWrapper);

// 生成去重后的大小序列
using AllocSizeSeq = unique_sizes_t<final_handler_size, raw_handler_size, wrapper_size>;

// 用大小序列实例化BucketPool
template <typename Seq>
struct make_bucket_pool;

template <size_t... Sizes>
struct make_bucket_pool<std::index_sequence<Sizes...>> {
    using type = BucketPool<Sizes...>;
};

using AutoSizedBucketPool = typename make_bucket_pool<AllocSizeSeq>::type;

第三步:替换硬编码的BucketPool

把原代码HandlerWrapper里的硬编码定义BucketPool<128> memory_;替换成AutoSizedBucketPool memory_;即可。

额外适配说明

  • 如果你有多个异步操作(比如async_read、async_write等),只需要把对应操作的handler_type生成的大小加到unique_sizes_t的参数里,工具会自动去重生成完整的大小列表
  • 升级Boost版本、修改编译选项(比如开启Asio调试、操作跟踪)时,只需要重新编译就会自动更新所有需要的大小,完全不需要手动调整参数
  • 如果有过对齐类型的分配需求,可以把alignof(T)也加入到编译期收集的参数中,调整Bucket的模板参数同时匹配大小和对齐即可,当前逻辑不用做大的改动

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.05 09:21:04