如何在可变参数长度 C++ 宏(...和 VA_ARGS)中迭代宏内整数值?
How to iterate in-macro integer value in a variable-parameter-length C++ macro (... and VA_ARGS)?
我正在尝试编写一个 C++ 宏来替换经常需要的冗长代码,例如
switch (id) {
case 0:
s << myFloat;
break;
case 1:
s << myInt;
break;
default: break;
}
类似 DESERIALIZE_MEMBERS(myFloat, myInt)
。 s
和 id
不会更改用例的名称,因此它们不需要是宏参数。
它应该支持可变参数长度,所以 DESERIALIZE_MEMBERS(myString, myArrayOfInts, myLong)
对于另一种情况也应该有效,向 switch 表达式添加第三个 case
语句。
但是,我不清楚如何在每个参数的宏中迭代 case N:
中的 N
值。
这在标准 C++ 宏中完全可行吗?
在c++17中,这里有一个解决方案。
首先是一个compile-time常量类型和值:
template<auto X>
using constant_t = std::integral_constant<decltype(X), X>;
template<auto X>
constexpr constant_t<X> constant_v;
接下来,这些常量的变体:
template<auto...Is>
using enum_t = std::variant<constant_t<Is>...>;
这让您可以在运行时生成 compile-time 枚举值并使用它。
现在可以使用一些代码将运行时整数转换为 compile-time 枚举值:
template<std::size_t...Is>
constexpr enum_t<Is...> get_enum_v( std::index_sequence<Is...>, std::size_t i ) {
using generator = enum_t<Is...>(*)();
constexpr generator generators[] = {
+[]()->enum_t<Is...> {
return constant_v<Is>;
}...
};
return generators[i]();
}
template<std::size_t N>
auto get_enum_v( std::size_t i ) {
return get_enum_v( std::make_index_sequence<N>{}, i );
}
所以如果你做 get_enum_v<10>(2)
,它 returns 一个 enum_t<...>
有 10 个备选方案,其中包含索引为 2 的备选方案,即 constant_v<std::size_t, 2>
.
现在我们只得到一个元组和一个索引,我们在索引描述的元组元素上调用一个函数:
template<class F, class Tuple>
auto apply_to_nth_element( std::size_t i, F f, Tuple tuple ) {
constexpr std::size_t N = std::tuple_size<Tuple>{};
auto Index = get_enum_v<N>( i );
return std::visit( [&](auto I){
return f( std::get<I>(tuple) );
}, Index );
}
您现在可以这样做:
apply_to_nth_element(id, [&](auto& elem) {
s << elem;
}, std::tie(myFloat, myInt));
而不是
DESERIALIZE_MEMBERS(myFloat, myInt)
我正在尝试编写一个 C++ 宏来替换经常需要的冗长代码,例如
switch (id) {
case 0:
s << myFloat;
break;
case 1:
s << myInt;
break;
default: break;
}
类似 DESERIALIZE_MEMBERS(myFloat, myInt)
。 s
和 id
不会更改用例的名称,因此它们不需要是宏参数。
它应该支持可变参数长度,所以 DESERIALIZE_MEMBERS(myString, myArrayOfInts, myLong)
对于另一种情况也应该有效,向 switch 表达式添加第三个 case
语句。
但是,我不清楚如何在每个参数的宏中迭代 case N:
中的 N
值。
这在标准 C++ 宏中完全可行吗?
在c++17中,这里有一个解决方案。
首先是一个compile-time常量类型和值:
template<auto X>
using constant_t = std::integral_constant<decltype(X), X>;
template<auto X>
constexpr constant_t<X> constant_v;
接下来,这些常量的变体:
template<auto...Is>
using enum_t = std::variant<constant_t<Is>...>;
这让您可以在运行时生成 compile-time 枚举值并使用它。
现在可以使用一些代码将运行时整数转换为 compile-time 枚举值:
template<std::size_t...Is>
constexpr enum_t<Is...> get_enum_v( std::index_sequence<Is...>, std::size_t i ) {
using generator = enum_t<Is...>(*)();
constexpr generator generators[] = {
+[]()->enum_t<Is...> {
return constant_v<Is>;
}...
};
return generators[i]();
}
template<std::size_t N>
auto get_enum_v( std::size_t i ) {
return get_enum_v( std::make_index_sequence<N>{}, i );
}
所以如果你做 get_enum_v<10>(2)
,它 returns 一个 enum_t<...>
有 10 个备选方案,其中包含索引为 2 的备选方案,即 constant_v<std::size_t, 2>
.
现在我们只得到一个元组和一个索引,我们在索引描述的元组元素上调用一个函数:
template<class F, class Tuple>
auto apply_to_nth_element( std::size_t i, F f, Tuple tuple ) {
constexpr std::size_t N = std::tuple_size<Tuple>{};
auto Index = get_enum_v<N>( i );
return std::visit( [&](auto I){
return f( std::get<I>(tuple) );
}, Index );
}
您现在可以这样做:
apply_to_nth_element(id, [&](auto& elem) {
s << elem;
}, std::tie(myFloat, myInt));
而不是
DESERIALIZE_MEMBERS(myFloat, myInt)