C++ register callback from server - c++

I've prototype of callback as:
typedef void (*update)(int id,...);
typedef void (*destroy)(int id,...);
typedef void (*create)(int id, update* _update, destroy* _destroy);
And than create callbacks function:
void updateCB(int id,...){/*Add id to collection*/}
void destroyCB(int id,...){/*Remove id from collection*/}
void createCB(int id,update* _update, destroy* _destroy)
{
//Register Callbacks
*_update = updateCB;
*_destroy = destroyCB;
}
When I register callbacks compiler give me error:
error: cannot convert 'ClassName::updateCB' from type 'void
(ClassName::)(int,...)' to type 'update {aka void (*)(int..)}'
How can I valid register callbacks?

You are trying to use member functions which is impossible in this case. This happens because every member function ( if it is not static ) carries with itself hidden pointer to class instance called this.
You need to use global functions or static member functions.

Related

Function Pointer Arduino as Callback Bluefruit Library

I have been trying to pass a callback to the setConnectCallback() function in the Bluefruit Library. When I pass the function names connect_callback into setConnectCallback()
I am getting the error invalid use of non-static member function of type 'void (AumeBluetooth::)()'
The function setConnectCallback() looks like it is asking for a function pointer:
exerpt from Adafruit_BLE Arduino Library:
/******************************************************************************/
/*!
#brief Set handle for connect callback
#param[in] fp function pointer, NULL will discard callback
*/
/******************************************************************************/
void Adafruit_BLE::setConnectCallback( void (*fp) (void) )
{
this->_connect_callback = fp;
install_callback(fp != NULL, EVENT_SYSTEM_CONNECT, -1);
}
I have a class "AumeBluetooth" defined as such, which I attempted to implement a function pointer to call connect_callback:
.h
class AumeBluetooth {
public:
bool isConnected = false;
Adafruit_BluefruitLE_SPI *_ble;
void error(const __FlashStringHelper*err);
void begin();
AumeBluetooth();
void loop();
void connect_callback(void);
};
.cpp
#include "AumeBluetooth.h"
#include <SPI.h>
#include "Adafruit_BLE.h"
#include "Adafruit_BluefruitLE_SPI.h"
#include "Adafruit_BluefruitLE_UART.h"
#include "BluefruitConfig.h"
AumeBluetooth::AumeBluetooth() {
}
void AumeBluetooth::begin() {
isConnected = false;
Adafruit_BluefruitLE_SPI ble(BLUEFRUIT_SPI_CS, BLUEFRUIT_SPI_IRQ, BLUEFRUIT_SPI_RST);
_ble = &ble;
if ( !_ble->begin() )
{
error(F("Couldn't find Bluefruit, make sure it's in CoMmanD mode & check wiring?"));
}
_ble->echo(false);
_ble->info();
_ble->setMode(BLUEFRUIT_MODE_DATA);
void (AumeBluetooth::*cc)(void) = &AumeBluetooth::connect_callback;
ble.setConnectCallback(this->*cc);
}
void AumeBluetooth::connect_callback(void) {
Serial.print("BLUETOOTH IS CONNECTED");
isConnected = true;
}
}
Not sure what to do try next. Thanks!
setConnectCallback is looking for a static function pointer. As the error message says, you are passing it a non-static function pointer.
Your callback function must be a static function - either a free function, or a class function that is specifically designated 'static' and therefore has no access to class variables.
This is a tricky API, because it also looks like the function parameter list is (void), which means you don't have a way to pass in an index or a pointer to tie it to a class instance. You only get one callback to a static function, and it is up to your code to know which class instance the callback might be for.
So, your connect_callback function won't be able to set a class variable isConnected inside the callback. You will only be able to access global/static variables.
I would expect the begin() and loop() function calls also to be static, non-class functions. It looks like maybe you are trying to put a class wrapper around code that doesn't have to be a class.

Cast error passing a void function with namespace

