# Functional Programming at Compile-Time

In the previous installment I talked about functional programming a bit, introducing the idea of functors and lambda expressions. This time, we will look at another type of functional programming: a type that is done at compile-time.

## Meta-functions

In functional programming, a function is anything you can call, and it can return anything — including another function. In meta-programming (programming “about” programming), functional programming takes the form of meta-functions returning meta-functions or values. All of this happens at compile-time, which means the values are constants and the meta-functions are types.

One of the simplest possible meta-functions is the `identity` function, which looks like this:

```template < typename T > class identity { typedef T type; };```

This meta-function “returns” the type passed to it, which would be equivalent to a function that returns the value passed to it, but far more useful. This also allows me to show you a common convention in meta-programming, namely that the return type of a meta-function is usually called `type` and the return value (if applicable) of a meta-function is called `value`. Often, a meta-function that returns a value (which must of course be a compile-time constant) also returns a type – namely itself. That is not strictly needed, though.

Before we dive into the real code, I’ll tell you what the real code does: it generates a Fibonacci sequence at compile-time, and uses a run-time construct to fill an array with the generated sequence – and it uses only functional programming techniques (both at compile-time and at run-time) to do so.

A Fibonacci sequence is a sequence of numbers initially meant to model the growth of a population of rabbits, given a fixed generation time and unlimited resources. Each number in the sequence is the sum of the two previous numbers, and the sequence starts with 0, 1. That means that, in the array `a` we will generate, `a[0] = 0; a[1] = 1; a[n] = a[n - 2] + a[n - 1]`. This means our meta-function, which calculates the same at compile-time, will look like this:

```template < unsigned int n__ > struct Fibonacci_ { enum { value = Fibonacci_< n__ - 1 >::value + Fibonacci_< n__ - 2 >::value }; typedef Fibonacci_< n__ - 1 > next; typedef Fibonacci_< n__ > type; };   template<> struct Fibonacci_<1> { enum { value = 1 }; typedef Fibonacci_< 1 > type; typedef Fibonacci_< 0 > next; };   template <> struct Fibonacci_<0> { typedef Fibonacci_< 0 > type; enum { value = 0 }; };```

As you can see, the meta-function is a class (or `struct` in this case), with an `enum` and one or more `typedef`s in it. Sometimes (as we will see later) there are also function declarations, though at compile-time, no run-time functions will actually be called — and there can also be other other types.

In this case, we have two specializations of our class template: one in which `n__` is 1, and one in which `n__` is 0. We need those because for those two values, the resulting value is pre-defined – not calculated. For all other values of `n__`, the resulting value is calculated at compile-time by recursively specializing the class template with smaller and smaller values of `n__`, until we run into 0 and 1.

Compilers are smart: while at run-time, a similar approach would require $2^n$ function calls, the compiler need only specialize a class template once to know what the value is going to be, so we don’t have to worry about optimizing this implementation to make it one of linear complexity — it already is!

## SFINAE

One of the basic rules of C++ overloading is “Substitution Failure Is Not An Error” – that is: it is not a compiler-time error for the computer to come up with a candidate for a function call, try it out and find that it won’t work because something is missing in the (substituted) type. It only becomes an error if there are no candidates left to try. For example, consider the following bit of code:

```#include <iostream>   using namespace std;   template < typename T > void foo(const typename T::type *) { cout << "first" << endl; }   template < typename T > void foo(...) { cout << "second" << endl; }   struct S { // typedef int type; };   int main() { S s; foo< S >(0); }```

Which version of `foo` gets called?

The second.

The reason is that the structure `S` does not have a member type named `type` (it was commented out). The compiler will try the first version of `foo` first, substituting `S` for `T`, fail, because `type` is missing, then choose the next candidate, which will work. In this case, `0` would first be considered as a pointer to `S::type`, which is better than considering it for a parameter to a variadic function — and therefore takes precedence.

If you remove the comment from the typedef in `S`, so `S::type` exists, the first version will be called.

For this to be useful, you don’t really have to call the function. In fact, for this to be useful at compile-time, you can’t call the function. You can, however, take the size of the return value of the function, like this:

```#include <iostream>   using namespace std;   typedef int yes; struct no { int no[2]; };   template < typename T > yes foo(const typename T::type *) { cout << "first" << endl; }   template < typename T > no foo(...) { cout << "second" << endl; }   struct S { typedef int type; };   int main() { S s; cout << ((sizeof(foo< S >(0)) == sizeof(yes)) ? "yes" : (sizeof(foo< S >(0)) == sizeof(no)) ? "no" : "dunno") << endl; }```

This code outputs “yes” when `S` has the `type` typedef, “no” if not – neither of the two functions get called (it doesn’t output “first” or “second” and will never output “dunno” either).

In fact, the bodies of the two functions don’t need to exist:

```#include <iostream>   using namespace std;   typedef int yes; struct no { int no[2]; };   template < typename T > yes foo(const typename T::type *);   template < typename T > no foo(...);   struct S { // typedef int type; };   int main() { S s; cout << ((sizeof(foo< S >(0)) == sizeof(yes)) ? "yes" : (sizeof(foo< S >(0)) == sizeof(no)) ? "no" : "dunno") << endl; }```

This version will work just as well.

This means we can now select on the existence of a member type of a class, which we can use to create a meta-function that will tell us just that:

```namespace Details { template < typename F > struct has_next { typedef char yes[1]; typedef char no[2];   template < typename C > static yes& test(typename C::next *);   template < typename C > static no& test(...);   enum { value = sizeof(test<F>(0)) == sizeof(yes) }; typedef has_next< F > type; };```

This meta-function will tell you whether a given type has a nested typedef (or type) called `next`. We’ll use this knowledge to know when to stop filling our array:

```template < typename F, bool has_next__ > struct Filler_ { static void fill(unsigned int *a) { *a = F::value; Filler_< typename F::next, has_next< typename F::next >::value >::fill(++a); } };   template < typename F > struct Filler_< F, false > { static void fill(unsigned int *a) { *a = F::value; } };   template < typename F > void fill(unsigned int *a) { Filler_< F, has_next< F >::value >::fill(a); }```

As you can see, `Filler_::fill` calls itself recursively until the corresponding instance of `Fibonacci_` no longer has a `next` nested type. So, now `fill` can look like this:

```template < typename F > void fill(unsigned int *a) { Filler_< F, has_next< F >::value >::fill(a); }```

which will fill the array with the Fibonacci sequence.

You can play with this code in the on-line IDE at ideone.com

 Tweet

## About rlc

Software Analyst in embedded systems and C++, C and VHDL developer, I specialize in security, communications protocols and time synchronization, and am interested in concurrency, generic meta-programming and functional programming and their practical applications. I take a pragmatic approach to project management, focusing on the management of risk and scope. I have over two decades of experience as a software professional and a background in science.
This entry was posted in C++ for the self-taught and tagged , , . Bookmark the permalink.