Възможно ли е да се предотвратят дублиращи се идентични аргументи към макрос в C?

Има определени редки случаи, може да е полезно да предотвратите дублиране на аргументи към макрос.

Вземете този ELEM(value, ...) макрос,

За да проверите дали value е A, B или C.

if (ELEM(value, A, B, C)) { .... }

Някой може случайно да премине един и същ аргумент няколко пъти, напр.

if (ELEM(value, A, B, B)) { .... }

Въпреки че е валиден C, но почти сигурно е злополука и много малко вероятно да бъде това, което разработчикът е възнамерявал.

... това разбира се е тривиален пример, действителните случаи на грешка биха били по-сложни.

Въпрос

Има ли начин компилаторът да предупреждава/греши
при предаване на дублирани аргументи?


Уточнение:

  • Аргументите не са задължително, всички константи (те също могат да се смесват с променливи).

Бележки...

  • Питам, защото това е действителен бъг, който наскоро открих в някакъв код.
    Въпреки че макросите/компилаторите могат да използват ограничение, за да предотвратят грешки, това можеше да бъде открито рано, ако макросът не го позволяваше.
    Разбира се, този вид грешки трябва да се откриват при преглед на код...
    въпреки това, грешки се случват.

  • Това може да трябва да използва някои специфични за GCC/компилатора разширения, като __builtin_constant_p.

  • Един от начините да направите това (което не е доказателство за глупаци, но би помогнало да се избегнат някои грешки), може да бъде да преобразувате идентификаторите в низове, след което статично заявявате, ако някой от идентификаторите са точни съвпадения.
    Имат очевидния недостатък, че различните идентификатори могат да представляват една и съща постоянна стойност. Същият идентификатор може да бъде написан така, че да не се сравнява, напр.: A[0] срещу A[ 0 ].

  • Ако препроцесорът/компилаторът не може да направи това лесно, резервно решение може да бъде да се направи някакъв основен инструмент за статична проверка.


