user1391279
user1391279

Reputation:

Access a type in a variadic template by index

I would like to obtain a type in a variadic template by index. The index is specified as a template argument. I managed to find a 'hack' that works, but I believe that it is not in the spirit of variadic template programming. Besides, it uses extra memory.

Here is the code with some explanations:

template <typename... InputPortTypes>
class PipelineReceiver
{

protected:

    // This tuple is used for storing types only
    // Hence, I would like to get rid of it, but I am not sure how.
    std::tuple<
    std::function<std::unique_ptr<InputPortTypes> (int)>...
    > InputPortsTuple;

    // This vector is used for storing the actual objects
    // This is needed to be able to access/change its elements
    // during run time later on.
    // The vector is used for storage of function pointers (i.e. of type std::function)
    // that represent methods of another object upstream the pipeline.
    std::vector<boost::any> InputPortsVector;

public:

    PipelineReceiver()
        {
            // create an empty vector of the required size
            InputPortsVector.resize(sizeof...(InputPortTypes));
        }

    void connectPorts(int InputPortIndex, boost::any c_OutputPort)
        {
            // connect ports
            InputPortsVector[InputPortIndex] = c_OutputPort;
        }

     // this function needs to be modified to avoid using InputPortsTuple
    template<int N>
    void getInputPortValue(void)
        {
            std::cout <<
                *boost::any_cast<decltype(std::get<N>(this -> InputPortsTuple))>(
                    InputPortsVector[N]
                    )(0) <<
                std::endl;
        }

};

I would like to remove the object InputPortsTuple and replace it with some form of a recursive procedure for inferring the types in getInputPortValue.

Ideally, I would like N to be a dynamic parameter instead of a template argument. However, I am not sure if this is possible.

Upvotes: 4

Views: 3585

Answers (2)

Dedmen Miller
Dedmen Miller

Reputation: 111

If you are fine with making your own template, you can create your own variant of std::tuple_element that directly takes a type list without having to wrap it in a std::tuple.

#include <type_traits>
using std::size_t;

template<std::size_t N, class ...T>
struct typelist_element;

// recursive case
template<std::size_t N, class Head, class... Tail>
struct typelist_element<N, Head, Tail...> : typelist_element<N-1, Tail...>
{ 
    static_assert(N < (sizeof...(Tail) + 1), "N out of bounds");
};

// base case
template<class Head, class... Tail >
struct typelist_element<0, Head, Tail...> 
{
    using type = Head;
};

// error out of bounds, only here to silence compiler warnings about undefined template
template<>
struct typelist_element<0> 
{
    using type = void;
};

template<std::size_t N, class ...T>
using typelist_element_t = typename typelist_element<N, T...>::type;

And then just

boost::any_cast<typelist_element_t<N, InputPortTypes...>>(InputPortsVector[N])(0)

A note on efficency, the static assert will evaluate for every recursion, so not the best to have it there.

Upvotes: 0

Wintermute
Wintermute

Reputation: 44043

You could simply abuse std::tuple_element:

typename std::tuple_element<N, std::tuple<InputPortTypes...>>::type

Note: if you can use C++14,

std::tuple_element_t<N, std::tuple<InputPortTypes...>>

is a nicer way to do the same thing. Not all common compilers know it yet, though.

Upvotes: 8

Related Questions