How to Pass a C++ Lambda to a C-Callback That Expects a Function Pointer and a Context

How can I pass a C++ lambda to a C-callback that expects a function pointer and a context?

The simple approach is to stick the lambda into a std::function<void()> which is kept somewhere. Potentially it is allocated on the heap and merely referenced by the void* registered with the entity taking the callback. The callback would then simply be a function like this:

extern "C" void invoke_function(void* ptr) {
(*static_cast<std::function<void()>*>(ptr))();
}

Note that std::function<S> can hold function objects with state, e.g., lambda functions with a non-empty capture. You could register a callback like this:

register_callback(&invoke_function,
new std::function<void()>([=](){ ... }));

C++ lambda with captures as a function pointer

Since capturing lambdas need to preserve a state, there isn't really a simple "workaround", since they are not just ordinary functions. The point about a function pointer is that it points to a single, global function, and this information has no room for a state.

The closest workaround (that essentially discards the statefulness) is to provide some type of global variable which is accessed from your lambda/function. For example, you could make a traditional functor object and give it a static member function which refers to some unique (global/static) instance.

But that's sort of defeating the entire purpose of capturing lambdas.

Passing capturing lambda as function pointer

A lambda can only be converted to a function pointer if it does not capture, from the draft C++11 standard section 5.1.2 [expr.prim.lambda] says (emphasis mine):

The closure type for a lambda-expression with no lambda-capture has a
public non-virtual non-explicit const conversion function to pointer
to function
having the same parameter and return types as the closure
type’s function call operator. The value returned by this conversion
function shall be the address of a function that, when invoked, has
the same effect as invoking the closure type’s function call operator.

Note, cppreference also covers this in their section on Lambda functions.

So the following alternatives would work:

typedef bool(*DecisionFn)(int);

Decide greaterThanThree{ []( int x ){ return x > 3; } };

and so would this:

typedef bool(*DecisionFn)();

Decide greaterThanThree{ [](){ return true ; } };

and as 5gon12eder points out, you can also use std::function, but note that std::function is heavy weight, so it is not a cost-less trade-off.

How can I pass a class member function as a callback?

That doesn't work because a member function pointer cannot be handled like a normal function pointer, because it expects a "this" object argument.

Instead you can pass a static member function as follows, which are like normal non-member functions in this regard:

m_cRedundencyManager->Init(&CLoggersInfra::Callback, this);

The function can be defined as follows

static void Callback(int other_arg, void * this_pointer) {
CLoggersInfra * self = static_cast<CLoggersInfra*>(this_pointer);
self->RedundencyManagerCallBack(other_arg);
}

Wrapping around a C function that takes its arguments with void* in C++

In C++11 onwards, you can use something like

static void call_task(void *args) {
auto& f = *static_cast<std::function<void()>*>(args);
f();
}
// note: need this to stay alive!
std::function<void()> f = [&](){
// Any arguments you like here
do_whatever(1, 2, 3)
};
CreateTask(call_task, static_cast<void*>(&f));

You need to ensure the lifetime of f is longer than that of the task (just as you would for your Params object).


You can actually avoid std::function altogether, as:

template<typename Func>
void call_func(void *args) {
auto& f = *static_cast<Func*>(args);
f();
}

template<typename Func>
void wrapped_create_task(Func& func) {
CreateTask(call_func<Func>, static_cast<void*>(&func));
}
// you can still use `std::function` here, but you don't have to.
auto f = [&](){
// Any arguments you like here
do_whatever(1, 2, 3)
};

// this works on any object that implements `operator ()`
wrapped_create_task(f)

Again, it's really important that f remains alive for the duration of its execution. You can't put it on a stack that dies before the task does.

Calling a stateless lambda without an instance (only type)

It gets even easier than @Yakk's answer if you take advantage of the fact that a lambda can see the static locals of its enclosing scope without needing to capture them. Using this fact you can wrap the user-provided stateless lambda in your own stateless lambda with the required signature, and convert the latter into a function pointer.

using callback = void (*)(int, TypeErasedValue*, TypeErasedValue*);

template <typename F>
callback stateless_to_callback(F f) {
static F static_f = f;
return [](int argc, TypeErasedValue* argv, TypeErasedValue* result) {
// extract arguments from argv
auto r = static_f( /* arguments... */ );
// store r in result
};
}

Convert C++ function pointer to c function pointer

If I recall it correctly, Only static methods of a class can be accessed via "normal" C pointer to function syntax. So try to make it static. The pointer to a method of a class needs extra information, such as the "object" (this) which has no meaning for a pure C method.

The FAQ shown here has good explanation and a possible (ugly) solution for your problem.

How to pass a std::function with capture through a C API?

I don't know much about FreeRTOS, but with your requirements, you should be able to use a pointer to your std::function object (either heap-allocated or a pointer to a non-heap allocated object if its lifetime permits) -- all raw pointers are POD, even if they point to C++ objects.

Here's a quick example of using a pointer to a bound std::function allocated on the stack:

std::function<void()> produce(int value)
{
return [value]() {
std::cout << "Value = " << value << std::endl;
};
}

void consume(std::function<void()> *callback)
{
(*callback)();
}

int main()
{
auto cb = produce(42);
consume(&cb);

return 0;
}

What kinds of C++ functions can be placed in a C function pointer?

I have a C library that uses a struct of function pointers for callbacks. The callbacks will be called from C code.

A C library only understands C. So you can only pass back things that are explicitly supported and understood by C.

Since there is no definition of calling conventions for any function types in C++ you can not explicitly pass back C++ functions. You can only pass back C function (those explicitly declared with extern "C") and guarantee that they are compatible.

Like all undefined behavior this may seem to work (like passing back normal C++ functions or static members). But like all undefined behavior it's allowed to work. You just can't guarantee that it's actually correct or portable.

extern "C" {
typedef struct callbacks_t {
void (*foo) (const char*);
int (*bar) (int);
} callbacks_t;

// Any functions you define in here.
// You can set as value to foo and bar.

}// extern C

What kinds of C++ functions can I safely place in those function pointers to be called from the C library?

Static member functions?

No. But this happens to work on a lot of platforms. This is a common bug and will bite people on some platforms.

Fully specified template functions?

No.

Non-capturing Lambdas?

No.

g++ seemingly lets me use all of the above,

Yes. But it is assuming that you are passing to objects that are built with the C++ compiler (which would be legal). As C++ code will use the correct calling convention. The problem is when you pass these things to a C library (pthreads springs to mind).



Related Topics



Leave a reply



Submit