C++懒惰的单例挂在加载时
C++ lazy singleton hangs on loading
我有懒惰的单例类,需要在第一次调用时使用boost进行序列化。
头文件:
class Singleton
{
public:
static Singleton& Instance()
{
static Singleton theSingleInstance;
return theSingleInstance;
}
void load();
private:
Singleton();
Singleton(const Singleton& root);
Singleton& operator=(const Singleton&);
std::map<std::string,std::string > m_desc;
friend class boost::serialization::access;
template<typename Archive>
void serialize(Archive& arc, const unsigned int version)
{
arc & BOOST_SERIALIZATION_NVP(m_desc);
}
const char* FILENAME = "./config.xml";
};
源文件
#include "singleton.h"
Singleton::Singleton()
{
load();
}
void Singleton::load()
{
try
{
std::ifstream f(FILENAME);
boost::archive::xml_iarchive arc(f);
arc & boost::serialization::make_nvp("Config",Instance());
}
catch(...)
{
std::cout << "Exception" << std::endl;
}
}
因此,当我尝试使用此单例启动代码时,它会挂起。使用调试器,我可以看到它不会多次转到load()
方法(没关系)。当我暂停调试器时,它会在return theSingleInstance;
行停止,但它也不会多次通过此行上的断点。我做错了什么?
从构造函数内部调用 load。这意味着theSingleInstance
当您...呼叫Instance
:
#0 in Singleton::load at test.cpp <test.cpp>
#1 in Singleton::Singleton at test.cpp <test.cpp>
#2 in Singleton::Instance at test.cpp <test.cpp>
#3 in main at test.cpp <test.cpp>
由于构造 c++11 函数本地静态保证是线程安全的,这意味着 - 在您的实现中 - 执行将阻塞,直到实例完全构造(或构造失败,因此可以重试)。
当然,这永远不会发生,因为建筑正在等待自己。
0x00000000004030f5 <+629>: callq 0x402be0 <__cxa_guard_acquire@plt>
=> 0x00000000004030fa <+634>: test %eax,%eax
0x00000000004030fc <+636>: je 0x402ff1 <Singleton::load()+369>
当然,正如您已经发现的那样,通过在构造实例时不使用外部访问器可以解决此问题。
您可能需要考虑为单例提供值语义的做法 - 以防有一天您不希望它不再是单例并且希望避免重构。
另一个优点是它提供了完整的封装:
例如:
// config.h - extremely sparse interface
#include <string>
struct config
{
/// Return a named value from the program's configuration
/// @param name is the name of the required parameter
/// @post the config file shall be cached
/// @return the value if it exists
/// @except std::invalid_argument if the value does not exist
/// @except other exceptions if the config file does not load
///
const std::string& value(const std::string& name) const;
private:
struct impl;
static impl& get();
};
// config.cpp - the implementation
#include <boost/serialization/serialization.hpp>
#include <boost/serialization/nvp.hpp>
#include <boost/serialization/map.hpp>
#include <boost/archive/xml_iarchive.hpp>
#include <fstream>
#include <map>
struct config::impl
{
impl()
{
std::ifstream f(FILENAME);
boost::archive::xml_iarchive arc(f);
arc & boost::serialization::make_nvp("Config", *this);
}
std::map<std::string,std::string > m_desc;
friend class boost::serialization::access;
template<typename Archive>
void serialize(Archive& arc, const unsigned int version)
{
arc & BOOST_SERIALIZATION_NVP(m_desc);
}
static constexpr const char* FILENAME = "./config.xml";
};
config::impl& config::get() {
static impl _;
return _;
}
const std::string& config::value(const std::string& name) const
{
return get().m_desc.at(name);
}
// demo.cpp
// note - config is copyable, and this has almost no cost
void do_something_with(config cfg)
{
}
struct no_config {}; // some other config source?
// now I can switch on config type at compile time - code can be
// more generic
void do_something_with(no_config)
{
}
int main()
{
// single-use
std::string my_value = config().value("foo");
// pass my config source to a function
do_something_with(config());
// a similar function that uses a different config source
do_something_with(no_config());
return 0;
}
答案很简单:替换这一行
arc & boost::serialization::make_nvp("Config",Instance());
跟
arc & boost::serialization::make_nvp("Config",*this);
相关文章:
- std::原子加载和存储都需要吗
- 如何加载(或映射)文件部分的最大大小,但适合在Windows上的RAM
- C++ 雷神库 - 使用资源加载器类时出现问题(不命名类型)
- 为什么加载SDF会导致Mobilizer创建闭环错误
- C++atioglxx.pdb未加载错误glBufferData OpenGL
- 如何使用tinyxml2从XML加载父实体和子实体
- 如何在C++中使用pybind11加载一个pickle python列表
- 为什么在单例中,检查类==空?
- 系统.将数组移交给c#中动态加载的c++DLL时发生AccessViolationException
- C++单例,不会为此文档加载任何符号
- 如何从QtQuickWidget加载qt快速UI表单
- 如何将项目共享"system calls"作为单例对象构建和链接到引导加载程序?
- 使用 C++ 和嵌入式单声道调用 C# DLL 时无法加载程序集系统
- 运行时动态加载和单例
- C++懒惰的单例挂在加载时
- 生成表单和加载时代码中出现Lambda错误
- 当我在win32项目c++应用程序中单击菜单项时,如何编写代码来加载一个特殊的对话框?
- 带有jQuery的CGI后表单未完全加载
- 如何将位图从文件加载到样例缓冲区
- 在运行时单击QTabWidget将内容加载到选项卡中