Hannes Reuscher of the PowerPoint team turned me on to this cool C++ trick, and I used it extensively in SafeInt 2.0. There’s a bunch of neat things about it – for one thing, it’s the only way in C++ to actually overload something based on return type. Here’s basically how it works:
template <typename T> class Foo;
template<> class Foo <char>
template<> class Foo <WCHAR>
You can also do things like template< typename T, typename U > class Baz, and then specialize just one of the types, which is known as partial template specialization.
Where the real power comes in is when you make one of the parameters a compile-time enum, and then you can get the compiler to do a lot of your logic for you. There’s a couple of books on it, but it does get arcane. For some extended examples, take a look in SafeInt 2.0 (which I need to get re-posted – it disappeared from MSDN). My friend Tim was up here visiting, and we were discussing the tendency for some programmers to use programming constructs not because they solved an actual problem, but because they were cool (to the programmer). We once worked with a programmer who thought recursion was cool, and suffered some horrendous bugs and perf problems as a result. When I was telling him how cool template specialization was, he ribbed me as to whether I was now using it everywhere.
I don’t use it everywhere (except in SafeInt) because it’s a little odd – for example, you might expect ordinary inheritance to apply, but it doesn’t – each specialization is a completely different class, and can have different methods, members, and so on. I don’t’ know immediately of any way to enforce that a given method is exposed by all the specializations. It’s also something that most programmers would have a hard time maintaining, so that’s another reason to be careful. Though I have been thinking about legitimate uses for it where it could really be helpful, and I thought of a neat trick. Some of you might be familiar with how many Windows APIs take an enum to determine what sort of information we’re getting or setting (SetTokenInformation is one good example), and depending on what the value is, the information is a whole different struct.
I’m working on an interesting piece of code where there’s a bunch of different request ID’s, and depending on what the ID is, the packet should be cast to some struct or another – sometimes several IDs use the same struct. It’s getting big enough that it’s annoying to keep it all straight, and I’ve been thinking of ways to simplify matters. Here’s one thing you could do:
template< int id > class MsgPtr;
template<> class MsgPtr <msgIdFoo >
And so on – now I can put all this in one place, and not have to try and keep it straight everywhere, and if I ever decide to map msgIdBaz to BazStruct2 instead of BazStruct, I can change it in one spot. Obviously, this only helps when the msgID is known at compile-time, but that’s fairly often the case. I’ll likely use it once this thing gets further along.
If you like C++, it’s certainly something to play with – you can make really efficient code this way, and I got huge improvements in readability in SafeInt between 1.0.7 and 2.0 using it.