С++ одноэлементный класс с dll
Я создал статическую библиотеку с классом:
class CLog
{
private:
CLog();
...
...
public:
static CLog& GetInstance()
{
static CLog Instance;
return Instance;
}
void Write(char *cpPr);
};
#define Log CLog::GetInstance()
Эта библиотека связана с dll и основной программой. DLL загружается LoadLibrary. В этом случае очевидно, что вызов Log.Write в основном exe и в dll создает два отдельных экземпляра CLog. Любые идеи, как обойти эту проблему и по-прежнему обеспечивать динамическую загрузку dll?
Ответы
Ответ 1
Проблема заключается в том, что каждый проект, который связывает статическую библиотеку, будь то основная программа или DLL, получит отдельную копию статической переменной. Это нарушает типичный метод создания одноэлементного кода.
Самый простой способ - создать другую DLL, которая содержит синглтон, а не статическую библиотеку. Поскольку только один вывод компоновщика будет содержать статическую переменную, проблема будет решена.
В моем собственном случае я создал диспетчер singleton, который идентифицировал каждый синглтон с помощью уникального GUID и обеспечивал, чтобы только одна копия существовала в приложении. Одиночный менеджер существовал как собственная DLL.
Ответ 2
Метод, который я использовал, заключался в том, чтобы экспортировать функцию, называемую GetLogger из EXE, которая предоставляет указатель на singleton. Реализация GetInstance() обусловлена определением препроцессора _USRDLL. Когда _USRDLL установлен (для компиляции DLL), GetInstance() вызывает GetModuleHandle(), чтобы получить дескриптор EXE и загружает функцию, называемую GetLogger. Здесь код, основанный на вашем примере:
У Static lib есть 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 есть 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 имеет только оператор журнала в 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;
}
И EXE выглядит так:
#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;
}
Ответ 3
Если вместо этого вы используете статический член класса (в отличие от использования статически назначенной локальной переменной), я считаю, что это создаст только один экземпляр (я не могу проверить это на данный момент). Что-то вроде:
class CLog
{
private:
CLog();
static CLog instance;
public:
static CLog & GetInstance( )
{
return instance;
}
void Write(char *cpPr);
};
CLog CLog::instance; // I believe this is also necessary
Изменить
Хорошо, поэтому Марк указал, что это тоже не сработает. DLL и EXE по-прежнему получают разные экземпляры CLog. О чем-то еще нужно подумать.