Wie portable Library designen
-
Hoi!

Ich bastle wieder an meiner Library, ich möchte (immer noch) Prozesse plattformunabhängig repräsentieren.
Mein aktueller Ansatz ist:
Eine Klasse process, die komplett abstrakt ist und von den Klassen win32_process, linux_process, bsd_process etc implementiert wird.
Dazu implementiert jede dieser Klassen noch zusätzliche Features, die nur auf diesen System verfügbar sind. Möchte jetzt jemand diese Features, dann muss er den Basisklassenzeiger bewusst auf die abgeleitete Klasse casten, dh. man sagt explizit dass man auf nicht-plattformunabhängige Funktionen zurückgreifen möchte. Erzeugt werden diese Instanzen von einer Factory-Funktion, die unter der Haube den richtigen Prozess für das aktuelle System aus wählt (bzw die anderen wären sowieso nicht verfügbar).Ist das so ein gutes Design? Was wäre eine bessere Option?
Und vlt nicht unbedingt passend hier, aber mir sind Python-Bindings sehr wichtig, die ich mit Boost.Python schreibe. In Python gibt es allerdings nicht die Möglichkeit einfach einen "Upcast" zu machen, wie wäre es geschickt die Bindings zu machen?
Grüße und Danke,
Ethon
-
Eine Klasse process, die komplett abstrakt ist und von den Klassen win32_process, linux_process, bsd_process etc implementiert wird.
Was spricht gegen bedingte Kompilierung? Die Klassen treten nie gleichzeitig auf, sie lassen sich sogar nicht einmal gleichzeitig kompilieren. Also mach eine gepimpelte Klasse process und drei Dateien, von denen je nach Target nur eine kompiliert wird.
-
nolib schrieb:
Eine Klasse process, die komplett abstrakt ist und von den Klassen win32_process, linux_process, bsd_process etc implementiert wird.
Was spricht gegen bedingte Kompilierung? Die Klassen treten nie gleichzeitig auf, sie lassen sich sogar nicht einmal gleichzeitig kompilieren. Also mach eine gepimpelte Klasse process und drei Dateien, von denen je nach Target nur eine kompiliert wird.
Noch weniger. Einfach ifdef und globale Funktionen, die die API Wrappen.
-
Ich schliesse mich volkard an was das #ifdef angeht.
In einem Fall, wo es nur eine Implementierung pro compilierter Version geben kann, muss man weder Factory Funktionen machen noch mit pimpl o.ä. arbeiten.
Wobei ich mir noch nicht sicher bin, was ich für besser halte: eine Prozess-Klasse die nur das nötigste bietet (RAII + Zugriff auf das native-Handle) + freie Funktionen, oder den konservativeren Ansatz mit einer Prozess-Klasse die die nötigen Funktionen als Member-Funktionen hat.
-
Noch weniger. Einfach ifdef und globale Funktionen, die die API Wrappen.
Exakt so habe ich es zur Zeit, aber irgendetwas stört mich daran.

Plattformabhängige Funktionen habe ich in extra Namespaces gestopft, so gibt es ein unix_like::get_procfs_directory etcIch weiß halt nur nicht was ich in die Prozess-Klasse selbst stecken soll. Unter Linux verkommt sie zu nem winzigen Wrapper um int, ner Windows-Version würde ich auch ungern eine HANDLE get_handle() Funktion direkt verpassen.
Angenommen mein Ansatz mit den Namespaces für plattformabhängige Funktionen ist so gut, was wäre davon zu halten eine freie win32::get_process_handle Funktion zu bauen, die ein friend von process ist und der einzige Accessor auf das Handle ist?
-
Hauptsache, der Typ ist auch geifdeft, und im Nutzcode mußt Du nicht jedesmal dran denken, wo Du jetzt bist.
Also nicht HANDLE win32::get_process_handle(), sondern os::handle os::get_process_handle().
(Das senkt auch die Compilezeiten ist Nichts.)
Ob eine Prozess-Klasse überhaupt sinnvoll ist, weiß ich nicht. Bei Semaphoren, Dateien, Mutexen weiß ich es, wegen RAII. Diese Klassen haben optimalerweise schon gar kein ifdef mehr drin, das geht auch, weil die zugehörigen Funktionen beider Betriebssysteme fast gleich sind.
Ich weiß auch nicht, was der accessor auf das Handle jetzt bezwecken soll.
-
Hauptsache, der Typ ist auch geifdeft, und im Nutzcode mußt Du nicht jedesmal dran denken, wo Du jetzt bist.
Also nicht HANDLE win32::get_process_handle(), sondern os::handle os::get_process_handle().
(Das senkt auch die Compilezeiten ist Nichts.)Jup, Windows.h will ich auf keinen Fall im Header haben, würde einfach void* als Handletyp nehmen, das sollte mit allen Windows-Versionen der Vergangenheit und Zukunft funktionieren.
Ob eine Prozess-Klasse überhaupt sinnvoll ist, weiß ich nicht. Bei Semaphoren, Dateien, Mutexen weiß ich es, wegen RAII.
Unter Windows schon, auch wegen RAII, die Handles müssen ja geschlossen werden, können dupliziert werden etc. Bei Unixen bringt es reichlich wenig, sollte aber auch kein Nachteil sein.
Ich weiß auch nicht, was der accessor auf das Handle jetzt bezwecken soll.
Um direkt mit der WinAPI arbeiten zu können, falls das jemand so machen möchte.
Danke für die Antworten, ich werds wohl mal so machen.

....
eine Frage noch: Was könnte ich mit den plattformabhängigen Funktionen machen, wenn sie auf der falschen Plattform benutzt werden? Sollen sie da gar nicht existieren, also muss der Nutzcode möglicherweise ge-ifdef-ed werden. Exceptionwerfen/assert? Oder einfach Dummy-Wert zurückgeben (leeren String, null oä.)
-
Sollten da nicht existieren, imo.
-
Ja, die sollte es auf keinen Fall geben.
Ein paar #ifdef im Client Code sind IMO besser als dass es ne Funktion gibt, die dann nix tut. Lieber beim Compilieren schon merken dass was fehlt, als dann komische Fehler bei der Ausführung haben.