Can you give an example of a problem which the preprocessor solves better than modern C++?
#define LIST_ERROR_TYPES(X) \
X(OK, "") \
X(ERR_OOM, "out of memory") \
X(ERR_BAD_STRING_ENCODING, "string was invalid utf-8") \
...
#define DEF_ENUM(ident,_) ident,
typedef enum { LIST_ERROR_TYPES(DEF_ENUM) } err_t;
const char *get_err_msg(err_t err) {
#define IDENT2MSG(ident,msg) case ident: return msg;
switch (err) {
LIST_ERROR_TYPES(IDENT2MSG)
default: return "unknown error";
}
}
Which turns out to be useful for more than just error messages (which is still useful in C++ if you're avoiding exceptions). Recently, for example, I did something like that to map a small fixed set of object properties to database columns.If you want to see some actual code, I use X-macros, as well as other macros, heavily in the test suite for my (very) WIP programming language: https://github.com/alpha123/yu/tree/master/test
There's some other interesting and generic macros that I frequently use in there too: https://github.com/alpha123/yu/blob/master/src/yu_common.h
#define a_ frob(-1,0,0); grob("a"); blah(...);
#define b_ frob(1,0,0); grob("b"); blah(...);
#define c_ frob(0,-1,0); grob("c"); blah(...);
#define d_ frob(0,-1,1); grob("d"); blah(...);
#define e_ frob(0,0,-1); grob("e"); blah(...);
#define f_ frob(0,1,1); grob("f"); blah(...);
if (normal_order) {
a_ b_ c_ d_ e_ f_;
c_ b_ a_ f_ e_ d_;
} else if (special_order_1) {
b_ c_ d_ e_ a_;
d_ e_ c_ b_ a_ f_;
} else if (...) {
// ... and so on ...
}
#undef a_
#undef b_
#undef c_
#undef d_
#undef e_
#undef f_ auto a = [] {frob(-1,0,0); grob("a"); blah(...);};
auto b = [] {frob(1,0,0); grob("b"); blah(...);};
auto c = [] {frob(0,-1,0); grob("c"); blah(...);};
...
if (normal_order) {
a(); b(); c(); ...
} else if (...) {
b(); c(); a(); ...
} ... #define DISALLOW_COPY_AND_ASSIGN(TypeName) \
TypeName(const TypeName&) = delete; \
void operator=(const TypeName&) = delete
Or for things like compile-time injection of wrappers that you need to be completely gone in production versions. An example would be wrapping all OpenGL calls with glGetError() checks in debug builds.Modern C++ did not replace macros nor did it even try. some uses of macros were made obsolete (like min/max), but most were not.