Jump to content

  • Log In with Google      Sign In   
  • Create Account





- - - - -

NLS Engine Module Management

Posted by , 12 December 2011 · 482 views

engine C++ componet based entity system CBES
Last entry was about the basics, and the generic module interface. This time I think we need to discuss how the modules are loaded and managed.

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 <string>
#include <map>

// Library Includes
#include <boost/foreach.hpp> // 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 <Windows.h>
typedef HMODULE DLLHANDLE;
#else
#include <dlfcn.h>
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<std::string, ModuleInterface*> modules; // A mapping of each core to its given name
	std::map<std::string, DLLHANDLE> 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 <boost/function.hpp>
#include <boost/any.hpp>

// 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.







Trackbacks for this entry [ Trackback URL ]

There are no Trackbacks for this entry

September 2016 »

S M T W T F S
    123
45678910
11121314151617
18192021222324
2526 27 282930 

Recent Entries

Recent Comments

PARTNERS