Check für is member function pointer
-
Hi zusammen,
ich suche einen Methode mit der ich überprüfen kann ob ein Type T ein Member function ptr is oder nicht.
also sowas in der form
is_member_function_ptr<void (MyClass::)(void)>::value => true
is_member_function_ptr<void ()(int)>::value => false da functionDas ganze muss mit C++03 funktionieren, VS + gcc
Boost kann ich leider nicht verwenden.Hat jemand ne idee?
Das ich SFINAE verwenden muss ist klar, aber ich möchte ungern eine substitution für alle mögliche argument variationen niederschreiben.
Was denkt ihr?
-
template <class T> bool is_mfun (T* ) { return false; } template <class C, class T> bool is_mfun (T C::*) { return true; }
-
arogmyxdfa schrieb:
template <class T> bool is_mfun (T* ) { return false; } template <class C, class T> bool is_mfun (T C::*) { return true; }Das prüft lediglich, ob du einen Pointer auf einen Member hast, nicht einen Pointer auf eine Memberfunktion. Zudem wird es einen Fehler bei allen Nicht-Pointer-Aufrufen geben...
@Nash26:
Tja, ohne Variadic Templates wird dir wohl nichts anderes übrig bleiben, als is_member_function_ptr für eine ausreichende Anzahl an Parametern zu spezialisieren:template<typename T> struct is_member_function_ptr { static const bool value = false; }; template<typename Ret, typename Class> struct is_member_function_ptr<Ret (Class::*)()> { static const bool value = true; }; template<typename Ret, typename Class, typename T1> struct is_member_function_ptr<Ret (Class::*)(T1)> { static const bool value = true; }; template<typename Ret, typename Class, typename T1, typename T2> struct is_member_function_ptr<Ret (Class::*)(T1, T2)> { static const bool value = true; }; //...
-
wxSkip schrieb:
arogmyxdfa schrieb:
template <class T> bool is_mfun (T* ) { return false; } template <class C, class T> bool is_mfun (T C::*) { return true; }Das prüft lediglich, ob du einen Pointer auf einen Member hast, nicht einen Pointer auf eine Memberfunktion.
Nicht ganz, auch Zeiger-auf-non-const-Memberfunktionen sind erfasst (in diesem Fall wird T als Funktionstyp deduziert).
-
Kennt ihr das Buch C++ Templates the complete guide?
Dort schreibt der Autor das es auch ohne die vielen parameter variationen geht:
Hier benutzt er es um zu überprüfen ob eine Funktion gegeben ist oder nicht.
http://www.josuttis.com/tmplbook/functors/typet.hpp.html@arogmyxdfa
bei anderen typen soll es aber nicht zum compile Fehler kommen
-
Nash26 schrieb:
Boost kann ich leider nicht verwenden.
Was hindert dich? Die typetraits von boost sind header-only, keine Installation, kein Linken, kein separates Übersetzen von Biblitoheken nötig.
-
Nicht ganz das, was du suchst, aber vielleicht enthaelt es Anregungen: http://stackoverflow.com/questions/4353878/checking-whether-a-template-argument-has-a-member-function
Die Frage ist natuerlich, warum du das brauchst!
-
Nash26 schrieb:
Kennt ihr das Buch C++ Templates the complete guide?
Dort schreibt der Autor das es auch ohne die vielen parameter variationen geht:
Hier benutzt er es um zu überprüfen ob eine Funktion gegeben ist oder nicht.
http://www.josuttis.com/tmplbook/functors/typet.hpp.html@arogmyxdfa
bei anderen typen soll es aber nicht zum compile Fehler kommenDann hast du doch schon eine Lösung.
In C++03 gibt es keine schöne Lösung dafür, da es keine Möglichkeit gibt, den Typ von Memberfunktionszeiger generisch aufzuschreiben (mangels variadic templates).
Es bleiben zwei Alternativen:
1. viele Spezialisierungen für ausreichend viele Parameter, Nachteil: eine Menge Codeduplikation, folglich langsames Compilieren, nicht allgemeingültig, falls doch mal längere Parameterlisten benötigt werden
2. durch Eliminierung, wenn alle anderen Typen ausgeschlossen werden, muss ein Memberfunktionszeiger übrig bleiben, Nachteil: funktioniert ggf. nicht mehr, wenn weitere Typen dazukommen, die usprünglich nicht existierten (Compilererweiterungen, neue Standards...).Das Testen auf non-const-non-volatile-Memberfunktionszeiger ist einfach. Schwierigkeiten machen nur const,volatile und const volatile-Memberfunktionen:
template <bool b> struct bool_ { static const bool value = b; }; typedef bool_<false> false_type; typedef bool_<true> true_type; template <typename T, typename U> struct is_same : false_type {}; template <typename T> struct is_same<T, T> : true_type {}; template <typename T> struct is_function : is_same<void(T),void(T*)> {}; // Spezialisierungen für Typen, für die void(T) oder void(T*) ill-formed ist: // leidet unter dem gleichen Problem wie 2., weil auch für rvalue-Referenzen spezialisiert werden müsste; kann man durch den sizeof-SFINAE-Trick in den Griff kriegen, ist mir aber hier zuviel Schreibarbeit template <> struct is_function<const void> : false_type {}; // template <> struct is_function<volatile void> : false_type {}; // kann nicht als Funktionsparameter auftreten (da immer unvollständig), die Sonderregel gilt nur für unqualifiziertes void template <> struct is_function<const volatile void> : false_type {}; // template <typename T> struct is_function<T&> : false_type {}; // kann keine Zeiger auf Referenzen bilden template <typename T> struct is_mem_fun_ptr : false_type {}; template <typename T, typename C> struct is_mem_fun_ptr<T C::*> : is_function<T> {};
-
ist ja interesant, dein code funktioniert. Prima.
template <typename T, typename C> struct is_mem_fun_ptr<T C::*> : is_function<T> {};Aber warum wird diese spezialisierung instanziert, obwohl ich ein ein type mit 10 parametern übergebe?
Soweit ich das jetzt verstehe, brichst du das ganze schnell runter zu einem
test für is_function_ptr.Das hier verstehe ich leider nicht:
template <typename T> struct is_function : is_same<void(T),void(T*)> {};
-
Nash26 schrieb:
ist ja interesant, dein code funktioniert. Prima.
template <typename T, typename C> struct is_mem_fun_ptr<T C::*> : is_function<T> {};Aber warum wird diese spezialisierung instanziert, obwohl ich ein ein type mit 10 parametern übergebe?
Weil T selbst ein Funktionstyp sein kann. Und das passiert genau dann, wenn du das Template mit einem Zeiger auf non-const-non-volatile-Memberfunktion fütterst.
Nimmst du inT (C::*)(...)den Deklarator ::* weg, bleibt ein Funktionstyp stehen. Leider können auf diese weise const und volatile Memberfunktionen nicht verarbeitet werden, weil Funktionstypen nicht qualifiziert sein dürfen (eine Einschränkung, für die mehr der Grund nie ganz klar geworden ist; dass Funktionen selbst nie qualifiziert sind, muss ja nichts mit dem Typen zu tun haben). Das führt zu der kuriosen Eigenschaft dass in z.B.
T (C::*)(...)constnach Wegnahme des Deklarator ::* etwas steht, was kein (legaler) Typ ist. Da Deklaratoren aber eigentlich einen Typ aus einem anderen ableiten, ist das in meinem Augen seltsam.
Nash26 schrieb:
Das hier verstehe ich leider nicht:
template <typename T> struct is_function : is_same<void(T),void(T*)> {};Der Test basiert auf den Besonderheiten die für die Deklarationen von Funktionsparametern gelten. Wird ein Funktionsparameter als Funktionstyp deklariert, so transformiert der Compiler das in eine Deklaration eines Funktionszeigers als Parameter (analog für Arrays, allerdings ist das Ergebnis der Transformation dort nicht ein Zeiger auf das Array sondern einer auf den Elementtyp). Es spielt also bei Funktionenparametern keine Rolle, ob ausdrücklich ein Zeiger deklariert wird, die Deklaration verweist trotzdem auf den selben Typ. Und das gilt so nur für Funktionstypen.
Dein verlinkter Code nutzt übrigens die gleiche Eigenschaft, nur wird hier der sizeof-Überladungstrick angewendet.
-
ach du grüne neune und ich dachte ich wusste etwas über C++ und templates.
Das hier ist eine Deklarator?
::*Was ist denn ein FunktionsType? Du meinst wohl eher wenn ich in
T (C::*)(...)"C::" weglasse wird es ein function pointer?
T (*)(...)Was meinst du mit
qualifiziert
in
Funktionstypen nicht qualifiziert sein dürfen
?
Es würde mir für das Verständnis extrem helfen wenn ich die einzelnen compiler schritte sehen könnte.
Beispiel:
struct Foo { void func(int,int) {} }; template<class T> void call(T member) { bool result = is_mem_fun_ptr<T>::value; } call(&Foo::func);führt doch zu:
//Schritt 1: template <typename T, typename C> struct is_mem_fun_ptr<void Foo::*> : is_function<void> {};//Schritt 2: template <typename T> struct is_function : is_same<void(void),void(void*)> {};mhm aber das macht doch keinen sinn...
PS: Das code Beispiel aus dem Buch hat überhaupt nicht funktioniert, zumindest nicht mit VS2008
-
"Qualifiziert" bedeutet, dass hinter einer Memberfunktion ein const und/oder ein volatile steht. Das bezieht sich auf den this-Pointer, der ja ohne das C nicht mehr vorhanden ist.
Bei Schritt 1 hast du dich geirrt:
//Schritt 1: template <typename T, typename C> struct is_mem_fun_ptr<void(int, int) Foo::*> : is_function<void(int, int)> {};@SeppJ: Wenn ich dich richtig verstanden habe, dann ist ein Funktionstyp mit oder ohne Zeiger das gleiche. Warum verwendest du dann nicht is_same<T, T*>?
-
Nash26 schrieb:
ach du grüne neune und ich dachte ich wusste etwas über C++ und templates.
Das hier ist eine Deklarator?
::*Was ist denn ein FunktionsType? Du meinst wohl eher wenn ich in
T (C::*)(...)"C::" weglasse wird es ein function pointer?
T (*)(...)Ein Zeiger auf Member wird deklariert durch
nested-name-specifier * attribute-specifier-seqopt cv-qualifier-seqopt D1
also einem Bezeichner gefolgt vonm scope-Operator, gefolgt von einem Stern, gefolgt von etc., ich hätte also korrekterweise schreiben müssen:C::*wegzulassen.
Nash26 schrieb:
Was meinst du mit
qualifiziert
in
Funktionstypen nicht qualifiziert sein dürfen
?
Qualifikation bedeutet immer, dass zu einer Entität noch etwas dazu kommt, wenn nichts weiter dazu gesagt wird, bezieht sich das in der Regel darauf, das ein Typ oder ein Objekt zusätzlich als const und/oder volatile bestimmt (qualifiziert) wird. Im Zusammenhang mit Namelookup bezieht es sich auf Bezeichner, bei denen zusätzlich ein Scope angegeben wird. Die konkrete Bedeutung muss man jeweils dem Kontext entnehmen, aus Bequemlichkeit schreibe ich es nicht immer ausdrücklich hin.
Nash26 schrieb:
Es würde mir für das Verständnis extrem helfen wenn ich die einzelnen compiler schritte sehen könnte.
Beispiel:
struct Foo { void func(int,int) {} }; template<class T> void call(T member) { bool result = is_mem_fun_ptr<T>::value; } call(&Foo::func);führt zu:T = void (Foo::*)(int, int)
//Schritt 1: template <typename T, typename C> struct is_mem_fun_ptr<void (Foo::*)(int, int)> : is_function<void(int,int)> {};T = void(int,int), C = Foo
//Schritt 2: template <typename T> struct is_function : is_same<void(void(int,int)),void(void(*)(int,int))> {};Auf beiden Seiten steht jeweils eine Funktion, die void zurückliefert, und einen Zeiger auf eine Funktion nimmt, welche ihrerseits void zurückgibt und 2 int-Parameter hat. Auf der linken Seite haben wir aber nicht ausdrücklich hingeschrieben, dess es ein Zeiger ist, diese Transformation nimmt der Compiler an dieser Stelle automatisch vor.
Der obskure Fakt, dass man Funktionen so deklarieren kann, als ob sie einen Funktionsparameter hätten, ist als solches nichts, was man als normaler Programmierer beachten muss. Es taucht in C++ allerdings als Hindernis auf:
falls du schon mal versucht hast, ein Objekt zu erstellen, dass mit einem default-konstruiertem anderen Objekt initialisiert werden sollte, bis du vielleicht darüber gestolpert:struct Foo { ... }; struct Bar { ... }; ... Foo my_foo( Bar() ); // Funktionsdeklaration, keine Variablendefinitionnoch ein Grund, Initilisierung nicht gewohnheitsmäßig mit Klammern statt = durchzuführen... :p (eine Lösungsmöglichkeit besteht in einem zusätzlichen Klammerpaar um Bar() )
-
@camper: Ich habe jetzt deine Variante genommen und etwas veraendert. const/volatile Memberfunktionen funktionieren bei mir auch. Habe ich was uebersehen?
template <bool B> struct bool_ { static bool const value = B; }; typedef bool_<true> true_; typedef bool_<false> false_; template <typename T, typename U> struct is_same : false_ {}; template <typename T> struct is_same<T, T> : true_ {}; template <typename T> struct is_function : is_same<void(T), void(T*)> {}; template <typename T> struct is_member_function_pointer : false_ {}; template <typename T, typename C> struct is_member_function_pointer<T C::*> : is_function<T> {}; /////////////////// #include <iostream> struct foo {}; int main() { std::cout << is_member_function_pointer<void (foo::*) (int, float)>::value << '\n'; std::cout << is_member_function_pointer<void (foo::*) (int, float) const>::value << '\n'; std::cout << is_member_function_pointer<void (foo::*) (int, float) volatile>::value << '\n'; std::cout << is_member_function_pointer<void (foo::*) (int, float) const volatile>::value << '\n'; }Edit: Mir faellt gerade auf, ich habe genau das gleiche wie du, nur ohne die Checks. Hab mir deinen Code nicht so genau angesehen

