Почему этот макрокоманда не расширяется правильно?

#c

#c

Вопрос:

Я пытаюсь инициализировать массив следующим образом :

    static tha_field_info_t person_t_fields[] = {
            HA_FIELD_INFO(person_t, name, CHAR)
    };
  

Соответствующие структуры данных:

 typedef struct _tha_field_info_{
    char fname[128];
    DATA_TYPE_t dtype;
    unsigned int size;
    unsigned int offset;
} tha_field_info_t;

typedef struct _person{
    char name[30];
    unsigned int age;
} person_t;
  

Используемые макрокоманды

 #define HA_FIELD_OFFSET(st, name)       ((int)amp;((st *)0)->name)
#define HA_FIELD_SIZE(st, name)         sizeof (((st *)0)->name)

#define HA_FIELD_INFO (st, fname, dtype)   
{#fname, dtype, HA_FIELD_SIZE(st, fname), HA_FIELD_OFFSET(st, fname)}
  

Вижу ошибки компиляции с этим макросом.

 tha.h:35:28: error: ‘fname’ undeclared (first use in this function)
tha.h:35:35: error: ‘dtype’ undeclared (first use in this function)
tha.h:36:2: error: expected ‘}’ before ‘{’ token
{#fname, dtype, HA_FIELD_SIZE(st, fname), HA_FIELD_OFFSET(st, fname)}
tha.h:36:3: error: stray ‘#’ in program
{#fname, dtype, HA_FIELD_SIZE(st, fname), HA_FIELD_OFFSET(st, fname)}
  

Однако, если я жестко закодирую это, тогда он будет работать нормально.

{"name", CHAR, sizeof(((person_t *)0)->name), ((int)amp;((person_t *)0)->name)}

В принципе, я хочу сохранить массив с информацией о поле структуры person_t.

Комментарии:

1. @Rhymoid: О, вау, хорошо заметил. Я хотел бы проголосовать за это; пожалуйста, опубликуйте это в качестве ответа. Спасибо.

2. Вы указываете на пробел после закрывающей скобки i определения макроса. Ну, я попытался удалить пробел, но та же ошибка. Кроме того, я написал много макрокоманд и оставляю много места перед символом продолжения «». Пожалуйста, уточните.

3. Хорошо, понял… у вас орлиный взгляд. : o Спасибо.

4. Как я могу пометить комментарий как ответ?

5. Я опубликовал это как ответ.

Ответ №1:

В C существует два типа директив определения:

 #define OBJECT_LIKE_MACRO     followed by a "replacement list" of preprocessor tokens
#define FUNCTION_LIKE_MACRO(with, arguments) followed by a replacement list
  

Что отличает эти два типа макросов, так это токен, который следует за идентификатором после #define : если это lparen, это функциональный макрос, а в противном случае это объектный макрос. Что такое lparen? Проект N1570 указывает в приложении A раздел 3:

(6.10) lparen:
( символ, перед которым не стоит пробел

Насколько я знаю, это один из немногих случаев в C, где интервал имеет значение (помимо // комментариев, сращивания строк и директив предварительной обработки). И это отчасти имеет смысл. В конце концов, как бы препроцессор различал любой функциональный макрос и объектный макрос, у которого есть список замены, начинающийся с ( токена? Например, следующий макрос является объектно-подобным, а не функциональным макросом с неправильным синтаксисом:

 #define NULL (void*)0
  

Давайте теперь ответим на ваш вопрос. Проблема в этих двух строках:

 #define HA_FIELD_INFO (st, fname, dtype)   
{#fname, dtype, HA_FIELD_SIZE(st, fname), HA_FIELD_OFFSET(st, fname)}
  

Поскольку ( символу после HA_FIELD_INFO «предшествует пробел», это не функциональный макрос, который вы предполагали. Просто удалите это пространство:

 #define HA_FIELD_INFO(st, fname, dtype)   
{#fname, dtype, HA_FIELD_SIZE(st, fname), HA_FIELD_OFFSET(st, fname)}
  

Комментарии:

1. Другие примеры, где имеет значение интервал: - -i , i j , 0x2E 1 . Последнее является неожиданным и очень сложным испытанием даже для продвинутых программистов на C.

2. @chqrlie Справедливое замечание, но я в основном имел в виду, что значительный интервал является значительным после токенизации. Я согласен, что 0x2E 1 / 0x2E 1 раскрывает очень неприятный дефект в спецификации C11.

3. Дефект существует со времен первой версии стандарта. Мне это тоже нравится: check_pattern(date, "??/??/????");