2016-09-15 4 views
1

Я пытаюсь написать макрос, который подсчитывает количество конкретных элементов в последовательности. Например. для последовательности (A) (B) (A) Я хочу, чтобы получить количество 2 для А.
Теперь принимая подход от https://stackoverflow.com/a/12540675/2525536 я в конечном итоге с этим кодом:C препроцессор - рекурсивный условный счетный макрос

#define CAT(x, ...) CAT1(x, __VA_ARGS__) 
#define CAT1(x, ...) CAT2(x, __VA_ARGS__) 
#define CAT2(x, ...) x ## __VA_ARGS__ 

#define EXPAND(...) __VA_ARGS__ 
#define EAT(...) 
#define DEFER(...) __VA_ARGS__ EAT() 
#define OBSTRUCT(...) __VA_ARGS__ DEFER(EAT)() 

#define SIZE(seq) CAT(SIZE_, SIZE_0 seq) 
#define SIZE_0(...) SIZE_1 
#define SIZE_1(...) SIZE_2 
#define SIZE_2(...) SIZE_3 
#define SIZE_3(...) SIZE_4 
#define SIZE_SIZE_0 0 
#define SIZE_SIZE_1 1 
#define SIZE_SIZE_2 2 
#define SIZE_SIZE_3 3 
#define SIZE_SIZE_4 4 

#define GET_FIRST(x) GET_FIRST2(GET_FIRST1 x) 
#define GET_FIRST1(x) x, EAT() 
#define GET_FIRST2(x) GET_FIRST3(x) 
#define GET_FIRST3(x, ...) x 

#define POP_FIRST(x) EAT x 

#define EVAL(...) EVAL1(EVAL1(__VA_ARGS__)) 
#define EVAL1(...) EVAL2(EVAL2(__VA_ARGS__)) 
#define EVAL2(...) EVAL3(EVAL3(__VA_ARGS__)) 
#define EVAL3(...) EVAL4(EVAL4(__VA_ARGS__)) 
#define EVAL4(...) __VA_ARGS__ 

#define CHECK_N(x, n, ...) n 

#define CHECK(...) CHECK_N(__VA_ARGS__, 0,) 
#define CHECK_PROBE(x) x, 1, 

#define NOT(x) CHECK(CAT(NOT_, x)) 
#define NOT_0 ~, 1, 

#define COMPL(b) CAT(COMPL_, b) 
#define COMPL_0 1 
#define COMPL_1 0 

#define BOOL(x) COMPL(NOT(x)) 

#define IIF(c) CAT(IIF_, c) 
#define IIF_0(t, ...) __VA_ARGS__ 
#define IIF_1(t, ...) t 

#define IF(c) IIF(BOOL(c)) 

#define WHEN(c) IF(c)(EXPAND, EAT) 

#define INC(x) CAT(INC_, x) 
#define INC_0 1 
#define INC_1 2 
#define INC_2 3 
#define INC_3 4 
#define INC_4 5 

#define DEC(x) CAT(DEC_, x) 
#define DEC_0 0 
#define DEC_1 0 
#define DEC_2 1 
#define DEC_3 2 
#define DEC_4 3 
#define DEC_5 4 

#define COUNT_IF(tpl, data, x) COUNT_IF1(0, SIZE(x), 0, tpl, data, x) 
#define COUNT_IF1(i, n, count, tpl, data, x) \ 
    IF(n) (\ 
     OBSTRUCT(CAT) (\ 
      COUNT_IF3_, \ 
      tpl(i, data, GET_FIRST(x)) \ 
     ) (\ 
      OBSTRUCT(COUNT_IF2)() \ 
       (INC(i), DEC(n), count, tpl, data, POP_FIRST(x)) /* call recursive */ \ 
     ) \ 
     , count \ 
    ) 
#define COUNT_IF2() COUNT_IF1 
#define COUNT_IF3_0 EXPAND 
#define COUNT_IF3_1 INC 

