Некоторое время назад я написал набор X-макросов для грандиозный проект. Мне нужно было поддерживать согласованные списки как строк, так и перечисляемых ссылок / хеш-значений / функций обратного вызова и т. Д. Вот как выглядит обратный вызов функции
#define LREF_LOOKUP_TABLE_TEXT_SIZE 32
#define _LREF_ENUM_LIST(_prefix,_ref,...) _prefix ## _ ## _ref,
#define _LREF_BASE_STRUCT_ENTRY(_prefix,_ref) .text= #_ref "\0", .position= _LREF_ENUM_LIST(_prefix, _ref)
#define _LREF_FUNCTION_STRUCT_LIST(_prefix,_ref,...) {_LREF_BASE_STRUCT_ENTRY(_prefix,_ref) _prefix ## _ ## _ref ## _callback},
#define _LREF_ENUM_TYPEDEF(_prefix) \
typedef enum _prefix \
{ \
_ ## _prefix ## s(_prefix,_LREF_ENUM_LIST) \
_LREF_ENUM_LIST(_prefix,tblEnd) \
} e_ ## _prefix
#define _LREF_LOOKUP_TABLE_TYPEDEF(_prefix, _extras) \
typedef struct _prefix ## _lookup \
{ \
const char text[LREF_LOOKUP_TABLE_TEXT_SIZE]; \
e_ ## _prefix position; \
_extras \
} _prefix ##_lookup_t
#define LREF_GENERIC_LOOKUP_TABLE(_prefix, _type, _tabledef, _listdef, _extras) \
_LREF_ENUM_TYPEDEF(_prefix); \
_LREF_LOOKUP_TABLE_TYPEDEF(_prefix,_tabledef); \
_extras \
_LREF_LOOKUP_TABLE_DECLARATION(_prefix,_listdef, _type)
#define LREF_FUNCTION_LOOKUP_TABLE(_prefix, _type) \
_ ## _prefix ## s(_prefix, _LREF_FUNCTION_DEF ) \
LREF_GENERIC_LOOKUP_TABLE( _prefix, \
_type, \
void* (*function) (void*);, \
_LREF_FUNCTION_STRUCT_LIST, )
Он находится в файле заголовка и позволяет мне писать такие вещи, как:
#define _cl_tags(x,_) \
_(x, command_list) \
_(x, command) \
_(x, parameter) \
_(x, fixed_parameter) \
_(x, parameter_group) \
_(x, group) \
_(x, map) \
_(x, transform)
LREF_FUNCTION_LOOKUP_TABLE(cl_tag, static);
Это сокращает процедуры обработки. Например, загрузить файл конфигурации с указанными выше тегами просто:
for (node_tag = cl_tag_lookup_table; node_tag->position != cl_tag_tblEnd; node_tag++)
{
if (strcasecmp(test_string, node_tag->text) == 0)
{
func_return = node_tag->function((void*)m_parser);
}
}
У меня такой вопрос: мне не нравится, что мне приходится включать второй параметр в мой #define
. Я хочу иметь возможность писать #define _cl_tags (_)
вместо #define _cl_tags (x, _)
. Как видите, x
используется только для передачи префикса (cl_tag) вниз. Но это излишне, поскольку префикс является параметром начального макроса.
Решение этой проблемы было бы простым, если бы мой препроцессор сначала расширил бы самые внешние макросы. К сожалению, препроцессор GCC работает через самые внутренние макросы, то есть значения параметров, перед расширением самого внешнего макроса.
Я использую gcc 4.4.5
Пояснение По стандарту C89 (и C99) следующие определения
#define plus(x,y) add(y,x)
#define add(x,y) ((x)+(y))
с вызовом
plus(plus(a,b),c)
должны давать
плюс (plus (a, b), c)
add (c, plus (a, b))
((c) + (плюс (a, b))
((c) + (add (b, a))
((c) + (((b) + (a)) ))
gcc 4.4.5 дает
плюс (плюс (a, b), c)
плюс (добавить (b, a), c)
плюс (((b) + (a )), c)
add (c, ((b) + (a)))
((c) + ((((b) + (a))))