Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
184 views
in Technique[技术] by (71.8m points)

c++ - c++11 variadic programming, how to define a tower of vectors

How (if possible) can I use c++11 variadic programming to define a series of vector's in a function body, (or in other words, a sequence of N-dimensional arrays with decreasing N's until 0), like the variables below?

vector<vector<vector<int>>> v<3>;
vector<vector<int>> v<2>;
vector<int> v<1>;
int v<0>;

What I imagined is something like:

#include <iostream>
#include <vector>
using namespace std;

template<int ...> struct seq {};
template<int N, int ...S> struct gens : gens<N-1, N-1, S...> {};
template<int ...S> struct gens<0, S...>{ typedef seq<S...> type; };

template<int ...S>
void f(seq<S...>) {
  //how do I write definitions of v<N> here?
  vector<vector<...(N layers)<vector<int> ...> v<N>;     //??how-to, not valid c++
  vector<vector<...(N -1 layers)<vector<int> ...> v<N-1>;//??how-to, not valid c++
  //...
  vector<int> v<1>;  //??how-to, not valid c++
  int v<0>;          //??how-to, not valid c++

  //...
}

int main() {
  f(typename gens<3>);
  return 0;
}

Also, would this be easier in c++14?

Thanks,

--EDIT--

Just to clarify, what I mean by "a tower of vectors" is best described by a N-tuple (v_1, v_2, ..., v_N), where N is an integral template parameter. v_1 is of vector, v_2 is of vector>, and so on.

--EDIT2--

So far, quantdev and R's answers have successfully solved the problem of defining a N-tuple for any fixed N, like 3, but cannot generate a tuple for an unspecified N. In addition to the functionality in the answers, I need a function which can be used like gen_tower<N> that returns tuple(v1,v2,...,vN).

Consider the example of using variadic programming to compute factorials. I need a function to compute factorial factorial<N>() for any N, in addition to the ability to write out any specific expression <1*2*3> manually. (That was the reason why I asked about variadic programming and whether c++14 will make it easier.)

P.S.

Purely out of personal interests, I want this sequence to hopefully implement a generic function that can read a N-dimensional array from file. I don't know exactly how yet, but I think at step one, I should be able to define the final N-dimensional array, and the intermediate k-dimensional arrays for k from N-1 to 1. I can read 2-dimensional arrays, and 3-dimensionals. But it would be nice to be able to read arrays of any dimensions.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

There is no need for variadics, a recursive typedef is sufficient to generate those types at compile time.


How is is implemented ?

1) Provide a template with 2 arguments : the vector elements type (T), and the desired dimension of the structure (size_t N). Declare a typedef type : it will be based on the declaration of type for the template instantiated with the depth N-1, hence the recursion.

template<typename T, size_t N>
struct VectorGenerator
{
    typedef std::vector< typename VectorGenerator<T, N-1>::type > type;
};

2) Provide a termination case for ending the recursion, here a specialization of our template with the dimension 0, declaring the type of a usual std::vector<T>.

template<typename T>
struct VectorGenerator<T, 0>
{
    typedef std::vector<T> type;
};

How to use it ?

We can now declare a vector v of type VectorGenerator<T, N>::type :

VectorGenerator<double, 4>::type v; // v as a depth of 4 and handle double

But it's not very readable or convenient, and pretty verbose. Let's introduce new names for our types.

This is the perfect case for template aliasing, with the (C++11) using keyword for aliasing. We have 2 different ways of aliasing :

1) Declare an alias for a particular dimension and type, here we call it V3 for N=3 and T=double :

using V3 = VectorGenerator<double, 3>::type;  // Alias

V3 v;                                         // Use the alias

Or,

2) Declare a template alias for a particular type, leaving the dimension as a template parameter:

template <size_t N> 
using V = typename VectorGenerator<double, N>::type;  // Alias

V<3> v;                                              // Use the Alias

Final code sample:

template<typename T, size_t N>
struct VectorGenerator
{
    typedef std::vector< typename VectorGenerator<T, N-1>::type > type;
};

template<typename T>
struct VectorGenerator<T, 0>
{
    typedef std::vector<T> type;
};

// Alias for V3, V2 ... usage
using V3 = VectorGenerator<double, 3>::type;
using V2 = VectorGenerator<double, 2>::type;

// Alias for V <k> usage
template <size_t N> 
using V = typename VectorGenerator<double, N>::type;

int main() {

    V<3> v3;
    V<2> v2;
    v3.push_back(v2);
    return 0;
}

Notes :

  • Consider the Boost MultiDimensional Array library.
  • I am not sure of what your final goal is, but this might well be an overkill.
  • As for your second edit, declaring a tuple with multiple vectors of different dimensions is now easy :

Example:

auto tower = std::tuple<V<1>, V<2>, V<3>>(v1, v2, v3);

For the generic tuple generation of multiple "towers", @mpark gave a working C++14 solution, I adapt it here to my code sample:

template <typename T>
struct identity { using type = T; };

// Generate a tuple of towers by mapping index_sequence over gen_tower.
template <typename T, std::size_t... Is>
std::tuple<VectorGenerator<T, Is>...> gen_towers_impl(std::integer_sequence<Is...>);

// Make an index_sequence for N and use gen_towers_impl.
template <typename T, std::size_t N>
struct gen_towers
    : identity<decltype(gen_towers_impl<T>(std::make_index_sequence<N>()))> {};

// Convenience type aliases
template <typename T, std::size_t N>
using gen_towers_t = typename gen_towers<T, N>::type;

You will need -std=c++1y to compile it (and include <utility> and <tuple> headers)

See a working example here.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...