 |
C++Talk.NET C++ language newsgroups
|
| View previous topic :: View next topic |
| Author |
Message |
Stefan Preuss Guest
|
Posted: Wed Sep 24, 2003 10:27 am Post subject: Kniffliges Kapselproblem... |
|
|
Hallo Newsgruppe,
ich hänge gerade an einem sehr kniffligen Problem und hoffe hier hat
jemand einen Tip für mich.
Es geht darum die c-Api einer Schnittstellenkarte in einem c++-Objekt zu
kapseln. Um nun auf Signale der Schnittstelle zu reagieren, kann man bei
der Api eine CallBack-Funktion anmelden:
z.B:
//Signal verarbeiten
int processSignal()
{ /*do somthing*/ }
|
|
| Back to top |
|
 |
Ludwig Pumberger Guest
|
Posted: Wed Sep 24, 2003 11:59 am Post subject: Re: Kniffliges Kapselproblem... |
|
|
Stefan Preuss schrieb:
| Quote: | ich hänge gerade an einem sehr kniffligen Problem und hoffe hier hat
jemand einen Tip für mich.
Es geht darum die c-Api einer Schnittstellenkarte in einem c++-Objekt zu
kapseln. Um nun auf Signale der Schnittstelle zu reagieren, kann man bei
der Api eine CallBack-Funktion anmelden:
z.B:
//Signal verarbeiten
int processSignal()
{ /*do somthing*/ }
.
.
.
//Verarbeitung an der Api anmelden
ApiCallBackFkt(processSignal);
Am liebsten würde ich nun gerne ein Objekt folgender Art bauen:
class IOCard
{
public:
IOCard();
~IOCard();
.
.
.
protected:
//Bearbeitung des Signals in den abgeleiteten Klassen
virtual int processSignal();
.
.
}
mit dem Konstruktor
IOCard::IOCard()
{ //Verarbeitung an der Api anmelden
ApiCallBackFkt(processSignal);
}
dies wird leider mit einem Compilerfehler
Argument of type `void (IOCard: (int)' does not match `void (*)(int)
verhindert.
|
Fast jede C-Callbackfunktion sieht grob gesprochen so aus:
some_type Callback(..., void *userdata);
Die user_data werden gewöhnlich bei der Registrierung übergeben, oder
können mit einer speziellen Funktion an das Handle gebunden werden für das
der Callback eingerichtet wird. Hier übergibt man dann einfach this. Und
castet in einer static Funktion der Klasse zurück um schliesslich die
virtuelle Funktion aufzurufen. (Vorsicht: Der Konstruktor muss
abgeschlossen sein bevor es zum Aufruf einer virtuellen Funktion kommt)
Manche C-APIs sind auch etwas lästiger und nehmen einen int als Userdata.
Da wird es dann plattforspezifisch. Auf üblichen 32 Bit Plattformen kannst
du eine Zeiger/int-Konvertierung mittels reinterpret_cast vornehmen, nur
portabel ist das nicht mehr.
Sollte es tatsächlich weder ein Möglichkeit geben direkt Benutzerdaten in
den Callback zu bringen noch die Daten mit einem Handle zu verknüpfen musst
du wohl oder übel auf static Daten zurückgreifen z.B. eine Map die
irgendwie ein Handle eine ID oder sonst etwas, womit du den Callback
verbindest, mit deinem Zeiger verknüpft. Hier gilt dann verstärkt: Vorsicht
bei Multithreading!
--
de.comp.lang.iso-c++ - Moderation: mailto:voyager+mod (AT) bud (DOT) prima.de
FAQ: http://www.voyager.prima.de/cpp/ mailto:voyager+send-faq (AT) bud (DOT) prima.de
|
|
| Back to top |
|
 |