person ideasman42    schedule 25.07.2015    source източник
comment
Възможен път напред: Накарайте ELEM(var, A, B, C) да се разшири до ({ static_assert(A != B, #A " != " #B); static_assert(B != C, #B " != " #C); (var) == (A) || (var) == (B) || (var) == (C); }) ... по някакъв начин.   -  person melpomene    schedule 26.07.2015
comment
Бих опитал просто да декларирам нови променливи в израз на израз на GNU.   -  person o11c    schedule 26.07.2015
comment
@o11c, запознат съм с изразите за изявления, но не съм сигурен как биха помогнали в този случай.   -  person ideasman42    schedule 26.07.2015
comment
@ideasman42 Не ми е удобно да правя for-each-vararg в препроцесора, но нещо като: #define declare_var(a) __attribute__((unused)) int _duplicate ## a; #define ELEM(var, ...) ({ FOREACH(declare_var, __VA_ARGS__); ELEM_NODUPLICATECHECK(var, __VA_ARGS__); })   -  person o11c    schedule 26.07.2015
comment
@o11c, виждам, че това работи в много случаи, но не и ако някои от аргументите са изрази като a->b или c[d].   -  person ideasman42    schedule 26.07.2015


Отговори (2)


Опитайте следното, не сте сигурни дали това е стандартен C или просто разширение на gcc.

#define MAC(a,b,c) _Static_assert(a != b, "must not be equal to b")

int main()
{
    MAC(2,1,0);
    MAC(1,1,0);
}

Редактиране: Това е част от стандарта C11, можете също да използвате static_assert, както е дефинирано в assert.h

person doron    schedule 25.07.2015
comment
Това ще работи само когато всички входове са константи. Актуализиран въпрос, тъй като това не беше изрично посочено (въпреки че беше намекнато с __builtin_constant_p). - person ideasman42; 26.07.2015
comment
_Static_assert е функция в C11, не само в GCC. - person Jonathan Leffler; 26.07.2015

Този метод работи, като сравнява идентификаторите и се проваля по време на компилиране, ако те не отговарят на нашите изисквания.

Не е напълно безупречно, тъй като 2 аргумента може да са едни и същи, но да не съвпадат, когато се преобразуват в техните идентификатори на низове. Въпреки това ще улови най-често срещаните грешки.


Обърнете внимание, че докато това работи, не съм сигурен, че бих искал да го ангажирам в производствения код, тъй като разчита на някои доста сложни макроси и значително забавя компилацията (поне в моите собствени тестове с gcc ).


Пример за връзка към един файл.

Пример, изходи:

Found!
Strings match? 0
Strings match? 1
Strings match? 0
Strings match? 1

C файл:

#include <stdio.h>

int main(void)
{
    int a = 1;

    int A = 0, B = 1, C = 2, D = 3;

    STATIC_ASSERT(!PP_ID_EQ(FOO, BAR));
    STATIC_ASSERT(UNIQUE(A, B, C, D));
    if (ELEM(a, A, B, C)) printf("Found!\n");

    /* uncomment will fail to compile */
    // STATIC_ASSERT(!PP_ID_EQ(MATCH, MATCH));
    // STATIC_ASSERT(UNIQUE(D, A, B, C, D));
    // if (ELEM(a, A, B, B)) printf("Found!\n");

    printf("Strings match? %d\n", PP_ID_EQ(FOO, BAR));
    printf("Strings match? %d\n", PP_ID_EQ(BAR, BAR));
    printf("Strings match? %d\n", PP_STR_EQ("HELLO", "WORLD"));
    printf("Strings match? %d\n", PP_STR_EQ("WORLD", "WORLD"));
}

C заглавие:

/**
 * Script to generate...
 * \code{.py}
 * MAX = 64
 * for i in range(1, MAX):
 *     if i == 1:
 *         print('#define _PP_STRCMP_%d(a, b)                       ((a)[1] == (b)[1])' % (i,))
 *     else:
 *         print('#define _PP_STRCMP_%d(a, b) _PP_STRCMP_%d(a, b) && ((a)[%d] == (b)[%d])' % (i, i - 1, i, i))
 * \endcode
 */
#define _PP_STRCMP_1(a, b)                       ((a)[1] == (b)[1])
#define _PP_STRCMP_2(a, b) _PP_STRCMP_1(a, b) && ((a)[2] == (b)[2])
#define _PP_STRCMP_3(a, b) _PP_STRCMP_2(a, b) && ((a)[3] == (b)[3])
#define _PP_STRCMP_4(a, b) _PP_STRCMP_3(a, b) && ((a)[4] == (b)[4])
#define _PP_STRCMP_5(a, b) _PP_STRCMP_4(a, b) && ((a)[5] == (b)[5])
#define _PP_STRCMP_6(a, b) _PP_STRCMP_5(a, b) && ((a)[6] == (b)[6])
#define _PP_STRCMP_7(a, b) _PP_STRCMP_6(a, b) && ((a)[7] == (b)[7])
#define _PP_STRCMP_8(a, b) _PP_STRCMP_7(a, b) && ((a)[8] == (b)[8])
#define _PP_STRCMP_9(a, b) _PP_STRCMP_8(a, b) && ((a)[9] == (b)[9])
#define _PP_STRCMP_10(a, b) _PP_STRCMP_9(a, b) && ((a)[10] == (b)[10])
#define _PP_STRCMP_11(a, b) _PP_STRCMP_10(a, b) && ((a)[11] == (b)[11])
#define _PP_STRCMP_12(a, b) _PP_STRCMP_11(a, b) && ((a)[12] == (b)[12])
#define _PP_STRCMP_13(a, b) _PP_STRCMP_12(a, b) && ((a)[13] == (b)[13])
#define _PP_STRCMP_14(a, b) _PP_STRCMP_13(a, b) && ((a)[14] == (b)[14])
#define _PP_STRCMP_15(a, b) _PP_STRCMP_14(a, b) && ((a)[15] == (b)[15])
#define _PP_STRCMP_16(a, b) _PP_STRCMP_15(a, b) && ((a)[16] == (b)[16])
#define _PP_STRCMP_17(a, b) _PP_STRCMP_16(a, b) && ((a)[17] == (b)[17])
#define _PP_STRCMP_18(a, b) _PP_STRCMP_17(a, b) && ((a)[18] == (b)[18])
#define _PP_STRCMP_19(a, b) _PP_STRCMP_18(a, b) && ((a)[19] == (b)[19])
#define _PP_STRCMP_20(a, b) _PP_STRCMP_19(a, b) && ((a)[20] == (b)[20])
#define _PP_STRCMP_21(a, b) _PP_STRCMP_20(a, b) && ((a)[21] == (b)[21])
#define _PP_STRCMP_22(a, b) _PP_STRCMP_21(a, b) && ((a)[22] == (b)[22])
#define _PP_STRCMP_23(a, b) _PP_STRCMP_22(a, b) && ((a)[23] == (b)[23])
#define _PP_STRCMP_24(a, b) _PP_STRCMP_23(a, b) && ((a)[24] == (b)[24])
#define _PP_STRCMP_25(a, b) _PP_STRCMP_24(a, b) && ((a)[25] == (b)[25])
#define _PP_STRCMP_26(a, b) _PP_STRCMP_25(a, b) && ((a)[26] == (b)[26])
#define _PP_STRCMP_27(a, b) _PP_STRCMP_26(a, b) && ((a)[27] == (b)[27])
#define _PP_STRCMP_28(a, b) _PP_STRCMP_27(a, b) && ((a)[28] == (b)[28])
#define _PP_STRCMP_29(a, b) _PP_STRCMP_28(a, b) && ((a)[29] == (b)[29])
#define _PP_STRCMP_30(a, b) _PP_STRCMP_29(a, b) && ((a)[30] == (b)[30])
#define _PP_STRCMP_31(a, b) _PP_STRCMP_30(a, b) && ((a)[31] == (b)[31])
#define _PP_STRCMP_32(a, b) _PP_STRCMP_31(a, b) && ((a)[32] == (b)[32])
#define _PP_STRCMP_33(a, b) _PP_STRCMP_32(a, b) && ((a)[33] == (b)[33])
#define _PP_STRCMP_34(a, b) _PP_STRCMP_33(a, b) && ((a)[34] == (b)[34])
#define _PP_STRCMP_35(a, b) _PP_STRCMP_34(a, b) && ((a)[35] == (b)[35])
#define _PP_STRCMP_36(a, b) _PP_STRCMP_35(a, b) && ((a)[36] == (b)[36])
#define _PP_STRCMP_37(a, b) _PP_STRCMP_36(a, b) && ((a)[37] == (b)[37])
#define _PP_STRCMP_38(a, b) _PP_STRCMP_37(a, b) && ((a)[38] == (b)[38])
#define _PP_STRCMP_39(a, b) _PP_STRCMP_38(a, b) && ((a)[39] == (b)[39])
#define _PP_STRCMP_40(a, b) _PP_STRCMP_39(a, b) && ((a)[40] == (b)[40])
#define _PP_STRCMP_41(a, b) _PP_STRCMP_40(a, b) && ((a)[41] == (b)[41])
#define _PP_STRCMP_42(a, b) _PP_STRCMP_41(a, b) && ((a)[42] == (b)[42])
#define _PP_STRCMP_43(a, b) _PP_STRCMP_42(a, b) && ((a)[43] == (b)[43])
#define _PP_STRCMP_44(a, b) _PP_STRCMP_43(a, b) && ((a)[44] == (b)[44])
#define _PP_STRCMP_45(a, b) _PP_STRCMP_44(a, b) && ((a)[45] == (b)[45])
#define _PP_STRCMP_46(a, b) _PP_STRCMP_45(a, b) && ((a)[46] == (b)[46])
#define _PP_STRCMP_47(a, b) _PP_STRCMP_46(a, b) && ((a)[47] == (b)[47])
#define _PP_STRCMP_48(a, b) _PP_STRCMP_47(a, b) && ((a)[48] == (b)[48])
#define _PP_STRCMP_49(a, b) _PP_STRCMP_48(a, b) && ((a)[49] == (b)[49])
#define _PP_STRCMP_50(a, b) _PP_STRCMP_49(a, b) && ((a)[50] == (b)[50])
#define _PP_STRCMP_51(a, b) _PP_STRCMP_50(a, b) && ((a)[51] == (b)[51])
#define _PP_STRCMP_52(a, b) _PP_STRCMP_51(a, b) && ((a)[52] == (b)[52])
#define _PP_STRCMP_53(a, b) _PP_STRCMP_52(a, b) && ((a)[53] == (b)[53])
#define _PP_STRCMP_54(a, b) _PP_STRCMP_53(a, b) && ((a)[54] == (b)[54])
#define _PP_STRCMP_55(a, b) _PP_STRCMP_54(a, b) && ((a)[55] == (b)[55])
#define _PP_STRCMP_56(a, b) _PP_STRCMP_55(a, b) && ((a)[56] == (b)[56])
#define _PP_STRCMP_57(a, b) _PP_STRCMP_56(a, b) && ((a)[57] == (b)[57])
#define _PP_STRCMP_58(a, b) _PP_STRCMP_57(a, b) && ((a)[58] == (b)[58])
#define _PP_STRCMP_59(a, b) _PP_STRCMP_58(a, b) && ((a)[59] == (b)[59])
#define _PP_STRCMP_60(a, b) _PP_STRCMP_59(a, b) && ((a)[60] == (b)[60])
#define _PP_STRCMP_61(a, b) _PP_STRCMP_60(a, b) && ((a)[61] == (b)[61])
#define _PP_STRCMP_62(a, b) _PP_STRCMP_61(a, b) && ((a)[62] == (b)[62])
#define _PP_STRCMP_63(a, b) _PP_STRCMP_62(a, b) && ((a)[63] == (b)[63])

/**
 * Strings must be constant and NULL terminated.
 *
 * Script to generate...
 * \code{.py}
 * MAX = 64
 * print('#define PP_STR_EQ(a, b) ( \\')
 * print('   (sizeof(int[(sizeof(a) > %d ? -1 : 1)])) && \\' % MAX)
 * print('   (sizeof(a) == sizeof(b)) && \\')
 * print(' (((sizeof(a) == 1)) || \\')
 * for i in range(2, MAX + 1):
 *     print('  ((sizeof(a) == %d) && _PP_STRCMP_%d(a, b)) %s' % (i, i - 1, "|| \\" if i != MAX else "))"))
 * \endcode
 */
#define PP_STR_EQ(a, b) ( \
   (sizeof(int[(sizeof(a) > 64 ? -1 : 1)])) && \
   (sizeof(a) == sizeof(b)) && \
 (((sizeof(a) == 1)) || \
  ((sizeof(a) == 2) && _PP_STRCMP_1(a, b)) || \
  ((sizeof(a) == 3) && _PP_STRCMP_2(a, b)) || \
  ((sizeof(a) == 4) && _PP_STRCMP_3(a, b)) || \
  ((sizeof(a) == 5) && _PP_STRCMP_4(a, b)) || \
  ((sizeof(a) == 6) && _PP_STRCMP_5(a, b)) || \
  ((sizeof(a) == 7) && _PP_STRCMP_6(a, b)) || \
  ((sizeof(a) == 8) && _PP_STRCMP_7(a, b)) || \
  ((sizeof(a) == 9) && _PP_STRCMP_8(a, b)) || \
  ((sizeof(a) == 10) && _PP_STRCMP_9(a, b)) || \
  ((sizeof(a) == 11) && _PP_STRCMP_10(a, b)) || \
  ((sizeof(a) == 12) && _PP_STRCMP_11(a, b)) || \
  ((sizeof(a) == 13) && _PP_STRCMP_12(a, b)) || \
  ((sizeof(a) == 14) && _PP_STRCMP_13(a, b)) || \
  ((sizeof(a) == 15) && _PP_STRCMP_14(a, b)) || \
  ((sizeof(a) == 16) && _PP_STRCMP_15(a, b)) || \
  ((sizeof(a) == 17) && _PP_STRCMP_16(a, b)) || \
  ((sizeof(a) == 18) && _PP_STRCMP_17(a, b)) || \
  ((sizeof(a) == 19) && _PP_STRCMP_18(a, b)) || \
  ((sizeof(a) == 20) && _PP_STRCMP_19(a, b)) || \
  ((sizeof(a) == 21) && _PP_STRCMP_20(a, b)) || \
  ((sizeof(a) == 22) && _PP_STRCMP_21(a, b)) || \
  ((sizeof(a) == 23) && _PP_STRCMP_22(a, b)) || \
  ((sizeof(a) == 24) && _PP_STRCMP_23(a, b)) || \
  ((sizeof(a) == 25) && _PP_STRCMP_24(a, b)) || \
  ((sizeof(a) == 26) && _PP_STRCMP_25(a, b)) || \
  ((sizeof(a) == 27) && _PP_STRCMP_26(a, b)) || \
  ((sizeof(a) == 28) && _PP_STRCMP_27(a, b)) || \
  ((sizeof(a) == 29) && _PP_STRCMP_28(a, b)) || \
  ((sizeof(a) == 30) && _PP_STRCMP_29(a, b)) || \
  ((sizeof(a) == 31) && _PP_STRCMP_30(a, b)) || \
  ((sizeof(a) == 32) && _PP_STRCMP_31(a, b)) || \
  ((sizeof(a) == 33) && _PP_STRCMP_32(a, b)) || \
  ((sizeof(a) == 34) && _PP_STRCMP_33(a, b)) || \
  ((sizeof(a) == 35) && _PP_STRCMP_34(a, b)) || \
  ((sizeof(a) == 36) && _PP_STRCMP_35(a, b)) || \
  ((sizeof(a) == 37) && _PP_STRCMP_36(a, b)) || \
  ((sizeof(a) == 38) && _PP_STRCMP_37(a, b)) || \
  ((sizeof(a) == 39) && _PP_STRCMP_38(a, b)) || \
  ((sizeof(a) == 40) && _PP_STRCMP_39(a, b)) || \
  ((sizeof(a) == 41) && _PP_STRCMP_40(a, b)) || \
  ((sizeof(a) == 42) && _PP_STRCMP_41(a, b)) || \
  ((sizeof(a) == 43) && _PP_STRCMP_42(a, b)) || \
  ((sizeof(a) == 44) && _PP_STRCMP_43(a, b)) || \
  ((sizeof(a) == 45) && _PP_STRCMP_44(a, b)) || \
  ((sizeof(a) == 46) && _PP_STRCMP_45(a, b)) || \
  ((sizeof(a) == 47) && _PP_STRCMP_46(a, b)) || \
  ((sizeof(a) == 48) && _PP_STRCMP_47(a, b)) || \
  ((sizeof(a) == 49) && _PP_STRCMP_48(a, b)) || \
  ((sizeof(a) == 50) && _PP_STRCMP_49(a, b)) || \
  ((sizeof(a) == 51) && _PP_STRCMP_50(a, b)) || \
  ((sizeof(a) == 52) && _PP_STRCMP_51(a, b)) || \
  ((sizeof(a) == 53) && _PP_STRCMP_52(a, b)) || \
  ((sizeof(a) == 54) && _PP_STRCMP_53(a, b)) || \
  ((sizeof(a) == 55) && _PP_STRCMP_54(a, b)) || \
  ((sizeof(a) == 56) && _PP_STRCMP_55(a, b)) || \
  ((sizeof(a) == 57) && _PP_STRCMP_56(a, b)) || \
  ((sizeof(a) == 58) && _PP_STRCMP_57(a, b)) || \
  ((sizeof(a) == 59) && _PP_STRCMP_58(a, b)) || \
  ((sizeof(a) == 60) && _PP_STRCMP_59(a, b)) || \
  ((sizeof(a) == 61) && _PP_STRCMP_60(a, b)) || \
  ((sizeof(a) == 62) && _PP_STRCMP_61(a, b)) || \
  ((sizeof(a) == 63) && _PP_STRCMP_62(a, b)) || \
  ((sizeof(a) == 64) && _PP_STRCMP_63(a, b)) ))


/* -------------------------------------------------------------------- */
#define STRINGIFY_ARG(x) "" #x
#define STRINGIFY_APPEND(a, b) "" a #b
#define STRINGIFY(x) STRINGIFY_APPEND("", x)

#define PP_ID_EQ(a, b) \
    PP_STR_EQ(STRINGIFY(a), STRINGIFY(b))


#define STATIC_ASSERT(expr) \
    ((void)sizeof(int[(expr) ? 1 : -1]))


/* -------------------------------------------------------------------- */
/* varargs macros (keep first so others can use) */
/* --- internal helpers --- */
#define _VA_NARGS_GLUE(x, y) x y
#define _VA_NARGS_RETURN_COUNT(\
    _1_, _2_, _3_, _4_, _5_, _6_, _7_, _8_, _9_, _10_, _11_, _12_, _13_, _14_, _15_, _16_, \
    _17_, _18_, _19_, _20_, _21_, _22_, _23_, _24_, _25_, _26_, _27_, _28_, _29_, _30_, _31_, _32_, \
    _33_, _34_, _35_, _36_, _37_, _38_, _39_, _40_, _41_, _42_, _43_, _44_, _45_, _46_, _47_, _48_, \
    _49_, _50_, _51_, _52_, _53_, _54_, _55_, _56_, _57_, _58_, _59_, _60_, _61_, _62_, _63_, _64_, \
    count, ...) count
#define _VA_NARGS_EXPAND(args) _VA_NARGS_RETURN_COUNT args
/* 64 args max */
#define _VA_NARGS_COUNT(...) _VA_NARGS_EXPAND((__VA_ARGS__, \
    64, 63, 62, 61, 60, 59, 58, 57, 56, 55, 54, 53, 52, 51, 50, 49, \
    48, 47, 46, 45, 44, 43, 42, 41, 40, 39, 38, 37, 36, 35, 34, 33, \
    32, 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, \
    16, 15, 14, 13, 12, 11, 10,  9,  8,  7,  6,  5,  4,  3,  2, 1, 0))
