c++0x variadic templates

Discussion in 'C++' started by Valeriu Catina, Mar 13, 2008.

  1. 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];
    };
     
    Valeriu Catina, Mar 13, 2008
    #1
    1. Advertisements

  2. Valeriu Catina wrote:
    > 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>.
     
    Valeriu Catina, Mar 13, 2008
    #2
    1. Advertisements

  3. On 2008-03-13 16:33, Valeriu Catina wrote:
    > 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.

    --
    Erik Wikström
     
    Erik Wikström, Mar 13, 2008
    #3
  4. On 2008-03-13 18:09, Erik Wikström wrote:
    > On 2008-03-13 16:33, Valeriu Catina wrote:
    >> 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.


    s/Integram/Integral/

    --
    Erik Wikström
     
    Erik Wikström, Mar 13, 2008
    #4
    1. Advertisements

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

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Colin Walters
    Replies:
    2
    Views:
    623
    Ben Pfaff
    Feb 13, 2004
  2. Ross A. Finlayson
    Replies:
    19
    Views:
    729
    Keith Thompson
    Mar 10, 2005
  3. Replies:
    2
    Views:
    446
    Dave Thompson
    Feb 27, 2006
  4. Replies:
    5
    Views:
    452
  5. Michael Haupt
    Replies:
    2
    Views:
    458
Loading...

Share This Page