Sign in to follow this  
hymerman

Maintaining state across a DLL boundary - help very much needed

Recommended Posts

hymerman    221
Hi all, I'm now abandoning my all-singing all-dancing classloader I'm having troubles with in this thread (unless someone gives me a solution!), and using an inferior but simpler alternative instead. However, I'm having absolutely no luck getting anything to work across a DLL boundary, full stop. Can any of you tell me a foolproof way to make sure (as an example) to ensure something added to a std::map in the main application from code in a DLL stays in the map for use by the rest of the application? A bit of code will go a very long way. I've tried all sorts of combinations of __declspecs and externs and statics and compiler/linker settings, but it just won't work. If it's of any use, I'm using MSVC2005. Thanks in advance!

Share this post


Link to post
Share on other sites
krum    255
You must use an exported accessor function, or an interface, to the instance of the map in the dll - otherwise, I'm guessing the problem you're having, is that the map is getting instantiated in each module.

So, for example, in my main DLL I have some code that looks like this in a source file called Factory.cpp


namespace
{
typedef std::map< UUID, Factory::FactoryFunc > FactoryMapT;

FactoryMapT& GetFactoryMap()
{
static FactoryMapT fm;
return fm;
}
}

void Factory::Register( const UUID& name, FactoryFunc func )
{
FactoryMapT& fm = GetFactoryMap();
Debug::Assert( fm.find( name ) == fm.end(),
L"That CLSID has already been registered." );
fm.insert( FactoryMapT::value_type( name, func ) );
}




And in my Factory.h file, I export a couple of methods of the Factory class:


class Factory
{
public:
typedef Object*(*FactoryFunc)();
CORE_API static void Register( const UUID& clsid, FactoryFunc func );
CORE_API static Object* CreateObject( const UUID& clsid );
};



note, that CORE_API is defined as


#if defined WIN32
#if defined CORE_EXPORTS
#define CORE_API __declspec( dllexport )
#else
#define CORE_API __declspec( dllimport )
#endif
#elif defined LINUX
#define CORE_API
#else
#error "platform not supported."
#endif



Hope this helps.

Share this post


Link to post
Share on other sites
rip-off    10976
From what I've heard you shouldn't pass STL objects across DLL boundaries.

Something to do with the DLL having a seperate heap.

I wish I could be more specific, but Im sure someone more knowlegable will be able to expand on ( or disprove [smile] ) what I'm saying.

Share this post


Link to post
Share on other sites
krum    255
Quote:
Original post by rip-off
From what I've heard you shouldn't pass STL objects across DLL boundaries.

Something to do with the DLL having a seperate heap.

I wish I could be more specific, but Im sure someone more knowlegable will be able to expand on ( or disprove [smile] ) what I'm saying.


This is basically true as a general rule. You never want to pass a reference to or a copy of an STL object across a DLL boundary unless you guarantee yourself that everything is built with the same STL code and C runtime libraries.

But this also holds true with a variety of other objects, not just STL.

Share this post


Link to post
Share on other sites
hymerman    221
Ah, don't worry, that was just an example. Actually I'm doing much the same thing as you; passing a pointer to a factory function and an identifier for that, to be registered in a factory. I can step through the code and see that the classes in the DLLs are statically calling the register methods, but when I come to read from the factory during main (as a test), there is nothing in it. I'm sure it's to do with the factory getting instantiated multiple times in different address spaces, but I didn't know how to stop this.

I do have a quick question for you, krum: where are each of those code snippets from? Which are compiled into DLLs and which are part of the application/API? What I guess I'm really asking is what parts need to be exported, and where?

Share this post


Link to post
Share on other sites
Dmytry    1151
Basically, what you must do is ensure that you don't have two things one in main program other in DLL. It looks like your problem is that your global data exist in two instances one in the DLL and other in main app. There isn't any magical transfers of changes. It's just that either main app and dll is using two different or two same datas.

Instead of putting line like
std::map lalala;
in header, you must put something like that in header
[dllexport or whatever] std::map &GetLalala();
and put the
std::map lalala;
only in one dll or only in main app , together with implementation of GetLalala(); that should return it.

