Access tuple element in C++11 via compile time variable in function
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.
Johannes
Updated on June 05, 2022Comments
-
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 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 theint i
parameter is being used as a template parameter instd::get<i>
. -
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 ofstd::get
varies based on that input. -
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 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 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 about 11 yearsThanks, but this still needs a template parameter. Is it impossible to use none?
-
Johannes about 11 yearsI would not like to use a macro. But this is interesting, thanks!