Bla*_*iwi 7 c++ enums visual-c++ c++11
在我最近一直在帮助的项目中,整个代码库依赖于一个可怕的枚举,它被有效地用作美化哈希表的键.唯一的问题是现在它是巨大的,只要枚举更改基本上是对已经很大的代码库的重建就编译.这需要永远,我真的很想替换它.
enum Values
{
Value = 1,
AnotherValue = 2,
<Couple Thousand Entries>
NumValues // Sentinel value for creating arrays of the right size
}
Run Code Online (Sandbox Code Playgroud)
我正在寻找的是替换这个枚举的方法,但仍然有一个类型安全的系统(没有未经检查的字符串),并且还与MSVC2010兼容(没有constexpr).额外的编译开销是可以接受的,因为编译时可能仍然比重新编译一堆文件更短.
我目前的尝试基本上可以概括为延迟定义值直到链接时间.
其用途的例子
GetValueFromDatabase(Value);
AddValueToDatabase(Value, 5);
int TempArray[NumValues];
Run Code Online (Sandbox Code Playgroud)
编辑:编译时和运行时预处理是可以接受的.同时在运行时基于某种缓存数据结构.
实现此目的的一种方法是使用一个包装数字 ID 的关键类,该类无法直接实例化,因此强制通过类型安全变量完成引用:
// key.h
namespace keys {
// Identifies a unique key in the database
class Key {
public:
// The numeric ID of the key
virtual size_t id() const = 0;
// The string name of the key, useful for debugging
virtual const std::string& name() const = 0;
};
// The total number of registered keys
size_t count();
// Internal helpers. Do not use directly outside this code.
namespace internal {
// Lazily allocates a new instance of a key or retrieves an existing one.
const Key& GetOrCreate(const std::string& name, size_t id);
}
}
#define DECLARE_KEY(name) \
extern const ::keys::Key& name
#define DEFINE_KEY(name, id) \
const ::keys::Key& name = ::keys::internal::GetOrCreate(STRINGIFY(name), id)
Run Code Online (Sandbox Code Playgroud)
通过上面的代码,键的定义将如下所示:
// some_registration.h
DECLARE_KEY(Value);
DECLARE_KEY(AnotherValue);
// ...
// some_registration.cpp
DEFINE_KEY(Value, 1);
DEFINE_KEY(AnotherValue, 2);
// ...
Run Code Online (Sandbox Code Playgroud)
重要的是,上面的注册代码现在可以分成几个单独的文件,这样您就不需要立即重新编译所有定义。例如,您可以将注册分成逻辑分组,如果您添加了一个新条目,则仅需要重新编译一个子集,并且仅需要重新编译实际依赖于相应 *.h 文件的代码(未引用该特定键值的其他代码将不再需要更新)。
用法与之前非常相似:
GetValueFromDatabase(Value);
AddValueToDatabase(Value, 5);
int* temp = new int[keys::count()];
Run Code Online (Sandbox Code Playgroud)
完成此操作的相应key.cpp文件如下所示:
namespace keys {
namespace {
class KeyImpl : public Key {
public:
KeyImpl(const string& name, size_t id) : id_(id), name_(name) {}
~KeyImpl() {}
virtual size_t id() const { return id_; }
virtual const std::string& name() const { return name_; }
private:
const size_t id_;
const std::string name_;
};
class KeyList {
public:
KeyList() {}
~KeyList() {
// This will happen only on program termination. We intentionally
// do not clean up "keys_" and just let this data get cleaned up
// when the entire process memory is deleted so that we do not
// cause existing references to keys to become dangling.
}
const Key& Add(const string& name, size_t id) {
ScopedLock lock(&mutex_);
if (id >= keys_.size()) {
keys_.resize(id + 1);
}
const Key* existing = keys_[id]
if (existing) {
if (existing->name() != name) {
// Potentially some sort of error handling
// or generation here... depending on the
// desired semantics, for example, below
// we use the Google Log library to emit
// a fatal error message and crash the program.
// This crash is expected to happen at start up.
LOG(FATAL)
<< "Duplicate registration of key with ID "
<< id << " seen while registering key named "
<< "\"" << name << "\"; previously registered "
<< "with name \"" << existing->name() << "\".";
}
return *existing;
}
Key* result = new KeyImpl(name, id);
keys_[id] = result;
return *result;
}
size_t length() const {
ScopedLock lock(&mutex_);
return keys_.size();
}
private:
std::vector<const Key*> keys_;
mutable Mutex mutex_;
};
static LazyStaticPtr<KeysList> keys_list;
}
size_t count() {
return keys_list->length();
}
namespace internal {
const Key& GetOrCreate(const std::string& name, size_t id) {
return keys_list->Add(name, id);
}
}
}
Run Code Online (Sandbox Code Playgroud)
正如下面的评论中所恰当指出的,允许分散注册的方法的一个缺点是,可能会陷入多次使用相同值的冲突场景(上面的示例代码为这种情况添加了一个错误,但是这发生在运行时,在编译时显示这样的事情真的很好)。缓解这种情况的一些方法包括提交钩子,运行测试检查此类条件或如何选择 ID 值的策略,以减少重复使用 ID 的可能性,例如指示必须递增和提交的下一个可用 ID 的文件作为分配 ID 的一种方式。或者,假设您被允许重新排列 ID(我假设在此解决方案中您必须保留您已有的当前 ID),您可以更改方法,以便从名称自动生成数字 ID(例如,通过名称的哈希值),并可能使用其他因素(例如 __FILE__ )来处理冲突,以便 ID 是唯一的。
| 归档时间: |
|
| 查看次数: |
1341 次 |
| 最近记录: |