Sign in to follow this  

Unity Need help getting classloader to work across DLL boundary

This topic is 4021 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Hi again, folks, Sorry for the long post, but I'm in need of a bit of help, so I'd really appreciate it if you'd read it. I need a classloader for my project, since it fits the plugin architecture perfectly and will (hopefully) simplify our current method of loading plugins. I'm basing my code pretty much exclusively on the s11n classloader, specifically the version presented in this paper: http://s11n.net/papers/classloading_cpp.html I've taken that code, rearranged it a little, refactored slightly, but it is still functionally equivalent - the one thing I have changed is the static object factory map, from a reference to a pointer, since it wouldn't compile otherwise. Now, I hate people that post all their code up and expect others to read through it all, but I'm really in desperate need here, and I promise it'll only happen this once! My exact code is shown below: ObjectFactory.h
#ifndef OBJECTFACTORY_H
#define OBJECTFACTORY_H

template <typename BaseT, typename SubT = BaseT> // SubT must be-a BaseT.
struct ObjectFactory {
	typedef BaseT resultType;
	typedef SubT actualType;

	static resultType* newInstance() { return new actualType; }
	static resultType* noInstance() { return 0; }
};

#endif

Instantiator.h
#ifndef INSTANTIATOR_H
#define INSTANTIATOR_H

#include <map>

template<typename BaseType, typename KeyType = std::string>
class Instantiator {
public:
	typedef BaseType valueType;
	typedef KeyType keyType;
	typedef valueType *( *factoryType )();
	typedef std::map<keyType, factoryType> objectFactoryMap;

	static valueType* instantiate(const keyType & key) {
		typename objectFactoryMap::const_iterator it = getFactoryMap()->find( key );

		if (it != getFactoryMap()->end())
			return (it->second)();

		return 0;
	}

	static void registerFactory( const keyType & key, factoryType fp = 0 ) {
		if( ! fp ) fp = ObjectFactory<valueType>::noInstance;
		getFactoryMap()->insert( objectFactoryMap::value_type( key, fp ) );
	}

	template <typename SubOfBaseType>
	static void registerSubtype( const keyType & key, factoryType fp = 0 ) {
		if( ! fp ) fp = ObjectFactory<valueType,SubOfBaseType>::newInstance;
		registerFactory( key, fp );
	}

	static objectFactoryMap * getFactoryMap() {
		static objectFactoryMap* meyers = new objectFactoryMap;
		return meyers;
	}

	static bool isRegistered( const keyType & key ) {
		return getFactoryMap()->end() != getFactoryMap()->find( key );
	}
};

#endif

Register.h
#ifndef ST_TYPE
#error "You must define ST_TYPE before including this file."
#endif

#ifndef ST_TYPE_NAME
#error "You must define ST_TYPE_NAME before including this file."
#endif

#ifndef ST_BASE_TYPE
#define ST_BASE_TYPE ST_TYPE
#endif

#include "Instantiator.h"
#include "ObjectFactory.h"

// Anonymous namespace for linking reasons
namespace {
	#ifndef ST_REG_CONTEXT
	#define ST_REG_CONTEXT
		// A unique (per Context/per compilation unit) space to assign
		// a bogus value for classloader registration purposes.
		// Included once per compilation unit
		template <typename Context>
		struct stereoshift_reg_context {
			static bool placeholder;
		};

		template<typename Context> bool stereoshift_reg_context<Context>::placeholder = false;
	#endif // ST_REG_CONTEXT

	// Register a factory with the classloader during static initialization
	// Use static dummy variable assigned using comma operator (very cheeky trick)
	bool stereoshift_reg_context< ST_TYPE >::placeholder = (
	#ifdef ST_ABSTRACT_BASE
		// register a no-op factory if registering an abstract class:
		Instantiator<ST_TYPE>::registerFactory(
			ST_TYPE_NAME,
			ObjectFactory< ST_TYPE >::noInstance ),
	#else
		// register the default factory if not abstract:
		Instantiator<ST_BASE_TYPE>::registerSubtype<ST_TYPE>(ST_TYPE_NAME),
	#endif // ST_ABSTRACT_BASE
		true);
}

// Undef everything so header can be included again
#undef ST_TYPE
#undef ST_BASE_TYPE
#undef ST_TYPE_NAME
#ifdef ST_ABSTRACT_BASE
# undef ST_ABSTRACT_BASE
#endif

... And to show you how this would all be used, here is an example pair of classes: ExampleClass.h
#ifndef EXAMPLECLASS_H
#define EXAMPLECLASS_H

#include <iostream>

class ExampleClass {
public:
	ExampleClass() { std::cout << "base constructing" << std::endl; }
	virtual ~ExampleClass() { std::cout << "base destructing" << std::endl; }

	virtual void run() = 0;
};

#define ST_TYPE ExampleClass		// the type we want to register
#define ST_TYPE_NAME "ExampleClass"	// the name of the type (need not be the same as the type)
#define ST_ABSTRACT_BASE 1
#include "Register.h"				// include the supermacro

#endif

ExampleSubclass.h
#ifndef EXAMPLESUBCLASS_H
#define EXAMPLESUBCLASS_H

#include <iostream>
#include "ExampleClass.h"

class ExampleSubclass : public ExampleClass {
public:
	ExampleSubclass() { std::cout << "subclass constructing" << std::endl; }
	virtual ~ExampleSubclass() { std::cout << "subclass destructing" << std::endl; }

	virtual void run() { std::cout << "subclass running" << std::endl; }
};

#define ST_TYPE ExampleSubclass			// the type we want to register
#define ST_BASE_TYPE ExampleClass		// the base type of the type we want to register
#define ST_TYPE_NAME "ExampleSubclass"	// the name of the type (need not be the same as the type)
#include "Register.h"					// include the supermacro

#endif

Now, this all works fine when in my lovely little test project, when things aren't in DLLs. Everything gets registered statically, and available for instantiation through Instantiator<Base>::instantiate("Name");. But when I put into my actual project, and try to register things in DLLs, things go a bit awry. When I run the debugger on the app, I see that as soon as the DLL is loaded (through whatever means), the register functions are called, which is great, but later on the information is not stored in the static object factory map so all instantiations fail. Now, I'm quite certain this is due to the function:
static objectFactoryMap * getFactoryMap() {
	static objectFactoryMap* meyers = new objectFactoryMap;
	return meyers;
}
Could it be that I changed the objectFactoryMap to a pointer rather than a reference? I know there are issues with using 'static' things across DLL boundaries, but I'm hoping somebody could point out what my particular problem is! So please, if you can, tell me what's wrong with my code, I'm getting desperate now! Thanks in advance, and than you for being such an awesome community!

Share this post


Link to post
Share on other sites
Guest Anonymous Poster
i think you don't want to recustruct your static map on every call to getFactoryMap(). Check if the pointer already is valid first.

static map_type * the_map = 0;
static map_type *get_map()
{ if(!the_map) the_map = new map_type; return the_map; }

Share this post


Link to post
Share on other sites
Thanks, that helped (well spotted), but the problem is still there. It seems it's more fundamental, as I've noticed in my other thread. I need to separate out the implementation and definition of some of the methods to avoid the code being compiled into both app and DLL, but the restraint of having to keep these in the same file due to the templated classes prevents me from doing this. Looks like I'm going to have to use an inferior solution!

Unless anyone can suggest a way round this, of course....

Share this post


Link to post
Share on other sites

This topic is 4021 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this