Reputation: 25592
A hypothetical variadic template tuple class would, as far as I can tell, have to use getters with template parameters.
int MyInt = MyTuple.Get<int>(0);
This is inconvenient, and introduces potential for error. I can't help but feel that there's a way to construct the class so you you don't have to explicitly specify it.
int MyInt = MyTuple.Get(0);
My first thought was for the Get()
member function to return another class that figures out the type on its own, probably by comparing typeid(Foo).name()
to values in some precomputed list. That still has to happen before runtime, though, and I couldn't figure out a way to iterate through anything like that at compile-time.
Is there any way for a variadic template container class - like a tuple - to have a getter that doesn't require the type to be explicitly specified?
Upvotes: 1
Views: 808
Reputation: 137830
You mean like std::tuple
?
The getter's template argument specifies the index of the member, not the type. By definition, the number and types of a tuple are fixed at compile time. Since the type depends on the index, and the type must be known at compile time, the getter must be templated.
template< typename ... types >
struct tuple;
template< typename head, typename ... tail_types >
struct tuple {
head value;
tuple< tail_types ... > tail;
};
template<>
struct tuple<> {};
template< typename tuple, size_t index >
struct tuple_element;
template< typename head, typename ... tail, size_t index >
struct tuple_element< tuple< head, tail ... >, index >
{ typedef typename tuple_element< tail ..., index - 1 >::type type; };
template< typename head, typename ... tail >
struct tuple_element< tuple< head, tail ... >, 0 >
{ typedef head type; };
template< size_t index, typename tuple >
typename std::enable_if< index != 0,
typename tuple_element< tuple, index >::type >::type
get( tuple const &t )
{ return get< index - 1 >( t.tail ); }
template< size_t index, typename tuple >
typename std::enable_if< index == 0,
typename tuple_element< tuple, index >::type >::type
get( tuple const &t )
{ return t.value; }
etc.
Upvotes: 7
Reputation: 224089
So you already got a few replies explaining that this would work if you pass the index as a compile-time parameter. I want to comment on the general idea, though:
I think I have seen the trick to return a proxy that has a templatized implicit conversion operator:
template<typename T> operator T()
(I'm too lazy to check this in code, though. If somebody is willing to do the work and show a working example, drop me a comment here and I'll likely upvote.)
Of course, as always with such hacks, you need to ask yourself whether it's worth it. Implicit conversion can trip you up any time. (I used to think that sometimes they are not evil. But someday I had to remove the last implicit conversion in working code of mine, because it had introduced a very hard to find bug, and swore to never to use them again.)
Also, the additional safety of having to spell out the type can act as seat belt (preventing you from crushing through the windshield when you fail badly).
Finally, C++11 allows this:
auto foo = bar.get<int>(); // available today at a compiler near you
See, the type is only spelled out once. :)
Upvotes: 1
Reputation: 473537
The rules of C++ do not generally allow what you're asking for.
A struct is a collection of values of known types, which are accessed by a name which must be provided at compile time. That last part is crucial, because it allows the C++ compiler to know which value you're talking about.
A tuple
is a collection of values of known types, which are accessed by a number. However, just because it's a number does not mean it can be a runtime-defined number, just as having a string name for a member of a struct does not mean you can get that member at runtime.
Each member of the tuple
is typed, and C++ needs to know the types of things at compile time in order to function. Similarly, the return type of any function is known at compile time; it cannot change based on the value of a parameter. You can select a different function based on the type (function overloading or template argument deduction), but it cannot be based on a value, because values (unless they're compile-time constants) are only known at runtime.
In order to do what you want, you will have to break the C++ type system in some way. For example, you could theoretically have a get
function which returned a typeless pointer. Or, if you want at least some pretense of type-safety, perhaps a boost::any
, or a boost::variant
of the tuple
's types. But that's about the best you're going to be able to do.
Even if you specify the type at compile time (get<Type>
), that's still not good enough due to the possibility of a type mismatch. What do you do then?
The concept is just not going to really work in a compile-time typed language like C++.
Upvotes: 3
Reputation: 54614
You certainly could do it, but it probably isn't worth it. One way you can do it is to supply a static function that converts the variadic template instance member to a boost::any
and have a static table of functions:
template <...> class any_tuple
{
...
typedef boost::any (*extract_member)(any_tuple &);
static extract_member member_extrators[...];
boost::any Get(int index) { return member_extractors[index](*this); }
};
But then you have to worry about initialization and all that stuff.
Upvotes: 0
Reputation: 21597
It's not hypothetical. boost::tuple behaves the way you describe.
boost::tuple<int, float> myTuple = boost::make_tuple(10, 3.1);
int myInt = myTuple.Get<0>();
Upvotes: 1
Reputation: 234504
The standard library includes std::get
function template that works with std::tuple
and std::pair
and doesn't require the type to be explicity specified. It works by taking the index as a template parameter.
std::tuple<int,std::string,char const*> t(1,"foo","blah");
auto i = std::get<0>(t); // i is int
auto s = std::get<1>(t); // s is std::string
auto p = std::get<2>(t); // p is char const*
The return type must always be known at compile-time, and it depends on the index, so you cannot possibly take the index as a runtime argument.
Upvotes: 1
Reputation: 81349
MyTuple.Get(0);
what would be the return type of such Get declaration? If you are okey with it returning a variant over every possible type in the tuple, then you could make it work, yes.
On the other hand, Boost.Tuple and the C++0x Tuple take the index as a template parameter. No potential for mistakes there. Check them out.
Upvotes: 1