Templating rocks. But it seems to be something that’s missed out when people are learning C++. It’s like they learn about syntax, variables, structs, classes, pointers – and then they’re off, and forget to keep learning. This is despite using them all the time without realizing it (the std library). Well here’s where you learn.
So imagine a simple maths function, Approach.
float Approach( float fCurrent, float fTarget, float fDelta )
{
if ( fCurrent < fTarget )
{
fCurrent += fDelta;
if ( fCurrent > fTarget ) return fTarget;
}
else if ( fCurrent > fTarget )
{
fCurrent -= fDelta;
if ( fCurrent < fTarget ) return fTarget;
}
return fCurrent;
}
Takes a starter float, a target float and a delta.. and approaches the number by delta units. Simple right?
But what if you want the same thing for an int? Then you’d probably end up writing it again, called ApproachInt with int. (Yeah more likely you’d just cast them sharrap).
Well if you code it as a template function you don’t have to worry about that.
template <typename T, typename T2>
inline T Approach( T fCurrent, T fTarget, T2 fDelta )
{
if ( fCurrent < fTarget )
{
fCurrent += fDelta;
if ( fCurrent > fTarget ) return fTarget;
}
else if ( fCurrent > fTarget )
{
fCurrent -= fDelta;
if ( fCurrent < fTarget ) return fTarget;
}
return fCurrent;
}
Now it can be used with any type! I won’t bother explaining the code since it’s pretty simple to work out.
One thing that might confuse you – why did I add T2. Why use 2 different types? Shouldn’t they all be the same? Well I was forward planning. This is used for vectors too. But since that template won’t translate directly to a Vector.. we have to specialize it.. like so:
template<> inline Vector Approach<Vector, float>( Vector fCurrent, Vector fTarget, float fDelta )
{
Vector vDelta = fTarget – fCurrent;
float fDist = vDelta.Length();
if ( fDist < fDelta )
{
return fTarget;
}
return fCurrent + vDelta.GetNormal() * fDelta;
}
Now whenever you call Approach( vector, vector, float ) it will use this function, and give us the expected results.
This works for a lot of things. I used to be a total Macro freak. Everything I coded was a macro. Then I found out that Macros Suck. For example, a Swap function.
#define Swap( typenme, x, y ) { typenme t = x; x = y; y = t; }
Becomes
template <class T>
void Swap( T &var1, T &var2 )
{
T var_temp = var1;
var1 = var2;
var2 = var_temp;
}
You’re probably thinking – how the fuck is that better. Look at it! It’s a mess! Well I’ve come to learn that you want to try to avoid macros as much as possible. Say you start using some 3rd party API
and one of the functions on one of the classes is called Swap – you are now fucked. Because your macro will replace it, everywhere in your code. Now you got a big un-compilable mess.
Using a template isn’t going to do that. You could even keep it in a namespace or a class – impossible with a macro. You could specialize it too – maybe when you Swap a certain type or class you want to do something different. Well now you can.
I hope this has opened some eyes to Templates. Have a think about it next time you’re coding a class with SetFloat, SetInt, SetChar members. There’s a lot you can do (or avoid doing) with them.
Here’s some more reading.
+1
You’re damned right about people not using templates most of the time, but I also think it gets silly when people use them too often (for example, in place of polymorphism, with _perhaps_ a templated getter method).
I use templates quite frequently because I’m writing an engine with reference counting. (So that I can use objects with scripting engines and have an object counted by both the script engine and the game engine, otherwise one has to take control of the object, defeating the point of reference counting.)
– Ricky
Actually in C++0x templates will be variadic too.
That’s gonna be hardcore
Seeing as I’ve been focusing on C programming lately…Thanks!
Thanks, this should come in handy!
And have you been going back to basics lately, Garry? Looks like you’ve taken up more C++ again (I’m not saying that as a bad thing!).
interesting, never looked at them like that (i only saw the Vector class from Valve with it)
Even though I hardly understand any of this, it’s still good to see that you’re updating your blog more often.
And Garry, when is FPS getting the office? 24th?
You just successfully taught me C++ templates with minimal explanation.
I’m looking forward to C++0x
Pretty good explanation, readers should also know that you can use templates with classes and such, very useful for making containers like stacks and lists.
*cough*
template
void Swap( T &var1, T &var2 )
{
T var_temp = *var1;
*var1 = *var2;
*var2 = var_temp;
}
Pretty sure you need to do this since C++ only passes by value (which is the reason you pass the pointers in the first place).
If you declared it inline instead (like a macro) it might work though as-is.
Aaaand wordpress ate my angle brackets. Om nom nom.
No you don’t
Another two things which i haven’t seen used much, are pointers to functions and the friend keyword.
Excellent read, thanks Garry!
I started coding like a year ago (mostly in c#, but switched to c++ recently) and this information is really useful to me.
cheers!
If you did all the proper operator overloading and declared your vector with typedef, would it work in templating?
I have never heard about these templates
Thank you for filling a gap in my c++ knowledge.
I’m a university student at UIUC, and we’ve been told this is part of the “classical” education for computer science – at least in regards to learning data structures.
And to #1, templating is the way you’re supposed to write parametric polymorphic code…
Templates are indeed awesome though, and have saved me a great deal of time.
Somehow i dont think that approach function is correct???
“Using a template [...] You could even keep it in a namespace or a class – impossible with a macro.”
Well, you can define a macro inside a class or a namespace. But it cannot be used to expand code outside that scope; it’s useful for internal work though – without polluting the global namespace.
Also you can put your utility macros in their own header which you only include in cpps where used to reduce possible name clashes.
Anyway point taken, macros suck, just wanted to add a little more information to readers.
The compiler doesn’t even really know about macros. They’re substituted before compilation proper begins, so the compiler doesn’t cares if they were inside a namespace or whatever they will be available anywhere after they were defined.