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

Adds std::boolalpha to some examples

parent cd68b973
Branches
No related merge requests found
This diff is collapsed.
%% 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:** ##### **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: %% Cell type:code id:05517c3a-eb4b-4827-9f74-54abf0264cca tags:
``` C++14 ``` C++14
int main() { int main() {
int a = 2; int a = 2;
int b = 3; int b = 3;
BinaryOperator adder( BinaryOperator::ADD ); BinaryOperator adder( BinaryOperator::ADD );
BinaryOperator multiplier( BinaryOperator::MULT ); BinaryOperator multiplier( BinaryOperator::MULT );
std::cout << adder( a, b ) << std::endl; std::cout << adder( a, b ) << std::endl;
std::cout << multiplier( a, b ) << std::endl; std::cout << multiplier( a, b ) << std::endl;
} }
``` ```
%% Cell type:code id:2777a5a1-f8bc-4907-90e2-488967071529 tags: %% Cell type:code id:2777a5a1-f8bc-4907-90e2-488967071529 tags:
``` C++14 ``` C++14
main(); main();
``` ```
%% Output %% Output
5 5
6 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 << std::boolalpha;
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 true
p2 < p1 is 0 p2 < p1 is false
(3,4) constructed (3,4) constructed
(5,6) constructed (5,6) constructed
%% Cell type:markdown id:c51deb94-f9c2-4a88-914b-ddf70f29601e tags: %% Cell type:markdown id:c51deb94-f9c2-4a88-914b-ddf70f29601e tags:
#### std::function #### std::function
Quoting from cppreference: Quoting from cppreference:
> Class template std::function is a general-purpose polymorphic function wrapper. Instances of std::function can store, copy, and invoke any CopyConstructible Callable target -- functions, lambda expressions, bind expressions, or other function objects, as well as pointers to member functions and pointers to data members. > Class template std::function is a general-purpose polymorphic function wrapper. Instances of std::function can store, copy, and invoke any CopyConstructible Callable target -- functions, lambda expressions, bind expressions, or other function objects, as well as pointers to member functions and pointers to data members.
> >
> The stored callable object is called the target of std::function. If a std::function contains no target, it is called empty. Invoking the target of an empty std::function results in std::bad_function_call exception being thrown. > The stored callable object is called the target of std::function. If a std::function contains no target, it is called empty. Invoking the target of an empty std::function results in std::bad_function_call exception being thrown.
The functionality of ```std::function``` is quite useful especially in combination with lambda functions (see below). The functionality of ```std::function``` is quite useful especially in combination with lambda functions (see below).
%% Cell type:markdown id:6c584ecb-10c3-4ff2-ab53-2a66c0963009 tags: %% Cell type:markdown id:6c584ecb-10c3-4ff2-ab53-2a66c0963009 tags:
##### **Example 4:** ##### **Example 4:**
Assign a simple free-function Assign a simple free-function
%% Cell type:code id:21120bbb-03a7-4048-97f4-0426c40c5bac tags: %% Cell type:code id:21120bbb-03a7-4048-97f4-0426c40c5bac tags:
``` C++14 ``` C++14
#include <iostream> #include <iostream>
void sayIt() { std::cout << "He said captain, I said wot" << std::endl; } void sayIt() { std::cout << "He said captain, I said wot" << std::endl; }
``` ```
%% Cell type:code id:bab0ab95-e5fd-4368-b3c2-77bcb869cfe0 tags: %% Cell type:code id:bab0ab95-e5fd-4368-b3c2-77bcb869cfe0 tags:
``` C++14 ``` C++14
// generate an object of type std::function; // generate an object of type std::function;
// need this header file // need this header file
#include <functional> #include <functional>
// need to supply the prototype of the function/invocation that we want to target // need to supply the prototype of the function/invocation that we want to target
std::function< void( ) > sensible; std::function< void( ) > sensible;
``` ```
%% Cell type:code id:b4e3f68a-6c70-49e7-a04c-924804c4cb35 tags: %% Cell type:code id:b4e3f68a-6c70-49e7-a04c-924804c4cb35 tags:
``` C++14 ``` C++14
// std::function overloads 'operator bool' so we can check if anything is targetted, yet // std::function overloads 'operator bool' so we can check if anything is targetted, yet
if( !sensible ) { if( !sensible ) {
std::cout << "empty -> nothing to do!" << std::endl; std::cout << "empty -> nothing to do!" << std::endl;
} }
``` ```
%% Output %% Output
empty -> nothing to do! empty -> nothing to do!
%% Cell type:code id:c5b3e4d0-64b4-47c6-848f-f787bf9c0b17 tags: %% Cell type:code id:c5b3e4d0-64b4-47c6-848f-f787bf9c0b17 tags:
``` C++14 ``` C++14
// now assign a target to our std::object // now assign a target to our std::object
sensible = sayIt; sensible = sayIt;
// and perform the invocation // and perform the invocation
sensible(); sensible();
``` ```
%% Output %% Output
He said captain, I said wot He said captain, I said wot
%% Cell type:markdown id:1ad030ff-9527-4436-be80-72baf9ac7b29 tags: %% Cell type:markdown id:1ad030ff-9527-4436-be80-72baf9ac7b29 tags:
##### **Example 5:** ##### **Example 5:**
Now lets use a functor with a slightly more complicated interface Now lets use a functor with a slightly more complicated interface
%% Cell type:code id:ff8d558b-da83-46d9-9842-8e0d9d5ba56d tags: %% Cell type:code id:ff8d558b-da83-46d9-9842-8e0d9d5ba56d tags:
``` C++14 ``` C++14
// that's our functor // that's our functor
struct Mult { struct Mult {
int operator() ( int a, int b ) { return a*b; } int operator() ( int a, int b ) { return a*b; }
} }
``` ```
%% Cell type:code id:b5790b96-bf62-4d6e-ad9c-e56839882728 tags: %% Cell type:code id:b5790b96-bf62-4d6e-ad9c-e56839882728 tags:
``` C++14 ``` C++14
// if we want, we can alias our std::function type // if we want, we can alias our std::function type
using binaryOp = std::function< int(int,int) >; using binaryOp = std::function< int(int,int) >;
// now instantiate an object that targets the functor // now instantiate an object that targets the functor
binaryOp multiply = Mult(); binaryOp multiply = Mult();
// note that we needed the () to differentiate between the invocation and the functor itself! // note that we needed the () to differentiate between the invocation and the functor itself!
multiply(2,3) multiply(2,3)
``` ```
%% Output %% Output
6 6
%% Cell type:markdown id:589b7083-7a36-4ff8-bdd0-c46f9994228c tags: %% Cell type:markdown id:589b7083-7a36-4ff8-bdd0-c46f9994228c tags:
##### **Example 6:** ##### **Example 6:**
A member function of a class can also serve as target A member function of a class can also serve as target
%% Cell type:code id:646b302f-cdf5-40ed-98f8-01d98a9b277a tags: %% Cell type:code id:646b302f-cdf5-40ed-98f8-01d98a9b277a tags:
``` C++14 ``` C++14
struct ItemsInStore{ struct ItemsInStore{
int num; int num;
ItemsInStore() = delete; ItemsInStore() = delete;
ItemsInStore( int num ) : num(num) {}; ItemsInStore( int num ) : num(num) {};
void decrementAndShowRest() { void decrementAndShowRest() {
std::cout << "Only " << --num << " items left!" << std::endl; std::cout << "Only " << --num << " items left!" << std::endl;
} }
}; };
``` ```
%% Cell type:code id:b56b370a-654f-4665-a86d-1b4a1497a813 tags: %% Cell type:code id:b56b370a-654f-4665-a86d-1b4a1497a813 tags:
``` C++14 ``` C++14
std::function< void( ItemsInStore& ) > takeOne = &ItemsInStore::decrementAndShowRest; std::function< void( ItemsInStore& ) > takeOne = &ItemsInStore::decrementAndShowRest;
``` ```
%% Cell type:code id:b267e62f tags: %% Cell type:code id:b267e62f tags:
``` C++14 ``` C++14
ItemsInStore candyBox( 5 ); ItemsInStore candyBox( 5 );
takeOne( candyBox ); takeOne( candyBox );
takeOne( candyBox ); takeOne( candyBox );
``` ```
%% Output %% Output
Only 4 items left! Only 4 items left!
Only 3 items left! Only 3 items left!
%% 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% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment