abgeleitete Objekte dynamisch in einen Vector speichern



  • Wow, eure erneuten Kommentare waren für mich noch einmal echt hilfreich. Danke für die C++ Nachhilfe. Allerdings verstehe ich vereinzelte Hinweise nicht komplett.

    @Jockelx : In Zeile 14 verwendest du das Wort static. Warum? Ich habe das Schlüsselwort nachgeschlagen und herausgefunden, dass mit dem Schlüsselwort static versehene Variablen während der gesamten Lebensdauer iherer übergordneten Einheit erhalten bleiben. Wieso sollte das hier wichtig sein? Bei jedem Aufruf von getType() wird die entsprechende Variable s doch neu belegt.
    @Jockelx : Ist das Verwenden von Initialisierungslisten (dein Vorschlag in Zeile 21) auch eine Geschmackssache? Für mich ist klarer, dass über den = Operator in Kombination mit this das Argument der Initialisierungsfunktion m dem gleichnamigen Attribut des Objektes zugeordnet wird. Deshalb habe ich auch this oft verwendet, um direkt im Code zu erkennen, was nun ein Attribut des Objektes und was Methodenargumente sind (bei Gleichnamigkeit von Attribut und Methodenargument).
    @Jockelx : Warum sollte man kein Semikolon nach "}" in for-Schleifen oder if-Anweisungen setzen? (Fast) jede Anweisung in C++ muss doch mit einem ";" abschließen oder?
    @Jockelx : Dein Hinweis const in Verbindung mit dem &-Zeichen wenn immer es geht zu benutzen bezieht sich sicherlich auf die Prämisse, dass referenzieren effektiver als kopieren ist oder?

    @wob : Den Destruktor habe ich "nachgerüstet" und mir entsprechendes dazu durchgelesen. Es ist ja echt tückisch, dass ein Minimalbeispiel funktionieren kann, im Kontext eines größeren Programms dann aber beim Weglassen von beispielsweise einem ordentlichen Destructor evtl. Probleme verursacht.
    Zu deinen Fragen: m soll der Mittelpunkt der geometrischen Formen sein. Daher die Abkürzung m wie "midpoint". Genauso ist das mit ass_of_shapes das für "assembly of shapes" steht. Deine Interpretation ist aber auch nicht schlecht 😂
    Mein Programmierbeispiel ist ja ein Minimalbeispiel ohne viel Firlefanz. Trotzdem finde ich deine Gedanken zu Kommentaren und Variablenbenennung wichtig. Als Programmierautodidakt und Ingenieur fehlen mir manchmal solche übergreifenden Gedanken.

    Zu guter letzt habe ich eure Anregungen der (Volständigkeithalber) implementiert:

    #include <iostream>
    #include <vector>
    #include <math.h>
    #include <memory>
    class AbstractShape{
        public:
            std::vector<double> m;
            AbstractShape(const std::vector<double>& m){this->m=m;}; // vector als const reference übergeben (kein kopieren da kopieren langsamer ist)
            
            // use virtual to make this function overloadable for every derived class/object
            // use keyword const to declare the return value not to be altered
            // use &-sign to give back a reference (avoid copying)
            // use keyword const at the end to indicate that this function cannot change a member variable of the class
            //     and makes it a compiler error if it would do so
            virtual const bool inside(const std::vector<double>& x) const = 0; 
            virtual const std::string& getType() const = 0;
            // use deconstructor to avoid problems connected to destroyed classes with pointers to the base class
            virtual ~AbstractShape() = default;
    };
    class Circle : public AbstractShape{
        public:
            double r;
            Circle(std::vector<double> m,double r):AbstractShape(m){this->r=r;};
            // use keyword override to tell the compiler that we excpect to override a virtual method 
            // (this is just a safety feature that throws a compiler error if we are not overriding a virtual method at that stage)
            const std::string& getType() const override{
                // use the keyword static to maintain this variable throughout the entire life of the objects life
                // but why is this important here???    
                static std::string s = "Circle";
                return s;
            };
            const bool inside(const std::vector<double>& x) const{ 
                bool inside_flag;
                inside_flag = (this->m[0]-x[0])*(this->m[0]-x[0]) 
                             +(this->m[1]-x[1])*(this->m[1]-x[1]) - this->r*this->r <0.;
                return inside_flag;
            };
    };
    class Rectangle : public AbstractShape{
        public:
            double width;
            double height;
            Rectangle(std::vector<double> m,double width,double height):AbstractShape(m){this->width=width;
                                                                                         this->height=height;};
    
            const std::string& getType() const override{
                static std::string s = "Rectangle";
                return s;
            };
    
            const bool inside(const std::vector<double>& x) const{ 
                bool inside_flag;
                inside_flag =   (fabs(x[0]-this->m[0])-width/2.<0.)
                              &&(fabs(x[1]-this->m[1])-height/2.<0.);
                return inside_flag;
            };
    };
    int main(){
        // FILL std::vector dynamically and try wanted functionality
        std::vector<double> m1={0.5,0.5};
        double r = 0.25;
        std::vector<double> m2={0.0,0.0};
        double width  = 0.1;
        double height = 0.2;
        std::vector<std::vector<double>> points{ {-0.03,0.06}, 
                                                 { 0.5,0.45 }, 
                                                 { 1.0,1.0  } };
    
        std::vector<std::unique_ptr<AbstractShape>> shapes;
        shapes.push_back( std::make_unique<Circle>(m1,r) );
        shapes.push_back( std::make_unique<Rectangle>(m2,width,height) );
    
        for (const auto& point: points){
            std::cout << "==== process new point ====" << std::endl;
            for (const auto& shape: shapes){
                std::cout << shape->getType()  << " ";
                std::cout << shape->inside(point) << "\n";
    
                std::string a = shape->getType();
                std::cout << "Now we change the variable : " << a << " to ";
                a = "change";
                std::cout << a << "\n";
            }
        }
    };
    

    Leider verstehe ich die Funktionalität des jeweils ersten Schlüsselwortes const in Zeile 15 und 16 nicht. Nach Literaturrecherche, soll const hier bewirken, dass der Rückgabewert der Funktion ein const ist und somit nicht veränderbar ist. In meinem Code-Beispiel, wird aber genau das in der Schleife durchgeführt. Was habe ich da falsch verstanden?

    Viele Grüße



  • @mawashi sagte in abgeleitete Objekte dynamisch in einen Vector speichern:

    @Jockelx : In Zeile 14 verwendest du das Wort static. Warum? Ich habe das Schlüsselwort nachgeschlagen und herausgefunden, dass mit dem Schlüsselwort static versehene Variablen während der gesamten Lebensdauer iherer übergordneten Einheit erhalten bleiben. Wieso sollte das hier wichtig sein? Bei jedem Aufruf von getType() wird die entsprechende Variable s doch neu belegt.

    s wird nur einmal beim ersten Aufruf erzeugt und danach ohne Kopie zurückgegeben. Du kannst auch eine statische Membervariable nehmen oder meintwegen auch "Circle" als Kopie zurückgeben - das wäre auch kein Untergang. Der wichtigere Punkt war, dass nicht jede Instanz diesen String hält, der eh immer gleich ist und nie geändert werden darf.

    @mawashi sagte in abgeleitete Objekte dynamisch in einen Vector speichern:

    @Jockelx : Ist das Verwenden von Initialisierungslisten (dein Vorschlag in Zeile 21) auch eine Geschmackssache?

    Nein, das ist mehr. Im Fall von float & co macht es keinen Unterschied, aber sobald du Klassen member hast, ändert sich das Verhalten. Dann wird in deinem Fall nämlich erst das Objekt konstruiert (default-Konstruktor) und dann wird eine Zuweisung gemacht. Bei Initialisierungslisten wird nur der Konstruktor aufgerufen. Hat die Klasse des members keinen default-Konstruktor, dann funktioniert deine Variante überhaupt nicht mehr.

    @mawashi sagte in abgeleitete Objekte dynamisch in einen Vector speichern:

    @Jockelx : Warum sollte man kein Semikolon nach "}" in for-Schleifen oder if-Anweisungen setzen? (Fast) jede Anweisung in C++ muss doch mit einem ";" abschließen oder?

    "}" ist aber keine Anweisung.



  • @Jockelx : Ok. Danke für die Erklärungen.

    Eine kleine Frage oder Bitte habe ich (leider) noch. Mein Versuch bestand nun darin, die erarbeitete Funktionalität in eine Übergeordnete Container-Klasse zu packen. Leider scheitere ich daran schon wieder 😵

    Hier mein neuer Code (der Übersichtlichkeit halber nur mit der Form Kreis):

    #include <iostream>
    #include <vector>
    #include <math.h>
    #include <memory>
    // HEADER
    template<unsigned int spacedim>
    class AbstractShape{
        public:
            std::vector<double> m;
            AbstractShape(const std::vector<double>& m); 
            virtual const bool inside(const std::vector<double>& x) const = 0; 
            virtual const std::string& getType() const = 0;
            virtual ~AbstractShape() = default;
    };
    
    template<unsigned int spacedim>
    class Circle : public AbstractShape<spacedim>{
        public:
            double r;
            Circle(std::vector<double> m,double r);
            const std::string& getType() const override;
            const bool inside(const std::vector<double>& x) const override;
    };
    
    template<unsigned int spacedim>
    class RVE{ // representative volume element with shapes
        public:
            std::vector<std::unique_ptr<AbstractShape<spacedim>>> shapes;
            void add_shape( std::unique_ptr<AbstractShape<spacedim>> shape );
    };
    
    // DEFINITIONS
    template<unsigned int spacedim>
    AbstractShape<spacedim>::AbstractShape(const std::vector<double>& m) : m{m}{};
    
    template<unsigned int spacedim>
    Circle<spacedim>::Circle(std::vector<double> m,double r):AbstractShape<spacedim>(m){
        this->r=r;
    };
    
    template<unsigned int spacedim>
    const std::string& Circle<spacedim>::getType() const{
        static std::string s = "Circle";
        return s;
    };
    
    template<unsigned int spacedim>
    const bool Circle<spacedim>::inside(const std::vector<double>& x) const{
        bool inside_flag;
        inside_flag = (this->m[0]-x[0])*(this->m[0]-x[0]) 
                     +(this->m[1]-x[1])*(this->m[1]-x[1]) - this->r*this->r <0.;
        return inside_flag;
    };
    
    template <unsigned int spacedim>
    void RVE<spacedim>::add_shape( std::unique_ptr<AbstractShape<spacedim>> shape ){
        this->shapes.push_back( shape );
    };
    
    // MAIN-PROGRAMM
    int main(){
        double r1 = 0.25;
        std::vector<double> m1={0.0,0.0};
        std::vector<std::vector<double>> points{ {-0.03,0.06}, 
                                                 { 0.5,0.45 } };
        const unsigned int dim=2;
        RVE<dim> rve;
        rve.add_shape( std::make_unique<Circle<dim>>(m1,r1) );
    };
    

    Kommentire ich die Zeile 57 aus, kompiliert das Programm ohne Probleme. Bei Verwendung der push_back-Methode funktioniert das Ganze leider nicht mehr.... habt ihr eine Idee?



  • @mawashi sagte in abgeleitete Objekte dynamisch in einen Vector speichern:

    Kommentire ich die Zeile 57 aus, kompiliert das Programm ohne Probleme. Bei Verwendung der push_back-Methode funktioniert das Ganze leider nicht mehr.... habt ihr eine Idee?

    Du hast als Parameter einen std::unique_ptr<AbstractShape<spacedim>> shape genommen. Es darf immer nur einen Pointerhalter mit diesem Pointer geben (unique), sonst müsstest du einen shared_ptr verwenden. Du musst hier explizit sagen, dass du den unique_pointer shape weitergeben möchtest. Du musst dazu std::move verwenden (shapes.push_back(std::move(shape));) (einen unique_ptr kann man nicht kopieren, sondern nur verschieben) Danach ist die shape-Variable erstmal in einem "moved-from" state, d.h. du darfst danach nicht mehr shape dereferenzieren.



  • Ok. Damit klappt es. Vielen Dank!👏

    Aber warum klappt std::cout << rve.shapes[0]->r << "\n"; dann nicht?
    Vielleicht ist es eh keine gute Praxis direkt auf die Attribute zuzugreifen, aber ich hätte nun gedacht, dass man so auf alle "public" Attribute und Methoden des Objectes Circle zugreifen kann...



  • @mawashi Aber dein rve.shapes ist doch ein Array von (Pointern auf) AbstractShape, nicht Circle!

    Nur der dynamische Typ ist Circle, aber der statische Typ ist AbstractShape.



  • @wob : Man sieht ich habe es noch nicht ganz verstanden 😤
    Was ich möchte ist das Folgende: In der Klasse RVE sollen im vector shapes Formen verschiedenen Typs gesammelt werden. Diese sollen alle die Methode inside beinhalten um damit in einer Methode der RVE-Klasse "global" abfragen zu können ob irgendein Punkt innerhalb irgendeiner Form liegt oder nicht. Zusätzlich möchte ich aber auch auf individuelle Methoden der Formen zugreifen können. Für einen Kreis oder eine Ellipse wäre die Methode curvature denkbar, die für ein Dreieck oder Rechteck keinen Sinn ergibt. Für ein Dreieck oder ein Rechteck wäre die Methode number_of_vertices möglich, die für Kreise oder Ellipse keinen Sinn ergibt.
    Daher wäre es wichtig, auch auf die individuellen Attribute und Methoden der Formen zugreifen zu können und nicht nur auf die der AbstractShape - Klasse.



  • Off-Topic: Ich würde empfehlen std::vector nicht für 2D/3D/4D Vektoren zu verwenden. Das ist bloss umständlich und auch vergleichsweise schrecklich langsam. Typischerweise verwendet man dafür spezielle Klassen ala Vector2D, Vector3D etc. - die dann direkt die Komponenten als Member haben. Bzw. wenn's schnell gehen muss und man keine passende Library zur Hand hat die das bereitstellt kann man dazu auch gut std::array verwenden.



  • @mawashi Was wäre denn, wenn in deinem shapes vector 3 Kreise und 4 Rechtecke wären und du dann in einer Schleife shape->radius ausgeben wolltest - das würde ja für die Rechtecke nicht funktionieren. Außerdem ist die Idee ja gerade, dass ein vector von AbstractShape das auch gar nicht zu wissen braucht, der behandelt alles wie AbstractShape.

    Warum sind deine Kreise zusammen mit anderen Formen in diesem Vector? Wir schon geschrieben, du kannst immer einen Downcast machen, aber schön ist das nicht. Wenn du eine bestimmte Funktionalität für alle Shapes einbauen willst und sich die Klassenanzahl nicht oft ändert, sind auch Visitors ein mögliches Pattern. Hier passt das aber eher nicht - du scheinst ja nur wild auf irgendeine Membervariable zugreifen zu wollen.

    Vielleicht ist es sinnvoller, eine Liste von Kreisen und eine von Rechtecken separat zu halten.

    Wenn du die Funktion "getType" oft brauchst, dann stimmt irgendwas am Design nicht. Wozu haben die Klassen diesen String? Ok, man kann sich damit leicht auf alle Objekte eines konkreten Typs filtern.

    Vielleicht kann jemand mit Ahnung von OOP mehr dazu beitragen.



  • @hustbaer : Ok. Danke für den Hinweis. Ich werde den Code noch auf arrays umstellen.
    @wob : Eine mögliche Aufgabe später in meinem Code ist es, Abstände zwischen den Formen zu berechnen um den minimalen Abstand zwischen zwei Formen einer Anordnung zu bestimmen. Ich fasse dann also alle Formen einer Anordnung (die irgendwie generiert wird) in dem Vektor shapes zusammen und iteriere über alle möglichen Paare von Formen. Diese Paare können dann in dem Beispiel also Kreis-Kreis, Kreis-Rechteck oder Rechteck-Rechteck sein. Bei den Abstandsberechnungen zwischen den Formen sind natürlich die Eigenschaften der Formen wesentlich, so dass dann genau einmal über die getType Funktion erfragt werden muss, um welche Form es sich handelt. Weiter müssen dann abhängig von der Form die individuellen beschreibenden Parameter wie rbeim Kreis oder width und height beim Rechteck benutzt werden um die tatsächlichen Abstände zu ermitteln.



  • @hustbaer : bloß wenn ich mit std::array arbeite, meine Punkte aber in einem std::vector gespeichert sind, dann habe ich doch das Problem, dass die Übergabe zur inside Funktion beispielsweise nicht funktioniert, da die Funktion ein std::array erwartet oder?
    Ich habe das nach folgendem Schema versucht:

    //...
    const bool Circle<spacedim>::inside(const std::array<double,spacedim>& x) const{
    //..
    int main(){
        const unsigned int dim=2;
        double r1 = 0.25;
        std::array<double,dim> m1{0.0,0.0};
        std::vector<std::vector<double>> points{ {-0.03,0.06}, 
                                                 { 0.5,0.45 } };
    // ..
        rve.indicator_bool( points[0] );
        rve.indicator_bool( points[0].data() );
        rve.indicator_bool( std::array<double,dim>{points[0][0],points[0][1]} );
    }
    

    was mir der Compiler mit der Fehlermeldungen quittiert. Einzig der Ausdruck in Zeile 13 funktioniert, sieht aber nicth gerade schön aus...



  • @mawashi sagte in abgeleitete Objekte dynamisch in einen Vector speichern:

        std::vector<std::vector<double>> points{ {-0.03,0.06}, 
                                                 { 0.5,0.45 } };
    ...
        rve.indicator_bool( std::array<double,dim>{points[0][0],points[0][1]} );
    

    was mir der Compiler mit der Fehlermeldungen quittiert. Einzig der Ausdruck in Zeile 13 funktioniert, sieht aber nicth gerade schön aus...

    Ich hab den Thread jetzt nur überflogen, aber ist hier ein std::vector<std::array<double, dim>> points = ... keine praktikable Lösung? Dann sollte eigentlich auch rve.indicator_bool(points[0]) funktionieren.

    Das dürfte ohnehin performanter sein, da ein std::vector immer noch eine Indirektion über einen im Vector gespeicherten Pointer auf die eigentlichen Daten erfordert - im Gegensatz zu einem "flachen" std::array. Oder eben speziellen Vector2D/Vector3D-Klassen, welche die Koordinaten direkt als Member haben - bei dem, was ich bisher bei deinem Code sehe, macht das denke ich schon Sinn, zumal ich vermute, dass du im weiteren Code auch einiges an Vektorarithmetik machen wirst.

    Wenn sowas ordentlich implementiert ist, dann könnte man auch einige Tests eleganter formulieren, z.B. statt

    template<unsigned int spacedim>
    const bool Circle<spacedim>::inside(const std::vector<double>& x) const{
        bool inside_flag;
        inside_flag = (this->m[0]-x[0])*(this->m[0]-x[0]) 
                     +(this->m[1]-x[1])*(this->m[1]-x[1]) - this->r*this->r <0.;
        return inside_flag;
    };
    

    einfach

    template<unsigned int spacedim>
    const bool Circle<spacedim>::inside(const Vector2D& x) const {
        auto m_x = m - x;
        return dot(m_x, m_x) - r * r < 0.0; // dot = Skalarprodukt
    };
    

    oder auch

    transpose(m_x) * m_x - r * r < 0.0
    

    ... falls man Vektoren als n×1n \times 1-Matrizen auffasst und entsprechende Operatoren implementiert sind.



  • @mawashi
    Naja, wenn dann müsstest du schon durchgehend mit std::array arbeiten.
    Also

        const unsigned int dim=2;
        double r1 = 0.25;
        std::array<double,dim> m1{0.0, 0.0};
        std::vector<std::array<double,dim>> points{ {-0.03, 0.06}, 
                                                    {  0.5, 0.45} };
    // ...
    


  • @Finnegan : Ja genau, das wäre natürlich ein Traum, wenn man diese Vektor-/Matrixarithmetik so formulieren könnte wie man es händisch aufschreibt. In anderen Sprachen wie Fortran oder Python geht das ja auch recht einfach, aber meines (Anfänger-)Wissens nach müsste man diese Funktionen in C++ auch erst einmal selbst implementieren oder? Das Skalarprodukt ist zwar in der Standardbibliothek über #include <numeric> verfügbar, aber wenn ich die Dokumentation richtig verstehe, sieht der entsprechende Befehl dann wie folgt aus:

    std::inner_product(m_x.begin(),m_x.end(),m_x.begin(),0);
    

    also bei weitem nicht so elegant wie du es aufgeschrieben hast...

    @hustbaer : Ok, stimmt, dass hatte ich übersehen. Ich hatte auch versucht aus einem std::vector<double,dim> einen std::array<double,dim> zu machen, leider ohne Erfolg. Da mir an dieser Stelle Geschwindigkeit noch nicht ganz so wichtig ist, bleibe ich erst einmal beim std::vector - Konstrukt um für spätere Eventualitäten flexibler zu bleiben.



  • @mawashi sagte in abgeleitete Objekte dynamisch in einen Vector speichern:

    @Finnegan : Ja genau, das wäre natürlich ein Traum, wenn man diese Vektor-/Matrixarithmetik so formulieren könnte wie man es händisch aufschreibt.

    Schau dir vielleicht mal eine der LinAlg-Bibliotheken an, z.B. Eigen oder blaze oder oder...

    In anderen Sprachen wie Fortran oder Python geht das ja auch recht einfach, aber meines (Anfänger-)Wissens nach müsste man diese Funktionen in C++ auch erst einmal selbst implementieren oder?

    In Python ist gibt es sowas auch nicht. Da muss man es auch selbst schreiben oder eine Bibliothek nutzen (wie z.B. numpy oder scipy.linalg). (zu Fortran kann ich nichts sagen)



  • @wob sagte in abgeleitete Objekte dynamisch in einen Vector speichern:

    @mawashi sagte in abgeleitete Objekte dynamisch in einen Vector speichern:

    @Finnegan : Ja genau, das wäre natürlich ein Traum, wenn man diese Vektor-/Matrixarithmetik so formulieren könnte wie man es händisch aufschreibt.

    Schau dir vielleicht mal eine der LinAlg-Bibliotheken an, z.B. Eigen oder blaze oder oder...

    Meines wissens sind die "grossen " Libs für Lineare Algebra auf riesige Matrizen dynamischer Größe ausgelegt, die vergleichbar mit einem std::vector im Speicher abgelegt sind. Für geometrische Probleme in 2D/3D braucht man aber meist nur Vektoren mit 2-4 Elementen und fast nur quadratische Matrizen in ebenfalls diesen Dimensionen. Diese Bibliotheken bringen daher für diesen Anwendungsfall wahrscheinlich eine Menge Performance-Overhead mit. Ich kann mir nicht vorstellen, dass Algorithmen, die für 1000x1000-Matrizen auf dem Heap optimiert sind, ihre Vorteile voll ausspielen können, wenn man die nur auf 4x4-Matrizen loslässt, die man mit nur einer handvoll SIMD-Operationen multiplizieren könnte.

    Ich würde eine simplere Bibliothek mit flachen Vektoren und Matrizen (wie ein std::array) empfehlen. Ich kenne da nur GLM (ich selbst verwende für sowas eine eigene Implementierung). Das ist wahrscheinlich erstmal der bessere Ansatz, falls die "grossen" Libs das nicht auch als Extra unterstützen (denke aber eher nicht).

    Eigen oder Blaze würde ich dann eventuell zusätzlich mit reinnehmen, wenn geometrische Probleme z.B. die Lösung von großen Gleichungssystemen erfordern oder die riesigen Matrizen anderweitig Sinn machen (z.B. wenn man auf einem Mesh in Adjazenzmatrix-Repräsentation arbeiten will).

    TL;DR: BLAS et al. sind gut, aber für jedes Problem das passende Werkzeug. Für die hier im Thread geschilderten Probleme eher GLM, für weitere dann eventuell BLAS und Verwandte - wenn gerechtfertigt.



  • @Finnegan Eigen behauptet zumindest, dass hinreichend kleine Vektoren / Matritzen intern einfache Arrays seien.

    When should one use fixed sizes (e.g. Matrix4f), and when should one prefer dynamic sizes (e.g. MatrixXf)? The simple answer is: use fixed sizes for very small sizes where you can, and use dynamic sizes for larger sizes or where you have to. For small sizes, especially for sizes smaller than (roughly) 16, using fixed sizes is hugely beneficial to performance, as it allows Eigen to avoid dynamic memory allocation and to unroll loops. Internally, a fixed-size Eigen matrix is just a plain array,



  • @Finnegan sagte in abgeleitete Objekte dynamisch in einen Vector speichern:

    Meines wissens sind die "grossen " Libs für Lineare Algebra auf riesige Matrizen dynamischer Größe ausgelegt

    Natürlich, aber selbstverständlich auch für kleine!

    Beispiel Eigen http://eigen.tuxfamily.org/dox/classEigen_1_1Matrix.html#fixedsize

    Fixed-size versus dynamic-size:
    Fixed-size means that the numbers of rows and columns are known are compile-time. In this case, Eigen allocates the array of coefficients as a fixed-size array, as a class member. This makes sense for very small matrices, typically up to 4x4, sometimes up to 16x16. Larger matrices should be declared as dynamic-size even if one happens to know their size at compile-time.

    Dynamic-size means that the numbers of rows or columns are not necessarily known at compile-time. In this case they are runtime variables, and the array of coefficients is allocated dynamically on the heap.

    Beispiel Blaze: https://bitbucket.org/blaze-lib/blaze/wiki/Vector Types

    The blaze::StaticVector class template is the representation of a fixed size vector with statically allocated elements of arbitrary type.



  • @wob @Schlangenmensch Jo cool. Das war mir nicht bekannt. Schon was länger her dass ich mir diese Bibliotheken angesehen habe, und das waren auch nicht Eigen und Blaze.

    In dem Fall gibts natürlich nichts daran auszusetzen, außer dass die Bibliothek je nach Anwendung eventuell etwas Overkill ist. Wichtig ist jedenfalls, dass nicht bei jeder kleinen Matrix eine zusätzliche Pointer-Indirektion stattfindet und man nicht die Algorithmen für die fetten Matrizen auf den Kleinen laufen lässt. Das sind schon andere Optimierungen die man hier braucht.



  • Danke noch einmal für die Hinweise.
    @wob : Du hast Recht was Python angeht. Ich hatte vergessen, dass die oft verwendete numpy-Bibliothek eben eine Bibliothek ist und nicht intrinsisch mitgeliefert wird.

    Eigentlich dachte ich, dass ich mein Programm jetzt fertig habe. Leider klappt der (nun hoffentlich) letzte Schritt nicht... war ja auch nicht anders zu erwarten.
    Ich möchte nämlich ein Objekt rve vom Typ RVE<2> an eine Funktion übergeben, die dann prüft ob Punkte innerhalb oder außerhalb der in rve liegen. Diese Punkte sind nur in dieser test_function verfügbar. Mein Versuch lautet dann wie folgt:

    #include <iostream>
    
    #include <numeric>
    #include <vector>
    #include <array>
    #include <memory>
    
    #include <math.h>
    // HEADER
    template<unsigned int spacedim>
    class AbstractShape{
        public:
            std::array<double,spacedim> m;
            AbstractShape(std::array<double,spacedim> m); 
            // use virtual to make this function overloadable for every derived class/object
            // use keyword const to declare the return value not to be altered
            // use &-sign to give back a reference (avoid copying)
            // use keyword const at the end to indicate that this function cannot change a member variable of the class
            //     and makes it a compiler error if it would do so
            virtual const bool inside(const std::array<double,spacedim>& x) const = 0; 
            virtual const std::string& getType() const = 0;
            // use deconstructor to avoid problems connected to destroyed classes with pointers to the base class
            virtual ~AbstractShape() = default;
    };
    
    template<unsigned int spacedim>
    class Circle : public AbstractShape<spacedim>{
        public:
            double r;
            Circle(std::array<double,spacedim> m,double r);
            // use keyword override to tell the compiler that we excpect to override a virtual method 
            // (this is just a safety feature that throws a compiler error if we are not overriding a virtual method at that stage)
            const std::string& getType() const override;
            const bool inside(const std::array<double,spacedim>& x) const override;
    };
    
    template<unsigned int spacedim>
    class RVE{ // representative volume element with shapes
        public:
            std::vector<std::unique_ptr<AbstractShape<spacedim>>> shapes;
            void add_shape( std::unique_ptr<AbstractShape<spacedim>> shape );
            // const std::vector<unsigned int> indicator_arr( const std::array<double,spacedim>&  x ) const;
            const bool indicator_bool( const std::array<double,spacedim>&  x) const;
        private:
            
    };
    
    // DEFINITIONS
    template<unsigned int spacedim>
    AbstractShape<spacedim>::AbstractShape(std::array<double,spacedim> m){
        for(unsigned int i=0;i<spacedim;i++){
            this->m[i] = m[i];
        }
    };
    
    template<unsigned int spacedim>
    Circle<spacedim>::Circle(std::array<double,spacedim> m,double r):AbstractShape<spacedim>(m){
        this->r=r;
    };
    
    template<unsigned int spacedim>
    const std::string& Circle<spacedim>::getType() const{
        // use the keyword static to maintain this variable throughout the entire life of the objects life
        // Der wichtigere Punkt war, dass nicht jede Instanz diesen String hält, der eh immer gleich ist und nie geändert werden darf.
        static std::string s = "Circle";
        return s;
    };
    
    template<unsigned int spacedim>
    const bool Circle<spacedim>::inside(const std::array<double,spacedim>& x) const{
        bool inside_flag;
        inside_flag = (this->m[0]-x[0])*(this->m[0]-x[0]) 
                     +(this->m[1]-x[1])*(this->m[1]-x[1]) - this->r*this->r <0.;
        return inside_flag;
    };
    
    template <unsigned int spacedim>
    void RVE<spacedim>::add_shape( std::unique_ptr<AbstractShape<spacedim>> shape ){
        this->shapes.push_back( std::move(shape) );
    }
    
    template <unsigned int spacedim>
    const bool RVE<spacedim>::indicator_bool(const std::array<double, spacedim>& x) const
    {
        for (unsigned int i=0;i<this->shapes.size();i++){
            if (this->shapes[i]->inside(x)){
                return true;
            }
        }
        return false;
    };
    
    // MAIN-PROGRAMM
    template<int spacedim>
    void test_function( RVE<spacedim> rve ){
        std::cout << rve.shapes.size() << "\n";
    }
    
    int main(){
        const unsigned int dim=2;
        double r1 = 0.25;
        std::array<double,dim> m1{0.0,0.0};
        std::vector<std::vector<double>> points{ {-0.03,0.06}, 
                                                 { 0.5,0.45 } };
        RVE<dim> rve;
        rve.add_shape( std::make_unique<Circle<dim>>( std::array<double,dim>{0.0,0.0} ,r1) );
        rve.add_shape( std::make_unique<Circle<dim>>( std::array<double,dim>{0.1,0.1} ,r1) );
        test_function( rve );
    };
    

    Zeile 108 macht hier Probleme, wobei ich denke, dass das Problem in der ursprünglichen Funktionsdefinition liegt. Habt ihr Vorschläge?


Anmelden zum Antworten