4

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.

  • You can trivially remove the unused member by using `std::declval (int)>...>>()` instead of `this->InputPortsTuple`, unless I'm missing something. (I know that's not what you're really after. It's merely a slightly less bad alternative.) –  Apr 19 '15 at 11:07
  • But actually, why don't you just store the elements in a tuple directly? Why use a vector in the first place? You can still change a tuple. –  Apr 19 '15 at 11:08
  • @hvd Thank you for your comments. What you suggested in the first comment is a better alternative to what I am doing at the moment, but, as you pointed out, still not perfect. I will post another comment to explain why I need to use the vector. –  Apr 19 '15 at 11:12
  • @hvd Regarding your second comment, I was trying to explain why I need the vector in the comment that comes before the definition of the vector (i.e. 'This (*the vector*) is needed to be able to access/change its elements during run time later on'). I will try to improve the comment to make it clearer. Thank you for pointing this out. –  Apr 19 '15 at 11:14
  • 2
    Yeah, I read that, but I don't see it: a tuple allows its elements to be changed as well: `std::get(tuple)` returns an lvalue reference that you can assign to without any issues. –  Apr 19 '15 at 11:18
  • @hvd You are right. However, N has to be a compile-time argument. I am worried about the size of the executable and readability of the code if I am to start abusing various pseudo-dynamic features, e.g. looping through the elements of the tuple. –  Apr 19 '15 at 11:25
  • A quick search shows that [Boost.Fusion can do that](http://stackoverflow.com/questions/1198260/iterate-over-tuple), while still keeping the code readable. And I think it would be good to actually measure the executable size: my expectations are the opposite of yours, I expect Boost.Any to add a lot of code that will end up unused, so by avoiding Boost.Any, you might reduce code size. But it's up to you, of course. If you think readability is helped by using a vector, and you're the one who ends up maintaining the code, that's a very strong reason for using a vector. :) –  Apr 19 '15 at 11:37
  • @hvd Thank you for your comment. I must admit I never heard about Boost.Fusion, but I will definitely look into it later on. Regarding Boost.Any, there are other reasons for using it. This is related to an (incomplete) solution to another problem that I did not describe in this question. –  Apr 19 '15 at 11:43

2 Answers2

8

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.

Wintermute
  • 42,983
  • 5
  • 77
  • 80
  • There's no need for `::type` suffix in `std::tuple_element_t`. –  Apr 19 '15 at 11:36
  • @ligoore Thank you. Silly copy&paste mistake. – Wintermute Apr 19 '15 at 11:40
  • @Wintermute Thank you for your reply. If I understand correctly, you are suggesting to replace `decltype(std::get(this -> InputPortsTuple))` with a pre-declared type `std::tuple_element>::type`. Unfortunately, this does not work. I get a run-time error `failed conversion using boost::any_cast`. The names associated with the typeid of the variables declared via decltype(std::get(this -> InputPortsTuple)) and `typename std::tuple_element>::type` are also different. –  Apr 19 '15 at 12:17
  • The code in the answer will give you the nth type of the argument pack `InputPortTypes`. If I read your code correctly, then you want to wrap this in `std::function(int)>`, so ultimately you're looking for `std::function>::type>(int)>` – Wintermute Apr 19 '15 at 12:27
  • @Wintermute I must apologise. I got lost in the notation and did not notice that you specified the type of the InputPortTypes template. Thank you for your answer. –  Apr 19 '15 at 14:26
0

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.