#define _VA_NARGS_OVERLOAD_MACRO2(name, count) name##count
#define _VA_NARGS_OVERLOAD_MACRO1(name, count) _VA_NARGS_OVERLOAD_MACRO2(name, count)
#define _VA_NARGS_OVERLOAD_MACRO(name,  count) _VA_NARGS_OVERLOAD_MACRO1(name, count)
/* --- expose for re-use --- */
#define VA_NARGS_CALL_OVERLOAD(name, ...) \
    _VA_NARGS_GLUE(_VA_NARGS_OVERLOAD_MACRO(name, _VA_NARGS_COUNT(__VA_ARGS__)), (__VA_ARGS__))


/* -------------------------------------------------------------------- */
/* UNIQUE#(v, ...): are all args unique?
 *
 * Script to generate...
 * \code{.py}
 * for i in range(1, 16):
 *     args = [(chr(ord('a') + (c % 26)) + (chr(ord('0') + (c // 26)))) for c in range(i + 1)]
 *     print("#define _VA_UNIQUE%d(%s) \\" % (i + 1, ", ".join(args)))
 *     if i > 1:
 *         expr = ["_VA_UNIQUE%d(%s)" % (i, ", ".join(args[:-1]))]
 *     else:
 *         expr = []
 *     for j in range(len(args) - 1):
 *             expr.append("!PP_ID_EQ(%s, %s)" % (args[j], args[-1]))
 *     print("    (" + " && ".join(expr) + ")")
 * \endcode
 */
