Как преобразует C препроцессор go из макроса в определение структуры - PullRequest
0 голосов
/ 26 марта 2020

Когда я искал способ сделать отражение в C, я нашел этот ответ { ссылка }.

В своем ответе он ссылается на метаре c библиотека, и он показывает пример, как его использовать:

TYPEDEF_STRUCT (point_t,
                double x,
                double y
                );

int main (int argc, char * argv[])
{
  point_t point = {
    .x = M_PI,
    .y = M_E,
  };

  ...
}

TYPEDEF_STRUCT определено в строке 237 https://github.com/alexanderchuranov/Metaresc/blob/master/src/metaresc.h

Я попытался извлечь источник макроса но я не уверен, что что-то пропустил, потому что это так сложно.

#define TYPEDEF_STRUCT(...) P00_TYPEDEF (STRUCT, __VA_ARGS__)

#ifndef MR_MODE
#define MR_MODE_UNDEFINED
#define MR_MODE PROTO
#endif
#include <mr_protos.h>
#ifdef MR_MODE_UNDEFINED
#undef MR_MODE_UNDEFINED
#undef MR_MODE
#endif

#define MR_IS_MR_MODE_EQ_MR_MODE 0
#define P00_TYPEDEF(...)                        \
  MR_IF_ELSE (MR_PASTE2 (MR_IS_MR_MODE_EQ_, MR_MODE))           \
  (P00_TYPEDEF_MODE (MR_MODE, __VA_ARGS__))             \
  (P00_TYPEDEF_MODE (PROTO, __VA_ARGS__) P00_TYPEDEF_MODE (DESC, __VA_ARGS__))

#define MR_IGNORE(...)
#define MR_IDENT(...) __VA_ARGS__
#define MR_IF_ELSE_CASE_0(...) __VA_ARGS__ MR_IGNORE
#define MR_IF_ELSE_CASE_1(...) MR_IDENT
#define MR_IF_ELSE(...) MR_PASTE2 (MR_IF_ELSE_CASE_, MR_IS_EQ_0 (__VA_ARGS__))

#define MR_PASTE2(...) MR_PASTE2_ (__VA_ARGS__)
#define MR_PASTE2_(_0, _1) _0 ## _1

#define MR_IS_0_EQ_0 ,
#define MR_IS_EQ_0_CASE_011 ,

#define MR_GET_SECOND(_0, ...) __VA_ARGS__
#define MR_IS_EQ_0(...) MR_IS_EQ_0_ (__VA_ARGS__) /* evaluate arguments */
#define MR_IS_EQ_0_(...) MR_IS_EQ_0__ ((__VA_ARGS__), (MR_PASTE2 (MR_IS_0_EQ_, __VA_ARGS__)))
#define MR_IS_EQ_0__(ARGS, ARGS_EQ_0)                   \
  MR_HAS_COMMA (MR_PASTE4 (MR_IS_EQ_0_CASE_,                \
               /* test if there is just one argument, eventually a zero */ \
               MR_HAS_COMMA ARGS,               \
               /* test if MR_IS_0_EQ_ together with the argument adds a comma */ \
               MR_HAS_COMMA ARGS_EQ_0,          \
               /* test that there is nothing after comma */ \
               MR_IS_EMPTY (MR_GET_SECOND ARGS_EQ_0)))

#define P00_TYPEDEF_MODE(P00_MODE, P00_TYPE, ...)           \
  P00_TYPEDEF_MODE_ (P00_MODE, P00_TYPE,                \
             ATTRIBUTES (P00_GET_ATTRIBUTES (__VA_ARGS__)), \
             P00_GET_NON_ATTRIBUTES (__VA_ARGS__))
#define P00_TYPEDEF_MODE_(...) P00_TYPEDEF_MODE__ (__VA_ARGS__)
#define P00_TYPEDEF_MODE__(P00_MODE, P00_TYPE, ATTR_META_RES, ...)  \

#define P00_GET_ATTRIBUTES(...) MR_FOREACH (P00_EXTRACT_ATTRIBUTES, __VA_ARGS__)
#define P00_GET_NON_ATTRIBUTES(...) MR_FOREACH (P00_EXTRACT_NON_ATTRIBUTES, __VA_ARGS__)

