initialisierung verschachtelter structs



  • Hallo Forum,

    folgendes Minimalbeispiel:

    struct TableEntry  {
      int x;
      int y;
    };
    
    struct Table {
      TableEntry data[];     // table data
    };
    
    Table foo = {
    	{5,   23 },
    	{11,   42 }
    };
    

    funktioniert leider nicht (gcc liefert "to many initializers for 'Table'").
    Wie ist das vom Standard vorgesehen?
    Ich möchte die Anzahl von Elementen in data[] in der Deklaration von Table zunächst unbestimmt lassen, da ich ja noch andere "Objekte" vom typ table anlegen möchte, mit veränderlicher Anzahl von TableEntry - Einträgen.
    Natürlich initialisiere ich alle Table´s zur Compilezeit.

    Danke euch schonmal

    (ich weiß natürlich, dass man auch STL-Container nehmen könnte aber darauf wird in meinem Fall bewusst verzichtet (u.a. Performancegründe))



  • struct Table { 
      TableEntry data[];     // table data 
    };
    

    Das ist kein gültiges C++, bei Array-Definitionen musst du die Dimension angeben. Aber an deiner Stelle würde ich sowieso Konstruktoren schreiben, die kannst du auch leicht verschachtelt aufrufen.

    wurfholz schrieb:

    (ich weiß natürlich, dass man auch STL-Container nehmen könnte aber darauf wird in meinem Fall bewusst verzichtet (u.a. Performancegründe))

    Performancegründe - du bist dir also sicher, dass Klassen langsamer sein müssen? Sowas solltest du erst sagen, nachdem du es genau nachgemessen hast und es für dein Problem wirklich eine Rolle spielt. Ausserdem gibts immer noch std::tr1::array , ein reiner Array-Wrapper, der bei einem brauchbaren Compiler garantiert keinen Overhead hat.



  • Hier hast du zwei meiner Meinung nach elegante Lösungen, um dynamisch Einträge anzuhängen. Der Einfachheit halber habe ich alle Funktionen inline definiert.

    struct TableEntry
    {
        int x;
        int y;
    
        TableEntry(int x, int y)
        : x(x), y(y)
        {
        }
    };
    
    class Table
    {
        private:
            std::vector<TableEntry> MyEntries;
    
        public:
            Table& operator<< (const TableEntry& NewEntry)
            {
                MyEntries.push_back(NewEntry);
                return *this;
            }
    
            Table& operator() (int x, int y)
            {
                MyEntries.push_back(TableEntry(x, y));
                return *this;
            }
    };
    

    Anwendung:

    int main()
    {
        Table Tab;
    
        // Möglichkeit mit operator<<
        Tab << TableEntry(3, 4)
            << TableEntry(5, 2);
    
        // Möglichkeit mit operator()
        Tab(3, 4)(5, 2);
    }
    


  • Danke für eure Hilfe soweit,

    ich bin mir natürlich bewusst, dass es schön und elegant und immernoch (relativ) performant ist, mit den STL Containern zu arbeiten aber ich bin hier in einer embedded Realtime Umgebung mit ein paar Fakten:

    1. Gerade diese Tables müssen wirklich performant sein (und ja, wir haben das schon mit profilern u.A. (Oszis :-)) nachgemessen..)
    2. Derzeit haben wir (noch) keine STL zur Verfügung
    3. Der obige Code hat mit einem älteren Compiler problemlos (und performant) funktioniert - und wurde dementsprechend eingesetzt.

    Ich würde deshalb gerne zunächst den obigen Code sinngemäß wieder zum laufen bringen, und anschließend schauen, ob mit dem neuen Compiler evtl. die STL wieder denkbar wäre.

    Hoffe ihr versteht meine Problematik..

    (PS: Sämtliche Table Einträge sind bereits zur Compilezeit bekannt und ändern sich auch zur Laufzeit nicht mehr.)



  • wurfholz schrieb:

    (PS: Sämtliche Table Einträge sind bereits zur Compilezeit bekannt und ändern sich auch zur Laufzeit nicht mehr.)

    Ok, das ist der Vorteil, sonst müsstest du wieder mit new arbeiten und wärst kaum noch schneller als die STL.

    Ich würde Templates empfehlen. Um die Initialisierungsliste zu nutzen, musst du bei PODs bleiben. So könnte das aussehen:

    struct TableEntry
    {
        int x;
        int y;
    };
    
    template <unsigned int Size>
    struct Table
    {
    	TableEntry MyEntries[Size];
    };
    
    int main()
    {
        Table<3> Tab =
        {
    		2, 3, 
    		5, 7, 
    		1, 4,
    	};
    }
    

  • Mod

    Wieso überhaupt das Ganze in eine Klasse packen? Was spricht gegen

    TableEntry foo[] = {
        {5,   23 },
        {11,   42 }
    };
    

    ?



  • @Camper
    Ja du hast recht aber: ich habe den Beispielcode etwas zu sehr abgespeckt. eigentlich ist in der Table struct noch ein element size enthalten, was die anzahl der Tabelleneinträge enthält, etwa so:

    struct Table {
    int size;  
    TableEntry data[];     // table data
    };
    

    und natürlich brauche ich dieses element später auch, da es die einzige Information über die Anzahl der Entrys pro Table enthält.

    @Nexus
    Hm, dass man Templates so auch (in Structs) verwenden konnte wusste ich nicht - Habe mich bisher auch erfolgreich um Templates gedrückt ;). Werde ich morgen mal ausprobieren, sieht recht elegant aus. Das könnte genau die Mischung aus elegantem Code und maximaler Performance sein, die ich suche.

    Vielen Dank schonmal für eure Ideen.

    Dennoch bleibt eine gewisse Unsicherheit, warum der eine Compiler den ursprünglichen Code akzeptiert und der andere nicht. Ist der Standard an dieser Stelle etwa undefiniert oder haben alle Compilerbauer mal wieder ihr eigenes Süppchen gekocht?


  • Mod

    wurfholz schrieb:

    und natürlich brauche ich dieses element später auch, da es die einzige Information über die Anzahl der Entrys pro Table enthält.

    Das weiß Table auch selbst.

    sizeof Table / sizeof *Table
    


  • Stimmt natürlich auch wieder. Leider gibt es da noch ein Element, nämlich den Namen der aktuellen Tabelle als const char*.
    Den bräuchte ich auch noch. Ansonsten wäre es natürlich mit nem Array von Entrys auch getan.



  • ok, habe die Template methode implementiert, stoße aber auf neue Probleme:

    Benutze in einer Klasse die Tables z.B. in der folgenden Art und Weise:

    void AddTable (const Table * pTable);
    

    Mit der Implementierung als Template müsste ich dann diese Methode ja folgendermaßen deklarieren

    void AddTable (const Table<42> * pTable);
    

    was ja offensichtlich Unsinn ist, da ich die Anzahl der Elemente ja an dieser Stelle nicht kenne. Gibts da ne Möglichkeit? Ansonsten gefällt mir die Template-methode ja ganz gut.



  • Funktionstemplate.

    template <unsigned int Size>
    void AddTable (const Table<Size>* pTable);
    

    Allerdings solltest du vielleicht mit der Zeit auch den Code-Bloat bedenken, wenn du in einer kritischen Umgebung arbeitest. Für jeden unterschiedlichen Templateparameter Size wird eine eigene Funktion AddTable erstellt, bzw. eine eigene Klasse Table . Ich weiss nicht, ob Codegrösse bei dir ein Problem darstellt, aber in dieser Hinsicht ist campers Vorschlag mit dem Array schon nicht ganz daneben; manchmal ist es besser, man programmiert direkt in C. Den const char* müsstest du dann eben zusätzlich speichern.



  • Ja, das ist der Grund warum ich mich vor Templates bisher immer gedrückt habe.
    Ich glaube mittlerweile auch, dass ich es auf die altmodische C-Art machen sollte.

    Dennoch Vielen Dank für eure Hilfe


  • Mod

    Es gibt noch die Möglichkeit, beide Ansätze zu kombinieren:

    struct struct TableEntry  {
      int x;
      int y;
    };
    
    template <unsigned int Size>
    struct Table_
    {
        const char* name;
        unsigned size;
        TableEntry MyEntries[Size];
    };
    
    typedef Table_<1> Table;
    
    union
    {
        Table_<2> table_;
        Table table;
    } foo = { "foo", 2, { { 5,   23 }, { 11,   42 } } };
    

    Das funktioniert, da wir es mit PODs zu tun haben, und wir uns daher auf die sog. "common initial sequence" verlassen können. Im Beispiel existiert foo.table_, damit wir bequem per {} initialisieren können und genügend Speicher reserviert wird. foo.table kann dann benutzt werden, um darauf wie bisher zuzugreifen.


Anmelden zum Antworten