Von dem Versuch, eine Tabelle zu iterieren



  • Eigentlich auch, aber das passiert wegen einem Fehler in for( a : b )... vielleicht ist Vc++2012 noch nicht so stabil bei der neuen Syntax...



  • Die deutsche Version stürzt bei inkorrektem Code ab, anstatt eine Fehlermeldung zur produzieren: http://www.c-plusplus.net/forum/308301



  • Danke für den Hinweis, das wird's wohl gewesen sein!
    Ich habe mal eine neue Variante hochgeschickt, die mit Tags arbeitet (array2d2.h im selben Verzeichnis) und Methodenüberladung. Ich hab jetzt nicht auf Anhieb gesehen, weshalb ich für die Methoden nochmal extra eine Template-Variante anbieten muss, daher habe ich das erstmal rausgelassen. Außerdem fehlen noch die meisten const-Alternativen.



  • So, noch eine kleine Detailfrage...
    Ich habe jetzt temporäre Prody-Objekte für Spalten und Zeilen. Wenn die nun auf ein const-Array zeigen, dann dürfen deren begin()- und end()-Methoden ja entweder nur const definiert sein, oder aber sie geben auch einen const_element_iterator zurück.

    Ich das jetzt folgendermaßen gemacht:

    template< typename ArrayType, typename Tag >
    	class slice_type {
    		typedef typename element_iterator< typename ArrayType::value_type, Tag > _tmp_iterator;
    	public:
    		typedef typename element_iterator< const typename ArrayType::value_type, Tag > const_iterator;
    		typedef typename static_if< std::is_const< ArrayType >::value, const_iterator, _tmp_iterator >::type iterator;
    
            iterator begin() { ... }
            ...
        };
    

    Gibts da irgendwie eine elegantere Lösung?



  • So? 🙂

    typedef element_iterator< typename ArrayType::value_type, Tag > iterator;
    typedef element_iterator< const typename ArrayType::value_type, Tag > const_iterator;
    

    Das erste typename ist übrigens unnötig. Was ist eigentlich static_if , warum nimmst du nicht std::conditional ?



  • Hey Nexus,
    ...static_if, weil ich nicht 10 Minuten nach etwas suchen wollte, das ich da eben in einer Minute schreibe, ich ging schon davon aus, dass es da auch was von std gibt...

    Dein Beispiel hatte ich vorher. Aber das Problem ist, dass wenn ArrayType "const array2d<...>" entspricht, dann versucht das nicht-const slice_type<const array2d<...>>::begin() eben trotzdem einen iterator zurückzugeben und nicht einen const_iterator. Aber ich kann die nicht-const-Methode auch nicht einfach so verschwinden lassen, weil sie kein Template ist. Daher dieser Hack, um slice_type<const array2d<...>>::iterator auch einfach als array2d::const_element_iterator zu definieren. Hoffentlich ist das jetzt etwas klarer, das lässt sich echt schlecht formulieren 😃



  • Ah, ich hatte vorher übersehen, dass du das is_const nur auf dem ArrayType und nicht ArrayType::value_type prüfst...

    Auf wie vielen Ebenen kann etwas konstant sein? const slice_type<const array2d<const float>>> ? Ich würde versuchen, keine unnötigen CV-Qualifizierer zu verwenden, weil das 1. Dinge umständlicher macht und 2. die Anzahl der Template-Instanziierungen mit jeder Ebene verdoppelt. Wichtig ist, dass die Schnittstelle const-correct ist.



  • Also aus dem array2d kann man sich einen slice-proxy besorgen, der nochmal so agieren soll wie ein container. Und diesen slice-proxy "const" zurückzugeben bringt ja irgendwie nix, würde den auch gerne abspeichern können, so dass der client-code meinetwegen auf 3 Spalten (slices) agieren kann, die er sich lokal abspeichert. Ansonsten könnte ich die ja einfach non-copyable machen und const zurückgeben.



  • D.h. 2 Ebenen?

    Haben die folgenden Instanziierungen die gleiche Semantik (weitere Templateparameter mal weggelassen)?

    slice_type<const array2d<      float>>>
    slice_type<      array2d<const float>>>
    slice_type<const array2d<const float>>>
    

    Oder kann man durch das eine Slice das Array verändern (d.h. Spalten und Zeilen einfügen/löschen), aber nicht die Elemente?

    Du musst bedenken, dass -- wenn slice_type zum API gehört -- der User auch const -Objekte davon erstellen kann. Die sollten auch funktionieren, d.h. du hättest schlussendlich doch 3 Ebenen 😉



  • Muss slice_type unbedingt das Array selbst als Template-Parameter nehmen? Falls nein, nimm direkt den Elementtyp.

    slice_type<float>
    


  • Hrmmm, also array2d< const float > hatte ich so eigentlich nicht vorgesehen. Bringt das was? Ich wüsste nicht, wie ich da im Moment Werte sinnvoll initialisieren sollte.

    Also es ist genau genommen so:

    array -> element_iterator -> element
    array -> slice_iterator -> slice -> element_iterator -> element

    Und eigentlich wollte ich, sobald irgendwo mal ein const in die Kette gekommen ist, mit dieser const-Entität (sei es nun slice oder die iteratoren) nicht mehr verändernd in das array eingreifen können. Würde es Sinn ergeben, denn noch die Operationen "auf der selben Ebene oder davor" damit zu erlauben?



  • Nexus schrieb:

    Muss slice_type unbedingt das Array selbst als Template-Parameter nehmen? Falls nein, nimm direkt den Elementtyp.

    slice_type<float>
    

    Also der Array-Typ muss irgendwo mitgeliefert werden, weil der slice ja ein proxy über dem array ist. So zumindest gerade meine Sicht auf die Dinge. Vielleicht siehst Du das als außenstehender gerade besser.
    Der Quelltext ist im Repo auf github unter https://github.com/decimad/na_containers/blob/master/include/na_containers/array2d.h auf aktuellem Stand, falls Du Dir das nochmal anschauen möchtest 😃



  • Decimad schrieb:

    Würde es Sinn ergeben, denn noch die Operationen "auf der selben Ebene oder davor" damit zu erlauben?

    Unter Umständen schon. Bei Containern ist das const zwar tendenziell transitiv, aber bei Iteratoren und Zeigern nicht.

    const_iterator != const iterator
          const T* != T* const
    

    Decimad schrieb:

    Also der Array-Typ muss irgendwo mitgeliefert werden, weil der slice ja ein proxy über dem array ist.

    Das ist nicht Grund genug. Du kannst ja intern wieder array2d<float> als Typ definieren, aber du hältst die Template-Parameter flach.

    Decimad schrieb:

    Der Quelltext ist im Repo auf github unter array2d.h auf aktuellem Stand, falls Du Dir das nochmal anschauen möchtest 😃

    Hab vorher kurz drüber geschaut. Du solltest dir echt mal den Header <type_traits> anschauen, statt strip_const gibts std::remove_const :p



  • Das array2d ist aber nicht direkt über den Wert-Typ templatisiert, sondern über einen ganzen Container im Moment. Den genauen Typ bekommt der slice also über den Wert-Typ selber nicht heraus.

    Und zu letzterem: Vokabeln lernen lag mir noch nie 😉



  • Also folgende Gedanken habe ich mir gemacht.
    Mit dem slice kann man selber nicht so viel anstellen, vor allem nichts, was die Topologie des Arrays verändern soll.
    Der slice_iterator hingegen soll verwendet werden können, um Spalten und Zeilen einfügen zu können.
    Sowohl slice_iterator wie auch element_iteratoren funktionieren soweit ich das auch getestet habe const-correct.
    Wenn ich nun aus einem const_slice_iterator einen slice hole, dann soll ich über diesen slice auch wiederum nur const_element_iterator'en rausholen können.
    So hatte ich die const-Behandlung zumindest geplant.

    Edit: Und aufgrund der static_if-Magie, die ich da unlängst exemplarisch gezeigt habe, gibt die nicht-const-Variante von slice::begin des slices, das man aus einem const_slice_iterator bekommt, auch einfach einen const_element_iterator zurück (das war wieder kompliziert^^).



  • Decimad schrieb:

    Das array2d ist aber nicht direkt über den Wert-Typ templatisiert, sondern über einen ganzen Container im Moment. Den genauen Typ bekommt der slice also über den Wert-Typ selber nicht heraus.

    Okay, das ist natürlich blöder. Ich nehme an, der Containertyp wird tatsächlich gebraucht und kann nicht wegabstrahiert werden. Ich würde jedenfalls davon ausgehen, dass sowohl Container als auch seine Elemente nicht const sind. Wenn jemand

    array2d<const std::vector<const int>>
    

    benutzt, darf ihm das ruhig um die Ohren fliegen.

    Das ändert aber nichts am Grundsatz, die Template-Parameter möglichst einfach zu halten. Versuche eben, statt slice_type<array2d<vector<float>>>> "nur" 3 Verschachtelungsebenen slice_type<vector<float>>> zu haben.



  • Das kann ich natürlich machen, stimmt, werde ich auch gleich mal.
    Jetzt sind wir aber ein bisschen von dieser begin-Sache mit dem static_if weggekommen. Habe ich das schlüssig genug erklärt, dass Du verstanden hast, weshalb ich das im Moment machen muss (ich denke, das funktioniert eigentlich ganz gut, aber schön ist es dennoch nicht)? Das ist alles sehr verwinkelt 🙂



  • Args, also array2d ist über den Container UND über die Element-Anordnung templatisiert, das heißt um eine Ebene zu gewinne, bräuchte ich mehr Template-Argumente bei den ganzen Typen, ist es das dann immernoch wert?
    Edit: Ich denke, schon alleine dass man die Typen damit auf array2d einschießt, ist Grund genug... Nicht dass jemand versucht, sie zweckzuentfremden.

    Edit2: Args2 -.- Über den ArrayType-Typ habe ich auch die Information übertragen, ob es sich um eine const-Ausprägung (dann war das einfach const array2d<...>) des Iterators handeln soll oder nicht, wenn ich das jetzt flach mache, weiß ich nicht, wie ich diese Information reinbekommen soll, ohne nochmal auszuholen und einen bool IsConst-Parameter hinzuzufügen -.-



  • Sooo!
    Ich habe einmal std::conditional und std::remove_const eingearbeitet. Dann habe ich, wie beschrieben, die Template-Argumente flach gemacht und bei den Typen, wo es nötig war ein bool-Template-Argument hinzugefügt, das ausdrückt, ob die jeweilige Instanz gerade const sein soll oder nicht. Schön ist, dass die const-Sache so auch etwas transparenter ist. Dafür sind die Zeilen etwas länger geworden, hehe.



  • Jetzt auch mit reshape und reserve, wobei etwas ungetestet 😃


Anmelden zum Antworten