Interface-Design: Alternative zu template virtual

Programmiersprachen, APIs, Bibliotheken, Open Source Engines, Debugging, Quellcode Fehler und alles was mit praktischer Programmierung zu tun hat.
Antworten
NeuroCoder
Beiträge: 14
Registriert: 24.05.2003, 17:31
Alter Benutzername: NeuroCoder
Wohnort: NRW
Kontaktdaten:

Interface-Design: Alternative zu template virtual

Beitrag von NeuroCoder »

Hallo,

ich suche einen Rat bei einer Architektur-Fragestellung in meinem C++ Projekt.

In einem Simulationscode müssen Matrizen aufgebaut werden. Die Matrizen sind tatsächlich Bandmatrizen und könnten daher sowohl in einer speziellen Bandmatrix-Datenstruktur als auch in einer Sparse-Struktur gespeichert werden. Die Objekte, die die Matrix befüllen, bekommen einen Zeilen-Iterator auf die jeweilige Matrix-Datenstruktur und können diese so befüllen. Beide Datenstrukturen haben in dem einen oder anderen Kontext Vorteile, weshalb beide nutzbar sein sollen. Die Objekte müssen auch ein Interface implementieren, damit der Simulator sie einheitlich verwenden kann.

Das sieht momentan so aus:

Code: Alles auswählen

class IUnit
{
public:
   virtual void assemble(SparseMatrixIterator& it) const = 0;
   virtual void assemble(BandMatrixIterator& it) const = 0;
};

class SomeUnit : public IUnit
{
public:
   virtual void assemble(SparseMatrixIterator& it) const { assembleImpl(it); }
   virtual void assemble(BandMatrixIterator& it) const { assembleImpl(it); }

protected:
   template <typename iter_t>
   void assembleImpl(iter_t& it) { /* ... */ }
};
Die Weiterleitungen an das Template

Code: Alles auswählen

   virtual void assemble(SparseMatrixIterator& it) const { assembleImpl(it); }
   virtual void assemble(BandMatrixIterator& it) const { assembleImpl(it); }
sind momentan hinter einem Makro versteckt, weil ich viele Implementierungen von IUnit habe und Tipparbeit sparen möchte. Außerdem ist das Refactoring einfacher.

Dieses Pattern habe ich noch an einigen anderen Stellen mit anderen Klassen und ich finde es nicht besonders schön. Ich habe diesen Ansatz gewählt, weil die Matrizen groß sind und ich ein Interface für die Iteratoren wegen des Overheads vermeiden möchte.
Ideal wären template virtual Funktionen, die es natürlich aus verständlichen Gründen nicht gibt. Ich könnte alles in Templates umwandeln und von dem Interface abrücken, aber das ist wegen der Kompilierzeiten inakzeptabel und verhindert außerdem, dass ich IUnits dynamisch als Plugin laden kann.

Gibt es eine andere / bessere Lösung für dieses Problem?
Wenn Ihr diese Lösung gar nicht schlimm findet, bin ich auch für diese Info dankbar.

Viele Grüße
NeuroCoder

PS: Warum gibts hier eigentlich kein Code-Highlighting oder zumindest die Option meine Einrückung nicht zu schlucken?
Zuletzt geändert von xq am 25.07.2018, 22:28, insgesamt 1-mal geändert.
Grund: Habe das Syntaxhighlighting mal dreist eingefügt
NytroX
Establishment
Beiträge: 387
Registriert: 03.10.2003, 12:47

Re: Interface-Design: Alternative zu template virtual

Beitrag von NytroX »

Mir ist die Verwendung noch nicht ganz klar.
Bist du sicher, dass virtuelle Funktionen von der Performance her ein Problem sind?
Immer wenn du einen Aufruf über das Interface "IUnit" machst, ist ja sowieso ein virtueller Funktionsaufruf nötig.

Ansonsten kannst du dir mal statischen Polymorphismus anschauen, der löst zumindest das Matrix-Interface Problem; wenn ich deinen Post richtig verstehe sind die Typen der Matrizen während der CompileTime immer bekannt.
https://en.wikipedia.org/wiki/Template_ ... lymorphism
Hier sogar ein Beispiel mit Iteratoren:
https://stackoverflow.com/questions/190 ... phism-in-c

