Как создать литерал массива структурных литералов в C? - PullRequest
0 голосов
/ 09 января 2019

Моя цель - автоматически создать управляющую логику системы, основанной на знаниях старой школы, которая, по сути, представляет собой серию из сотен схожим образом структурированных операторов if-then. Мы также хотим знать свойства этих операторов в своего рода отражении, которое помогают генерировать литералы массива.

Я рассмотрел многие ответы здесь на SO, но большинство из них касаются массивов строковых литералов, а это не то, чего я пытаюсь достичь.

Я пытаюсь создать X-макрос, который содержит в качестве одного из своих параметров массив структурных литералов, которые можно использовать для инициализации переменной массива, размещенной в стеке.

Вот код, который не компилируется и показывает мою попытку:

#include <stdio.h>

typedef enum qty_t {
    qty_None, qty_Some, qty_Many    
} qty_t;

typedef enum arg_t {
    arg_X, arg_Y, arg_Z
} arg_t;

typedef struct action_t {
    int id;
    qty_t quantity;
    arg_t argument;
} action_t;

/* a change is a sequence of actions, and a change identifier */
/* the outermost {} are the array, the inner ones being the structs */
#define CHANGES \
CHANGE(1, ({{.id=1,.quantity=qty_None,.argument=arg_X}, \
            {.id=2,.quantity=qty_Many,.argument=arg_Z}\
            }\
        )\
    )\
CHANGE(2, ({{1,qty_Some,arg_Y}}))

int main(void) {
    // your code goes here
#define CHANGE(change_id, actions) \
    action_t current_actions ## change_id[] = actions;

    CHANGES

#undef CHANGE
    return 0;
}

Как поместить литерал массива struct literal в макрос CHANGE, в частности, тот, который можно использовать для инициализации переменной current_actions1?

Ответы [ 2 ]

0 голосов
/ 09 января 2019

Не существует такого понятия, как «массив структурных литералов», независимо от того, является ли сам литерал или нет. Элементы массива могут быть структурами, и вы можете использовать структурные литералы для инициализации элементов массива структур (yuck), в том числе в литерале массива, но сами элементы массива не являются литералами. Это может звучать - действительно, может быть - педантичным, но использование непротиворечивой и правильной терминологии улучшает ясность мышления и коммуникации.

На самом деле вы вообще не используете структурные литералы или литералы массивов. Вы просто используете struct initializer внутри инициализатора для массива структур. Это уже немного упрощает проблему.

Первая проблема с вашим кодом - это круглые скобки, которые вы поместили в определение макроса CHANGE, вокруг инициализаторов массива. Они являются частью второго аргумента для каждого вызова макроса CHANGE, и, как таковые, они выводятся как часть замещающего текста макроса CHANGES. Но они не являются частью разрешенного синтаксиса для инициализаторов, поэтому полученный код недействителен.

Я предполагаю, что круглые скобки стоят там во-первых, чтобы избежать запятых внутри инициализаторов, интерпретируемых как разделяющие аргументы макроса. Альтернативный способ решения этой проблемы, по крайней мере, в этом случае, состоит в том, чтобы задать определение макрокоманды CHANGE:

#define CHANGE(change_id, ...) \
    action_t current_actions ## change_id[] = __VA_ARGS__;

Это решает проблему для меня, используя стандартную форму макроса variadic, представленную в C99.

Еще одна, более структурированная альтернатива - добавить макросы для инициализаторов структуры, чтобы запятые внутри и между ними не влияли на идентификацию аргументов макроса. Например,

#define FIRST_ACTION(i,q,a) {.id=i,.quantity=q,.argument=a}
#define ACTION(i,q,a) ,FIRST_ACTION(i,q,a)

#define CHANGES \
CHANGE(1, {\
    FIRST_ACTION(1,qty_None,arg_X)\
    ACTION(2,qty_Many,arg_Z)\
})\
CHANGE(2, {\
    FIRST_ACTION(1,qty_Some,arg_Y)\
})

// ...

#define CHANGE(change_id, actions) \
    action_t current_actions ## change_id[] = actions;

    CHANGES

Конечно, все это предполагает наличие некоторого хорошего значения, которое можно получить из подхода X-макроса в вашем случае. Для этого требуется как минимум другое использование макроса CHANGES в другом месте вашего кода. Ничего такого не видно в том, что вы представляете, но это можно было бы опустить. Однако мне не сразу понятно, что ваш конкретный макрос CHANGES предоставляет дополнительные возможности, которые не будут лучше обслуживаться другим подходом.

0 голосов
/ 09 января 2019

Что насчёт этого:

#define CHANGES \
CHANGE(1, {{.id=1,.quantity=qty_None,.argument=arg_X}, \
            {.id=2,.quantity=qty_Many,.argument=arg_Z}\
            }\
           \
    )\
CHANGE(2, {{.id=1,.quantity=qty_Some,.argument=arg_Y}})

int main(void) {
    // your code goes here
#define CHANGE(change_id, actions ...) \
    action_t current_actions ## change_id[] = actions;

    CHANGES

#undef CHANGE
    return 0;
}

Я удалил скобки в ИЗМЕНЕНИЯХ и добавил ... в ИЗМЕНЕНИЯХ

Это компилируется и gcc -E foo.c дает:

   int main(void) {




        action_t current_actions1[] = {{.id=1,.quantity=qty_None,.argument=arg_X}, {.id=2,.quantity=qty_Many,.argument=arg_Z} }; action_t current_actions2[] = {{.id=1,.quantity=qty_Some,.argument=arg_Y}};


        return 0;
    }
...