Pass N-D array by reference to variadic function - c++

I'd like to make the function multi_dimensional accept a multidimensional array by reference.
Can this be done with a variation of the syntax below which works for three_dimensional?
#include <utility>
// this works, but number of dimensions must be known (not variadic)
template <size_t x, size_t y, size_t z>
void three_dimensional(int (&nd_array)[x][y][z]) {}
// error: parameter packs not expanded with ‘...’
template <size_t... dims>
void multi_dimensional(int (&nd_array)[dims]...) {}
int main() {
int array[2][3][2] = {
{ {0,1}, {2,3}, {4,5} },
{ {6,7}, {8,9}, {10,11} }
};
three_dimensional(array); // OK
// multi_dimensional(array); // error: no matching function
return 0;
}

The main problem is that you cannot make the number of array dimensions itself variadic. So whichever way you go, you will almost certainly need a recursive approach of some sort to deal with the individual array layers. What exactly such approach should look like will mainly depend on what exactly you're planning to do with the array once it's been given to you.
If really all you want is a function that can be given any multi-dimensional array, then just write a function that can be given anything but only exists as long as that anything is an array:
template <typename T>
std::enable_if_t<std::is_array_v<T>> multi_dimensional(T& a)
{
constexpr int dimensions = std::rank_v<T>;
// ...
}
However, this by itself will most likely not get you very far. To actually do anything meaningful with the array you've been given, you will most likely need some recursive walking through subarrays. Unless you really just want to look at the topmost layer of the structure.
Another approach is to use a recursive template to peel back the individual array levels, for example:
// we've reached the bottom
template <typename T, int N>
void multi_dimensional(T (&a)[N])
{
// ...
}
// this matches any array with more than one dimension
template <typename T, int N, int M>
void multi_dimensional(T (&a)[N][M])
{
// peel off one dimension, invoke function for each element on next layer
for (int i = 0; i < N; ++i)
multi_dimensional(a[i]);
}
I would, however, suggest to at least consider using std::array<> instead of raw arrays as the syntax and special behavior of raw arrays tends to turn everything into a confusing mess in no time. In general, it might be worth to implement your own multi-dimensional array type, like an NDArray<int, 2, 3, 2> which internally works with a flattened representation and just maps multi-dimensional indices to a linear index. One advantage of this approach (besides the cleaner syntax) would be that you can easily change the mapping, e.g., to switch from row-major to column-major layout, e.g., for performance optimization…
To implement a general nD array with static dimensions, I would introduce a helper class to encapsulate the recursive computation of a linear index from an nD index:
template <std::size_t... D>
struct row_major;
template <std::size_t D_n>
struct row_major<D_n>
{
static constexpr std::size_t SIZE = D_n;
std::size_t operator ()(std::size_t i_n) const
{
return i_n;
}
};
template <std::size_t D_1, std::size_t... D_n>
struct row_major<D_1, D_n...> : private row_major<D_n...>
{
static constexpr std::size_t SIZE = D_1 * row_major<D_n...>::SIZE;
template <typename... Tail>
std::size_t operator ()(std::size_t i_1, Tail&&... tail) const
{
return i_1 + D_1 * row_major<D_n...>::operator ()(std::forward<Tail>(tail)...);
}
};
And then:
template <typename T, std::size_t... D>
class NDArray
{
using memory_layout_t = row_major<D...>;
T data[memory_layout_t::SIZE];
public:
template <typename... Args>
T& operator ()(Args&&... args)
{
memory_layout_t memory_layout;
return data[memory_layout(std::forward<Args>(args)...)];
}
};
NDArray<int, 2, 3, 5> arr;
int main()
{
int x = arr(1, 2, 3);
}

Related

Pointer level return type based on template function arguments

