Sign in to follow this  
King_DuckZ

Templates and specializations

Recommended Posts

King_DuckZ    128
Hello, I'm writing some templated classes and for each of them I ran into the problem of having to copy and paste lines and lines of code for every specialization I made.

For example:

[code]

template <typename T, size_t S>
class Vector { ... };


template <typename T> class Vector<T, 2> {
T GetX();
T GetY();
T operator[] (size_t index) { ... }
};

template <typename T> class Vector<T, 3> {
T GetX();
T GetY();
T GetZ();
T operator[] (size_t index) { ... }
};

template <typename T> class Vector<T, 4> {
T GetX();
T GetY();
T GetZ();
T GetW();
T operator[] (size_t index) { ... }
};

[/code]

Here already, GetX() and GetY() are common to every specialization, as well as dot(), cross(), operators and all the whistles and bells I can add to implement vectors. Everything is generic enough so that I never need to write different code for each method, and the interface only changes slightly.


The best approach I've found to solve the problem of having to add each new method's declaration 3 or more times is this:

[code]

template <typename T, size_t S, typename D>
class VectorBase {
public:
// Note that VectorBase has no specialization, so it doesn't know how to implement a operator[] - thus the cast to D*
T dot ( const D& other) { const D& self = *static_cast<D*>this; ... }
};


template <typename T, size_t S>
class Vector : private VectorBase<T, S, Vector<T, S> > {
public:
T dot ( const Vector& other) { return VectorBase<, S, Vector<T, S> >::dot(other); }
T operator[] (size_t index) { ... } // Different size-based specializations only have to define methods that really change, like this one
};

[/code]

This has the advantage that I can put all of the wrapper methods into a macro and then only call that macro for each specialization, and I still get specialized constructors, no public base classes and relatively clear interfaces. The drawbacks is that I have to cast this most of the times, and that for cases where one specialization needs to be friend with another, then the base class must have the same friendships. Also macros are incredibly annoying and ugly.


What do you think of this pattern? Has anyone a better solution to this recurring problem?

Share this post


Link to post
Share on other sites
jyk    2094
This may or may not be helpful, but for the CML we went with indexed access only, e.g.:

[code]v[0] = 0.f;[/code]
Rather than named accessors like getX() and so forth.

Making vectors indexable can make it a lot easier to generalize code when dealing with vectors of different sizes. For example, the same dot product function can be used for vectors of any size (and consequently, squared length, length, and normalization, for example, can be dimension-independent as well).

Some developers prefer to be able to access vector elements by name though (e.g. v.x). There are some tricks for providing both named and indexed element access. Some of them aren't strictly portable, but are often used anyway simply because they work (or can be made to work) on the platforms of interest. There's also a method that's been discussed on occasion on these forums that (I believe) is fully portable, but I don't know off the top of my head if/how it could be adapted for vectors of arbitrary dimension.

One option would be to store the vector elements using an array, and then provide get*() functions that either assert, throw, or generate a compile-time error if used with a vector of the wrong dimension.

Also, some will say that 2-, 3-, and 4-dimensional vectors don't have enough in common to warrant parameterizing by dimension; that's up to you to decide though, of course. If you do decide to go that route, all this stuff can be worked out, although it can be a bit tricky. (Personally I find it worth parameterizing by dimension, since there are more operations that are dimension-independent than are dimension-dependent.)

Share this post


Link to post
Share on other sites
King_DuckZ    128
[quote]Making vectors indexable can make it a lot easier to generalize code when dealing with vectors of different sizes. For example, the same dot product function can be used for vectors of any size (and consequently, squared length, length, and normalization, for example, can be dimension-independent as well).[/quote]
I should've have said that explicitly, my Vector class has operator[] overloads, so I can already write generic dot() functions and all the rest. The code I posted is just conceptual, it doesn't represent anything real - I just wrote a dot implementation to show that it was implemented there.

[quote]are you driveing those other templates from "vector" ?[/quote]
Do you mean std::vector? Certainly not, unfortunately names clash quite a bit but here I'm not using anything at all from std.

Share this post