Trotzdem funktionierts auch mit const/volatile Memberfunktionen.
-
Kellerautomat schrieb:
Habe ich was uebersehen?
Nein, scheint als bin ich hier einem Irrtum aufgessen.
Es ist ist nicht möglich einen bereits bestehenden Funktionstyp mit cv-Qualifikation zu versehen, als Teil des Funktionstyp existiert die Qualifikation aber durchaus:typedef void f(); typedef void g() const; // ältere Compiler werden das u.U. verweigern typedef const f h; // const wird ignoriert struct foo { f fun; // deklariert void foo::fun() g fun; // deklariert void foo::fun() const h fun; // Fehler: Redeklaration von void foo::fun() };Umso besser: dann gibt es doch eine schöne Lösung auch in C++03.
wenn ich
template <bool b> struct bool_ { static const bool value = b; }; typedef bool_<false> false_type; typedef bool_<true> true_type; template <typename T, typename U> struct is_same : false_type {}; template <typename T> struct is_same<T, T> : true_type {}; template <typename T> struct is_function : is_same<void(T),void(T*)> {}; template <> struct is_function<const void> : false_type {}; template <> struct is_function<volatile void> : false_type {}; template <> struct is_function<const volatile void> : false_type {}; template <typename T> struct is_function<T&> : false_type {}; template <typename T> struct is_mem_fun_ptr : false_type {}; template <typename T, typename C> struct is_mem_fun_ptr<T C::*> : is_function<T> {}; struct Foo { void f(); void g() const; }; template <typename T> void foo(T) { int dummy[is_mem_fun_ptr<T>::value]; } int main() { foo(&Foo::f); foo(&Foo::g); }durch Online-Comeau C++ jage, gibt es Fehler. g++4.6 und clang sind damit zufrieden. Evtl. sollte man noch ältere Versionen von g++ und Visual C++ testen. Mir ist allerdings auch nicht bewusst, dass es hier eine Änderung in C++11 gegeben hätte. Ich bin allerdings auch ziemlich sicher, dass das früher nicht so funktioniert hat.
-
Also in VS2008 wird mit eurem code eine const member funktion nicht erkannt.
Kann man noch irgendwo eine Spezialisierung einbauen, so das es funktioniert?Das der compiler das so hier auflöst hätte ich nicht gedacht.
template <typename T, typename C> struct is_mem_fun_ptr<void (Foo::*)(int, int)> : is_function<void(int,int)> {};Mit ist leider immer noch einiges nicht klar:
template <typename T, typename C> struct is_mem_fun_ptr<T C::*> : is_function<T> {};Für welchen Typ T wird dieses template instanziert, obwohl es keine member function ist?
Durch T C::* kann es doch eigentlich nur für member functions aufgerufen werden.So das dann der nachfolgende Test mit is_function überhaupt nocht sinn macht.
jetzt zu dem Konstrukt:
template <typename T> struct is_function : is_same<void(void(int,int)),void(void(*)(int,int))> {};Auf der linken Seite deklarierst du einen Funktionszeiger, dieser wird durch den compiler nochmal
zu einem Funktionszeiger transformiert? Auf der rechten Seite machen wir das manuell. Ist das so korrekt?PS: wo lernt man solche Tricks mit
is_same<void(T),void(T*)> {};
?
Also das wäre mit nicht im traum eingefallen.
-
Nash26 schrieb:
template <typename T, typename C> struct is_mem_fun_ptr<T C::*> : is_function<T> {};Für welchen Typ T wird dieses template instanziert, obwohl es keine member function ist?
Wenn der Parameter nicht auf diese Spezialisierung passt, wird das Default-Template genommen:
template <typename T> struct is_mem_fun_ptr : false_type {};
-
@wxSkip
das heißt also ich kann direkttemplate <typename T, typename C> struct is_mem_fun_ptr<T C::*> : true_type {};schreiben?
-
Nash26 schrieb:
@wxSkip
das heißt also ich kann direkttemplate <typename T, typename C> struct is_mem_fun_ptr<T C::*> : true_type {};schreiben?
Nein. Es gibt auch Nicht-Memberfunktions-Typen, die auf diese Spezialisierung passen. Und zwar sind das die relativ unbekannten Zeiger auf Member. Wenn du eine Klasse mit verschiedenen Variablen hast, kannst du einen Zeiger auf eine Klassenvariable holen, der praktisch die interne Position in der Klasse speichert. Wenn du dann noch ein konkretes Objekt hast, kannst du mit diesem Zeiger auf die Variable dieses Objektes zugreifen.
-
Nash26 schrieb:
jetzt zu dem Konstrukt:
template <typename T> struct is_function : is_same<void(void(int,int)),void(void(*)(int,int))> {};Auf der linken Seite deklarierst du einen Funktionszeiger, dieser wird durch den compiler nochmal
zu einem Funktionszeiger transformiert? Auf der rechten Seite machen wir das manuell. Ist das so korrekt?Links wird ein Funktionstyp deklariert, rechts wird ein Funktionstyp deklariert. Beiden Typen haben ihrerseits einen Funktionszeiger als Parametertyp.
Nash26 schrieb:
PS: wo lernt man solche Tricks mit
is_same<void(T),void(T*)> {};
?
Also das wäre mit nicht im traum eingefallen.Den Trick als solchen habe ich mir on-the-fly beim Schreiben ausgedacht. Den zugrunde liegenden Mechanismus lernt man irgendwann nebenbei kennen, wenn es um etwas ganz anderes geht.
-
@camper
tja bleibt nur noch die Frage warum es mit VS2008 nicht mit const methoden klappt.
Hast du einen workaround dafür?