• Advertisement
Sign in to follow this  

How to do this using templates in C++

This topic is 4247 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

I'm writing a Vector and Matrix library and the problem I stumbled upon is the fact that every Vector2, Vector3 and Vector4 needs to be defined as a separate class, even though the functions are all the same, they just use a different number of data members. I was just thinking that maybe there was a way to use templates, macros or inheritance to reuse some of that code. Is this possible using templates:
template <class T, int n>
class Vector
{
	T x;
	T y;
$if(n>=3) //just kidding, I WISH there was a keyword like that
	T z;
$endif
$if(n==4)
	T w;
$endif
};

I tried doing this, but partial template specialization requires that I redefine the class every time, so it's kind of pointless:
template <class T, int n>
class Vector
{
	T data[n];
};

template <class T, int n>
class Vector<T, 2>
{
	T data[n];
	Vector(T _z, T _y);
};

template <class T, int n>
class Vector<T, 3>
{
	T data[3];
	Vector(T _x, T _y, T _z);
};

Did anybody have this issue, and if so, how did you solve it? Thanks.

Share this post


Link to post
Share on other sites
Advertisement
With me i had so many problems with C++ that i changed over to C# language. even though its almost the same thing but it is easier to use C#, you also should get alot less problems for your "Vector and Matrix library". i hope this will help.

Share this post


Link to post
Share on other sites
via explicit instantiation. A code generator could help the tedium, but there's no template magic I know of to get that working.

Share this post


Link to post
Share on other sites
I specialised the classes, using the little anonymous union/struct trick:


template <typename T> SVector<3, T>
{
union
{
struct
{
T x, y, z;
};
T mElements[3];
};
};



Now, since each specialisation has a member called mElements, I can make friend functions/operators of the unspecialised class without needing to rewrite them. In the above, Any SVector<3> has members x y and z, but SVector<253> won't (my SVector<2> specialisation has x and y, and my SVector<4> has x y z w). If that makes any sense. I can divulge source code if it helps.

Share this post


Link to post
Share on other sites
I would make two suggestions (although neither may be to your liking).

First, look at how the standard library does it. Those guys are pretty clever. Examine, for instance, the std::valarray class.

