/* * Copyright (C) 2007-2023 Xagasoft, All rights reserved. * * This file is part of the libbu++ library and is released under the * terms of the license contained in the file LICENSE. */ #ifndef BU_PLUGGER_H #define BU_PLUGGER_H #include "bu/hash.h" #include "bu/list.h" #include "bu/exceptionbase.h" #include "bu/string.h" #include #include "bu/config.h" #ifdef WIN32 # include #else # include #endif namespace Bu { subExceptionDecl( PluginException ); typedef struct PluginInfo { const char *sID; const char *sAuthor; unsigned short nVersion; unsigned short nRevision; void *(*createPlugin)(); void (*destroyPlugin)( void * ); } PluginInfo; typedef struct PluginReg { bool bBuiltin; #ifdef WIN32 HMODULE dlHandle; #else void *dlHandle; #endif PluginInfo *pInfo; } PluginReg; #define PluginInterface( classname, baseclass, name, ver, rev ) \ extern "C" { \ baseclass *create ##classname() \ { \ return new classname(); \ } \ void destroy ##classname( baseclass *pCls ) \ { \ delete pCls; \ } \ Bu::PluginInfo classname = { \ #classname, name, ver, rev, \ create ##classname, destroy ##classname }; \ } #define PluginInterface2( pluginname, classname, baseclass, name, ver, rev ) \ extern "C" { \ baseclass *create ##classname() \ { \ return new classname(); \ } \ void destroy ##classname( baseclass *pCls ) \ { \ delete pCls; \ } \ Bu::PluginInfo pluginname = { \ #pluginname, name, ver, rev, \ (void *(*)())(create ##classname), \ (void (*)( void * ))(destroy ##classname) }; \ } // // This is probably the main interface to use, I'll describe it some here... // structname - The name of the structure, this is what you have to pass to // register. Depending on how you build your dll/so files this // will need to be unique (generally not) // pluginname - This is what will be used by the plugin system to refer to // your plugin once it's loaded. This should be unique, but not // a string // classname - The name of the class that is the plugin // baseclass - The name of the base class that is the parent of the plugin // name - The name of the author of this class (or company) // ver - an integer version number for the plugin // rev - an integer revision number for the plugin // #define PluginInterface3( structname, pluginname, classname, baseclass, name, ver, rev ) \ extern "C" { \ baseclass *create ##classname() \ { \ return new classname(); \ } \ void destroy ##classname( baseclass *pCls ) \ { \ delete pCls; \ } \ Bu::PluginInfo structname = { \ #pluginname, name, ver, rev, \ (void *(*)())(create ##classname), \ (void (*)( void * ))(destroy ##classname) }; \ } /** * A complete dynamic plugin manager system. This will allow you to design * and use plugins that are compiled into your program and dynamically * linked to your program interchangably. It works on windows and on *nix * and bsd type systems (anything that supports dlopen). Basically you * create a base class that will be the basic interface of your plugins. * Then you create some classes that inherit from it, and use the * PluginInterface3 macro to create the required data structures for it. * * Once you have plugins you can create a Plugger, by passing in the base * class as it's template parameter. Once it's created, you can register * plugins. To register a plugin that is builtin, you just need to pass * a pointer to it's interface structure to the registerBuiltinPlugin * function. To register a plugin that is in a shared object or dll file * you just pass the filename (with path, probably), and the name of the * structure to load and you're all set. * * To instantiate an object from a plugin simply call instantiate with the * name of the plugin as specified in the interface macro. To destroy an * object crated with the plugger do not delete it, instead pass it into * Plugger's destroy function. * * Any objects not destroyed when the plugger is deleted will be destroyed * automatically. * * It is important to note that some systems (linux at least) partition off * the memory allocated by objects linked in at run time into a seperate * segment that, while it can be accessed by the main program, cannot be * safely or reliably freed by the main program. With that in mind it is * a good idea to free all memory allocated by a plugin object in the plugin * object and not allow the calling program to delete it. */ template class Plugger { public: typedef Bu::Hash PluginHash; typedef Bu::Hash InstHash; public: Plugger() { } virtual ~Plugger() { for( InstHash::iterator i = hObj.begin(); i != hObj.end(); i++ ) { T *pPlug = (T *)i.getKey(); PluginReg *pReg = (PluginReg *)*i; pReg->pInfo->destroyPlugin( pPlug ); } for( PluginHash::iterator i = hPlugin.begin(); i != hPlugin.end(); i++ ) { if( (*i)->bBuiltin == false ) { #ifdef WIN32 FreeLibrary( (*i)->dlHandle ); #else dlclose( (*i)->dlHandle ); #endif } delete (*i); } } void registerBuiltinPlugin( PluginInfo *pInfo ) { PluginReg *pReg = new PluginReg; pReg->bBuiltin = true; pReg->pInfo = pInfo; hPlugin.insert( pInfo->sID, pReg ); } void registerExternalPlugin( const Bu::String &sFName, const Bu::String &sPluginName ) { PluginReg *pReg; if( hPlugin.has( sPluginName ) ) throw Bu::ExceptionBase("A plugin with name '%s' is already " "loaded.", sPluginName.getStr() ); pReg = new PluginReg; pReg->bBuiltin = false; #ifdef WIN32 pReg->dlHandle = LoadLibrary( sFName.getStr() ); if( pReg->dlHandle == NULL ) { throw PluginException( 1, "Error opening %s: %s", sFName.getStr(), Bu::getLastWinError().getStr() ); } pReg->pInfo = (PluginInfo *)GetProcAddress( pReg->dlHandle, sPluginName.getStr() ); if( pReg->pInfo == NULL ) { throw PluginException( 2, "Error mapping %s: %s", sFName.getStr(), Bu::getLastWinError().getStr() ); } #else pReg->dlHandle = dlopen( sFName.getStr(), RTLD_NOW ); if( pReg->dlHandle == NULL ) { throw PluginException( 1, "Error opening %s: %s", sFName.getStr(), dlerror() ); } pReg->pInfo = (PluginInfo *)dlsym( pReg->dlHandle, sPluginName.getStr() ); if( pReg->pInfo == NULL ) { throw PluginException( 2, "Error mapping %s: %s", sFName.getStr(), dlerror() ); } #endif hPlugin.insert( pReg->pInfo->sID, pReg ); } T *instantiate( const Bu::String &lpName ) { PluginReg *pReg = (PluginReg *)hPlugin[lpName]; if( pReg == NULL ) return NULL; T *p = (T *)pReg->pInfo->createPlugin(); hObj.insert( (ptrdiff_t)p, pReg ); //printf("pReg: %08X, pPlug: %08X\n", pReg, p ); return p; } bool hasPlugin( const Bu::String &lpName ) { return hPlugin.has( lpName ); } void destroy( T *pPlug ) { PluginReg *pReg = (PluginReg *)hObj.get((ptrdiff_t)pPlug); //printf("pReg: %08X, pPlug: %08X\n", pReg, pPlug ); if( pReg == NULL ) return; pReg->pInfo->destroyPlugin( pPlug ); hObj.erase( (ptrdiff_t)pPlug ); } void unloadAll() { for( PluginHash::iterator i = hPlugin.begin(); i != hPlugin.end(); i++ ) { if( (*i)->bBuiltin == false ) { #ifdef WIN32 FreeLibrary( (*i)->dlHandle ); #else dlclose( (*i)->dlHandle ); #endif } delete (*i); } hPlugin.clear(); } Bu::List getPluginList() { return hPlugin.getKeys(); } private: PluginHash hPlugin; InstHash hObj; }; } #endif