functor_traits und lambda_traits
-
"muss jedoch nur max. 20 Argumente unterstützen, den Code kann ich mir dann generieren lassen". Geht also auch um Funktoren und Lambdas mit Parametern (parameter_count würde ansonsten ja auch gar keinen Sinn ergeben).
Was es können soll steht im Code schon etwa drin. Einsetzen kann man es z.B. in etwa so:
template<typename T> struct result_of { typedef typename conditional<function_traits<T>::is_function, function_traits<T>::return_type, typename conditional<method_traits<T>::is_method, method_traits<T>::return_type, typename conditional<functor_traits<T>::is_functor, functor_traits<T>::return_type, /*Felertyp*/>::type>::type>::type type; }; template<typename T> struct varcall { T fnc; explicit varcall(T fnc) : fnc(fnc) {} operator typename result_of<T>::type () { return fnc(); } }; template<typename T> varcall<T> fnc_to_var(T fnc) { return varcall<T>(fnc); } int main() { std::vector<int> vec(1000); std::fill(vec.begin(), vec.end(), fnc_to_var(&std::rand)); //Ich kenne std::generate, das ist nur ein Anwendungsbeispiel }
-
Zudem weiß ich nicht, ob declval mit einem Lambdatypen erlaubt ist.
Hier liegt dein Denkproblem - ein Lambda ist nichts weiter als ein Klassenobjekt.
Du kannst nicht prüfen ob ein Funktionsobjekt ein Lambda ist - genauso wenig wie du prüfen kannst ob ein Funktionsobjekt-Typ ein closure-Typ ist.Dein Code ist auch teils falsch - was wenn
decltype(std::declval<U>()())eine Referenztyp ist?Um zu prüfen ob ein Typ eine Klasse mit überladenem
operator()ist, nutz sowas:template<typename U> static std::true_type check(decltype(&U::operator())); template<typename> static std::false_type check(...); public: static const bool is_functor = decltype(check<T>(0))::value;Es gibt aber selbstverständlich auch Probleme mit diesem Code - zum Beispiel könnte der
operator()auch ein Funktionstemplate sein. Es ist AFAIK unmöglich jeden Funktionsobjekt-Typ als solchen feststellen zu können, selbst wenn man sich auf eine Parameterzahl beschränkt.Falls du also den Template-fall weglässt lässt sich
functor_traitsrecht einfach (durch partielle Spezialisierungen) implementieren.
-
Arcoth schrieb:
Hier liegt dein Denkproblem - ein Lambda ist nichts weiter als ein Klassenobjekt.
Ist es daher instanziierbar?
Arcoth schrieb:
Du kannst nicht prüfen ob ein Funktionsobjekt ein Lambda ist - genauso wenig wie du prüfen kannst ob ein Funktionsobjekt-Typ ein closure-Typ ist.
Was ist ein closure-Typ?
Arcoth schrieb:
Dein Code ist auch teils falsch - was wenn
decltype(std::declval<U>()())eine Referenztyp ist?Stimmt, daran dachte ich nicht. Macht
std::remove_referenceden Trick?Arcoth schrieb:
Um zu prüfen ob ein Typ eine Klasse mit überladenem
operator()ist, nutz sowas:template<typename U> static std::true_type check(decltype(&U::operator())); template<typename> static std::false_type check(...); public: static const bool is_functor = decltype(check<T>(0))::value;Geht das irgendwie auch in C++03?
Arcoth schrieb:
Es gibt aber selbstverständlich auch Probleme mit diesem Code - zum Beispiel könnte der
operator()auch ein Funktionstemplate sein.Stimmt, daran dachte ich ebenfalls nicht.
Arcoth schrieb:
Falls du also den Template-fall weglässt lässt sich
functor_traitsrecht einfach (durch partielle Spezialisierungen) implementieren.Auch in C++03? Könntest du das in groben Zügen zeigen? Ich kann's mir leider echt nicht vorstellen.
-
Macht
std::remove_referenceden Trick?Das gibt es aber doch erst seit C++11.
Was ist ein closure-Typ?
Der Typ eines Lambdas - also die Klasse dessen Objekt es ist.
Geht das irgendwie auch in C++03?
Wie wärs mit
template <void*> struct Nothing { typedef char type; }; template <typename U> static typename Nothing<&U::operator()>::type check(int); template <typename> static int check(char); public: static const bool is_functor = sizeof(check<T>(0)) == 1;Könntest du das in groben Zügen zeigen?
Ist eklig mit C++03. Hast du Boost.PP zur Verfügung?
-
Arcoth schrieb:
Macht
std::remove_referenceden Trick?Das gibt es aber doch erst seit C++11.
Und nahezu trivial zu implementieren, für vor C++11.
-
Arcoth schrieb:
Macht
std::remove_referenceden Trick?Das gibt es aber doch erst seit C++11.
Joa schon, ich habe es aber auch in C++03 implementiert mit partieller Spezialisierung.
Arcoth schrieb:
Wie wärs mit
template <void*> struct Nothing { typedef char type; }; template <typename U> static typename Nothing<&U::operator()>::type check(int); template <typename> static int check(char); public: static const bool is_functor = sizeof(check<T>(0)) == 1;Sieht zwar sehr gut aus, geht aber leider irgendwie nicht: http://ideone.com/LEJVUl
Mache ich etwas falsch?Arcoth schrieb:
Könntest du das in groben Zügen zeigen?
Ist eklig mit C++03. Hast du Boost.PP zur Verfügung?
Ist egal ob's eklig wird oder nicht. Boost wollte ich eigentlich nicht verwenden (einiges davon gibt's da ja eh schon fertig glaube ich).
-
Du kannst keine Funktionspointer implizit nach void* konvertieren.
Wieso so check-Dinger nicht gehen, kannste gut erkennen indem du den default-Fall deaktivierst.
Übrigens: int kann genauso groß sein wie char.
Nimm besser char und char[2] oder so.
-
Arcoth schrieb:
Wie wärs mit
template <void*> struct Nothing { typedef char type; }; template <typename U> static typename Nothing<&U::operator()>::type check(int); template <typename> static int check(char); public: static const bool is_functor = sizeof(check<T>(0)) == 1;Weder Funktionszeiger noch Zeiger auf Member sind implizit in void* konvertierbar.
Schnell hingeschrieben könnte es so funktionieren. Kann man noch etwas vereinfachen.
-
camper schrieb:
Schnell hingeschrieben könnte es so funktionieren.
Völlig irre! Warum sind die Rückgabetypen Referenzen?
Ich schlage als korrigierte Version
template<typename T> class test { template <int> struct Nothing {typedef char type;}; template <typename U> static typename Nothing<sizeof(&U::operator())>::type check(int); template <typename> static int check(char); public: static const bool is_functor = sizeof(check<T>(0)) == 1; };vor.
-
template<typename T> class test { template <int>//int rein => char raus. struct Nothing {typedef char type;}; template <typename U>//int rein => char raus. static typename Nothing<sizeof(&U::operator())>::type check(int); template <typename>//char rein => int raus. static int check(char); public: static const bool is_functor = sizeof(check<T>(0)) == 1; };
-
volkard schrieb:
template<typename T> class test { template <int>//int rein => char raus. struct Nothing {typedef char type;}; template <typename U>//int rein => char raus. static typename Nothing<sizeof(&U::operator())>::type check(int); template <typename>//char rein => int raus. static int check(char); public: static const bool is_functor = sizeof(check<T>(0)) == 1; };?
Das ist Absicht.
-
Nathan schrieb:
Das ist Absicht.
Ist es notwendig?
-
volkard schrieb:
Nathan schrieb:
Das ist Absicht.
Ist es notwendig?
Ja.
Der unterschiedlich Rückgabewert weil sonst sizeof auf den Rückgabewert nicht funktioniert um die ausgewählte Funktion zu bestimmen und der unterschiedliche Paramter, damit die char-Version wenn man sie mit einem int aufruft, geringere Priorität wegen der Konvertierung nach char hat, als die andere Funktion, die man direkt mit einem int aufrufen kann. Ansonsten hätte man eine Ambiguität.
-
Nathan schrieb:
volkard schrieb:
Nathan schrieb:
Das ist Absicht.
Ist es notwendig?
Ja.
Der unterschiedlich Rückgabewert weil sonst sizeof auf den Rückgabewert nicht funktioniert um die ausgewählte Funktion zu bestimmen und der unterschiedliche Paramter, damit die char-Version wenn man sie mit einem int aufruft, geringere Priorität wegen der Konvertierung nach char hat, als die andere Funktion, die man direkt mit einem int aufrufen kann. Ansonsten hätte man eine Ambiguität.Danke, daß Du jetzt neben Arcoth auch mich für einen Vollidionten hältst.
Und jetzt antworte nochmal gescheit, warum Du zweimal int/char vertauscht, mir scheint der Doppeltausch nichts zu bewirken, außer die Lesbarkeit zu senken.
-
volkard schrieb:
Nathan schrieb:
volkard schrieb:
Nathan schrieb:
Das ist Absicht.
Ist es notwendig?
Ja.
Der unterschiedlich Rückgabewert weil sonst sizeof auf den Rückgabewert nicht funktioniert um die ausgewählte Funktion zu bestimmen und der unterschiedliche Paramter, damit die char-Version wenn man sie mit einem int aufruft, geringere Priorität wegen der Konvertierung nach char hat, als die andere Funktion, die man direkt mit einem int aufrufen kann. Ansonsten hätte man eine Ambiguität.Danke, daß Du jetzt neben Arcoth auch mich für einen Vollidionten hältst.
Äh, moment.
Arcoth halte ich nicht für einen Vollidioten, ich dachte der fehlerhafte Code den Abrakadabra zitiert hatte, stammte von ihm, als ich meinen Beitrag geschrieben habe, da ich den von ihm nur überflogen hatte. Das Arcoth den Fehler gemacht hat, fiel mir nicht auf, weswegen ich meine Antwort halt so formuliert.
Und dich halte ich auch nicht für einen Vollidioten. Ich hatte mich schon gewundert, dass du das gefragt hast*, habs aber normal beantwortet, weil ich dachte, dass du auf etwas hinauswillst, was du ja scheinbar auch wolltest. Ich hatte gerechnet, dass du mich jetzt total bloßstellst, weil ich irgendetwas nicht wusste.Und jetzt antworte nochmal gescheit, warum Du zweimal int/char vertauscht, mir scheint der Doppeltausch nichts zu bewirken, außer die Lesbarkeit zu senken.
Der Code ist von Arcoth, nicht von mir.
Und was meinst du mit "Doppeltausch"?*Siehe mein Fragezeichen, bevor ich "Das ist Absicht" schrieb.
-
Sorry, ambiq formuliert, gemein war "daß nach Arcoth auch Du mich für".
Nathan schrieb:
weil ich dachte, dass du auf etwas hinauswillst,
Mich hat's verwirrt, daß int/char zweimal vertauscht werden und dachte, wenn man das vermeiden würde, müßte man nur noch halb so viel sich im Kopf merken, um zu verstehen, wie der Code geht. Vielleicht bin ich noch nicht richtig wach heute, aber irgendwie scheint mir der "Doppeltausch" ungünstig zu sein.
template<typename T> class test { template <int>//int rein => int raus. struct Nothing {typedef int type;}; template <typename U>//int rein => int raus. static typename Nothing<sizeof(&U::operator())>::type check(int); template <typename>//char rein => char raus. static char check(char); public: static const bool is_functor = sizeof(check<T>(0)) != 1; };
-
volkard schrieb:
Sorry, ambiq formuliert, gemein war "daß nach Arcoth auch Du mich für".
Nathan schrieb:
weil ich dachte, dass du auf etwas hinauswillst,
Mich hat's verwirrt, daß int/char zweimal vertauscht werden und dachte, wenn man das vermeiden würde, müßte man nur noch halb so viel sich im Kopf merken, um zu verstehen, wie der Code geht. Vielleicht bin ich noch nicht richtig wach heute, aber irgendwie scheint mir der "Doppeltausch" ungünstig zu sein.
template<typename T> class test { template <int>//int rein => int raus. struct Nothing {typedef int type;}; template <typename U>//int rein => int raus. static typename Nothing<sizeof(&U::operator())>::type check(int); template <typename>//char rein => char raus. static char check(char); public: static const bool is_functor = sizeof(check<T>(0)) != 1; };Ach so, das meinst du. Die int Version sollte auch int nehmen und die char Version char.
Ja, da stimme ich dir zu, ich habe das Problem bei meinem generischem "Ausdruck gültig?" folgendermaßen umgangen:template <typename T, typename Expr> class expression_valid { // char und int haben keinerlei semantische Bedeutung und können außerdem noch gleich groß sein using matched_t = char(&)[1]; using unmatched_t = char(&)[2]; template <class T2> static matched_t checker(decltype(Expr::template check<T2>())) noexcept; // ellipsis als default, das ist aktuell noch der einzig anehmbare Verwendungszweck in C++ und außerdem garantiert das schlechteste Match template <class T2> static unmatched_t checker(...) noexcept; public: // weitaus mehr Semantik als == 1 // besser wäre sogar noch is_same, aber gut static constexpr auto value = sizeof(checker<T>(0)) == sizeof(matched_t); };
-
@volkard Niemand hält dich für einen Idioten.
Vielleicht bin ich noch nicht richtig wach heute, aber irgendwie scheint mir der "Doppeltausch" ungünstig zu sein.
Das ist mir direkt aufgefallen nachdem ich den Code geschrieben habe - die Tatsache dass das irgendjemand benörgeln wird war mir auch sofort bewusst, aber ich war einfach zu faul es zu korrigieren.
@Nathan Für
matched_t& co. etwas benutze ich das oder ähnliches. Fasst du so etwas nicht zusammen?
Und war es wirklich nötigcheckeralsnoexceptzu markieren?Endlich ist mir bei Nathans Version aufgefallen warum die Rückgabetypen Referenzen sind - wären ja sonst beides Zeiger.
-
Arcoth schrieb:
@Nathan Für
matched_t& co. etwas benutze ich das oder ähnliches. Fasst du so etwas nicht zusammen?Ich brauchte zu dem Zeitpunkt nur zwei einfache Typedefs unterschiedlicher Größe, da kam ich gar nicht auf die Idee das zu generalisieren.
Dein rank Ding ist aber nice.Und war es wirklich nötig
checkeralsnoexceptzu markieren?Gewohnheit. Ich war damals (vor 6 Monaten oder so) in der Phase, wo noexcept mein Default für Funktionen war.
Für neueren Code hab ich meine Exceptionspolicies geändert und somit auch mein noexcept. Ich nutz das jetzt nur aus semantischen Gründen, für Funktionen die nie schief gehen können (Ein operator[] ist bspw. nicht noexcept*, selbst wenn er nicht den Index checkt, man kann einen Segfault kriegen, d.h. die Funktion failen).Endlich ist mir bei Nathans Version aufgefallen warum die Rückgabetypen Referenzen sind - wären ja sonst beides Zeiger.
Da hast du mir was voraus. Ich musste beim Copy-Pasten erst einmal ewig überlegen, wieso Referenzen.
*Genau genommen ist der idR ein spezielles NOFAIL, was "geht nur schief, wenn Programmierer dumm ist" bedeutet und je nachdem ob Logikfehler gecheckt werden noexcept oder nicht ist, aber darum gehts hier nicht.
-
Genau genommen ist der idR ein spezielles NOFAIL, was "geht nur schief, wenn Programmierer dumm ist" bedeutet

man kann einen Segfault kriegen,
Du tust da aber entsprechende Assertions rein, richtig?