{"metadata":{"orig_nbformat":4,"kernelspec":{"display_name":"C++14","language":"C++14","name":"xcpp14"},"language_info":{"codemirror_mode":"text/x-c++src","file_extension":".cpp","mimetype":"text/x-c++src","name":"c++","version":"14"}},"nbformat_minor":5,"nbformat":4,"cells":[{"cell_type":"markdown","source":"# Functors, Lambdas and std::function","metadata":{},"id":"789d7a72"},{"cell_type":"markdown","source":" \n ","metadata":{},"id":"43efc656"},{"cell_type":"markdown","source":"#### Function Pointers\nIn a previous example we had implemented a function to compare to objects of type ```myPair```.","metadata":{},"id":"008aa425"},{"cell_type":"code","source":"class myPair;\nbool cmp( const myPair& left, const myPair& right );","metadata":{},"execution_count":17,"outputs":[],"id":"96f5ab84"},{"cell_type":"markdown","source":"We needed that function to be able to create a set of ```myPair```'s.\n\n std::set< myPair, decltype( cmp )* > pairs ( cmp );\nWhat we pass to ```std::set```'s constructor is a **function pointer** of type\n\n bool (*) ( const myPair&, const myPair& )","metadata":{},"id":"ea6fcef6"},{"cell_type":"markdown","source":"***\n- This way to work with functions is something that C++ inherited from C.\n- 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: \n - Consider e.g. implementing an algorithm to approximate the integral\n$$I = \\int_a^b f(x)\\, dx$$\n - When we code that we would like to leave the details of the integrand to be specified flexibly by the user. \n - As with std::set we can let the user provide a function that we then call inside our code.\n - This is known as a **callback**.\n- 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.\n - Note: We can add *state* to a function by making some of its local variables ```static```. See the ```accumulate``` examples.\n - However, a function is a global entity. Thus, there can always only be one state for it.","metadata":{},"id":"dc79e822"},{"cell_type":"markdown","source":"#### Functors\nIn 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()```.\n\n**Example #1:**","metadata":{},"id":"9ce08962"},{"cell_type":"code","source":"#include <iostream>\n\nclass greeter{\npublic:\n void operator() ( const std::string& str ) {\n std::cout << \"Greetings \" << str << std::endl;\n }\n};","metadata":{},"execution_count":5,"outputs":[],"id":"9bf146ec"},{"cell_type":"code","source":"int main() {\n greeter hello;\n hello( \"Class\" );\n}","metadata":{},"execution_count":6,"outputs":[],"id":"b13aa200"},{"cell_type":"code","source":"main();","metadata":{},"execution_count":7,"outputs":[{"name":"stdout","output_type":"stream","text":"Greetings Class\n"}],"id":"f7a3701a"},{"cell_type":"markdown","source":"***\nNow that was a simple functor and did only demonstrate **how** it works and not **why** that approach can be advantageous.\n\n**Example 2:** \nIn 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.","metadata":{},"id":"58a187c4"},{"cell_type":"code","source":"#include <iostream>\n\nclass BinaryOperator {\n\npublic:\n\n typedef enum { ADD, MULT } type;\n\n BinaryOperator() = delete;\n BinaryOperator( BinaryOperator::type op ) : whatToDo( op ) {};\n\n int operator() ( int a, int b ) {\n switch( whatToDo ) {\n case ADD:\n return a+b;\n case MULT:\n return a*b;\n }\n }\n\nprivate:\n type whatToDo;\n\n};","metadata":{},"execution_count":8,"outputs":[],"id":"8c0f30c5"},{"cell_type":"code","source":"int main() {\n\n int a = 2;\n int b = 3;\n\n BinaryOperator adder( BinaryOperator::ADD );\n BinaryOperator multiplier( BinaryOperator::MULT );\n\n std::cout << adder( a, b ) << std::endl;\n std::cout << multiplier( a, b ) << std::endl;\n}","metadata":{},"execution_count":3,"outputs":[],"id":"05517c3a-eb4b-4827-9f74-54abf0264cca"},{"cell_type":"code","source":"main();","metadata":{},"execution_count":4,"outputs":[{"name":"stdout","output_type":"stream","text":"5\n6\n"}],"id":"2777a5a1-f8bc-4907-90e2-488967071529"},{"cell_type":"markdown","source":"**Example 3:** \nSo how would our set example for myPair look like with a functor?","metadata":{},"id":"6adaab27"},{"cell_type":"code","source":"%%file functor.cpp\n\n#include <iostream>\n#include <set>\n\nstruct myPair {\n myPair( int fir, int sec ) : fir_( fir ), sec_( sec ) {\n std::cout << \"(\" << fir_ << \",\" << sec_ << \") constructed\" << std::endl;\n };\n int fir_;\n int sec_;\n};\n\nstruct myCompare {\n bool operator() ( const myPair& left, const myPair& right ) const {\n int val1 = left.fir_ * left.fir_ + left.sec_ * left.sec_;\n int val2 = right.fir_ * right.fir_ + right.sec_ * right.sec_;\n return val1 < val2;\n }\n};\n\nint main() {\n\n myCompare cmp;\n std::set< myPair, myCompare > pairs( cmp );\n\n myPair p1( 1, 2 );\n myPair p2( 3, 4 );\n\n std::cout << \"p1 < p2 is \" << cmp( p1, p2 ) << std::endl;\n std::cout << \"p2 < p1 is \" << cmp( p2, p1 ) << std::endl;\n\n pairs.insert( p1 );\n pairs.insert( p2 );\n\n pairs.emplace( 3, 4 );\n pairs.emplace( 5, 6 );\n}","metadata":{},"execution_count":17,"outputs":[{"name":"stdout","output_type":"stream","text":"Overwriting functor.cpp\n"}],"id":"856ee747"},{"cell_type":"code","source":"!g++ -Wall -Wextra functor.cpp","metadata":{},"execution_count":18,"outputs":[],"id":"4adb7f0b"},{"cell_type":"code","source":"!./a.out","metadata":{},"execution_count":19,"outputs":[{"name":"stdout","output_type":"stream","text":"(1,2) constructed\n(3,4) constructed\np1 < p2 is 1\np2 < p1 is 0\n(3,4) constructed\n(5,6) constructed\n"}],"id":"f9bfde7f"},{"cell_type":"markdown","source":"#### Lambdas\nUsing *function pointers* or *functors* implies some significant coding overhead.\n\nC++11 introduced **lambda (functions)**, also known as **closures** as a leight-weight alternative, that can be written inline in the source code.","metadata":{},"id":"9d2c9715"},{"cell_type":"code","source":"","metadata":{},"execution_count":null,"outputs":[],"id":"8729d298"}]}
"cells": [
\ No newline at end of file