Reputation: 4214
I am tinkering with polymorphic serialization and deserialization in C++.
For that purpose I use a static map: [type id-string] -> [type factory function]
.
Each type has to be registered in this map and I would like to do it at compile-time.
The naïve approach is:
/// Creates a concrete serializable type provided as a template parameter
template <typename T>
ISerializable* createSerializable() { return new T; }
/// Factory that registers a serializable type T
template <typename T>
struct SerializableFactory
{
SerializableFactory(const char* type_name)
{
// registerType adds a type_name->factory_function entry to the map
registerType(type_name, createSerializable<T>);
}
};
Registering the types is done with the macro:
/// Macro that registers the type at compile-time using a static factory instance
#define REGISTER_TYPE(T) \
static SerializableFactory<T> global_##T##Factory(#T);
For example REGISTER_TYPE(ArbitraryClass)
will become:
static SerializableFactory<ArbitraryClass>
global_ArbitraryClassFactory("ArbitraryClass");
Unfortunately this will not work for ArbitraryClass<int>
bacause <
, >
are not allowed to be used in identifier.
Is there a good work-around to achieve registering arbitrary template type this way?
I considered the following alternatives (each has disadvantages):
Update:
Upvotes: 2
Views: 253
Reputation: 4214
I had an aha! moment inspired by comment from @W.F. , @Svalorzen answer, and this answer. I believe it is quite a clever trick, which has none of alternative's disadvantages.
Solution: using an unnamed/anonymous namespace and adding __LINE__
to the identifier should always give a unique identifier (unless the macro is used twice at the same line).
Here's how it looks:
#define MERGE(A, B) A##B
#define CREATE_UNIQUE_IDENTIFIER(line) MERGE(unique_identifier_on_line_, line)
/// UNIQUE_IDENTIFIER generates identifiers like:
/// "unique_identifier_on_line_" + line_number
#define UNIQUE_IDENTIFIER CREATE_UNIQUE_IDENTIFIER(__LINE__)
/// Macro that registers the type at compile-time using a static factory instance
#define REGISTER_TYPE(T) \
namespace \
{ \
static SerializableFactory<T> UNIQUE_IDENTIFIER(#T); \
}
Upvotes: 1
Reputation: 5608
A partial solution would be to always use the same name, but wrap it in an unnamed namespace. This would only allow you to register a single type per translation unit, but maybe that's good enough.
/// Macro that registers the type at compile-time using a static factory instance
#define REGISTER_TYPE(T) \
namespace { \
static SerializableFactory<T> serial_global_factory(#T); \
}
Otherwise, you could use the __LINE__
and __FILE__
macro tokens to create a unique name for your object - as long as you don't have to reference it anywhere else. There are others, a list can be found here.
Upvotes: 2
Reputation: 1856
Try to define the type as a single name and use the name:
typedef ArbitraryClass<int> ArbitraryClass_int_;
REGISTER_TYPE(ArbitraryClass_int_);
You could also try to put it in a hash-map, where the key is the typename.
Upvotes: 0