c++ plugins



  • hi, hat jemand eine idee wie ich in linux mit c++ plugins machen kann. also eine
    main-anwendung und dann mittem im programm plugins dazuladen kann und entladen
    kann. die sollten so wie dlls unter windows funktionieren, also das ich funktionen von der main-anwendung aus dem plugin ansprechen kann und umgekehrt.
    ambesten auch das man die funktion via string, also dynamisch aufrufen kann... jemand eine idee?

    mfg blan



  • Evtl ist dieser Thread von Interesse für Dich.



  • jo genau sowas hab ich gesucht, aber hat noch jemand ein plan ich aus dem modul / plugin ne funktion / klasse aus der main-anwendung aufrufen kann?

    mfg blan



  • Deine Frage ist ein wenig unverständlich formuliert, aber ich interpretiere sie mal so, daß Du aus der shared-library eine Funktion Deines Hauptprogrammes aufrufen möchtest.

    Das Problem ist, daß das Modul ja praktisch gegen Dein Hauptprogramm gelinkt werden müsste, was natürlich nicht geht. Ich schlage vor, Du lagerst alle gemeinsam genutzten Funktionen in eine getrennte shared-library aus, gegen die Du dann sowohl dein Hauptprogramm und deine shared-library linken kannst.

    Ich habe genau das in meinem Tntnet (http://www.tntnet.org/) getan. Tntnet ist ein Webapplikationsserver für C++. Die eigentliche Webapplikation ist dabei ein Modul, welches über dlopen geladen wird. Das Modul muß natürlich Funktionen aus dem Hauptprogramm aufrufen, um z. B. seine Ausgabe zu senden oder Informationen über den Request zu bekommen.

    Das System sieht dann so aus, daß ich ein Hauptprogramm tntnet und eine shared-library libtntnet.so habe. Die Module werden, wie auch das tntnet selbst, gegen die libtntnet.so gelinkt.

    Tommi



  • tntnet schrieb:

    Das Problem ist, daß das Modul ja praktisch gegen Dein Hauptprogramm gelinkt werden müsste, was natürlich nicht geht. Ich schlage vor, Du lagerst alle gemeinsam genutzten Funktionen in eine getrennte shared-library aus, gegen die Du dann sowohl dein Hauptprogramm und deine shared-library linken kannst.

    Ich benutze eigentlich schon sehr lange eine Plugin-API, von der aus es problemlos möglich ist, Funktionen aus dem Hauptprogramm aufzurufen, da der gesamte Symbolraum des ladenden Programms an die DLL vererbt werden kann und umgekehrt. Das lässt sich zwar beim dlopen durch die Flags beeinflussen, ist aber keinesfalls nicht möglich 😉



  • LordJaxom schrieb:

    tntnet schrieb:

    Das Problem ist, daß das Modul ja praktisch gegen Dein Hauptprogramm gelinkt werden müsste, was natürlich nicht geht. Ich schlage vor, Du lagerst alle gemeinsam genutzten Funktionen in eine getrennte shared-library aus, gegen die Du dann sowohl dein Hauptprogramm und deine shared-library linken kannst.

    Ich benutze eigentlich schon sehr lange eine Plugin-API, von der aus es problemlos möglich ist, Funktionen aus dem Hauptprogramm aufzurufen, da der gesamte Symbolraum des ladenden Programms an die DLL vererbt werden kann und umgekehrt. Das lässt sich zwar beim dlopen durch die Flags beeinflussen, ist aber keinesfalls nicht möglich 😉

    jo mit DLLs hab ich des auch geschafft aber leider will ich das unter linux und net unter windows machen. och werd wohl alle wichtigen funktionen in eine extra shared-library auslagern und es so machen wie es tntnet gesagt hat.. danke

    mfg blan



  • ein *.so macht nur Sinn um Funktionen auszulagern die man öfters brauchen kann. Insbesondere wenn mehrere Programme/Prozesse auf diese zugreifen wollen. Spart auch speicher.
    warum willst du da auf dein Mainprogramm zugreifen?
    Schreibt dir ein SO und lade dieses mit dlload dynamisch. Also nicht linken. Willst du wieder funktionen haben kannst du zu dieser dann wieder in der SO linken oder dyn laden u.s.w.
    Das Mainprogramm soll ja nur GUI (falls nötig und die Plugins verwalten)
    Braucht deine Main daten aus der SO dann gib diese zurück und füttere deine Mainfunktion damit.



  • Hier mal meine Klasse welche sowas übernimmt.

    #ifndef __DLLMAnager_H
    #define __DLLMAnager_H
    
    #include <string>
    #include <cstdio>
    #include <map>
    #include <dlfcn.h>
    
    using namespace std;
    
    class DLLManager
    {
    private:
    
    public:
    
        DLLManager();
        ~DLLManager();
    
        void *m_pDLLhInstance;
    
        bool LoadDLL(string szFileName);
        unsigned int InitDLL();
        bool FreeDLL();
        string m_szDLLFileName;
        void SetDLLFileName(string szFileName);
    
        typedef map<std::string, std::string> m_mapdaten;
        m_mapdaten mdaten;
    
    };
    #endif
    
    #include "dllmanager.h"
    
    typedef int (*PINITFUNC)(map<std::string, std::string> *m_smsdaten);
    
    DLLManager::DLLManager()
    {
        m_pDLLhInstance = NULL;
    }
    
    DLLManager::~DLLManager()
    {
        if (FreeDLL() == false)
        {
        }
    
        m_pDLLhInstance = NULL;
    }
    
    bool DLLManager::LoadDLL(string szFileName)
    {
    m_szDLLFileName = szFileName;
    
    void *hModule = dlopen(szFileName.c_str(),RTLD_LAZY);
    
        if (!hModule)
        {
    //	cout << "Lib nicht gefunden" << endl;	
    	return false;
        }
        else
        {
    	m_pDLLhInstance = hModule;
        }
    return true;
    }
    
    bool DLLManager::FreeDLL()
    {
    
        if (m_pDLLhInstance)
        {
    
    	PINITFUNC pDestroyFunc = (PINITFUNC)dlsym(m_pDLLhInstance,"DestroyPlugIn");
    	if (!pDestroyFunc)
    	{
    //	    cout << "Funktion nicht gefunden" << dlerror() << endl;
    	}
    	else
    	{
    	    pDestroyFunc(&mdaten);
    	}
    
    	dlclose(m_pDLLhInstance);
    	m_pDLLhInstance = NULL;
    	m_szDLLFileName.empty();
        }
    return true;
    }
    
    unsigned int DLLManager::InitDLL()
    {
    
        if (m_pDLLhInstance)
        {
    
    	PINITFUNC pInitFunc = (PINITFUNC)dlsym(m_pDLLhInstance,"SetMap");
    	if (!pInitFunc)
    	{
    	    FreeDLL();
    	    return 0;
    	}
    	else
    	{
    	    return pInitFunc(&mdaten);
    	}
        }
    return true;
    }
    

    Die Klasse übernimmt auch eine MAP um Werte zu übergeben.
    Durch die MAP ist man flexible in der Anzahl der Werte.
    Man kann dann auch über die MAP Werte zurückgeben.



  • hi, ich wollte egtl solche plugins und modules machen wie sie bei http://www.amxmodx.org gemacht sind, diese plugins sind glaub in SmallC geschrieben, da ich aber net wirklich durch den Source blicke versteh ich net wie diese geladen und benutzt werden - habt ihr eine idee?

    mfg blan



  • blan schrieb:

    jo mit DLLs hab ich des auch geschafft aber leider will ich das unter linux und net unter windows machen. och werd wohl alle wichtigen funktionen in eine extra shared-library auslagern und es so machen wie es tntnet gesagt hat.. danke

    Sorry für's Missverständnis, ich meinte auch Linux. DLL hat sich halt auch in unserem Projekt statt Shared Object durchgesetzt.



  • Meine cxxtools (http://www.tntnet.org/cxxtols.hms) enthalten ein paar Klassen, die für Deine Plugins hilfreich sein könnten. Ich benutze das auch in Tntnet. Hier mal ein minimales Codebeispiel:

    plugin.h - die Plugin-Schnittstelle

    #ifndef PLUGIN_H
    #define PLUGIN_H
    
    class Plugin
    {
      public:
        virtual void doSomething() = 0;
    };
    
    #endif // PLUGIN_H
    

    myplugin.cpp - ein Beispiel-Plugin:

    #include "plugin.h"
    #include <iostream>
    
    class MyPlugin : public Plugin
    {
      public:
        void doSomething();
    };
    
    void MyPlugin::doSomething()
    {
      std::cout << "Here is MyPlugin" << std::endl;
    }
    
    MyPlugin myplugin;
    

    plugincaller.cpp - ein Programm, welches das Plugin aufrufen kann:

    #include <cxxtools/dlloader.h>
    #include <map>
    #include <iostream>
    #include "plugin.h"
    
    // Es ist wichtig, daß die Libraries in einem Container gehalten werden,
    // da der Destruktor von cxxtools::dl::Library die shared-library entladen
    // würde. Die Klasse cxxtools::dl::Library hat einen Referenzzähler, so daß
    // sie kopierbar ist und damit für einen STL-Container geeignet ist.
    typedef std::map<std::string, cxxtools::dl::Library> libraries_type;
    static libraries_type libraries;
    
    Plugin* loadPlugin(const std::string& plugin)
    {
      cxxtools::dl::Library lib;
    
      // schauen wir erst mal, ob die Library schon geladen wurde
      libraries_type::const_iterator it = libraries.find(plugin);
      if (it == libraries.end())
      {
        // Mit diesem Aufruf wird die Library geladen:
        lib = cxxtools::dl::Library(plugin.c_str());
        libraries[plugin] = lib;
      }
      else
        lib = it->second;
    
      cxxtools::dl::Symbol sym = lib.sym(plugin.c_str());
      return static_cast<Plugin*>(sym.getSym());
    }
    
    void callPlugin(const std::string& plugin)
    {
      Plugin* p = loadPlugin(plugin);
      p->doSomething();
    }
    
    int main(int argc, char* argv[])
    {
      try
      {
        for (int a = 1; a < argc; ++a)
          callPlugin(argv[a]);
      }
      catch (const std::exception& e)
      {
        std::cerr << e.what() << std::endl;
      }
    }
    

    Makefile

    CXXFLAGS=-Wall -pedantic
    
    all: myplugin.so plugincaller
    
    myplugin.o: CXXFLAGS+=-fPIC
    plugincaller: LDFLAGS=-lcxxtools
    
    myplugin.so: myplugin.o
            g++ -shared -o myplugin.so myplugin.o
    
    plugincaller: plugincaller.o
    

    Wenn cxxtools installiert ist und diese Dateien in ein Verzeichnis gepackt sind, baut ein Aufruf von "make" ein Plugin myplugin.so und ein Programm plugincaller. Um das Plugin aufzurufen ist dieses Kommando notwendig:

    LD_LIBRARY_PATH=. ./plugincaller myplugin
    

    Das Plugin meldet sich daraufhin auf der Konsole. Man bemerke, daß plugincaller nicht mit myplugin.so gelinkt ist. Ein Plugin läßt sich unabhängig von plugincaller erstellen und aufrufen.

    Tntnet


Anmelden zum Antworten