implementierung matrix-speicheranordnung
-
hallo
ich arbeite atm an einer kleinen lib für ein paar mathematische funktionen zur analytischen geometrie. dazu gehören auch matrizen. diese matrizen sehen aktuell grob etwa so aus: (nur der für die frage relevante teil ist gezeigt)
typedef ::std::size_t SizeType; template<SizeType Width, SizeType Height> class MatrixDefaultMemoryPolicy { protected: MatrixDefaultMemoryPolicy() { } SizeType Redirect(SizeType IndexX, SizeType IndexY) const { return IndexX + IndexY * Width; } }; template</* ... */ template<SizeType, SizeType> class MemoryPolicy = MatrixDefaultMemoryPolicy /* ... */> class Matrix : /* ... */ private MemoryPolicy<Width, Height> /* ... */ { /* ... */ ValueType MyData[Elements]; public: TransferType Get(SizeType IndexX, SizeType IndexY) const { return this->MyData[this->Redirect(IndexX, IndexY)]; } /* ... */ ValueType* Data() { return this->MyData; } ValueType const* Data() const { return const_cast<Matrix*>(this)->Data(); } /* ... */ };das gefällt mir persönlich schonmal recht gut, man kann eigene policy-template-klassen definieren und als policy verwenden. nun wollte ich aber ein wenig komfort für den kunden bieten, so dass dieser nicht für jede änderung eine eigene klasse schreiben muss sondern dass ich die elementarsten funktionen (bottom-up, right-left, columnwise, ...) anbiete. da man diese natürlich auch kombiniert anwenden können sollte (alle möglichen kobinationen einzeln zu implementieren ist viel redunanz und verschwendete zeit), dachte ich an boolsche template-parameter, mit denen man die operationen ein- / ausschalten kann. da ich aber noch keine ausreichende c++11-erfahrung habe, soll dieses projekt c++11-unabhängig bleiben. also fallen die template-using-direktiven weg. ergo weiss ich nicht wie ich das implementieren soll.
wie würdet ihr das lösen?
gruss
ps.: für die, die sich fragen wieso ich so etwas überhaupt vom nutzer adjustierbar mache: windows bitmaps wollen eine bottom-up-formation, opengl will eine spaltenweise-formation etc etc.
pps.: ihr braucht mir nicht sagen dass es da draussen haufenweise solcher libs gibt. das ist mir selbst bewusst und dennoch mache ich nun meine eigene.
-
Ich hab mir in solchen Fällen lieber mit einer Storage-Basisklasse geholfen, der man dann die Parameter mitgibt.
template< typename ValueType, SizeType Rows, SizeType Cols, typename Storage = ColumnMajorStorage<ValueType, Rows, Cols>, typename AccessTransform = Identity<Rows,Cols> > struct Matrix : public Storage { // ... ValueType& operator()( SizeType row, SizeType col ) { return Storage::Get( AccesTransform::row(row, col), AccessTransform::col( row, col ) ); } // ... };Und den AccessTransform-Typen könnte man dann noch ein bissl zerstückeln. Irgendwie in InvertX, InvertY oder meinetwegn Rotate90 im Falle von quadratischen Matrizen oder so (wozu auch immer man das braucht, hehe).
-
danke schonmal für die antwort.
ich sehe in meinem fall gerade den vorteil einer storage-klasse nicht. alles was ich verändern lassen möchte (die reihenfolge der matrix-einträge im speicher) kann man ja durch meine memory-policy bzw. deinen access-transform bestimmen.
was bei mir eher das problem ist (vielleicht habe ich es falsch / missverständlich beschrieben), ist dass ich nicht weiss wie ich eine solche memory-policy anbieten kann, die der nutzer dann einfach "zusammenstecken" kann ohne das ganze memory-policy-klassentemplate selbst neu zu schreiben nur um irgendwo eine subtraktion reinzutun.
wunschdenken:
using namespace mg::Matrix; using namespace mg::Matrix::MemoryPolicies; Matrix<float, 10, 10, BottomUp & RightLeft> MyMat;sozusagen ein einfaches und codesparendes kombinieren der elementarsten transformationsoperationen zu compilezeit.
ich hoffe, das ist verständlicher.
-
Na wenn's Dir nur um die Komposition geht, dann brauchst R²->R² für Transformationen, und eine R²->R Abbildung für Storage.
Wenn man der üblichen mathematischen Notation mit Spaltenvektoren folgt, könntest Du also die Transformationen so in die Variadic-Liste hauen und eine Komposit-Transformation R²->R bilden, wobei das Pack natürlich rechts nach links ausgewertet wird. So hast Du das maximale an Flexibilität.
Keine Ahnung, ob der Benutzer später dann lieber eine Nicht-Template-Variante bevorzugt, bei dem ganzen Wust
-
Da es einen Kunden gibt:
Was sind denn überhaupt die Anforderungen? Was für Layouts gibt es denn konkret? Ist es komplexer als row-major/column-major?
-
decimad schrieb:
Na wenn's Dir nur um die Komposition geht, dann brauchst R²->R² für Transformationen, und eine R²->R Abbildung für Storage.
Wenn man der üblichen mathematischen Notation mit Spaltenvektoren folgt, könntest Du also die Transformationen so in die Variadic-Liste hauen und eine Komposit-Transformation R²->R bilden, wobei das Pack natürlich rechts nach links ausgewertet wird. So hast Du das maximale an Flexibilität.
Keine Ahnung, ob der Benutzer später dann lieber eine Nicht-Template-Variante bevorzugt, bei dem ganzen Wust
ah jetzt wird mir dein vorheriger post schon ein wenig klarer. variadische templates gibts erst mit c++11. ich versuche mal da etwas zusammenzubasteln mit c++03. ich habe da schon eine kleine idee nachdem du den begriff spaltenvektor in den raum geworfen hast.

otze schrieb:
Da es einen Kunden gibt:
Was sind denn überhaupt die Anforderungen? Was für Layouts gibt es denn konkret? Ist es komplexer als row-major/column-major?
oh da habe ich mich wohl falsch ausgedrückt. ich nenne alle benutzer automatisch kunden. es wird ein eigenes persönliches opensource-projekt. und etwas komplexeres als spiegelungen oder veränderung der majority der spalten / zeilen wird wahrscheinlich nicht benötigt. (mir fällt zumindest atm keinen anwendugsort für was anderes ein)
gruss und schönen abend noch
-
dann brauchst du nur row/column major. Eine row_major matrix zu transponieren ist das Selbe, wie den Speicher als column_major zu interpretieren und umgekehrt.