V
Valeriu Catina
Hi,
I've programed a TinyArray class (see below) using some C++0x features
already available in g++ 4.3.0. I would be interested in your comments
about the implementation of the ()(I...) operators. The use of variadic
templates and static assertions allow an elegant programing of these
operators but there is a major inconvenience: one is able to call them
like this:
TinyArray<double,3,3> rotation_matrix;
rotation_matrix(2,1 ) = 3; // ok
rotation_matrix(2,1.3) = 3; // still ok, no waring issued
Here is the complete code:
// ==================================================================
// ==================================================================
// length helper: get the length (N1*N2*...*Nn) of the static array
template<size_t...N> struct hlp_length;
// length helper specialization, expansion of the parameter pack
template<size_t N1, size_t...N> struct hlp_length<N1,N...>
{
static const size_t length = N1 * hlp_length<N...>::length;
};
// length helper specialization, 1-arg param pack
template<size_t N1> struct hlp_length<N1>
{
static const size_t length = N1;
};
// ==================================================================
// ==================================================================
// position helper: the position of an array element
// (i1,i2,i3,...,in) ==> i1*1 + i2*stride2 + ... + in*striden,
// stride1 == 1
template<size_t...N> struct hlp_position;
// position helper specialization, expansion of the parameter pack
template<size_t N1, size_t ...N> struct hlp_position<N1,N...>
{
// stride = N2*N3*...*Nn
static const size_t stride = hlp_length<N...>::length;
template<typename...I>
static size_t get(size_t i1, I...i)
{
return i1*stride + hlp_position<N...>::get(i...);
}
};
// position helper specialization, 1-arg parameter pack
template<size_t N1> struct hlp_position<N1>
{
static size_t get(size_t i1)
{
return i1;
}
};
// ==================================================================
// ==================================================================
// set last element
template<int N, typename T, typename UN>
inline void hlp_populate(T* data, UN un)
{
data[N-1] = un;
}
// ==================================================================
// ==================================================================
// set array elements
template<int N, typename T, typename U0, typename ...U>
inline void hlp_populate(T* data, U0 u0, U ...u)
{
// write at position zero
data[N-sizeof...(U)-1] = u0;
// recursive call, write at positions 1,2,3,...,N-1
hlp_populate<N,T,U...>(data,u...);
}
// ==================================================================
// ==================================================================
// array class
template<typename T, size_t...N> class TinyArray;
// array class specialization, expansion of the parameter pack
template<typename T, size_t N1, size_t...N>
class TinyArray<T,N1,N...>{
public:
static const size_t size = hlp_length<N1,N...>::length;
public:
// constructor
TinyArray() { }
// constructor, initialize array elements
template<typename ...U> explicit TinyArray(U ...u)
{
// check number of arguments
static_assert(size == sizeof...(U),
"TinyArray<T,N...>::TinyArray(U...), bad number of args");
// write values
hlp_populate<size,T,U...>(data_,u...);
}
// element position in data array
template<typename ...I>
size_t position(I... i) const
{
return hlp_position<N1,N...>::get(i...);
}
// element access, const
template<typename ...I> T operator()(I... i) const
{
// check the number of arguments
static_assert(sizeof...(I) == 1+sizeof...(N),
"TinyArray<T,N...>:
perator()(I...) const, bad number of args");
// get element
return data_[position(i...)];
}
// element access, non-const
template<typename ...I> T& operator()(I... i)
{
// check the number of arguments
static_assert(sizeof...(I) == 1+sizeof...(N),
"TinyArray<T,N...>:
perator()(I...), bad number of args");
// get element
return data_[position(i...)];
}
private:
// data
T data_[size];
};
I've programed a TinyArray class (see below) using some C++0x features
already available in g++ 4.3.0. I would be interested in your comments
about the implementation of the ()(I...) operators. The use of variadic
templates and static assertions allow an elegant programing of these
operators but there is a major inconvenience: one is able to call them
like this:
TinyArray<double,3,3> rotation_matrix;
rotation_matrix(2,1 ) = 3; // ok
rotation_matrix(2,1.3) = 3; // still ok, no waring issued
Here is the complete code:
// ==================================================================
// ==================================================================
// length helper: get the length (N1*N2*...*Nn) of the static array
template<size_t...N> struct hlp_length;
// length helper specialization, expansion of the parameter pack
template<size_t N1, size_t...N> struct hlp_length<N1,N...>
{
static const size_t length = N1 * hlp_length<N...>::length;
};
// length helper specialization, 1-arg param pack
template<size_t N1> struct hlp_length<N1>
{
static const size_t length = N1;
};
// ==================================================================
// ==================================================================
// position helper: the position of an array element
// (i1,i2,i3,...,in) ==> i1*1 + i2*stride2 + ... + in*striden,
// stride1 == 1
template<size_t...N> struct hlp_position;
// position helper specialization, expansion of the parameter pack
template<size_t N1, size_t ...N> struct hlp_position<N1,N...>
{
// stride = N2*N3*...*Nn
static const size_t stride = hlp_length<N...>::length;
template<typename...I>
static size_t get(size_t i1, I...i)
{
return i1*stride + hlp_position<N...>::get(i...);
}
};
// position helper specialization, 1-arg parameter pack
template<size_t N1> struct hlp_position<N1>
{
static size_t get(size_t i1)
{
return i1;
}
};
// ==================================================================
// ==================================================================
// set last element
template<int N, typename T, typename UN>
inline void hlp_populate(T* data, UN un)
{
data[N-1] = un;
}
// ==================================================================
// ==================================================================
// set array elements
template<int N, typename T, typename U0, typename ...U>
inline void hlp_populate(T* data, U0 u0, U ...u)
{
// write at position zero
data[N-sizeof...(U)-1] = u0;
// recursive call, write at positions 1,2,3,...,N-1
hlp_populate<N,T,U...>(data,u...);
}
// ==================================================================
// ==================================================================
// array class
template<typename T, size_t...N> class TinyArray;
// array class specialization, expansion of the parameter pack
template<typename T, size_t N1, size_t...N>
class TinyArray<T,N1,N...>{
public:
static const size_t size = hlp_length<N1,N...>::length;
public:
// constructor
TinyArray() { }
// constructor, initialize array elements
template<typename ...U> explicit TinyArray(U ...u)
{
// check number of arguments
static_assert(size == sizeof...(U),
"TinyArray<T,N...>::TinyArray(U...), bad number of args");
// write values
hlp_populate<size,T,U...>(data_,u...);
}
// element position in data array
template<typename ...I>
size_t position(I... i) const
{
return hlp_position<N1,N...>::get(i...);
}
// element access, const
template<typename ...I> T operator()(I... i) const
{
// check the number of arguments
static_assert(sizeof...(I) == 1+sizeof...(N),
"TinyArray<T,N...>:
// get element
return data_[position(i...)];
}
// element access, non-const
template<typename ...I> T& operator()(I... i)
{
// check the number of arguments
static_assert(sizeof...(I) == 1+sizeof...(N),
"TinyArray<T,N...>:
// get element
return data_[position(i...)];
}
private:
// data
T data_[size];
};