In college I was told that behind any easy-to-use API is a tangled web of complicated code spaghetti. While I don’t necessarily agree with this in many cases, it is in line with the common sentiment that C++ template meta-programming is best used in libraries that prioritize elegant or expressive usage over clear internal code, especially when those libraries emphasize complicated mathematics.
From a C++ nerd perspective, I love the idea of template meta-programming. It’s sort of a pipe-dream for me to be able to fully understand Eigen or STL source code and be able to write libraries that, in spite of their incomprehensible complexity, allow for simple and easy to understand abstractions in the library’s usage.
But I’d like to talk about a case in which I found a common template meta-programming construct to be useful in more everyday code. Variadic templates tend to be fairly “coupled” with template meta-programming, as they are a key component in creating many fancy, recursive types that are written by a compiler. For this reason, it caught me by surprise that I found a more “common” usage the other day that isn’t really template meta-programming… more in line with template “normal”-programming.
I was dealing with a class that took a stream of bytes (between 2 and 8 bytes in length) and parsed/stored them in its own special way.
The problem was,
null bytes were totally valid in this data stream (as were all other byte values), so we couldn’t just use
0x0 as default arguments and pass them to this function that took an array. We could just change the constructor to take an array-of-bytes reference/
std::array or use a
numBytes argument or something, but we were working with an API that people were used to and didn’t want to rock the boat in that way. So, my first “let’s just get this to work in the ugliest way possible and stay externally the same” solution looked like this:
No more default arguments muddying our byte stream! So we know the length of our byte-stream without any
numBytes argument or passing an array reference or
std::array<uint8_t, N>, etc. But it sure is ugly…
This is a case where we want the same code, but we just want the compiler to “copy and paste” it for us rather than us leaving all this redundant code explicitly written out, which is exactly what variadic templates can easily do. The equivalent code using a variadic template looks like this:
static_assert: yes, you could use SFINAE here, but looking at variadic templates for the first time is enough to make one’s eyes cross on its own!
My point here is that using a simple variadic template in this case made some very redundant, unwieldy code into a short snippet that can be at least reasoned about – and may serve as a gateway drug into using variadic templates for more nefarious deeds!