Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Making a C style callback object oriented?

I'm using a library that has callbacks like this:

void onReceive (Lacewing::Server &Server, Lacewing::Server::Client &Client,
                char * Data, int Size) {
  /* callback body */
}

Server.onReceive (onReceive); /* to register the handler */

I would like to be able to wrap this in a class that can decide what to do when it receives a packet (observer pattern).

How can I do this with C style callbacks? The library does not define an interface to inherit from.

Thanks

like image 356
jmasterx Avatar asked May 13 '12 23:05

jmasterx


People also ask

Does C have callback?

Callback functions are one the most powerful mechanisms in C. A callback function is any code that is passed as an argument to some other code, in such a way that this last code is able to call back, i.e., to execute, the code passed as argument. In C, callback functions are implemented using function pointers.

What is callback mechanism in C?

A callback is any executable code that is passed as an argument to other code, which is expected to call back (execute) the argument at a given time [Source : Wiki]. In simple language, If a reference of a function is passed to another function as an argument to call it, then it will be called as a Callback function.

Should callbacks be static?

Callbacks need to be static so that they don't have an implicit this parameter as the first argument in their function signature. Save this answer. Show activity on this post. Non-static methods require a 'this' instance, and can only be called upon an object instance.

How do you write a callback function in CPP?

Note the callback function expression, like a function call, in the definition of the principal function; any argument for the callback function call is passed there. The statement for this function call is: int idr = (*ptr)(id2); Where id2 is an argument.


2 Answers

Since you're using liblacewing, every class has a void * Tag member provided for user data:

/* class method */

void MyServer::onReceive (Lacewing::Server &Server, Lacewing::Server::Client &Client,
            char * Data, int Size)
{
     /* callback body - this is inside the class */
}


/* global function wraps the class method */

void onReceive (Lacewing::Server &Server, Lacewing::Server::Client &Client,
            char * Data, int Size)
{
    ((MyServer *) Server.Tag)->onReceive (Server, Client, Data, Size);
}

then:

Server.Tag = myServerInstance; /* set the class instance pointer */
Server.onReceive (::onReceive); /* register the global function */
like image 87
James McLaughlin Avatar answered Oct 19 '22 23:10

James McLaughlin


If I understand you correctly, you could do it 2 different ways.

Assuming that this callback structure is using the Data parameter to allow you to pass your own data to the callback function (a common paradigm), you could do it like this:

class MyProcessingClass
{
public:
    MyProcessingClass();
    virtual ~MyProcessingClass();

    // Do whatever processing in this method
    virtual void onReceive(Lacewing::Server &Server, Lacewing::Server::Client &Client);
}

void onReceive (Lacewing::Server &Server, Lacewing::Server::Client &Client,
                char * Data, int Size)
{
    if ( Data != NULL )
    {
        MyProcessingClass *handler = reinterpret_cast<MyProcessingClass *>(Data);
        handler->onReceive( Server, Client );
    }
}

Or, if the data pointer is something you need to process instead of a "user data pointer", you'd probably have to use a singleton, some kind of global variable, or similar. In my experience, this is a less common way to use callbacks, and hopefully isn't what you're dealing with.

MyProcessingClass g_Processor;

MyProcessingClass *GetProcessor()
{
    return &g_Processor; // or some other way of getting your instance
}

void onReceive (Lacewing::Server &Server, Lacewing::Server::Client &Client,
                char * Data, int Size)
{
    MyProcessingClass *handler = GetProcessor();
    if ( handler != NULL )
    {
        handler->onReceive( Server, Client );
    }
}
like image 41
Patrick W Avatar answered Oct 19 '22 23:10

Patrick W