Fabian Scheler Guest
|
Posted: Wed Sep 24, 2003 12:02 pm Post subject: Re: Kniffliges Kapselproblem... |
|
|
Hallo Stefan,
hier hast du eine ganz normale Funktion die einen int zurückgibt,
| Quote: | int processSignal()
{ /*do somthing*/ }
.
|
hier aber hast du eine Methode, die zu einer Klasse gehört
| Quote: | class IOCard
{
public:
IOCard();
~IOCard();
.
.
.
protected:
//Bearbeitung des Signals in den abgeleiteten Klassen
virtual int processSignal();
.
.
}
|
und der Compiler übergibt jeder Methode, soweit ich weiß, implizit einen
this-Zeiger als erstes Argument (es sei denn sie ist als static deklariert),
die Methode processSignal() der Klasse IOCard hat also keinesfalls dieselbe
Signatur wie die Globale Funktion processSignal().
| Quote: | IOCard::IOCard()
{ //Verarbeitung an der Api anmelden
ApiCallBackFkt(processSignal);
}
dies wird leider mit einem Compilerfehler
Argument of type `void (IOCard: (int)' does not match `void (*)(int)
verhindert.
|
und das mag der Compiler zu recht nicht.
Ciao, Fabian
--
de.comp.lang.iso-c++ - Moderation: mailto:voyager+mod (AT) bud (DOT) prima.de
FAQ: http://www.voyager.prima.de/cpp/ mailto:voyager+send-faq (AT) bud (DOT) prima.de
|
|
| Back to top |
|
 |
