0

In continuation of this topic Variadic template heterogeneous container, I would like to ask the following. Assume, that we have several classes with two members that is dynamic arrays. Now suppose that there is a sequence of objects of these classes, which is packed in heterogeneous container. In this sequence one of arrays-mebers is "output" vector and another array-member is "input" vector, which is pointer to appropriate output array from preceding object. This sequence is implemented as variadic template class:

//Classes, objects which are members of the sequence
template<int NumberElements>
struct A 
{
    A() : output(new float[NumberElements]){}//allocate output
    ~A(){delete[] output;}
    float *input;//input vector - pointer to output vector from preceding object of sequence 
    float *output;// output vector (size - NumberElements) of current member of sequence
};   
template<int NumberElements> 
struct B 
{
    B() : output(new float[NumberElements]){}//allocate output
     ~B(){delete[] output;}
    float *input;
    float *output;
};
template<int NumberElements>    
struct C 
{
    C() : output(new float[NumberElements]){}//allocate output
    ~C(){delete[] output;}
    float *input;
    float *output;
};

//Container
template<typename...Arg>
struct HeterogenousContainer
{
    HeterogenousContainer();//Do something to setup the sequence
    std::tuple<Arg...> elements;

};

How can I properly allocate memory (via new/malloc) for output vectors, and set up input pointers to preceding output vectors? For example, I write next code:

HeterogenousContainer<A<5>, B<7>, C<9>> sequence;

I want that input from first member of sequence to be nullptr, input from second - points to output from first, etc. How to implement it correctly?

Community
  • 1
  • 1
gorill
  • 1,623
  • 3
  • 20
  • 29
  • Why do you want to construct a linked list inside a container that can provide every kind of (including random) access to its elements? – dyp Jul 29 '13 at 21:56
  • It is assumed that access to the elements will be sequental - strongly from A to C in example above – gorill Jul 29 '13 at 22:01

2 Answers2

3

Firstly, don't mess around with manual (de)allocation if you can avoid it. For a simple array, you can:

#include <array>
#include <tuple>

template<int NumberElements>
struct A 
{
    float *input;
    std::array<float, NumberElements> output;
};

You just need to recurse down the tuple, and specialize for the termination case. I'm doing it backwards since the first element is your special case.

namespace detail {
    template <int N> struct Connector;

    template <>
    struct Connector<0> {
        template <typename... TL> static void connect(std::tuple<TL...> &t) {
            std::get<0>(t).input = nullptr;
        }
    };

    template <int N> struct Connector {
        template <typename... TL> static void connect(std::tuple<TL...> &t) {
            std::get<N>(t).input = &std::get<N-1>(t).output.front();
            Connector<N-1>::connect(t);
        }
    };

    template <typename... TL> void connect(std::tuple<TL...> &t) {
        Connector<sizeof...(TL)-1>::connect(t);
    }
}

and use it something like this:

template <typename... Arg> struct HeterogenousContainer {
    std::tuple<Arg...> elements;

    HeterogenousContainer() { detail::connect(elements); }
};
Useless
  • 64,155
  • 6
  • 88
  • 132
  • Is it possible to align std::array by 16-byte for SSE-intrinsics usage? – gorill Jul 29 '13 at 22:08
  • 1
    Not directly. Either wrap your `std::array` in something that handles the alignment (or use compiler-specific attributes - you haven't said which compiler you're using), or switch to `std::unique_ptr` and a suitable allocator. That's really a separate question though. – Useless Jul 30 '13 at 05:50
1

Inspired by Useless' answer (no pun intended), I came up with this:

template<typename...Arg>
struct HeterogenousContainer
{
    std::tuple<Arg...> elements;

    void init(std::integral_constant<std::size_t, 0>)
    {
        std::get<0>(elements).input = nullptr;
    }

    template < std::size_t index = sizeof...(Arg)-1 >
    void init(std::integral_constant<std::size_t, index> = {})
    {
        std::get<index>(elements).input = std::get<index-1>(elements).output;
        init(std::integral_constant<std::size_t, index-1>{});
    }

    HeterogenousContainer()
        : elements{}
    {
        init();
    }
};
Community
  • 1
  • 1
dyp
  • 38,334
  • 13
  • 112
  • 177
  • "in" and "out" means "input" and "output" from my example? – gorill Jul 29 '13 at 22:26
  • @gorill Oops, yes. I simplified your example for a compilation test ;) – dyp Jul 29 '13 at 23:18
  • Is there a difference between syntax - elements{} and elements()? Both compiled well – gorill Jul 30 '13 at 09:23
  • 1
    @gorill In this case, no. Both will call the default-ctor of `tuple`, which will value-initialize its elements. This also means that strictly speaking, the line `std::get<0>(elements).input = nullptr;` shouldn't be necessary. – dyp Jul 30 '13 at 12:53