如何在编译时获取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
相关产品推荐
相关产品推荐

