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...} {
}
};
Related
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 ...
};
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...} {}
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);
}
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.
This question already has answers here:
How does this template magic determine array parameter size?
(3 answers)
Closed 6 years ago.
template<typename T, size_t n>
size_t array_size(const T (&)[n])
{
return n;
}
The part that I don't get is the parameters for this template function. What happens with the array when I pass it through there that gives n as the number of elements in the array?
Well, first you have to understand that trying to get a value out of an array can give you a pointer to its first element:
int a[] = {1, 2, 3};
int *ap = a; // a pointer, size is lost
int (&ar)[3] = a; // a reference to the array, size is not lost
References refer to objects using their exact type or their base-class type. The key is that the template takes arrays by reference. Arrays (not references to them) as parameters do not exist in C++. If you give a parameter an array type, it will be a pointer instead. So using a reference is necessary when we want to know the size of the passed array. The size and the element type are automatically deduced, as is generally the case for function templates. The following template
template<typename T, size_t n>
size_t array_size(const T (&)[n]) {
return n;
}
Called with our previously defined array a will implicitly instantiate the following function:
size_t array_size(const int (&)[3]) {
return 3;
}
Which can be used like this:
size_t size_of_a = array_size(a);
There's a variation I made up some time ago [Edit: turns out someone already had that same idea here] which can determine a value at compile time. Instead of returning the value directly, it gives the template a return type depending on n:
template<typename T, size_t n>
char (& array_size(const T (&)[n]) )[n];
You say if the array has n elements, the return type is a reference to an array having size n and element type char. Now, you can get a compile-time determined size of the passed array:
size_t size_of_a = sizeof(array_size(a));
Because an array of char having n elements has sizeof n, that will give you the number of elements in the given array too. At compile time, so you can do
int havingSameSize[sizeof(array_size(a))];
Because the function never is actually called, it doesn't need to be defined, so it doesn't have a body. Hope I could clear the matter up a little bit.
Think of it this way, suppose you had a bunch of functions:
// Note that you don't need to name the array, since you don't
// actually reference the parameter at all.
size_t array_size(const int (&)[1])
{
return 1;
}
size_t array_size(const int (&)[2])
{
return 2;
}
size_t array_size(const int (&)[3])
{
return 3;
}
// etc...
Now when you call this, which function gets called?
int a[2];
array_size(a);
Now if you templatize the arraysize, you get:
template <int n>
size_t array_size(const int (&)[n])
{
return n;
}
The compiler will attempt to instantiate a version of array_size that matches whatever parameter you call it with. So if you call it with an array of 10 ints, it will instantiate array_size with n=10.
Next, just templatize the type, so you can call it with more than just int arrays:
template <typename T, int n>
size_t array_size(const T (&)[n])
{
return n;
}
And you're done.
Edit: A note about the (&)
The parentheses are needed around the & to differentiate between array of int references (illegal) and reference to array of ints (what you want). Since the precedence of [] is higher than &, if you have the declaration:
const int &a[1];
because of operator precedence, you end up with a one-element array of const references to int. If you want the & applied first, you need to force that with parentheses:
const int (&a)[1];
Now the you have a const reference to a one element array of ints. In the function parameter list, you don't need to specify the name of a parameter if you don't use it, so you can drop the name, but keep the parentheses:
size_t array_size(const int (&)[1])
Nothing happens to the array. It's an unused parameter that is used to resolve the signature of the template function.
It also cannot be used as a template argument, but that's a separate nit.
A little weird way to get the result as compile-time const for those of us who don't have "constexpr":
#include <iostream>
namespace
{
template <size_t V>
struct helper
{
enum
{
value = V
};
};
template<typename T, size_t Size>
auto get_size(T(&)[Size]) -> helper < Size >
{
return helper < Size >() ;
}
template<typename T>
struct get_value
{
enum
{
value = T::value
};
};
}
int main()
{
std::cout << get_value<decltype(get_size("Foo bar baz"))>::value;
}