CRTP and unique persistent identifiers

Consider the following code:

#include <iostream> #include <cstdlib> #include <ctime> struct BaseClass { static int identifier() { static int identifier_counter = 0; return identifier_counter++; } }; template <class D> struct Class: public BaseClass { static int identifier() { static int class_identifier = BaseClass::identifier(); return class_identifier; } }; struct A: public Class<A> { }; struct B: public Class<B> { }; int main() { std::srand(std::time(0)); int r = std::rand()%2; if(r) { std::cout << "A: " << A::identifier() << std::endl; std::cout << "B: " << B::identifier() << std::endl; } else { std::cout << "B: " << B::identifier() << std::endl; std::cout << "A: " << A::identifier() << std::endl; } } 

This is a reduced but plausible representation of the problem.

Any derived class will have a specific, different identifier at runtime, and two instances of the same type will have the same identifier. Of course, this is a good solution to such a problem.

Unfortunately, these identifiers depend on the order in which the identifier members is called (we can easily see it by running the example several times). In other words, given the two classes A and B , if it happens that when you run the software twice, their identifier members are called in a different order, they have different identifiers.

My problem is that for some reason I need to store these identifiers somewhere and let them survive one execution, so that I can talk about the original types when the application starts again, and decide to read these values ​​from storage.

An alternative would be to use hash_code from type_info , but it suffers from other problems. Another solution would be to force identifier members to run while the application is loading, but there are several drawbacks to this as well.

I would like to know if there is still an easy-to-use, although still elegant, solution completely transparent for the developer to identify types in several executions, since one of them refers to one application launch.

+1
c ++ templates crtp
Nov 30 '15 at 21:13
source share
2 answers

The problem of having a unique constant identifier for each class is unsolvable with C ++. I'm sorry. You will either depend on the order in which your initialization functions are called, or, if you call them from static object initializers, in the order of the static initializer (which usually depends on the order of your object files in your link line).

And, of course, there is no guarantee that the hash will be unique.

For this you will have to use an external script. In particular, you can use something like this:

 // when class is first created class Foo { static int class_id = ?CLASS_ID?; }; // after class is process by the script class Foo { static int class_id = 123; // Autogenerated by 'stamp_id.pl' }; 

You can have a perl script executed as part of the compilation (the very first one) that opens all the .h files in the project directory, reads all of them, counts all instances of Autogenerated by 'stamp_id.pl' and marks everything ?CLASS_ID? with incremented counter (starting from the number of identifiers already generated). To add some security, you might need a better sample than a simple one? ...?, But I think you have an idea.

+1
Nov 30 '15 at 21:24
source share

Even if they are slightly different as questions, here , I have proposed a solution that may perhaps fit well with this question.
It is not based on the CRTP idiom and has the advantage of being a non-intrusive solution.

This follows a minimal working example:

 #include<cstddef> #include<functional> #include<iostream> template<typename T> struct wrapper { using type = T; constexpr wrapper(std::size_t N): N{N} {} const std::size_t N; }; template<typename... T> struct identifier: wrapper<T>... { template<std::size_t... I> constexpr identifier(std::index_sequence<I...>): wrapper<T>{I}... {} template<typename U> constexpr std::size_t get() const { return wrapper<U>::N; } }; template<typename... T> constexpr identifier<T...> ID = identifier<T...>{std::make_index_sequence<sizeof...(T)>{}}; // --- struct A {}; struct B {}; constexpr auto id = ID<A, B>; int main() { switch(id.get<B>()) { case id.get<A>(): std::cout << "A" << std::endl; break; case id.get<B>(): std::cout << "B" << std::endl; break; } } 

The main problem is that identifiers can change if an item is removed from the type list.
In any case, it is trivial to define an empty placeholder to solve the problem.

0
Sep 24 '16 at 10:46
source share



All Articles