#define _VA_UNIQUE2(a0, b0) \
    (!PP_ID_EQ(a0, b0))
#define _VA_UNIQUE3(a0, b0, c0) \
    (_VA_UNIQUE2(a0, b0) && !PP_ID_EQ(a0, c0) && !PP_ID_EQ(b0, c0))
#define _VA_UNIQUE4(a0, b0, c0, d0) \
    (_VA_UNIQUE3(a0, b0, c0) && !PP_ID_EQ(a0, d0) && \
     !PP_ID_EQ(b0, d0) && !PP_ID_EQ(c0, d0))
#define _VA_UNIQUE5(a0, b0, c0, d0, e0) \
    (_VA_UNIQUE4(a0, b0, c0, d0) && !PP_ID_EQ(a0, e0) && \
     !PP_ID_EQ(b0, e0) && !PP_ID_EQ(c0, e0) && !PP_ID_EQ(d0, e0))
#define _VA_UNIQUE6(a0, b0, c0, d0, e0, f0) \
    (_VA_UNIQUE5(a0, b0, c0, d0, e0) && !PP_ID_EQ(a0, f0) && \
     !PP_ID_EQ(b0, f0) && !PP_ID_EQ(c0, f0) && !PP_ID_EQ(d0, f0) && !PP_ID_EQ(e0, f0))
