Functional programming associated with C++
Functional programming is another addition to C++ that provides the user with compiler assistance, in the form of lambda functions. Currently, this must be carried out by hand in C.
In C, a functional programming construct can be achieved using a callback. For example, consider the following code:
void
guard(void (*ptr)(int *val), int *val)
{
lock();
ptr(val);
unlock();
}
void
inc(int *val)
{
*val++;
}
void
dec(int *val)
{
*val--;
}
void
foo()
{
int count = 0;
guard(inc, &count);
guard(dec, &count);
}
In the preceding code example, we create a guard function that locks a mutex, calls a function that operates on a value, and then unlocks the mutex on exit. We then create two functions, one that increments a value given to it, and one that decrements a value given to it. Finally, we create a function that instantiates a count, and then increments the count and decrements the count using the guard function.
There are a couple of issues with this code:
- The first issue is the need for pointer logic to ensure we can manipulate the variable we wish to operate on. We are also required to manually pass this pointer around to keep track of it. This makes the APIs clunky, as we have a lot of extra code that we have to write manually for such a simple example.
- The function signature of the helper functions is static. The guard function is a simple one. It locks a mutex, calls a function, and then unlocks it. The problem is that, since the parameters of the function must be known while writing the code instead of at compile time, we cannot reuse this function for other tasks. We will need to hand-write the same function for each function signature type we plan to support.
The same example can be written using C++ as follows:
template<typename FUNC>
guard(FUNC f)
{
lock();
f();
unlock();
}
void
foo()
{
int count = 0;
guard(inc, [&]{ count++ });
guard(inc, [&]{ count-- });
}
In the preceding example, the same functionality is provided, but without the need for pointers. In addition, the guard function is generic and can be used for more than one case. This is accomplished by leveraging both template programming and functional programming.
The lambda provides the callback, but the parameters of the callback are encoded into the lambda's function signature, which is absorbed by the use of a template function. The compiler is capable of generating a version of the guard function for use that takes the parameters (in this case, a reference to the count variable) and storing it in the code itself, removing the need for users to do this by hand.
The preceding example will be used a lot in this book, especially when creating benchmarking examples, as this pattern gives you the ability to wrap functionality in code designed to time the execution of your callback.