Introducere
Am observat ca nu prea sunt articole despre cum sa faci un sistem de plugin-uri simplu, eficient si usor de adaugat in orice aplicatie C++ (in acest articol), asa ca am sa incerc sa va prezint modelul la care m-am stabilit eu dupa multe alte modele mai mult sau mai putin reusite. Voi fi scurt si la obiect, nu-mi place sa vorbesc / scriu prea mult

.
Arhitectura sistemului de plugin-uriEste formata din cateva clase: PluginClassInfo, PluginInfo, Plugin si PluginManager ( singleton anyone?

, no it's not )
Plugin-urile sunt defapt DLL-uri care sunt incarcate de PluginManager, acesta cauta o functie speciala in acel DLL, de exemplu:
Cod sursă:
PluginInfo* __declspec(dllexport) __getPluginInfo() { return (PluginInfo*) &g_thisPluginInfo; }
unde g_thisPluginInfo este o clasa derivata din PluginInfo, care specifica info despre acel plugin.
Fiecare plugin DLL poate contine mai multe clase-plugin, sa luam exemplu un plugin dll numit extendedEntities.dll, in care ai mai multe clase-plugin: MonsterEntity, VehicleEntity, SkyboxEntity ..etc
Clasa interfata PluginClassInfo, care tine informatii despre o clasa-plugin, sa ii zicem un fel de plugin-class-descriptor ( surse din motorul meu Nytro ):
Cod sursă:
//! This class holds the info about a C++ class from inside a plugin
class NYTRO_API PluginClassInfo
{
public:
//! returns am instance of the class
virtual void* newInstance() = 0;
//! returns the super class ID, see nyCommon.h
virtual long getSuperClassID() = 0;
//! returns the class title string
virtual char* getTitle() = 0;
//! returns the class name as it is in C++
virtual char* getClassName() = 0;
//! returns the class description, comments
virtual char* getDescription() = 0;
//! returns the class version number
virtual int getVersion() = 0;
//! returns user data pointer
virtual void* getUserData() = 0;
};
- cand se apeleaza newInstance prin intermediul PluginManager vei obtine o instanta la acea clasa plugin
- super class ID e un ID de genul: ID_ENTITY, ID_EDITOR, ID_UTILITY etc, care specifica clasa de baza din care plugin-class-ul e derivat
- classname este numele C++ al clasei, exact asa cum se numeste in codul sursa, trebuie specificat neaparat deoarece asta e un fel de UID ( unique ID ), prin care acea clasa este identificata in sistemul de plugins
- restul sunt diverse informatii despre clasa.
Urmatoarea clasa este PluginInfo care tine informatii despre acel DLL-plugin si clasele plugin exportate de acesta:
Cod sursă:
//! Plugin info class that holds the info about a dynamic library plugin
class NYTRO_API PluginInfo
{
public:
//! get the number of classes hosted by the plugin
virtual int getClassCount() = 0;
//! get the class info object for the class from the aIndex
virtual PluginClassInfo* getClassInfo( int aIndex ) = 0;
//! get the plugin title string
virtual char* getTitle() = 0;
//! get the plugin description string
virtual char* getDescription() = 0;
//! get the plugin copyright string
virtual char* getCopyright() = 0;
//! get the plugin version
virtual int getVersion() = 0;
//! get the plugin package name, some kind of plugin grouping,
//! for example provider plugins have "PROVIDER_PACKAGE"
virtual char* getPackageTypeName() = 0;
//! get this plugin's user data
virtual void* getUserData() = 0;
};
Se intelege de la sine ce semnificatie are fiecare membru.
Apoi clasa Plugin care tine toate aceste date, dupa ce pluginul a fost incarcat de PluginManager:
Cod sursă:
//! A plugin holder class, from a dynamic library
class NYTRO_API Plugin
{
public:
Plugin();
Plugin( const char* pFile );
virtual ~Plugin();
//! returns the dynamic library handle
LibraryHandle getLibHandle();
//! returns the dynamic library file name
string& getLibFile();
//! returns this plugin's info class
PluginInfo* getPluginInfoClass();
//! loads a dynamic library plugin
Result load( const char* pFile );
//! free the dynamic library plugin
Result free();
//! sorts and retrieves the plugin classes which have the specified super class ID, into rClasses
//! return the number of classes found, or zero if none found
int sortBySuperClassID( long aSuperClassID, vector& rClasses );
private:
LibraryHandle m_hLib;
PluginInfo* m_pPluginInfo;
vector m_classesInfo;
string m_libFile;
//! the plugin entry point exported function from the dynamic library, retrieved with getProcAddress
PluginInfo* (*getPluginInfo)();
};
La fel e o clasa simpla, cu diverse metode utilitare care nu necesita vreo explicatie

.
In cele din urma managerul central care incarca si tine lista plugin-urilor, si care arata cam asa:
Cod sursă:
//! A plugin manager, holds an array of Plugin objects
class NYTRO_API PluginManager
{
public:
PluginManager
();
//! creates the object and loads all plugins from pDir using the pFileMask, ex.: "*.nyp"
PluginManager
( const char* pDir,
const char* pFileMask
);
virtual ~PluginManager
();
//! load all plugins from pDir using the pFileMask, ex.: "*.nyp"
int loadPlugins
( const char* pDir,
const char* pFileMask
);
//! free the loaded plugins and their dynamic libraries
Result freePlugins
();
//! sorts and retrieves from all the plugins the classes that have super class ID as aSuperID
//! return the number of found classes
int sortBySuperClassID
( long aSuperID, vector& rList
);
//! get the plugins array
vector& getPlugins
();
//! get the class info for a specified class name residing in one of the plugins, or NULL if none found
PluginClassInfo* getClassInfo
( const char* pClass
);
private:
// ( ) in loc de mai mare si mai mic, un bug in gdro 
vector
(Plugin*
) m_plugins;
};
Inca odata, explicatiile sunt in comentariile metodelor.
In continuare sa facem un exemplu de DLL plugin cu 2 clase exportate:
MyPluginClass1.h
Cod sursă:
class MyPluginClass1 : public Entity
{
public:
.................
}
MyPluginClass2.h
Cod sursă:
class MyPluginClass2 : public Entity
{
public:
.................
}
MyPluginPack.cpp
Cod sursă:
class NYTRO_API MyPluginClass1Info : public PluginClassInfo
{
public:
void* newInstance() { return new MyPluginClass1; }
long getSuperClassID() { return SUPER_ID_ENTITY; }
char* getTitle() { return "My Cool Entity 1"; }
char* getClassName() { return "MyPluginClass1"; }
char* getDescription() { return "No smart description here"; }
int getVersion() { return 1; }
void* getUserData() { return NULL; }
};
class NYTRO_API MyPluginClass2Info : public PluginClassInfo
{
public:
void* newInstance() { return new MyPluginClass2; }
long getSuperClassID() { return SUPER_ID_ENTITY; }
char* getTitle() { return "My Cool Entity 2"; }
char* getClassName() { return "MyPluginClass2"; }
char* getDescription() { return "No smart description here"; }
int getVersion() { return 1; }
void* getUserData() { return NULL; }
};
// declaram cele doua class-info
MyPluginClass1Info g_MyPluginClass1Info;
MyPluginClass2Info g_MyPluginClass2Info;
// vectorul cu lista de clase ale plugin DLL-ului
PluginClassInfo* g_thisPluginClassInfos[] = { &g_MyPluginClass1Info, &g_MyPluginClass2Info };
// clasa care contine informatiile despre acest DLL plugin
class NYTRO_API ThisPluginInfo : public PluginInfo
{
public:
int getClassCount() { return sizeof( g_thisPluginClassInfos )
sizeof( g_thisPluginClassInfos[0] ); }
PluginClassInfo* getClassInfo( int aIndex ) { return g_thisPluginClassInfos[aIndex]; }
char* getTitle() { return "My Plugin Pack"; }
char* getDescription() { return "This plugin pack contains 2 classes! yay!"; }
char* getCopyright() { return "(C) None"; }
int getVersion() { return 1; }
char* getPackageTypeName() { return "ENTITY_PACKAGE"; }
void* getUserData() { return NULL; }
};
// declaram obiectul pentru plugin info
ThisPluginInfo g_thisPluginInfo;
// entry-point care va fi chemat de PluginManager, daca nu e gasit atunci acest DLL nu va fi considerat plugin
PluginInfo* NYTRO_API __getPluginInfo() { return (PluginInfo*) &g_thisPluginInfo; }
Pentru definirea acelor clase se pot face si niste #define-uri care sa usureze viata, cu ele ar arata cam asa:
Cod sursă:
PLUGIN_CLASS_INFO( MyPluginClass1, SUPER_ID_ENTITY, "My Cool Entity 1", "No smart description here", 1, NULL );
PLUGIN_CLASS_INFO( MyPluginClass2, SUPER_ID_ENTITY, "My Cool Entity 2", "No smart description here", 1, NULL );
BEGIN_PLUGIN_CLASSES
ADD_PLUGIN_CLASS( MyPluginClass1 )
ADD_PLUGIN_CLASS( MyPluginClass2 )
END_PLUGIN_CLASSES;
PLUGIN_INFO( "My Plugin Pack", "This plugin pack contains 2 classes! yay!", "(C) None", 1, "ENTITY_PACKAGE", NULL );
Utilizarea PluginManager si instantierea unui plugin intr-un game.exe de exemplu:
Cod sursă:
pluginMan->loadPlugins( "/plugins" );
.....................
PluginClassInfo* classInfo = pluginMan->getClassInfo( "MyPluginClass1" );
// daca exista
if( classInfo )
{
// clasa de baza este entity, pentru ca din ea am derivat MyPluginClass1
Entity* pluggedEntity = (Entity*)classInfo->newInstance();
// voila!, new plugin entity, use it!
}
Cam asta ar fi, sper ca s-a inteles cat de cat

.
Astept intrebari si comentarii.