c++0x variadic templates

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...>::eek: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...>::eek:perator()(I...), bad number of args");
// get element
return data_[position(i...)];
}


private:
// data
T data_[size];
};
 
V

Valeriu Catina

Valeriu said:
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...>::eek: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...>::eek:perator()(I...), bad number of args");
// get element
return data_[position(i...)];
}


private:
// data
T data_[size];
};


Got it. Added:

template<typename I> struct hlp_get_index;

#define DECL_GET_INDEX(T) \
template<> struct hlp_get_index<T> { static inline T get(T i) { return
i; } };

// specialize hlp_get_index for valid index types
DECL_GET_INDEX(char)
DECL_GET_INDEX(unsigned char)
DECL_GET_INDEX(short int)
DECL_GET_INDEX(unsigned short int)
DECL_GET_INDEX(int)
DECL_GET_INDEX(unsigned int)
DECL_GET_INDEX(long)
DECL_GET_INDEX(unsigned long int)

#undef DECL_GET_INDEX


and modified hlp_position to:



// 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 I1, typename...I>
static I1 get(I1 i1, I...i)
{
return hlp_get_index<I1>::get(i1)*stride +
hlp_position<N...>::get(i...);
}
};

// position helper specialization, 1-arg parameter pack
template<size_t N1> struct hlp_position<N1>
{
template<typename I>
static I get(I i)
{
return hlp_get_index<I>::get(i);
}
};

Now a call like:

TinyArray<some_type,3,2,5> a;

a(3,2,1.13,2) will generate an error message (not a very friendly one
though) related to the instantiation of the incomplete type
hlp_get_index<double>.
 
E

Erik Wikström

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

I do not know how variadic templates and concepts play together but I
imagine that in the future you will want to limit the types to those
matching the Integram (or perhaps UnsignedIntegral) concept.
 
E

Erik Wikström

I do not know how variadic templates and concepts play together but I
imagine that in the future you will want to limit the types to those
matching the Integram (or perhaps UnsignedIntegral) concept.

s/Integram/Integral/
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,766
Messages
2,569,569
Members
45,043
Latest member
CannalabsCBDReview

Latest Threads

Top