Introducing the ModuleManager
#pragma once
/**
* \file file base name
* \author Adam Martin
* \date 2011-10-23
* \brief A manager class to load and start modules.
*
* A manager that can load/unload and start/stop modules at runtime through dynamically loaded
* libraries. The use of a common interface ModuleInterface allows us to have a uniform loading
* and starting procedure.
*/
// Standard Includes
#include
#include
// Library Includes
#include // Quick simple looping
// Local Includes
#include "../SharedBase/MessageRouter.h" // Needed in several locations throughout the header
// Forward Declarations
class GlobalProperties;
class ModuleInterface;
class EntityManager;
// Typedefs
typedef ModuleInterface* (*ModuleInstanceFactory)(GlobalProperties*, MessageRouter*, EntityManager*); // Used to find the address of the create system function
// Preprocessor selection based on OS
#ifdef _WIN32
#include
typedef HMODULE DLLHANDLE;
#else
#include
typedef void* DLLHANDLE;
#endif
class ModuleManager {
public:
ModuleManager(GlobalProperties* gprops, MessageRouter* msgrouter, EntityManager* emgr);
void Load(std::string name); // The name is required in order to load the new library
void Unload(std::string name = "");
void Update(double dt = 0.0f);
private:
GlobalProperties* gprops;
MessageRouter* msgrouter;
EntityManager* emgr;
std::map modules; // A mapping of each core to its given name
std::map libraries; // A mapping of each loaded library to a given filename
};
*Note that any non-windows code as not been tested, and I really don't know if it works.
This is the meat of the module manager. Simple put you create an instance, call Load/Unload respectively to load a module with the given filename.
The actual meat of the code is as follows:
/**
* \file file base name
* \author Adam Martin
* \date 2011-10-23
* \brief A manager class to load and start modules.
*
* A manager that can load/unload and start/stop modules at runtime through dynamically loaded
* libraries. The use of a common interface ModuleInterface allows us to have a uniform loading
* and starting procedure.
*/
#include "ModuleManager.h"
// Standard Includes
// Library Includes
#include
#include
// Local Includes
#include "../sharedbase/ModuleInterface.h"
#include "../sharedbase/EventLogger.h"
#include "../sharedbase/EntityManager.h"
#include "../ScriptDLL/EntityFactory.h"
// Static class member initialization
// Class methods in the order they are defined within the class header
ModuleManager::ModuleManager( GlobalProperties* gprops, MessageRouter* msgrouter, EntityManager* emgr) : gprops(gprops), msgrouter(msgrouter), emgr(emgr) {
}
void ModuleManager::Load(std::string name) {
char buf[256];
if (this->libraries.find(name) == this->libraries.end()) {
// Load the DLL ONLY if the libary has not already been loaded in the past.
#ifdef _WIN32
HMODULE libdll = LoadLibrary(name.c_str());
#else
void * libdll = dlopen(fname.c_str(), RTLD_LAZY);
#endif
this->libraries[name] = libdll;
if (libdll != NULL) {
LOG(1, "Loaded library '" + name + "' successfully.");
}
else {
#ifdef _WIN32
DWORD errcode = GetLastError();
FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, errcode, 0, buf, 256, NULL);
#else
buf = "Error loading library: " + name;
#endif
LOG(4, buf);
LOG(4, "Module loading aborted due to error.");
return; // *NOTE: This early return may be considered bad style, but was added to maintain modularity between the DLL loader code and the module facotry loading code.
}
}
else {
LOG(2, "Library '" + name + "' already loaded, not reloading.");
}
{
ModuleInstanceFactory fact;
#ifdef _WIN32
fact = (ModuleInstanceFactory)GetProcAddress(this->libraries[name], "ModuleFactory");
#else
fact = (ModuleInstanceFactory)dlsym(this->libraries[name], "ModuleFactory");
#endif
if (fact != nullptr) {
LOG(1, "Module factory acquired successfully.");
ModuleInterface* module = fact(this->gprops, this->msgrouter, this->emgr);
this->modules[name] = module;
}
else {
#ifdef _WIN32
DWORD errcode = GetLastError();
FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, errcode, 0, buf, 256, NULL);
#else
buf = "Error loading module factory";
#endif
LOG(4, buf);
LOG(4, "Module loading aborted due to error.");
Unload(name);
return;
}
}
}
void ModuleManager::Unload( std::string name /*= ""*/ ) {
if (this->libraries.find(name) != this->libraries.end()) { // Lib WAS found
if (this->modules.find(name) != this->modules.end()) { // Mod WAS found
//Shutdown(name);
}
#ifdef _WIN32
if (FreeLibrary(this->libraries[name]) != 0) {
#else
if (dlclose(this->libraries[name])) {
#endif
this->libraries.erase(name);
}
else {
LOG(4, "Unable to unload the library '" + name + "'!");
}
}
}
void ModuleManager::Update( double dt /*= 0.0f*/ ) {
for (auto it = this->modules.begin(); it != this->modules.end(); ++it) {
(*it).second->Update(dt);
}
}
*Note LOG is located in a separate header and just logs a message with a certain log level.
In the loading process a function pointer to get an instance of the modules ModuleInterface is found and called. ModuleManager simple loads and unloads modules and there respective library files from memory.