anda_skoa
24-04-2005, 14:24
Wenn man aus C++ Code heraus C APIs benutzt, kommt man manchmal in die Situation eine Callback Funktion im C Stil angeben zu müssen, aber man gerne eine Methode einer bestimmten Instanz einer eigenen Klasse aufgerufen hätte.
Das läßt sich üblicherweise ohne weitere Probleme bewerkstelligen, denn die Funktionen der C API, die zum Registrieren der Callbacks benutzt werden, bzw. die Signaturen der Callbacks sehen üblicherweise ein Kontextargument vor.
Dieses Kontextargument benutzt man um die Instanz der Klasse zu transportieren, sie ist ja in diesem Fall unser Anhaltspunkt.
Konkret sieht das so aus:
#include <iostream>
using std::cout;
using std::endl;
///////////////////// C API /////////////////////
// Ab hier steht die "Simulation" unserer C API, d.h. dieser Abschnitt dient nur zur
// Vereinfachung des Beispiels, normalerweise wäre das in der jeweiligen Bibliothek
// implementiert und wir würden den entsprechenden Header inkludieren
int numArgs;
char** args;
// die simulierte Callbackregistrierfunktion ruft nur gleich direkt den Callback
// auf
int registerCallback(int callback(void*, int, char**), void* context)
{
if (callback == 0) return 0;
return callback(context, numArgs, args);
}
///////////////////// C API /////////////////////
// Ab hier beginnt der eigentliche C++ Teil
class CallbackHandler
{
public:
// Die static Methode entspricht in ihrer Signatur genau den Anforderungen
// für den C API Callback. Da sie static ist, kann sie ohne Instanz benutzt werden
// und kann darum an die C API weiter gegeben werden, die ja nichts von
// Instanzen weiß
static int callback(void* context, int argCount, char** args)
{
cout << "CallbackHandler::callback: context=" << context << endl;
if (context == 0) return 0;
// wie wir "wissen" ist das Kontextargument unsere Instanz, also brauchen wir
// nur zu casten und die "echte" Methode zur Behandlung der Daten aufrufen
CallbackHandler* handler = reinterpret_cast<CallbackHandler*>(context);
return handler->doCallback(argCount, args);
}
private:
// Methode die die Daten eigentlich verarbeiten soll und dabei Zugriff auf
// eine bestimmte Instanz der Klasse haben soll, also nicht static sein kann
int doCallback(int argCount, char** args)
{
cout << "CallbackHandler::doCallback: this=" << this << endl;
for (int i = 0; i < argCount; ++i)
{
cout << "args[" << i << "]=" << args[i] << endl;
}
return 1;
}
};
int main(int argc, char** argv)
{
// Daten für unsere C API Simulation bereitstellen
numArgs = argc;
args = argv;
// Unseren Handler erzeugen
CallbackHandler handler;
// static Methode als Callback angeben und die Instanz, bzw. den Pointer darauf
// als Wert für das Kontextargument
bool success = registerCallback(CallbackHandler::callback, &handler) != 0;
cout << "success=" << (success ? "true" : "false") << endl;
return 0;
}
Ciao,
_
Das läßt sich üblicherweise ohne weitere Probleme bewerkstelligen, denn die Funktionen der C API, die zum Registrieren der Callbacks benutzt werden, bzw. die Signaturen der Callbacks sehen üblicherweise ein Kontextargument vor.
Dieses Kontextargument benutzt man um die Instanz der Klasse zu transportieren, sie ist ja in diesem Fall unser Anhaltspunkt.
Konkret sieht das so aus:
#include <iostream>
using std::cout;
using std::endl;
///////////////////// C API /////////////////////
// Ab hier steht die "Simulation" unserer C API, d.h. dieser Abschnitt dient nur zur
// Vereinfachung des Beispiels, normalerweise wäre das in der jeweiligen Bibliothek
// implementiert und wir würden den entsprechenden Header inkludieren
int numArgs;
char** args;
// die simulierte Callbackregistrierfunktion ruft nur gleich direkt den Callback
// auf
int registerCallback(int callback(void*, int, char**), void* context)
{
if (callback == 0) return 0;
return callback(context, numArgs, args);
}
///////////////////// C API /////////////////////
// Ab hier beginnt der eigentliche C++ Teil
class CallbackHandler
{
public:
// Die static Methode entspricht in ihrer Signatur genau den Anforderungen
// für den C API Callback. Da sie static ist, kann sie ohne Instanz benutzt werden
// und kann darum an die C API weiter gegeben werden, die ja nichts von
// Instanzen weiß
static int callback(void* context, int argCount, char** args)
{
cout << "CallbackHandler::callback: context=" << context << endl;
if (context == 0) return 0;
// wie wir "wissen" ist das Kontextargument unsere Instanz, also brauchen wir
// nur zu casten und die "echte" Methode zur Behandlung der Daten aufrufen
CallbackHandler* handler = reinterpret_cast<CallbackHandler*>(context);
return handler->doCallback(argCount, args);
}
private:
// Methode die die Daten eigentlich verarbeiten soll und dabei Zugriff auf
// eine bestimmte Instanz der Klasse haben soll, also nicht static sein kann
int doCallback(int argCount, char** args)
{
cout << "CallbackHandler::doCallback: this=" << this << endl;
for (int i = 0; i < argCount; ++i)
{
cout << "args[" << i << "]=" << args[i] << endl;
}
return 1;
}
};
int main(int argc, char** argv)
{
// Daten für unsere C API Simulation bereitstellen
numArgs = argc;
args = argv;
// Unseren Handler erzeugen
CallbackHandler handler;
// static Methode als Callback angeben und die Instanz, bzw. den Pointer darauf
// als Wert für das Kontextargument
bool success = registerCallback(CallbackHandler::callback, &handler) != 0;
cout << "success=" << (success ? "true" : "false") << endl;
return 0;
}
Ciao,
_