C++ Template Singletons in a dll

The "correct" way to do this is...not to use a singleton.

If you want all other code to use the same instance of some type, then give that code a reference to that instance - as a parameter to a function or a constructor.

Using a singleton (non-template) would be exactly the same as using a global variable, a practice you should avoid.

Using a template means the compiler decides how to instantiate the code, and how to access the "instance". The problem you're experiencing is a combination of this and using a static in a DLL.

There are many reasons why singletons are bad, including lifetime issues (when, exactly, would it be safe to delete a singleton?), thread-safety issues, global shared access issues and more.

In summary, if you only want one instance of a thing, only create one instance of it, and pass it around to code that needs it.


Here's a really sketchy solution that you might be able to build from. Multiple templates will be instantiated but they will all share the same instance objects.

Some additional code would be needed to avoid the memory leak (e.g. replace void * with boost::any of shared_ptr or something).

In singleton.h

#if defined(DLL_EXPORTS)
    #define DLL_API __declspec(dllexport)
#else
    #define DLL_API __declspec(dllimport)
#endif

template <class T>
class Singleton
{
public:
  static T &instance()
  {
      T *instance = reinterpret_cast<T *>(details::getInstance(typeid(T)));
      if (instance == NULL)
      {
          instance = new T();
          details::setInstance(typeid(T), instance);
      }

      return *instance;
  }
};

namespace details
{

DLL_API void setInstance(const type_info &type, void *singleton);
DLL_API void *getInstance(const type_info &type);

}

In singleton.cpp.

#include <map>
#include <string>

namespace details
{

namespace
{

std::map<std::string, void *> singletons;

}

void setInstance(const type_info &type, void *singleton)
{
    singletons[type.name()] = singleton;
}

void *getInstance(const type_info &type)
{
    std::map<std::string, void *>::const_iterator iter = singletons.find(type.name());
    if (iter == singletons.end())
        return NULL;

    return iter->second;
}

}

I can't think of a better way right now. The instances have to be stored in a common location.


MSDN says that

Win32 DLLs are mapped into the address space of the calling process. By default, each process using a DLL has its own instance of all the DLLs global and static variables. If your DLL needs to share data with other instances of it loaded by other applications, you can use either of the following approaches:

Create named data sections using the data_seg pragma.

Use memory mapped files. See the Win32 documentation about memory mapped files.

http://msdn.microsoft.com/en-us/library/h90dkhs0%28v=vs.80%29.aspx


The trick that works for me is to add __declspec(dllexport) to the singleton's template definition; split the template implementation from the class definition and only include the implementation in the A DLL; and finally, force the template to be instantiated in the A DLL by creating a dummy function that calls Singleton<Logger>::instance().

So in your A DLL's header file, you define the Singleton template like this:

template <class T>
class __declspec(dllexport) Singleton {
public:
  static T &instance();
};

Then in your A DLL's cpp file you define the template implementation, and force an instantiation of Singleton<Logger> like this:

template <class T>
T &Singleton<T>::instance() {
  static T _instance;
  return _instance;
};

void instantiate_logger() {
  Singleton<Logger>::instance();
}

With my compiler at least, I don't need to call instantiate_logger from anywhere. Just having it exist forces the code to be generated. So if you dump the A DLL's export table at this point, you should see an entry for Singleton<Logger>::instance().

Now in your C DLL and D DLL, you can include the header file with the template definition for Singleton, but because there is no template implementation, the compiler won't be able to create any code for that template. This means the linker will end up complaining about unresolved externals for Singleton<Logger>::instance(), but you just have to link in the A DLL's export library to fix that.

The bottom line is that the code for Singleton<Logger>::instance() is only ever implemented in DLL A, so you can never have more than one instance.