Stefan Reuther Guest
|
Posted: Wed Sep 24, 2003 12:15 pm Post subject: Re: Kniffliges Kapselproblem... |
|
|
Hallo,
Stefan Preuss <stpreuss (AT) ira (DOT) uka.de> wrote:
| Quote: | Es geht darum die c-Api einer Schnittstellenkarte in einem c++-Objekt zu
kapseln. Um nun auf Signale der Schnittstelle zu reagieren, kann man bei
der Api eine CallBack-Funktion anmelden:
[...]
void globalCallBack (int)
{ if (globalp_IOCard != NULL)
{ globalp_IOCard->processSignal(int);}
}
[...]
IOCard::IOCard()
{
globalp_IoCard = this;
ApiCallBackFkt(globalCallBack);
}
Ist hier noch einigermassen übersichtlich, mit mehreren Karten und
Signalarten wird das ganze aber sehr unübersichtlich und hässlich.
|
Das ist so ziemlich die einzige Möglichkeit, die bei meinem
begrenzten Wissen über dein API in Frage kommt.
| Quote: | Das Problem sollte eigentlich öfters auftauchen, habe aber in der
Literatur und im Netz keine Lösung gefunden.
|
Die übliche Lösung besteht darin, daß die Callback-Funktion
ein irgendwie geartetes Handle der "Karte" übergeben bekommt.
Aus diesem Handle kannst du dann das Objekt ermitteln.
Beispiel Signal-Handling: der Signal-Handler bekommt als
Parameter die Signalnummer (dein `globalCallBack' auch. Ist das
vielleicht schon das gewünschte?). Damit läßt sich ein
objektorientierter Signalhandler bauen als
// Prinzipskizze. Für echte Signale müßte man das
// etwas anders aufziehen. Insbesondere darf ein
// ISO-konformer Signalhandler nicht auf eine std::map
// zugreifen.
class SigHandler {
public:
virtual void handle() = 0;
void attachToSignal(int);
};
std::map<int, SigHandler*> signals;
void globalSigHandler(int n) {
signals[n]->handle();
}
void SigHandler::attachToSignal(int n) {
signals[n] = this;
std::signal(n, globalSigHandler);
}
Beispiel Win32- oder X11-API: wenn eine Botschaft an ein Fenster
zugestellt wird, enthält diese das Fenster-Handle. Ein Wrapper
für Fenster wäre damit analog zu oben machbar: die Zuordnung
HWND -> Handlerobjekt kommt in eine std::map, die globale
Handler-Funktion sucht das passende Objekt raus.
Des weiteren bieten beide Windowing-Systeme dir die Möglichkeit,
benutzerdefinierte Daten in dem Window-Objekt abzulegen
('SetWindowLong' IIRC). Du kannst also einfach direkt in dem
Window-Objekt den Zeiger auf das Handler-Objekt ablegen.
Beispiel SDL: hier ist dieser Zeiger gleich im API vorgesehen:
// Typ einer Callback-Funktion
typedef Uint32 (*SDL_NewTimerCallback)(Uint32 interval, void *param);
// Erzeugen eines Callbacks
SDL_TimerID SDL_AddTimer(Uint32 interval, SDL_NewTimerCallback callback, void *param);
Damit könnte dein Handler so aussehen:
class Handler {
SDL_TimerID id;
static Uint32 callback(Uint32 interval, void* param) {
static_cast<Handler*>(param)->handle();
return interval;
}
public:
Handler(int n) { id = SDL_AddTimer(n, callback); }
~Handler() { SDL_RemoveTimer(id); }
virtual void handle() = 0;
};
Das ist so ziemlich der Optimalfall.
Nuja, langer Rede kurzer Sinn: irgend ein Feature dieser Art
sollte sich in deiner Library finden lassen, wenn sie nicht
gerade von 1975 stammt. Ansonsten bleibt dir nur die Lösung, die
du schon hast.
Stefan
--
de.comp.lang.iso-c++ - Moderation: mailto:voyager+mod (AT) bud (DOT) prima.de
FAQ: http://www.voyager.prima.de/cpp/ mailto:voyager+send-faq (AT) bud (DOT) prima.de
|
|
| Back to top |
|
 |
Falk Tannhäuser Guest
|
Posted: Wed Sep 24, 2003 1:20 pm Post subject: Re: Kniffliges Kapselproblem... |
|
|
Ludwig Pumberger wrote:
| Quote: | Fast jede C-Callbackfunktion sieht grob gesprochen so aus:
some_type Callback(..., void *userdata);
Die user_data werden gewöhnlich bei der Registrierung übergeben, oder
können mit einer speziellen Funktion an das Handle gebunden werden für das
der Callback eingerichtet wird. Hier übergibt man dann einfach this. Und
castet in einer static Funktion der Klasse zurück um schliesslich die
virtuelle Funktion aufzurufen. (Vorsicht: Der Konstruktor muss
abgeschlossen sein bevor es zum Aufruf einer virtuellen Funktion kommt)
Manche C-APIs sind auch etwas lästiger und nehmen einen int als Userdata.
Da wird es dann plattforspezifisch. Auf üblichen 32 Bit Plattformen kannst
du eine Zeiger/int-Konvertierung mittels reinterpret_cast vornehmen, nur
portabel ist das nicht mehr.
|
Weiterhin ist nicht garantiert, dass Zeiger auf C++-Funktionen mit
Zeigern auf C-Funktionen kompatibel sind, selbst wenn die Signaturen
übereinstimmen (dies funktioniert z.B. mit gcc, aber nicht auf
[url]http://www.comeaucomputing.com/tryitout/)[/url]. Für maximale Portabilität
muss die Callback-Funktion daher als 'extern "C"' deklariert werden
(und kann somit kein statisches Klassenmitglied sein und muss im globalen
Namespace stehen). Gegebenenfalls muss sie dann als 'friend' der Klasse
deklariert werden.
Falk
--
de.comp.lang.iso-c++ - Moderation: mailto:voyager+mod (AT) bud (DOT) prima.de
FAQ: http://www.voyager.prima.de/cpp/ mailto:voyager+send-faq (AT) bud (DOT) prima.de
|
|
| Back to top |
|
 |
Georg Gast Guest
|
Posted: Thu Sep 25, 2003 6:03 am Post subject: Re: Kniffliges Kapselproblem... |
|
|
| Quote: | Beispiel Signal-Handling: der Signal-Handler bekommt als
Parameter die Signalnummer (dein `globalCallBack' auch. Ist das
vielleicht schon das gewünschte?). Damit läßt sich ein
objektorientierter Signalhandler bauen als
// Prinzipskizze. Für echte Signale müßte man das
// etwas anders aufziehen. Insbesondere darf ein
// ISO-konformer Signalhandler nicht auf eine std::map
// zugreifen.
class SigHandler {
public:
virtual void handle() = 0;
void attachToSignal(int);
};
std::map<int, SigHandler*> signals;
void globalSigHandler(int n) {
signals[n]->handle();
}
void SigHandler::attachToSignal(int n) {
signals[n] = this;
std::signal(n, globalSigHandler);
}
|
Hallo,
warum dürfte ein ISO-konformer Signalhandler auf keine std::map<> zugreifen?
Gruß
Georg
--
de.comp.lang.iso-c++ - Moderation: mailto:voyager+mod (AT) bud (DOT) prima.de
FAQ: http://www.voyager.prima.de/cpp/ mailto:voyager+send-faq (AT) bud (DOT) prima.de
|
|
| Back to top |
|
 |