#define A_EQUAL_A(...) 1 
#define A_EQUAL_B(...) 0 
#define COUNT_A(i, data, x) CAT(A_EQUAL_, x)(i) 

EVAL(COUNT_IF(COUNT_A, ~, (A))) 
EVAL(COUNT_IF(COUNT_A, ~, (B))) 
EVAL(COUNT_IF(COUNT_A, ~, (A)(B))) 
EVAL(COUNT_IF(COUNT_A, ~, (B)(A))) 
EVAL(COUNT_IF(COUNT_A, ~, (A)(A))) 
EVAL(COUNT_IF(COUNT_A, ~, (B)(B))) 
EVAL(COUNT_IF(COUNT_A, ~, (A)(B)(A))) 

Это работает уже довольно хорошо первые 4 примера, но заканчиваются неправильным расширением имени макроса для других (где INC или EXPAND необходимо развернуть более одного раза).

Причина, вероятно, в том, что макросы отмечены синим цветом в C, поэтому это работает https://stackoverflow.com/a/11640759/2525536.
Но я не могу найти обходной путь для этого.
Любая идея?

+2

Я не могу не задаться вопросом, является ли препроцессор C правильным инструментом для этой работы, каков бы он ни был. Это похоже на [XY Problem] (http://mywiki.wooledge.org/XyProblem). –

+0

Конечно, это часть другой проблемы. Я хочу применить макрос к последовательности, если последовательность содержит определенный элемент/токен, в конце концов. Самый короткий путь, по моему мнению, состоял в том, чтобы подсчитать случаи этого токена и проверить результат на 0. – user2525536

+0

Я должен согласиться с Джонатаном Леффлером здесь. Обходным решением будет внедрение грамотного генератора кода, поскольку препроцессор C не является. Даже если вам удастся реализовать это, код будет неуправляемым, и SO на самом деле не является местом для вопросов и ответов об искусстве обфускации кода. –

ответ

1

Как я уже получил некоторые комментарии об использовании препроцессора C для этой задачи, не является подходящим способом решения этой проблемы. Я по-прежнему намерен идти этим путем не только для минимизации зависимостей кода, но и для того, чтобы сделать все возможное из того, что C предлагает в моем коде (что может быть не очевидно здесь).
После этого я выяснил, почему макрос не расширялся правильно, а также способ заставить препроцессор выполнить эту задачу правильно.
Проблема заключалась в том, что макрос создавал конструкцию вроде INC(EXPAND(INC(0))) извне (первый элемент) внутрь (последний элемент). Препроцессор попытался решить эту проблему, всегда зная все значения до текущего уровня/элемента. Это превратилось во что-то вроде INC(COUNT_IF1(...)), в котором точка препроцессора начала заменять, например, INC.
Правильный способ обращения с этим состоял в том, чтобы препроцессор расширил макрос изнутри наружу.
упростив код немного дает это решение:

#define CAT(x, ...) CAT1(x, __VA_ARGS__) 
#define CAT1(x, ...) CAT2(x, __VA_ARGS__) 
#define CAT2(x, ...) x ## __VA_ARGS__ 

#define EXPAND(...) __VA_ARGS__ 
#define EAT(...) 
#define DEFER(...) __VA_ARGS__ EAT() 
#define OBSTRUCT(...) __VA_ARGS__ DEFER(EAT)() 

#define SIZE(seq) CAT(SIZE_, SIZE_0 seq) 
#define SIZE_0(...) SIZE_1 
#define SIZE_1(...) SIZE_2 
#define SIZE_2(...) SIZE_3 
#define SIZE_3(...) SIZE_4 
#define SIZE_SIZE_0 0 
#define SIZE_SIZE_1 1 
#define SIZE_SIZE_2 2 
#define SIZE_SIZE_3 3 
#define SIZE_SIZE_4 4 

#define GET_FIRST(x) GET_FIRST2(GET_FIRST1 x) 
#define GET_FIRST1(x) x, EAT() 
#define GET_FIRST2(x) GET_FIRST3(x) 
#define GET_FIRST3(x, ...) x 

#define POP_FIRST(x) EAT x 

#define EVAL(...) EVAL1(EVAL1(__VA_ARGS__)) 
#define EVAL1(...) EVAL2(EVAL2(__VA_ARGS__)) 
#define EVAL2(...) EVAL3(EVAL3(__VA_ARGS__)) 
#define EVAL3(...) EVAL4(EVAL4(__VA_ARGS__)) 
#define EVAL4(...) __VA_ARGS__ 

#define CHECK_N(x, n, ...) n 

#define CHECK(...) CHECK_N(__VA_ARGS__, 0,) 
#define CHECK_PROBE(x) x, 1, 

#define NOT(x) CHECK(CAT(NOT_, x)) 
#define NOT_0 ~, 1, 

#define COMPL(b) CAT(COMPL_, b) 
#define COMPL_0 1 
#define COMPL_1 0 

#define BOOL(x) COMPL(NOT(x)) 

#define IIF(c) CAT(IIF_, c) 
#define IIF_0(t, ...) __VA_ARGS__ 
#define IIF_1(t, ...) t 

#define IF(c) IIF(BOOL(c)) 

#define WHEN(c) IF(c)(EXPAND, EAT) 

#define INC(x) CAT(INC_, x) 
#define INC_0 1 
#define INC_1 2 
#define INC_2 3 
#define INC_3 4 
#define INC_4 5 

#define DEC(x) CAT(DEC_, x) 
#define DEC_0 0 
#define DEC_1 0 
#define DEC_2 1 
#define DEC_3 2 
#define DEC_4 3 
#define DEC_5 4 

#define IS_PAREN(x) CHECK(IS_PARENT1 x) 
#define IS_PARENT1(...) CHECK_PROBE(~) 

#define IS_COMPARABLE(x) IS_PAREN(CAT(COMPARE_, x)(())) 

#define BITAND(lhs, rhs) CAT(CAT(CAT(BITAND_, lhs), _), rhs) 
#define BITAND_0_0 0 
#define BITAND_0_1 0 
#define BITAND_1_0 0 
#define BITAND_1_1 1 

#define NOT_EQUAL(lhs, rhs) \ 
    IIF(BITAND(IS_COMPARABLE(lhs), IS_COMPARABLE(rhs)))(\ 
     NOT_EQUAL_HELPER, \ 
     1 NULL \ 
    )(lhs, rhs) 
#define NOT_EQUAL_HELPER(lhs, rhs) IS_PAREN(\ 
     CAT(COMPARE_, lhs)(CAT(COMPARE_, rhs))(()) \ 
    ) 

