Anfänger benötigt Hilfe bei Templates



  • Hallo zusammen,

    ich beschäftige mich erst seit kurzer Zeit mit C++ und möchte nun ein Problem mit Hilfe von Templates lösen. Leider komme ich nicht weiter.
    Und zwar möchte ich die Signatur von Funktionsaufrufen zu XML serialisieren.
    Beispiel:

    void SuperFunction1( char* param1, char* param2, int param3 )
    

    soll werden zu:

    <SuperFunction1 param1="abc" param2="def" param3="25" />
    

    Da hatte ich mir gedacht, ich schreibe ein Funktionstemplate, welches ich dann innerhalb der jeweiligen Funktion (hier also SuperFunction1) aufrufe, und welches dann den XML-Code erzeugt.
    Leider hakt es etwas, da ich nicht weiß wie ich ein Template bauen kann, was eine dynamische Anzahl an Parametern entgegen nimmt, und dann immer in 2er Paaren abarbeitet (Parametername und Parameterwert).
    Folgendes habe ich bisher:

    //Rekursionsende
    template < typename T>
    int addAttribute( T t )
    {
    	return 1;
    }
    
    //rekursiv
    template < typename T, typename... Args>
    int addAttribute( T t, Args... args)
    {
    	//t ist abgetrenter Parameter 
    
    	return addAttribute(  args... );
    }
    
    template <typename Function, typename... Args>
    int genXML(Function func, Args... args)
    {
    	// func ist Funktionsname
    
    	return addAttribute( args... );
    }
    

    Nun stehe ich vor dem Problem, das ich nicht weiß wie man es hinbekommen könnte, dass immer Parameterpaare betrachtet werden, anstatt nur ein einzelner. Gehts das so überhaupt, oder sollte man einen völlig anderen Weg wählen?

    Und wie kann ich die Typen der Parameter unterscheiden (sowohl in der rekursiven Funktion als auch im Rekusionsende)? Ich muss für die Erstellung des XML zumindest zwischen char* und int unterscheiden können. Ich hatte etwas von spezialisierten Templates gelesen, aber as wollte gar nicht hinhauen.

    Danke Karl



  • So,

    folgenden Stand habe ich nun:

    //Rekursionsende
    template<typename U>
    int addAttribute( const char* name, U val )
    {
    
    	return 1;
    }
    
    //rekursiv
    template <typename U, typename... Args>
    int addAttribute(const char* name, U val, Args... args)
    {
    	//t ist abgetrenter Parameter 
    	// u ist 2.
    
    	const char *bla = typeid(val).name();
    
    	return addAttribute( args... );
    }
    
    
    template <typename... Args>
    int genXML(char* func,  Args... args)
    {
    	// func ist Funktionsname
    
    	return addAttribute( args... );
    }
    
    //Aufruf mit
    genXML(__FUNCTION__, 
    		"att1", 5, 
    		"att2", "wort", 
    		"att3", 3, 
    		"Att4", "wort2" );
    
    

    Da der Parametername immer ein char* ist, nehme ich ihn nicht ins Template auf und somit komem ich immer ein Paar
    weiter. Das könnte ich wohl auch mit einem std::pair machen, anstelle von einzelnen Parametern.
    Nun stehe ich noch vor dem Problem, mit den Datentypen. U ist ja entweder ein int, oder ein char* und das müsste ich unterscheiden. Ist da das typeid das Mittel der Wahl?

    Oder wie würde ich spezialisierte Templates für int und für char* erstellen? Und wie würde das für ein std::pair aussehen müssen?



  • @KlammerKarl sagte in Anfänger benötigt Hilfe bei Templates:

    U ist ja entweder ein int, oder ein char* und das müsste ich unterscheiden. Ist da das typeid das Mittel der Wahl?

    Inwiefern musst du das unterscheiden? Zum Serialisieren? Ich würde das über Spezialisierung und ein Hilfstemplate, evtl. trait-artig machen.



  • @KlammerKarl sagte in Anfänger benötigt Hilfe bei Templates:

    Oder wie würde ich spezialisierte Templates für int und für char* erstellen?

    Why Not Specialize Function Templates?



  • @Mechanics: Ja ich muss das unterscheiden für die Serialisierung. Die Lib die ich dafür verwende benötigt einen andere Funktion, wenn man ein Attribut vom Typ int hinzufügen möchte.

    @Swordfish: Ok, also müsste ich je eine Überladung für die rekursive Funktion und eine für die endgültige Funktion erstellen?
    So in etwa?

    //Rekursionsende
    template<typename U>
    int addAttribute( const char* name, int val )
    {
    
    	return 1;
    }
    
    template<typename U>
    int addAttribute( const char* name, char* val )
    {
    
    	return 1;
    }
    
    //rekursiv
    template <typename U, typename... Args>
    int addAttribute(const char* name, int val, Args... args)
    {
    	//t ist abgetrenter Parameter 
    	// u ist 2.
    
    	const char *bla = typeid(val).name();
    
    	return addAttribute( args... );
    }
    
    template <typename U, typename... Args>
    int addAttribute(const char* name, char* val, Args... args)
    {
    	//t ist abgetrenter Parameter 
    	// u ist 2.
    
    	const char *bla = typeid(val).name();
    
    	return addAttribute( args... );
    }
    


  • Warum so kompliziert? Du brauchst am Ende ja keine Templates mehr, sondern nur überladene Funktionen. Das hatte ich mit Hilfstemplate gemeint, aber mit überladenen Funktionen gehts hier noch einfacher.



  • @Mechanics sagte in Anfänger benötigt Hilfe bei Templates:

    Warum so kompliziert? Du brauchst am Ende ja keine Templates mehr, sondern nur überladene Funktionen. Das hatte ich mit Hilfstemplate gemeint, aber mit überladenen Funktionen gehts hier noch einfacher.

    Hmm ok, dann kann ich dir nicht folgen.
    Die Anzahl an Parametern ist ja dynamisch. Ich dachte da gibt es nur den Weg über die paked Parameters in templates.
    Gibt es noch eine andere Möglichkeit?
    Wie gesagt, ich bin noch recht neu in C++.



  • Deine letzte Funktion, die nur noch einen Parameter hat, muss den Wert ja auch speichern. Ok, die heißt schon addAttribute, vielleicht war das verwirrend. Dann ruf in der doch einfach "storeAttribute" oder so auf, und das ist keine template Funktion mehr.



  • So, kam nun zum testen:

    #include "stdafx.h"
    #include <iostream>
    
    #include <typeinfo>
    
    using namespace std;
    
    
    //Rekursionsende
    int addAttribute(const char* name, int val)
    {
    
    	return 1;
    }
    
    int addAttribute(const char* name, char* val)
    {
    
    	return 1;
    }
    
    //rekursiv
    template <typename... Args>
    int addAttribute(const char* name, int val, Args... args)
    {
    	//t ist abgetrenter Parameter 
    	// u ist 2.
    
    	const char *bla = typeid(val).name();
    
    	return addAttribute(args...); // <-- Fehlermeldung
    }
    
    template < typename... Args>
    int addAttribute(const char* name, char* val, Args... args)
    {
    	//t ist abgetrenter Parameter 
    	// u ist 2.
    
    	const char *bla = typeid(val).name();
    
    	return addAttribute(args...);
    }
    
    template <typename... Args>
    int genXML(char* func,  Args... args)
    {
    	// func ist Funktionsname
    
    	return addAttribute( args... );
    }
    
    
    int main()
    {
    	genXML(__FUNCTION__, 
    		"att1", 5, 
    		"att2", "wort", 
    		"att3", 3, 
    		"Att4", "wort2" );
    
    
        return 0;
    }
    

    Die beiden "Endpunktfunktionen" sind nun kein Template mehr. Aber ich bekomme so wie der Code ist, folgende Fehlermeldung:

    
    Fehler	C2665	"addAttribute": Durch keine der 4 Überladungen konnten alle Argumenttypen konvertiert werden.	Zeile 41	
    

    Die Fehlermeldung habe ich mal im Code markiert.
    Weshalb findet er keine passende Überladung?



  • @KlammerKarl sagte in Anfänger benötigt Hilfe bei Templates:

    Beispiel:

    void SuperFunction1( char* param1, char* param2, int param3 )
    

    soll werden zu:

    <SuperFunction1 param1="abc" param2="def" param3="25" />
    

    Ich weiß jetzt nicht wirklich, wo das Problem ist.

    #include <cstddef>
    #include <vector>
    #include <string>
    #include <iostream>
    #include <sstream>
    
    std::string my_to_string(char const *value) { return value; }
    
    template<typename T>
    std::string my_to_string(T value) { return std::to_string(value); }
    
    template<typename... Args>
    std::string genXML(char const *name, Args... args)
    {
    	std::ostringstream oss;
    	oss << '<' << name;
    
    	std::vector<std::string> foo{ my_to_string(args)... };
    
    	for (std::size_t i{}; i + 1 < foo.size(); i += 2) {
    		oss << " \"" << foo[i] << "\"=\"" << foo[i + 1] << '"';
    	}
    	oss << " />";
    	return oss.str();
    }
    
    int main()
    {
    	std::cout << genXML(__FUNCTION__, "att1", 5, "att2", "wort", "att3", 3, "Att4", "wort2", 1) << '\n';
    }


  • @Swordfish: Vielen Dank für dein Beispiel.

    @Swordfish sagte in Anfänger benötigt Hilfe bei Templates:

    Ich weiß jetzt nicht wirklich, wo das Problem ist.

    Mein Problem ist es zu verstehen, weshalb er mit die Fehlermeldung bringt.
    In deinem Besipiel wandelst du die int zu std::string um und schreibst das XML "manuell".
    Ich nutze eine Lib dafür und die erwartet für ganzzahlige Parameter eben auch den konkreten Typ.

    Ich werde mal schauen, ob ich den später brauche, falls nicht kann ich deinen Ansatz verfolgen.



  • @KlammerKarl sagte in Anfänger benötigt Hilfe bei Templates:

    Ich nutze eine Lib dafür und die erwartet für ganzzahlige Parameter eben auch den konkreten Typ.

    Wen du verraten würdest welche library und wie die entsprechenden Funktionen aufgerufen werden ...



  • @Swordfish sagte in Anfänger benötigt Hilfe bei Templates:

    @KlammerKarl sagte in Anfänger benötigt Hilfe bei Templates:

    Ich nutze eine Lib dafür und die erwartet für ganzzahlige Parameter eben auch den konkreten Typ.

    Wen du verraten würdest welche library und wie die entsprechenden Funktionen aufgerufen werden ...

    Ich nutze die ChilKat Library: https://www.chilkatsoft.com/refdoc/vcCkXmlRef.html

    Und hier dann:

    bool AddAttribute(const char *name, const char *value);
    
    bool AddAttributeInt(const char *name, int value); //für int
    

    Edit: Würde genre noch wissen, ob man den eigentlichen Aufruf noch eleganter machen kann? Also stat einzelner Parameter evetneull std::pair nutzen oder gibt es da noch etwas besseres?


Anmelden zum Antworten