dll中的C++模板Singleton
C++ Template Singletons in a dll
在dll A中,我有一个模板singleton:
template <class T>
class Singleton
{
public:
static T &instance()
{
static T _instance;
return _instance;
}
private:
//All constructors are here
};
在Dll B中,我定义了一个Logger类。Dlls C、D和E使用Logger,其访问方式如下:
Singleton<Logger>::instance();
问题是每个dll都实例化了自己的副本
Singleton<Logger>.
而不是使用相同的singleton实例。我知道这个问题的解决方案是使用外部模板。也就是说,dll C、D和E必须包括
extern template class Singleton<Logger>;
和dll B必须包括:
template class Singleton<Logger>;
这仍然会导致创建多个模板实例。我试着把extern放在所有dll中,但仍然不起作用。我试着从所有dll中删除extern,但仍然没有起作用。这不是实现模板单件的标准方法吗?正确的方法是什么?
对我有用的技巧是将__declspec(dllexport)
添加到单例的模板定义中;将模板实现与类定义分离,并且仅将该实现包括在A DLL中;最后,通过创建一个调用Singleton<Logger>::instance()
的伪函数,强制模板在A DLL中实例化。
因此,在A DLL的头文件中,您定义Singleton模板如下:
template <class T>
class __declspec(dllexport) Singleton {
public:
static T &instance();
};
然后在A DLL的cpp文件中定义模板实现,并强制实例化Singleton<Logger>
,如下所示:
template <class T>
T &Singleton<T>::instance() {
static T _instance;
return _instance;
};
void instantiate_logger() {
Singleton<Logger>::instance();
}
至少使用我的编译器,我不需要从任何地方调用instantiate_logger
。只要它存在,就会强制生成代码。因此,如果此时转储A DLL的导出表,您应该会看到Singleton<Logger>::instance()
的条目。
现在,在您的C DLL和D DLL中,您可以包含带有Singleton
模板定义的头文件,但由于没有模板实现,编译器将无法为该模板创建任何代码。这意味着链接器最终会抱怨Singleton<Logger>::instance()
的未解析外部,但您只需要链接到DLL的导出库中即可解决此问题。
最重要的是,Singleton<Logger>::instance()
的代码只在DLL A中实现过,所以永远不能有多个实例。
实现这一点的"正确"方法是……不使用单例。
如果您希望所有其他代码都使用某个类型的同一实例,那么请为该代码提供对该实例的引用,作为函数或构造函数的参数。
使用singleton(非模板)与使用全局变量完全相同,应该避免这种做法。
使用模板意味着编译器决定如何实例化代码,以及如何访问"实例"。你遇到的问题是这一点和在DLL中使用静态的结合。
单身汉不好的原因有很多,包括生存期问题(确切地说,删除单身汉什么时候安全?)、线程安全问题、全局共享访问问题等等。
总之,如果你只想要一个事物的一个实例,只需要创建一个实例并将其传递给需要它的代码
Win32 DLL被映射到调用进程的地址空间中。默认情况下,每个使用DLL的进程都有自己的所有DLL全局变量和静态变量。如果您的DLL需要与共享数据其他应用程序加载的其他实例,可以使用以下方法之一:
使用data_seg杂注创建命名数据节。
使用内存映射文件。请参阅有关内存映射文件的Win32文档。
http://msdn.microsoft.com/en-us/library/h90dkhs0%28v=vs.80%29.aspx
这里有一个非常粗略的解决方案,您可能可以从中构建。多个模板将被实例化,但它们都将共享相同的实例对象。
需要一些额外的代码来避免内存泄漏(例如,将void*替换为boost::shared_ptr中的任何一个或其他什么)。
在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);
}
在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;
}
}
我现在想不出更好的办法了。实例必须存储在一个公共位置。
我建议在Logger类中组合一个refcounted类和一个导出的api:
class Logger
{
public:
Logger()
{
nRefCount = 1;
return;
};
~Logger()
{
lpPtr = NULL;
return;
};
VOID AddRef()
{
InterLockedIncrement(&nRefCount);
return;
};
VOID Release()
{
if (InterLockedDecrement(&nRefCount) == 0)
delete this;
return;
};
static Logger* Get()
{
if (lpPtr == NULL)
{
//singleton creation lock should be here
lpPtr = new Logger();
}
return lpPtr;
};
private:
LONG volatile nRefCount;
static Logger *lpPtr = NULL;
};
__declspec(dllexport) Logger* GetLogger()
{
return Logger::Get();
};
代码需要一些修改,但我试着给你一个基本的想法。
我认为您在实现中的问题是:
static T _instance;
我假设static修饰符会导致编译器创建代码,其中T类为每个dll实例一个。尝试使用singletone的不同实现。您可以尝试在Singletone类中创建静态T字段。或者,类中带有静态指针的Singletone应该可以工作。我建议你使用第二种方法,在你的B dll中你会指定
Singletone<Logger>::instance = nullptr;
在第一次调用instance()时,此指针将被初始化。我认为这会解决你的问题。
PS。不要忘记手动处理mutlithreading实例化
创建一些类似的条件
instance()
{
if ( _instance == NULL ) {
_instance = new Singleton();
}
return _instance;
}
这将只创建一个实例,当它第二次被调用时,它将只返回旧实例。
- .cpp和.h文件中的模板专用化声明
- C++模板来检查友元函数的存在
- 如何使用默认参数等选择模板专业化
- 模板参数替换失败,并且未完成隐式转换
- 具有默认模板参数的多态类的模板推导失败
- 部分定义/别名模板模板参数
- 模板-模板参数推导:三个不同的编译器三种不同的行为
- 具有奇怪重复模板模式的派生类中的成员变量已损坏
- 如何在c++中为模板函数实例创建快捷方式
- 使用C++中的模板和运算符重载执行矩阵运算
- 有人能分解一下这个c++模板的语法吗
- 如何在c++17中制作一个模板包装器/装饰器
- Singleton模板为C 的基类
- 具有模板化类型的动态类型Singleton.这是可行的方法吗?[提供的解决方案]
- C++中使用CRTP的模板化Singleton策略
- C++Singleton模板类继承
- Singleton模板专业化编译的奇怪错误
- 使用模板并调用析构函数的singleton
- dll中的C++模板Singleton
- Singleton模板不在Visual C++中编译