Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can tuple variadic template recursion be improved with C++14 index sequences?

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.

like image 411
Vittorio Romeo Avatar asked Apr 14 '26 05:04

Vittorio Romeo


2 Answers

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)

like image 90
Casey Avatar answered Apr 16 '26 20:04

Casey


Option #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...>;
    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


Option #2

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


Option #3

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

like image 44
Piotr Skotnicki Avatar answered Apr 16 '26 22:04

Piotr Skotnicki



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!