Funktion nur über Anzahl variabler Template Parameter überladen
-
Hoi.
Ich poste einfach mal nur leicht zusammengekürzten Code:#include <map> #include <iostream> #include <typeinfo> #include <string> #include <stdexcept> #include <cstdint> #include <math.h> class native_type_manager { private: std::map<std::type_info const*, std::string> m_name_mapping; public: void add_type(std::type_info const& typeinfo, std::string name) { if(name.empty()) { throw std::logic_error( "Native Type Manager : Not allowed to add type with " "empty name"); } m_name_mapping[&typeinfo] = std::move(name); } std::string get_type_name(std::type_info const& typeinfo) { std::string result = m_name_mapping[&typeinfo]; if(result.empty()) { throw std::logic_error( "Native Type Manager : Attempt to retrieve name of " "unknown type"); } return result; } }; namespace detail { template<typename Head, typename... Tail> void append_args_to_decl(native_type_manager& types, std::string& decl) { decl += types.get_type_name(typeid(Head)); decl += ", "; append_args_to_decl<Tail...>(types, decl); } template<typename Head> void append_args_to_decl(native_type_manager& types, std::string& decl) { decl += types.get_type_name(typeid(Head)); } } // Creates the decleration of a global function without arguments. template<typename ResultType> std::string make_fun_decl(native_type_manager& types, std::string const& name, ResultType (*)()) { return types.get_type_name(typeid(ResultType)) + ' ' + name + "()"; } // Creates the decleration of a global function with arguments. template<typename ResultType, typename... ArgTypes> std::string make_fun_decl(native_type_manager& types, std::string const& name, ResultType (*)(ArgTypes...)) { std::string decl(types.get_type_name(typeid(ResultType))); decl += ' '; decl += name; decl += '('; detail::append_args_to_decl<ArgTypes...>(types, decl); decl += ')'; } int main() { native_type_manager types; types.add_type(typeid(double), "double"); std::cout << make_fun_decl(types, "cos", &cos); }Mein Problemkind sind die beiden Funktionen im detail-namespace, die eigentlich alle Argumente iterieren sollen, mein Ansatz ist aber recht falsch.
Nur zur Info was der Compiler sagt:
clang++ -Wall -Wextra -std=c++11 -O3 -march=native -o "test" "test.cpp" (in directory: /home/ethon) test.cpp:85:2: error: call to 'append_args_to_decl' is ambiguous detail::append_args_to_decl<ArgTypes...>(types, decl); ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ test.cpp:99:15: note: in instantiation of function template specialization 'make_fun_decl<double, double>' requested here std::cout << make_fun_decl(types, "cos", &cos); ^ test.cpp:44:7: note: candidate function [with Head = double, Tail = <>] void append_args_to_decl(native_type_manager& types, ^ test.cpp:53:7: note: candidate function [with Head = double] void append_args_to_decl(native_type_manager& types, ^ 1 error generated. Compilation failed.Hoffe mir kann ein Template-Guru bei der Lösung des Problems helfen, bin ziemlich eingerostet nach einigen Programmierfreien Monaten.

Danke und Grüße,
Ethon
-
Du hast 2 Funktionstemplates. Eines, das 1 Template-Argument nimmt, und ein zweites, das 1 + n Template-Argumente nimmt. n kann aber auch 0 sein. Loesung: Mach aus dem zweiten Template eine normale Funktion ohne Template-Argumente, die einfach gar nichts tut.
-
Ethon schrieb:
Hoffe mir kann ein Template-Guru bei der Lösung des Problems helfen, bin ziemlich eingerostet nach einigen Programmierfreien Monaten.

Wenn zwei Funktionstemplates daraufhin untersucht werden, ob das eine Template spezieller als das andere ist, wird nur auf die Funktionsparameter geschaut, nicht die Templateparameter (Funktionstemplate A ist mindestens so speziell wie Funktionstemplate B, wenn für jedes mögliche Funktionsparametertuple für A Templateparameter für B deduziert werden können, die im gleichen Parametertuple für B resultieren).
Deine Funktionsparameter hängen aber gar nicht von den Templateargumenten ab, sondern diese müssen stets explizit angegeben werden. Also ist keines der beiden Funktionstemplates spezieller als das andere.Übrigens lässt sich das auch nicht-rekursiv darstellen:
#include <map> #include <iostream> #include <typeinfo> #include <string> #include <stdexcept> #include <cstdint> #include <cmath> #include <typeindex> class native_type_manager { private: std::map<std::type_index, std::string> m_name_mapping; public: void add_type(std::type_info const& typeinfo, std::string name) { if(name.empty()) { throw std::logic_error( "Native Type Manager : Not allowed to add type with " "empty name"); } m_name_mapping[typeinfo] = std::move(name); } std::string get_type_name(std::type_info const& typeinfo) { std::string result = m_name_mapping[typeinfo]; if(result.empty()) { throw std::logic_error( "Native Type Manager : Attempt to retrieve name of " "unknown type"); } return result; } }; // Creates the decleration of a global function without arguments. template<typename ResultType> std::string make_fun_decl(native_type_manager& types, std::string const& name, ResultType (*)()) { return types.get_type_name(typeid(ResultType)) + ' ' + name + "()"; } // Creates the decleration of a global function with arguments. template<typename ResultType, typename Arg0, typename... Args> std::string make_fun_decl(native_type_manager& types, std::string const& name, ResultType (*)(Arg0, Args...)) { std::string decl(types.get_type_name(typeid(ResultType))); decl += ' '; decl += name; decl += '('; decl += types.get_type_name(typeid(Arg0)); // Im Gegensatz zu normalen Funktionsaufrufen ist bei Initialisierungen die Reihenfolge vorgegeben, // wir benutzen die Initialisierung des arrays nur dazu, um die Packexpansion in eine Reihe von Ausdrücken, // die nacheinander ausgwertet werden, zu überführen bool dummy[] = { ( ( ( decl += ", " ) += types.get_type_name(typeid(Args)) ), false )... }; (void)dummy; decl += ')'; return decl; } double foo() { return 0; } double foo1(double) { return 0; } double foo2(double,double) { return 0; } double foo3(double,double,double) { return 0; } int main() { native_type_manager types; types.add_type(typeid(double), "double"); std::cout << make_fun_decl(types, "foo", &foo) << '\n'; std::cout << make_fun_decl(types, "foo1", &foo1) << '\n'; std::cout << make_fun_decl(types, "foo2", &foo2) << '\n'; std::cout << make_fun_decl(types, "foo3", &foo3) << '\n'; }type_index nicht vergessen, denn type_info-Objekte sind nicht unbedingt einzigartig.
-
So, scheint ja eine echt nette Lösung zu sein, könntest du bitte kurz den Hack mit der Initialisierungsliste erklären? Habe den heute schon einmal von dir gesehen, verstehe allerdings das Konzept nicht.
Wegen type_info: Weißt du zufälligerweise in welchen Fällen?
-
n3337 8.5.4/4 schrieb:
Within the initializer-list of a braced-init-list, the initializer-clauses, including any that result from pack
expansions (14.5.3), are evaluated in the order in which they appear. That is, every value computation and
side effect associated with a given initializer-clause is sequenced before every value computation and side
effect associated with any initializer-clause that follows it in the comma-separated list of the initializer-list.
[ Note: This evaluation ordering holds regardless of the semantics of the initialization; for example, it applies
when the elements of the initializer-list are interpreted as arguments of a constructor call, even though
ordinarily there are no sequencing constraints on the arguments of a call. — end note ]
An object of type std::initializer_list<E> is constructed from an initializer list as if the implementation
allocated an array of N elements of type E, where N is the number of elements in the initializer list.
Each element of that array is copy-initialized with the corresponding element of the initializer list, and
the std::initializer_list<E> object is constructed to refer to that array. If a narrowing conversion is
required to initialize any of the elements, the program is ill-formed.
-
bool dummy[] = { ( ( ( decl += ", " ) += types.get_type_name(typeid(Args)) ), false )... }; (void)dummy;
Okay, wenn ich es richtig verstanden habe nutzt du den Komma-Operator aus um ein bool-Array füllen zu können (Anstatt einem String-Array, das möglicherweise/warscheinlich nicht wegoptimiert wird). Durch das Entpacken des Argumentenpacks wird das dummy Array also so lange mit false gefüllt (bzw die Initialisierungsliste wächst) bis alle Argumente abgearbeitet sind. Das bool-Array wegzuoptimieren kann wohl jeder Compiler wohl sogar im Debugmodus im Schlaf. Soweit richtig?
Aber wieso castest du das dummy-Array auf void?
Vielen Dank schonmal!
-
Ethon schrieb:
bool dummy[] = { ( ( ( decl += ", " ) += types.get_type_name(typeid(Args)) ), false )... }; (void)dummy;
Okay, wenn ich es richtig verstanden habe nutzt du den Komma-Operator aus um ein bool-Array füllen zu können (Anstatt einem String-Array, das möglicherweise/warscheinlich nicht wegoptimiert wird). Durch das Entpacken des Argumentenpacks wird das dummy Array also so lange mit false gefüllt (bzw die Initialisierungsliste wächst) bis alle Argumente abgearbeitet sind. Das bool-Array wegzuoptimieren kann wohl jeder Compiler wohl sogar im Debugmodus im Schlaf. Soweit richtig?
ja. Und selbst wenn es nicht wegoptimiert wird, dürfte es weniger Platz und Zeit beanspruchen als rekursive Funktionsaufrufe.
Ethon schrieb:
Aber wieso castest du das dummy-Array auf void?
Um die Compilerwahrung über unbenutzte Variablen auszuschalten.