Second, I would suggest (and I don't do this lightly) using inheritance. Yes, you'll have to duplicate some code, but most functions can be moved into a templated base class.


template<typename T, int n>
class VectorBase
{
public:
VectorBase operator+=(const VectorBase& rhs)
{
for (int i = 0; i < n; ++i)
{
m_data += rhs.m_data;
}
return *this;
}

// other operations...

protected:
T m_data[n];
};

template<typename T>
class Vector2: public VectorBase<T, 2>
{
public:
Vector2(T x, T y) { i/* ... */ }
};

template<typename T>
class Vector3: public VectorBase<T, 3>
{
public:
Vector3(T x, T y, T z)
{ this->m_data[0] = x; this->m_data[1] = y; this->m_data[2] = z; }
};

main()
{
Vector3<int> v0(0, 0, 0);
Vector3<int> v1(1, 1, 1);
v1 += v0;
}

Share this post


Link to post
Share on other sites
My vector class is similar. It is parametised by the type of the elements and the number of elements. I had to specialise the vector for sizes 2, 3 and 4 to define the x, y, z and w members and define an operator[]. Then all other operators and functions were made non-member non-friends, and operated on vectors of all sizes generically by using the size template parameter and looping through all elements via operator[]. These loops are optimized out by the compiler.

Share this post


Link to post
Share on other sites
Quote:
Original post by _goat
Now, since each specialisation has a member called mElements, I can make friend functions/operators of the unspecialised class without needing to rewrite them.


That sounds like a great idea. Can you provide an example how to reuse code using friend functions? So far my impression of partial specialization was that for each specialized template I have to rewrite everything.

Quote:
Original post by Bregma
First, look at how the standard library does it. Those guys are pretty clever. Examine, for instance, the std::valarray class.

Second, I would suggest (and I don't do this lightly) using inheritance. Yes, you'll have to duplicate some code, but most functions can be moved into a templated base class.


Templates and inheritance... that's pretty clever, I'll try it. Thanks.

Share this post


Link to post
Share on other sites
Quote:
Original post by _goat
I specialised the classes, using the little anonymous union/struct trick:


Could you please post a little more on this - i'm not sure i fully understand it, but it sounds very interesting.

Note: I need to see the some member functions, as well as some operators.

Thenk you.

Share this post


Link to post
Share on other sites
A better solution than using an anonymous union was posted a while back. Search for "A slick trick in C++".

Share this post


Link to post
Share on other sites
Unless you have severe memory constraints It would be way easier to just use a vec4 all the time and if you don’t need the extra parameters just set them to 0

Share this post


Link to post
Share on other sites
There are tricks. They get tedious.

The easiest way to do it would be to steal boost::tuple's work.


Share this post


Link to post
Share on other sites
Quote:
Original post by Rebooted
A better solution than using an anonymous union was posted a while back. Search for "A slick trick in C++".


Wow, that trick looks complicated. I don't know if it will work, because my Vectors need to be POD-types.

Quote:
Original post by Grain
Unless you have severe memory constraints It would be way easier to just use a vec4 all the time and if you don’t need the extra parameters just set them to 0


I will be using Vector2, Vector3 and Vector4 for texture coordinates, normals and colors, so they need to be separate classes with exactly 2, 3 or 4 values.

Quote:
Original post by NotAYakk
There are tricks. They get tedious. The easiest way to do it would be to steal boost::tuple's work.


Cool, yet another solution! I'll take a look.

_goat,
I'm trying your solution right now. I was able to reuse a lot of code and specialize some of the comparison code for floats. Everything works well except for the fact that anonymous structs are not standard, so it probably won't work on GCC.

Rebooted,
Thanks for the non-member, non-friend suggestion. It magically works and the compiler optimizes everything. What would it take to have member functions as well? Do I still have to define all of them in each specialized class template?

Share this post


Link to post
Share on other sites
Quote:
Original post by deathkrush
Wow, that trick looks complicated. I don't know if it will work, because my Vectors need to be POD-types.

Same here. A static const member shouldn't affect the vector's status as a POD type.
Quote:

Rebooted,
Thanks for the non-member, non-friend suggestion. It magically works and the compiler optimizes everything. What would it take to have member functions as well? Do I still have to define all of them in each specialized class template?
You will have too, yes. You might be able to use inheritance to get around this though.

However, why do you need member functions? It is generally recommended to prefer non-member functions where possible to increase encapsulation (this might seem backwards, but it makes the functions separate from the data type - the STL does this extensively). You only need member functions when you have to access private variables, and in this case x, y, z and w are public. Annoyingly, there are some operators that have to be members, like operator[].

Share this post


Link to post
Share on other sites
Quote:
Original post by Rebooted
However, why do you need member functions? It is generally recommended to prefer non-member functions where possible to increase encapsulation (this might seem backwards, but it makes the functions separate from the data type - the STL does this extensively). You only need member functions when you have to access private variables, and in this case x, y, z and w are public. Annoyingly, there are some operators that have to be members, like operator[].


Some functions like Normalize() and Length() will have to be member functions, otherwise I will have to do something crazy like VectorNormalize<float,3>(vec3) instead of vec3.Normalize(); The good thing is that I can write a common templated function that will do the actual calculation. To make it a member of every specialized class template, I will just define a member and call that 00beR function from the member.

Share this post


Link to post
Share on other sites
Quote:
Original post by Rebooted
Some functions like Normalize() and Length() will have to be member functions, otherwise I will have to do something crazy like VectorNormalize<float,3>(vec3) instead of vec3.Normalize();

There is no need to provide the template parameters where they can be inferred. The only difference is vec3.Normalize() vs Normalize(vec3).

Share this post


Link to post
Share on other sites
Well "a slick trick in c++" realy looks great, but it doesn't seem to be possible to put the dimension into a template parameter - you have to write Vector2, Vector3 and Vector4 (and more, if needed) classes. Or am i missing something?

Share this post


Link to post
Share on other sites
Quote:
Original post by deathkrush
Is this possible using templates:


Not with much gain. That said, this is quite possible using boost preprocessor!

#include <boost/preprocessor/repetition.hpp>
#include <boost/preprocessor/tuple.hpp>
#define ELEMENTS 4
#define ELEMENT_NAMES (x,y,z,w)

#define IMPLEMENT_VECTOR_ELEM(z,n,~) T BOOST_PP_TUPLE_GET(ELEMENTS,n,ELEMENT_NAMES);
#define IMPLEMENT_VECTOR(z,n,~) template < typename T > \
class Vector ## n { \
BOOST_PP_REPEAT(n,IMPLEMENT_VECTOR_ELEM,~) \
}; \
/*******************************************************/

BOOST_PP_REPEAT_FROM_TO(2,ELEMENTS,IMPLEMENT_VECTOR,~)

//since we're using short names, lets be nice and clean them up:
#undef IMPLEMENT_VECTOR
#undef IMPLEMENT_VECTOR_ELEM
#undef ELEMENT_NAMES
#undef ELEMENTS






Another copy more closely matching the original post:

#include <boost/preprocessor/repetition.hpp>
#include <boost/preprocessor/control/if.hpp>
#define IMPLEMENT_VECTOR(z,n,~) template < typename T > \
class Vector ## n { \
T x; \
T y; \
BOOST_PP_IF( BOOST_PP_GREATER_EQUAL(n,3) , T z; , ) \
BOOST_PP_IF( BOOST_PP_GREATER_EQUAL(n,4) , T w; , ) \
}; \
/*******************************************************/