Plugins und dynamsiches Laden sind immer so ne Sache in C++, ich denke da ist das Interface eine gute Idee.
Musst halt auch immer alles mit dem gleichen Compiler bauen; vielleicht nochmal überlegen wie dynamisch du wirklich sein willst/musst, da sehe ich noch ein paar Hürden auf dich zukommen :-)

Wenn du auch noch beliebige Matrix-Implementierungen mischen musst (z.B. jede Matrix Implementierung kann mit jeder anderen addiert werden), dann wirds kompliziert.
Da würde ich dann zu Open Multi Methods greifen, wenns die Compilezeit nicht zu sehr nach oben treibt (https://github.com/jll63/yomm2).
Da gibts auch direkt ein Beispiel mit Matrizen.
NeuroCoder
Beiträge: 14
Registriert: 24.05.2003, 17:31
Alter Benutzername: NeuroCoder
Wohnort: NRW
Kontaktdaten:

Re: Interface-Design: Alternative zu template virtual

Beitrag von NeuroCoder »

Hi NytroX,
NytroX hat geschrieben:Bist du sicher, dass virtuelle Funktionen von der Performance her ein Problem sind?
Immer wenn du einen Aufruf über das Interface "IUnit" machst, ist ja sowieso ein virtueller Funktionsaufruf nötig.
Das habe ich (natürlich) nicht gemessen. Die Calls in IUnit verrichten überwiegend kompliziertere Dinge, verursachen also genug Arbeit, sodass der virtual Call hier nicht ins Gewicht fällt.

Die Funktionen in den Matrix-Zeilen-Iteratoren haben hingegen fast nichts zu tun (an einer bestimmten Stelle in ein Array schreiben) und werden ziemlich oft aufgerufen.
Sowas in der Art:

Code: Alles auswählen

void SomeUnit::assemble(BandMatrixIterator& it) const
{
   for (int i = 0; i < nElements; ++i)
   {
      // ...
      it.set(-2, 0.1);
      it.set(-1, 0.22);
      it.set(0, -0.4);
      it.set(1, 0.1);
      // ...
      it.nextRow();
   }
}
Dabei kann nElements ziemlich groß werden (Diskretisierungsparameter einer PDE).
NytroX hat geschrieben:Ansonsten kannst du dir mal statischen Polymorphismus anschauen, der löst zumindest das Matrix-Interface Problem; wenn ich deinen Post richtig verstehe sind die Typen der Matrizen während der CompileTime immer bekannt.
Ja, die Anzahl und die genauen Typen der Matrizen sind zur Compile-Time bekannt. Allerdings weiß ich nicht, wie ich statischen Polymorphismus hinkriegen soll, ohne dass assemble() eine Template Member Function wird, was das virtual dann wieder ausschließt.
NytroX hat geschrieben:Plugins und dynamsiches Laden sind immer so ne Sache in C++, ich denke da ist das Interface eine gute Idee.
Musst halt auch immer alles mit dem gleichen Compiler bauen; vielleicht nochmal überlegen wie dynamisch du wirklich sein willst/musst, da sehe ich noch ein paar Hürden auf dich zukommen :-)
Das stimmt. Ich werde wahrscheinlich eine C-API als Zwischenschritt einbauen. Da die IUnit Calls alle viel Arbeit verrichten, ist der zusätzliche Overhead des Function Calls hier nicht schlimm.

Viele Grüße
NeuroCoder
NytroX
Establishment
Beiträge: 387
Registriert: 03.10.2003, 12:47

Re: Interface-Design: Alternative zu template virtual

Beitrag von NytroX »

Allerdings weiß ich nicht, wie ich statischen Polymorphismus hinkriegen soll, ohne dass assemble() eine Template Member Function wird, was das virtual dann wieder ausschließt.
Hmmja, hast recht, dann hättest du Templates drin, da fällt mir auch nix besseres ein.
C++ ist aber auch ein Biest :-P

Ich habs mal versucht, aber den Code, der dabei rauskam, will keiner sehen - dann vielleicht doch so lassen wie du es hast.
Eventuell könntest du mehrere "set" calls der Iteratoren noch zusammenfassen, sodass du ein Array übergibst, dann sparst du dir mehrfache Aufrufe. Eventuell macht das eine virtuelle Funktion möglich.
Aber ehrlich gesagt finde ich es garnicht schlecht, das "assemble" in 2 Funktionen zu haben, jeweils für einen Matrix-Typ (solange du nur 2 verschiedene hast). Wenn du das später erweitern willst, kannst du das dann später immernoch umschreiben.
Antworten