This post is an optional post, meaning you can skip this post without it being detrimental for viewing future posts, and simply exists as an aside “FYI”.
For convenience here’s a shortened version of the deserialise
function that was created in the first post of this series.
This kind of ‘design’ being used here is to have a singular template function
(deserialise
) that takes any type as its input, and then leverage static if
to determine
the actual functionality.
What if we could write this another way? First, we need to know about template constraints.
Instead of a function that can take any type, imagine that we instead of multiple seperate templated functions that can only take certain types (e.g. only integers, but any kind of integer).
While we could go the route of nesting a bunch of static ifs
inside the function’s body
followed by a static assert
should all of these ifs
fail, what if instead
we tell the compiler the exact conditions needed for it to even consider using the template function?
I feel an example will help clear things up:
So what’s happening here is that we’re creating a template function called someFunc
which takes a type parameter (T
), and it does stuff, similar to the other template functions we’ve made so far.
However, take note that we can actually attach an if
statement directly to the function’s signature.
This is known as a constraint which you can imagine as a static if
that applies for the entire function as a whole.
The constraint’s condition must pass the given template parameters, otherwise the compiler will refuse to use it for that specific permutation.
For example:
So where am I going with this?
By using constraints, we can essentially create overloads for template functions.
Take these two functions for example:
They both have the same name, same template parameters, and same runtime parameters. The only difference are their constraints.
When calling a template function with overloads like this, the compiler will try to match each overload with the parameters that you pass to it (which includes testing the constraints).
If no overloads matches your parameters, the compile fails.
If more than one overload matches your parameters, the compile fails due to ambiguity.
If exactly one overload matches your parameters, that overload will be used.
So it’s pretty much the same as overloading a non-templated function, except we have more specific control on when each overload can be used thanks to constraints.
What this means is that instead of a singular deserialise
function with a bunch
of static ifs
, you could also/instead use seperate overloads for each type
of data you want to deserialise:
There are pros and cons to both designs, and there are of course more complicated ways/features that can be used to design your code around templates that need to discriminate by things like types.
However, the main point of this one-off post was just to show and explain a different way
of doing things, so that it doesn’t seem like a nested web of static ifs
is the only
way to handle something like this.
D will let you mold your code to the exact vision (or close enough to it) of how you want your API to look without compromising on maintainability.