Vektor-Klasse



  • Hallo!

    Eines vorwerg: hierbei handelt es sich um eine Designfrage, kein Implementierungsproblem.

    Ich habe eine Vektor-Klasse implementiert (im streng mathematischen Sinne, nicht einen Objekt-Container). Momentan verwendet sich intern ein float-Array. Diese Klasse gibt es nun 3 Mal: für 2-, 3- und 4-wertige Vektoren.

    Jetzt brauche ich sie auch für Integer-Daten. Eine naheliegende Lösung wäre es daher, ein Template zu machen. Daran gefällt mir allerdings nicht, dass dann die Implementierungen der Funktionen (hauptsächlich Operatoren) alle in die Headerdatei verschoben werden müssen (was selbige sehr groß und unübersichtlich macht), da der Linker ja Probleme damit hat wenn sich der Code für Templates in der cpp befindet.

    Alternativ könnte ich natürlich alle 3 Größen von Vektoren für die 2 Typen separat implementieren. Das ist aber kein besonders professionelles Design.

    Wie würdet ihr diese Klassen designen? Am besten wäre es natürlich wenn man überhaupt nur eine Vektor-Klasse hätte, aber ich will auch nicht Speicher für 4 Elemente belegen, wenn dann vielleicht eh nur 2 verwendet werden.

    Danke schonmal!

    mfg



  • Templates -> Keine .cpp Datei für die Vektorklassen benutzen.



  • vorschlag

    template<
    	typename T_, // datentyp
    	unsigned n> // 2, 3, 4, usw
    class Vector
    {
    	private:
    		T_	val[n];
    
    	public:
    };
    
    #include "vector.inl" // implementierung
    


  • Der Vorschlag lag irgendwie auf der Hand. Und jetzt wird's spannend. Zum Bespiel Das Skalarprodukt für eine 2D-Vektor sieht wohl so aus:

    T_ operator*( const Vektor2D& b ) const
        {
            return va[0] * b.val[0] + val[1] * b.val[1];
        }
    

    und die Verallgemeinerung mit N konsequenterweise

    T_ operator*( const Vektor& b ) const
        {
            return std::inner_produkt( val, val + N, b.val, T() );
        }
    

    Jetzt kommen die Rufe aus der Performance-Ecke, die Schleife in inner_product ist doch viel langsamer als wenn man die Summe direkt hinschreibt. Es müßte doch irgendwie so was gehen:

    T_ operator*( const Vektor& b ) const
        {
            return va[0] * b.val[0] + val[1] * b.val[1] 
                + WENN( N > 2 )( val[2] * b.val[2] ) + WENN( N > 3 )( val[3] * b.val[3] );
        }
    

    Frage an die Experten: wie setzt man das WENN mit boost::mpl & Co derart um, dass der Compiler in Abhängigkeit von N immer einen Summen-Ausdruck wie beim 2D-Vektor erzeugt?

    Gruß
    Werner



  • Hier findet man etwas zu diesem Thema. Einfach ein wenig nach unten scrollen... siehe da: Loop unrolling. Verstehe es zwar selbst noch nicht ganz, wird aber hoffentlich noch. 😮



  • Werner Salomon schrieb:

    Frage an die Experten: wie setzt man das WENN mit boost::mpl & Co derart um, dass der Compiler in Abhängigkeit von N immer einen Summen-Ausdruck wie beim 2D-Vektor erzeugt?

    Wie Gideon schon andeutete, das Stichwort heisst Loop-Unrolling. Wie es mit boost::mpl funktioniert, kann ich dir allerdings nicht sagen, da ich solch triviale Sachen wie im Beispiel idR direkt implementiere. Vom Prinzip her funktioniert das wie folgt:

    template <unsigned int I>
    struct unroller // Klasse mit Funktion, die sozusagen fuer einen Schleifendurchlauf steht
    {
        template <class T>
        static void makeitso(const T* val, const T* val2)
        {
            return *val * *val2 + unroller<I - 1>::makeitso(val + 1, val2 + 1);
        }
    };
    
    template <>
    struct unroller<1> // Spezialisierung fuer das Ende des Loops (Vorsicht: unroller nicht mit 0 aufrufen oder halt eine Spezialisierung fuer unroller<0> machen)
    {
        template <class T>
        static void makeitso(const T* val, const T* val2)
        {
            return *val + *val2;
        }
    };
    
    T_ operator*( const Vektor& b ) const
    {
        return unroller<N>::makeitso(val, b.val);
    }
    

    Für Meta Loop-Unrolling muss man sich natürlich darauf verlassen, dass der Compiler die Funktionen inlined. Und die Funktionen brauchst du, sofern du Parameter hast, welche du nicht als Template Parameter der Klasse übergeben kannst.


Anmelden zum Antworten