For a tensor class I would like to have a template creating functions like
double* mat(int size1);
double** mat(int size1, int size2);
double*** mat(int size1, int size2, int size3);
i.e. the pointer level of the return type depends on the number of inputs.
The base case would just be
double* mat(int size1){
return new double[size1];
}
I thought a variadic template could somehow do the trick
template<typename T, typename... size_args>
T* mat(int size1, size_args... sizes) {
T* m = new T[size1];
for(int j = 0; j < size1; j++){
m[j] = mat(sizes...);
}
return m;
}
Deducing the template parameter in m[j] = mat(sizes...); doesn't seem to work though when more than one recursive call would be necessary, as in
auto p = mat<double**>(2,3,4);
but how could I provide the parameter in the recursive call? The correct parameter would be one pointer level below T, that is mat<double**> for T=double***.
I feel like I miss something important about templates here.
you cannot declare m and return type as T* since it is not in multiple dimension.
template<typename T, typename size_type>
auto mat(size_type size){return new T[size];}
template<typename T, typename size_type, typename... size_types>
auto mat(size_type size, size_types... sizes){
using inner_type = decltype(mat<T>(sizes...));
inner_type* m = new inner_type[size];
for(int j = 0; j < size; j++){
m[j] = mat<T>(sizes...);
}
return m;
}
For lack of a better name, I'm going to call the template meta-function that creates a pointer type with the desired "depth" and type, a "recursive pointer". Such a thing can be implemented like so:
template <size_t N, class T>
struct RecursivePtr
{
using type = RecursivePtr<N-1, T>::type*;
};
template <class T>
struct RecursivePtr<0, T>
{
using type = T;
};
template <size_t N, class T>
using recursive_ptr_t = RecursivePtr<N, T>::type;
recursive_ptr_t<4, int> for example creates int****. So in your case you can go ahead and implement the mat function as:
template <class... Args>
auto mat(Args... args)
{
recursive_ptr_t<sizeof...(Args), double> m;
// Runtime allocate your Npointer here.
return m;
}
Demo
Some ideas to give added type safety to the mat function are:
Add static asserts that all provided types are sizes
Statically assert that at least one size has been provided
Minor note, when I say runtime allocate your Npointer above, I mean something like:
template <class T, class... Sz>
void alloc_ar(T* &ar, size_t s1, Sz... ss)
{
if constexpr (sizeof...(Sz))
{
ar = new T[s1];
for (size_t i(0); i < s1; i++)
alloc_ar(ar[i], ss...);
}
else
{
ar = new T[s1];
}
}
Made a Demo where I show the allocation, but not the deallocation.
A reasonable alternative to this is to allocate one contiguous chunk of memory (sized == multiplicate of dimensions) and use the multidimensional pointer to the beginning of that chunk for syntactic sugar when accessing it. This also provides an easier way to deallocate memory.
A second alternative is to use nested vector of vectors (of vectors of vectors...) with the same generation mechanics as the Npointer. This eliminates the need for manual memory management and would probably force you to wrap the whole thing in a more expressive class:
template <class... Dims>
class mat
{
template <size_t N, class T>
struct RecursivePtr
{
using type = std::vector<RecursivePtr<N-1, T>::type>;
};
template <class T>
struct RecursivePtr<0, T>
{
using type = T;
};
// This is the replacement to double***
// translates to vector<vector<vector<double>>>
RecursivePtr<N, T>::type _data;
public:
// construction, argument deduction etc ...
};

Multidimensional array with dynamic extents (without raw pointers)

