Variadic Function ohne Parameteranzahl



  • @ Nexus: Ich weigere mich aber, C++0x zu nutzen, bis es der Standard ist 🙂

    @ pumuckl: Eine Vektor-Klasse mit beliebig vielen Dimensionen. Die soll dann z.B. Vector<3> heißen. Beim Konstruktor soll man minimal 0 und maximal so viele Parameter angeben, wie es Dimensionen gibt. Der Rest wird dann mit einem Standardwert (z.B. 0) initialisiert.

    Vector<3> v(1.0, 2.0); // <- soll klappen
    


  • Varidatisch Parameter gibt es auch aber die sind nicht typsicher...



  • Wurstinator schrieb:

    Vector<3> v(1.0, 2.0); // <- soll klappen
    

    Es ist zwar nicht ganz das von dir gewünschte, aber hast du dir mal Boost-Assignment angeschaut?



  • Wurstinator schrieb:

    @ pumuckl: Eine Vektor-Klasse mit beliebig vielen Dimensionen.

    Meinst du Dimensionen oder Elemente? Nur dass wir von Gleichen reden. Ein vector mit 3 Dimensionen hätte z.B. 3 Längen/Ausdehnungen, also z.B. 8x14x22 Elemente. Ein vector mit 3 Elementen hat eben 3 Elemente. Das was du da skizzierst sieht eher nach letzterem aus, zumindest verstehe ich sonst den Konstruktoraufruf nicht.

    Da du Vector<3> schreibst, also ein Template, nehme ich an, dass du die Größe (d.h. die Anzahl der Elemente) zur Compilezeit festnageln möchtest. Das spräche dann für std::array (C++0x-Standardbibliothek) bzw. std::tr1::array (C++ TR1) bzw. boost::array.
    Das Ganze gewürzt mit boost::assign oder ggf. initializer-Listen in C++0x



  • pumuckl schrieb:

    Ein vector mit 3 Dimensionen hätte z.B. 3 Längen/Ausdehnungen, also z.B. 8x14x22 Elemente.

    Ein dreidimensionaler Vektor ist für mich (x,y,z), also ein Vektor mit drei Komponenten. Die Dimension des Vektorraums ist 3. Zumindest falls wir von mathematischen Vektoren sprechen...



  • Ja, ich meinte mathematische Vektoren.

    @ asc: Ich werds mir mal ansehen, danke.
    edit: Entweder hab ich das falsch verstanden oder du hast mich (auch) falsch verstanden und dachtest, ich meine Vektoren-Listen.



  • Boost.Assign verwendet im Prinzip folgende Technik:

    #include <algorithm>
    
    template <unsigned int N>
    struct Vector
    {
    	int values[N];
    
    	Vector<N+1> operator() (int value) const
    	{
    		Vector<N+1> v;
    		std::copy(values, values+N, v.values);
    		v.values[N] = value;
    		return v;
    	}
    };
    
    Vector<1> MakeVector(int value)
    {
    	Vector<1> v;
    	v.values[0] = value;
    	return v;
    }
    
    int main()
    {
    	Vector<4> x = MakeVector(4)(5)(7)(8);
    }
    

    Hier kopierst du natürlich unnötig viel. Kapselung ist auch nicht optimal, da man auf einem fertigen Vektor-Objekt immer noch operator() aufrufen kann. Aber das Grundprinzip sollte verständlich werden...



  • Aber wenn ich das richtig verstehe, wird am Ende doch ein Vektor<N> zurückgegeben, wobei N die Anzahl der Parameter ist.

    Vector<5> x = MakeVector(4)(5)(7)(8); // Fehler?
    


  • Ja, sollte einen Fehler ergeben weil ein Vector<5> ein komplett anderer Typ ist als Vector<4> .



  • Das meinte ich ja.
    Somit löst das eben genau nicht das, was ich erreichen will 😉



  • Man kann aber mit einem impliziten Konvertierungsoperator das Verhalten verändern...



  • Wurstinator schrieb:

    Das meinte ich ja.
    Somit löst das eben genau nicht das, was ich erreichen will

    Ich sagte ja, das Codebeispiel solle nur das Prinzip verdeutlichen. Wenn du rumbastelst, kannst du das beliebig konfigurieren und optimieren.



  • Wie verwende ich denn implizite Konvertierung mit Templates?



  • // Konstruktor-Definition ausserhalb der Klasse
    template <typename N> // Templateparameter der Klasse
    template <typename M> // Templateparameter der Funktion
    Vector<N>::Vector(const Vector<M>& smallerVector)
    {
        static_assert(M < N); // nicht <=, für == ist der Kopierkonstruktor
        // ...
    }
    

    Aber wenn du das wirklich so durchziehen willst und Performance eine Rolle spielt, solltest du einiges optimieren. Oder gleich Boost.Assign verwenden...



  • Ich würde jetzt gerne, neben der variablen Dimensionsgröße, auch noch den Typ für die Werte variabel machen.
    Das klappt nicht:

    template<unsigned int DIM, class VTYPE=double>
    Vektor<1, VTYPE> Vektor::MakeVektor(VTYPE value)
    {
        Vektor<1, VTYPE> i;
        i[0] = value;
        return i;
    }
    

    error: 'template<unsigned int DIM, class VTYPE> class Vektor' used without template parameters|
    error: default template arguments may not be used in function templates|

    Hat da noch jemand einen Rat für mich?
    Ich bedanke mich aber schon für die bisherige Hilfe.

    Oder gleich Boost.Assign verwenden...

    Ich dachte, Boost.Assign ist für Listen?



  • template<unsigned int DIM, class VTYPE> 
    Vektor<1, VTYPE> Vektor<DIM, VTYPE>::MakeVektor(VTYPE value) //Unbekannter Typ lieber als Referenz auf const übergeben
    { 
        Vektor<1, VTYPE> i; 
        i[0] = value; 
        return i; 
    }
    

    EDIT: Wieso ist MakeVektor eine Methode und keine freie Funktion? Und bei der Implementierung gibt man keine Default-Parameter an (bei Funktionen sowie bei Templates).



  • Ich habe schon mal so etwas hier gebaut:

    template<class T, int Dim>
    struct point
    {
      T coeffs[Dim];
      T const& operator[](int idx) const {return coeffs[idx];}
      T      & operator[](int idx)       {return coeffs[idx];}
    };
    
    template<class Iter>
    inline void assign(Iter) {}
    
    template<class Iter, class Arg, class...More>
    inline void assign(Iter ptr, Arg&& arg, More&&... more)
    {
      *ptr = std::forward<Arg>(arg);
      ++ptr;
      assign(ptr,std::forward<More>(more)...);
    }
    
    template<class T, class...Args>
    point<T,sizeof...(Args)> make_point(Args&&...args)
    {
      point<T,sizeof...(Args)> result;
      assign(result.coeffs+0,std::forward<Args>(args)...);
      return result;
    }
    
    int main() {
      point<int,3> p = {{11,22,33}}; // Listen-Initialisierung klappt sogar in C++98
                                     // da es sich um einen Aggregat-Typen handelt.
      auto q = make_point<double>(1.618,2.718,3.142);
    }
    

    (Warnung1: Dies ist einfach so runtergetippt ohne zu testen)
    (Warnung2: Dies ist C++11 Code)



  • Wieso ist MakeVektor eine Methode und keine freie Funktion?

    Weil Funktionen keine default-Parameter bei Templates haben können.

    @ krümelkacker: Manches davon versteh ich leider nicht und ich bin um diese Uhrzeit auch nicht mehr wild darauf, mir die ganzen Befehle zusammenzusuchen 🙂
    Aber danke, dass du mich an die const-Correctness erinnert hast. Die hab ich bei mir vergessen.

    Also, meine Klasse ist jetzt fertig. Falls jemand Lust hat, kann er ja mal drüberlesen und mir gegebenenfalls Tipps geben.
    Ansonsten bedanke ich mich erneut und verziehe mich 🙂

    #ifndef VEKTOR_HPP_INCLUDED
    #define VEKTOR_HPP_INCLUDED
    
    #include <stdarg.h>
    #include <string.h>
    #include <iostream>
    using namespace std;
    
    template<unsigned int DIM, class VTYPE=double> class Vektor;
    
    template<unsigned int DIM, class VTYPE>
    class Vektor
    {
        template<unsigned int friendDIM, class friendVTYPE> friend class Vektor;
        public:
            // Constructors
            Vektor() {};
            template<class any> Vektor(any* x, ...);
            Vektor(VTYPE vals[]);
            template<unsigned int N> Vektor(const Vektor<N, VTYPE>& other);
            // Operators
            template<unsigned int N> Vektor<DIM, VTYPE> operator=(Vektor<N, VTYPE> other);
            Vektor<DIM, VTYPE> operator=(VTYPE vals[]);
            Vektor<DIM+1, VTYPE> operator()(VTYPE value) const;
            VTYPE& operator[](unsigned int index);
            const VTYPE& operator[](unsigned int index) const;
            // static "MakeVector" function
            static Vektor<0u, VTYPE> MakeVektor();
            static Vektor<1u, VTYPE> MakeVektor(const VTYPE& value);
        protected:
            VTYPE values[DIM];
    };
    
    template<unsigned int DIM, class VTYPE>
    template<class any>
    Vektor<DIM, VTYPE>::Vektor(any* x, ...)
    {
        va_list args;
        va_start(args, x);
        for (unsigned int n = 0; n < DIM; n++)
            values[n] = va_arg(args, VTYPE);
        va_end(args);
    }
    template<unsigned int DIM, class VTYPE>
    Vektor<DIM, VTYPE>::Vektor(VTYPE vals[])
    {
        memcpy(values, vals, sizeof(vals) * sizeof(VTYPE));
    }
    
    template<unsigned int DIM, class VTYPE>
    template<unsigned int N>
    Vektor<DIM, VTYPE>::Vektor(const Vektor<N, VTYPE>& other)
    {
        memcpy(values, other.values, sizeof(other.values));
    }
    
    template<unsigned int DIM, class VTYPE>
    template<unsigned int N>
    Vektor<DIM, VTYPE> Vektor<DIM, VTYPE>::operator=(Vektor<N, VTYPE> other)
    {
        memcpy(values, other.values, sizeof(other.values));
        return *this;
    }
    
    template<unsigned int DIM, class VTYPE>
    Vektor<DIM, VTYPE> Vektor<DIM, VTYPE>::operator=(VTYPE vals[])
    {
        memcpy(values, vals, sizeof(vals) * sizeof(VTYPE));
        return *this;
    }
    
    template<unsigned int DIM, class VTYPE>
    Vektor<DIM+1, VTYPE> Vektor<DIM, VTYPE>::operator()(VTYPE value) const
    {
        Vektor<DIM+1, VTYPE> i(*this);
        i[DIM] = value;
        return i;
    }
    
    template<unsigned int DIM, class VTYPE>
    VTYPE& Vektor<DIM, VTYPE>::operator[](unsigned int index)
    {
        return values[index];
    }
    
    template<unsigned int DIM, class VTYPE>
    const VTYPE& Vektor<DIM, VTYPE>::operator[](unsigned int index) const
    {
        return values[index];
    }
    
    template<unsigned int DIM, class VTYPE>
    Vektor<0u, VTYPE> Vektor<DIM, VTYPE>::MakeVektor()
    {
        Vektor<0u, VTYPE> i;
        return i;
    }
    
    template<unsigned int DIM, class VTYPE>
    Vektor<1u, VTYPE> Vektor<DIM, VTYPE>::MakeVektor(const VTYPE& value)
    {
        Vektor<1u, VTYPE> i;
        i[0] = value;
        return i;
    }
    
    #endif // VEKTOR_HPP_INCLUDED
    


  • Mach das Array values doch private . Klasse die von Vektor erben können darauf mit dem Feldoperator zugreifen. Und die beiden Zeilen 4 & 5 sind nicht standardkonform.



  • Mach das Array values doch private. Klasse die von Vektor erben können darauf mit dem Feldoperator zugreifen.

    Schon klar, aber solange ich die Daten nicht geheim halten will, kann ich sie ja auch zugänglich machen. z.B. für memcpy braucht man ja den Array direkt.

    Und die beiden Zeilen 4 & 5 sind nicht standardkonform.

    Die beiden includes? 😮
    Was soll ich denn dann nehmen?


Anmelden zum Antworten