Skip to content
Snippets Groups Projects
Commit 9e2c89ea authored by Marcus Mohr's avatar Marcus Mohr
Browse files

Starts new notebook on functors, lambda functions and std::function

parent b0b5680d
Branches
Tags release/1.0.1
No related merge requests found
%% Cell type:markdown id:789d7a72 tags:
# Functors, Lambdas and std::function
%% Cell type:markdown id:43efc656 tags:
%% Cell type:markdown id:008aa425 tags:
#### Function Pointers
In a previous example we had implemented a function to compare to objects of type ```myPair```.
%% Cell type:code id:96f5ab84 tags:
``` C++14
class myPair;
bool cmp( const myPair& left, const myPair& right );
```
%% Cell type:markdown id:ea6fcef6 tags:
We needed that function to be able to create a set of ```myPair```'s.
std::set< myPair, decltype( cmp )* > pairs ( cmp );
What we pass to std::set's constructor is a **function pointer** of type
bool (*) ( const myPair&, const myPair& )
%% Cell type:markdown id:dc79e822 tags:
***
- This way to work with functions is something that C++ inherited from C.
- Not only in the STL, but also in scientific programming we often have the case that we implement something, but want to leave some details to the user:
- Consider e.g. implementing an algorithm to approximate the integral
$$I = \int_a^b f(x)\, dx$$
- When we code that we would like to leave the details of the integrand to be specified flexibly by the user.
- As with std::set we can let the user provide a function that we then call inside our code.
- This is known as a **callback**.
- The main downside to using function (pointers) in this approach is that a free function is **stateless**. In order to influence its behaviour we have to pass arguments through its interface. That does not work nicely with the callback idea.
- Note: We can add *state* to a function by making some of its local variables ```static```. See the ```accumulate``` examples.
- However, a function is a global entity. Thus, there can always only be one state for it.
%% Cell type:markdown id:9ce08962 tags:
#### Functors
In C++ we can as one alternative use a **function object**, a.k.a. **functor**. This is an object of a class that overloads the function call operator ```operator()```.
%% Cell type:code id:9bf146ec tags:
``` C++14
#include <iostream>
class greeter{
public:
void operator() ( const std::string& str ) {
std::cout << "Greetings " << str << std::endl;
}
};
```
%% Cell type:code id:b13aa200 tags:
``` C++14
int main() {
greeter hello;
hello( "Class" );
}
```
%% Cell type:code id:f7a3701a tags:
``` C++14
main();
```
%% Output
Greetings Class
%% Cell type:markdown id:58a187c4 tags:
***
Now that was a simple functor and did only demonstrate **how** it works and not **why** that approach can be advantageous.
**Example 2:**
In our second example we will use a stateful functor that allows to perform different binary operations. The type of operation will be passed via its constructor.
%% Cell type:code id:8c0f30c5 tags:
``` C++14
#include <iostream>
class BinaryOperator {
public:
typedef enum { ADD, MULT } type;
BinaryOperator() = delete;
BinaryOperator( BinaryOperator::type op ) : whatToDo( op ) {};
int operator() ( int a, int b ) {
switch( whatToDo ) {
case ADD:
return a+b;
case MULT:
return a*b;
}
}
private:
type whatToDo;
};
```
%% Cell type:markdown id:6adaab27 tags:
**Example 3:**
So how would our set example for myPair look like with a functor?
%% Cell type:code id:856ee747 tags:
``` C++14
%%file functor.cpp
#include <iostream>
#include <set>
struct myPair {
myPair( int fir, int sec ) : fir_( fir ), sec_( sec ) {
std::cout << "(" << fir_ << "," << sec_ << ") constructed" << std::endl;
};
int fir_;
int sec_;
};
struct myCompare {
bool operator() ( const myPair& left, const myPair& right ) const {
int val1 = left.fir_ * left.fir_ + left.sec_ * left.sec_;
int val2 = right.fir_ * right.fir_ + right.sec_ * right.sec_;
return val1 < val2;
}
};
int main() {
myCompare cmp;
std::set< myPair, myCompare > pairs( cmp );
myPair p1( 1, 2 );
myPair p2( 3, 4 );
std::cout << "p1 < p2 is " << cmp( p1, p2 ) << std::endl;
std::cout << "p2 < p1 is " << cmp( p2, p1 ) << std::endl;
pairs.insert( p1 );
pairs.insert( p2 );
pairs.emplace( 3, 4 );
pairs.emplace( 5, 6 );
}
```
%% Output
Writing functor.cpp
%% Cell type:code id:4adb7f0b tags:
``` C++14
!g++ -Wall -Wextra functor.cpp
```
%% Cell type:code id:f9bfde7f tags:
``` C++14
!./a.out
```
%% Output
(1,2) constructed
(3,4) constructed
p1 < p2 is 1
p2 < p1 is 0
(3,4) constructed
(5,6) constructed
%% Cell type:code id:fe907464 tags:
``` C++14
int main() {
int a = 2;
int b = 3;
BinaryOperator adder( BinaryOperator::ADD );
BinaryOperator multiplier( BinaryOperator::MULT );
std::cout << adder( a, b ) << std::endl;
std::cout << multiplier( a, b ) << std::endl;
}
main();
```
%% Output
5
6
%% Cell type:markdown id:9d2c9715 tags:
#### Lambdas
Using *function pointers* or *functors* implies some significant coding overhead.
C++11 introduced **lambda (functions)**, also known as **closures** as a leight-weight alternative, that can be written inline in the source code.
%% Cell type:code id:8729d298 tags:
``` C++14
```
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment