Templates and specializations

Started by
13 comments, last by Storyyeller 12 years, 10 months ago
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:



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) { ... }
};



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:



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
};



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?
[ King_DuckZ out-- ]
Advertisement
This may or may not be helpful, but for the CML we went with indexed access only, e.g.:

v[0] = 0.f;
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.)
are you driveing those other templates from "vector" ?

Never say Never, Because Never comes too soon. - ryan20fun

Disclaimer: Each post of mine is intended as an attempt of helping and/or bringing some meaningfull insight to the topic at hand. Due to my nature, my good intentions will not always be plainly visible. I apologise in advance and assure you I mean no harm and do not intend to insult anyone.

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.

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.
[ King_DuckZ out-- ]

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.

Oh, sorry. (I saw the dot function implemented in terms of the Get*() accessors and assumed that's what you were doing.)
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:


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 * v;

return product;
}


But I get the impression I'm missing your point. Can you give a better example of where you're getting stuck?
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.

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).

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.

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)

Never say Never, Because Never comes too soon. - ryan20fun

Disclaimer: Each post of mine is intended as an attempt of helping and/or bringing some meaningfull insight to the topic at hand. Due to my nature, my good intentions will not always be plainly visible. I apologise in advance and assure you I mean no harm and do not intend to insult anyone.

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.

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.
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.


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.


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.

[ King_DuckZ out-- ]

This topic is closed to new replies.

Advertisement