#define _VA_UNIQUE7(a0, b0, c0, d0, e0, f0, g0) \
    (_VA_UNIQUE6(a0, b0, c0, d0, e0, f0) && !PP_ID_EQ(a0, g0) && \
     !PP_ID_EQ(b0, g0) && !PP_ID_EQ(c0, g0) && !PP_ID_EQ(d0, g0) && !PP_ID_EQ(e0, g0) && \
     !PP_ID_EQ(f0, g0))
#define _VA_UNIQUE8(a0, b0, c0, d0, e0, f0, g0, h0) \
    (_VA_UNIQUE7(a0, b0, c0, d0, e0, f0, g0) && !PP_ID_EQ(a0, h0) && \
     !PP_ID_EQ(b0, h0) && !PP_ID_EQ(c0, h0) && !PP_ID_EQ(d0, h0) && !PP_ID_EQ(e0, h0) && \
     !PP_ID_EQ(f0, h0) && !PP_ID_EQ(g0, h0))
#define _VA_UNIQUE9(a0, b0, c0, d0, e0, f0, g0, h0, i0) \
    (_VA_UNIQUE8(a0, b0, c0, d0, e0, f0, g0, h0) && !PP_ID_EQ(a0, i0) && \
     !PP_ID_EQ(b0, i0) && !PP_ID_EQ(c0, i0) && !PP_ID_EQ(d0, i0) && !PP_ID_EQ(e0, i0) && \
     !PP_ID_EQ(f0, i0) && !PP_ID_EQ(g0, i0) && !PP_ID_EQ(h0, i0))
