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

Performs small fixes (cell ordering, typos) to notebook #8

parent 9e2c89ea
No related branches found
No related tags found
No related merge requests found
%% Cell type:markdown id:789d7a72 tags: %% Cell type:markdown id:789d7a72 tags:
# Functors, Lambdas and std::function # Functors, Lambdas and std::function
%% Cell type:markdown id:43efc656 tags: %% Cell type:markdown id:43efc656 tags:
%% Cell type:markdown id:008aa425 tags: %% Cell type:markdown id:008aa425 tags:
#### Function Pointers #### Function Pointers
In a previous example we had implemented a function to compare to objects of type ```myPair```. In a previous example we had implemented a function to compare to objects of type ```myPair```.
%% Cell type:code id:96f5ab84 tags: %% Cell type:code id:96f5ab84 tags:
``` C++14 ``` C++14
class myPair; class myPair;
bool cmp( const myPair& left, const myPair& right ); bool cmp( const myPair& left, const myPair& right );
``` ```
%% Cell type:markdown id:ea6fcef6 tags: %% Cell type:markdown id:ea6fcef6 tags:
We needed that function to be able to create a set of ```myPair```'s. We needed that function to be able to create a set of ```myPair```'s.
std::set< myPair, decltype( cmp )* > pairs ( cmp ); std::set< myPair, decltype( cmp )* > pairs ( cmp );
What we pass to ```std::set```'s constructor is a **function pointer** of type
What we pass to std::set's constructor is a **function pointer** of type
bool (*) ( const myPair&, const myPair& ) bool (*) ( const myPair&, const myPair& )
%% Cell type:markdown id:dc79e822 tags: %% Cell type:markdown id:dc79e822 tags:
*** ***
- This way to work with functions is something that C++ inherited from C. - 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: - 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 - Consider e.g. implementing an algorithm to approximate the integral
$$I = \int_a^b f(x)\, dx$$ $$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. - 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. - 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**. - 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. - 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. - 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. - However, a function is a global entity. Thus, there can always only be one state for it.
%% Cell type:markdown id:9ce08962 tags: %% Cell type:markdown id:9ce08962 tags:
#### Functors #### 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()```. 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()```.
**Example #1:**
%% Cell type:code id:9bf146ec tags: %% Cell type:code id:9bf146ec tags:
``` C++14 ``` C++14
#include <iostream> #include <iostream>
class greeter{ class greeter{
public: public:
void operator() ( const std::string& str ) { void operator() ( const std::string& str ) {
std::cout << "Greetings " << str << std::endl; std::cout << "Greetings " << str << std::endl;
} }
}; };
``` ```
%% Cell type:code id:b13aa200 tags: %% Cell type:code id:b13aa200 tags:
``` C++14 ``` C++14
int main() { int main() {
greeter hello; greeter hello;
hello( "Class" ); hello( "Class" );
} }
``` ```
%% Cell type:code id:f7a3701a tags: %% Cell type:code id:f7a3701a tags:
``` C++14 ``` C++14
main(); main();
``` ```
%% Output %% Output
Greetings Class Greetings Class
%% Cell type:markdown id:58a187c4 tags: %% 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. Now that was a simple functor and did only demonstrate **how** it works and not **why** that approach can be advantageous.
**Example 2:** **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. 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: %% Cell type:code id:8c0f30c5 tags:
``` C++14 ``` C++14
#include <iostream> #include <iostream>
class BinaryOperator { class BinaryOperator {
public: public:
typedef enum { ADD, MULT } type; typedef enum { ADD, MULT } type;
BinaryOperator() = delete; BinaryOperator() = delete;
BinaryOperator( BinaryOperator::type op ) : whatToDo( op ) {}; BinaryOperator( BinaryOperator::type op ) : whatToDo( op ) {};
int operator() ( int a, int b ) { int operator() ( int a, int b ) {
switch( whatToDo ) { switch( whatToDo ) {
case ADD: case ADD:
return a+b; return a+b;
case MULT: case MULT:
return a*b; return a*b;
} }
} }
private: private:
type whatToDo; type whatToDo;
}; };
``` ```
%% Cell type:code id:05517c3a-eb4b-4827-9f74-54abf0264cca 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;
}
```
%% Cell type:code id:2777a5a1-f8bc-4907-90e2-488967071529 tags:
``` C++14
main();
```
%% Output
5
6
%% Cell type:markdown id:6adaab27 tags: %% Cell type:markdown id:6adaab27 tags:
**Example 3:** **Example 3:**
So how would our set example for myPair look like with a functor? So how would our set example for myPair look like with a functor?
%% Cell type:code id:856ee747 tags: %% Cell type:code id:856ee747 tags:
``` C++14 ``` C++14
%%file functor.cpp %%file functor.cpp
#include <iostream> #include <iostream>
#include <set> #include <set>
struct myPair { struct myPair {
myPair( int fir, int sec ) : fir_( fir ), sec_( sec ) { myPair( int fir, int sec ) : fir_( fir ), sec_( sec ) {
std::cout << "(" << fir_ << "," << sec_ << ") constructed" << std::endl; std::cout << "(" << fir_ << "," << sec_ << ") constructed" << std::endl;
}; };
int fir_; int fir_;
int sec_; int sec_;
}; };
struct myCompare { struct myCompare {
bool operator() ( const myPair& left, const myPair& right ) const { bool operator() ( const myPair& left, const myPair& right ) const {
int val1 = left.fir_ * left.fir_ + left.sec_ * left.sec_; int val1 = left.fir_ * left.fir_ + left.sec_ * left.sec_;
int val2 = right.fir_ * right.fir_ + right.sec_ * right.sec_; int val2 = right.fir_ * right.fir_ + right.sec_ * right.sec_;
return val1 < val2; return val1 < val2;
} }
}; };
int main() { int main() {
myCompare cmp; myCompare cmp;
std::set< myPair, myCompare > pairs( cmp ); std::set< myPair, myCompare > pairs( cmp );
myPair p1( 1, 2 ); myPair p1( 1, 2 );
myPair p2( 3, 4 ); myPair p2( 3, 4 );
std::cout << "p1 < p2 is " << cmp( p1, p2 ) << std::endl; std::cout << "p1 < p2 is " << cmp( p1, p2 ) << std::endl;
std::cout << "p2 < p1 is " << cmp( p2, p1 ) << std::endl; std::cout << "p2 < p1 is " << cmp( p2, p1 ) << std::endl;
pairs.insert( p1 ); pairs.insert( p1 );
pairs.insert( p2 ); pairs.insert( p2 );
pairs.emplace( 3, 4 ); pairs.emplace( 3, 4 );
pairs.emplace( 5, 6 ); pairs.emplace( 5, 6 );
} }
``` ```
%% Output %% Output
Writing functor.cpp Overwriting functor.cpp
%% Cell type:code id:4adb7f0b tags: %% Cell type:code id:4adb7f0b tags:
``` C++14 ``` C++14
!g++ -Wall -Wextra functor.cpp !g++ -Wall -Wextra functor.cpp
``` ```
%% Cell type:code id:f9bfde7f tags: %% Cell type:code id:f9bfde7f tags:
``` C++14 ``` C++14
!./a.out !./a.out
``` ```
%% Output %% Output
(1,2) constructed (1,2) constructed
(3,4) constructed (3,4) constructed
p1 < p2 is 1 p1 < p2 is 1
p2 < p1 is 0 p2 < p1 is 0
(3,4) constructed (3,4) constructed
(5,6) 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: %% Cell type:markdown id:9d2c9715 tags:
#### Lambdas #### Lambdas
Using *function pointers* or *functors* implies some significant coding overhead. 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. 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: %% Cell type:code id:8729d298 tags:
``` C++14 ``` C++14
``` ```
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment