Forum: PC-Programmierung Callback-Funktionen in C++


Announcement: there is an English version of this forum on EmbDev.net. Posts you create there will be displayed on Mikrocontroller.net and EmbDev.net.
von Michael (Gast)


Bewertung
-2 lesenswert
nicht lesenswert
Hallo,
kann mir jemand eine verständliche Quelle für  Callbackfunktionen nennen 
und gleichzeitig sagen, ob es dasselbe wie rekursive Funktionen ist?

von Der Andere (Gast)


Bewertung
0 lesenswert
nicht lesenswert
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

von Michael (Gast)


Bewertung
0 lesenswert
nicht lesenswert
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.

von Nop (Gast)


Bewertung
1 lesenswert
nicht lesenswert
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.

von Sebastian E. (sbe_15)


Bewertung
0 lesenswert
nicht lesenswert
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
}

von Torsten R. (Firma: Torrox.de) (torstenrobitzki)


Bewertung
0 lesenswert
nicht lesenswert
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.

von Timm R. (Firma: privatfrickler.de) (treinisch)


Bewertung
-1 lesenswert
nicht lesenswert
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
von Ruediger A. (Firma: keine) (rac)


Bewertung
0 lesenswert
nicht lesenswert
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...

von Torsten R. (Firma: Torrox.de) (torstenrobitzki)


Bewertung
0 lesenswert
nicht lesenswert
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.

von Timm R. (Firma: privatfrickler.de) (treinisch)


Bewertung
0 lesenswert
nicht lesenswert
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

von Yalu X. (yalu) (Moderator)


Bewertung
3 lesenswert
nicht lesenswert
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
}

von Timm R. (Firma: privatfrickler.de) (treinisch)


Bewertung
0 lesenswert
nicht lesenswert
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

von Torsten R. (Firma: Torrox.de) (torstenrobitzki)


Bewertung
0 lesenswert
nicht lesenswert
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
von Timm R. (Firma: privatfrickler.de) (treinisch)


Bewertung
0 lesenswert
nicht lesenswert
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

von Torsten R. (Firma: Torrox.de) (torstenrobitzki)


Bewertung
0 lesenswert
nicht lesenswert
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 ;-)

von Michael (Gast)


Bewertung
0 lesenswert
nicht lesenswert
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??

von Timm R. (Firma: privatfrickler.de) (treinisch)


Bewertung
0 lesenswert
nicht lesenswert
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

von Torsten R. (Firma: Torrox.de) (torstenrobitzki)


Bewertung
0 lesenswert
nicht lesenswert
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.

von Timm R. (Firma: privatfrickler.de) (treinisch)


Bewertung
0 lesenswert
nicht lesenswert
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
von zer0 (Gast)


Bewertung
0 lesenswert
nicht lesenswert
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.
von Nop (Gast)


Bewertung
0 lesenswert
nicht lesenswert
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.

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.