#define _VA_UNIQUE10(a0, b0, c0, d0, e0, f0, g0, h0, i0, j0) \
    (_VA_UNIQUE9(a0, b0, c0, d0, e0, f0, g0, h0, i0) && !PP_ID_EQ(a0, j0) && \
     !PP_ID_EQ(b0, j0) && !PP_ID_EQ(c0, j0) && !PP_ID_EQ(d0, j0) && !PP_ID_EQ(e0, j0) && \
     !PP_ID_EQ(f0, j0) && !PP_ID_EQ(g0, j0) && !PP_ID_EQ(h0, j0) && !PP_ID_EQ(i0, j0))
#define _VA_UNIQUE11(a0, b0, c0, d0, e0, f0, g0, h0, i0, j0, k0) \
    (_VA_UNIQUE10(a0, b0, c0, d0, e0, f0, g0, h0, i0, j0) && !PP_ID_EQ(a0, k0) && \
     !PP_ID_EQ(b0, k0) && !PP_ID_EQ(c0, k0) && !PP_ID_EQ(d0, k0) && !PP_ID_EQ(e0, k0) && \
     !PP_ID_EQ(f0, k0) && !PP_ID_EQ(g0, k0) && !PP_ID_EQ(h0, k0) && !PP_ID_EQ(i0, k0) && \
     !PP_ID_EQ(j0, k0))
