Access tuple element in C++11 via compile time variable in function

10,543

Solution 1

You could use a std::array instead of a std::tuple. In the example given, the members of the tuple all have the same type.

So, we could do:

char get_elem_i(int i, std::array<char, 2> t)
{
    return t[i];
}

Here's a slight variant on the example you gave to show why it's not directly possible in the general case:

???? get_elem_i(int i, std::tuple<char, struct foo, class bar> t) {
    return std::get<i>(t);
}

What is the return type of that function? char? struct foo?


And you could always write a function like this:

char get_elem_i(int i, std::tuple<char, char> t) {
    switch (i) {
        case 0: return std::get<0>(t);
        case 1: return std::get<1>(t);
    }

    assert(false);
}

Solution 2

If you can know the value of i at compile time then you can getting around specifying specifying the explicit value of i by wrapping your logic for i in aconstexpr.

For example:

#include <tuple>
#include <iostream>

constexpr int compile_time_known_i(int input) { return input / 3; }

template<int i>
char get_elem_i(std::tuple<char, char> t)
{
    return std::get<i>(t);
}

int main()
{
    std::tuple<char, char> t('H','i');
    char c = get_elem_i<0>(t);
    char d = get_elem_i<compile_time_known_i(3)>(t);
    std::cout << "The char is: " << c << " " << d <<  std::endl;
}

Given your i's can be known at compile time, this may help clean things up (although how much sense this makes depends on your use case).

If it's the syntax of passing a parameter you are after, you could achieve this with the preprocessor - with some optional type_traits safety.

#include <tuple>
#include <iostream>
#include <type_traits>

#define get_elem_i_ct(i, t)                                                                \
    std::get<i>(t);                                                                        \
    static_assert(std::is_integral<decltype(i)>::value, #i " must be an integral type");   \
    static_assert(std::is_same<decltype(t), std::tuple<char, char>>::value, #t " must be a tuple");

int main()
{
    std::tuple<char, char> t('H','i');
    char c = get_elem_i_ct(0, t)
    char d = get_elem_i_ct(1, t)
    std::cout << "The char is: " << c << " " << d <<  std::endl;
}

Although this achieves the stated syntactical requirement, I wouldn't recommend using this approach in anger - there's almost certainly a better solution to your actual problem.

Share:
10,543
Johannes
Author by

Johannes

Updated on June 05, 2022

Comments

  • Johannes
    Johannes almost 2 years

    The following minimal example compiles with g++ -std=c++11 -Wall tuple.cpp -o tuple:

    #include <tuple>
    #include <iostream>
    
    template<int i>
    char get_elem_i(std::tuple<char, char> t)
    {
        return std::get<i>(t);
    }
    
    int main()
    {
        std::tuple<char, char> t('H','i');
        char c = get_elem_i<0>(t);
        std::cout << "The char is: " << c << std::endl;
    }
    

    Now, I do not want to use a template which specifies the index (the exact reason why: I have templates that are deduced automatically, and I do not want to need to specify them all). So my first try was:

    char get_elem_i(int i, std::tuple<char, char> t)
    {
        return std::get<i>(t);
    }
    

    I understand that this can not compile. Is there any way to assure the compiler that i will be known at compile time? Maybe something like this?

    char get_elem_i(compile_time_known int i, std::tuple<char, char> t)
    
  • Timothy Shields
    Timothy Shields about 11 years
    "What is the return type of that function?" I'd say that isn't actually the problem - you could use a auto ... -> decltype(...) kind of function declaration to fix that problem. The problem is that the int i parameter is being used as a template parameter in std::get<i>.
  • Bill Lynch
    Bill Lynch about 11 years
    @TimothyShields: Sure, what's the result of the decltype? A variant? The reason that i is a template argument is because the return type of std::get varies based on that input.
  • Bill Lynch
    Bill Lynch about 11 years
    @TimothyShields: I also agree that my middle function doesn't compile. It's to explain that i needs to be a template parameter in the general case.
  • Johannes
    Johannes about 11 years
    @sharth Thanks, the switch works! Is this the only/best solution? I just ask because it is still 4 lines of code, and maybe not runtime-perfect. If there will be no solution to that in the next days, I'll accept your answer.
  • Johannes
    Johannes about 11 years
    @sharth What's also a problem: The switch relies on knowing the type of the std::tuple. So I can not use a template for the type of tuple.
  • Johannes
    Johannes about 11 years
    Thanks, but this still needs a template parameter. Is it impossible to use none?
  • Johannes
    Johannes about 11 years
    I would not like to use a macro. But this is interesting, thanks!