PluginSystem - benötige Lesestoff



  • Hallo.
    Ich möchte in eines meiner Programme ein Pluginsystem einbauen. Das Programm funktioniert sowohl für Windows als auch für Linux. Ich habe schon für einige Programme Plugins geschrieben, jedoch selber ein PluginSystem zu schreiben ist da schon was anderes.
    Bevor ich anfange möchte ich mich genaustens über verfügbare Methoden informieren. Ich persönlich kenne nur 2 (?), einmal über extern "C" und dann mit GetProcAddress - aber wie soll das auf Linux gehen?!, und dann via Interfaces (wie genau das funktioniert, keine Ahnung).
    Die Interfacemethode gefällt mir deutlich besser. Gibt es zu so etwas was gutes zu lesen? Ich verstehe nämlich immer noch nicht wirklich, wie ich so etwas realisieren kann. Ich möchte am Ende den Entwicklern nur die Headerdateien zur Verfügung stellen welche dann auf Funktionen in mein Programm zurückgreifen so wie Events die aufgerufen werden (zum Beispiel OnLoad).

    Bin dankbar für Hilfe und erwarte natürlich keinen fertigen Code, das will ich auch gar nicht. Das ist schon ein längeres Ziel von mir, mal zu verstehen, wie so etwas überhaupt (auch platformübergreifend) funktioniert.

    Gruß und Danke





  • @theliquidwave:
    Plugin-Systeme sind immer einigermassen plattformabhängig. Allerdings ist der plattformabhängige Teil sehr klein. Drei System-Calls (Laden, Funktionszeiger holen, Entladen) sind ja nicht gerade viel.

    Was die Interfaces angeht: die verwendet man in Kombination mit dem oben Erwähnten. Mit GetProcAddress holst du dir einen "extern "C"" Funktionszeiger. Die Funktion sieht dann oft so aus:

    class MyInterface { ... };
    
    extern "C" MyInterface* MyFactoryFunction();
    

    Löschen tut man das Objekt dann auch nicht mit "delete", sondern mit einem Methoden-Aufruf (der in der DLL dann als "delete this" implementiert ist).



  • Hi.
    Danke @ Zeus!
    Auch besonderen dank @ hustbaer, das habe ich nämlich verstanden und klingt auch verdammt logisch.
    Ich habe noch eine Frage zum "Design". In meinem Programm habe ich verschiedene Klassen die einige Prozeduren durchführen und Arbeit abnehmen. Angenommen ich habe eine Grafikklasse in meinem Programm erstellt und möchte diese nun für die Plugins verfügbar machen. Wie genau gehe ich vor? Wie realisiere ich es, dass ich nur die Headerdatei veröffentlichen muss? Wie würdet ihr das übergeben des Pointers auf die Grafikklasse realisieren? Meine Annahme: Man übergibt in der Load-Funktionen einen Pointer auf eine weitere Klasse die nur für Plugins existiert. Die hat dann Methoden wie getGraphics() welches den Pointer zur echten Klasse returnt. Stimmt das in etwa so?

    Gruß



  • Ja das stimmt in etwa so.

    Wichtig: veröffentliche nur Interfaces, keine ganzen Klassen.
    Also für alles was du "rausgeben" willst, machst du eine Interface-Klasse, die nur aus "virtual pure" Funktionen besteht.
    Die Implementierung leitest du dann von der Interface-Klasse ab.

    Die "nur-für-Plugins" Klasse gibt dann immer nur Zeiger auf die Interface-Klassen her.

    Nichts wird direkt mit "new" angelegt, nichts direkt mit "delete" zerstört, alles geht über Interface-Methoden.

    Dadurch kannst du die Implementierung komplett abändern, ohne die öffentlichen Header-Files auch nur angreifen zu müssen. D.h. die Plugins funktionieren auch weiterhin, da der Teil den sie "sehen" sich ja überhaupt nicht geändert hat.

    Guck dir einfach mal an wie die ganzen DirectX Dinger (DirectDraw, Direct3D, DirectSound etc.) aufgebaut sind. Oder ganz allgemein: wie COM funktioniert.

    p.S.: du musst natürlich in den Interfaces auf sämtliche Klassen verzichten, die sich evtl. ändern könnten. D.h. wenn du es auf z.B. Windows ermöglichen willst, dass man mit verschiedenen Compilern (z.B. verschiedenen Visual C++ Versionen) Plugins erstellt, dann darfst du KEINE Klassen der Standard C++ Library verwenden. Also kein std::string, std::vector, einfach garnix was in "std" lebt. Oder anders gesagt: eigentlich überhaupt nur selbst geschriebene Klassen. Das ist natürlich sehr mühsam. Oft wird daher der zu verwendende Compiler vorgeschrieben. Dann sind Standard-Library Klassen wieder erlaubt, was einem die Arbeit oft ungemein erleichtert.
    Dasselbe gilt natürlich für Klassen aus anderem "fremden" Code. Also diverse Libraries ala Boost, Crypto++, Xerces, ICU etc.
    Entweder du musst auch hier die exakte zu verwendende Version der Library vorschreiben, oder du musst im Interface auf die Libraries verzichten.
    (In der Implementierung kannst du natürlich verwenden was du magst!)



  • Cool.
    Danke 🙂
    Werde das dann mal probieren.

    Gruß



  • Hi... Da ich heute mal mit einem Projekt dieser Art beginne, wollte ich fragen, ob dieser Ansatz so richtig ist:

    // öffentliche Interfacedatei - ITest.hpp
    class ITest
    {
    public:
    	virtual int GetRuntimeInfo();
    	virtual void SetBla();
    	virtual void OnListen() = 0;
    };
    
    // interne Verarbeitung - ITest.cpp
    // CTest *pTest; von CTest.hpp
    int ITest::GetRundtimeInfo()
    {
    	return pTest->GetRuntimeInfo();
    }
    
    void ITest::SetBla()
    {
    	pTest->SetBla();
    }
    
    // interne Headerdatei - CTest.hpp
    class CTest
    {
    public:
    	int GetRuntimeInfo();
    	void SetBla();
    };
    
    extern CTest *pTest;
    
    // interne Verarbeitung - CTest.cpp
    CTest *pTest = new CTest;
    
    int CTest::GetRundtimeInfo()
    {
    	return 0;
    }
    
    void CTest::SetBla()
    {
    	//machwas
    }
    

    Desweiteren würde ich gerne wissen...
    ...wann genau ich virtual benutzen sollte?
    ...was ich machen soll, wenn man eine virtuelle Funktion überladen kann, aber nicht muss? In diesem Beispiel müsste man OnListen ja überladen.

    Gruß & Danke


Anmelden zum Antworten