#define _VA_UNIQUE12(a0, b0, c0, d0, e0, f0, g0, h0, i0, j0, k0, l0) \
    (_VA_UNIQUE11(a0, b0, c0, d0, e0, f0, g0, h0, i0, j0, k0) && !PP_ID_EQ(a0, l0) && \
     !PP_ID_EQ(b0, l0) && !PP_ID_EQ(c0, l0) && !PP_ID_EQ(d0, l0) && !PP_ID_EQ(e0, l0) && \
     !PP_ID_EQ(f0, l0) && !PP_ID_EQ(g0, l0) && !PP_ID_EQ(h0, l0) && !PP_ID_EQ(i0, l0) && \
     !PP_ID_EQ(j0, l0) && !PP_ID_EQ(k0, l0))
#define _VA_UNIQUE13(a0, b0, c0, d0, e0, f0, g0, h0, i0, j0, k0, l0, m0) \
    (_VA_UNIQUE12(a0, b0, c0, d0, e0, f0, g0, h0, i0, j0, k0, l0) && !PP_ID_EQ(a0, m0) && \
     !PP_ID_EQ(b0, m0) && !PP_ID_EQ(c0, m0) && !PP_ID_EQ(d0, m0) && !PP_ID_EQ(e0, m0) && \
     !PP_ID_EQ(f0, m0) && !PP_ID_EQ(g0, m0) && !PP_ID_EQ(h0, m0) && !PP_ID_EQ(i0, m0) && \
     !PP_ID_EQ(j0, m0) && !PP_ID_EQ(k0, m0) && !PP_ID_EQ(l0, m0))
#define _VA_UNIQUE14(a0, b0, c0, d0, e0, f0, g0, h0, i0, j0, k0, l0, m0, n0) \
    (_VA_UNIQUE13(a0, b0, c0, d0, e0, f0, g0, h0, i0, j0, k0, l0, m0) && !PP_ID_EQ(a0, n0) && \
     !PP_ID_EQ(b0, n0) && !PP_ID_EQ(c0, n0) && !PP_ID_EQ(d0, n0) && !PP_ID_EQ(e0, n0) && \
     !PP_ID_EQ(f0, n0) && !PP_ID_EQ(g0, n0) && !PP_ID_EQ(h0, n0) && !PP_ID_EQ(i0, n0) && \
     !PP_ID_EQ(j0, n0) && !PP_ID_EQ(k0, n0) && !PP_ID_EQ(l0, n0) && !PP_ID_EQ(m0, n0))
#define _VA_UNIQUE15(a0, b0, c0, d0, e0, f0, g0, h0, i0, j0, k0, l0, m0, n0, o0) \
    (_VA_UNIQUE14(a0, b0, c0, d0, e0, f0, g0, h0, i0, j0, k0, l0, m0, n0) && !PP_ID_EQ(a0, o0) && \
     !PP_ID_EQ(b0, o0) && !PP_ID_EQ(c0, o0) && !PP_ID_EQ(d0, o0) && !PP_ID_EQ(e0, o0) && \
     !PP_ID_EQ(f0, o0) && !PP_ID_EQ(g0, o0) && !PP_ID_EQ(h0, o0) && !PP_ID_EQ(i0, o0) && \
     !PP_ID_EQ(j0, o0) && !PP_ID_EQ(k0, o0) && !PP_ID_EQ(l0, o0) && !PP_ID_EQ(m0, o0) && \
     !PP_ID_EQ(n0, o0))