Link to post
Share on other sites
jyk    2094
[quote name='King_DuckZ' timestamp='1306321442' post='4815531']
I should've have said that explicitly, my Vector class has operator[] overloads, so I can already write generic dot() functions and all the rest. The code I posted is just conceptual, it doesn't represent anything real - I just wrote a dot implementation to show that it was implemented there.[/quote]
Oh, sorry. (I saw the dot function implemented in terms of the Get*() accessors and assumed that's what you were doing.)

Share this post


Link to post
Share on other sites
the_edd    2109
I'm not exactly sure what the exact problem is you're having, but in the case of something like a dot or cross product, you could consider making them free functions. Something like:

[code]
template<typename T, std::size_t N>
T dot(const Vector<T, N> &u, const Vector<T, N> &v)
{
T product = 0;
for (std::size_t i = 0; i != n; ++i)
product += u[i] * v[i];

return product;
}
[/code]

But I get the impression I'm missing your point. Can you give a better example of where you're getting stuck?

Share this post


Link to post
Share on other sites
Slavik81    360
You'll be writing maybe 4 specializations and they're unlikely to change much in the foreseeable future. There's little point to avoiding copy/pasting code here. Particularly code as trivial as accessors.

In this case, I'd say it's not worth making simple code complex just remove a few lines of duplication.

Share this post


Link to post
Share on other sites
ryan20fun    2635
[quote name='King_DuckZ' timestamp='1306321442' post='4815531']
[quote]Making vectors indexable can make it a lot easier to generalize code when dealing with vectors of different sizes. For example, the same dot product function can be used for vectors of any size (and consequently, squared length, length, and normalization, for example, can be dimension-independent as well).[/quote]
I should've have said that explicitly, my Vector class has operator[] overloads, so I can already write generic dot() functions and all the rest. The code I posted is just conceptual, it doesn't represent anything real - I just wrote a dot implementation to show that it was implemented there.

[quote]are you driveing those other templates from "vector" ?[/quote]
Do you mean std::vector? Certainly not, unfortunately names clash quite a bit but here I'm not using anything at all from std.
[/quote]

sorry, i meant your class Vector (it was easyer to not hold shift :D)

Share this post


Link to post
Share on other sites
inavat    317
What exactly are you trying to do? This is perhaps the most unclear question I've ever read.

For example, why do you show a dot method using GetX() and GetY() if your "Vector class has operator[] overloads, so I can already write generic dot() functions and all the rest" ?

If you want help, I suggest you start over and try to describe your problem more carefully. Your implementation of a dot method is the craziest one I've ever seen, it makes no sense.

Share this post


Link to post
Share on other sites
King_DuckZ    128
[quote name='edd²' timestamp='1306343304' post='4815659']
I'm not exactly sure what the exact problem is you're having, but in the case of something like a dot or cross product, you could consider making them free functions.[/quote]I already considered making free functions, but I really liked to have everything in my classes. I really wanted to put focus on the cast inside my dot implementation - I'm correcting the code if I can edit my post. Anyways read below, I'll try to better explain myself.

[quote name='Slavik81' timestamp='1306349818' post='4815713']
You'll be writing maybe 4 specializations and they're unlikely to change much in the foreseeable future. There's little point to avoiding copy/pasting code here. Particularly code as trivial as accessors.

In this case, I'd say it's not worth making simple code complex just remove a few lines of duplication.
[/quote]

I think you hit the nail on the head. I've been writing a few generic classes lately (vector, matrix, bounding box, containers...) and I kept on running into the problem of having to update a lot of code when all I want is to add a single method. So my question could be turned into this: do you have any special technique to keep duplication to a minimum when you write your templates? And what do you think of my solution? (That is, of passing the derived class' type to the base class and cast this) Do you see any downside in doing so?

The problem is more evident in some cases, as with matrices, where my techniques forced me to declare plenty of friends in non-square matrix specialization. Also, I only write code when I need it, so at a given point I will have to go back to my Vector for example, and add a Cross() method. I realize that templated code is especially verbose, but I'm wondering if there's any special pattern I'm not aware of that reduces the copy & paste.

Share this post


Link to post
Share on other sites
King_DuckZ    128
[quote name='ryan20fun' timestamp='1306350522' post='4815719']
sorry, i meant your class Vector (it was easyer to not hold shift :D)
[/quote]

Hehe ok, making my point clear is proving to be harder than anything else :P


No, Vector is the final class, and it's the one getting all the specializations. I added VectorBase just so that common functions never needing a specialization get grouped, and Vector can inherit those. However, I don't want clients of Vector class to do anything like:

[code]

VectorBase<float, 3>* vectors = new Vector<float, 3>[10];

[/code]

hence the private inheritance. However, this beats the purpose of my obscure inheritance model in a way, as inherited methods get hidden and I need to wrap all of them to make them public again. And if I do public inheritance I would allow code like above, which I don't want.

Share this post


Link to post
Share on other sites
jyk    2094
[quote name='King_DuckZ' timestamp='1306365312' post='4815827']
I already considered making free functions, but I really liked to have everything in my classes.[/quote]
This is contrary to what's generally recommended, I believe. ([url="http://drdobbs.com/cpp/184401197"]Here[/url] is an article on the topic you might want to read.)

Share this post


Link to post
Share on other sites
Storyyeller    215
Why is the possibility of accessing the base class harmful? I can't think of any way it could cause problems, and I think it's very unlikely that you can find a way to share the functions without an accessible base class.

Here's my attempt. The base class is accessible, but I don't really see any problem with this.
Also, this has the advantage of not requiring any type casting.

[code]
typedef unsigned long vec_size_t;

template <typename T, vec_size_t S>
class VectorBase
{
T e[S];

public:
vec_size_t size() const {return S;}
T& operator[](vec_size_t i) {return e[i];}
const T& operator[](vec_size_t i) const {return e[i];}
};

template <typename T, vec_size_t S>
class Vector: public VectorBase<T, S> {};

//Specializations with conveinence acessors
template <typename T>
class Vector<T,4>: public VectorBase<T, 4>
{
public:
T GetX() const {return this->operator[](0);}
T GetY() const {return this->operator[](1);}
T GetZ() const {return this->operator[](2);}
T GetW() const {return this->operator[](3);}
};

template <typename T>
class Vector<T,3>: public VectorBase<T, 3>
{
public:
T GetX() const {return this->operator[](0);}
T GetY() const {return this->operator[](1);}
T GetZ() const {return this->operator[](2);}
};

template <typename T>
class Vector<T,2>: public VectorBase<T, 2>
{
public:
T GetX() const {return this->operator[](0);}
T GetY() const {return this->operator[](1);}
};

//Operators and free functions
template <typename T, vec_size_t S>
Vector<T, S>& operator += (Vector<T, S>& left, const Vector<T, S>& right)
{
for(vec_size_t i=0; i<S; ++i) {left[i] += right[i];}
return left;
}

// etc
[/code]


P.S., How can your cross function be common? Cross products aren't even defined except in 3 and 7 dimensions.

Share this post


Link to post
Share on other sites
King_DuckZ    128
[quote name='jyk' timestamp='1306366664' post='4815838']
[quote name='King_DuckZ' timestamp='1306365312' post='4815827']
I already considered making free functions, but I really liked to have everything in my classes.[/quote]
This is contrary to what's generally recommended, I believe. ([url="http://drdobbs.com/cpp/184401197"]Here[/url] is an article on the topic you might want to read.)
[/quote]

Very interesting reading, I'll be certainly be coding differently after understanding it. Thanks a lot!




[quote name='Storyyeller' timestamp='1306369091' post='4815851']
Why is the possibility of accessing the base class harmful? I can't think of any way it could cause problems, and I think it's very unlikely that you can find a way to share the functions without an accessible base class.

Here's my attempt. The base class is accessible, but I don't really see any problem with this.
Also, this has the advantage of not requiring any type casting.

P.S., How can your cross function be common? Cross products aren't even defined except in 3 and 7 dimensions.
[/quote]

I wanted my vector classes to have .x, .y and so on, and not .x(), .y() etc, so each specialization declares its own properties (I know I wrote getters in my post, it was just that I felt it was not important to show my point). I can still put the operator[] in the base class after the due refactoring, but at this point I will decide after reading jyk's link. And yes, probably making public inheritance is acceptable.


About the cross product yeah, my bad... that's what I get for posting at 2am :/ Although it can be handy to have a cross on 2D vectors that are zero-extended to 3D, but it's not really what I meant to do... nor to develop some 7D game :D

Well, thanks everyone for the hints!

Share this post


Link to post
Share on other sites
Storyyeller    215
C++ doesn't have properties, so if you want values to be accessible without a function call, you'll probably have to use some sort of complicated union trickery.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this