Von dem Versuch, eine Tabelle zu iterieren



  • Decimad schrieb:

    Also bei den Iteratoren muss ich zwei Typen implementieren, weil je nach Ordering ein Iterator einen Stride hat und einer nicht

    oder der eine hat stride > 1 und der andere stride = 1



  • Otze: Ich implementiere hier keine 2D-Iteratoren. Nur 2 1D-Iteratoren über Spalten bzw. Zeilen. Stride = 1 mag ich nicht, weil dann immernoch nicht-optimale Addition durchgeführt würde, auch wenn es auf inkrementieren hinausläuft, deshalb 2 Klassen.



  • Decimad schrieb:

    Stride = 1 mag ich nicht, weil dann immernoch nicht-optimale Addition durchgeführt würde, auch wenn es auf inkrementieren hinausläuft, deshalb 2 Klassen.

    Willst du das Ganze nicht zuerst sauber implementieren, und dich danach um Mikrooptimierungen kümmern? Der Code wird auch so schon kompliziert genug sein...



  • ich hole meinen edit nochmal vor:

    das könnte dir eine Menge Code sparen. Dann baust du statt 2 Spezialisierungen nur eine helfer klasse, die dir aus deinem 2d array die richtigen strides je nach row-major und colum-major zurück gibt:

    struct row_major{
        std::size_t stride1(std::size_t rows, std::size_t cols){
            return cols;
        }
        std::size_t stride2(std::size_t rows, std::size_t cols){
            return 1;
        }
    };
    struct column_major{
        std::size_t stride1(std::size_t rows, std::size_t cols){
            return 1;
        }
        std::size_t stride2(std::size_t rows, std::size_t cols){
            return rows;
        }
    };
    

    und bist fast fertig.

    zu deinm Argument: ich bin mir sicher, dass der Unterschied nicht messbar ist.



  • Ach, das passt schon! 🙂 Ich danke euch aber natürlich, dass ihr mit mir fühlt 😉



  • Jetzt stürzt mir zwar der compiler ab, daber das wird 😉



  • Decimad schrieb:

    Jetzt stürzt mir zwar der compiler ab

    Immer ein gutes Indiz für zu komplizierte Denkansätze :xmas1:



  • 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


Anmelden zum Antworten