Curious
Curious

Reputation: 21510

How to distinguish the type stored in a templated class

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

Answers (2)

T.C.
T.C.

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 the apply_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

Christopher Oicles
Christopher Oicles

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

Related Questions