cpp std function

some tips about the cpp std::function

The main contents here come from the https://en.cppreference.com/w/cpp/utility/functional/function, I just found that the std::function is a good callable abstraction in cpp, and take some notes here.

defination

1
2
template< class R, class... Args >
class function<R(Args...)>;

It is straight forward, since we only need the return data type (R), the input data type (Args…) for a callable.

For instances

1
2
void print_num(int i){...}
std::function<void(int)> f_display = print_num;

For defination of the std::function, we just need to specify the void and int in its defination.

examples

the flexibility of std::function is the wrapp the different callable in cpp, although it might not a necessary things from the perspective of the system programming, it is good to grasp it for quickly understanding other people’s code.

this is the whole function examples from the cpp references, and I added more comments

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
#include <functional>
#include <iostream>

struct Foo {
Foo(int num) : num_(num) {}
void print_add(int i) const { std::cout << num_+i << '\n'; }
int num_;
};

void print_num(int i)
{
std::cout << i << '\n';
}

struct PrintNum {
void operator()(int i) const
{
std::cout << i << '\n';
}
};

int main()
{
// store a free function
// this is the basic usage of the function as a wrapper
// the code is more clean compared with define a function pointer
std::function<void(int)> f_display = print_num;
f_display(-9);

// store a lambda
// this may looks more general since we basically do not care about the
// type of the input parameter and return parameters
// we can call different functions with the lambda expression
std::function<void()> f_display_42 = []() { print_num(42); };
f_display_42();

// store the result of a call to std::bind
// this may more like the grammar suger and we bind the function with a particular parameter
// we can also wrap it by the std::function
std::function<void()> f_display_31337 = std::bind(print_num, 31337);
f_display_31337();

// store a call to a member function
// it looks fancy to call a member function of the instance
// the first parameter should be the reference of the instance
// we need to use & when assign the member function to the std::function but we do not need the & for the common function previously
std::function<void(const Foo&, int)> f_add_display = &Foo::print_add;
const Foo foo(314159);
f_add_display(foo, 1);
f_add_display(314159, 1);

// store a call to a data member accessor
std::function<int(Foo const&)> f_num = &Foo::num_;
std::cout << "num_: " << f_num(foo) << '\n';

// store a call to a member function and object
using std::placeholders::_1;
std::function<void(int)> f_add_display2 = std::bind( &Foo::print_add, foo, _1 );
f_add_display2(2);

// store a call to a member function and object ptr
std::function<void(int)> f_add_display3 = std::bind( &Foo::print_add, &foo, _1 );
f_add_display3(3);

// store a call to a function object
// the PrintNum is a kind of functor in this case that overload the () operator
std::function<void(int)> f_display_obj = PrintNum();
f_display_obj(18);
}

/* output
-9
42
31337
314160
314160
num_: 314159
314161
314162
18

*/

more examples

In one project, we use the function as a wrapper for factory pattern.

this is the key data structure:

1
2
std::unordered_map<std::string,
std::function<std::unique_ptr<Backend>(const PipelineFactoryArgs&)>> create_fn;

the key of the map is the identifier of the backend, the value of the map is a std::function wrapper. For this wrapper, the return value is a unique pointer which hold the backend class, and the input parameter is the pipelineFactorArgs.

when we init the map, the code (registration function) looks like this:

1
2
3
4
5
6
7
8
9
10
11
12
template<typename BackendType>
class __ColzaBackendRegistration {
...
public:

__ColzaBackendRegistration(const std::string& backend_name)
{
colza::PipelineFactory::create_fn[backend_name] = [](const colza::PipelineFactoryArgs& args) {
return BackendType::create(args);
};
}
};

we can see that one anonimous function (lambda function) is used here to wrap the creat function. the BackendType is sent from the template parameter. By this way, we can achieve the creation for anytype of the backend by creating a new backend registration class.

推荐文章