As far as I can see from the vague "the macro goes on..." comment, the DECLARATION
macro seems to do one or several of these things:
- define members are independent of
ABC
- define members (methods or data members) that depend on the type
ABC
- define members that use the string
"ABC"
tl;dr: #1 is trivial, as you noted in the question. #2 is relatively easy, use CRTP, #3 is not satisfactorily doable without a macro.
update:
In the comments below you mention that you want a guarantee that the string and the class name be "sync-ed". This is definitely impossible without macros in current C++. (see "update" below)
The first is easy to achieve without a macro - just inherit from a ordinary base class that defines the members.
The second can be done relatively easy too, via a CRTP: create a template class that takes the type as parameter and inherit from the template, instantiated with the type itself:
template <class T>
class Base {
public:
void doSomething(T const&);
};
class ABC : public Base<ABC> {
// inherited void doSomething(ABC const&);
};
The third one is tricky and not easily solvable without at least a bit of boilerplate.
C++ has no features apart from macros that convert a name (class name, function name, variable name...) into a string representing that name. (such language features are part of a family of features commonly called reflection). An exception is typeid(T).name()
, nut the outcome is not standardized, so it may or may not give you the name of the class, something different, readable or unreadable or just an empty string.
So, to (portably and realiably) get a string "ABC" for a class ABC
you have to write "ABC" at least once in addition to the class definition. That's not so bad yet, you have to do something similar for #2 as well, as it seems impossible to use the type of ABC without explicitly mentioning ABC again.
Update: Since you have to explicitly type both the string and the class name, there can be no guarantee that both are always the same, except with a macro. Even the macro you have in the question is prone to changes/typos:
class ABC {
DECLARATION(ACB);
};
This will give a wrong class name if the macro only provides the stringified version of it's argument without doing something with the type (in that case the compiler would tell you that ACB is no type). If you want the guarantee, use one macro that does everything, including the class definition:
#define CLASS_DEF(x) \
class x##_base { \
std::string GetClassName() { \
return std::string(#x); \
} \
}; \
class x : public x##_base
and then later:
CLASS_DEF(ABC)
{
private:
void ABCFun1();
void ABCFun2();
// ... and so on
}
Putting it in a base class makes it possible to write the macro and then just a normal class body.
So what can we do for #3 without a macro, if we don't need the sync-guarantee?
providing the string as a template parameter to some base class would be the nicest way but is, in short, not possible. This article covers strings as template parameters and has to fall back to macros in the end. The closest we can get is some form of the boost::mpl::string<'Hell','o Wo','rld!'>
mentioned in the article. The length would be limited by some arbitrary definition, if C++11 variadic templates are not available, and the definition of the base class (or boost::mpl::string
) would probably use a good deal of macro magic itself, but at least you are rid of self-defined macros in the class itself. Something that uses #2 and #3 would have to look somewhat like (i make the name longer to demonstate the restrictions of the approach)
class ABCDE: public Base<ABCDE, 'ABCD', 'E'> { /* ... */ }
having the string as a static data member constant with a predefined name. This is an option that fits well together with CRTP, if you have to use it anyways:
template <class T>
class Base {
public:
std::string GetClassName() const {
return T::name;
};
};
class ABC : public Base<ABC> {
public:
constexpr static char const * name = "ABC";
};
In C++03, you would have to use a static const
member that has to be defined outside the class definition (i.e. in ABC.cpp)
having the string as a nonstatic data member of ABC works similarly:
template <class T>
class Base {
public:
std::string GetClassName() const {
return static_cast<T const*>(this)->name;
};
};
class ABC : public Base<ABC> {
public:
const std::string name = "ABC";
};
This requires C++11 for the in-class member initialization. In C++03 the name has to be properly initialized in every constructor.
having the string as data member of the base class works and should be preferred imo, if you don't have type-dependent members, i.e. if you don't need CRTP to do #2:
class Base {
public:
Base(std::string nm) : name(nm) {}
std::string GetClassName() const {
return name;
};
std::string name;
};
class ABC : public Base {
public:
ABC() : Base("ABC") {}
ABC(int i) : ABC() { /*another ctor*/ }
};
To do the initialization of Base with the class name just once you can use the C++11 delegating constructors, although it's not much different writing Base("ABC")
or ABC()
in each initializer list.