Modulare Architektur für die Verarbeitung von Pipelines


8

Ich versuche, die Architektur eines Systems zu entwerfen, das ich in C ++ implementieren werde, und ich habe mich gefragt, ob die Leute sich einen guten Ansatz vorstellen oder den Ansatz, den ich bisher entworfen habe, kritisieren könnten.

Das allgemeine Problem ist zunächst eine Bildverarbeitungspipeline. Es enthält mehrere Stufen, und das Ziel besteht darin, eine hochmodulare Lösung zu entwerfen, so dass jede der Stufen leicht ausgetauscht und durch einen benutzerdefinierten Code ersetzt werden kann (so dass der Benutzer eine Geschwindigkeitssteigerung erzielen kann, wenn er es weiß dass ein bestimmtes Stadium in seinem Problem auf eine bestimmte Weise eingeschränkt ist).

Das gegenwärtige Denken ist ungefähr so:

struct output; /*Contains the output values from the pipeline.*/

class input_routines{
    public:
    virtual foo stage1(...){...}
    virtual bar stage2(...){...}
    virtual qux stage3(...){...}
    ...
}

output pipeline(input_routines stages);

Dies würde es den Leuten ermöglichen, input_routines zu unterklassifizieren und die gewünschte Stufe zu überschreiben. Das heißt, ich habe schon in solchen Systemen gearbeitet, und ich finde, dass die Unterklassen und die Standardmaterialien dazu neigen, chaotisch zu werden, und dass es schwierig sein kann, sie zu verwenden, so dass ich nicht schwindlig bin, selbst eines zu schreiben. Ich habe auch über einen STLish-Ansatz nachgedacht, bei dem die verschiedenen Stufen (es gibt 6 oder 7) standardmäßige Vorlagenparameter sind.

Kann jemand eine Kritik des obigen Musters, Gedanken zum Vorlagenansatz oder zu einer anderen Architektur abgeben, die ihm in den Sinn kommt?


1
Sind Sie sicher, dass Sie für diese Art von Projekt keine Sprache der Lisp-Familie verwenden möchten?
Job

Oh, ich möchte mit Sicherheit eine Sprache der Lisp-Familie für dieses Projekt verwenden. Tragischerweise füge ich einer vorhandenen C ++ - Imaging-Bibliothek Funktionen hinzu.
Anjruu

Antworten:


2

Das Design hängt stark davon ab, was die verschiedenen Phasen tatsächlich tun. Ich bevorzuge meistens reine virtuelle Funktionen gegenüber nicht reinen virtuellen Funktionen (abstrakte Klassen).

Gemeinsame Stufen können in abstrakten Unterklassen zusammengefasst werden. Durch Ableiten von der abstrakten Hauptklasse können Sie immer noch jede Stufe anpassen, aber durch Ableiten von einer Unterklasse können Sie vorhandenes Verhalten wiederverwenden, das bereits geschrieben wurde. Es ist in der Regel weniger chaotisch, wie Sie für virtuelle Methoden erwähnen.

Wenn die verschiedenen Phasen auch für sich alleine existieren können (außerhalb der gesamten Pipeline), sollten Sie auch Klassen schreiben, um dieses Verhalten zu trennen.


2

Erstellen Sie möglicherweise eine Liste von Funktoren in einer Fabrik, die die Stufen implementieren. Pseudocode:

functorFactory() {
  return [ foo(), bar(), baz() ]
}

Benutzer können die Fabrik neu implementieren oder einfach die Liste der Funktoren bearbeiten. Pseudocode

myFactory() {
  return [ foo(), myBar() ]
}

oder

myFactory() {
  return functorFactory()[2] = myBar()
}

Wenn das Setup abgeschlossen ist, können Sie jeden Funktor mit dem Ergebnis des letzten aufrufen.


+1: Dies bietet maximale Flexibilität, da der Benutzer Schritte hinzufügen / entfernen kann, wenn er dies wünscht.
Matthieu M.

1
Das sieht gut aus, aber wie würde ich das in C ++ implementieren? Ich konnte kein Array von Funktionszeigern haben, da jeder Funktionszeiger einen anderen Typ hätte, wenn die Funktionen unterschiedliche Dinge zurückgaben, und Arrays nur Objekte desselben Typs enthalten können. Vielen Dank!
Anjruu

0

Werfen Sie einen Blick auf die in Haskell implementierten Monaden, die Ihnen möglicherweise eine gute Vorstellung davon geben, wie Sie die Dinge einrichten. Haskell macht so etwas wirklich richtig.


0

Ich würde einen Zwischentyp definieren. Alle Bilder würden in dieses Format konvertiert. Jede Stufe würde ein Intermediate nehmen und ein Intermediate zurückgeben - nicht dasselbe, ein neues.

Eine Bühne wäre dies:

Intermediate DoSomething(const Intermediate &i);

Normalerweise vermeide ich vererbungsbasierte Lösungen im Allgemeinen, es sei denn, sie sind eine ziemlich offensichtliche Abbildung auf das Problem, z. B. Objekte in einer 3D-Welt.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.