std::function
is a class (template) with a misleading name: It is not a function. It is rather a polymorphic function (object) wrapper. Let's take that apart: it's a wrapper class for functions and function objects, and it's polymorphic in the sense of type erasure.
The main job of std::function
is to store arbitrary callable objects (function objects) and provide a single interface for them:
double myFunc(int);
struct MyStruct {
double operator()(int);
};
std::function<int(double)> f = myFunc; // can store function pointers
f = MyStruct{}; // or class instances
// ... and many more
The type of f
is the same, while the type of the thing stored inside can differ. This is called type erasure.
A C callback with signature int32_t (*) (std::string &, uint32_t)
expects a function pointer. In other words, a pointer to a piece of executable code.
You cannot pass a class object as a function pointer, since a class object is not a piece of code but a piece of data, with related pieces of code (the member functions).
Thinking about it in terms of state, a function is stateless. It's always the same piece of code. A class object can be stateful (if it has any members, for example).
A function pointer is a single pointer to executable code, it cannot transfer state directly. Therefore, many C callbacks have an additional parameter to pass state:
typedef int32_t (*callback_c_type) (void* state, std::string &, uint32_t);
With such a signature, you can pass the function object as a pointer through the callback "bottleneck" and restore it on the other end, but with caveats:
typedef int32_t (*callback_c_type) (void*, std::string &, uint32_t);
void execute_c_callback(callback_c_type cb, void* state)
{
std::cout << "execute_c_callback" << std::endl;
std::string text = "foo";
cb(state, text, 777);
}
using plain_cb_signature = int32(std::string&, uint32_t);
int32_t callback_wrapper(void* state, std::string &s, uint32_t i)
{
auto* f = static_cast<std::function<plain_cb_signature>*>(state);
(*f)(s, i);
}
int main ()
{
std::function <int32_t (std::string &, uint32_t)> cb_2 = cb;
auto* f = &cb;
execute_c_callback(callback_wrapper, f);
return 0;
}
The trouble here is maintaining the lifetime of cb
, in other words, keeping the state valid long enough for all invocations of execute_c_callback
.
Usually one writes callback_wrapper
as a generic function template. See, for example, this version by Yakk.