Reputation: 23052
I am trying to interface with a library written in c
, that uses this familiar pattern:
void some_c_handler(void(*func)(void*), void* data);
Now, I want to write a C++
wrapper for this function that looks like this:
void my_new_cpp_handler(std::function<void()>&& func)
{
void (*p)() = foo(func);
void* data = bar(func);
some_c_handler(p, data);
}
Both some_c_handler
and my_new_cpp_handler
are solving the same problem; they're taking in some kind of function along with some state. But the latter is preferred in that it abstracts much of the implementation details from the user, and allows for simply passing in a lambda object.
So my_new_cpp_handler
should take the func
parameter it is given, convert it to a function pointer and pass its state on to data
.
I don't know enough about the standard, or the implementation of std::function
to know if this is even a reasonable request. Can foo
and bar
exist?
To put it differently, what I want is to be able to pass a stateful function to a c
callback handler without having to manually instantiate my own struct
type to pass along with it. Obviously std::function
has already done this for us, so I'd love to be able to separate the function pointer from the state somehow and pass it onto the c
-style handler. Is this possible?
Upvotes: 12
Views: 11953
Reputation: 213526
Is this possible?
No.
You can wrap a C-style callback into an std::function<>...
, and the compiler will emit code to extract the data
and the handler
and call it. However, the exact code to do so will depend on the compiler, the ABI
and the standard C++ library being used. You can't magically reconstruct that code given only the std::function
wrapper.
Further, an arbitrary std::function...
(or a lambda) may wrap code other than a call to C-style handler. It should be obvious that applying the "magically reconstructed" code to extract C-style handler from such an instance of std::function...
can't possibly succeed.
P.S.
To put it differently, what I want is to be able to pass a stateful function to a c callback handler without having to manually instantiate my own struct type to pass along with it. Obviously
std::function
has already done this for us
So why don't you just use what std::function
has already done for you:
void my_new_cpp_handler(std::function<void()>&& func)
{
func(); // calls some_c_handler(p, data) IFF that is what "func" encodes.
}
Upvotes: 10
Reputation: 118340
If you were to carefully review the documentation for this library, you will find that the
void some_c_handler(void(*func)(void*), void* data);
Invokes func
, passing it the data
argument.
This is a very common design pattern for C libraries that take a callback function. In addition to the callback function, they also take an additional opaque pointer that is not interpreted by the library, but is blindly forwarded to the func
. In other words, the C library invokes
func(data);
You can use this from C++ code to pass an ordinary pointer to any class.
This includes std::function
, too.
The trick is that in most situations it will be necessary to use new
:
auto *pointer=new std::function< function_type >...
The end result is a pointer that can be passed to the C library, together with a pointer to a "trampoline function":
some_c_handler(&cpp_trampoline, reinterpret_cast<void *>(pointer));
And the trampoline recasts the opaque pointer:
void cpp_trampoline(void *pointer)
{
auto real_pointer=reinterpret_cast<std::function< ... >*>(pointer);
// At this point, you have a pointer to the std::function here.
// Do with it as you wish.
The only detail you will need to square away here is to figure out the correct scope for the dynamically-allocated function pointer, in order to avoid memory leaks.
Upvotes: 9
Reputation: 119174
You can make a wrapper function whose purpose is to simply execute the std::function
callback.
void some_c_handler(void(*)(void*), void*) {}
void std_function_caller(void* fn) {
(*static_cast<std::function<void()>*>(fn))();
};
auto make_std_function_caller(std::function<void()>& fn) {
return std::make_pair(std_function_caller, static_cast<void*>(&fn));
}
void my_new_cpp_handler(std::function<void()>&& func) {
const auto p = make_std_function_caller(func);
some_c_handler(p.first, p.second);
}
Upvotes: 7
Reputation: 351
According to this link, the std::function
object has no accessible member that can provide raw access to the pointer. You should probably define a struct
that contains a pointer to the function pointer and the object, and a constructor wrapper that stores the pointer's address to the struct before the construction of your std::struct
, so as to assign the address stored in the pointer it points to to your C handler's parameter.
Upvotes: -1