What is the best way of passing a callback function parameter in C++?
I thought of simply using templates, like this:
template <typename Function>
void DoSomething(Function callback)
This is the way used e.g. in std::sort for the comparison function object.
What about passing using &&? E.g.:
template <typename Function>
void DoSomething(Function&& callback)
What are the pros and cons of those two methods, and why does the STL uses the former e.g. in std::sort?
The
template <typename Function>
void DoSomething(Function&& callback)
… which uses a forwarding reference to pass by reference, is IMHO superior for the case where the function just uses the callback. Because a functor object, although usually small, can be arbitrarily large. Passing it by value then incurs some needless overhead.
The formal argument can end up as T&, T const& or T&& depending on the actual argument.
On the other hand, passing a simple function pointer by reference involves an extra needless indirection, which in principle could mean some slight overhead.
If in doubt whether this is significant for a given compiler, system and application, then measure.
Regarding
” why does the STL uses the former [passing by value] e.g. in std::sort?
… it's worth noting that std::sort has been there since C++98, well before forwarding references were introduced in C++11, so it could not have that signature originally.
Possibly there just has not been sufficient incentive to improve this. After all, one should generally not fix that which works. Still, extra oveloads with an “execution policy” argument are introduced in C++17.
Since this concerns a possible C++11 change, it's not covered by the usual source for rationales, namely Bjarne Stroustrup's “The design and evolution of C++”., and I do not know of any conclusive answer.
Using the template<class F> void call( F ) style, you can still get back "pass by reference". Simply do call( std::ref( your callback ) ). std::ref overrides operator() and forwards it to the contained object.
Simularly, with template<class F> void call( F&& ) style, you can write:
template<class T>
std::decay_t<T> copy( T&& t ) { return std::forward<T>(t); }
which explicitly copies something, and force call to use a local copy of f by:
call( copy(f) );
so the two styles mainly differ in how they behave by default.
Since this is C++11 (or higher): <functional> and std::function are your best friends here.
#include <iostream>
#include <functional>
#include <string>
void DoSomething(std::function<void()> callback) {
callback();
}
void PrintSomething() {
std::cout << "Hello!" << std::endl;
}
int main()
{
DoSomething(PrintSomething);
DoSomething([]() { std::cout << "Hello again!" << std::endl; });
}
In my opinion, using the && would be the superior way, basically because it avoids copy constructing the functor when the argument is an lvalue. In case of a lambda, this means avoiding to copy construct all captured values. In case of an std::function you also have an additional heap allocation unless small object optimizations are used.
But there are some cases when it could be a good thing to have a copy of the functor. One example is when using a generator function object like this:
#include <functional>
#include <cstdio>
template <typename F>
void func(F f) {
for (int i = 0; i < 5; i++) {
printf("Got %d\n", (int)f());
}
}
int main() {
auto generator0 = [v = 0] () mutable { return v++; };
auto generator1 = generator0; generator1();
printf("Printing 0 to 4:\n");
func(generator0);
printf("Printing 0 to 4 again:\n");
func(generator0);
printf("Printing 1 to 5:\n");
func(generator1);
}
If we used F&& instead, the second group of prints would print values 5 to 9 instead of 0 to 4. I guess it's up to the application/library what behaviour is preferred.
When using function pointers, we could also end up in a situation when an extra level of indirection must be used in the invoke2 function:
template <typename F>
void invoke1(F f) {
f();
}
template <typename F>
void invoke2(F&& f) {
f();
}
void fun();
void run() {
void (*ptr)() = fun;
void (&ref)() = fun;
invoke1(fun); // calls invoke1<void (*)()>(void (*)())
invoke1(&fun);// calls invoke1<void (*)()>(void (*)())
invoke1(ptr); // calls invoke1<void (*)()>(void (*)())
invoke1(ref); // calls invoke1<void (*)()>(void (*)())
invoke2(fun); // calls invoke2<void (&)()>(void (&)())
invoke2(&fun);// calls invoke2<void (*)()>(void (*&&)())
invoke2(ptr); // calls invoke2<void (*&)()>(void (*&)())
invoke2(ref); // calls invoke2<void (&)()>(void (&)())
}
The statement invoke2(&fun); puts the address to the function on the stack, then sends the reference (i.e. an address) to this temporary stack slot to the invoke2 function. The invoke2 function must then use an extra memory lookup to read the reference on the stack before it can use it. The extra indirection applies for invoke2(ptr) too. In all other cases, the address to the function is sent directly to the invoke1/invoke2 function and does not need any extra indirection.
This example shows one interesting difference between a function reference and a function pointer.
Of course, compiler optimisation such as inlining could easily get rid of this extra indirection.
Related
I was wondering about the main differences, pros and cons in writing a higher order function taking, as input parameter, a std::function or a forwarding reference, e.g. template<typename F> void hof(F&& fun);.
Obviously, the former is more strict than the latter in that it specifies the function type that the input callable object has to conform to.
std::function often has a significant run-time overhead. Passing a generic callable object through a template parameter avoids std::function's indirection costs and allows the compiler to aggressively optimize.
I wrote some simple benchmarks for lambda recursion (Y-combinator vs std::function) at the end of this article. std::function always generates at least 3.5x times more assembly than a non-polymorphic Y-combinator implementation. This is a decent example that shows how std::function can be more expensive than a template parameter.
I suggest playing around on gcc.godbolt.org to see assembly differences between the two techniques.
Here's an example I just came up with:
#if defined(STDFN)
void pass_by_stdfn(std::function<void()> f)
{
f();
}
#else
template <typename TF>
void pass_by_template(TF&& f)
{
f();
}
#endif
volatile int state = 0;
int main()
{
#if defined(STDFN)
pass_by_stdfn([i = 10]{ state = i; });
#else
pass_by_template([i = 10]{ state = i; });
#endif
}
With STDFN not defined, the generated assembly is:
main:
mov DWORD PTR state[rip], 10
xor eax, eax
ret
state:
.zero 4
With STDFN defined, the generated assembly is 48 lines long.
std::function has a lot of pros, but it has also a series of cons that you must take into account.
As an example:
The callable object should be copy constructible.
In other terms, this compiles:
#include <functional>
#include <utility>
#include <memory>
template<typename F>
void func(F &&f) { std::forward<F>(f)(); }
int main() {
func([ptr = std::make_unique<int>()](){});
}
But this does not:
#include <functional>
#include <utility>
#include <memory>
void func(std::function<void(void)> f) { f(); }
int main() {
func([ptr = std::make_unique<int>()](){});
}
Even though (emphasis mine):
Implementations are encouraged to avoid the use of dynamically allocated memory for small callable objects, for example, where f's target is an object holding only a pointer or reference to an object and a member function pointer.
You have no guarantees that you won't have allocations on the dynamic storage when they could be avoided, and you'll have allocations for sure in all the other cases.
When you construct a std::function, the constructor may throw a bad_alloc.
... Probably we can continue, but it doesn't worth it, you got the point.
std::functions are in incredible useful tool, but you should use them when you need them.
As an example, if you plan to store your function somewhere, likely you will end up using a std::function. On the other side, if you plan to accept a callable object and invoke it on the fly, probably you won't use a std::function.
These are only a few examples, a golden rule doesn't exist unfortunately.
I was in a position where I was using std::forward wherever I had a forwarding reference and I was wondering if some of that was unnecessary or even wrong. For example having std::forward in a std::begin() call.
Because of the ability of a class being able to overload its member functions based on whether the call is made with the object as an rvalue or not https://akrzemi1.wordpress.com/2014/06/02/ref-qualifiers/, I assumed that a templated function would be as efficient as possible if you were knew that the inner function you were forwarding the object to was non-mutating and/or was a member function of the object. For example
template <typename Something>
void do_something(Something&& something) {
std::forward<Something>(something).do_something();
auto iter = std::begin(std::forward<Something>(something));
for_each(iter, std::end(std::forward<Something>(something), []() {});
}
I saw this question (When not to use std::forward with r-values?) but it did not address the member function ref-qualifiers and it also did not clearly address what the best practice is when you don't have access to the inner functions definitions that you are calling.
Is there a general guideline for when not to use std::forward that addresses the things I mentioned? Or is there some key concept that I am missing?
Except if you know the type you will have, avoid to use std::forward several time in the same function for the same object, as the second time, your object might be moved.
// assuming implementation which really need pass by value instead of const reference
template <typename T> void pass_by_value(T t) { std::cout << t; }
template <typename T>
void foo(T&& t)
{
pass_by_value(std::forward<T>(t));
pass_by_value(std::forward<T>(t));
}
a call of foo with std::string
foo(std::string("Hello world")); // or "Hello world"s
might call the equivalent to
pass_by_value(std::string("Hello world"));
pass_by_value(std::string("")); // Moved string...
The problem is a use after object moved from ("stealed"). If something used in a multiple lines of do_something, then use std::forward only at the last such line to prevent the problem.
I have a Signal class in my application that provides classes with an option to expose events (same as in .NET).
The class works and all is well.
Yesterday I saw this SO question (and its answer) and was familiarized with std::forward.
I decided to try to use it in my code so I changed every std::function<void(Args...)> to std::function<void(Args&&...)> and in the raise function (the operator()) I used the same logic I saw in the above link so now the function takes Args&&...args and the callback uses std::forward<Args>(args)...
Here's a simplified version of my Signal class (with some changes to make it a good example):
template<typename... Args> class Signal
{
public:
int operator+=(const std::function<void(Args&&...)>& func) {
int token = getNewToken();
m_subscribers.emplace(token, func);
return token;
}
void operator()(Args&&... args) {
for (auto it : m_subscribers) {
it.second(std::forward<Args>(args)...);
}
}
private:
std::map<int, std::function<void(Args&&...)>> m_subscribers;
};
int main() {
int* six = new int(6);
int seven = 7;
Signal<int*> e1;
e1 += [](int* x) { std::cout << *x; };
Signal<int> e2;
e2 += [](int x) { std::cout << x; };
e1(&seven);
e2(6);
e1(six); //Error C2664 'void Signal<int *>::operator ()(int *&&)':
// cannot convert argument 1 from 'int *' to 'int *&&'
e1(std::move(six)); //This is a workaround
return 0;
}
The issue I'm seeing is with classes (or main in this example) that try to raise events with pointers and I'm not sure how to solve this.
My main goal is to have the Signal class a general API and if the developers chose to use Signal<int*> I don't want him\her to raise with std::move.
What am I doing wrong here?
T&& is only a universal reference if T is a non-cv-qualified function template parameter. In your call operator:
void operator()(Args&&... args) {
Args isn't a template parameter of the function, it's a template parameter of the class. So for Signal<int*>, this operator() takes an rvalue reference to int*. Since six is an lvalue, that fails.
What you want is to provide the correct reference qualifications to Signal. Like so:
template<typename... Args>
class Signal
{
using F = std::function<void(Args...)>; // NB: Just Args...
public:
int operator+=(F func) {
int token = getNewToken();
m_subscribers.emplace(token, std::move(func));
return token;
}
void operator()(Args... args) { // NB: just Args...
for (auto& it : m_subscribers) { // NB: auto&
it.second(args...);
}
}
private:
std::map<int, F> m_subscribers;
};
Note that forwarding Args... is questionable anyway. What if you had two subscribers? Once you forward the args once you can't really use them a second time.
The above will make Signal<int*> do what you expect. The operator() will just take an int*, which you can pass either an lvalue or an rvalue to.
Barry's answer is correct, but perhaps not as clearly explained as it could be.
&& is only given its special treatment as a forwarding (or "universal") reference when template parameter deduction occurs. But there is no deduction occurring here:
Signal<int*> e1; // `Args...` is explicitly `int*`
...
e1(six); // `Args...` *has already been specified*
When template classes are instantiated, they are essentially transformed into normal classes that just happen to be written by the compiler. See this answer for an example of what this might look like if written out in C++ code.
In C++14, there is no way to trigger template-parameter deduction of class templates without an auxiliary function (not a constructor):
template <typename Args...>
Signal<Args...> make_signal(Args&&...) { return Signal<Args...>; }
....But note that in your case, this makes no sense: you don't want to infer the types of your arguments when create the Signal, you want to specify them in advance.
(Note that in C++17, there will be support for template argument deduction of class templates. I assume this means that it will be possible to forward template-class arguments, though it's not immediately clear to me what the implications of doing such a thing would be.)
What you want to permit is for arguments to be forwarded at call time. This is actually reasonably simple:
template<typename... Args> class Signal
{
public:
// .... skipping some code...
template <typename... CallArgs>
void operator()(CallArgs&&... args) {
callback(std::forward<CallArgs>(args)...);
}
};
....But, again, in your case this doesn't quite make sense, as noted in Barry's answer. You don't want to forward arguments if you have multiple callbacks, to prevent moving and re-using them.
It's possible to work around this by checking the size of m_subscribers and only using the forwarding code if it's 1, and just passing the arguments as-is otherwise. However, this might lead to confusing behavior, since the way callbacks are invoked shouldn't generally depend on the state of your Signal object. So you could perhaps write a separate class, SingletonSignal, for callbacks that must be invoked with forwarded arguments (e.g. in case a callback wants to transfer ownership of an uncopyable object such as unique_ptr).
I've been using function pointers till now, like this format in c++. I do have some uses now and then and I'm wondering is there anything else introduced in c++11/14 as their alternative.
#include <iostream>
using namespace std;
void sayHello();
void someFunction(void f());
int main() {
someFunction(sayHello);
return 0;
}
void sayHello(){
std::cout<<"\n Hello World";
}
void someFunction(void f()){
f();
}
I did take a look at this question but couldn't understand any advantages over traditional use of function pointers. Also I would like to ask , is there anything wrong (not recommended) thing with using function pointers since I never see anyone using them. Or any other alternative present.
The question you mention suggest std::function but does not emphasize (or mentions at all) its value when combined with std::bind.
Your example is the simplest possible, but suppose you have a
std::function<void (int, int)> f ;
A function pointer can do more or less the same things. But suppose that you need a function g(int) which is f with second parameter bound to 0. With function pointers you can't do much, with std::function you can do this:
std::function<void(int)> g = std::bind(f, _1, 0) ;
As an alternative to traditional function pointers, C++11 introduced template alias which combined with variadic templates could simplify the function pointer sintax. below, an example of how to create a "template" function pointer:
template <typename R, typename ...ARGS> using function = R(*)(ARGS...);
It can be used this way:
void foo() { ... }
int bar(int) { ... }
double baz(double, float) { ... }
int main()
{
function<void> f1 = foo;
function<int, int> f2 = bar;
function<double, double, float> f3 = baz;
f1(); f2({}); f3({}, {});
return 0;
}
Also, it can deal neatly with function overloads:
void overloaded(int) { std::cout << "int version\n"; }
void overloaded(double) { std::cout << "double version\n"; }
int main()
{
function<void, int> f4 = overloaded;
function<void, double> f5 = overloaded;
f4({}); // int version
f5({}); // double version
return 0;
}
And can be used as a pretty neat way to declare function-pointers parameters:
void callCallback(function<int, int> callback, int value)
{
std::cout << "Calling\n";
std::cout << "v: " << callback(value) << '\n';
std::cout << "Called\n";
}
int main()
{
function<int, int> f2 = bar;
callCallback(f2, {});
return 0;
}
This template alias could be used as an alternative of std::function which doesn't have its drawbacks nor its advantages (good explanation here).
Live demo
As a brief, I think that template alias combined with variadic templates is a good, nice, neat and modern C++ alternative to raw function pointers (this alias still are function pointers after all) but std::function is good, nice, neat and modern C++ as well with good advantages to take into account. To stick in function pointers (or alias) or to choose std::function is up to your implementation needs.
Also I would like to ask , is there anything wrong (not recommended)
thing with using function pointers since I never see anyone using
them.
Yes. Function pointers are terrible, awful things. Firstly, they do not support being generic- so you cannot take a function pointer that, say, takes std::vector<T> for any T. Secondly, they do not support having bound state, so if at any time in the future, anybody, ever, wishes to refer to other state, they are completely screwed. This is especially bad since this includes this for member functions.
There are two approaches to taking functions in C++11. The first is to use a template. The second is to use std::function.
The template kinda looks like this:
template<typename T> void func(F f) {
f();
}
The main advantages here are that it accepts any kind of function object, including function pointer, lambda, functor, bind-result, whatever, and F can have any number of function call overloads with any signature, including templates, and it may have any size with any bound state. So it's super-duper flexible. It's also maximally efficient as the compiler can inline the operator and pass the state directly in the object.
int main() {
int x = 5;
func([=] { std::cout << x; });
}
The main downside here is the usual downsides of templates- it doesn't work for virtual functions and has to be defined in the header.
The other approach is std::function. std::function has many of the same advantages- it can be any size, bind to any state, and be anything callable, but trades a couple off. Mainly, the signature is fixed at type definition time, so you can't have a std::function<void(std::vector<T>)> for some yet-to-be-known T, and there may also be some dynamic indirection/allocation involved (if you can't SBO). The advantage of this is that since std::function is a real concrete type, you can pass it around as with any other object, so it can be used as a virtual function parameter and such things.
Basically, function pointers are just incredibly limited and can't really do anything interesting, and make the API incredibly unflexible. Their abominable syntax is a piss in the ocean and reducing it with a template alias is hilarious but pointless.
I did take a look at this question but couldn't understand any
advantages over traditional use of function pointers. Also I would
like to ask , is there anything wrong (not recommended) thing with
using function pointers since I never see anyone using them.
Normal "global" functions typically don't/can't have state. While it's not necessarily good to have state during traversal in functional programming paradigm, sometimes state might come in handy when it relates orthogonally to what has been changed (heuristics as example). Here functors (or function objects) have the advantage.
Normal functions don't compose very well (creating higher level functions of lower level functions.
Normal functions don't allow for binding additional parameters on the fly.
Sometimes normal functions can act as replacement for lambdas, and visa versa, depending on the context. Often one wouldn't want to write a special function just because you have some very local/specific requirement during "container traversal".
The code I'm looking for is like following.
bool Func1(int Arg1, C++11LambdaFunc Arg2){
if(Arg1 > 0){
return Arg2(Arg1);
}
}
Later I'll be using this code.
Func1(12, [](int D) -> bool { ... } );
Basic version, for use in a header file:
template<typename Lambda>
bool Func1(int Arg1, Lambda Arg2){ // or Lambda&&, which is usually better
if(Arg1 > 0){
return Arg2(Arg1);
} else {
return false; // remember, all control paths must return a value
}
}
More complex version, if you want to split your interface from your implementation (it has run time costs):
bool Func1(int Arg1, std::function<bool(int)> Arg2){
if(Arg1 > 0){
return Arg2(Arg1);
} else {
return false; // remember, all control paths must return a value
}
}
std::function uses type erasure to create a custom-created wrapper around your lambda, and then exposes a non-virtual interface that uses the pImpl pattern to forward it to the custom-created wrapper.1
Or, in less technical terms, std::function<bool(int)> is a class that can wrap nearly anything that you can call like a function, passing one parameter that is compatible with passing an int, and it returns something that is compatible with returning a bool.
A call through a std::function has a run time cost roughly equal to a virtual function call (caused by the above type erasure), and when you create it it has to copy the state of the function object (aka functor) passed in (which can be cheap -- stateless lambdas, or lambdas capturing arguments by reference -- or expensive in some other cases) and store it (typically on the free store or heap, which has a cost), while the pure-template versions can be "inlined" at the point of call (ie, can not only cost less than a function call, the compiler can even optimize over the function call and return boundaries!)
If you want to split interface/implementation without all of the runtime costs of std::function, you can roll your own function_ref (in c++17, because that cuts down on some boilerplate):
template<class Sig>
struct function_ref;
template<class R, class...Args>
struct function_ref<R(Args...)> {
R operator()(Args...args) const {
return pf(state, std::forward<Args>(args)...);
}
function_ref()=default;
function_ref(function_ref const&)=default;
function_ref& operator=(function_ref const&)=default;
explicit operator bool()const{ return pf!=nullptr; }
// this overload reduces indirection by 1 step
// and allows function_ref<Sig> to resolve overloads
// on an overload set sometimes.
function_ref( R(*f)(Args...) ):
pf([](State const& state, Args&&...args)->R{
return reinterpret_cast<R(*)(Args...)>(state.pfunstate)(std::forward<Args>(args)...);
})
{
state.pfunstate = reinterpret_cast<void(*)()>(f);
}
// this grabs anything callable (that isn't this own type)
// and stores a pointer to it to call later.
template<class F>
requires (
std::is_convertible_v<
std::invoke_result_t< std::remove_reference_t<F>, Args... >, R
>
&& !std::is_same_v< std::decay_t<F>, function_ref >
)
function_ref( F&& f ):
pf([](State const& state, Args&&...args)->R{
return (*(std::remove_reference_t<F>*)state.pstate)(std::forward<Args>(args)...);
})
{
state.pstate = std::addressof(f);
}
private:
union State {
void* pstate = nullptr;
void(*pfunstate)();
};
State state;
R(*pf)(State const&, Args&&...) = nullptr;
};
// a deduction guide permitting function_ref{foo} to work
// if foo is a non-overloaded function name.
template<class R, class...Args>
function_ref( R(*)(Args...) )->function_ref<R(Args...)>;
Live example.
This removes the need to ever do any allocation from std::function by removing ownership semantics from it and just type-erasing calling.
A fancy version of the first example that also handles some corner cases a tad better: (also must be implemented within a header file, or in the same translation unit as it is used)
template<typename Lambda>
bool Func1(int Arg1, Lambda&& Arg2){
if(Arg1 > 0){
return std::forward<Lambda>(Arg2)(Arg1);
} else {
return false; // remember, all control paths must return a value
}
}
which uses a technique known as "perfect forwarding". For some functors, this generates slightly different behavior than #1 (and usually more correct behavior).
Most of the improvement comes form the use of && in the argument list: this means that a reference to the functor is passed in (instead of a copy), saving some costs, and allows both a const or non-const functor to be passed in.
The std::forward<Lambda>(...) change would only cause a change in behavior if someone used a relatively new C++ feature that allows methods (including operator()) to override on the rvalue/lvalue status of the this pointer. In theory, this could be useful, but the number of functors I've seen that actually override based on the rvalue status of this is 0. When I'm writing serious library code (tm) I go to this bother, but rarely otherwise.
There is one more possible thing to consider. Suppose you want to take either a function that returns bool, or a function that returns void, and if the function returns void you want to treat it as if it returned true. As an example, you are taking a function that is being called when iterating over some collection, and you want to optionally support early halting. The function returns false when it wants to stop prematurely, and true or void otherwise.
Or, in a more general case, if you have multiple overrides of a function, one of which takes a function and others take some other type at the same location.
This is possible, which is as far as I'm going to get into here (either with a smart adapter, or via SFINAE techniques). However, you are probably better off just creating two different named functions, because the techniques required are way too heavy weight.
1 Technically std::function could use magic fairy dust to do what it does, as its behavior is described by the standard, and not its implementation. I'm describing a simple implementation that approximates the behavior of the std::function implementation I have interacted with.
First solution:
You can make your Func1() function a function template:
template<typename T>
bool Func1(int Arg1, T&& Arg2){
if(Arg1 > 0){
return Arg2(Arg1);
}
return false; // <== DO NOT FORGET A return STATEMENT IN A VALUE-RETURNING
// FUNCTION, OR YOU WILL GET UNDEFINED BEHAVIOR IF FLOWING
// OFF THE END OF THE FUNCTION WITHOUT RETURNING ANYTHING
}
You could then invoke it as you desire:
int main()
{
Func1(12, [](int D) -> bool { return D < 0; } );
}
Second solution:
If you do not want to use templates, an alternative (that would bring some run-time overhead) is to use std::function:
#include <functional>
bool Func1(int Arg1, std::function<bool(int)> Arg2){
if(Arg1 > 0){
return Arg2(Arg1);
}
return false;
}
Once again, this would allow you to call Func1() the way you desire:
int main()
{
Func1(12, [](int D) -> bool { return D < 0; } );
}
For those whose tastes are more traditional, note that non-capturing lambdas can convert to function pointers. So you can write your function above as:
bool Func1(int Arg1, bool (*Arg2)(int)) { ... }
And it will work correctly for both traditional functions and lambdas.