C-Style Array zu std::array



  • Hi,

    gibt es eine effiziente Methode ein z.B. double[4] in ein std::array<double,4> zu konvertieren; elementweise kopieren funktioniert:

    int numel = mxGetNumberOfElements(mxarray);
    std::array<double,4> def;
    double buf[4];
    memcpy(buf, mxGetData(mxarray), mxGetElementSize(mxarray)*numel);
    
    for(int i=0; i < numel; i++) {
    	def[i] = buf[i];
    }
    

    Aber irgendwie habe ich das Gefühl, das müsste auch besser gehen;). Google konnte mir da nicht weiterhelfen, deshalb frage ich einmal bei den Profis nach.

    mfg,
    soad


  • Mod

    Du könntest ein copy benutzen. Das könnte dann intern zu einen memcpy optimiert werden falls mit dem Elementtyp möglich und falls nicht, dann bleibt es eben bei der Schleife.



  • Kannst du nicht direkt ein std::array erstellen und dir die Werte dort reinspeichern lassen?

    In welchem Zusammenhang verwendest du das Array? Dein Code sagt darüber leider nicht viel aus.



  • Ah, danke euch!
    Ja, man kann mit

    memcpy(def.data(), mxGetData(mxarray), mxGetElementSize(mxarray)*numel);
    

    gleich direkt die Daten kopieren.

    mfg,
    soad

    edit:

    Nexus schrieb:

    In welchem Zusammenhang verwendest du das Array?

    Ich muss Matlab-Strukturen nach C++ parsen.



  • Es geht, wenn Referenzsemantik reicht und man bereit ist, jegliche ästhetischen Ansprüche aufzugeben, auch noch schneller (für lau).

    std::(tr1::)array ist POD und layout-kompatibel zu

    // layout-kompatibel zu std::tr1::array<double, 4>:
    
    struct foo {
      double elems[4];
    };
    

    Ferner ist in POD-Structs die Adresse des ersten Members eines Structs identisch mit der Adresse des Structs selbst. Effektiv bedeutet das: array<double, 4> ist layout-kompatibel zu double[4], und Type-Punning nach C-Manier kann betrieben werden.

    Es mögen (sollten) einem die Haare zu Berge stehen, wenn man es sieht, aber der Standard gibt es (denke ich) implizit her, Folgendes zu schreiben:

    double c_array[4];
    std::tr1::array<double, 4> &cpp_array = *static_cast<std::tr1::array<double, 4> *>(static_cast<void*>(c_array));
    

    Wenn dir dein Profiler nicht sagt, dass Array-Kopien ein Flaschenhals sind, würde ich davon allerdings absehen. Lohnt sich dieser Hack, so würde ich an geeigneter Stelle Compilezeit-Prüfungen durchführen, um auf Nummer sicher zu gehen. Wenn dein Compiler static_assert bereits kennt, wäre

    static_assert(sizeof(double[4]) == sizeof(std::array<double, 4>), "std::array sollte zu C-Arrays layout-kompatibel sein, ist es aber nicht");
    

    eine Möglichkeit. Ferner musst du dann (wie immer im Zusammenhang mit Type-Punning) aufpassen, dass du keine Probleme mit strict aliasing bekommst.


  • Mod

    seldon schrieb:

    std::(tr1::)array ist POD und layout-kompatibel zu

    // layout-kompatibel zu std::tr1::array<double, 4>:
    
    struct foo {
      double elems[4];
    };
    

    Das ist so nicht ganz korrekt. elems ist nicht Teil des Interfaces von std::array, und eine Implementation könnte theoretisch weitere Datenmember haben. Zudem ist std::array zumindest dann kein POD, wenn die Element keine PODs sind (bzw. in C++0x muss std::array für solche Späße Standardlayout haben, was auch rekursiv von den Elementen abhängt). Der Standard bestimmt nur, dass std::array ein Aggregat ist (und keinen explizit definierten Destruktor hat).
    Trotzdem ist Obiges nat. die offensichtliche Implementation, wenn man also auf Portabilität verzichten kann, kann man das sicher so tun.

    seldon schrieb:

    double c_array[4];
    std::tr1::array<double, 4> &cpp_array = *static_cast<std::tr1::array<double, 4> *>(static_cast<void*>(c_array));
    

    Das ist zwar bloß meine persönliche Meinung, aber für solche Zwecke gibt es Referenzcasts, komisch dass niemand sie zu verwenden scheint. Liegt evtl. daran, dass normalerweise nur C-Programmierer so etwas verwenden..., mal abgesehen davon ist ein doppelter static_cast auch sowas wie zwei halbe Lügen.

    std::tr1::array<double, 4>& cpp_array = reinterpret_cast<std::tr1::array<double, 4>&>(c_array);
    

    seldon schrieb:

    static_assert(sizeof(double[4]) == sizeof(std::array<double, 4>), "std::array sollte zu C-Arrays layout-kompatibel sein, ist es aber nicht");
    

    Theoretisch könnte ein std::array am Ende noch Padding besitzen, allerdings dürfte das trotzdem ein gutes Kriterium sein.


  • Mod

    soad schrieb:

    Ah, danke euch!
    Ja, man kann mit

    memcpy(def.data(), mxGetData(mxarray), mxGetElementSize(mxarray)*numel);
    

    gleich direkt die Daten kopieren.

    Ich habe nicht umsonst gesagt, du sollst copy verwenden, nicht memcpy. Das was du da hast geht gründlich schief bei nicht-POD Typen.



  • SeppJ schrieb:

    Ich habe nicht umsonst gesagt, du sollst copy verwenden, nicht memcpy. Das was du da hast geht gründlich schief bei nicht-POD Typen.

    Ok danke, d.h. solange ich das std::array nur mit PODs fülle sollte der Ansatz ok sein, bei nicht-POD Typen sollte copy verwendet werden, wenn ich das richtig verstanden habe.


  • Mod

    Warum willst du nicht immer copy benutzen? Nur weil memcpy hier mal funktio0niert ist das trotzdem hochgradig gefährlich. Wenn du den Typ änderst und nicht dran denkst (weil vielleicht das memcpy irgendwo ganz tief im Code versteckt ist, oder der Typ wird von jemand anderem geändert, oder du hast es einfach vergessen, oder...), dann geht es schief. Und du bekommst noch nicht einmal eine Compilerwarnung und der Debugger springt zu irgendeiner Stelle viel später, weil UB eben nicht unbedingt sofort abstürzt. Und dann haste den Salat.



  • Außerdem verwendet copy() im Normalfall immer memcpy, wenn möglich.



  • Heißt das, um memcopy sollte man grundsätzlich einen Bogen machen?



  • Genau das.



  • Ok, danke euch! Wieder was gelernt :)!


Log in to reply