Array2D: Evolution von manueller Speicherverwaltung zur STL



  • @docshoe sagte in Array2D: Evolution von manueller Speicherverwaltung zur STL:

    Nachteil: Der Zugriff auf die Elemente erfolgt nicht mehr über eckige Klammern, >sondern über runde, weil sich der operator[] nicht für zwei Parameter überladen lässt.

    Doch. Der Erste muss nur ein Proxy (oder wie du es nennen magst) zurück geben.



  • @jockelx
    nein und ja. Der operator[] lässt sich nicht für zwei Parameter überladen, aber man kann natürlich einen Proxy zurückgeben, der selbst wieder den operator[] überlädt. Die Klasse kann sowieso nicht als in-place Ersatz für den naiven Ansatz benutzt werden, und wenn man das doch tut muss man halt auch noch den Elementzugriff anfassen.

    Edit:
    Selbst wenn man ihn für zwei Parameter überladen könnte sähe der Aufruf dann so aus

    Array2D<double> Arr;
    double v = Arr[0,0];
    

    was auch nicht so aussieht wieder der Zugriff auf ein Array aus Arrays. Mit nem Proxyobjekt kommt man dem wohl am nächsten, wenn es unbeding so aussehen muss.



  • Hey,
    ich hatte auch schon öfter an sowas gedacht und auch umgesetzt. Letztendlich finde ich es aber deutlich angenehmer, wenn dieses Array2D lediglich ein Wrapper um einen templatisierten Container ist. Im Endeffekt limitierst du hier lediglich den Benutzer dieser Klasse, der gerne eine Abstraktion nutzen würde. Im Endeffekt kann es dir eigentlich egal sein, ob die Daten nun als plain Array, std::array, std::vector oder std::list vorliegen; es geht lediglich darum eine Abstraktionsebene zwischen den Eindimensionalen- und Mehrdimensionalen-Indexzugriff zu kreieren. Prinzipiell kann man das gut mit einem std::string_view vergleichen; du möchtest eine View mit entsprechendem Interface auf die Daten erstellen, dafür musst du den MemoryOwner aber nicht besitzen.



  • @dnkpp
    Ich denke, das sind zwei unterschiedliche Szenarien. In deinem Fall gibt es bestehende Daten, die iwie organisiert sind, und man möchte ein einfaches Interface, um sie wie ein 2D-Array zu behandeln. Die Klasse, die das Interface zur Verfügung stellt, besitzt die Daten nicht, sondern erlaubt nur den Zugriff auf die Elemente. array2d_view wäre da wohl ein passender Name.

    Den Fall, für den ich die Klasse geschrieben habe ist ein anderer. Hier besitzt das Array die Daten und es ist für Fälle gedacht, wo eine andere Darstellung als 2D keinen Sinn ergibt. Mir fällt kein Argument ein, warum man ein Schachfeld als std::vector implementieren sollte und bei jedem Zugriff entweder den linearen Index umrechnen oder ein array2d_view drüberlegen muss. Oder eine Implementation als std::list, wo man keinen wahlfreien Zugriff auf die Elemente hat sondern sich erst durch die Liste hangeln muss. In diesem Fall kaschiert array2d_view sogar die Benutzung eines ungeeigneten Containers, da es einen einfachen und schnellen Zugriff vorgaukelt.

    Mal sehen, wenn ich Zeit und Bock haben sollte kann ich ja Version 8 bauen, in der man den zu Grunde liegenden Containertyp als template Parameter festlegen kann. Ich nehm´ den Punkt aber mal als potenziellen Nachteil in die Liste auf, wobei mir aber wirklich kein Grund gegen std::vector einfällt. Wobei... wenn so ein Array mehrere GiB oder sogar TiB groß sein kann möchte man als Container vielleicht eine Klasse haben, die nur Teile im Speicher hält und die Arraydaten auf der HD liegen.

    Edit:
    Version 8 ergänzt 😉



  • Gibt es einen Grund, warum du die Funktionsparameter und Klassenmember mit einem Großbuchstaben schreibst (also Row, Col etc. anstatt row, col etc.)? Finde ich nicht einheitlich (stattdessen z.B. other) und auch gegen die häufigst verwendeten Coding Guidelines.



  • @th69
    Äh, ne, gibts nicht. Ist wohl verbesserungswürdig 😉



  • Bekommt das Ding noch einen Iterator?



  • @swordfish
    Von mir jetzt erst Mal nicht. Habe ich ja unter mögliche Verbesserungen aufgeführt... kannst dich austoben, wenn du magst. Oder wenn mir später noch mal langweilig wird. Obwohl Iteratoren wieder ein Kapitel für sich sind, dazu macht man besser ein eigenes Kapitel/FAQ. Zumal ich mich mit dem Thema auch nicht wirklich gut auskenne. Ne naive Lösung ist kein Problem, aber ob das dann tutorial-tauglich ist weiß ich nicht. Und eine boost Lösung finde ich hier unpassend, weil einem vorgekaut wird, wie man eine Array Klasse entwickelt und einem für die Iteratoren dann boost um die Ohren gehauen wird.



  • Die ganzenarray2d_container_xyz Funktionen würde man normalerweise eher in eine Traits-Klasse packen. Guck z.B. wie es std::basic_string mit den char-Traits macht.

    Was den Indexingoperator angeht: Man könnte ihn auch ne "Index2D" struct als Parameter nehmen lassen, die nen (nicht-explicit) ctor mit zwei size_t hat. Und dann mit myArray2D[{x, y}] aufrufen. Ist natürlich auch nicht gerade schön.

    Bevor ich mit nem Proxy arbeite würde ich aber auf jeden Fall mal gucken was für Code dabei rauskommt. Also ob der ganze Overhead wirklich wegoptimiert werden kann.


  • Gesperrt

    @lemon03 sagte in Array2D: Evolution von manueller Speicherverwaltung zur STL:

    Finde ich gut 👍
    Habe es mal gebookmarkt, falls irgendwo irgendwann die Frage nach einem 2DArray auftauchen sollte.

    Finde ich auch und habe es auch gebookmarkt.

    @docshoe sagte in Array2D: Evolution von manueller Speicherverwaltung zur STL:

    eine Art array_view für einzelnen Zeilen oder Spalten

    Also dann sind je nach dem (wenn container_type = std::vector) nur die Zeilen oder die Spalten im Speicher zusammenhängend (?).

    Eventuell praktisch wäre für mein derzeitige Problemstellung:

    • wenn die Spalten unterschiedliche Datentypen haben könnten
    • wenn die Spalten Namen als std::string haben könnten
    • wenn das Array2D eine Ausgabe als std::ostream (?) als csv-Dateiformat haben könnte


  • @titan99_: Das wäre aber dann kein 2D-Array mehr, sondern eine Tabelle (data table).



  • @hustbaer
    Die Sache mit den traits gefällt mir, das nehme ich mal in die to-do Liste auf. Eigentlich braucht man auch nur die resize() Funktionalität. size und empty hängen direkt von Rows_ und Cols_ ab, begin lässt sich über std::begin realisieren. Auf der anderen Seite ist die Bündelung der Funktionalität in einer traits-Klasse besser handhabbar, weil alles an einer Stelle gemacht werden kann.

    @Titan99

    • wenn die Spalten unterschiedliche Datentypen haben könnten
      Wenn die Elemente alle unterschiedlich sein sollen nimmste halt den entsprechenden Datentyp (variant, any, etc.). Oder baust dir deinen eigenen mit deinen Anforderungen.
    • wenn die Spalten Namen als std::string haben könnten
      Strings für Spalten- oder Zeilennamen braucht man je nach Anwendungsfall, die immer mitzuschleppen halte ich für keine gute Idee.
    • wenn das Array2D eine Ausgabe als std::ostream (?) als csv-Dateiformat haben könnte
      Finde ich auch nicht gut. Du brauchst jetzt gerade die Daten im CSV Format, aber was ist bei jemandem, der die kompakt im Binärformat schreiben möchte? Oder ein anderes Trennzeichen als das Semikolon? Oder die Zellen in einzelne oder doppelte Hochkommata einfassen möchte, weil die Zelle ein Trennzeichen als Dateninhalt enthält?

    Wenn du eine Klasse Datatable brauchst kannst du die doch einfach über Komposition realisieren. Dann bauste noch deine Spaltenüberschriften und Stream I/O dazu und fertig ist die Laube. Das geht mit weniger als 100 Zeilen Code.