The rationale behind said array is to emulate a 2D/3D pixel matrix.
After doing a fair amount of research and reading, I reckon Boost.MultiArray might come in handy for this. However, I still have to create a neat wrapper on top of it to allow for less verbose coding.
Ultimately, what I want to achieve is the following:
PixMat<u8, 3, {2, 4, 3}> pixMat;
or
PixMat<u8, 3> pixMat(2,3,4);
, which would basically create a 2x4x3 matrix of u8 values.
What I've come up with so far is:
template <typename T, int Dims>
class PixMat {
public:
typedef typename boost::multi_array<T, Dims> PixMatType;
typedef typename PixMatType::index PixMatTypeIdx;
PixMat(int dim1Ext, int dim2Ext) : pixMat(PixMatType(boost::extents[dim1Ext][dim2Ext])) {}
PixMat(int dim1Ext, int dim2Ext, int dim3Ext) : pixMat(PixMatType(boost::extents[dim1Ext][dim2Ext][dim3Ext])) {}
private:
PixMatType pixMat;
};
template <typename T>
class Pix2DMat : PixMat<T, 2> {
public:
Pix2DMat(int dim1Ext, int dim2Ext) : PixMat<DataType, 2>(dim1Ext, dim2Ext) {}
};
template <typename T>
class Pix3DMat : PixMat<T, 3> {
public:
Pix3DMat(int dim1Ext, int dim2Ext, int dim3Ext) : PixMat<DataType, 3>(dim1Ext, dim2Ext, dim3Ext) {}
};
I'm not too keen on this solution. Normally, the matrix won't be either 2D or 3D, but still, I'd like a more generic solution.
Questions:
Is there a way to provide the extents of the dimensions as template arguments as well instead of via C-TOR?
Is there a better way than inheritance to achieve this e.g. template specialization, variadic templates? But then how to deal with not duplicating the typedefs for boost all over the place?
Here are a few techniques that I think you could use:
Variadic constructor argument
Rather than having a separate constructor for each possible dimension, you could use variadic argument techniques to create a generic N-dimensional constructor. Something that is your friend here: boost::extents is not required for the constructor argument, but instead anything that meets the requirements of a Collection. One example is just a plain STL or boost array:
template <typename... DimSizes>
PixMat(DimSizes&&... sizes)
: pixMat(boost::array<std::size_t, sizeof...(DimSizes)>{{ static_cast<size_t>(sizes)...}}) {
}
This isn't the most polished implementation; in particular it doesn't place much of a requirement on DimSizes, which should really all be the same unsigned integer type (see this question for possible improvements). Also (for simplicity) perfect forwarding isn't implemented, but that probably just requires wrapping sizes with std::forward<DimSizes>(sizes) in the constructor.
You can consult this stackoverflow post for possible alternative implementations.
Static assertion / SFINAE
Your template base class has a 2D constructor and 3D constructor --- or if you follow the above, a template N-dimensional constructor --- regardless of the value of the actual template parameter. You could use static assertion or SFINAE so that only the the Dims-D dimensional constructor is compilable. This will convert run-time bugs into compilation errors:
template <typename... DimSizes>
PixMat(DimSizes&&... sizes)
: pixMat(boost::array<std::size_t, sizeof...(DimSizes)>{{ static_cast<size_t>(sizes)...}}) {
static_assert(sizeof...(DimSizes) == Dims);
}
Dimension sizes as templates
I think this is a possible though completely orthogonal solution. It's implementation would follow from the above without too much work, but to make this interoperable with the constructor argument based solution would require a lot of hard work (i.e. to make them part of the same class or class hierarchy).
Other libraries
You might want to take a look at Eigen, for example. It does a lot of the aforementioned hard work.
If I understood you correctly, you want compile-time dimension, but run-time extents.
I would use a design like this:
template <typename T,std::size_t Dim>
class mdvector
{
private:
std::vector<T> Data;
std::array<std::size_t,Dim> Extents;
private:
std::size_t Offset(std::size_t const Sum) const
{ return Sum; }
template <typename... IndexType>
std::size_t Offset(std::size_t const Sum,std::size_t const Index,IndexType const... Indices) const
{ return Offset(Sum*Extents[Dim-sizeof...(Indices)-1u]+Index,Indices...); }
public:
template <typename... IndexType,typename= std::enable_if_t<sizeof...(IndexType)==Dim>>
mdvector(IndexType const... Indices):
Data((... * Indices)), // c++17 fold expression
Extents{Indices...} {}
template <typename... IndexType,typename= std::enable_if_t<sizeof...(IndexType)==Dim>>
T const &operator()(IndexType const... Indices) const
{ return Data[Offset(0u,Indices...)]; }
template <typename... IndexType,typename= std::enable_if_t<sizeof...(IndexType)==Dim>>
T &operator()(IndexType const... Indices)
{ return Data[Offset(0u,Indices...)]; }
};
The data are stored in a std::vector while the extents make up an std::array.
Since I assume you want multi-dimensional access I have used a given mapping via the recursive function Offset. You have quite freedom in this regard.
Here's an example of use:
int main()
{
mdvector<double,3u> myvec(2u,3u,4u);
for (int i= 0; i<2; ++i)
for (int j= 0; j<3; ++j)
for (int k= 0; k<4; ++k)
myvec(i,j,k)= i;
for (int k= 0; k<4; ++k)
{
for (int i= 0; i<2; ++i)
{
for (int j= 0; j<3; ++j)
std::cout << myvec(i,j,k) << "\t";
std::cout << "\n";
}
std::cout << "\n";
}
}
Since the extents are dynamic, you can change them at runtime, for example:
template <typename T,std::size_t Dim>
template <typename... IndexType,typename= std::enable_if_t<sizeof...(IndexType)==Dim>>
void mdvector<T,Dim>::Resize(IndexType const... Indices)
{ Data.resize((... * Indices)); Extents= {Indices...}; }
If, on the other hand, you prefer the extents to be template parameters, they should stay fixed at runtime. In that case, you would include them in the class declaration:
template <typename T,std::size_t... Indices>
class mdvector { /* ... */};
but the implementation would be pretty much the same. Note that specifying the dimension would be unnecessary since you can get it with sizeof...(Indices).
The fold expression in the above code is not estrictly necessary. An equivalent outcome is achieved with:
static constexpr std::size_t Product()
{ return 1u; }
template <typename... IndexType>
static constexpr std::size_t Product(std::size_t const Index,IndexType const... Indices)
{ return Index*Product(Indices...); }
template <typename... IndexType,typename= std::enable_if_t<sizeof...(IndexType)==Dim>>
mdvector(IndexType const... Indices):
Data(Product(Indices...)), Extents{Indices...} {}

