template in Klassen



  • Ich habe eine Klasse, der verschiedene structs mit Werten übergeben werden soll. Da ich aber vorher nicht weiß, welche struct, wollte ich template nehmen.

    class G3DObject
    {
    public:
    
       template<typename Object>
       G3DObject( const Object& obj, double length = 1.0 ) : side_length( length )
       {
           createObject( obj );
       }
    
       void printObject() const;
    
    
    private:
    
       double side_length = 0;
    
       struct Point
       {
           int x = 0;
           int y = 0;
           int z = 0;
       } point;
       std::vector<Point> points;
    
       struct FloatPoint
       {
           double x = 0;
           double y = 0;
           double z = 0;
       } fpoint;
       std::vector<FloatPoint> fpoints;
    
       struct Edge
       {
           std::size_t a = 0;
           std::size_t b = 0;
       } edge;
       std::vector<Edge> edges;
    
       template<typename Object>
       void createObject( const Object& );
    };
    
    template<typename Object>
    inline void G3DObject::createObject( const Object& obj )
    {
       for ( std::size_t i = 0; i < obj.vc; ++i )
       {
           point.x = obj.x[ i ];
           point.y = obj.y[ i ];
           point.z = obj.z[ i ];
           points.push_back( point );
           fpoint.x = point.x * side_length;
           fpoint.y = point.y * side_length;
           fpoint.z = point.z * side_length;
           fpoints.push_back( fpoint );
       }
       for ( std::size_t i = 0; i < obj.edges; ++i )
       {
           edge.a = obj.a[ i ];
           edge.b = obj.b[ i ];
           edges.push_back( edge );
       }
    }
    
    inline void G3DObject::printObject() const
    {
       for ( const auto& fpoint : fpoints )
       {
           std::cout << "x: " << fpoint.x << "  y: " << fpoint.y << " z: " << fpoint.z << '\n';
       }
    }
    
    struct _Line
    {
       std::size_t vc = 2;
       std::vector<int> x                 = {  -1,  1 };
       std::vector<int> y                 = {   0,  0 };
       std::vector<int> z                 = {   0,  0 };
    
       std::size_t edges = 1;
       std::vector<std::size_t> a         = {  0 };
       std::vector<std::size_t> b         = {  1 };
    };
    

    Das Erzeugen eines Objekt funktioniert scheinbar noch, bei printObject() dann aber nicht mehr

    G3DObject line( _Line );
    line.printObject();  //hier Fehlermeldung
    ready();
    

    //|error: request for member 'printObject' in 'line', which is of non-class type 'G3DObject(_Line)'|
    Wahrscheinlich weil die Klasse das Objekt nicht kennt, aber ich weiß nicht, wie ich es sonst machen soll?
    Außerdem funktioniert die vordefinierte length nicht, wenn ich tatsächlich einen Wert übergebe. Kann aber sein, weil die ganze Sache nicht klappt.


  • Mod

    @lemon03 Du deklarierst kein Objekt, sondern eine Funktion mit Parametertyp _Line. Google mal "most vexing parse". Ich bin mir nicht sicher, was Du eigentlich schreiben wolltest; ein Objekt kann nicht mit einem Typen initialisiert werden.

    Darüberhinaus ist der Bezeichner _Line reserviert (beginnt mit einem Unterstrich, gefolgt von einem Großbuchstaben), den solltest Du mal anpassen.



  • @columbo sagte in template in Klassen:

    Darüberhinaus ist der Bezeichner _Line reserviert (beginnt mit einem Unterstrich, gefolgt von einem Großbuchstaben), den solltest Du mal anpassen.

    Jepp, das sollte nur temporär sein, weil es noch die ursprünglichen structs gibt.

    Aber schon mal danke für das Stichwort 'most vexing parse'. Zumindest im wiki-Artikel taucht die selbe Fehlermeldung auf.



  • So ganz blicke ich nicht, wie mir das helfen soll. Deshalb habe ich mal auf deutsch geschaut und hänge an diesem Satz fest.
    "Werden hier statt den geschweiften Klammern runde Klammern verwendet, kommt es zu dem berühmt berüchtigten most vexing parse".

    Mir geht es aber nicht um Klammersetzung, der obige Code ist wahrscheinlich grundsätzlich falsch, ob ich nun runde oder geschweifte Klammern nehme.Ich möchte eine noch unbekannte Klasse übergeben. Oder wie mache ich diese bekannt?

    Ich hatte eigentlich die Vorstellung, das es eine korrekte Weise gibt, wie ich mein Anliegen umsetzen muss.



  • Du kannst dem Konstruktor keine Klasse (oder sonst irgendeinen Typen) übergeben. Du musst zuerst ein Objekt davon erzeugen:

    _Line lineStructObj;
    G3DObject line { lineStructObj };


  • Aha danke. Da sind dann auch die geschweiften Klammern. Das sieht natürlich schon ganz anders aus, als wenn man einen Typen übergibt.

    Ich wage gar nicht zu fragen ( 😉 ), aber kann ich das Objekt von _Line nicht schon irgendwo vorher erzeugen? Vielleicht in einer Zwischenklasse, die alle structs liest und daraus Objekte erzeugt und daraus dann enums macht, die ich dann übergebe?



  • @lemon03 sagte in template in Klassen:

    Ich wage gar nicht zu fragen ( ), aber kann ich das Objekt von _Line nicht schon irgendwo vorher erzeugen?

    Du kannst Instanzen von sonstwas deklarieren wo Du willst. An der Stelle an der sie einer Funktion übergeben werden sollen müssen sie halt sichtbar sein. (oder über eine Funktion "besorgt" werden können.)

    @lemon03 sagte in template in Klassen:

    Vielleicht in einer Zwischenklasse, die alle structs liest und daraus Objekte erzeugt und daraus dann enums macht, die ich dann übergebe?

    Das ganze bitte nochmal und bitte auch für jemanden verständlich, der nicht ausgerechnet Du selbst bist.



  • @swordfish sagte in template in Klassen:

    Du kannst Instanzen von sonstwas deklarieren wo Du willst.

    Das ganze bitte nochmal und bitte auch für jemanden verständlich, der nicht ausgerechnet Du selbst bist.

    Sorry. Ich möchte eine Instanz von einer beliebigen struct in der obigen G3DObject-Klasse deklarieren.



  • @lemon03 sagte in template in Klassen:

    Sorry. Ich möchte eine Instanz von einer beliebigen struct in der obigen G3DObject-Klasse deklarieren.

    struct beliebige_struct { int foo; };
    
    template<typename T>
    class G3DObject
    {
        T instanz;
        // ...
    };
    
    // ...
    
    G3DObject<beliebige_struct> bar;
    // ...
    

    ?



  • Dein Fragezeichen irritiert mich, als ob Du nicht sicher bist, das dieser Vorschlag ist, was ich mir vorstelle. Das kann ich im Moment nicht sagen, weil ich ihn erst umsetzen müsste, leider fehlt mir im Moment die Zeit dazu. Später.

    Aber ich bedanke mich schon mal.



  • @lemon03 sagte in template in Klassen:

    als ob Du nicht sicher bist, das dieser Vorschlag ist, was ich mir vorstelle. Das kann ich im Moment nicht sagen, weil ich ihn erst umsetzen müsste

    Wenn du das kurze & einfache Stückchen Code nicht lesen und verstehen kannst, dann ist Üben angesagt.



  • Das verstehe ich. Zum Glück drehe ich keine Däumchen, sondern übe eben.

    Allerdings kann ich nicht nur den Code auf Anhieb verstehen, ich weiß auch nicht, wie ich beliebige_struct deuten soll? Klar, ist meine zitierte Bezeichnung, aber wenn es nun eine weitere struct namens noch_eine_beliebige_struct gibt, bin ich doch kein Stück weiter gekommen?

    Aber selbst wenn, kompilieren tuts eh nicht. Hat die obige Vorgehensweise einen bestimmten Namen, damit ich mich da besser informieren kann?



  • Oder halt stop!

    Bin ja schon weiter. Etwas weiter oben hat ja funktioniert. Der Rest ist nur Sahne, um eine händische Erzeugung der Objecte vor der Übergabe zu übergehen.

    Das kapiere ich vielleicht auch irgendwann mal, also belassen wir es dabei. Also ich werde es jedenfalls. Danke.



  • @lemon03 sagte in template in Klassen:

    Aber selbst wenn, kompilieren tuts eh nicht.

    wtf?? tut es wohl!?

    @lemon03 sagte in template in Klassen:

    Klar, ist meine zitierte Bezeichnung, aber wenn es nun eine weitere struct namens noch_eine_beliebige_struct gibt, bin ich doch kein Stück weiter gekommen?

    struct beliebige_struct { int foo; };
    struct noch_eine_beliebige_struct{ int baz; };
    
    template<typename T>
    class G3DObject
    {
        T instanz;
        // ...
    };
    
    // ...
    
    G3DObject<beliebige_struct> bar;
    G3DObject<noch_eine_beliebige_struct> qax;
    // ...
    

    oder, je nach dem

    struct beliebige_struct { int foo; };
    struct noch_eine_beliebige_struct{ int baz; };
    
    template<typename T, typename U>
    class G3DObject
    {
        T instanz_1;
        U instanz_2;
        // ...
    };
    
    // ...
    
    G3DObject<beliebige_struct, noch_eine_beliebige_struct> bar;
    

    aus deinen Beiträgen wird nicht wirklich klar, was es werden soll, wenns fertig ist. Oder, was Du mit solchen schwammigen Aussagen meinst:

    @lemon03 sagte in template in Klassen:

    Der Rest ist nur Sahne, um eine händische Erzeugung der Objecte vor der Übergabe zu übergehen.

    @lemon03 sagte in template in Klassen:

    Hat die obige Vorgehensweise einen bestimmten Namen, damit ich mich da besser informieren kann?

    Template Classes.



  • Danke, aber genau diese Aufzählung aller vorhandener structs wollte ich doch vermeiden. Ich kann doch nicht jedesmal, wenn eine neue struct dazu kommt, die Klasse umschreiben. Zwar hält sich die tatsächliche Anzahl der structs in Grenzen, aber ich wollte das möglichst flexibel.

    Es gibt diese Klasse G3DObject und vollkommen unabhängig davon einen header voll structs. Es kann eine sein oder hunderte, die Klasse selbst soll nicht mehr angefasst werden.

    Aber wie geschrieben, diese Sache ist schon gelöst. Zwar mit den Schönheitsflecken

    Rect_( rectObj ); //hier
    Triangle_( trObj ); //und hier
    G3DObject rect{ rectObj };
    G3DObject triangle{ trObj };
    rect.printObject();
    std::cout << '\n';
    triangle.printObject();
    ready();
    

    aber ansonsten wie gewünscht.

    PS: die schwammige Ausdrucksweise kommt wahrscheinlich daher, das ich keine Ahnung habe, wie solche Techniken bezeichnet werden und ich sie deshalb in Prosa umschreiben muss. Falls ich irgendwann ein ähnliches Problem haben sollte, habe ich dann immerhin schon das Stichwort 'Template Classes'. Davor hatte ich gar nichts.



  • Brauchst du denn die Objekte rectObj und trObj überhaupt außerhalb deiner Klasse?

    Außerdem finde ich die Klammern in beiden mit "hier" markierten Zeilen störend/verwirrend.



  • Jupp, sehe ich auch so. Fangen wir wieder von vorne an. Ich habe eine Klasse und einen header voller structs, die übergeben werden wollen.

    class G3DObject
    {
    public:
    
        template<typename Object>
        G3DObject( const Object& obj, double length = 1.0 ) : side_length( length )
        {
            createObject( obj );
        }
    
    
        void printObject() const;
    
    
    
    private:
    
        double side_length = 0;
    
        struct Point
        {
            int x = 0;
            int y = 0;
            int z = 0;
        } point;
        std::vector<Point> points;
    
        struct FloatPoint
        {
            double x = 0;
            double y = 0;
            double z = 0;
        } fpoint;
        std::vector<FloatPoint> fpoints;
    
        struct Edge
        {
            std::size_t a = 0;
            std::size_t b = 0;
        } edge;
        std::vector<Edge> edges;
    
        template<typename Object>
        void createObject( const Object& obj );
    };
    
    template<typename Object>
    inline void G3DObject::createObject( const Object& obj )
    {
        for ( std::size_t i = 0; i < obj.vc; ++i )
        {
            point.x = obj.x[ i ];
            point.y = obj.y[ i ];
            point.z = obj.z[ i ];
            points.push_back( point );
            fpoint.x = point.x * side_length;
            fpoint.y = point.y * side_length;
            fpoint.z = point.z * side_length;
            fpoints.push_back( fpoint );
        }
        for ( std::size_t i = 0; i < obj.edges; ++i )
        {
            edge.a = obj.a[ i ];
            edge.b = obj.b[ i ];
            edges.push_back( edge );
        }
    }
    
    inline void G3DObject::printObject() const
    {
        for ( const auto& fpoint : fpoints )
        {
            std::cout << "x: " << fpoint.x << "  y: " << fpoint.y << " z: " << fpoint.z << '\n';
        }
    }
    

    Wie mache ich das besser? Bitte mit einem minimal-kompilierbaren Beispiel.

    EDIT: Sorry, ich werde für einige Stunden abwesend sein. Bitte nicht wundern.



  • Wenn du nicht ständig neue structs für neue Formen machen willst, dann musst du eine struct machen, die eine Form beschreibt:

    • Ist die Form rund oder hat sie Ecken?
    • Wie viele Ecken hat die Form?
    • Zwischen welchen Ecken besteht eine Verbindung?
    • Wie ist die Verbindung zwischen den Ecken? Gerade Linie? Kurve?
      Die Daten kannst du dann dem Konstruktor übergeben und eine gewünschte Form erzeugen.

    Andere Möglichkeiten sehe ich nicht. Entweder du machst für unterschiedliche Formen mehrere structs, oder du machst eine struct und beschreibst eine Form.



  • Hallo, sorry, hat etwas länger gedauert, ist aber zum Glück nicht viel aufgelaufen. Will versuchen, die Fragen ebenso strukturiert zu beantworten.

    Ich will nicht ständig neue structs für neue Formen machen, sie existieren schon. Allerdings soll die Anzahl und auch die Bezeichnungen für die Klasse unbekannt sein. Erst bei der Übergabe ist klar, um welche struct, um welche Form es sich handelt. Und ich möchte, selbst wenn nur theoretisch, neue Formen hinzufügen können, ohne etwas an der Klasse zu ändern.

    • Die Formen bestehen aus Eckpunkten und Kanten.
    • Die maximale Anzahl der Ecken einer Form sind 360. Damit wird angenähert ein Kreis gebildet.
    • Alle zwei Ecken sind mit einer Kante verbunden. Es sind immer geschlossene Formen. Die Anzahl der Kanten sind Ecken -1.
    • Die Kanten sind gerade Linien zwischen zwei Punkten.

    Durch Deine abschließende Schlussfolgerung werde ich bestätigt, alles so sein zu lassen wie geplant.

    Alle bisher erstellten Formen können auch durch eine einzige Form, nämlich durch ein regelmäßiges Polygon erstellt werden, das durch die Anzahl der Eckpunkte bestimmt wird. Dies wurde schon implementiert.

    Daneben gibt es noch den Würfel und die Pyramide. Schön wäre es, auch diese Formen durch ein regelmäßiges Polyeder erstellen zu können. Habe aber noch keine Berechnungsgrundlage dafür. Habe auch noch nicht wirklich nachgeforscht. Danke.



  • Ich frage mich gerade mehrere Dinge:

    1. Sind Templates überhaupt das richtige Mittel der Wahl hier? Die Klassen, die du übergeben kannst, müssen ja mindestens mal die Member vc, x,y,z, edges,a und b haben. Man könnte auch über Polymorphie nachdenken und dieses in einer Shape-Klasse einbauen.
    2. Deine Methode createObject scheint mir eher sowas wie eine Kopie zu erzeugen? Ich bin mit dem Namen nicht einverstanden. Wäre sowas wie "initializeFromObject" oder "initializeFromShape" nicht besser? Bei "create" stelle ich mit immer vor, dass darin irgendwas erzeugt wird (und auch zurückgegeben wird), also à la auto obj = createObject();.
    3. Deine structs Point und FloatPoint sind dagegen ja mal klare Template-Kandidaten! Ich würde sie außerhalb von G3DObject deklarieren. Ich würde das dann Point3d<int> und Point3d<double> nennen. Zumal man bei deinem FloatPoint sich auch wundert, dass da keine floats, sondern doubles verwendet werden. Gut, double ist auch Fließkomma, aber wozu hier die Ambiguität einbauen? (abgesehen davon, dass es Point3d-Klassen bestimmt wie Sand am Meer gibt)

Anmelden zum Antworten