PrevUpHomeNext

Introduction

"I like to start documentation with a quote. A nice, pithy one."

— Eric Niebler (paraphrased)

Motivation

Expression templates are rad. They are used in lots of libraries; here are just three of the most impressive:

However, this can come at a high cost. Expression templates are costly to implement and maintain. Each of Eigen and Boost.Ublas has a large volume of complex expression template code that cannot be reused elsewhere.

With the language facilities available in the C++14 and C++17 standards, an expression template library is now straightforward to write and use, and has very reasonable compile times.

As a quick example, let's say we are doing a bit of matrix math, and we write this statement:

D = A * B + C;

in which all the variables are matrices. It turns out that making a temporary for A * B and then another temporary for the resulting product plus C is very inefficient. Most matrix math libraries will have a single function that does it in one go:

mul_add_assign(D, A, B, C);

If you use a matrix library that offers both kinds of syntax, you have to notice when some bit of operator-using code should be replaced with some more efficient function; this is tedious and error-prone. If the library does not provide the operator syntax at all, only providing the more-efficient function calls, code using the library is a lot less writable and readable.

Using Boost.YAP, you can write some library code that enables expressions like D = A * B + C to be automatically transformed into expressions like mul_add_assign(D, A, B, C).

Consider another example. Many of us have used Unix command line tools to remove duplicate lines in a file:

sort file_with_duplicates | uniq > file_without_duplicates

We can do something very similar with the standard algorithms, of course:

std::vector<int> v1 = {0, 2, 2, 7, 1, 3, 8};
std::sort(v1.begin(), v1.end());
auto it = std::unique(v1.begin(), v1.end());
std::vector<int> const v2(v1.begin(), it);
assert(v2 == std::vector<int>({0, 1, 2, 3, 7, 8}));

However, it would be much better if our code did exactly that, but with a more concise syntax:

std::vector<int> v1 = {0, 2, 2, 7, 1, 3, 8};
std::vector<int> const v2 = sort(v1) | unique;
assert(v2 == std::vector<int>({0, 1, 2, 3, 7, 8}));

This looks much more similar to the Unix command line above. (Let's pretend that Range-v3 doesn't already do almost exactly this.)

Boost.YAP can be used to do both of these things, in a pretty small amount of code. In fact, you can jump right into the Pipable Algorithms example if you want to see how the second one can be implemented.

Features


PrevUpHomeNext