#define EQUAL(lhs, rhs) COMPL(NOT_EQUAL(lhs, rhs)) 

#define COUNT_IF(match, x) COUNT_IF1(SIZE(x), match, 0, x) 
#define COUNT_IF1(n, match, count, x) \ 
    IF(n) (\ 
     OBSTRUCT(COUNT_IF2)()(\ 
      DEC(n), \ 
      match, \ 
      IF(match(GET_FIRST(x)))(INC, EXPAND)(count), POP_FIRST(x) \ 
     ) /* call recursive */ \ 
     , count \ 
    ) 
#define COUNT_IF2() COUNT_IF1 

#define COMPARE_A(x) x 
#define EQUAL_A(x) EQUAL(x, A) 


EVAL(COUNT_IF(EQUAL_A, (A))) 
EVAL(COUNT_IF(EQUAL_A, (B))) 
EVAL(COUNT_IF(EQUAL_A, (A)(B))) 
EVAL(COUNT_IF(EQUAL_A, (B)(A))) 
EVAL(COUNT_IF(EQUAL_A, (A)(A))) 
EVAL(COUNT_IF(EQUAL_A, (B)(B))) 
EVAL(COUNT_IF(EQUAL_A, (A)(B)(A)(A))) 

P.S .: 11 строки макроса все еще кажется, достаточно просто мне справиться с точки зрения сложности и ремонтопригодности.

Смежные вопросы