Hallo C++Builder-Fachleute! In einem C++Builder-6-Projekt (ich weiß, der ist steinalt, die Fragestellung gilt aber auch für RAD-Studio XE) verwende ich eine nichtvisuelle Komponente (nennen wir sie "TSerial"). Sie wird zur Laufzeit erzeugt: Serial = new TSerial(NULL); Serial->Name = "ABC"; Die Komponente kennt das Ereignis "OnEventChar". Diesem Ereignis soll eine Funktion zugewiesen werden. Ist die Ereignisbehandlungsroutine innerhalb eines Formulars deklariert, etwa in der Art void __fastcall TForm1::EventChar(TObject *Sender) { ... } ist auch die Zuweisung kein Problem: Serial->OnEventChar = EventChar; *** In meinem Problemfall verwende ich die Komponente jedoch nicht in einem Formular sondern in einer DLL sozusagen "standalone", d.h. die Variable "Serial" ist eine globale Variable in der DLL. Da es kein Formular gibt, ist auch die Ereignisbehandlungsroutine als normale Funktion deklariert: void __fastcall EventChar(TObject *Sender) { ... } Dadurch funktioniert die Zuweisung einer Ereignisbehandlungsroutine, wie oben gezeigt, nicht mehr. Der Compiler wirft die Fehlermeldung [C++ Fehler] Unit_Serial.cpp(109): E2034 Konvertierung von 'void (_fastcall *)(TObject *)' nach 'void (_fastcall * (_closure )(TObject *))(TObject *)' nicht möglich aus. Der Typ der Ereignisbehandlungsroutine passt also nicht mehr ganz. Ich habe schon einiges rumprobiert und nach passenden Lösungen im Netz gesucht -- erfolglos. Vielleicht hat in diesem Forum jemand die richtige Lösung zur Hand, wie die Ereignisbehandlungsroutine in diesem Fall deklariert werden muss oder wie der Typecast bei der Zuweisung auszusehen hat. Übrigens: Unter Pascal (Delphi) ist die Lösung sehr einfach. Der Funktionsdeklaration der Ereignisbehandlungsroutine wird einfach "of object" dran gehängt. Beim C++Builder wird das aber anders sein! Für eure produktiven Lösungsvorschläge schon mal vielen Dank!
Rainer R. schrieb: > Übrigens: Unter Pascal (Delphi) ist die Lösung sehr einfach. Der > Funktionsdeklaration der Ereignisbehandlungsroutine wird einfach "of > object" dran gehängt. Beim C++Builder wird das aber anders sein! Naja nicht wirklich. Eine Function "of object" besteht aus 2 Dingen: Einer Objektrefrenz und dem eigentlichen Methodenzeiger. Das kann man nicht auf einen einfachen Funktionszeiger casten. Daher deine längliche Fehlermeldung, die Dir IMO aber auch eine mögliche Typdeklaration aufzeigt.
Der C++-Builder benutzt für seine Events sogenannte Closures. Das sind Funktionspointer mit implizitem this-Pointer, um auch nicht-statische Objektmethoden aufrufen zu können. Bei Microsoft heißt das im Allgemeinen Delegate. Du brauchst für die Zuweisung des Events nicht unbedingt ein Form, sondern vielmehr ein lebendes Objekt, welches z.B. folgende Methode implementiert:
1 | void __fastcall MyObject::EventChar(TObject *Sender) |
2 | {
|
3 | ...
|
4 | }
|
und dann später
1 | ...
|
2 | MyObject meins; |
3 | Serial->OnEventChar = meins.EventChar; |
Falls Du ansonsten keine Objekte hast, kannst Du ja aus Deinem Hilfsobjekt "MyObject" die entsprechenden C-Funktionen oder was auch immer aufrufen. Man kann auch eigene Closures deklarieren, wie z.B.
1 | /// Event, wenn Aktionen für markierte Messungen ausgelöst werden
|
2 | /// \param BrowserAction Gibt an, was mit der Selektion geschehen soll
|
3 | /// \param Selection Indizes der ausgewählte Messungen (set)
|
4 | typedef bool (__closure *TDBBrowserEvent)(const BrowserEventAction BrowserAction, const SelectionVec & Selection); |
Dies deklariert beispielsweise eine Funktion, die einen bool zurückliefert und einen Enum sowie einen Vektor als Parameter erwartet. In Deinem Programm kann man dann den Closure wie eine Variable verwenden:
1 | TDBBrowserEvent MyBrowserEvent; |
Gruß, Oliver
Hallo Oliver! Vielen Dank für deine Ausführungen! An ein "Hilfsobjekt" habe ich auch schon gedacht, als Notlösung, falls sich doch keine "saubere" Lösung auftut. Da es diese in Pascal (Delphi) ja gibt, sollte es diese auch unter C++ geben. Du hast das mit Deinem zweiten Ansatz ja auch angedeutet. Das spiegelt auch das wider, was meine Internet-Recherche ergeben hat. Angepasst auf meinen Anwendungsfall schreibe ich also:
1 | typedef void __fastcall (__closure *TMyEvent)(TObject *Sender); |
2 | TMyEvent MyEvent; |
3 | Serial->OnEventChar = MyEvent; |
Bis dahin schluckt der Compiler auch alles. Doch wie deklariere ich nun die eigentliche Ereignisbehandlungsroutine? (irgendwie stehe ich auf dem Schlauch)
Hi Rainer, die Definition des Handlers habe ich in meinem Post ja schon hingeschrieben:
1 | void __fastcall MyObject::MyEvent(TObject *Sender) |
2 | {
|
3 | ...
|
4 | }
|
Im Header wäre dann die Deklaration
1 | class MyObject |
2 | {
|
3 | ...
|
4 | void __fastcall MyEvent(TObject *Sender); |
5 | ...
|
6 | }
|
Du kannst also Deinem Event jede Member-Funktion eines beliebigen Objekts zuweisen, sofern sie den Rückgabewert void hat und ein TObject als Parameter erwartet. Nochmal: Ein Closure ist IMMER ein Zeiger auf eine Memberfunktion eines Objekts. Das ist keine Notlösung, sondern genau so gewollt. Dies ist der Mechanismus, mit dem die VCL als Oberflächenframework arbeitet. Gruß, Oliver
Hallo Oliver! Nun ja, das ist die Lösung mit dem Hilfsobjekt! Aber gut, das ist jetzt zwar nicht so elegant, die Umsetzung ist aber auch nicht allzu aufwändig. Zusammengefasst sieht das in etwa so aus:
1 | class TMyObject { |
2 | public:
|
3 | void __fastcall SerialEventChar(TObject *Sender); |
4 | __fastcall TMyObject(void); |
5 | __fastcall ~TMyObject(void); |
6 | };
|
7 | TMyObject *MyObject; |
8 | |
9 | __fastcall TMyObject::TMyObject(void) |
10 | {
|
11 | //
|
12 | }
|
13 | |
14 | __fastcall TMyObject::~TMyObject(void) |
15 | {
|
16 | //
|
17 | }
|
18 | |
19 | void __fastcall TMyObject::SerialEventChar(TObject *Sender) |
20 | {
|
21 | ...
|
22 | }
|
23 | |
24 | MyObject = new TMyObject(); |
25 | Serial->OnEventChar = MyObject->SerialEventChar; |
Nochmals vielen Dank für Deine Hilfe!
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.