BOOST_PP_REPEAT_FROM_TO(2,4,IMPLEMENT_VECTOR,~)

//since we're using short names, lets be nice and clean them up:
#undef IMPLEMENT_VECTOR





Note: Examples have trailing whitespace after their backslashes to prevent "\ <newline>" from grokked by the forum and replacing it with "".

Share this post


Link to post
Share on other sites

template<int n>
stuct storage {
int elem;
storage():elem(0) {};
};

template<int n>
struct n_ary:
nary<n-1>,
storage<n>
{};

template<>
struct n_ary<0> {};

template<int n>
int& access(storage<n>& value) {
return value.elem;
};

typedef n_ary<4> Vector4;

void test() {
n_ary<4> vec4;
access<1>(vec4) = 2;
printf("%d %d %d %d\n", access<1>(vec4), access<2>(vec4), access<3>(vec4), access<4>(vec4) );
}


The above is where you might start writing up arbitrary vector code.

Getting "for_each" based addition and dot product is relatively simple.

Cross product might be a bit trickier. Especially if you go all the way up to wedge products. ;)


Share this post


Link to post
Share on other sites
There was recently a huge amount of traffic on the sweng-gamedev mailing list about vector and matrix lbirary design. You should try to find an archive of it and sift through it.

Share this post


Link to post
Share on other sites
Quote:
Original post by _goat
I specialised the classes, using the little anonymous union/struct trick:

*** Source Snippet Removed ***



That is not standard compliant code, it's not portable C++ code.

Share this post


Link to post
Share on other sites
Ok, so i was thinking very very hard on this... I pushed my mind to the limits... :)
Anyway, i've changed "a slick trick in c++" to have the dimension as a template argument. So anyway, here it goes:

#include <iostream>

template< typename T, unsigned int D >
class Vector
{
public:
const T & operator [] (unsigned int i) const
{
return m_vec;
}
T & operator [] (unsigned int i)
{
return m_vec;
}
private:
T m_vec [D];
};