Stefan Preuss Guest
|
Posted: Thu Sep 25, 2003 9:20 am Post subject: Re: Kniffliges Kapselproblem... |
|
|
Hallo Newsgruppe,
Besten Dank für die vielen Einsichten und Vorschläge.
Ich werde davon jetzt einge in der nächsten Zeit mal ausprobieren und
eventuell posten, was zum Erfolg geführt hat.
Bis dann
Stefan Preuß
--
/-----------------------------------------------------
| Quote: | Stefan Preuß EMAIL: [email]stpreuss (AT) ira (DOT) uka.de[/email] |
-----------------------------------------------------/ |
--
de.comp.lang.iso-c++ - Moderation: mailto:voyager+mod (AT) bud (DOT) prima.de
FAQ: http://www.voyager.prima.de/cpp/ mailto:voyager+send-faq (AT) bud (DOT) prima.de
|
|
| Back to top |
|
 |
Stefan Reuther Guest
|
Posted: Thu Sep 25, 2003 10:53 am Post subject: Re: Kniffliges Kapselproblem... |
|
|
Hallo,
Georg Gast <schorsch_76 (AT) despammed (DOT) com> wrote:
| Quote: | // Prinzipskizze. Für echte Signale müßte man das
// etwas anders aufziehen. Insbesondere darf ein
// ISO-konformer Signalhandler nicht auf eine std::map
// zugreifen.
warum dürfte ein ISO-konformer Signalhandler auf keine std::map<> zugreifen?
|
Warum kommt die Frage eigentlich jedes Mal, wenn ich was zu
Signalen poste? :-)
Ein Signalhandler darf portabel nichts weiter tun als
- eine Variable vom Typ 'volatile sig_atomic_t' setzen
- 'abort' oder 'signal' aufrufen.
Stell dir einfach vor, jemand macht gerade
'signals[SIGTERM] = my_function', und während der operator[]
gerade lustig irgendwelche AVL-Baum-Knoten durch die Gegend
schiebt kommt das SIGTERM.
Du bräuchtest Synchronisation (z.B. sigblock/sigunblock). Sowas
ist aber nicht Teil von ISO C++, daher sind Signalhandler, die
mehr tun als oben angeführt undefiniert.
In der Praxis gilt z.B. oft sizeof(sig_atomic_t)==sizeof(void*)
bzw. sizeof(sig_atomic_t)==sizeof(void(*)(int)), so daß du ein
Array, welches über die Signalnummer indiziert wird, problemlos
lesen kannst.
Und das ganze noch mal in offizieller Standard-Speak:
ISO C 7.14.1.1p5:
# If the signal occurs other than as the result of calling the
# abort or raise function, the behavior is undefined if the
# signal handler refers to any object with static storage
# duration other than by assigning a value to an object declared
# as volatile sig_atomic_t, or the signal handler calls any
# function in the standard library other than the abort
# function, the _Exit function, or the signal function with the
# first argument equal to the signal number corresponding to the
# signal that caused the invocation of the handler. Furthermore,
# if such a call to the signal function results in a SIG_ERR
# return, the value of errno is indeterminate.211)
in Verbindung mit ISO C++ 1.9p9:
# When the processing of the abstract machine is interrupted by
# receipt of a signal, the values of objects with type other
# than volatile sig_atomic_t are unspecified, and the value of
# any object not of volatile sig_atomic_t that is modified by
# the handler becomes undefined.
und 18.7p5:
# The common subset of the C and C++ languages consists of all
# declarations, definitions, and expressions that may appear in
# a well formed C++ program and also in a conforming C program.
# A POF (plain old function) is a function that uses only
# features from this common subset, and that does not directly
# or indirectly use any function that is not a POF. All signal
# handlers shall have C linkage. A POF that could be used as a
# signal handler in a conforming C program does not produce
# undefined behavior when used as a signal handler in a C++
# program. The behavior of any other function used as a signal
# handler in a C++ program is implementation defined.
Stefan
--
Siehe auch <1015413989.ganymed%sr21 (AT) inf (DOT) tu-dresden.de> ff.
und <1051092995.irz777%sr21 (AT) inf (DOT) tu-dresden.de> ff.
--
de.comp.lang.iso-c++ - Moderation: mailto:voyager+mod (AT) bud (DOT) prima.de
FAQ: http://www.voyager.prima.de/cpp/ mailto:voyager+send-faq (AT) bud (DOT) prima.de
|
|
| Back to top |
|
 |
|
|
You cannot post new topics in this forum You cannot reply to topics in this forum You cannot edit your posts in this forum You cannot delete your posts in this forum You cannot vote in polls in this forum
|
|