интро

Отнсительно недавно, примерно с год назад, изучая libuv, наткнулся на отличный прием работы с препроцессором. Уже позже выяснилось что называется он X-macro и позволяет в макросах вызывать другие макросы. С его помошью очень легко избежать дублирования и ошибок в задачах где перечисления надо конвертировать в строки, давать строковые имена битовым флагам, рефлексии, сериализации и т.п.

пример

Перечисление с кодами ошибок и строкой с описанием:

#define JSON_ERRNO_MAP(XX)                           \
    XX(OK, "ok")                                     \
    XX(BAD_NUMBER, "bad number")                     \
    XX(BAD_STRING, "bad string")                     \
    XX(BAD_IDENTIFIER, "bad identifier")             \
    XX(STACK_OVERFLOW, "stack overflow")             \
    XX(STACK_UNDERFLOW, "stack underflow")           \
    XX(MISMATCH_BRACKET, "mismatch bracket")         \
    XX(UNEXPECTED_CHARACTER, "unexpected character") \
    XX(UNQUOTED_KEY, "unquoted key")                 \
    XX(BREAKING_BAD, "breaking bad")                 \
    XX(ALLOCATION_FAILURE, "allocation failure")

enum JsonErrno {
#define XX(no, str) JSON_##no,
    JSON_ERRNO_MAP(XX)
#undef XX
};

const char *jsonStrError(int err) {
    switch (err) {
#define XX(no, str) \
    case JSON_##no: \
        return str;
        JSON_ERRNO_MAP(XX)
#undef XX
    default:
        return "unknown";
    }
}

После препроцессора первращается в:

enum JsonErrno {
    JSON_OK,
    JSON_BAD_NUMBER,
    JSON_BAD_STRING,
    JSON_BAD_IDENTIFIER,
    JSON_STACK_OVERFLOW,
    JSON_STACK_UNDERFLOW,
    JSON_MISMATCH_BRACKET,
    JSON_UNEXPECTED_CHARACTER,
    JSON_UNQUOTED_KEY,
    JSON_BREAKING_BAD,
    JSON_ALLOCATION_FAILURE,
};

const char *jsonStrError(int err) {
    switch (err) {
    case JSON_OK:
        return "ok";
    case JSON_BAD_NUMBER:
        return "bad number";
    case JSON_BAD_STRING:
        return "bad string";
    case JSON_BAD_IDENTIFIER:
        return "bad identifier";
    case JSON_STACK_OVERFLOW:
        return "stack overflow";
    case JSON_STACK_UNDERFLOW:
        return "stack underflow";
    case JSON_MISMATCH_BRACKET:
        return "mismatch bracket";
    case JSON_UNEXPECTED_CHARACTER:
        return "unexpected character";
    case JSON_UNQUOTED_KEY:
        return "unquoted key";
    case JSON_BREAKING_BAD:
        return "breaking bad";
    case JSON_ALLOCATION_FAILURE:
        return "allocation failure";
    default:
        return "unknown";
    }
}