Metaprogramming and Lambda Expressions

Subjects

  • auto show in cppinsights

  • Templates with constants (fib,sum,array)

  • Template specialization

  • Template Metaprogramming (TMP) Physicsarrow-up-right

  • Lambdas

auto

vector example

list example

Fixed vector example

Lets make up a new container called fixed_vector

TODO add iterators

Specialization

Sometimes we just want to feel special 😂

notice the emty <> in template<> on then the struct SpecializedStruct<double>

There are 2 types of c++ developers:

  • those who like TMP

  • and those who don't

Template metaprogramming

Template metaprogramming (TMP) in C++ is a technique for expressing and executing arbitrary algorithms in compile-time using C++ templates.

TMP is a form of declarative programming in which the logic of computation is expressed without the use of explicit control flow statements (if, else, for). Some of these languages include SQL,cypher and sparql

So remember:

  • no mutability

  • no virtual functions

  • no RTTI,etc.

Sum recursion

The run-time isn't the run-time but rather the compiler is the run-time.

Fibonacci recursion

VS

more than 700x speedup (the compilation took 2x the amout so really 350x)

The entire recursion is done by the compiler, while the final program just contain a constant.

Fib with tail recursion (skip this)

The first 300 Fibonacci numbers, factoredarrow-up-right to check :)

Physics example

Velocity=metersec1,Velocity=\frac{meter}{sec^1}, Acceleration=metersec2,Acceleration=\frac{meter}{sec^2}, Energy=J=kg2metersec2Energy=J=\frac{kg^2*meter}{sec^2}

The code above allows us to only add + the same units.This is because they have the same m,k and s

But we can divide / and multiply * different unit

Use TMP (Template Meta-Programming) when:

  • A macro is not enough. You need something more complex than a macro, and you need it expanded before compiled.

  • Using recursive function with a predetermined number of loops. In this case the overhead of function calls and setting up stack variables can be avoided and runtime will significantly decrease.

  • Using loops that can be unrolled at compile time. For example hash-algorithms like MD5 and SHA1 contains well-defined block processing loops that can be unrolled with TMP.

  • When calculating constant values. If you have constants that depend on other constants in your program, they might be a candidate for TMP.

  • When the program should be portable to other platforms. In this case TMP might be an alternative to macros.

    Don't use TMP when:

  • When a macro will do. In most cases a macro will be enough. And a macro is often easier to understand than TMP.

  • You want a small executable. Templates in general, and TMP in particular will in often increase the code size.

  • Your program already takes a long time to compile. TMP might significantly increase compile time.

  • You are on a strict deadline. As noted above the complier will be very unfriendly when working with Template Metaprograms. Unless the changes you intend to make are very simple, you might want to save yourself a few hours of banging your head in the wall.

sourcearrow-up-right

Functors - Overriding Function call operator

Understanding Lambdas

What does a lambda really look like?

main- is the function, v- is void.

Here the lambda l is similar to the following struct:

Remove the double underscores to compile

Now if we were to add a int param like this

It would be similar to this

If we add a capture it would look like this:

It is similar to this:

First call the ctor with val, then call the functor with 3.

Now if we replace val with ++val wouldn't work, here is why

this is because our structs () operater is const in auto operator()(int i) const {

mutable

Now If we add the mutable specifier we would remove the const

lets use the cpp insightsarrow-up-right to see what is really happening here.

auto in param

Using auto i:

Would result to this:

Lets Review

  • Basically a lamba is an object.

  • The capture list is init list of the ctor.

  • The params are a functor that is by default const unless we use the keyword mutable

Lambda syntaxes:

  • [ captures ] ( params ) -> ret { body }

  • [ captures ] ( params ) { body }

  • [ captures ] { body }

Added C++20 syntax

  • [ captures ] <tparams>( params ) specifiers exception-> { body }

Lambda capture

Capture Many

  • & : by reference

  • = : by value

Capture individual:

  • &var_name: by-reference capture

  • this : by-reference capture of the current object

Mixing up captures:

More about lambdasarrow-up-right

Examples of Lambdas

Next we'll use the find_if function

InputIterator find_if (InputIterator first, InputIterator last, UnaryPredicate predicate); Returns: an iterator to the first element in the range [first, last] for which pred(function) returns true

explanationarrow-up-right

Lets change our main function to find first number greater than N. [N] denotes, can access only N by value

if we were to change it to this we would get an error

Because k cannot be implicitly captured because no default capture mode has been specified.

Finally, lets use the count_if function in our main, which will count numbers greater than or equal to N.

This code also works without spefically defining the return type ->bool on line 6.

We could clean this up to be one line:

Here we also used [=] to access all the variables by value

Last updated