Jump to content

  • Log In with Google      Sign In   
  • Create Account

Banner advertising on our site currently available from just $5!


1. Learn about the promo. 2. Sign up for GDNet+. 3. Set up your advert!






NLS Engine Module Management

Posted by adam4813, 12 December 2011 · 243 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.




July 2015 »

S M T W T F S
   1234
567891011
12131415161718
19202122232425
262728 29 3031 

Recent Entries

Recent Comments

PARTNERS