template< typename T >
class Vector< T, 2 >
{
public:
Vector (T _x = 0, T _y = 0)
: x(_x), y(_y)
{
}
const T & operator [] (unsigned int i) const
{
return this->*s_v ;
}

T & operator [] (unsigned int i)
{
return this->*s_v ;
}
public:
T x, y;
private:
typedef T Vector< T, 2 >::* const vec[2];
static const vec s_v;
};
template< typename T >
const typename Vector< T, 2 >::vec Vector< T, 2 >::s_v = { &Vector< T, 2 >::x, &Vector< T, 2 >::y };

template< typename T >
class Vector< T, 3 >
{
public:
Vector (T _x = 0, T _y = 0, T _z = 0)
: x(_x), y(_y), z(_z)
{
}
const T & operator [] (unsigned int i) const
{
return this->*s_v ;
}

T & operator [] (unsigned int i)
{
return this->*s_v ;
}
public:
T x, y, z;
private:
typedef T Vector< T, 3 >::* const vec[3];
static const vec s_v;
};
template< typename T >
const typename Vector< T, 3 >::vec Vector< T, 3 >::s_v = { &Vector< T, 3 >::x, &Vector< T, 3 >::y, &Vector< T, 3 >::z };

template< typename T, unsigned int D >
Vector< T, D > operator + (const Vector< T, D > & vec1, const Vector< T, D > & vec2)
{
Vector< T, D > vecr;
for (unsigned int i = 0; i < D; ++i)
{
vecr = vec1 + vec2;
}
return vecr;
}

int main ()
{
std::cout << "sizeof( Vector< int, 1 > ) == " << sizeof( Vector< int, 1 > ) << ";" << std::endl;
std::cout << "sizeof( Vector< int, 2 > ) == " << sizeof( Vector< int, 2 > ) << ";" << std::endl;
std::cout << "sizeof( Vector< int, 3 > ) == " << sizeof( Vector< int, 3 > ) << ";" << std::endl;
std::cout << "sizeof( Vector< int, 4 > ) == " << sizeof( Vector< int, 4 > ) << ";" << std::endl;
std::cout << "sizeof( Vector< int, 5 > ) == " << sizeof( Vector< int, 5 > ) << ";" << std::endl;
Vector< float, 2 > vec2fa (1.5f, 2.7f), vec2fb (2.1f, 1.8f);
std::cout << "vec2fa[0] == " << vec2fa[0] << ";\tvec2fa[1] == " << vec2fa[1] << ";" << std::endl;
std::cout << "vec2fa.x == " << vec2fa.x << ";\tvec2fa.y == " << vec2fa.y << ";" << std::endl;
std::cout << "vec2fb[0] == " << vec2fb[0] << ";\tvec2fb[1] == " << vec2fb[1] << ";" << std::endl;
std::cout << "vec2fb.x == " << vec2fb.x << ";\tvec2fb.y == " << vec2fb.y << ";" << std::endl;
Vector< int, 3 > vec3ia (1, 2, 3), vec3ib (3, 6, 9), vec3ir;
std::cout << "vec3ia.x == " << vec3ia.x << ";\tvec3ia.y == " << vec3ia.y << ";\tvec3ia.z == " << vec3ia.z << ";" << std::endl;
std::cout << "vec3ib.x == " << vec3ib.x << ";\tvec3ib.y == " << vec3ib.y << ";\tvec3ib.z == " << vec3ib.z << ";" << std::endl;
vec3ir = vec3ia + vec3ib;
std::cout << "vec3ir.x == " << vec3ir.x << ";\tvec3ir.y == " << vec3ir.y << ";\tvec3ir.z == " << vec3ir.z << ";" << std::endl;
Vector< float, 4 > vec4fa, vec4fb, vec4fr;
vec4fa[0] = 12.8f; vec4fb[0] = 10.7f;
vec4fa[1] = 17.2f; vec4fb[1] = 13.3f;
vec4fa[2] = 19.1f; vec4fb[2] = 16.5f;
vec4fa[3] = 11.9f; vec4fb[3] = 15.6f;
std::cout << "vec4fa[0] == " << vec4fa[0] << ";\tvec4fa[1] == " << vec4fa[1] << ";\tvec4fa[2] == " << vec4fa[2] << ";\tvec4fa[3] == " << vec4fa[3] << ";" << std::endl;
std::cout << "vec4fb[0] == " << vec4fb[0] << ";\tvec4fb[1] == " << vec4fb[1] << ";\tvec4fb[2] == " << vec4fb[2] << ";\tvec4fb[3] == " << vec4fb[3] << ";" << std::endl;
vec4fr = vec4fa + vec4fb;
std::cout << "vec4fr[0] == " << vec4fr[0] << ";\tvec4fr[1] == " << vec4fr[1] << ";\tvec4fr[2] == " << vec4fr[2] << ";\tvec4fr[3] == " << vec4fr[3] << ";" << std::endl;
return 0;
}




