是否可以确保仅在 'static initialization' 步骤期间调用函数
Is it possible to ensure that a function is only called during the 'static initialization' step
我想知道是否可以确保仅在程序的静态初始化步骤中调用函数?
举个例子,假设我有一些单例 class,它包含一个 std::map
对象并公开了它的 insert
和 at
方法。我想确保从中读取数据(at
方法)是线程安全的,据我所知,这需要确保没有任何内容正在修改数据(即使用 insert
方法)。
该映射旨在 仅 在静态初始化期间填充,此时我假设只有一个线程。有什么方法可以确保在 main()
开始后不会有误导的用户调用 insert
?
示例代码
#include <map>
#include <string>
class Singleton {
private:
std::map<std::string, std::string> m_map;
public:
static Singleton& instance() {
static Singleton theSingleton;
return theSingleton;
}
static bool insert(const std::string& key, const std::string& value) {
return instance().m_map.insert(std::make_pair(key, value) ).second;
}
static std::string at(const std::string& key) {
return instance().m_map.at(key);
}
};
static bool inserted = Singleton::insert("Hello", "World"); // fine
bool addItem(const std::string& key, const std::string& value) {
return Singleton::insert(key, value); // not OK
}
不用说(?)实际代码比这个简单的例子要复杂得多。
解决方案后编辑:看起来尽可能安全的最好方法是维护一个status
变量来记录单例是否在'insert' 或 'read' 模式并相应地执行操作。感谢大家的想法和建议!
如果在初始化阶段可以保证用户不会在main()
之前读取map,一种解决方案是构造一个静态map,仅用于初始化,然后在单例运行时将其移动到单例中。正在建设中。
由于构造是在第一次调用 instance()
时发生的,因此您可以确定地图已正确初始化。
然后对 insert
的其他调用不会对单例产生影响。您还可以添加互斥锁来保护 insert
以避免 UB 出现竞争条件。
class Singleton {
private:
std::map<std::string, std::string> m_map;
static auto& init_map() {
static std::map<std::string, std::string> m;
return m;
}
Singleton() {
m_map = std::move(init_map());
init_map().clear();
}
public:
static Singleton& instance() {
static Singleton theSingleton;
return theSingleton;
}
static bool insert(const std::string& key, const std::string& value) {
// you can also add mutex to protect here,
// because calling insert from different threads without
// protection will screw up its internal state, even if
// the init_map becomes useless after main
return init_map().insert(std::make_pair(key, value) ).second;
}
static std::string at(const std::string& key) {
return instance().m_map.at(key);
}
};
我猜您还想使用 'at' 方法来设置您的应用程序。
为什么不添加一个 'lock' 方法并在 main 中调用那个简单的第一个函数?
#include <map>
#include <string>
class Singleton {
private:
std::map<std::string, std::string> m_map;
bool m_locked;
Singleton() : m_locked(false) { }
public:
static Singleton& instance() {
static Singleton theSingleton;
return theSingleton;
}
static void lock() {
instance().m_locked = true;
}
static bool insert(const std::string& key, const std::string& value) {
if (instance().m_locked) { return false; }
return instance().m_map.insert(std::make_pair(key, value)).second;
}
static std::string at(const std::string& key) {
return instance().m_map.at(key);
}
};
static bool inserted = Singleton::insert("Hello", "World"); // fine
bool addItem(const std::string& key, const std::string& value) {
return Singleton::insert(key, value); // not OK
}
int main(int argc, char** argv)
{
Singleton::lock();
Singleton::insert("Hello2", "World2"); // fails
return 0;
}
就像 Jürgen 使用非 Java 方式但 c/c++ 创建单例(即名称空间)的方式。
为什么要这样:
- 在 link 时更高效且更容易优化,因为无需取消引用
this
即可访问状态;
- 无需维护代码来确保唯一性:唯一性由 linker.
确保
singleton.hpp
namespace singleton{
void lock();
bool instert(const std::string& key, const std::string& value);
std::string at(const std::string& key)
}
singleton.cpp
namespace singleton{
namespace{
inline decltype(auto)
get_map(){
static std::map<std::string, std::string> m_map;
return m_map;
}
bool m_locked=false; //OK guarenteed to happen before any dynamic initialization [basic.start.static]
}
void lock() {
m_locked = true;
}
bool insert(const std::string& key, const std::string& value) {
if (m_locked) { return false; }
return get_map().insert(std::make_pair(key, value)).second;
}
std::string at(const std::string& key) {
return get_map().at(key);
}
}
此外,如果必须在通用代码中使用单例,则可以使用结构:
struct singleton_type{
static void lock() {singleton::lock();}
static auto insert(const std::string& key, const std::string& value) {
return singleton::insert(key,value);
}
static auto at(const std::string& key) {
return singleton::at(key,value);
}
};
auto x = singleton_type{};
auto y = singleton_type{};
// x and y refers to the same and unique object file!!!
明天,停止编码 java :)。
我想知道是否可以确保仅在程序的静态初始化步骤中调用函数?
举个例子,假设我有一些单例 class,它包含一个 std::map
对象并公开了它的 insert
和 at
方法。我想确保从中读取数据(at
方法)是线程安全的,据我所知,这需要确保没有任何内容正在修改数据(即使用 insert
方法)。
该映射旨在 仅 在静态初始化期间填充,此时我假设只有一个线程。有什么方法可以确保在 main()
开始后不会有误导的用户调用 insert
?
示例代码
#include <map>
#include <string>
class Singleton {
private:
std::map<std::string, std::string> m_map;
public:
static Singleton& instance() {
static Singleton theSingleton;
return theSingleton;
}
static bool insert(const std::string& key, const std::string& value) {
return instance().m_map.insert(std::make_pair(key, value) ).second;
}
static std::string at(const std::string& key) {
return instance().m_map.at(key);
}
};
static bool inserted = Singleton::insert("Hello", "World"); // fine
bool addItem(const std::string& key, const std::string& value) {
return Singleton::insert(key, value); // not OK
}
不用说(?)实际代码比这个简单的例子要复杂得多。
解决方案后编辑:看起来尽可能安全的最好方法是维护一个status
变量来记录单例是否在'insert' 或 'read' 模式并相应地执行操作。感谢大家的想法和建议!
如果在初始化阶段可以保证用户不会在main()
之前读取map,一种解决方案是构造一个静态map,仅用于初始化,然后在单例运行时将其移动到单例中。正在建设中。
由于构造是在第一次调用 instance()
时发生的,因此您可以确定地图已正确初始化。
然后对 insert
的其他调用不会对单例产生影响。您还可以添加互斥锁来保护 insert
以避免 UB 出现竞争条件。
class Singleton {
private:
std::map<std::string, std::string> m_map;
static auto& init_map() {
static std::map<std::string, std::string> m;
return m;
}
Singleton() {
m_map = std::move(init_map());
init_map().clear();
}
public:
static Singleton& instance() {
static Singleton theSingleton;
return theSingleton;
}
static bool insert(const std::string& key, const std::string& value) {
// you can also add mutex to protect here,
// because calling insert from different threads without
// protection will screw up its internal state, even if
// the init_map becomes useless after main
return init_map().insert(std::make_pair(key, value) ).second;
}
static std::string at(const std::string& key) {
return instance().m_map.at(key);
}
};
我猜您还想使用 'at' 方法来设置您的应用程序。 为什么不添加一个 'lock' 方法并在 main 中调用那个简单的第一个函数?
#include <map>
#include <string>
class Singleton {
private:
std::map<std::string, std::string> m_map;
bool m_locked;
Singleton() : m_locked(false) { }
public:
static Singleton& instance() {
static Singleton theSingleton;
return theSingleton;
}
static void lock() {
instance().m_locked = true;
}
static bool insert(const std::string& key, const std::string& value) {
if (instance().m_locked) { return false; }
return instance().m_map.insert(std::make_pair(key, value)).second;
}
static std::string at(const std::string& key) {
return instance().m_map.at(key);
}
};
static bool inserted = Singleton::insert("Hello", "World"); // fine
bool addItem(const std::string& key, const std::string& value) {
return Singleton::insert(key, value); // not OK
}
int main(int argc, char** argv)
{
Singleton::lock();
Singleton::insert("Hello2", "World2"); // fails
return 0;
}
就像 Jürgen 使用非 Java 方式但 c/c++ 创建单例(即名称空间)的方式。
为什么要这样:
- 在 link 时更高效且更容易优化,因为无需取消引用
this
即可访问状态; - 无需维护代码来确保唯一性:唯一性由 linker. 确保
singleton.hpp
namespace singleton{
void lock();
bool instert(const std::string& key, const std::string& value);
std::string at(const std::string& key)
}
singleton.cpp
namespace singleton{
namespace{
inline decltype(auto)
get_map(){
static std::map<std::string, std::string> m_map;
return m_map;
}
bool m_locked=false; //OK guarenteed to happen before any dynamic initialization [basic.start.static]
}
void lock() {
m_locked = true;
}
bool insert(const std::string& key, const std::string& value) {
if (m_locked) { return false; }
return get_map().insert(std::make_pair(key, value)).second;
}
std::string at(const std::string& key) {
return get_map().at(key);
}
}
此外,如果必须在通用代码中使用单例,则可以使用结构:
struct singleton_type{
static void lock() {singleton::lock();}
static auto insert(const std::string& key, const std::string& value) {
return singleton::insert(key,value);
}
static auto at(const std::string& key) {
return singleton::at(key,value);
}
};
auto x = singleton_type{};
auto y = singleton_type{};
// x and y refers to the same and unique object file!!!
明天,停止编码 java :)。