在a project of mine中,我有一个ID generator用于类似于以下类型的类型:
class Family {
static std::size_t identifier;
template<typename...>
static std::size_t family() {
static const std::size_t value = identifier++;
return value;
}
public:
template<typename... Type>
inline static std::size_t type() {
return family<std::decay_t<Type>...>();
}
};
std::size_t Family::identifier{};
用法:
const auto id = Family::type<FooBar>();
对于我来说,它可以正常工作,但是有一些限制。最令人讨厌的一个问题(该问题的目的)是,如果链接共享库的所有可执行文件都尝试创建标识符,则该可执行文件将无法使用它。结果通常是,第n个标识符跨边界分配给不同的类型,因为每个共享库都维护各自独立的Family::identifier
。
一些共享库专家指出了一种更可靠的解决方案,但并没有提出一个不会破坏性能的解决方案(几乎所有解决方案都引入了容器,查找功能和内存分配) )。
有没有其他方法可以解决上述限制而又不损失当前设计的性能?
我搜索了SO,发现了一些有趣的答案。其中许多已经有好几年了。只要现有类的接口保持完整,我想探索的是最新标准版本的解决方案。
This one是最有趣的。它使用静态成员的地址来实现相同目的,但是不符合顺序生成标识符的想法
注意:不幸的是,不能选择使用RTTI。
注意:ID必须按照上面介绍的解决方案从0开始按顺序生成。
答案 0 :(得分:4)
出现问题是因为您的头文件中包含以下行:
std::size_t Family::identifier{};
因此,它结束于每个翻译单元。取而代之的是,您需要为此将存储移动到.cpp源文件,该文件仅被编译一次,也许会编译到其自身的共享库中。这样,程序中将只有一个identifier
实例,它将按您的预期工作。
您还可以将identifier
从类static
移到头文件中的全局extern
中(并且如上所述,在单个.cpp文件中定义)。
如果您使用的是C ++ 17或更高版本,也可以尝试:
inline std::size_t Family::identifier{};
尽管该语言不能保证(甚至无法提及)跨共享库边界使用此新功能时会发生什么,但它确实可以在我的计算机上使用。
答案 1 :(得分:0)
如果不要求id为连续整数,则可以使用模板静态成员的地址作为id。这种方法的好处是它不需要任何运行时初始化(使用static initialization):
// in a header
class Family {
template<class...> struct Id { static char const id; };
template<typename... T>
static std::size_t family() {
return reinterpret_cast<std::size_t>(&Id<T...>::id);
}
public:
template<typename... Type>
static std::size_t type() {
return family<std::decay_t<Type>...>();
}
};
// in a header
template<class... T>
char const Family::Id<T...>::id = {};
// elsewhere
int main() {
auto int_id = Family::type<int>();
auto int_int_id = Family::type<int, int>();
}
您还可以将id
设为编译时间常数,并将其用作模板参数:
// in a header
struct Family {
template<class...> struct Id { static char const id; };
};
// in a header
template<class... T>
char const Family::Id<T...>::id = {};
// elsewhere
template<char const*>
struct X {};
int main() {
X<&Family::Id<int>::id> x;
}
答案 2 :(得分:0)
如果您不关心顺序ID,请使用函数的地址作为标识符。
template<typename... T>
uintptr_t getID() {
return reinterpret_cast<uintptr_t>(&getID<T...>);
}
然后
auto intID = getID<int>();
auto floatID = getID<float>();
...