Von dem Versuch, eine Tabelle zu iterieren



  • So, ich komme hier allmählich weiter, aber bin halt nicht so der Template-Guru. Hat jemand von euch ein gutes Auge für Template-Container und Spaß an der Sache und würde mal über
    https://github.com/decimad/na_containers/blob/master/include/na_containers/array2d.h
    drüberschauen? Der begleitende Beispielcode ist unter
    https://github.com/decimad/na_containers/blob/master/projects/test/test.cpp
    zu finden. Es sind noch keine wirklichen Operationen vorhanden. Im Moment gefällt mir erstmal noch nicht, dass man gerade den voll spezialisierten Container in array2d reinstecken muss, vielleicht finde ich da noch einen Weg, der vector als default nimmt. Eigentlich wäre es auch cool, wenn man da ein normales array reinstecken könnte, aber dann müsste ich ja irgendwie alle Operationen einschränken könnnen, weil resize nicht geht. Hrmmm. Würde mich über Tipps und Vorschläge sehr freuen :xmas2: :xmas1:

    Viele Grüße,
    Deci



  • Mir fällt nur gerade auf, dass du recht viel Codeduplizierung hast. col_type , row_type , cols_sequence und rows_sequence sehen recht ähnlich aus, ebenso die einzelnen Iteratoren.

    Allerdings wirklich merkwürdig, dass es keine solche Implementierung bereits zu geben scheint. Dürfte doch ab und zu benutzt werden. Naja, kopierbare Smart-Pointer braucht scheinbar auch niemand, Leute schreiben lieber jedes Mal wieder alles neu 😉



  • Also bei den Iteratoren muss ich zwei Typen implementieren, weil je nach Ordering ein Iterator einen Stride hat und einer nicht (und wenn man bei den Iterationen major_element_iterator benutzt, bekommt man automatisch sequenziellen Zugriff ohne stride). Bei den anderen Typen bin ich mir gerade nicht sicher, ob ich da Zeilen sparen kann, hast natürlich Recht, dass da Duplizierung ist. Schaue ich gerade mal drüber.
    Danke für's Anschauen! :xmas2:



  • Also bei cols_sequence und rows_sequence, welche Adapter sind um mit for( a : b ) kompatibel zu sein, weiß ich jetzt nicht, wie ich das anders machen sollte, da kommt es ja auf die Namen der Symbole an, die dort weitergeleitet werden.

    Edit: bei row_type und col_type ist es dasselbe... die verlassen sich auch darauf, dass array2d_base ihnen die entsprechenden Iteratoren liefert, weil sie sonst ja selbst darüber entscheiden müssten, ob der row_ oder col_iterator einer mit oder ohne Stride ist.



  • Eine Möglichkeit wäre Funktionsüberladung. Damit kannst du ein sehr nettes Idiom benutzen.

    Statt

    row_element_iterator get_row_begin(size_type rowIndex);
    col_element_iterator get_col_begin(size_type colIndex);
    

    hättest du

    struct row_tag {};
    struct col_tag {};
    
    // Generischer Code
    template <typename Tag>
    element_iterator<Tag> get_begin(size_type index, Tag);
    
    // Spezifischer Code
    element_iterator<row_tag> get_begin(size_type index, row_tag);
    element_iterator<col_tag> get_begin(size_type index, col_tag);
    

    Vielleicht kannst du sogar mehr als Row/Column damit abdecken. Unter Umständen könntest du so einiges zusammenfassen.



  • Hrmmm, das könnte klappen! Uiuiui, das wird jetzt Arbeit 😃



  • 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.


Anmelden zum Antworten