Dynarray Implementation



  • Wie vielleicht schon aus meinem anderen Thread hervorgegangen ist, hab' ich zum Spaß gestern Abend (und ein wenig heute Morgen) eine Dynarray Implementation geschrieben.

    Zur Einleitung: Was ist ein Dynarray?

    Es gab mal ein Proposal für TR2.

    Darin geht es um einen neuen sequentiellen STL-Container: Ein Mittelding zwischen std::array und std::vector .
    Dynarray hat eine feste, allerdings zur Laufzeit festgelegte Größe.
    Das ist praktisch, wenn man ein Array fester Größe haben möchte, diese aber erst zur Laufzeit weiß.

    Es ist eigentlich das schöne Äquivalent zu

    int* array = new int[size];
    

    bzw. mit VLAs

    int array[size];
    

    Stattdessen schreibt man jetzt:

    Dynarray array(size);
    

    Und hat die üblichen Vorteile von STL-Containern.

    Hier der Code auf GitHub: https://github.com/bingojamigo/dynarray/blob/master/Dynarray.hxx

    Her mit der Kritik :xmas1:

    Danke im Voraus,
    MfG :xmas2:



  • Hab den Code nur kurz überflogen, da ist sicher noch mehr krumm.
    1. Ich vermisse das swap
    2. Warum hältst du die Daten nicht in einem unique_ptr<T[]> ?
    3. Du weisst schon, dass throw(std::out_of_range) veraltet ist?
    4. Deine noexcept sind etwas random verteilt



  • Der 2. Punkt entfällt, hab nicht genau geguckt.



  • iksepschensaif schrieb:

    Hab den Code nur kurz überflogen, da ist sicher noch mehr krumm.
    1. Ich vermisse das swap

    Korrigiert.

    3. Du weisst schon, dass throw(std::out_of_range) veraltet ist?

    Standard? Wo ist das deprecated?

    4. Deine noexcept sind etwas random verteilt

    Ist das so?



  • Huhu, habs auch nur überflogen. Kein Bock gerad 🙂

    0. swap fehlt.
    1. Copy operator: Mach copy-swap. Das sind drei (mit return :)) zeilen (nutzt den copy constructor) und ist exception safe (?)
    2. Move operator: Wer kümmert sich um die Rescourcen des temporären Objekts? Würde hier auch einfach swappen.

    Edit: das war blöd ausgedrückt. Ich meine die Resourcen des Objekts.



  • Ich wollte eigentlich den Copy-Assignment Operator optimieren, aber das hier klappt natürlich nicht:

    Dynarray& operator=(Dynarray const& d)
        {
            if(d.size() > size())
            {
                _clean();
                mArrayPtr = mAllocator.allocate(d.size());
                _constructWith(d.begin(), d.end());
            }
            else
            {
                if(d.size() < size())
                {
                    for(auto i = begin() + d.size();i != end();++i)
                        mAllocator.destroy(i);
                    std::cout << size() - d.size();
                    mAllocator.deallocate(begin() + d.size(), size() - d.size());
                }
                std::copy(d.begin(), d.end(), begin());
            }
            mSize = d.size();
        }
    

    Und das ganze mit logischer und echt allokierten Größe zu lösen ist Overbloat. Dann lieber so wie jetzt.

    ScottZhang schrieb:

    0. swap fehlt.

    Korrigiert 🙂

    ScottZhang schrieb:

    1. Copy operator: Mach copy-swap. Das sind drei (mit return :)) zeilen (nutzt den copy constructor) und ist exception safe (?)

    Ich denk drüber nach, hört sich eigentlich besser an als der Vierzeiler den ich jetzt hab'.

    ScottZhang schrieb:

    2. Move operator: Wer kümmert sich um die Rescourcen des temporären Objekts?

    Das muss kein temporäres Objekt sein. Du kannst alle Objekte moven.



  • ja das war doof ausgedrückt. ich würde trotzdem swappen.

    Copy operator:

    Dynarray& operator=(Dynarray const& d)
    {
      Dynarray d_copy(d);
      d_copy.swap(*this);
    
      return *this;
    }
    

    Move operator:

    Dynarray& operator=(Dynarray && d)
    {
      d.swap(*this);
      return *this;
    }
    

    Du siehst

    swap
    

    ist enorm wichtig 🙂



  • Andere Frage:

    auto i = begin();
            while(i != end())
                mAllocator.construct(i++, val);
    

    Was passiert wenn der 5. construct-Aufruf fehlschlägt? Wer kümmert sich darum, dass die anderen 4 Elemente ordnungsgemäss aufgeräumt werden?



  • Das Ding ist nicht Exceptionsicher.
    zB _constructWith()
    und empty() ist broken.



  • Shade Of Mine schrieb:

    Das Ding ist nicht Exceptionsicher.
    zB _constructWith()
    und empty() ist broken.

    Was ist der Vorteil gegenueber std::vector?

    Eigentlich "nur" Speicherbedarf oder?
    Was ist mit std::valarray?



  • ScottZhang schrieb:

    Shade Of Mine schrieb:

    Das Ding ist nicht Exceptionsicher.
    zB _constructWith()
    und empty() ist broken.

    Was ist der Vorteil gegenueber std::vector?

    Eigentlich "nur" Speicherbedarf oder?

    Nachdem ich kurz ueberlegt habe: ja.
    Aber das Problem hier ist, dass es schwer ist komplexe Objekte reinzupacken - weil es keinen uninitialisierten Speicher gibt.
    Alles in allem gefaellt mir die Idee nicht so besondern.

    Aber es ist natuerlich eine nette Uebung.



  • Das Ding ist nicht Exceptionsicher.

    Das ist ein großer Fehler, du hast Recht.

    und empty() ist broken.

    Korrigiert.

    ScottZhang schrieb:

    ja das war doof ausgedrückt. ich würde trotzdem swappen.

    Copy operator:

    Dynarray& operator=(Dynarray const& d)
    {
      Dynarray d_copy(d);
      d_copy.swap(*this);
    
      return *this;
    }
    

    Move operator:

    Dynarray& operator=(Dynarray && d)
    {
      d.swap(*this);
      return *this;
    }
    

    Du siehst

    swap
    

    ist enorm wichtig 🙂

    Hmm, ja. 👍

    Bin dran.



  • Shade Of Mine schrieb:

    Aber es ist natuerlich eine nette Uebung.

    Darum gehts. Übung für Eception-Safety und Move-Semantics.

    Allerdings finde ich die Idee gut, im Gegensatz zu dir...



  • Ich glaube, du räumst in der falschen Reihenfolge auf.

    Ich denke, der Sinn einer Standardisierung eines solchen Klassen-Templates liegt in der Optimierungsmöglichkeit, die sich ergibt, wenn Compilerhersteller und Bibliotheks-Autoren gut zusammenarbeiten und für kleine, Funktions-lokale Arrays den Speicher ggf vom Stack holen.



  • Welchen Sinn hat max_size()?
    Und warum gibt es kein non constant data()?

    PS:
    was ich gerne habe ist ein fixed_vector. Also ein vector der wachsen kann, intern aber einen fixen Speicher hat - den er nie reallokiert.



  • Ineffizienter Copyassignment-Operator. Und swap hat da nichts zu suchen.
    Hint: Zuerst schauen ob du Speicher recyclen kannst bevor du neuen allozierst.



  • krümelkacker schrieb:

    Ich glaube, du räumst in der falschen Reihenfolge auf.

    Warte 👍

    Shade Of Mine schrieb:

    Welchen Sinn hat max_size()?

    Gar keinen. Ist raus.



  • So, habe jetzt ein paar try-catch Blöcke hinzugefügt.

    Was die Reihenfolge angeht: da müsste ich nur die _destroy_all -Funktion ändern.



  • ScottZhang schrieb:

    Move operator:

    Dynarray& operator=(Dynarray && d)
    {
      d.swap(*this);
      return *this;
    }
    

    Ähm, nö, so nicht. Das lässt ggf die Objekte, die vorher logisch in *this wohnten zu lange am Leben. Wenn es ein normaler Vector wär, würde man das so machen:

    Dynarray& operator=(Dynarray && d)
    {
      d.swap(*this);
      d.clear();
      return *this;
    }
    

    Im dynarray-Proposal taucht Move-Semantik nicht auf und das ist schätzungsweise Absicht.



  • Im dynarray-Proposal taucht Move-Semantik nicht auf und das ist schätzungsweise Absicht.

    Das Proposal ist von 2008.


Anmelden zum Antworten