In a project of mine, I have an ID generator for types that looks similar to this:
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{};
Usage:
const auto id = Family::type<FooBar>();
It works just fine for my purposes, but it has some limitations. The most annoying one (purpose of the question) is that it fails when used by an executable that links to shared libraries if all of them try to create identifiers. The result is usually that the n-th identifier is assigned to different types across boundaries because each shared library maintains their own separate Family::identifier
.
Some shared library guys pointed out that a more reliable solution would be appreciated, but failed to suggest one that didn't ruin the performance (almost all of them introduce containers, find functionalities and memory allocation).
Is there any alternative approach that works around the aforementioned limitations without losing the performance of the current design?
I searched through SO and found some interesting answers. Many of which were several years old. I'd like to explore solutions up to the latest revision of the standard instead, as long as the interface of the existing class remains intact.
This one is the most interesting one. It uses addresses of static members to achieve the same, but it doesn't fit with the idea of sequentially generated identifiers
Note: using RTTI isn't an option unfortunately.
Note : ids must be generated sequentially and starting from 0 as in the solution presented above.
Your problem occurs because you have this line in your header file:
std::size_t Family::identifier{};
It therefore ends up in each translation unit. Instead, you need to move the storage for this to a .cpp source file which is compiled only once, perhaps into a shared library of its own. Then there will be just one instance of identifier
in a program, and it will work as you intend.
You could also move identifier
from being a class static
variable to a global extern
one in the header file (and as above, define it in a single .cpp file).
If you have C++17 or later, you can also try:
inline std::size_t Family::identifier{};
While the language does not guarantee (or even mention) what happens when you use this new feature across shared libraries boundaries, it does work on my machine.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With