I'm trying to wrap, in a C++ class, a server that I wrote using mongoose (a C library). The problem is that I'm trying to pass the function ev_handler to the mg_create_server(), which create the instance of the server in mongoose. But it gives a casting error I believe:
src/Server.cpp:16:44: error: cannot convert 'Server::ev_handler' from
type 'int (Server::)(mg_connection*, mg_event)' to type 'mg_handler_t
{aka int (*)(mg_connection*, mg_event)}' server =
mg_create_server(NULL, ev_handler);
I tried to make ev_handler static but it has send_index_page(conn) that has to be inside the wrapper class.
void Server::start() {
struct mg_server *server;
int numberOfObjects;
_application = new Application();
_application->start();
// Create and configure the server
server = mg_create_server(NULL, ev_handler);
//... more code here ...
}
int Server::ev_handler(struct mg_connection *conn, enum mg_event ev) {
switch (ev) {
case MG_AUTH: return MG_TRUE;
case MG_REQUEST: return send_index_page(conn);
default: return MG_FALSE;
}
}
Your problem is that you're passing a C++ member function to parameter that wants a free function pointer.
Mongoose is a C API and all of its callback parameters are C style functions, which in C++ are free (not member) functions.
A member function pointer is different from a free function pointer in that it needs the this , or the object on which the method is being called, in order to be called.
In your case, you are passing a member function pointer on the Server class.
When interacting which C APIs, it's common to pass a void* context object which is then passed to the callback. You then pass a pointer to a free function or a static class method (which has no this and can therefore work with C APIs). When the callback is invoked, you then cast the context object to the correct type and call a member function to get back into the object context. I can't see any such facility in Mongoose. Maybe it's there and I'm just not finding it.
You may want to try the already exising Mongoose C++ which forks the original Mongoose project to work better with C++: https://github.com/Gregwar/mongoose-cpp
The callback needs to be static, then you should use a static stub to redirect to the class instance.
Storing the instance of your class in server_param attribute of mg_server will allow to get it back in a static stub and forward it to this instance.
This could be achieve like this :
class Server
{
public:
void start() {
mg_create_server(this, ev_handlerStub);
}
static int ev_handlerStub(struct mg_connection *conn, enum mg_event ev) {
((Server*)conn->server_param)->ev_handler(conn, ev);
}
int ev_handler(struct mg_connection *conn, enum mg_event ev) {
// job to do with the class instance
}
};
Proceeding like this, allow access to class instance inside its ev_handler method.

Passing an instance method to an API that expects a C function pointer

I have a C API to a data source. To be notified that new data is available you give the API a callback in the form of a function pointer; your callback will be called when data comes in. The API’s header contains lines like this:
struct DataRecord { ... };
typedef void (*DataCallback)(DataRecord *data);
void set_data_callback(DataCallback processor);
My C++ class has an instance method with the signature
void MyClass::process_data(DataRecord *data);
and in the constructor of MyClass I’d like to set the new instance’s process_data method as the data callback for the C API. Following this answer I tried to write this code in the constructor:
typedef void (MyClass::data_callback_t)(DataRecord*);
data_callback_t callback = &MyClass::process_data;
set_data_callback(callback);
When I do this I get the error
error C2664: 'set_data_callback' : cannot convert parameter 2 from 'data_callback_t' to 'DataCallback'
There is no context in which this conversion is possible
(I am using Visual C++ 2010 Express, although I hope that doesn’t make a difference.)
How can I extract a C function pointer from an instance and a method?
You can't. MyClass::process_data can be thought of as a void(MyClass*, DataRecord*), which is the wrong type. You'd have to wrap your class pointer into the call somehow.
One approach might be to introduce a type with a static pointer:
struct MyClassCallbackHelper
{
static MyClass* myClass;
static void callback(DataRecord* record) {
myClass->process_data(record);
}
};
So that you can do:
MyClassCallbackHelper::myClass = this;
set_data_callback(&MyClassCallbackHelper::callback);

non-member function pointer as a callback in API to member function

I'm using an API that requires me to pass a function pointer as a callback. I'm trying to use this API from my class in C++ but I'm getting compilation errors.
The API definition is:
typedef void (__stdcall *STREAM_CALLBACK)(void *userdata);
__declspec(dllimport) int __stdcall set_stream_callback(
STREAM_CALLBACK streamCB, void *userdata);
One example file, provided by the third party, is:
void __stdcall streamCB(void *userdata)
{
// callback implementation
}
int main(int argc, const char argv[])
{
int mid = 0;
set_stream_callback(streamCB, &mid);
}
And that works fine.
However when I try to use that in a class, I have an error:
error C3867: 'MyClass::streamCB': function call missing argument list;
use '&MyClass::streamCB' to create a pointer to member
The suggestion to use
&MyClass::streamCB
doesn't work.
I understood that the set_stream_callback only accepts a non-member function.
The problem is very similar to
How can I pass a class member function as a callback?
in which Johannes makes a concise suggestion, however I do not understand it very well. Could anyone expand a bit, if I am correct that it is relevant to this question?
I have tried:
void __stdcall MyClass::streamCB(void *userdata)
{
// callback implementation
}
static void MyClass::Callback( void * other_arg, void * this_pointer ) {
MyClass * self = static_cast<ri::IsiDevice*>(this_pointer);
self->streamCB( other_arg );
}
//and in the constructor
int mid = 0;
set_stream_callback(&MyClass::Callback, &mid);
But
error C2664: 'set_stream_callback' : cannot convert parameter 1 from
'void (__cdecl *)(void *,void *)' to 'STREAM_CALLBACK'
How do I get around this?
Edit1: Also, I want to use userdata inside the streamCB callback.
The idea of calling a member function from a callback taking only non-member functions is to create a wrapper for you member function. The wrapper obtains an object from somewhere and then calls the member function. If the callback is reasonably well designed it will allow you to pass in some "user data" which you'd use to identify your object. You, unfortunately, left out any details about your class so I'm assuming it looks something like this:
class MyClass {
public:
void streamCB() {
// whatever
}
// other members, constructors, private data, etc.
};
With this, you can set up your callback like so:
void streamCBWrapper(void* userData) {
static_cast<MyClass*>(userData)->streamCB()
}
int main() {
MyClass object;
set_stream_callback(&streamCBWrapper, &object);
// ...
}
There are various games you can play with how to create the streamCBWrapper function (e.g., you can make it a static member of your class) but all come down to the same: you need to restore your object from the user data and call the member function on this object.
You can achieve what you want to do by turning the userdata into a property of MyClass. Then you don't have to pass it to MyClass::Callback, which would be impossible, since you can only pass one parameter, and it would be the object instance.
Here's an example.
void __stdcall MyClass::streamCB()
{
// callback implementation
}
static void MyClass::Callback(void * this_pointer ) {
MyClass * self = static_cast<MyClass>(this_pointer);
self->streamCB();
}
MyClass::MyClass(void *userdata) {
// do whatever you need to do with userdata
// (...)
// and setup the callback at C level
set_stream_callback(&MyClass::Callback, (void *)this);
}
In your example, the int mid variable would become a property of that class, and thus be accessible from the callback implementation streamCB.

