vector von eigener Struktur - Linear im Speicher?



  • Hi,

    sorry, ich muss nochmal nachfragen.

    Wenn ich einen vector von einer eigenen Struktur habe, in der ich die einzelnen Elemente als Array abgelegt habe, kann ich dann davon ausgehen, dass die Daten linear im Speicher liegen?

    Hintergrund ist halt wieder, dass eine OpenGL-Funktion ein const float* erwartet. Ich habe aber einen vector von Matrizen. Da ich zur Laufzeit die Matrizen wegen Animationen komplett neu berechne, ändern sich die Werte ständig. Wenn ich jetzt durch alle Matrizen laufen würde und jedes Mal alle Werte auslesen und in ein normales Array/einen Vektor von floats speichern würde, hätte ich einen neuen Flaschenhals für meine Engine...

    Meine Idee wäre also:

    class Matrix
    {
    // random Methoden
    
    const float& getElement(std::size_t n) const {return m[n];}
    
    float m[12];
    };
    
    vector<Matrix> v;
    
    void openGlFunction(const float* values, unsigned int number);
    
    openGLFunction(&v.begin()->getElement(0), v.size() * 12);
    

    Ist gewährleistet, dass das klappt? Wenn nein, wie könnte ich das denn sonst alles organisieren? Alternative wäre ja nur, dass ich ein paar globalen Matrixberechnungsfunktionen jeweils Zeiger auf Speicherbereiche mitgebe. Aber das wäre ja nun alles andere als fehler-unanfällig und sowieso reichlich unschön. 😞

    Vielen Dank!



  • Eisflamme schrieb:

    Wenn ich einen vector von einer eigenen Struktur habe, in der ich die einzelnen Elemente als Array abgelegt habe, kann ich dann davon ausgehen, dass die Daten linear im Speicher liegen?

    Ja, das ist beim std::vector garantiert (Weshalb man in C++ eigentlich immer um Arrays herum kommt, gerade auch nachdem der TR1 auch noch die Klasse array für Arrays fixer Länge eingeführt hat).



  • Okay, und die Daten sind auch linear in der Klasse im Speicher, sodass ich davon ausgehen kann, dass m[0] immer direkt vor m[1] ist? Im letzten Thread zu nem ähnlichen Thema kam ja die Aussage auf, dass bei mehreren Attributen, die nicht durch ein Array verknüpft sind, das nicht unbedingt sicher gestellt ist. Bei Arrays an sich ist das ja auch klar, aber ... hm... Arrays in Klassen sollten vermutlich genau so funktionieren, richtig?

    OT: Hat Mal wer nen Link zum Standard oder so? Da solche Fragen hier oft durch Verweise darauf beantwortet werden (, müsste ich den Link bereits haben), sollte ich den glaube ich einfach Mal komplett durchlesen, um die Sprache so langsam Mal zu verstehen, oder?





  • Ja Moment, was nicht gesichert ist, ist, dass m[0] des zweiten Objektes direkt auf m[11] des ersten folgt (Stichwort Padding, und wenn da eine vtable drin ist, fliegt dir das ganz auseinander).

    Wenn es sich um POD handelt, befrag mal die Dokumentation deines Compilers nach packed structures. Gcc benutzt __attribute__((__packed__)), MSVC hat, wenn ich das richtig im Kopf habe, ein Pragma dafür. Wenn das Ding Laufzeitpolymorphie betreibt, wird dein Vorhaben so nicht funktionieren.



  • Cool, danke!

    Endlich eine Nachtlektüre 😋

    seldon:
    vtable gibt's doch nur, wenn ich virtual functions habe, oder? und dann ist sizeof(Matrix) ja auch != sizeof(12 * float), oder? Deswegen hatte ich ja diesen Zusatz drin, meine Matrix ist daher bewusst nicht vererbbar gemacht. Laufzeitpolymorphie für nicht-virtuelle Klassen? Und ich hab jetzt keine Ahnung von Alignment, aber wenn ich Vielfache von 4 Bytes habe, ist das dann nicht irgendwie ausgeschlossen? 😕

    #pragma pack(show) sagt 8. sizeof(float) * 12 ist ein Vielfaches von 8. Bin ich damit aus dem Schneider? Und wie kann ich das compilerübergreifend korrekt programmieren?



  • Ohne virtuelle Funktionen gibt's keine vtable, das ist richtig. Padding kann es aber trotzdem noch geben - wenn sizeof(Matrix) == sizeof(float * 12) ist, ist das bei dir wohl nicht der Fall, aber ich würde das mindestens irgendwo in den Testcases prüfen.

    Was du da betreibst, wird in der Praxis womöglich funktionieren, Garantien dafür sind aber schwer aufzutreiben. Wenn du diesen Weg gehen willst, wäre es sinnvoll, sich ein paar Compilezeitprüfungen für Voraussetzungen zu basteln. Etwa

    template<std::size_t N1, std::size_t N2> struct assert_equal_size         { };
    template<std::size_t N1>                 struct assert_equal_size<N1, N1> { typedef int type; };
    
    typedef assert_equal_size<sizeof(float) * 12, sizeof(Matrix)>::type matrix_layout_is_alright_t;
    


  • Genial, musste drei Mal hinschauen, um das zu verstehen.

    Und... jetzt ganz doofe Frage... wie nutz ich das dann? #ifdef ist ja nur für den Präprozessor, passt also nicht. Oder soll ich einfach irgendwo, wo ich die Matrix nutze, "matrix_layout_is_alright_t;" schreiben, weil das im Falle der Nichtdefinition einen "ist nicht definiert"-Fehler ergibt? Schön wär ja, wenn ich das wirklich mit einem assert verbinden könnte.



  • Solche Compile Time Assertions wurden von Alexandrescu eingeführt. Du kannst dafür Boost StaticAssert benutzen, einen Compiler, der dieses C++0x-Feature schon unterstützt (z. B. VS 2010 oder gcc) oder schnell selbst schreiben (dafür sind die Fehlermeldungen nicht so schön):

    template<bool>
    struct static_assert;
    
    template<>
    struct static_assert<true> {};
    
    class Matrix
    {
    	float foo[12];
    };
    
    static_assert<sizeof(Matrix) == 12 * sizeof(float)> static_assert_no_padding_in_class_matrix;
    

    Edit: Es wird sogar schon im Forum gehighlightet 😃



  • Ich will einen Herzsmiley, mehr wollte ich nämlich nicht schreiben.

    Danke 🙂



  • seldon schrieb:

    Ohne virtuelle Funktionen gibt's keine vtable, das ist richtig. Padding kann es aber trotzdem noch geben - wenn sizeof(Matrix) == sizeof(float * 12) ist, ist das bei dir wohl nicht der Fall, aber ich würde das mindestens irgendwo in den Testcases prüfen.

    Was du da betreibst, wird in der Praxis womöglich funktionieren, Garantien dafür sind aber schwer aufzutreiben. Wenn du diesen Weg gehen willst, wäre es sinnvoll, sich ein paar Compilezeitprüfungen für Voraussetzungen zu basteln. Etwa

    template<std::size_t N1, std::size_t N2> struct assert_equal_size         { };
    template<std::size_t N1>                 struct assert_equal_size<N1, N1> { typedef int type; };
    
    typedef assert_equal_size<sizeof(float) * 12, sizeof(Matrix)>::type matrix_layout_is_alright_t;
    

    Ok, ich habs gerade ausprobiert und es geht 😉 Was mich interessiert ist wieso. Ich ruf doch das assert_equal_size immer mit zwei parametern auf. Wie macht er die Unterscheidung zwischen zwei gleichen parametern wie z.B. <8, 8> und <7, 8>? wieso nimmt er bei einem template "aufruf" mit zwei paramtern (aber den gleichen) das template mit nur einem parameter?!



  • Wenn zwei Templates gleich gut passen, wird das spezialisiertere genommen.



  • Wenn eins davon spezialisierter ist, dann passen sie ja nimmer gleich gut 😃

    BTW @Eisflamme: lies mal in Standard bezüglich "standard layout class" nach (unter "9 Classes").
    Halte die Bedingungen für eine "standard layout class" ein, und es ist garantiert dass deine Matrix so im Speicher liegt wie du es brauchst.



  • hustbaer schrieb:

    Wenn eins davon spezialisierter ist, dann passen sie ja nimmer gleich gut 😃

    :p


Anmelden zum Antworten