I have many variadic template functions to process std::tuple instances in my code similar to these ones:
// Convert JSON object to `std::tuple`
template<int TI = 0, typename... TS>
std::enable_if_t<TI == sizeof...(TS)> toTpl(const JVal&, std::tuple<TS...>&)
{
// Do nothing (end of the recursion)
}
template<int TI = 0, typename... TS>
std::enable_if_t<TI < sizeof...(TS)> toTpl(const JVal& mV, std::tuple<TS...>& mX)
{
std::get<TI>(mX) = mV[TI].as<std::tuple_element_t<TI, decltype(mX)>;
toTpl<TI + 1, TS...>(mV, mX);
}
Basically, I use a template int counter and enable_if to instantiate the template 0 -> sizeof...(TS) times.
I was wondering if and how the new C++14 std::integer_sequence or std::index_sequence could help in these situations.
I'm looking to make the code more readable (and easier to maintain). Improving compilation time is also a big plus.
I tried using std::index_sequence but ran into issues - I'm confused about its usage, especially when passing it as an argument in the function.
I'd return the tuple by value to avoid unnecessary default construction and the nasty out parameter:
template <typename... Ts, std::size_t... Is>
auto toTpl(const JVal& mV, std::index_sequence<Is...>)
{
return std::tuple<Ts...>{ mV[Is].as<Ts>()... };
}
template<typename... Ts>
auto toTpl(const JVal& mV)
{
return toTpl<Ts...>(mV, std::index_sequence_for<Ts...>{});
}
// Usage:
auto t = toTpl<int, double, long>(mV);
Maybe rename the function as_tuple:
auto t = as_tuple<int, char, std::string>(some_json_value);
which seems elegant and consistent with the usage of JVal::as. (Live at Coliru)
template <typename... TS, std::size_t... Is>
void toTpl(const JVal& mV, std::tuple<TS...>& mX, std::index_sequence<Is...>)
{
using Tuple = std::tuple<TS...>;
mX = std::forward_as_tuple(mV[Is].as<std::tuple_element_t<Is, Tuple>>()...);
}
template <typename... TS>
void toTpl(const JVal& mV, std::tuple<TS...>& mX)
{
toTpl(mV, mX, std::make_index_sequence<sizeof...(TS)>{});
}
DEMO 1
template <typename... TS, std::size_t... Is>
void toTpl(const JVal& mV, std::tuple<TS...>& mX, std::index_sequence<Is...>)
{
using Tuple = std::tuple<TS...>;
int dummy[] = { 0, ((void)(std::get<Is>(mX) = mV[Is].as<std::tuple_element_t<Is, Tuple>>()), 0)... };
}
template <typename... TS>
void toTpl(const JVal& mV, std::tuple<TS...>& mX)
{
toTpl(mV, mX, std::make_index_sequence<sizeof...(TS)>{});
}
DEMO 2
template <std::size_t N, typename... TS>
void op(const JVal& mV, std::tuple<TS...>& mX)
{
using Tuple = std::tuple<TS...>;
std::get<N>(mX) = mV[N].as<std::tuple_element_t<N, Tuple>>();
}
template <typename... TS, std::size_t... Is>
void toTpl(const JVal& mV, std::tuple<TS...>& mX, std::index_sequence<Is...>)
{
int dummy[] = { 0, ((void)op<Is>(mV, mX), 0)... };
}
template <typename... TS>
void toTpl(const JVal& mV, std::tuple<TS...>& mX)
{
toTpl(mV, mX, std::make_index_sequence<sizeof...(TS)>{});
}
DEMO 3
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With