Fun with Template Specialization
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>
{
char* Bar();
};
template<> class Foo <WCHAR>
{
WCHAR* Bar();
};
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:
enum msgId
{
msgIdFoo,
msgIdBar,
msgIdBaz
};
template< int id > class MsgPtr;
template<> class MsgPtr <msgIdFoo >
{
public:
FooStruct* ptr;
};
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.
Comments
Anonymous
April 30, 2007
Thanks for the article. I enjoyed it a lot! It would be great if you posted SafeInt 2.0 class. I've seen only v1.0.3 you posted in 2003(?)... I'm sure the new one is much better :)Anonymous
May 16, 2007
> Fun with Template Specialization Have you tried meta-template programming... and get the compiler to work out the result of the program for you. Factorial calculated in O(1) runtime... (from memory, so likely typos) template<int n> class Factorial { public: const int value = n * Factorial<n-1>; }; template<> class Factorial<0> { public: const int value=1; }; See "Modern C++ Programming" and www.boost.org for more. [dcl] Yep - that's some cool stuff. I use some of that internally to SafeIntAnonymous
June 04, 2007
Template specialization can indeed do some fun -- and even more important, incredibly useful -- stuff. But as you point out, it pays to remember the impact on maintenance of using really sophisticated techniques, especially when you have other programmers working in the same code. It's a good idea to underestimate how sophisticated your fellow developers are, to be on the extra-safe side when it comes to "future-proofing" your code. It's not really a time saver when someone replaces your crafted precision code with an entirely different, but more understandable, creation :).Anonymous
June 21, 2007
The comment has been removedAnonymous
July 14, 2007
Where is SafeInt available from these days? I can't find a download. Thanks. [dcl] It's apparently lost from MSDN. I'm working on v3, and will be publishing that sometime soon.