Templateparameter -> Erkennung von Funktion/Funktor/Variable
-
Ne,
is_functionprüft nur, ob man eine Funktion vor sich hat (nichtmal für Funktionszeiger gilt das). Du bräuchtest einis_callable. Damit undboost::enable_ifwäre das leicht zu machen. Mal sehen…
-
Also die Unterscheidung zu Funktionspointer ist ja banal. (Wobei es dann wieder Probleme mit normalen Zeiger gibt.. )
Hmm. Für die Unterscheidung zu Funktoren (ohne Boost) fällt mir nur ein kleines Workaround ein.
Es gibt hald eine Bedingung, wenn man das Template benutzen will.
class func { public: void operator ()(int i){} void operator = (int i) { this->operator () (i);} };
-
Ah Concept Checking sollte helfen:
template <typename T> struct Callable { BOOST_CONCEPT_USAGE(Callable) { int i; f(i); } private: T f; }; template<typename ParamT> BOOST_CONCEPT_REQUIRES((Callable<ParamT>), void) foo(ParamT param) { param(5); } template <typename ParamT> void foo (ParamT param) { param = 5; }
-
Ein allgemeines is_callable dürfte schwierig sein. Referenzen und Zeiger auf Funktionen sind kein Problem.
Bei Funktoren weiß man, dass sie eine entsprechende Memberfunktion haben - dummerweise könnte diese überladen werden oder es könnte sich um ein Membertemplate handeln und dann ist es nicht mehr so einfach. Im konkreten Fall gehe ich aber davon aus, dass das ARgument, mit dem die Funktion aufzurufen wäre, bereits feststeht. Dann ist es relativ einfachtypedef char no; typedef no yes[2]; template <typename T> T& make_T(); template <typename T> yes& func(T, char(*)[sizeof( make_T<T>()(5) )]=0); no& func(...); template <typename T> struct is_callable_by_5 { static const bool value = sizeof( func( make_T<T>() ) ) == sizeof( yes ); };Bzgl. concepts: ich hatte den Eindruck, dass mit dem Framework nur eine Überpfrüfung bestimmter Eigenschaften möglich ist (d.h. eine fehlgeschlagene Überprüfung führt zum Abbruch der Compilierung und nicht zur Auswahl eines Alternativen Code-Pfades), aber gerade nicht die Möglichkeit, das Vorliegen von einer Eigenschaft herauszufinden und entsprechend zu reagieren. Habe ich etwas übersehen?
Selbst wenn ich mich irre, funktioniert es jedenfalls nicht so, wie gezeigt. Denn für den callable-Fall hätte man dann zwei Templates, die instantiiert werden können, und keines der Templates ist spezieller als das andere.
-
@.filmor,
1. Es gibt auch nochboost::remove_pointer. In Kombinatin mitboost::is_functionist die Unterscheidung von Variablen und Funktionen möglich:template<typename ParamT> void foo(ParamT& param) { if(boost::is_function<boost::remove_pointer<ParamT> >::value) { std::cout << "function" << std::endl; } else { std::cout << "no function" << std::endl; } }2. Bei den Concept Checks ist es genau wie camper gesagt hat: Es dient nur zu einer Überprüfung. Dadurch meckert der Compiler bei der Deklaration des zweiten Foo-Funktiontemplates, da dieses Funktionstemplate bereits existiert.
@drakon,
Es würde gerade darum gehen, dass man den User nicht einschränken soll. Zum Beispiel dass er ohne Probleme Boost.Bind verwenden könnte, oder ähnliches, um zum Beispiel auch Memberfunktionen verwenden zu können oder anderes.@camper,
Und wenn das Argument nicht feststeht?
Aber ich denke, am einfachsten wäre es, einfach zwei Funktionen anzubieten. Eine Funktion für Callable und eine für Nicht-Callable.
Grüssli
-
Dravere schrieb:
Es gibt auch noch
boost::remove_pointer. In Kombinatin mitboost::is_functionist die Unterscheidung von Variablen und Funktionen möglichMir war schon klar, dass das nicht das Problem sein sollte, aber damit hast du keine Funktionsobjekte drin.
Dravere schrieb:
Bei den Concept Checks ist es genau wie camper gesagt hat: Es dient nur zu einer Überprüfung. Dadurch meckert der Compiler bei der Deklaration des zweiten Foo-Funktiontemplates, da dieses Funktionstemplate bereits existiert.
Nö, eigentlich ist das genau die richtige Baustelle für Konzepte (Standardbeispiel ist ja std::sort für RandomAccessIterator und andere). Man müsste halt den zweiten Pfad etwas verlängern. Leite mal nicht aus meiner Unfähigkeit ab, dass Konzepte dafür nicht geeignet sind

