C++ Funktionszeiger mit Rekursion
-
Hi,
Wie definiert man eine Funktion f, die als Parameter einen int und eine Funktion desselben Typs besitzt? Wie kann man den Funktionszeiger an f auf andere
Weise übergeben, sodass keine Rekursion entsteht, z.B. als ungetypten Zeiger?Danke für eure Hilfe!
-
Cream schrieb:
Wie definiert man eine Funktion f, die als Parameter einen int und eine Funktion desselben Typs besitzt?
Das ist nicht möglich.
Wie kann man den Funktionszeiger an f auf andere
Weise übergeben, sodass keine Rekursion entsteht, z.B. als ungetypten ZeigerDu könntest einen beliebigen anderen Funktionszeigertyp verwenden und explizit casten. etwa:
void f(int i,void(*pf)()) { ... reinterpret_cast<void(*)(int,void(*)())>(pf)(i,pf); ... }; ... f(0,reinterpret_cast<void(*)())>(f));
Das ist nicht schön und ziemlich umständlich. Verbessern können wir das, indem wir den Zeiger in eine Klasse verpacken, die ohne irgendwelche Umwandlungen auskommt (hier umgehen wir die Deklarationsproblematik, weil wir den Klassennamen als Funktionsargument in einer Deklaration benutzen können, bevor die Klasse definiert wurde):
struct fun_param { typedef void fun(int,void(*)(int,fun_param)); fun_param(fun* p) : p_(p) {} operator fun*() const { return p_; } fun& operator*() const { return *p_; } void operator()(int i,fun_param p) { return p_(i,p); } fun* p_; }; // unsere Funktion void f(int i,fun_param p) { ... p(i,f); p(i,p); } ... f(0,f);
Möglicherweise funktioniert das durch einen Trick auch mit boost::function, allerdings sehe ich auf die Schnelle nicht wie.
-
hmmmm könnte man das nicht direkt mit einem Funktoren hinbekommen ?
struct f { void operator()(int, f*); };
(ich bevorzuge ja Referenzen, aber hier muss es wohl ein Zeiger sein, damit man auch 0 übergeben kann ... oder ?)
Gruß,
Simon2.
-
Simon2 schrieb:
hmmmm könnte man das mit Funktoren hinbekommen ?
Damit übergibst du aber nicht direkt verschiedene Funktionen (was verschiedenen Funktortypen entspräche), sondern nur Funktoren gleicher Art, die möglicherweise unterschiedliche Zustände haben. Natürlich könnte man das über ein abstraktes Interface lösen, aber dann kommt noch Vererbung ins Spiel... hier müsste ein Entwurf her, um das zu diskutieren. Es sei denn, der Funktor selbst benutzt intern nur einen Funktionszeiger, über den dann entsprechend die Funktion aufgerufen wird... das ist allerdings im Grunde das, was ich schon gezeigt habe - nur der Weg dahin ist etwas anders.
-
camper schrieb:
...Natürlich könnte man das über ein abstraktes Interface lösen, aber dann kommt noch Vererbung ins Spiel...
Das hatte ich im Sinn...
Mit Funktionszeigern hätte ich's jetzt auch nicht gemacht, das wäre mir zu sehr "mit der Hand am Arm" gewesen.camper schrieb:
...das ist allerdings im Grunde das, was ich schon gezeigt habe ...
Das hatte ich auch nicht anders erwartet - nur schien es mir etwas schlichter als Deine Variante.
Gruß,
Simon2.
-
Es ist sehr interessant, was man mit C alles machen kann.
Dafür sieht die Lösung sehr kompliziert aus.@camper: Ich verstehe die Struktur bis auf die erste Zeile nicht ganz. Ist der Rest für syntactic sugar zuständig? Was bedeutet der Doppelpunkt in der zweiten Zeile?
Bitte erläutere den Code, denn ich würde ihn gerne verstehen und nicht nur kopieren.
-
Cream schrieb:
Es ist sehr interessant, was man mit C alles machen kann.
Dafür sieht die Lösung sehr kompliziert aus.Das ist kein C - C haben wir es allerdings zu verdanken, dass das Problem überhaupt erst entsteht und keine ganz einfache Lösung hat.
@camper: Ich verstehe die Struktur bis auf die erste Zeile nicht ganz. Ist der Rest für syntactic sugar zuständig? Was bedeutet der Doppelpunkt in der zweiten Zeile?
Bitte erläutere den Code, denn ich würde ihn gerne verstehen und nicht nur kopieren.Ziel ist es, eine Klasse zu haben, die sich wie ein Funktionszeiger verhält. Dazu untersuchen wir, welche Ausdrücke mit einem Funktionszeiger möglich sind:
Sei f eine Funktion beliebigen Typs T und pf ein Zeiger auf eine Funktion dieses Typs T.T* x = &f; T* x(&f); T* x = f; T* x(f); // Initialisierung
pf = pf; pf = f; // Zuweisung
pf; // Dereferenzierung - Ergebnis hat Funktionstyp
pf(...); // Funktionsaufruf
T* ppf = &pf;Sei mpf ein Objekt unserer Klasse my_fun_ptr, dann soll möglich sein:
my_fun_ptr x = &f; my_fun_ptr x(&f); my_fun_ptr x = f; my_fun_ptr x(f); // benötigt Konvertierungskonstruktor
mpf = pf; mpf = f; // benötigt Konvertierungskonstruktor
my_fun_ptr x = mpf; mpf = mpf; // ganz normales Kopieren durch implizit deklarierten Konstruktor bzw. Zuweisungsoperator
T* x = mpf; T* x(mpf); pf = mpf; pf = f; // benötigt Konvertierungsoperator
mpf;
pf(...);
// aber nicht: T* ppf = &mpf; - verursacht unnötige ProblemeUm dieses Verhalten nachzubilden, benötigen wird die Konvertierungsmöglichkeiten und müssen einige Operatoren überladen. Der Reihe nach:
class my_fun_ptr { public: typedef void fun(int,void(*)(int,my_fun_ptr)); // Der Typ der Funktion, auf die gezeigt werden soll my_fun_ptr(fun* p) : p_(p) {} // unser Konvertierungskonstruktor, initialiert Member p_ mit dem Argument operator fun*() const { return p_; } // Konvertierungsoperator zurück in Funktionszeiger fun& operator*() const { return *p_; } // Dereferenzierung void operator()(int i,fun_param p) const { return p_(i,p); } // Funktionsaufruf // möglich, aber sehr ungünstig: // fun** operator&() { return &p_; } // fun* const* operator&() const { return &p_; } private: fun* p_; };
Die Erläuterung weiter auszubreiten, passt thematisch nicht, Operatorüberladung und Konstruktorinitialisierungslisten sind regelmäßig Thema in diesem Forum wie auch der FAQ oder praktisch jedes beliebigen Tutorials.
Interessant wäre die Verallgemeinerung in ein Template für verschiedene Funktionstypen - wenn Interesse besteht, kann das noch diskutiert werden. Der Schwierigkeitsgrad ist dafür allerdings erheblich höher.
-
@camper: Die Erläuterung hat völlig ausgereicht.
Ich arbeite praktisch nie mit Operatorenüberladung und diesen sog. Konvertierungskonstruktoren, deshalb konnte ich da nicht gleich durchsteigen.
Aber gelesen habe ich darüber schon. Es brauchte nur einen kleinen Anstoß bis der Groschen gefallen ist.
Das lag auch daran, dass ich noch nie einen struct mit Konstruktor gesehen habe
So habe ich wieder etwas gelernt. Vielen vielen Dank!