#define MR_FOREACH(X, ...) MR_PASTE2 (MR_FOREACH, MR_NARG (__VA_ARGS__)) (X, __VA_ARGS__)
#define MR_FOR(NAME, N, OP, FUNC, ...) MR_PASTE2 (MR_FOR, N) (NAME, OP, FUNC, __VA_ARGS__)

#define P00_TYPEDEF_ATTR_STRUCT TYPEDEF_ATTR
#define P00_TYPEDEF_ATTR_UNION TYPEDEF_ATTR
#define P00_TYPEDEF_ATTR_ENUM TYPEDEF_ATTR
#define P00_TYPEDEF_ATTR_CHAR_ARRAY(P00_MODE, P00_TYPE, ATTR_META_RES, P00_TYPE_NAME, SIZE, ...) MR_PASTE2 (MR_TYPEDEF_CHAR_ARRAY_, P00_MODE) (P00_TYPE_NAME, SIZE, MR_PASTE2 (P00_REMOVE_, ATTR_META_RES), __VA_ARGS__)
#define P00_TYPEDEF_ATTR_FUNC(P00_MODE, P00_TYPE, ATTR_META_RES, RET_TYPE, P00_TYPE_NAME, ARGS, ...) MR_PASTE2 (MR_TYPEDEF_FUNC_, P00_MODE) (RET_TYPE, P00_TYPE_NAME, ARGS, MR_PASTE2 (P00_REMOVE_, ATTR_META_RES), __VA_ARGS__)

#define P00_UNFOLD(PREFIX, P00_TYPE, P00_MODE, ...) MR_PASTE4 (PREFIX, P00_TYPE, _, P00_MODE) (__VA_ARGS__)

#define TYPEDEF_ATTR(P00_MODE, P00_TYPE, ATTR_META_RES, P00_TYPE_NAME, ...) \
  P00_UNFOLD (MR_TYPEDEF_, P00_TYPE, P00_MODE, P00_TYPE_NAME, MR_PASTE2 (P00_GET_FIRST_, ATTR_META_RES)) \
  MR_FOR ((P00_MODE, P00_TYPE_NAME), MR_NARG (__VA_ARGS__), MR_SER, MR_PASTE3 (P00_, P00_TYPE, _HANDLER), __VA_ARGS__) \
  P00_UNFOLD (MR_END_, P00_TYPE, P00_MODE, P00_TYPE_NAME, MR_PASTE2 (P00_GET_OTHER_, ATTR_META_RES))

# define P00_IS_ATTRIBUTES_EQ_ATTRIBUTES(...) 0 /* help macro for ATTRIBUTES test IF clause */
#define P00_REMOVE_ATTRIBUTES(...) __VA_ARGS__
#define P00_GET_FIRST_ATTRIBUTES(FIRST, ...) FIRST /* extract typedef attributes */
#define P00_GET_OTHER_ATTRIBUTES(FIRST, ...) __VA_ARGS__ /* extract typedef meta information */

Все, что я хочу знать, - это как расширить вызов макроса, такой как

TYPEDEF_STRUCT (point_t,
                    double x,
                    double y
                );

, до этого

typedef struct point_t {
          double x;
          double y;
} point_t;

1 Ответ

0 голосов
/ 31 марта 2020

Я не уверен, почему вы хотите отделить заголовки от остальной части библиотеки. Вероятно, первые 700 строк метарес c .h используются в TYPEDEF_STRUCT, поэтому вам понадобится большая часть этого файла. Хотя это вам не поможет, просто потому, что результатом макроса является typedef + метаданные, необходимые для сериализации. Функции сериализации реализованы в библиотеке, поэтому нет смысла отделять генерацию метаданных от кода, который использует эти метаданные.

Вы можете запустить пример через препроцессор и оценить результат. Это даст вам представление о том, что я имею в виду под метаданными.

Если вы действительно заинтересованы в понимании деталей реализации макроса TYPEDEF_STRUCT, то подготовьтесь к более чем 100 слоям вложенных макросов. В качестве первого упражнения вы можете начать здесь https://gustedt.wordpress.com/2010/06/08/detect-empty-macro-arguments/ и продолжить другие трюки с макросами из https://p99.gforge.inria.fr/

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...