The idea is very simple. General Vector only has a simple array of D (template parameter) elements. But the specialized versions of Vector for D=2 and D=3 (you can write D=4 on your own) is using the actual slick trick in c++. So as you can see - it is very easy to write operators (and other functions) for the general case - no need to worry about specialized versions.

I didn't make the Matrix yet, but if you understand the Vector template then it is easy to write it on your own.

Good luck! :)

Share this post


Link to post
Share on other sites
Quote:
Original post by Paulius Maruska
Ok, so i was thinking very very hard on this... I pushed my mind to the limits... :)
Anyway, i've changed "a slick trick in c++" to have the dimension as a template argument. So anyway, here it goes:
*** Source Snippet Removed ***
:)


That looks like the best solution ever, thanks! I already have VectorN, Vector3, Vector3, Vector4, MatrixNM, Matrix44 and Matrix33 coded using the struct/union technique, partial template specialization and functors. I guess it wouldn't be too hard to switch over to the "Slick trick in C++" technique.

Another thing I was looking at is Expression Templates. Have anyone used them in a real-world application? I'm too tempted to try them, but I don't know if expression templates will make my code more bloated than it already is and the performance increase of 10x-16x sounds too good to be true.

Share this post


Link to post
Share on other sites
Quote:
Original post by deathkrush
That looks like the best solution ever, thanks!

No problem. Im just glad it helps. :)

Quote:
Original post by deathkrush
Another thing I was looking at is Expression Templates. Have anyone used them in a real-world application? I'm too tempted to try them, but I don't know if expression templates will make my code more bloated than it already is and the performance increase of 10x-16x sounds too good to be true.

I haven't heard the title before. It realy sounds interesting.

Share this post


Link to post
Share on other sites
boost::lambda can be viewed as a form of expression templates.

(_1/_2) * (_3/_4)

evaluates to a 4-arguement functor.

Toss in some mojo, and you should be able to write:

foreach( result, _1/_2 + _3/_4, vec1, vec2, vec3, vec4 );

Share this post


Link to post
Share on other sites
Quote:
Original post by Paulius Maruska
Quote:
Original post by deathkrush
Another thing I was looking at is Expression Templates. Have anyone used them in a real-world application? I'm too tempted to try them, but I don't know if expression templates will make my code more bloated than it already is and the performance increase of 10x-16x sounds too good to be true.

I haven't heard the title before. It realy sounds interesting.


It's not, really. It's a lot of template trickery, but from what I've read, only allows, in general, a 3x speed increase, and only on certain operations. Furthermore, it's not really useful for modern day processing, ie, sending things to the GPU and processing things in parallel. I read somewhere that a sufficiently advanced compiler/optimiser could optimise "our" regular vector operations into using them, but I wouldn't be holding my breath. In effect, too much time investment, not enough gain. Unless you really need it, of course [grin].

And for the record, my vectors look almost identical to Paulius', after changing them from the union/struct trick to SNK's trick (I even named the private static function mSNK in honour).

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement