I am trying to write a C++ macro which would substitue a frequently-needed verbose code like
switch (id) {
case 0:
s << myFloat;
break;
case 1:
s << myInt;
break;
default: break;
}
with something like DESERIALIZE_MEMBERS(myFloat, myInt)
. s
and id
will not change names for the use case, so they don't need to be macro parameters.
It should support variable argument length, so DESERIALIZE_MEMBERS(myString, myArrayOfInts, myLong)
for another case should also work, adding a third case
statement to the switch expression.
However, it's not clear to me how to iterate the N
value in case N:
inside the macro for each argument.
Is that possible at all in standard C++ macros?
In c++17, here is a solution.
First a compile-time constant type and value:
template<auto X>
using constant_t = std::integral_constant<decltype(X), X>;
template<auto X>
constexpr constant_t<X> constant_v;
Next, a variant over such constants:
template<auto...Is>
using enum_t = std::variant<constant_t<Is>...>;
this lets you generate a compile-time enumeration value at runtime and use it.
Now some code that lets you convert a runtime integer into a compile-time enumeration value:
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 );
}
so if you do get_enum_v<10>(2)
, it returns an enum_t<...>
with 10 alternatives containing the alternative with index 2, which is a constant_v<std::size_t, 2>
.
Now we just get a tuple and an index, and we call a function on the tuple element described by the index:
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 );
}
you can now do this:
apply_to_nth_element(id, [&](auto& elem) {
s << elem;
}, std::tie(myFloat, myInt));
instead of
DESERIALIZE_MEMBERS(myFloat, myInt)
Live example; Code can be rewritten in versions older than c++17 but gets extremely ugly very fast.