Share this post


Link to post
Share on other sites
krum    255
Quote:
Original post by hymerman
krum: where are each of those code snippets from? Which are compiled into DLLs and which are part of the application/API? What I guess I'm really asking is what parts need to be exported, and where?


Factory.cpp is compiled into a core.dll.

Consumers would need to include Factory.h - which also includes some static methods to ease object creation. In fact, here's the whole Factory.h:


#ifndef included_pyx_core__Factory_h
#define included_pyx_core__Factory_h

#include "core/Core.h"
#include "core/Object.h"

BEGIN_NAMESPACE_PYX;

//////////////////////////////////////////////////////////////////////////

class Factory
{
public:
typedef Object*(*FactoryFunc)();
CORE_API static void Register( const UUID& clsid, FactoryFunc func );
CORE_API static Object* CreateObject( const UUID& clsid );
};

// --------------------------------------------------------------------------

template< class T >
inline T* CreateObject()
{
Object* obj = Factory::CreateObject( T::GetClassID() );
return static_cast<T*>( obj );
}

// --------------------------------------------------------------------------

template< class T >
inline bool CreateObject( const UUID& clsid, T** result )
{
*result = static_cast<T*>( Factory::CreateObject( clsid ) );
return *result != 0;
}

// --------------------------------------------------------------------------

template< class T >
inline bool CreateObject( const UUID& clsid, boost::shared_ptr<T>& result )
{
T* obj = static_cast<T*>( Factory::CreateObject( clsid ) );
result = boost::shared_ptr< T >( obj );
return obj != 0;
}

// --------------------------------------------------------------------------

template< class T >
class AutoRegister
{
public:
AutoRegister()
{
Factory::Register( T::GetClassID(), T::CreateObject );
}
};

// --------------------------------------------------------------------------

END_NAMESPACE;

#endif // included_pyx_core__Factory_h





Consumers usually include Factory.h either in their precompiled header, or in a source file that uses it (since objects that register with Factory use the AutoRegister class). Usually the only consumer for the Factory is the serialization engine; an application developer wouldn't normally use this to create an object unless they wanted to create an object that had an abstract base type (like the renderer).

Share this post


Link to post
Share on other sites
krum    255
Also, come to think of it, is your map instance in a static library that you're linking with the DLLs? If so, that would definitely result in the behavior that you describe.

Share this post


Link to post
Share on other sites
hymerman    221
Thanks very much for that, krum, I think that's pretty much exactly how I'm going to have to implement it now.

Dymytry: Okay, I now see why I have to do it this way. If my actual code were as simple as just a single std::map used as a factory, I'd do that straight away, but unfortunately I'm using a templated class, and my understanding of templated classes is that the implementation must go in the same header file, as mentioned at the bottom of this page. So, is what I'm trying to do basically impossible? Do the requirements of having to separate implementation and declaration, as well as keep them together conflict to such a point that I have to choose one or the other? It's such a shame if that's the case, since otherwise I'd have a pretty swish classloader :(

Share this post


Link to post
Share on other sites
Guest Anonymous Poster   
Guest Anonymous Poster
Actually they don't have to go into the same header. I use .inl files for "inline" code like templates. You just put in a #include "somefilename.inl". I find it keeps code a lot cleaner and does the same thing as having it in the header. Anyways, hope your issue is solved.

Share this post


Link to post
Share on other sites
Guest Anonymous Poster   
Guest Anonymous Poster
Actually they don't have to go into the same header. I use .inl files for "inline" code like templates. You just put in a #include "somefilename.inl". I find it keeps code a lot cleaner and does the same thing as having it in the header. Anyways, hope your issue is solved.

Share this post


Link to post
Share on other sites
hymerman    221
Ah, that's a good idea. I'll do that from now on, actually, I've always thought this way of doing things was a bit messy!

But still, does it solve the problem? It all still has to be compiled into each thing that uses it (the app and each DLL, that is), meaning there will be a separate copy of all static things, which is not what I want at all. Is there any way around this at all? Can some clever C++ guru help out here?

Share this post


Link to post
Share on other sites

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