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.
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 :
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.
You can find a similar question but dealing with std::map
at shorthand syntax for c++ map in map.
Here's one for std::vector
.
#include <iostream>
#include <vector>
template<int N, typename V>
struct NVector { typedef std::vector<typename NVector<N-1, V>::type> type; };
template<typename V>
struct NVector<1, V> { typedef std::vector<V> type; };
int main(int argc, const char *argv[]) {
NVector<1, int>::type v1(10, 0);
NVector<2, int>::type v2;
v2.push_back(v1);
NVector<3, int>::type v3;
v3.push_back(v2);
for ( int i = 0; i < 10; ++i )
{
std::cout << v3[0][0][i] << " ";
}
std::cout << std::endl;
}
Output:
0 0 0 0 0 0 0 0 0 0
Update
You can simplify the usage of these vectors with a using
declaration.
#include <iostream>
#include <vector>
template<int N, typename V>
struct NVector { typedef std::vector<typename NVector<N-1, V>::type> type; };
template<typename V>
struct NVector<1, V> { typedef std::vector<V> type; };
template<int N, typename Val>
using V = typename NVector<N, Val>::type;
int main(int argc, const char *argv[]) {
V<1, int> v1(10, 0);
V<2, int> v2;
v2.push_back(v1);
V<3, int> v3;
v3.push_back(v2);
for ( int i = 0; i < 10; ++i )
{
std::cout << v3[0][0][i] << " ";
}
std::cout << std::endl;
}
If you want to make it still simplified by assuming the value type to be int
, you can use:
#include <iostream>
#include <vector>
template<int N, typename V>
struct NVector { typedef std::vector<typename NVector<N-1, V>::type> type; };
template<typename V>
struct NVector<1, V> { typedef std::vector<V> type; };
template<int N>
using V = typename NVector<N, int>::type;
int main(int argc, const char *argv[]) {
V<1> v1(10, 0);
V<2> v2;
v2.push_back(v1);
V<3> v3;
v3.push_back(v2);
for ( int i = 0; i < 10; ++i )
{
std::cout << v3[0][0][i] << " ";
}
std::cout << std::endl;
}
I won't go into too much detail in generating a single tower since it's explained by other answers here. Here is my version of gen_tower<T, I>
which generates a tower of vectors depth I
.
For example, gen_tower_t<int, 2>
is std::vector<std::vector<T>>
.
// Useful for defining meta-functions compactly.
template <typename T>
struct identity { using type = T; };
// Forward declaration.
template <typename T, std::size_t I>
struct gen_tower;
// Convenience type alias.
template <typename T, std::size_t I>
using gen_tower_t = typename gen_tower<T, I>::type;
// Base case.
template <typename T>
struct gen_tower<T, 0> : identity<T> {};
// Wrap std::vector around tower of depth I - 1.
template <typename T, std::size_t I>
struct gen_tower : identity<std::vector<gen_tower_t<T, I - 1>>> {};
Now we can use std::index_sequence
to define N
towers.
// Generate a tuple of towers by mapping index_sequence over gen_tower.
template <typename T, std::size_t... Is>
std::tuple<gen_tower_t<T, Is>...> gen_towers_impl(std::index_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;
static_assert(std::is_same<gen_tower_t<int, 0>, int>::value, "");
static_assert(std::is_same<
gen_tower_t<int, 2>,
std::vector<std::vector<int>>>::value, "");
static_assert(std::is_same<
gen_towers_t<int, 2>,
std::tuple<int, std::vector<int>>>::value, "");
static_assert(std::is_same<
gen_towers_t<int, 3>,
std::tuple<int,
std::vector<int>,
std::vector<std::vector<int>>>>::value, "");
int main() {}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With