Dravere schrieb:
Und wenn das Argument nicht feststeht?

Es reicht, wenn die Typen der Argumentliste bekannt sind:
typedef char no; typedef no yes[2]; template <typename T> T& make(); template <typename T, typename T1, typename T2, etc.> struct is_callable_with { struct tester { static yes& test( T const volatile&, char(*)[sizeof(make<T>()(make<T1>(), make<T2>(), etc.))] = 0 ); static no& test(...); }; static const bool value = sizeof(tester::test(make<T>())) == sizeof( yes ); };/edit: Hmm, ne, so klappt das aber nur wenn das fragliche Objekt ne Ganzzahlkonstante zurückliefert, oder?
Dravere schrieb:
Aber ich denke, am einfachsten wäre es, einfach zwei Funktionen anzubieten. Eine Funktion für Callable und eine für Nicht-Callable.
Ist zumindest sauberer.
-
Dravere schrieb:
Und wenn das Argument nicht feststeht?

Das ist schon begrifflich ein Problem. Was heißt das eigentlich, dass eine Entität callable ist, ohne dies gleichzeitig auf ein Argumenttupel zu beziehen?
class strange { private: struct stranger {}; public: void operator()(stranger) {} };callable oder nicht? Die Klasse hat zwar einen operator (), nur ist es tatsächlich unmöglich, diese Funktion aufzurufen (wir können allerdings noch einen Zeiger auf Member dazu erhalten).
struct strangest { template<typename T> typename enable_if_c<sizeof(T)==0>::type operator()(T); };callable?
-
Ich habe mir jetzt nochmals Gedanken dazu gemacht und komme endgültig zum Schluss, dass es keine Vernünftige Unterscheidung, zwischen Funktor und Variable gibt. Bzw. unter speziellen Umständen gäbe es womöglich ein paar, allerdings scheint mir es besser zu sein, wenn man es direkt in den Namen der Funktion nimmt. Muss es der Anwender wissen, was er möchte.
Mir kam die Idee ursprünglich davon, dass ich meiner XML Bibliothek eine Möglichkeit schenken wollte, dass man XPaths direkt auf Variablen mappen können soll. Jetzt werde ich es wahrscheinlich so oder so ähnlich lösen:
// Normale variable, über Zuweisung. template<typename VarT> void map(std::string const& xpath, VarT& var); // Freie Funktion. template<typename VarT, typename RetT> void map(std::string const& xpath, RetT (*func)(VarT)); // Member-Funktion, im Visier sind die Setter. template<typename VarT, typename RetT, typename ClassT> void map(std::string const& xpath, RetT (ClassT::*func)(VarT), ClassT& obj); // Möglichkeit für Funktoren. Muss man dann allerdings den Typ von VarT selber angeben. template<typename VarT, typename CallableT> void map_callable(std::string const& xpath, CallableT callable);Danke für die Antworten!
Grüssli
-
camper schrieb:
class strange { private: struct stranger {}; public: void operator()(stranger) {} };callable oder nicht? Die Klasse hat zwar einen operator (), nur ist es tatsächlich unmöglich, diese Funktion aufzurufen (wir können allerdings noch einen Zeiger auf Member dazu erhalten).
Den Zeiger auf Member können wir aber nur erhalten wenn strange sowas per typedef publik macht oder? Ich hab jetz keinen Compiler zur Hand, aber müsste es bei sowas nicht Probleme geben:
class strange {/*wie oben*/}; typedef void (strange::*strange_memfun_ptr)(strange::stranger); //Problem, da strange::stranger private?
-
class strange { private: struct stranger {}; public: void operator()(stranger) {} }; template<typename C, typename T> T get_stranger(void(C::*)(T)) { return T(); } ... strange foo; foo(get_stranger(&strange::operator()));Sobald ein Typ Teil eines öffentlichen Interfaces ist, kommt man prinzipiell heran, auch wenn der Typ selbst private ist. Das ist dann aber wieder Machiavellis Domäne.
Man kann die Frage natürlich auch anders stellen, z.B. indem man den Operator private macht und ggf. eine Handvoll friends hat. Interessiert uns, ob jemand bestimmtes das Objekt mit ganz konkreten Argumenten aufrufen kann, oder ob das irgendjemand irgendwie machen kann?