#define _VA_UNIQUE16(a0, b0, c0, d0, e0, f0, g0, h0, i0, j0, k0, l0, m0, n0, o0, p0) \
    (_VA_UNIQUE15(a0, b0, c0, d0, e0, f0, g0, h0, i0, j0, k0, l0, m0, n0, o0) && !PP_ID_EQ(a0, p0) && \
     !PP_ID_EQ(b0, p0) && !PP_ID_EQ(c0, p0) && !PP_ID_EQ(d0, p0) && !PP_ID_EQ(e0, p0) && \
     !PP_ID_EQ(f0, p0) && !PP_ID_EQ(g0, p0) && !PP_ID_EQ(h0, p0) && !PP_ID_EQ(i0, p0) && \
     !PP_ID_EQ(j0, p0) && !PP_ID_EQ(k0, p0) && !PP_ID_EQ(l0, p0) && !PP_ID_EQ(m0, p0) && \
     !PP_ID_EQ(n0, p0) && !PP_ID_EQ(o0, p0))

/* reusable UNIQUE macro */
#define UNIQUE(...) VA_NARGS_CALL_OVERLOAD(_VA_UNIQUE, __VA_ARGS__)


/* -------------------------------------------------------------------- */
/* ELEM#(v, ...): is the first arg equal any others? */
/* internal helpers*/
#define _VA_ELEM2(v, a) \
       ((v) == (a))
#define _VA_ELEM3(v, a, b) \
       (_VA_ELEM2(v, a) || ((v) == (b)))
#define _VA_ELEM4(v, a, b, c) \
       (_VA_ELEM3(v, a, b) || ((v) == (c)))
#define _VA_ELEM5(v, a, b, c, d) \
       (_VA_ELEM4(v, a, b, c) || ((v) == (d)))
#define _VA_ELEM6(v, a, b, c, d, e) \
       (_VA_ELEM5(v, a, b, c, d) || ((v) == (e)))
#define _VA_ELEM7(v, a, b, c, d, e, f) \
       (_VA_ELEM6(v, a, b, c, d, e) || ((v) == (f)))
#define _VA_ELEM8(v, a, b, c, d, e, f, g) \
       (_VA_ELEM7(v, a, b, c, d, e, f) || ((v) == (g)))
#define _VA_ELEM9(v, a, b, c, d, e, f, g, h) \
       (_VA_ELEM8(v, a, b, c, d, e, f, g) || ((v) == (h)))
#define _VA_ELEM10(v, a, b, c, d, e, f, g, h, i) \
       (_VA_ELEM9(v, a, b, c, d, e, f, g, h) || ((v) == (i)))
#define _VA_ELEM11(v, a, b, c, d, e, f, g, h, i, j) \
       (_VA_ELEM10(v, a, b, c, d, e, f, g, h, i) || ((v) == (j)))
#define _VA_ELEM12(v, a, b, c, d, e, f, g, h, i, j, k) \
       (_VA_ELEM11(v, a, b, c, d, e, f, g, h, i, j) || ((v) == (k)))
#define _VA_ELEM13(v, a, b, c, d, e, f, g, h, i, j, k, l) \
       (_VA_ELEM12(v, a, b, c, d, e, f, g, h, i, j, k) || ((v) == (l)))
#define _VA_ELEM14(v, a, b, c, d, e, f, g, h, i, j, k, l, m) \
       (_VA_ELEM13(v, a, b, c, d, e, f, g, h, i, j, k, l) || ((v) == (m)))
#define _VA_ELEM15(v, a, b, c, d, e, f, g, h, i, j, k, l, m, n) \
       (_VA_ELEM14(v, a, b, c, d, e, f, g, h, i, j, k, l, m) || ((v) == (n)))
#define _VA_ELEM16(v, a, b, c, d, e, f, g, h, i, j, k, l, m, n, o) \
       (_VA_ELEM15(v, a, b, c, d, e, f, g, h, i, j, k, l, m, n) || ((v) == (o)))
#define _VA_ELEM17(v, a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p) \
       (_VA_ELEM16(v, a, b, c, d, e, f, g, h, i, j, k, l, m, n, o) || ((v) == (p)))

/* reusable ELEM macro */
#define ELEM(...) ((void)(sizeof(int[UNIQUE(__VA_ARGS__) ? 1 : -1])), \
                  VA_NARGS_CALL_OVERLOAD(_VA_ELEM, __VA_ARGS__))

Тествано с gcc5.2

person ideasman42    schedule 26.07.2015