Callback to member function using libevent

I am trying to pass a member function to libevent which should be treated as a callback.
#include <event.h>
class A
{
public:
void eventcb(evutil_socket_t fd, short events, void *ctx) { }
};
static void global_eventcb(evutil_socket_t fd, short events, void *ctx) { }
typedef void (A::*mthd)(evutil_socket_t, short, void*);
int main(void)
{
struct event_base *evbase = event_base_new();
mthd eventcb = &A::eventcb;
A *instance = new A;
(instance->*eventcb)(NULL, 0, NULL);
struct event *timer1 = evtimer_new(evbase, global_eventcb, NULL);
struct event *timer2 = evtimer_new(evbase, (instance->*eventcb), NULL);
return 0;
}
I can successfully create a method pointer to eventcb in class A and call it on an instance of A (row 20).
Also, passing a global function (as one would do in C) on row 22 also works fine.
However, on row 23, I attempt to pass my method pointer to libevent, and when I compile this I get the following error (using the clang compiler)
example.cpp:23:25: error: no matching function for call to 'event_new'
struct event *timer2 = evtimer_new(evbase, (instance->*eventcb), NULL);
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
In file included from example.cpp:1:
In file included from /usr/local/include/event.h:71:
/usr/local/include/event2/event.h:749:40: note: instantiated from:
#define evtimer_new(b, cb, arg) event_new((b), -1, 0, (cb), (arg))
^~~~~~~~~
/usr/local/include/event2/event.h:833:15: note: candidate function not viable: no know conversion from '<bound member function type>' to 'event_callback_fn'
(aka 'void (*)(int, short, void *)') for 4th argument
struct event *event_new(struct event_base *, evutil_socket_t, short, event_callback_fn, void *);
^
1 error generated.
What am I doing wrong?
Instance method pointers need an instance to be invoked on. Since libevent is a C library, it doesn't directly provide a mechanism to associate an instance and an instance method, so you'll have to do it yourself. libevent's various event creation functions let you pass arbitrary data as a callback argument. The instance pointer can be passed via this argument, either directly or packaged in a class with other arguments if the callback takes additional data. The event callback can be a free function or a static method; which approach to take depends on the class's responsibility (in the SOLID, single-responsibilty sense).
An example using a static method and passing no additional data:
class A {
public:
A(struct event_base *);
bool start_timer();
static void invoke_timer_handler(evutil_socket_t fd, short events, void *ctx);
void handle_timeout(evutil_socket_t fd, short events);
protected:
struct event_base *evbase;
struct event *timer;
};
A::A(struct event_base *event_base) : evbase(event_base), timer(NULL) {}
bool A::start_timer() {
// not thread safe.
if (! timer) {
timer = evtimer_new(evbase, &A::invoke_timer_handler, this);
return true;
}
return false;
}
void A::invoke_timer_handler(evutil_socket_t fd, short events, void *ctx) {
(static_cast<A*>(ctx))->handle_timeout(fd, events);
}
void A::handle_timeout(evutil_socket_t fd, short events) {
...
if (evtimer_del(timer)) {
// error deleting event
...
} else {
timer=NULL;
}
}
In the example, since A::handle_timeout is only called from within A::invoke_timer_handler, it could be made private or protected.
The sample has very basic memory management. In general, the code must ensure the instance (and other callback arguments, if the callback argument isn't simply an A*) exists for the lifetime of the event to prevent access errors. It should also ensure the instance doesn't leak once the event is no longer needed. If the instance owns the event, memory management is relatively straightforward. Concurrency can also add complication that affect memory management.
Existing code-level implementations of anonymous functions (e.g. boost::lambda) and the forthcoming lambda expressions from C++11 rely on the function call operator (operator()), which is unsupported in plain C. Thus anonymous functions are unsuitable for use as libevent callbacks or any other C-library callbacks.