Reputation: 21510
Assuming I have a class which pretends to be one of the types in from its template pack, how can I get the index at which one template parameter is at in a function or which type the current index points to?
template<typename... Vs>
struct TypeIndex {
int index_of_type;
void some_function() {
cout << static_cast<get_type_from_index()> (some_member);
}
};
I understand that there needs to be some class specialization done here but I cannot totally understand how that will be implemented in code. Any help would be great, thanks!
Upvotes: 1
Views: 117
Reputation: 137330
get the index at which one template parameter is at in a function
There are two million ways to do this. Here's one:
// base case is "not found"
template<size_t N, class T, class... Ts>
struct index_of_impl : std::integral_constant<std::size_t, std::size_t(-1)> {};
template<size_t N, class T, class... Ts>
struct index_of_impl<N, T, T, Ts...> : std::integral_constant<std::size_t, N> {};
template<size_t N, class T, class U, class... Ts>
struct index_of_impl<N, T, U, Ts...> : index_of_impl<N+1, T, Ts...> {};
template<class T, class... Ts>
struct index_of : index_of_impl<0, T, Ts...> {};
which type the current index points to
The value of the index is only known at runtime and C++ is statically typed, so that's not possible.
For a compile-time index it is trivial:
template<size_t I, class... Ts>
using at = std::tuple_element_t<I, std::tuple<Ts...>>;
But then how does
boost::variant
do this with theapply_visitor
function?
The idea with apply_visitor
is to compile it down to a switch
-like construct:
// pseudocode
switch(index) {
case 0:
return visitor(get<0>(variant));
break;
case 1:
return visitor(get<1>(variant));
break;
// etc.
};
Note that visitor
is required to work with every type in the variant and return the same type in every case, which is what makes the above work.
Of course, in reality you can't write a switch
if the possible number of types is unbounded (in C++03 - without variadic templates - boost::variant
obviously has an upper bound on the number of types it can handle, so they can write a switch
directly if they wanted). The simplest way to do it is to simply try every index until you find the right one. Sketch:
template<size_t N>
struct impl {
template<class Visitor, class Variant>
decltype(auto) apply(Visitor visitor, const Variant& variant){
if(variant.index == N) return visitor(get<N>(variant));
return impl<N-1>::apply(visitor, variant);
}
};
template<>
struct impl<0>{
template<class Visitor, class Variant>
decltype(auto) apply(Visitor visitor, const Variant& variant){
assert(variant.index == 0);
return visitor(get<0>(variant));
}
};
template<class Visitor, class Variant>
decltype(auto) apply_visitor(Visitor visitor, const Variant& variant){
return impl<variant_size<Variant>::value>::apply(visitor, variant);
}
There are more efficient approaches (e.g., do a binary search, or use a jump table).
Upvotes: 3
Reputation: 3107
The class below was working with code in the answer to your other question. I didn't include it because the destructor dispatch which was to rely on it hadn't been implemented yet.
Use it like: using my_type = pack_type_at<index, Vs...>::type;
, where index
is an integer available at compile-time, like from a template parameter.
Note before upvoting: This is just for compile-time indexes, so it doesn't fully answer the question. Elsewhere I said the task was "nontrivial" because of the need to use the run-time index to somehow find and run the appropriate function overload which corresponds to one of the pack indexes.
template <int IDX, int CURI, bool ATIDX, typename V, typename ...Vs>
struct pack_type_at_helper {
typedef typename pack_type_at_helper<IDX, CURI+1, CURI+1==IDX, Vs...>::type type;
};
template <int IDX, int CURI, typename V, typename ...Vs>
struct pack_type_at_helper<IDX, CURI, true, V, Vs...> {
typedef V type;
};
template <int IDX, typename ...Vs>
struct pack_type_at {
typedef typename pack_type_at_helper<IDX, 0, IDX==0, Vs...>::type type;
};
Upvotes: 2