Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C++ a singleton class with dll

Tags:

c++

static

dll

I created a static library with class:

class CLog
{
   private:
   CLog();
   ...
   ...
   public:
   static CLog& GetInstance()                                
   {
           static CLog Instance;
           return Instance;
   }
   void Write(char *cpPr);
};
#define Log CLog::GetInstance()

This library is linked to a dll and a main program. The dll is loaded by LoadLibrary. In this case is obvious that calling Log.Write in a main exe and in dll, creates two separate instances of CLog. Any ideas how to work around this issue and still provide dynamic loading a dll?

like image 432
blackwater Avatar asked Aug 04 '11 02:08

blackwater


3 Answers

The problem is that every project that links the static library, be it main program or DLL, will get a separate copy of the static variable. This breaks the typical method of creating a singleton.

The simplest way around this is to create another DLL which holds the singleton, rather than a static library. Since only one linker output will contain the static variable, the problem is solved.

In my own case I created a singleton manager that identified each singleton by a unique GUID and ensured that only one copy existed application wide. The singleton manager existed as its own DLL.

like image 145
Mark Ransom Avatar answered Sep 19 '22 21:09

Mark Ransom


The method I used was to export a function called GetLogger from the EXE which provides a pointer to the singleton. GetInstance() implementation is conditional on the _USRDLL preprocessor define. When _USRDLL is set (for the DLL compilation) GetInstance() calls GetModuleHandle() to get a handle to the EXE and loads the function called GetLogger. Here's the code based on your example:

Static lib has Log.h:

class Log
{
  private:
  Log();

  public:
  ~Log();

  static Log& GetInstance()                                
  {                 
  #ifdef _USRDLL
    typedef Log* (*GetLoggerFn)();
    HMODULE mod = GetModuleHandle( NULL );
    GetLoggerFn getLogger = (GetLoggerFn)::GetProcAddress( mod, "GetLogger" );
    Log* Instance = getLogger();
    return *Instance;
  #else
    static Log Instance;
    return Instance;
  #endif
  }
  void Write(const std::string& str );
};
#define LOG Log::GetInstance()

Static lib has Log.cpp:

#include "Log.h"

void Log::Write(const std::string& str )
{
    std::cout << this << "  " << str << std::endl;
}

Log::Log()
{
}

Log::~Log()
{
    std::cout << "Log destroyed" << std::endl;
}

DLL just has a log statement in DllMain:

#include "../static/Log.h"
BOOL APIENTRY DllMain( HMODULE hModule,
                       DWORD  ul_reason_for_call,
                       LPVOID lpReserved
                     )
{
    switch (ul_reason_for_call)
    {
    case DLL_PROCESS_ATTACH:
    case DLL_THREAD_ATTACH:
    case DLL_THREAD_DETACH:
    case DLL_PROCESS_DETACH:
        break;
    }
    LOG.Write("Hello from dll");
    return TRUE;
}

And EXE looks like this:

#include "stdafx.h"
#include "../static/Log.h"
#include <Windows.h>
extern "C"
{
    __declspec( dllexport ) Log* GetLogger()
    {
        return &LOG;
    }
}


int _tmain(int argc, _TCHAR* argv[])
{
    LOG.Write("Hello from exe");
    HMODULE mod = ::LoadLibraryA( "../Debug/tdll.dll");
    ::FreeLibrary( mod );
    LOG.Write("unloaded library");
    return 0;
}
like image 2
Skrymsli Avatar answered Sep 22 '22 21:09

Skrymsli


Here's a simple library that supports to share the same singleton instance between dynamical libraries and exectuable. (tested on win, linux, macos)

To get the singleton instance of type T, just use singleton<T>() is OK.

https://github.com/xhawk18/singleton-cpp

#include "singleton-cpp/singleton.h"

MyObject &obj = singleton<MyObject>();
like image 2
xhawk18 Avatar answered Sep 21 '22 21:09

xhawk18