
{
"cell_type": "markdown",
"id": "789d7a72",
"metadata": {},
"source": [
"# Functors, Lambdas and std::function"
]
},
{
"cell_type": "markdown",
"id": "43efc656",
"metadata": {},
"source": [
" \n",
" "
]
},
{
"cell_type": "markdown",
"id": "008aa425",
"metadata": {},
"source": [
"#### Function Pointers\n",
"In a previous example we had implemented a function to compare to objects of type ```myPair```."
]
},
{
"cell_type": "code",
"execution_count": 17,
"id": "96f5ab84",
"metadata": {},
"outputs": [],
"source": [
"class myPair;\n",
"bool cmp( const myPair& left, const myPair& right );"
]
},
{
"cell_type": "markdown",
"id": "ea6fcef6",
"metadata": {},
"source": [
"We needed that function to be able to create a set of ```myPair```'s.\n",
"What we pass to std::set's constructor is a **function pointer** of type\n",
"\n",
" bool (*) ( const myPair&, const myPair& )"
]
},
{
"cell_type": "markdown",
"id": "dc79e822",
"metadata": {},
"source": [
"***\n",
"- This way to work with functions is something that C++ inherited from C.\n",
"- 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: \n",
" - Consider e.g. implementing an algorithm to approximate the integral\n",
"$$I = \\int_a^b f(x)\\, dx$$\n",
" - When we code that we would like to leave the details of the integrand to be specified flexibly by the user. \n",
" - As with std::set we can let the user provide a function that we then call inside our code.\n",
" - This is known as a **callback**.\n",
"- 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.\n",
" - Note: We can add *state* to a function by making some of its local variables ```static```. See the ```accumulate``` examples.\n",
" - However, a function is a global entity. Thus, there can always only be one state for it."
]
},
{
"cell_type": "markdown",
"id": "9ce08962",
"metadata": {},
"source": [
"#### Functors\n",
"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()```."
"Now that was a simple functor and did only demonstrate **how** it works and not **why** that approach can be advantageous.\n",
"\n",
"**Example 2:** \n",
"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",
"execution_count": 23,
"id": "8c0f30c5",
"metadata": {},
"outputs": [],
"source": [
"#include <iostream>\n",
"\n",
"class BinaryOperator {\n",
"\n",
"public:\n",
"\n",
" typedef enum { ADD, MULT } type;\n",
"\n",
" BinaryOperator() = delete;\n",
" BinaryOperator( BinaryOperator::type op ) : whatToDo( op ) {};\n",
"\n",
" int operator() ( int a, int b ) {\n",
" switch( whatToDo ) {\n",
" case ADD:\n",
" return a+b;\n",
" case MULT:\n",
" return a*b;\n",
" }\n",
" }\n",
"\n",
"private:\n",
" type whatToDo;\n",
"\n",
"};"
]
},
{
"cell_type": "markdown",
"id": "6adaab27",
"metadata": {},
"source": [
"**Example 3:** \n",
"So how would our set example for myPair look like with a functor?"
]
},
{
"cell_type": "code",
"execution_count": 25,
"id": "856ee747",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Writing functor.cpp\n"
]
}
],
"source": [
"%%file functor.cpp\n",
"\n",
"#include <iostream>\n",
"#include <set>\n",
"\n",
"struct myPair {\n",
" myPair( int fir, int sec ) : fir_( fir ), sec_( sec ) {\n",
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 ) {};