eure meinung: array-klasse



  • Ein ziemlicher Brocken für eine statische Array-Klasse jedenfalls 🙂 Was mir beim Drüberfliegen nur aufgefallen ist: "char value[]" sieht nicht sehr portabel aus und dein künstliches Lifetime-Management (construct etc.) scheint Exceptions aus Konstruktoren nicht standzuhalten. Hast du dir std::unitialized_fill mal angesehen?



  • operator void schrieb:

    Ein ziemlicher Brocken für eine statische Array-Klasse jedenfalls 🙂

    naja, das ding kann ja auch was 😉

    boosts array ist auch nicht viel kleiner (nur gibts dort halt kein copy-policy)

    Was mir beim Drüberfliegen nur aufgefallen ist: "char value[]" sieht nicht sehr portabel aus

    wieso denn nicht?
    bitte erklaeren.

    und dein künstliches Lifetime-Management (construct etc.) scheint Exceptions aus Konstruktoren nicht standzuhalten.

    danke 👍 habe ich jetzt verbessert.

    Hast du dir std::unitialized_fill mal angesehen?

    ja, aber wo sollte ich das verwenden koennen?



  • Copy::destructiveInit< (otherSize < Size ? otherSize : Size), value_type >(begin(), other.begin());
    

    ist gar häßlich.



  • volkard schrieb:

    Copy::destructiveInit< (otherSize < Size ? otherSize : Size), value_type >(begin(), other.begin());
    

    ist gar häßlich.

    da stimm ich dir zu, doch leider weiss ich nicht, wie man das schoener machen kann.

    uU ein makro my_min() definieren, aber das macht die ganze sache auch nicht schoener.

    std::min() ist ja ne inline funktion - da verliere ich damit die compile time konstante 😞

    ich bin sicher, dass du eine bessere idee hast - ich wuerde mich freuen diese zu erfahren. danke 👍



  • Shade Of Mine schrieb:

    operator void schrieb:

    Was mir beim Drüberfliegen nur aufgefallen ist: "char value[]" sieht nicht sehr portabel aus

    wieso denn nicht?
    bitte erklaeren.

    Wegen allerlei seltsamen Alignmentproblemen. Zumindest habe ich das mal als Begründung gegen solche Techniken auf einer Internetseite gelesen, Erfahrung habe ich da keine.

    Shade Of Mine schrieb:

    Hast du dir std::unitialized_fill mal angesehen?

    ja, aber wo sollte ich das verwenden koennen?

    Anstelle des zweiten constructs, hätte ich gedacht.



  • #ifndef ANGST_VOR_MAKROS
    #define MIN(a,b) Min<a,b>::value
    #endif
    


  • volkard schrieb:

    #ifndef ANGST_VOR_MAKROS
    #define MIN(a,b) Min<a,b>::value
    #endif
    

    *patsch* entweder bin ich blind und bloed, dass ich auf sowas nie komme, oder du bist n genie.

    danke!! 👍

    @operator void:
    kannst du auch was kongretes zum alignment problem posten?

    so wie ich unitialized_fill verstanden habe, wuerde es in meinem Fall
    T(T()); aufrufen, oder?
    denn ich will nur den default ctor aufrufen, also uebergebe ich unitialized_fill einfach nur T() und unitialized_fill ruft dann jeden ctor mit T() als param auf.

    wenn das nicht so ist, bitte aufklaeren.



  • #define ANGST_VOR_MAKROS
    

    🙂 😞



  • Shade Of Mine schrieb:

    @operator void:
    kannst du auch was kongretes zum alignment problem posten?

    http://www.gotw.ca/gotw/028.htm

    Daher hatte ich meine Bedenken wohl. Mir ist vorhin nur nicht eingefallen, wo ich das aufgeschnappt hatte.

    Shade Of Mine schrieb:

    so wie ich unitialized_fill verstanden habe, wuerde es in meinem Fall
    T(T()); aufrufen, oder?
    denn ich will nur den default ctor aufrufen, also uebergebe ich unitialized_fill einfach nur T() und unitialized_fill ruft dann jeden ctor mit T() als param auf.

    So wäre es in deiner ersten construct-Funktion. Mit unitialized_fill kannst du aber, so wie ich es wiederum verstanden habe, deine zweite construct-Funktion ersetzen (und sparst damit immerhin die Hälfte des Exception-Ärgers).



  • die array-klasse hat ein massives simplizitätsdefizit und bekommt von mir nur die note 3.



  • operator void schrieb:

    Shade Of Mine schrieb:

    @operator void:
    kannst du auch was kongretes zum alignment problem posten?

    http://www.gotw.ca/gotw/028.htm

    danke, aber so schlimm sehe ich das nicht.
    schliesslich gibt es keine andere moeglichkeit mein vorhaben anders zu implementieren.

    in Sutters Beispiel wird char[] ja auch missbraucht.

    So wäre es in deiner ersten construct-Funktion. Mit unitialized_fill kannst du aber, so wie ich es wiederum verstanden habe, deine zweite construct-Funktion ersetzen (und sparst damit immerhin die Hälfte des Exception-Ärgers).

    hab mir gerade uninitialized_fill angesehen:

    es ist so implementiert: (vereinfacht)

    template <class ForwardIter, class Type>
    void uninitialized_fill(ForwardIter first, ForwardIter last, const Tp& x)
    {
      ForwardIter cur = first;
      for(; cur != last; ++cur)
      {
        new (&*cur) Type(x);
      }
    }
    

    es wird also nicht der Default Ctor aufgerufen, sondern der copy ctor (fuer x==T())

    das macht die sache nicht unbedingt langsamer, aber es ist nicht das was ich will. es soll ja der default ctor aufgerufen werden 🙂



  • Ich glaube, wir reden aneinander vorbei 🙂

    Jo, die Version mit dem Defaultkonstruktor musst du dir selbst schreiben, aber ich wollte ja auch nur folgende Funktion aus deiner array.hpp durch uninitialized_fill ersetzen:

    template<typename TRG, typename SRC>
    static void construct(void* value, SRC src, std::size_t size)
    {
        char* p=static_cast<char*>(value);
        for(std::size_t i=0; i<size; ++i)
        {
            new(p+(i*sizeof(TRG)))TRG(*src);
            ++src;
        }
    }
    


  • @operator void:
    aso, jetzt kapier ich.

    ne, das geht nicht. denn bei uninitialized_fill kann man nur 1 wert angeben, ich moechte aber ein ganzes array verwenden.

    also brauch ich unitialized_copy 🙂
    danke! dank dir bin ich auf uninitalized_copy gestossen!!



  • Ups. Hab deine Funktion irgendwie falsch gelesen *patsch* Naja, jetzt ist ja alles klar 🙂



  • volkard schrieb:

    die array-klasse hat ein massives simplizitätsdefizit und bekommt von mir nur die note 3.

    😞

    dabei hat keine funktion mehr als 6 zeilen

    darf man mal deine static array klasse sehen? vielleicht kann ich mir da n paar tricks abschauen wie man das einfacher machen kann?

    denn ich bin zu unfaehig das ding zu vereinfachen 😞



  • lies einfach diese gotw artikel nicht...dann schreibst du auch nicht so freakige klassen 🤡



  • Shade Of Mine schrieb:

    darf man mal deine static array klasse sehen? vielleicht kann ich mir da n paar tricks abschauen wie man das einfacher machen kann?

    hab keine. aber ich les mir den code mal durch und meckere ein wenig rum.

    aber ich würde dran denken, den rohen speicher in ne klasse zu packen. sagen wir mal, sie hat platz für Size objekte vom typ Type, aber konstuiert und destruiert mal gar nix. macht aber arraygrenzencheck und adressarithmetik.

    die objekte sollen andersrum zerstört werden, als sie angelegt wurden.

    ich kann nicht nachvollziehen, warum construct nen void* kriegen tut.

    ClassCopy kommt mir sehr groß vor. werden wirklich alle diese funktionen gebraucht?

    template<typename otherType> Array(otherType const* value, size_type size)
    

    muß weg wegen is nicht. wenn du auf stl abfährst, dann haste die version mit zwei iteratoren.

    assert(size>Size);
    std::size_t length=std::min(size,Size);
    

    erscheint mir wirr. ich denke, nach assert(size>Size) ist die zweite zeile mindersinnvoll. ich schließe auf programmiererüberlastung weils nicht einfach genug ist.

    Copy::construct<value_type>(this->value, value, length);
    Copy::construct<value_type>(this->value+(length*sizeof(value_type)), Size-length);
    

    überlastet mich. ich lese immer nur construct. kein wunder, daß ich nicht kapiere, daß die obere zeile ein copyconstruct und die untere ein defaultconstruct macht.

    std::size_t length=std::min(end-beg,Size);
    

    warum denke ich an const?

    Copy::destructiveCopy< (otherSize < Size ? otherSize : Size) >(begin(), other.begin());
    

    oh, du nimmst begin() als parameter für Copy-sachen. warum? um zu postulieren, daß der iterator ein Type* ist? oder warum nicht bei

    Copy::construct<value_type>(this->value+(length*sizeof(value_type)), Size-length);
    

    ?

    wozu assign?

    wozu at?

    wozu data?

    *static_cast<TRG*>(p+(i*sizeof(TRG)))=src;
    

    und

    return *(((value_type*)value)+Size-1);
    

    wundert mich so vom stilbruch her.

    und mit

    return ((value_type*)value);
    

    machte komische klammern wie in c.

    reverse_iterator rbegin()
    {
        return reverse_iterator(begin());
    }
    

    kapier ich net.

    wozu empty? ohne push und pop kaum nutzbar.

    template<typename T1, std::size_t Size1, class Copy1, typename T2, std::size_t Size2, class Copy2>
        bool operator==(Array<T1, Size1, Copy1> const& a, Array<T2, Size2, Copy2> const& b)
        {
            if(Size1!=Size2) return false;
    
            return Copy::equal<Size1>(a.begin(), b.begin());
        }
    

    wolltest du da nicht zur compilezeit bei ungleichen größen false berechnen und nur bei gleichheit der größen zur laufzeit ne schleife benutzen? könnt ja sonst nen nutzlosen funktionsaufruf erzeugen.

    irgendwie gefällt mir

    template<typename T>
           static void construct(void* value, std::size_t size)
            {
                std::size_t i=0;
                try
                {
                    char* p=static_cast<char*>(value);
                    for(; i<size; ++i)
                    {
                        new(p+(i*sizeof(T)))T();
                    }
                }
                catch(...)
                {
                    destruct<T>(value,i);
                    throw;
                }
            }
    

    nicht.

    template<typename T>
            static void construct(T* begin,T* end)
            {
                T* pos=begin;
                try
                {
                    while(pos!=end)
                    {
                       new(pos)T;
                       ++pos;
                    }
                }
                catch(...)
                {
                    do
                    {
                       --pos;
                       pos->~T();
                    }while(pos!=begin);
                    throw;
                }
            }
    

    oder vielleicht

    template<typename T,std::size_t SIZE>
            static void construct(T* data)
            {
                std::size_t i=0;
                try
                {
                    while(i!=SIZE)
                    {
                       new(data+i)T;
                       ++i;
                    }
                }
                catch(...)
                {
                    do
                    {
                       --i;
                       (data+i)->~T();
                    }while(i!=0);
                    throw;
                }
            }
    


  • @volkard:

    danke!
    ich habe die array klasse mal diesbezueglich n bisschen ueberarbeitet.

    und heute nacht denke ich ueber die RawStorage Klasse nach - mir sind da einige dinge noch nicht ganz klar.

    aber danke fuer die verbesserungsvorschlaege.



  • Shade Of Mine schrieb:

    @volkard:
    danke!
    ich habe die array klasse mal diesbezueglich n bisschen ueberarbeitet.

    na, eigentlich egal. diese punkte sind nicht das, was mir ein ungutes gefühl macht.

    Shade Of Mine schrieb:

    und heute nacht denke ich ueber die RawStorage Klasse nach - mir sind da einige dinge noch nicht ganz klar.

    mach das mal.

    hab mal alten code von mir angeguckt. am häufigsten verwende ich wohl sowas hier:

    //21.04.00
    #ifndef VECTOR_H
    #define VECTOR_H
    #pragma once
    
    #include <assert.h>
    #include "tools/TestAtStartup.h"
    
    /*Einfache Vektor-Klasse mit Überprüfung der Indexgrenzen im Debug-Modus
    */
    template<class T,int SIZE>
    class Vector
    {
    private:
       T data[SIZE];
    public:
       const T &operator [](int index) const
       {
          assert(0<index && index<SIZE);
          return data[SIZE];
       }
       T &operator [](int index)
       {
          assert(0<index && index<SIZE);
          return data[SIZE];
       }
    };
    DECLARE_TESTATSTARTUP(Vector);
    
    #endif
    

    <anderes thema>
    die DECLARE_TESTATSTARTUP(Vector); sorgt dafür, daß ne in der Vector.cpp definierte Funktion testVector aufgerufen wird. dadrin wird irgendwie die ganze vectorklasse getestet, zum beispiel 1000 vectoren verschiedener größe sortieren und dann auf sortiertheit testen oder sowas. und allein durchs inkludieren der "Vector.h" wird dafür gesorgt, daß am programmstart das passiert. kostet zwar ne sekunde mehr bei programmstart (abschaltbar), aber man kann immer sicher sein, daß nicht wieder ne dumme kleine performanceoptimierung oder ein bugfix in der Vector.h nen fehler eingebaut hat und man sich im ftpserver nen wolf sucht. sag ich nur mal, da in nem anderen thread der begriff "automatisches testen" oder so gefallen ist.
    </anderes thema>

    und gerne auch mal ganz unwiederverwertbares, hier ein mutierter stack:

    //21.06.00
    #ifndef STACK_H
    #define STACK_H
    
    #include <xutility>//f³r std::swap
    
    template<class T,int COUNT>
    class Stack
    {
       private:
          T data[COUNT];
          int size;
       public:
          Stack()
          {
             size=0;
          }
          void push(const T &t)
          {
             data[size++]=t;
          }
          void moveToFront(const T &t)
          {
             for(int i=0;i<size;++i)
             {
                if(data[i]==t)
                {
                   while(i<size-1)
                   {
                                       std::swap(data[i],data[i+1]);
                      ++i;
                   }
                   return;
                }
             }
          }
          T &peek()
          {
             return data[size-1];
          }
          void pop()
          {
             --size;
          }
          bool isEmpty()
          {
             return size==0;
          }
          void flush()
          {
             size=0;
          }
    };
    
    #endif
    

    aber was ich eigentlich suchte, ist auf dem rechner nicht da. ich versuche, es mal so hinzuzappeln.

    template<typename T>
    class Stack
    {
       private:
       GenericStack<sizeof(T)> stack;
       public:
       void push(const T& t)
       {
          stack.push();
          new(stack.top())T(t);
       }
       void pop()
       {
          stack.top()->~T();
          stack.pop();
       }
       T& top()
       {
          return *static_cast<T*>(stack.top());
       }
       bool isEmpty()
       {
          return stack.isEmpty();
       }
    };
    

    sowas sollte man vermutlich auch für nen vector machen (sicher bin ich erst, wenns geklappt oder nicht geklappt hat). aber da haste noch ein wenig arbeit vor dir, die ganzen sachen sauber einzuteilen, was wohin gehört.
    aber das ist auch nur ein schritt von vielen. die Copy-Klasse ist mir zu groß. evtl sind es eher nur drei funktionen, nämlich constructOne, copyconstructOne und destructOne, die als policy definiert werden muß und Copy kann daraus sich den ganzen anderen kram basteln. oder vielleicht ist die gegenrichtung gut, daß man nen Vector und nen PodVector als zwei ganz getrennte klassen baut. uih, ist da viel spielraum und keiner verrät mir, was die allerbeste lösung ist.



  • volkard schrieb:

    reverse_iterator rbegin()
    {
        return reverse_iterator(begin());
    }
    

    kapier ich net.

    HTH

    namespace std
    {
        template<typename Iter>
        class reverse_iterator
        {
        public:
            typedef typename Iter::value_type value_type;
    
            reverse_iterator(Iter iter)
                :    iter_(i)
            {
            }
    
            reverse_iterator & operator++ ()
            {
                --iter_;
                return *this;
            }
    
            reverse_iterator & operator-- ()
            {
                ++iter_;
                return *this;
            }
    
            value_type & operator* ()
            {
                return *iter_;
            }
    
            ...
        };
    }// end of namespace std
    
    reverse_iterator rbegin()
    {
        reverse_iterator r( begin() );
        return r;
    }
    

Anmelden zum Antworten