Variadic template and constructor

I was writting some class that map specific multidimensionnal array to unidimensionnal array (like 2D array N by M size is like a 1D array NM size you know, and then you can accees the cell [n, m] through [n+mN]). And it was quite boring since I must handle any multidimensional array (forced to copy/paste many times the class definition).
And I found something that could be great in my case: variadic template function.
I would like to have my constructor and accessors using variadic template so my accessors can use any number of parameters (2 for 2D array, 3 for 3D array...) and same for my constructor, since I need to save the size in each dimension (N, M, ...) and multiply then to have the size of my unidimentionnal array.
The problem is that I don't know how to do that :(
Every examples I found relies on two functions, one with one parameter of type T and another one with one parameter of type T and the Args... args).
Is it possible in one function ? Without recursion or so ?
I gave you what I've made so far:
template <typename T, typename... Args>
T returner(T v, Args... args){
return v;
}
template <typename T>
class Array{
public:
int* dim; //contain the size of each dimension
T* array; //the actual array
template<typename... Args>
Array(Args... args){
constexpr int size = sizeof...(Args);
dim = new int[size];
for(int i=0; i<size; i++)
dim[i] = returner(args...);
/*dim[0] should be equal to the first argument, dim[1]
should be equal to the second argument and so on */
}
};
int main(){
Array<int>(2,2,2); // meant to be a 3D array, 2cells in each dimension
return 0:
}
Obviously, "returner" always return the first argument and I understand why, but the only solution I see it to pass the dim array as a parameter and I would like to not do that. Is there a solution ??
PS: I could do that with classical variadic function, like in C, but it would be quite bad in performance :(
This should do what you want (if I understood correctly):
template <typename T>
class Array{
public:
int* dim; //contain the size of each dimension
T* array; //the actual array
template<typename... Args>
Array(Args... args){
constexpr int size = sizeof...(Args);
dim = new int[size]{args...};
}
};
But you'd better use std::vector instead of raw pointers - It would save you a lot of troubles:
template <typename T>
class Array{
public:
std::vector<int> dim; //contain the size of each dimension
std::vector<T> array; //the actual array
template<typename... Args>
Array(Args... args) : dim{args...} {
}
};

Programmatic initialization of a long array at compile or initialization time

Here is a code snippet illustrating my question:
const float D = 0.1F;
const float A[4] = {sin(0*D), sin(1*D), sin(2*D), sin(3*D)};
Imagine that global array A is much longer and you don't want to do all of this repetitive typing. Is there a shorter way to initialize array A at compile or initialization time, i.e. without having to write initialization function and call it somewhere in my program?
You could initialize A during dynamic initialization time as follows:
const float *init_a(float x_)
{
static float data[4];
for(unsigned i=0; i<4; ++i)
data[i]=sin(i*x_);
return data;
}
const float D=0.1f;
const float *A=init_a(D);
You may use code generator to generate initialization code. That is, write program that will write your initialization code for you. You may actually calculate values at generation-time.
Remember that C++ allows placing , after last element. It's also isn't necessary to specify array size. These two things should ease writing of generator.
This simple python code should work well:
from math import sin
print('const float A[', N, '] = {')
for i in range(N):
print('\t', sin(i*D), ',', sep='')
print('};')
Ok I just realized this doesn't actually answer the question, because it specifies "without having to write initialization function and call it somewhere in my program?" But I can't think of a convenient alternative.
template<size_t N>
std::array<float, N> GetLookupTable(float d)
{
std::array<float, N> table;
// .. populate table here
return table;
}
// a global somewhere
(static?) const auto Table_10x5 = GetLookupTable<10>(5.0f);
This first part is obsolete in C++14, but not long:
template<unsigned...>struct indexes { using type=indexes; };
template<unsigned Max, unsigned...Is>struct make_indexes:make_indexes<Max-1,Max-1,Is...>{};
template<unsigned...Is>struct make_indexes<0,Is...>:indexes<Is...>{};
template<unsigned Max>using make_indexes_t=typename make_indexes<Max>::type;
this is some template meta programming that lets us create and pass around bundles of indexes.
Then some code to generate the array:
namespace details {
template<std::size_t N, unsigned...Is>
std::array<float, N> poly_sin(float src, indexes<Is...>) {
return { (Is*sin(src))... };
}
}
template<std::size_t N>
std::array<float, N> poly_sin(float src) {
return details::poly_sin<N>( src, make_indexes_t<N>{} );
}
The first method takes indexes<Is...> and we plan that Is... is 0, 1, 2, ..., N-1. It then expands the parameter pack into a std::array of the right size.
make_indexes_t<N>{} expands (at compile time) to indexes<0, 1, 2, ..., N-1>{}, which is then passed to details::poly_sin, which then can deduce the Is... and use them within itself.
And point of use:
const float D = 0.1F;
const auto A = poly_sin<4>(D);
if you had a constexpr sin function, you could even make poly_sin a constexpr function and have it basically guaranteed to be evaluated at compile time.
If this is the case, make D constexpr and same with the two poly_sin functions.
As written, this occurs at dynamic initialization time.
While it appears that the array is copied twice, RVO elision means that any decent compiler will directly construct it in A.
If you want to be able to do this in general, first start with the above indexes code. Then add this:
template<class Sig>using result_of_t=typename std::result_of<Sig>::type;
namespace details {
template<std::size_t N, class F, unsigned... Is>
std::array< result_of_t< F(unsigned) >, N >
make_array( F&& f, indexes<Is...> ) {
return { f( Is )... };
}
}
template<std::size_t N, class F>
std::array< result_of_t< F(unsigned) >, N >
make_array( F&& f ) {
return details::make_array( std::forward<F>(f), make_indexes_t<N>{} );
}
const auto A = make_array<4>( [](unsigned i){ return float(i*sin(D)); } );
which uses a lambda to pass in the code that is repeated to build the array. Sadly, lambdas are not by default constexpr so you cannot do it at compile time.
You could either use boost.preprocessor and in particular the BOOST_PP_ENUM macro, like the example below:
#include <iostream>
#include <cmath>
#include <boost/preprocessor/repetition/enum.hpp>
#define SIZE 4
#define D 0.1
#define ORDER(z, n, text) std::sin(n * D)
double const A[SIZE] = { BOOST_PP_ENUM(SIZE, ORDER, ~) };
int main() {
for(auto i : A) std::cout << i << std::endl;
}
Or, you could use std::array instead of raw arrays, and via use of template meta-programming to generate a std::array at compile time. like the example below:
template<typename T, typename F, int SIZE, int... N>
constexpr std::array<T, SIZE>
genarray(F f) {
return std::array<T, SIZE>{{ f(N)... }};
}
template<typename T, typename F, int SIZE, int...> struct recursive_gen;
template<typename T, typename F, int SIZE, int... Args>
struct recursive_gen<T, F, SIZE, 0, Args...> {
static constexpr std::array<T, SIZE> generate(F f) {
return genarray<T, F, SIZE, 0, Args...>(f);
}
};
template<typename T, typename F, int SIZE, int N, int... Args>
struct recursive_gen<T, F, SIZE, N, Args...> {
static constexpr std::array<T, SIZE> generate(F f) {
return recursive_gen<T, F, SIZE, N - 1, N, Args...>::generate(f);
}
};
template<typename T, int SIZE>
struct array_generator {
template<typename F>
static constexpr std::array<T, SIZE> generate(F f) {
return recursive_gen<T, F, SIZE, SIZE - 1>::generate(f);
}
};
std::array<double, 4> const A = array_generator<double, 4>::generate([](int i) { return std::sin(0.1 * i);});
std::array<double, 4> const B = array_generator<double, 4>::generate([](int i) { return std::cos(0.1 * i);});
constexpr int fun(int i) { return 2 * i; }
constexpr std::array<int, 4> const C = array_generator<int, 4>::generate(fun); // generation during compile time
LIVE DEMO
Note however, that in order for generation to take place at compile time input function in array_generator must be constexpr. This is not the case for trigonometric functions (i.e., they are not constexpr). Thus initialization of arrays A and B will take place at initialization time, whereas generation of array C will take place at compile time.
Imagine that global array A is much longer and you don't want to do all of this repetitive typing. Is there a shorter way to initialize array A at compile or initialization time
Create a generator and pass it through std::generate_n() (or plain std::generate()).
#include <algorithm>
#include <array>
#include <cmath>
template <typename Value_t>
struct SinGenerator{
SinGenerator(std::size_t start = 0, Value_t counter_scalar = 1)
: index{start},
scalar{counter_scalar} {
}
Value_t operator()() {
return sin(index++ * scalar);
}
std::size_t index;
Value_t scalar;
};
template <typename Value_t, std::size_t Size>
std::array<Value_t, Size> init_table(const std::size_t start,
const Value_t counter_scalar) {
std::array<Value_t, Size> arr;
SinGenerator<Value_t> gen(start, counter_scalar);
std::generate(arr.begin(), arr.end(), gen);
return arr;
}
const auto kSinTable(init_table<float, 10>(0, 0.1f));
In case you A array will always stays the same and is very big, you can always write a short script, which calculates every value in array and output could be used in source code to have static initialization.
In case formula is simple, even MS Excel could be used to generate that kind of static initialization data.

Template Specifications for constructor of n-dimensional array

I am implementing an n-dimensional array class which is a template as follows (Note that the data is stored in a linear array whose length is the product of all the dimensions):
template< class valType, int rank >
class NDimensionalArray
{
public:
private:
valType* m_data;
int* m_dimensions;
int m_rank;
};
So the idea is that a user (me) can specify an array of rank 2, and of a certain dimension, ie:
NDimensionalArray<double,2> matrix(10,10);
Now the difficulty is in specializing constructors for 1->n dimensions, each constructor takes n parameters where n is the rank of the array. Now I thought of using a valarray like is used in printf(), however with this defining a 1-dimensional array with 2 dimensions ie:
NDimensionalArray<double,1> matrix(10,10);
would be perfectly acceptable behavior. Is there some neat trick I can use to let the compiler do the repetition? Realistically so long as I know the rank, and have the length of each dimension the constructor can be generic:
{
int nElements = m_dimensions[0];
for ( int i=1 ; i<m_rank ; ++i )
nElements *= m_dimensions[i];
m_data = new valType[nElements];
}
Edit: Note that a similar operation will be needed for accessors.
Also I have considered the option of a constructor which looks like:
NDimensionalArray( const NDimensionalArray<int,1>& dimensions );
Which could be used like:
NDimensionalArray<int,1> dimVec(2); // Need a specification for 1-dimensional arrays.
dimVec(0) = 10;
dimVec(1) = 10;
NDimensionalArray<double,2> matrix(dimVec);
This would be a viable solution, but its ugly compared to the use I would like. Also accessing multi-dimensional arrays would become a serious pain, and seriously slow having to construct a dimension vector for each access.
Okay, I've played with this for a while. Here's some template metaprogramming hackery that does something close to what you want. It lets you specify all dimensions inline, it doesn't do any dynamic memory allocation or other such things. In addition, with a good C++ compiler (I tested with VC++ /O2 option), the code will be fully inlined, with no copies done (in fact, for me it inlined the whole NDimensionalArray constructor at the point of the call). It will typecheck completely at compile-time, and won't let you pass too few or too many dimensions. And it can be reused for indexers. Here goes:
template<class T, int N>
class value_pack : private value_pack<T, N-1>
{
public:
enum { size = N };
value_pack(value_pack<T, N-1> head, const T& tail)
: value_pack<T, N-1>(head)
, value(tail)
{
}
value_pack<T, N+1> operator() (const T& tail) const
{
return value_pack<T, N+1>(*this, tail);
}
template<int I>
const T& get() const
{
return this->value_pack<T, I+1>::value;
}
protected:
const T value;
};
template<class T>
struct value_pack<T, 0>
{
};
struct
{
template <class T>
value_pack<T, 1> operator() (const T& tail) const
{
return value_pack<T, 1>(value_pack<T, 0>(), tail);
}
} const values;
template <class ValType, int Rank>
struct NDimensionalArray
{
NDimensionalArray(value_pack<ValType, Rank> values)
{
// ...
}
};
int main()
{
NDimensionalArray<int, 3> a(values(1)(2)(3));
}
I think the best solution is to take a vector of ints and let the constructor validate it against the template parameter 'rank'.
NDimensionalArray matrix(std::vector<int> matrixDimensions)
{
if (matrixDimensions.size() != rank)
{
throw SomeException();
}
...
}
I don't think any compiler trick can be an alternative here. (Except perhps using macros, if you can think of something, although that wouldn't be a compiler trick strictly speaking.)
Not a direct answer, but check out the blitz library.
There's no good way to do it in C++ as currently standardized. In C++0x, you'll be able to use template parameter packs to approximate (I think I've got the syntax right, but not sure about expansion in requires):
template <class ValType, int Rank>
struct NDimensionalArray
{
template <class... Args>
requires std::SameType<Args, ValType>... && std::True<sizeof...(Args) == Rank>
NDimensionalArray(Args... values)
{
...
}
};
You could take a std::tr1::array. Hmm:
#include <array>
template< class valType, int rank >
class NDimensionalArray
{
public:
NDimensionalArray(const std::tr1::array<rank>& dims);
// ...
};
NDimensionalArray<double,2> foo({10,10});
NDimensionalArray<double,2> bar({10}); // second dimension would be 0
NDimensionalArray<double,1> baz({10,10}); // compile error?
I'm not actually sure if that works! I'll run it through comeau.
Edited As per the comments, looks like this approach would look more like:
std::tr1::array<2> dims = {10, 10};
NDimensionalArray<double,2> foo(dims);
Boost has a multi-array library that uses a custom object for constructing their multidimensional array. It's a really good way to do it; I suggest you study (or better yet, use) their code.