AMA
AMA

Reputation: 4214

Macro generating a valid identifier for template-class static instance

Background

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.

Approach

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");

Problem

Unfortunately this will not work for ArbitraryClass<int> bacause <, > are not allowed to be used in identifier.

Question

Is there a good work-around to achieve registering arbitrary template type this way?

Alternatives

I considered the following alternatives (each has disadvantages):

Update:

Upvotes: 2

Views: 253

Answers (3)

AMA
AMA

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

Svalorzen
Svalorzen

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

cmdLP
cmdLP

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

Related Questions