When implementing a callback function in C++, should I still use the C-style function pointer:
void (*callbackFunc)(int);
Or should I make use of std::function:
std::function< void(int) > callbackFunc;
When implementing a callback function in C++, should I still use the C-style function pointer:
void (*callbackFunc)(int);
Or should I make use of std::function:
std::function< void(int) > callbackFunc;
In short, use std::function
unless you have a reason not to.
Function pointers have the disadvantage of not being able to capture some context. You won't be able to for example pass a lambda function as a callback which captures some context variables (but it will work if it doesn't capture any). Calling a data member of an object (i.e. non-static) is thus also not possible, since the object (this
-pointer) needs to be captured.(1)
std::function
(since C++11) is primarily to store a function (passing it around doesn't require it to be stored). Hence if you want to store the callback for example in a data member, it's probably your best choice. But also if you don't store it, it's a good "first choice" although it has the disadvantage of introducing some (very small) overhead when being called (so in a very performance-critical situation it might be a problem but in most it should not). It is very "universal": if you care a lot about consistent and readable code as well as don't want to think about every choice you make (i.e. want to keep it simple), use std::function
for every function you pass around.
Think about a third option: If you're about to implement a small function which then reports something via the provided callback function, consider a template parameter, which can then be any callable object, i.e. a function pointer, a functor, a lambda, a std::function
, ... Drawback here is that your (outer) function becomes a template and hence needs to be implemented in the header. On the other hand you get the advantage that the call to the callback can be inlined, as the client code of your (outer) function "sees" the call to the callback will the exact type information being available.
Example for the version with the template parameter (write &
instead of &&
for pre-C++11):
template <typename CallbackFunction>
void myFunction(..., CallbackFunction && callback) {
...
callback(...);
...
}
As you can see in the following table, all of them have their advantages and disadvantages:
function ptr | std::function | template param | |
---|---|---|---|
can capture context variables | no1 | yes | yes |
no call overhead (see comments) | yes | no | yes |
can be inlined (see comments) | no | no | yes |
can be stored in a class member | yes | yes | no2 |
can be implemented outside of header | yes | yes | no |
supported without C++11 standard | yes | no3 | yes |
nicely readable (my opinion) | no | yes | (yes) |
(1) Workarounds exist to overcome this limitation, for example passing the additional data as further parameters to your (outer) function: myFunction(..., callback, data)
will call callback(data)
. That's the C-style "callback with arguments", which is possible in C++ (and by the way heavily used in the WIN32 API) but should be avoided because we have better options in C++.
(2) Unless we're talking about a class template, i.e. the class in which you store the function is a template. But that would mean that on the client side the type of the function decides the type of the object which stores the callback, which is almost never an option for actual use cases.
(3) For pre-C++11, use boost::function
std::function
type-erased one if you need to store it outside the immediately called context). –
Bufford std::function
have that void (*callbackFunc)(int)
doesn't? I believe they have the same overhead when there is no function context. std::function
only has "overhead" when doing things callbackFunc
can't do at all. –
Physostomous std::function
. It is pretty obvious that there has to be an overhead when you assign a functor with context due to type-erasure. –
Stylobate std::function
, it captures it in a callable object which (when you call it) performs that additional function call. The "callable object" could be a concrete class of an abstract class being kept as a member in std::function
(whose operator() is virtual) to implement type-erasure. But that sounds like an unoptimized, trivial implementation... –
Stylobate std::function
which is very easy to be used compared to the rest. In my opinion, you should never care about constant overhead unless you found performance hurting code (by profiling). Then the question is a whole different one. –
Stylobate CallbackFunction
in a template? Can you define a type CallbackFunction
that can be used in a template <CallbackFuntion func>
that takes functors? –
Heteronomous In short, use std::function unless you have a reason to not
seems a very bad default. Whatever happened to not paying for what we don't use? IMO don't use std::function
, or whatever feature, unless you can objectively show you need it (& then it might be great). See #43948740 @moose It's not a matter of just a static callback: lambdas & templates also exist & can suffice in many use-cases where std::function
might be relatively very wasteful –
Camire using functionptr_f = std::add_pointer_t<bool(void*, std::string const&, int)>
– that will declare a function pointer, equivalent to typedef void (* functionptr_f)(void*, std::string const&, int)
in the older syntax. I find the using
form much more legible and expressive; it also has the advantage of being easily convertible to a std::function
declaration just by replacing add_pointer_t
with function
. –
Caboose typedef
your function. For example, typedef int CallBackFunc(int, int);
and then you can use it wherever you need a pointer to that type (say, int(int, int)) of function: CallBackFunc* func = CallBackImplementation;
BTW, I don't know if it works for C++11's using
type aliases as well. –
Rancho use std::function unless you have a reason to not
...such as trying to be light-weight and space and time efficient on a space-constrained embedded microcontroller--because apparently std::function
is pretty heavy-weight. See here ("std::function is heavy weight, so it is not a cost-less trade-off.") & here ("std::function
is a complex, heavy, stateful, near-magic type...If you can get away with it, you should prefer either naked function pointers.."). So, for light-wt embedded prefer raw ptrs. –
Drabble OnNewClient
and OnData
) it's often the right choice imho. –
Decay void (*callbackFunc)(int);
may be a C style callback function, but it is a horribly unusable one of poor design.
A well designed C style callback looks like void (*callbackFunc)(void*, int);
-- it has a void*
to allow the code that does the callback to maintain state beyond the function. Not doing this forces the caller to store state globally, which is impolite.
std::function< int(int) >
ends up being slightly more expensive than int(*)(void*, int)
invocation in most implementations. It is however harder for some compilers to inline. There are std::function
clone implementations that rival function pointer invocation overheads (see 'fastest possible delegates' etc) that may make their way into libraries.
Now, clients of a callback system often need to set up resources and dispose of them when the callback is created and removed, and to be aware of the lifetime of the callback. void(*callback)(void*, int)
does not provide this.
Sometimes this is available via code structure (the callback has limited lifetime) or through other mechanisms (unregister callbacks and the like).
std::function
provides a means for limited lifetime management (the last copy of the object goes away when it is forgotten).
In general, I'd use a std::function
unless performance concerns manifest. If they did, I'd first look for structural changes (instead of a per-pixel callback, how about generating a scanline processor based off of the lambda you pass me? which should be enough to reduce function-call overhead to trivial levels.). Then, if it persists, I'd write a delegate
based off fastest possible delegates, and see if the performance problem goes away.
I would mostly only use function pointers for legacy APIs, or for creating C interfaces for communicating between different compilers generated code. I have also used them as internal implementation details when I am implementing jump tables, type erasure, etc: when I am both producing and consuming it, and am not exposing it externally for any client code to use, and function pointers do all I need.
Note that you can write wrappers that turn a std::function<int(int)>
into a int(void*,int)
style callback, assuming there are proper callback lifetime management infrastructure. So as a smoke test for any C-style callback lifetime management system, I'd make sure that wrapping a std::function
works reasonably well.
void*
come from? Why would you want to maintain state beyond the function? A function should contain all code it needs, all functionality, you just pass it the desired arguments and modify and return something. If you need some external state then why would a functionPtr or callback carry that luggage? I think that callback is unnecessarily complex. –
Voodoo this
. Is it because one has to account the case of a member function being called, so we need the this
pointer to point to the address of the object? If I'm wrong could you give me a link to where I can find more info on this, because I can't find much about it. Thanks in advance. –
Voodoo void*
to permit transmission of runtime state. A function pointer with a void*
and a void*
argument can emulate a member function call to an object. Sorry, I don't know of a resource that walks through "designing C callback mechanisms 101". –
Bufford this
. That's what I meant. Ok, thanks anyway. –
Voodoo void*
is supposed to be left blank nullptr
correct? –
Voodoo void increment_int(void* ptr){ int* pint = static_cast<int*>(ptr); ++*pint; }
could be passed along with a void*
pointing at an int
to count how many times the callback is called. Or you could struct counter { int x; void increment() { ++x; } }; void counter_increment( void* ptr )
–
Bufford { static_cast<counter*>(ptr)->increment(); }
and get the same result and same semantics. The void*
makes it type-unsafe, but when passing through a type-unaware C API some type-unsafety is unavoidable. –
Bufford Use std::function
to store arbitrary callable objects. It allows the user to provide whatever context is needed for the callback; a plain function pointer does not.
If you do need to use plain function pointers for some reason (perhaps because you want a C-compatible API), then you should add a void * user_context
argument so it's at least possible (albeit inconvenient) for it to access state that's not directly passed to the function.
The only reason to avoid std::function
is support of legacy compilers that lack support for this template, which has been introduced in C++11.
If supporting pre-C++11 language is not a requirement, using std::function
gives your callers more choice in implementing the callback, making it a better option compared to "plain" function pointers. It offers the users of your API more choice, while abstracting out the specifics of their implementation for your code that performs the callback.
std::function
may bring VMT to the code in some cases, which has some impact on performance.
The other answers answer based on technical merits. I'll give you an answer based on experience.
As a very heavy X-Windows developer who always worked with function pointer callbacks with void* pvUserData
arguments, I started using std::function
with some trepidation.
But I find out that combined with the power of lambdas and the like, it has freed up my work considerably to be able to, at a whim, throw multiple arguments in, re-order them, ignore parameters the caller wants to supply but I don't need, etc. It really makes development feel looser and more responsive, saves me time, and adds clarity.
On this basis I'd recommend anyone to try using std::function
any time they'd normally have a callback. Try it everywhere, for like six months, and you may find you hate the idea of going back.
Yes there's some slight performance penalty, but I write high-performance code and I'm willing to pay the price. As an exercise, time it yourself and try to figure out whether the performance difference would ever matter, with your computers, compilers and application space.
There is 1 use case where plain C function pointers are the right answer: Comparison.
std::function has no proper concept of (in)equality. The only supported comparison is to nullptr, AKA the 'empty' state. Whereas function pointers are just pointers, so "==" works and does what you usually want: Return true for callbacks pointing to the exact same code (or both null), otherwise compare false. So if you really need to support comparing your callbacks, you need plain old function pointers or a custom functor type (a struct with an "operator ()") instead of std::function.
© 2022 - 2024 — McMap. All rights reserved.