Templates as first-class citizens in C++11

templates as citizens

C++11 treats functions as first-class citizens and this gives us ability to construct a lot of nice things as design patterns borrowed from functional languages. Meanwhile C++ has very powerful template metaprogramming. This post is aimed to make templated functions closer to first-class citizens, and to construct some simplicity and beauty which you can get from it.

Also here will be implementation for currying of such functions! If you don’t know what currying is – just remember std::bind.

And to make it shine we’ll add piping. (This article will improve some ideas from post about functional piping ). This step is optional and you can replace such piping with commas and function calls.

TEMPLATE TO SIMPLE FUNCTOR

Ok, let’s start from very simple template function.

I will use different styles of code colouring here to indicate difference between example code and inner implementation which is similar for all cases.

To pass it to some function we need to wrap it into functor:

Here operator() is overloaded to pass all arguments to print template. As we will modify this functor to cover all cases, the list of arguments is provided using variadic templates. Note: you can define all functions inside such wrappers from the start, but there is way to do it through macro:

This small macro creates class from templated function.

Important note for macro haters. Yes, macros should be avoided when you can substitute them using variadic templates and other new stuff. But still there are some cases when you can’t do that. So when you have couple of very small obvious macro definitions which will make your code considerably smaller, more readable and maintainable – use it. Such cases are rare and when it happens C++ committee should look upon it and add some means into the language itself to fix it.

Example:  

We redefined print in smaller scope as instance of object. Now we can use it as function and pass as argument to another function. Note that the same function is used with arguments of different type – so no need to create distinct functors.

Next we will create some additional instruments to work with such ‘templated’ functors more effectively.

PASS TUPLE TO FUNCTION AS ARGUMENT LIST

As we will use such ability later – let’s write simple function which will expand std::tuple and feed results into the given function.

In C++14 this could be done a bit shorter using std::integer_sequence, but at the moment I’m forced to use just C++11 ( to compile things for Android for example). The sequence of integer indices S is constructed through template recursion, and than is passed as additional argument for unpacking. This is just one of possible implementations.

Usage:

The next step is the idea that you can combine function input parameters into tuple using several steps instead of one std::make_tuple call.

TUPLE CONCATENATION

For concatenation of tuples C++11 has function named std::tuple_cat. To make things more compact we can overload << operator to add new parameter into tuple.

Usage:

FUNCTIONAL PIPELINE 

See post about functional piping to get the idea (we use different implementation for piping here). Anyway, this step is optional and you can replace such piping with commas and function calls, but piping here looks very nice.

First simple overload will provide ability to pipe single argument into function:

Usage:

One new sample template function count() is just returning size() of provided collection. We provide the list of three strings into count function and after that pipe the result into print().

This is nice but application seems to be rather limited as functions tend to have more than one argument. Let’s solve this using some curry…

CURRYING

curry

Currying in C++11 is usually made using std::bind, which is flexible and useful for a lot of cases. But we can’t use it for templates. So let’s create some wrapper class which will implement curry and provide the simplest way to work with it inside pipelines.

General way (like in haskell) is to curry from left to right argument – so call f(1,2,3) is equivalent to f(1)(2)(3). But in piping example everything is slightly different –f(1,2,3) is equivalent to 1 | f(2,3). In other words I want to curry both ways – left and right. Yes, std::bind gives ability to specify any order but this is for price of a bit longer syntax that it’s required in general cases. And haskell’s syntax is not so good also, imho. That’s because it’s hard for programmer to identify difference between currying and function call. So here i’m using different syntax (and this decision is optional) where you have separate operators for function call and argument curry.

The next class is called fn_universal as representation of some function which is polymorphic (in sense that it can accept arguments of different types) and can be used with currying and piping. May be this class will be extended further later. You might want to rename it to fn_curryed or something like it.

Of course, to hold arguments we will use tuples. The implementation:

Note that class is immutable.

The main nice thing about this class: there is no restriction on types or count of arguments. All what is done here is combining provided arguments into two tuples – right and left parameters. And when time comes to execute function we just combine everything into single tuple and feed this tuple into function. So you can curry function which supports variadic number of arguments!

And to provide easy interface for currying I use the following overloads for operators:

Let’s add small builder helper. Also (very optional) I might add definition macro to make examples a bit shorter.

This line is just defining new ‘universal’ function from given template function. You could change this using more convenient way.

EXAMPLES FOR CURRYING

Trivial examples:

Note the order of arguments. Not so complicated to read.

Now let’s write couple of template functions to get some realistic examples. This will look like functional operators. I even could mark these functions as inner implementation as they are so common, but to underline that you have the ability to control this behaviour yourself I leave it marked as sample:

It’s obvious that such primitives can be reused avoiding a lot of code duplication.

And one minor function – make sum of all arguments:

Here could be more trivial implementation and couple of overloads, but to show that you have no limitation with count of arguments I leave it like this.

Also we can modify print to handle any amount of arguments:

AND NOW: Let’s try this in action:

This is quite readable and functional-style code. Not the one we could expect from C++.

So the idea is that now we can write small universal templates and combine them in pipelines as we like. Looks great!

Consider small templates as small universal building blocks.

More examples…

Let’s assume we are working with some business data like collection of users.

First 3 methods are trivial – checking  that name or id fields are equal to given values and that whole object is not null.

The last one is find method inside container (we assume here that business data items are presented as immutable structures inside smart pointers). Find receives one function as validate functor and key argument to pass to this validator. Let’s see how to use it:

Such examples do not require an explanation I suppose!

We can convert user names to xml like this:

As you can see we can still use lambdas inside expressions.

FUNCTIONAL CHAINS

take-action

Now let’s add one more important element to proposed scheme. I want to be able to compose pipeline of functions, store it under some name and then call it multiple times as single function. In other words i want this:

In fact this is simple functional composition (like the one discussed in appendix here ), and it can be implemented with ease in case of general functions. But when we are talking about templates things become a bit more complicated. But this is still possible!

How this works? When constructing such chain we just put functors into tuple. Tricky part comes when we need to call the function – we need to construct universal caller without knowing function specifications. This is possible using compile-time recursion and std::decltype/std::declval. Creating dummy arguments we go through whole chain recursively and detect final result type.

And to make it work under GCC i hit one of compiler bugs – inside call method recursion during type detection you need to add ‘this->‘ to make it compile (bug).

And to chain functors into the list we need to overload | operator again:

Now we can use this whole concept like:

Note that we use different types during chain execution.

So now we have the ability to store and combine pipelines as we like. Nice.

FINAL TOUCH – MONADIC PIPING (OPTIONAL)

This part is optional but it fits so well… because here will be very smooth shift to monads. Smooth and easy.

What if we change the direction of piping? Let’s pipe functions into data instead piping data into functions! Of cause, we can’t do this with raw data – we need some object to wrap it and call received functions for us. This object can do some additional manipulations with functions along the way. And yes, thats a kind of monad.

Why we need this kind of stuff? Because we can gain additional control of functional chain evaluation.

As example i’ll use maybe monad – which has very simple logic: if we have nullptr as output from any function in chain – we don’t execute further and report empty state. This is kind of implicit error protection.

Implementation will be a bit extended to support pointer and non-pointer types.

Also, this is not ‘true’ monad as true monadic bind operation requieres functions which return monad as result. But using the following implementation you can apply normal functions without any modifications.

This class can be extended to handle error messages and so on.

I use the same pipe | operator here because its ‘unix’ meaning is perfectly applicable here.

Examples:

So here we execute processing chain on non existing user and nothing bad happens. Also we can pipe our saved chain countUsers into maybe as expected.

I could produce a lot of other examples here, but may be this is nice topic for another post.

CONCLUSION

Using compile-time recursion and type-detection you can create powerful tools for combining templated functional blocks. Using currying and piping together with functional chains gives very flexible instrument to build compact functional-style methods in C++11. Monadic execution could be added to this scheme without any problems.

Full working sample on github:    gist

ps. About templating – i think usage of templates in production should be very minimalistic because template overuse can lead to very unmaintainable code. So when using proposed scheme keep in mind that all blocks should be very small and logically reasonable.

 

  • Hi, that was a very interesting and enlightening read. Thanks!

    I have a small question though: is it purely academic for now or was it used in some real project? In the latter case how maintainable was the code in long term?

    • Victor Laskin

      Hello, thanks for comment. No, it’s not just academic – you can easily compile gist from link at the end of post. It may be more problematic to get your developers think in more functional way, but starting to use the technic is easy.
      As this is quite new approach I can’t provide any information about long term maintainability (A lot of people still can’t use even c++11 in production).

  • AntonBikineev

    I just read your first code snippet and got a question. Is there any sense to use std forward for non universal references?

    • Victor Laskin

      Anton, Thanks. No, just && was missing there. I fixed it.

  • Dan George

    Hi Victor, thanks for another inspiring post. I think there is a typo in the tfn_print wrapper: dectype(X( s/b dectype(print(, right?

    • Victor Laskin

      Yes, thanks. Sure, it’s obvious typo during manual macro expansion. ps. I can’t wait for C++14 where result types could be written as decltype(auto) and whole code will be a lot shorter.

  • qicosmos

    Hi Victor, i don’t think “<<" is a good choice, it's not natural, your old post "functional piping" is better.

    • Victor Laskin

      Hello, yes, it’s less natural – but you can switch it with some other syntax you prefere. The main idea was to show that such decomposition is possible.

  • Goobley

    Hey, great job with this, I’ve based some code on some of this, in the process of becoming a small header-only library. Any chance you could officially state it being under a certain license so I can properly credit it. (I’d rather anything non-GPL). I found your talk via your MeetingCPP 2015 slides, any chance there’ll be a video of the talk that goes with them? Cheers!

    • Victor Laskin

      Thanks. No problem with that – you could use code as you like. As for the video – it will be available soon (I expect it to be at jan or feb).

  • qicosmos

    Hi, Victor, can you improve the code with c++14, now vs2015 and gcc6.0 are surpport most c++11 features. I think c++14 will make the code clean and simple.

  • qicosmos

    call_impl can be resolved by std::index_sequence, here is the code:
    template
    auto call_impl(Arg&& arg, const std::index_sequence&) const ->decltype(std::get(functions_)(std::forward(arg)))
    {
    return std::get
    (functions_)(std::forward(arg));
    }

    template
    auto call_impl(Arg&& arg, const std::index_sequence
    &) const ->decltype(call_impl(std::get(functions_)(std::forward(arg)), std::index_sequence{}))
    {
    return call_impl(std::get
    (functions_)(std::forward(arg)), std::index_sequence{});
    }

    template
    auto call(Arg&& arg) const-> decltype(call_impl(std::forward(arg), std::make_index_sequence{}))
    {
    return call_impl(std::forward(arg), std::make_index_sequence{});
    }