Hallo, kann mir jemand eine verständliche Quelle für Callbackfunktionen nennen und gleichzeitig sagen, ob es dasselbe wie rekursive Funktionen ist?
:
Gesperrt durch Moderator
Michael schrieb: > kann mir jemand eine verständliche Quelle für Callbackfunktionen nennen Dein C++ Buch Michael schrieb: > ob es dasselbe wie rekursive Funktionen ist? Nein
Ich habe eine poll-Funktion, die auf Events wartet. Die ausgelösten Events (z.B. Zeit, Signal, etc..) Prüfe ich über if/else, also ob Signal oder Zeit ausgelöst wurden. Bei hunderten von Events ist das keine sinnvolle Lösung. Daher dachte ich an Callbackfunktionen, denen ich meine Event-Struktur bzw. die Adresse davon übergebe und dann über Callbacks die Abfrage umsetze.
Naja dann übergib halt in dem Struct einen Funktionszeiger und führ die Funktion aus. Allerdings ist das die Art, wie man in C objektorientiert programmiert. Man kann das auch in C++ machen, aber wenn man C++ nutzt, um damit ein C-artiges Objektsystem zu bauen, stimmt etwas grundsätzlich mit dem Design nicht.
In C++ wäre die Lösung eine abstracte Klasse zu definieren, welche die "Callback"-Funktion als virtuelle Methode entält. z.B. so:
1 | class SomeEventListener |
2 | {
|
3 | public:
|
4 | virtual void OnSomeEvent(const SomeEventArgs& args) = 0; |
5 | };
|
6 | |
7 | class SomeEventSource |
8 | {
|
9 | public:
|
10 | void AddListener(SomeEventListener& listener); |
11 | void RemoveListener(SomeEventListener& listener); |
12 | |
13 | /* ... */
|
14 | }
|
15 | |
16 | class MyClassThatListensToSomeEvent : public SomeEventListener |
17 | {
|
18 | public:
|
19 | virtual void OnSomeEvent(const SomeEventArgs& args) |
20 | {
|
21 | /* Do stuff */
|
22 | }
|
23 | }
|
Sebastian E. schrieb: > In C++ wäre die Lösung eine abstracte Klasse zu definieren, welche die > "Callback"-Funktion als virtuelle Methode entält. Oder std::function. Aber das hängt von den Anforderungen ab.
Hallo, ähm, könnte vielleicht noch jemand erläutern, warum die Antwort von Sebastian E. downgevoted wird? Das ist doch das klassische GoF Observer Pattern, das brutalst weit verbreitet ist? Zur Frage des TO passt es auch, zumindest liegen keine objektiven Befunde vor, die vermuten lassen, es passe nicht? Vlg Timm
:
Bearbeitet durch User
Timm R. schrieb: > Hallo, > > ähm, könnte vielleicht noch jemand erläutern, warum die Antwort von > Sebastian E. downgevoted wird? > > Das ist doch das klassische GoF Observer Pattern, das brutalst weit > verbreitet ist? Zur Frage des TO passt es auch, zumindest liegen keine > objektiven Befunde vor, die vermuten lassen, es passe nicht? > > Vlg > Timm Hab ich mich gefragt, ich hatte eine fast identische Antwort schon absendefertig, aber Sebastian war schneller...
Timm R. schrieb: > ähm, könnte vielleicht noch jemand erläutern, warum die Antwort von > Sebastian E. downgevoted wird? Ich war es nicht. Ich könnte mir vorstellen, dass andere die Lösung zu kompliziert finden, wenn nach Callback-_Funktion_ gefragt wurde. Oder vielleicht war es der Camel Case Java-Style des Beispiels ;-) Ich mag das Observer-Pattern auch sehr gerne, auch wenn Vererbung eine ziemlich starke Kopplung ist. Als "Verbesserungsvorschlag" zur Lösung von Sebastian: Man kann das Interface auf privat erben, wenn man z.B. den c'tor der Implementierung das Registrieren überläßt. In modernem C++ würde ich vielleicht die deutlich losere Kopplung über std::function wählen und insbesondere im Embedded Bereich versuchen, dass ganze zur Compile-Zeit aufzulösen (sprich: Curiously recurring template pattern). Das hängt aber alles ein wenig von den nicht bekannten Anforderunen ab.
Hallo Torsten, ich bin im Moment noch sehr rückständig, was aktuelles C++ angeht, mein letztes ernsthaftes C++ war in den 90ern :-) Bin aber dabei mich zu modernisieren und hätte daher ein paar Nachfragen: Torsten R. schrieb: > In modernem C++ würde ich vielleicht die deutlich losere Kopplung über > std::function wählen und insbesondere im Embedded Bereich versuchen, > dass ganze zur Compile-Zeit aufzulösen (sprich: Curiously recurring > template pattern). Das hängt aber alles ein wenig von den nicht > bekannten Anforderunen ab. wenn man den Callback als template Parameter realisieren würde, hätte man ja als Vorteil, dass man sowohl Funktionszeiger, als auch einen Funktor / Funktionsobjekt oder ein lambda als Callback verwenden kann ? Was ja nicht alles bei std::function gehen würde. Außerdem könnte der Callback ge-inlined werden, was bei std::function auch nicht gehen würde ? Aber, was mir nicht ganz klar ist, wo das crtp ins Spiel kommt? Die Implementierung würde doch so ähnlich aussehen?
1 | template <typename cf> |
2 | void blabla(..., cf&& callback) { |
3 | ...
|
4 | callback(...); |
5 | ...
|
6 | }
|
Wo kommt denn crtp ins Spiel? vlg Timm
Timm R. schrieb: > wenn man den Callback als template Parameter realisieren würde, hätte > man ja als Vorteil, dass man sowohl Funktionszeiger, als auch einen > Funktor / Funktionsobjekt oder ein lambda als Callback verwenden kann ? std::function macht doch genau das:
1 | #include <iostream> |
2 | #include <functional> |
3 | |
4 | void sub(std::function<int(int)> f) { |
5 | std::cout << f(10) << '\n'; |
6 | }
|
7 | |
8 | int f1(int x) { |
9 | return x + 1; |
10 | }
|
11 | |
12 | struct C { |
13 | int operator()(int x) { return 2 * x; } |
14 | };
|
15 | |
16 | int main() { |
17 | sub(f1); // Funktionszeiger |
18 | sub(C()); // callable Object |
19 | sub([] (int x) { return x * x; }); // Lambda |
20 | }
|
Hallo Yalu, vielen Dank für das detaillierte Beispiel! Sehr nett von Dir. Bleibt als einziger Vorteil template vs. std::function, dass template inlining wahrscheinlicher macht, richtig? vlg Timm
Timm R. schrieb: > Wo kommt denn crtp ins Spiel? Wenn Du Callbacks verwenden möchtest, um die Kopplung zwischen Modulen zu reduzieren, dann kannst Du anstatt von Laufzeit-Polymorphie (Dein Vorschlag) auch Compilezeit-Polymorphie verwenden:
1 | class MyClassThatListensToSomeEvent |
2 | {
|
3 | public:
|
4 | void OnSomeEvent(const SomeEventArgs& args); |
5 | };
|
6 | |
7 | template < class Listener > |
8 | class SomeEventSource : public Listener |
9 | {
|
10 | public:
|
11 | void EventFired() |
12 | {
|
13 | static_cast< *MyClassThatListensToSomeEvent >( this )->OnSomeEvent( SomeEventArgs{ ... } ); |
14 | }
|
15 | }
|
Es kommt halt darauf an, warum der OP nach Callback-Functions fragt. Ggf. wäre sogar Zeiger auf Member Functions die richtige Lösung für sein Problem (aber das hat er uns ja nicht verraten).
:
Bearbeitet durch User
Hallo Torsten, uff! Das ist aber mal eine Ansage. Habs aber verstanden, danke für die Erklärung. So ein Klimmzug wäre aber dann doch eher was für embedded, wenn man Takte zählt, oder? vlg Timm
Timm R. schrieb: > So ein Klimmzug wäre aber dann doch eher was für embedded, wenn man > Takte zählt, oder? Naja, der "Klimmzug" von Sebastian ist sogar noch einen Tick länger. (wir sind hier in einem Forum für Embedded-Entwicklung ;-)
Callbacks folgen ja der Syntax:
1 | int foobar (int x, int (*moo)(int)) |
2 | {
|
3 | return x + moo(x); // function pointer moo called using argument x |
4 | }
|
5 | // analog
|
6 | int foobar (int x, f_int_t moo) |
7 | {
|
8 | return x + moo(x); // function pointer moo called using argument x |
9 | }
|
Ich kenne lediglich Funktionszeiger, aber eben mit folgender Syntax:
1 | int addiere (int x, int y) {return x+y;} |
2 | int (*pFunktion) (int, int); |
3 | |
4 | int main(){ |
5 | pFunktion = addiere; |
6 | pFunktion(100, 200); |
7 | return 0; |
8 | }
|
Sind beide Codes ein und dasselbe??
Hallo Torsten, darf ich noch eine letzte Nachfrage stellen? Torsten R. schrieb: > Wenn Du Callbacks verwenden möchtest, um die Kopplung zwischen Modulen > zu reduzieren, dann kannst Du anstatt von Laufzeit-Polymorphie (Dein > Vorschlag) auch Compilezeit-Polymorphie verwenden: in „meinem“ Beispiel wird die Callback-Funktion geinlined, also in blabla(..., cf&& callback) eingesetzt, warum ist das dann Laufzeit-Polymorphie? Versteh mich bitte nicht falsch, ich will nicht diskutieren, dazu habe ich auch gar nicht die Kenntnisse, es geht mir um Verständnis. Vlg Timm
Timm R. schrieb: > in „meinem“ Beispiel wird die Callback-Funktion geinlined, also in > blabla(..., cf&& callback) eingesetzt, warum ist das dann > Laufzeit-Polymorphie? Sorry, ich habe Dich mit Sebastian verwechselt.
Hallo Michael, mal eine Antwort von Anfänger zu Anfänger: Das ist alles schon etwas kryptisch! Einerseits scheint dir ja klar zu sein, was Du erreichen willst: > Ich habe eine poll-Funktion, die auf Events wartet. Die ausgelösten > Events (z.B. Zeit, Signal, etc..) Prüfe ich über if/else, also ob Signal > oder Zeit ausgelöst wurden. Bei hunderten von Events ist das keine > sinnvolle Lösung. anderseits schreibst Du > Daher dachte ich an Callbackfunktionen, denen ich > meine Event-Struktur bzw. die Adresse davon übergebe und dann über > Callbacks die Abfrage umsetze. was sich extrem seltsam liest, oder Du fragst Dich, ob Callback das selbe ist, wie Rekursion. Michael schrieb: > Callbacks folgen ja der Syntax: >
1 | > int foobar (int x, int (*moo)(int)) |
2 | > { |
3 | > return x + moo(x); // function pointer moo called using argument x |
4 | > } |
5 | >
|
nein. Also ja, schon, aber nicht nur, der Callback ist aber „moo”. > [x]// analog > int foobar (int x, f_int_t moo)[/c] was möchtest Du damit sagen? f_int_t gibt es nicht. Klar kann man das aliasen, aber was hat das mit Deinem Problem oder den Eigenschaften von Callbacks zu tun? > Ich kenne lediglich Funktionszeiger, aber eben mit folgender Syntax: >
1 | > int addiere (int x, int y) {return x+y;} |
2 | > int (*pFunktion) (int, int); |
3 | >
|
4 | > int main(){ |
5 | > pFunktion = addiere; |
6 | > pFunktion(100, 200); |
7 | > return 0; |
8 | > } |
9 | >
|
> > Sind beide Codes ein und dasselbe?? Die Frage kann man vermutlich nicht entschlüsseln? Das beide Codes nicht identisch sind, sieht man ja. Aber beide verwenden Funktionszeiger. Dein erstes Beispiel auch. Du hast ja jetzt insgesamt vier (oder so) Varianten vorgeschlagen bekommen, wie Du einen Callback realisieren kannst. Du solltest vielleicht versuchen etwas exakter einzukreisen, wo das Problem liegt. Bild: Ein Callback ist ein Rückruf, dazu benötigst Du ein Telefon (Funktionspointer o.ä.) aber deswegen ist nicht jedes Telefonat ein Rückruf. Callback-Funktionen sind Funktionen, die anderen Funktionen als Argument übergeben werden und von diesen aufgerufen werden. Dazu gibt es in C++ (soweit ich das überblicke) mehrere Möglichkeiten: Funktionspointer, den polymorphen Allzweck-Funktionswrapper std::function, verwendbar mit Funktionspointern, Funktionsobjekten und Lambdas, das observer Pattern mit Ableitung von einer abstrakten Basisklasse oder eben Template-Argumente. Wobei sich für mein unmaßgebliches Bauchgefühl die Funktionspointer-Variante am wenigsten nach C++ anfühlt, wenn ich mich nicht irre, würde man für einen member function pointer sowohl den function pointer, als auch den Pointer auf das Object benötigen. Wollte man also function pointers mit Klassen einsetzen, müsste vermutlich die member function static sein oder man müsste halt mit zwei Pointern arbeiten. Das wäre schon sehr gewollt. Am performantesten wird vermutlich das Template-Argument sein, weil das geinlined werden kann. vlg Timm
:
Bearbeitet durch User
Timm R. schrieb: > Das wäre schon sehr gewollt. Warum das? Timm R. schrieb: > Am performantesten wird vermutlich das Template-Argument sein, weil das > geinlined werden kann. Und das ginge sonst nicht?
Beitrag #5137747 wurde von einem Moderator gelöscht.
Beitrag #5137780 wurde von einem Moderator gelöscht.
Beitrag #5137838 wurde von einem Moderator gelöscht.
Beitrag #5137945 wurde von einem Moderator gelöscht.
Beitrag #5138074 wurde von einem Moderator gelöscht.
Beitrag #5138358 wurde von einem Moderator gelöscht.
Beitrag #5138524 wurde von einem Moderator gelöscht.
Beitrag #5139165 wurde von einem Moderator gelöscht.
Michael schrieb: > Sind beide Codes ein und dasselbe?? Im Prinzip ja, nur daß Du das in der main den Funktionszeiger per expliziter Zuweisung setzt und bei der Funktion die Zuweisung bei der Parameter-Übergabe erfolgt.
Beitrag #5139830 wurde von einem Moderator gelöscht.
Beitrag #5139979 wurde von einem Moderator gelöscht.
Beitrag #5140042 wurde von einem Moderator gelöscht.
Beitrag #5140093 wurde von einem Moderator gelöscht.
Beitrag #5140235 wurde von einem Moderator gelöscht.
Beitrag #5140377 wurde von einem Moderator gelöscht.
Beitrag #5140399 wurde von einem Moderator gelöscht.
Beitrag #5140402 wurde von einem Moderator gelöscht.
Beitrag #5140408 wurde von einem Moderator gelöscht.
Beitrag #5140409 wurde von einem Moderator gelöscht.
Beitrag #5140436 wurde von einem Moderator gelöscht.
Beitrag #5142594 wurde von einem Moderator gelöscht.
Beitrag #5143769 wurde von einem Moderator gelöscht.
Beitrag #5144963 wurde von einem Moderator gelöscht.
Beitrag #5144992 wurde von einem Moderator gelöscht.
Beitrag #5145004 wurde von einem Moderator gelöscht.
Beitrag #5145228 wurde von einem Moderator gelöscht.
Beitrag #5145802 wurde von einem Moderator gelöscht.
Beitrag #5145820 wurde von einem Moderator gelöscht.
Beitrag #5145936 wurde von einem Moderator gelöscht.
Beitrag #5145962 wurde von einem Moderator gelöscht.
Beitrag #5145973 wurde von einem Moderator gelöscht.
Beitrag #5148066 wurde von einem Moderator gelöscht.
Beitrag #5149295 wurde von einem Moderator gelöscht.
Beitrag #5149360 wurde von einem Moderator gelöscht.
Beitrag #5149381 wurde von einem Moderator gelöscht.
Beitrag #5149416 wurde von einem Moderator gelöscht.
Beitrag #5153206 wurde von einem Moderator gelöscht.
Beitrag #5153955 wurde von einem Moderator gelöscht.
Beitrag #5158698 wurde von einem Moderator gelöscht.
Beitrag #5159358 wurde von einem Moderator gelöscht.
Beitrag #5166158 wurde von einem Moderator gelöscht.
Beitrag #5166167 wurde von einem Moderator gelöscht.
Beitrag #5167775 wurde von einem Moderator gelöscht.
Beitrag #7197960 wurde von einem Moderator gelöscht.
Dieser Beitrag ist gesperrt und kann nicht beantwortet werden.