Reputation: 537
Is there a way to use boost or std bind() so I could use a result as a callback in C API? Here's sample code I use:
#include <boost/function.hpp>
#include <boost/bind/bind.hpp>
typedef void (*CallbackType)();
void CStyleFunction(CallbackType functionPointer)
{
functionPointer();
}
class Class_w_callback
{
public:
Class_w_callback()
{
//This would not work
CStyleFunction(boost::bind(&Class_w_callback::Callback, this));
}
void Callback(){std::cout<<"I got here!\n";};
};
Thanks!
Upvotes: 4
Views: 2110
Reputation: 20798
As mentioned by the others, you need a global (a static member is a global hidden as a variable member) and of course if you need multiple objects to make use of different parameters in said callback, it won't work.
A C library may offer a void *
or some similar context. In that case use that feature.
For example, the ffmpeg library supports a callback to read data which is defined like so:
int(*read_packet)(void *opaque, uint8_t *buf, int buf_size);
The opaque
parameter can be set to this
. Within your callback, just cast it back to your type (name of your class).
A C library may call your callback with its object (struct
pointer). Say you have a library named example
which offers a type named example_t
and defines callbacks like this:
callback(example_t *e, int param);
Then you may be able to place your context (a.k.a. this
pointer) in that example_t
structure and retrieve it back out in your callback.
Assuming you have only one thread using that specific C library and that the callback can only be triggered when you call a function in the library (i.e. you do not get events triggered at some random point in time,) you could still use a global variable. What you have to do is save your current object in the global before each call. Something like this:
object_i_am_working_with = this;
make_a_call_to_that_library();
This way, inside the callback you can always access the object_i_am_working_with
pointer. This does not work in a multithreaded application or when the library automatically generates events in the background (i.e. a key press, a packet from the network, a timer, etc.)
This is an interesting solution in a multi-threaded environment. When none of the previous solutions are available to you, you may be able to resolve the problem using threads.
In C++11, there is a new special specifier named thread_local
. In the old days, you had to handle that by hand which would be specific to each thread implementation... now you can just do this:
thread_local Class_w_callback * callback_context = nullptr;
Then when in your callback you can use the callback_context
as the pointer back to your Class_w_callback
class.
This, of course, means you need to create one thread per object you create. This may not be feasible in your environment. In my case, I have components which are all running their own loop and thus each have their own thread_local
environment.
Note that if the library automatically generates events you probably can't do that either.
As I mentioned above, in the old days you would have to manage the local thread environment yourself. With pthread
(Linux based), you have the thread specific data accessed through pthread_getspecific()
:
void *pthread_getspecific(pthread_key_t key);
int pthread_setspecific(pthread_key_t key, const void *value);
This makes use of dynamically allocated memory. This is probably how the thread_local
is implemented in g++ under Linux.
Under MS-Windows, you probably would use the TlsAlloc function.
Upvotes: 1
Reputation: 155296
You cannot do this in portable C++. However, there are libraries out there that enable creation of C functions that resemble closures. These libraries include assembly code in their implementation and require manual porting to new platforms, but if they support architectures you care about, they work fine.
For example, using the trampoline library by Bruno Haible, you would write the code like this:
extern "C" {
#include <trampoline.h>
}
#include <iostream>
typedef int (*callback_type)();
class CallbackDemo {
static CallbackDemo* saved_this;
public:
callback_type make_callback() {
return reinterpret_cast<callback_type>(
alloc_trampoline(invoke, &saved_this, this));
}
void free_callback(callback_type cb) {
free_trampoline(reinterpret_cast<int (*)(...)>(cb));
}
void target(){
std::cout << "I got here, " << this << '\n';
};
static int invoke(...) {
CallbackDemo& me = *saved_this;
me.target();
return 0;
}
};
CallbackDemo *CallbackDemo::saved_this;
int main() {
CallbackDemo x1, x2;
callback_type cb1 = x1.make_callback();
callback_type cb2 = x2.make_callback();
cb1();
cb2();
}
Note that, despite the use of a static member, the trampolines created by alloc_trampoline
are reentrant: when the returned callback is invoked, it first copies the pointer to the designated address, and then invokes the original function with original arguments. If the code must also be thread-safe, saved_this
should be made thread-local.
Upvotes: 3
Reputation: 54727
This won't work.
The problem is that bind
returns a functor, that is a C++ class with an operator()
member function. This will not bind to a C function pointer. What you need is a static or non-member function that stores the this
pointer in a global or static variable. Granted, finding the right this
pointer for the current callback might be a non-trivial task.
Upvotes: 2
Reputation: 248199
No, there is no way to do that. The problem is that a C function pointer is fundamentally nothing more than an instruction address: "go to this address, and execute the instructions you find". Any state you want to bring into the function has to either be global, or passed as parameters.
That is why most C callback APIs have a "context" parameter, typically a void pointer, that you can pass in, and just serves to allow you to pass in the data you need.
Upvotes: 5