C++可变参数宏拼接生成变量名及编译期constexpr实现方法
可变参数宏实现方案
预处理器本身没有原生的递归拼接可变参数的能力,需要借助参数计数+分支展开的辅助宏实现。另外你需求中调用时参数带引号的形式无法直接生成合法变量名,因为引号会被识别为预处理器标记的一部分,无法自动剥离,因此我们调整为参数传无引号的标记,宏内部自动处理标识符拼接和字符串加引号逻辑,完全符合你要求的生成结果:
// 手动实现基础辅助宏,无需引入第三方依赖 #define PP_NARG(...) \ PP_NARG_(__VA_ARGS__,PP_RSEQ_N()) #define PP_NARG_(...) \ PP_ARG_N(__VA_ARGS__) #define PP_ARG_N( \ _1, _2, _3, _4, _5, _6, _7, _8, _9,_10, \ _11,_12,_13,_14,_15,_16,_17,_18,_19,_20, \ _21,_22,_23,_24,_25,_26,_27,_28,_29,_30, \ _31,_32,_33,_34,_35,_36,_37,_38,_39,_40, \ _41,_42,_43,_44,_45,_46,_47,_48,_49,_50, \ _51,_52,_53,_54,_55,_56,_57,_58,_59,_60, \ _61,_62,_63,N,...) N #define PP_RSEQ_N() \ 63,62,61,60, \ 59,58,57,56,55,54,53,52,51,50, \ 49,48,47,46,45,44,43,42,41,40, \ 39,38,37,36,35,34,33,32,31,30, \ 29,28,27,26,25,24,23,22,21,20, \ 19,18,17,16,15,14,13,12,11,10, \ 9,8,7,6,5,4,3,2,1,0 // 可按需扩展到更多参数,这里以3参数为例匹配你的示例 #define JOIN_IDENT_1(delim, a) a #define JOIN_IDENT_2(delim, a, b) a##delim##b #define JOIN_IDENT_3(delim, a, b, c) a##delim##b##delim##c #define JOIN_STR_1(delim, a) #a #define JOIN_STR_2(delim, a, b) #a #delim #b #define JOIN_STR_3(delim, a, b, c) #a #delim #b #delim #c #define JOIN_IDENT(N, delim, ...) JOIN_IDENT_##N(delim, __VA_ARGS__) #define JOIN_STR(N, delim, ...) JOIN_STR_##N(delim, __VA_ARGS__) #define def_name(delim, ...) \ inline constexpr char const JOIN_IDENT(PP_NARG(__VA_ARGS__), delim, __VA_ARGS__)##_name[] { JOIN_STR(PP_NARG(__VA_ARGS__), delim, __VA_ARGS__) };
使用示例
调用写法和生成结果完全匹配你的要求:
// 下划线分隔场景 def_name(_, abc, def, ghi); // 展开后生成:inline constexpr char const abc_def_ghi_name[]{"abc_def_ghi"}; // 无分隔符场景 def_name(, abc, def, ghi); // 展开后生成:inline constexpr char const abcdefghi_name[]{"abcdefghi"};
宏实现限制
- 最多支持的参数个数由你扩展的
JOIN_IDENT_xx/JOIN_STR_xx最大序号决定,上述示例默认支持最多3个参数,可自行扩展到需要的数量 - 参数只能是合法的C++标识符字符,不能包含空格、特殊符号
C++ constexpr实现方案
首先明确:变量名属于C++语法层面的标识符,只能由预处理器生成,模板和constexpr只能操作编译期值,无法生成新的标识符。如果你不需要自动生成变量名,只需要编译期生成拼接后的字符串常量,可以用纯constexpr实现,不需要依赖预处理器:
#include <string_view> #include <array> #include <algorithm> // C++20及以上版本实现 template<typename... Args> constexpr auto join_str(std::string_view delim, Args... parts) requires (std::is_convertible_v<Args, std::string_view> && ...) { constexpr size_t N = sizeof...(parts); std::array<std::string_view, N> part_arr{static_cast<std::string_view>(parts)...}; size_t total_size = 0; for (auto p : part_arr) total_size += p.size(); total_size += delim.size() * (N > 0 ? N - 1 : 0); std::array<char, total_size + 1> res{}; char* ptr = res.data(); for (size_t i = 0; i < N; ++i) { if (i > 0) { std::copy_n(delim.data(), delim.size(), ptr); ptr += delim.size(); } std::copy_n(part_arr[i].data(), part_arr[i].size(), ptr); ptr += part_arr[i].size(); } *ptr = '\0'; return res; }
使用示例
constexpr auto abc_def_ghi_name = join_str("_", "abc", "def", "ghi"); constexpr auto abcdefghi_name = join_str("", "abc", "def", "ghi");
该实现的优点是不需要预处理器,支持任意数量的参数,参数可以是任意编译期字符串,不需要符合标识符规则。
内容的提问来源于stack exchange,提问作者Meekaa Saangoo
相关产品推荐
相关产品推荐

