This question already has an answer here:
-
Does C++ support compile-time counters?
7 answers
I have a set of C++ classes and each one must declare a unique sequential id as a compile-time constant.
For that I'm using the __COUNTER__
built-in macro which translates to an integer that is incremented for every occurrence of it. The ids need not to follow a strict order. The only requirement is that they are sequential and start from 0:
class A {
public:
enum { id = __COUNTER__ };
};
class B {
public:
enum { id = __COUNTER__ };
};
// etcetera ...
My question is: Is there a way to achieve the same result using a C++ construct, such as templates?
Here is a possible way to do it using __LINE__
macro and templates:
template <int>
struct Inc
{
enum { value = 0 };
};
template <int index>
struct Id
{
enum { value = Id<index - 1>::value + Inc<index - 1>::value };
};
template <>
struct Id<0>
{
enum { value = 0 };
};
#define CLASS_DECLARATION(Class) \
template <> \
struct Inc<__LINE__> \
{ \
enum { value = 1 }; \
}; \
\
struct Class \
{ \
enum { id = Id<__LINE__>::value }; \
private:
Example of using:
CLASS_DECLARATION(A)
// ...
};
CLASS_DECLARATION(B)
// ...
};
CLASS_DECLARATION(C)
// ...
};
See live example.
Is a explicit chaining acceptable?
class A {
public:
static const unsigned int id = 1;
};
class B {
public:
static const unsigned int id = A::id+1;
};
The advantage of this approach is that you always get the same Id and you know what it is no matter what your compiler is. While with __LINE__
or __COUNTER__
approach may not be so predicatable. The disadvantage is that with chaining your class must always know the previous one on the chain.
Playing with templates (and C++11):
template <typename... T>
class Identificable;
template <>
class Identificable<> {
public:
static const unsigned int id = 1;
};
template <typename Prev>
class Identificable<Prev> {
public:
static const unsigned int id = Prev::id+1;
};
class A : public Identificable<> {
public:
};
class B : public Identificable<A> {
public:
};
Standard C++ has the __LINE__
macro.
That is, __LINE__
is a "C++ construct", as requested, in contrast to __COUNTER__
, which isn't.
__LINE__
differs from Visual C++’s __COUNTER__
in that at least earlier versions of Visual C++ produced garbled expansions of __LINE__
when a certain compilation option was used.
Depeneding on your needs you may however be able to simply use type_info
instances for identification. C++11 added general support for comparing type_info
, called std::typeindex
. This means you can use standard collections.
Is there a way to achieve the same result using a C++ construct, such
as templates?
Yes, there is :-) The basic idea is to use chaining allocated IDs, which avoids the use of __COUNTER__
, __LINE__
or other approaches proposed earlier and does not require injecting "extra" info into the type definition.
Here is a brief description of the solution proposed in v1 for the counter implemented on C++03, using template metaprograming. Two template specializations ID_by_T
and T_by_ID
are used to define links type <=> ID
at compile time. The type's ID is a enum constant. If the link was not defined, ID_by_T<type>::ID
returns -1
, and T_by_ID<undefinedID>::type
returns the null_t
predefined type. The macro DEF_TYPE_ID(type_name)
generates a new ID
at the point of the definition the type <=> ID
link.
This approach is based on a macro redefinition: When a macro is undefined using #undef
, its value is expanded into the C++ code. For instance:
DEF_TYPE_ID(int)
#undef PREV_TYPE
#define PREV_TYPE int
Macro DEF_TYPE_ID
uses the following call to the previous definition of macro PREV_TYPE
: ID_T_pair<type_name, ID_by_T<PREV_TYPE>::ID+1>
. That is why I said about chaining allocated IDs.