Макрос C / C ++ определяет тип из определений типов - PullRequest
0 голосов
/ 04 мая 2020

Я хотел бы расширить определение внутри другого определения, например, так:

#define SCALAR_TYPES uint8_t, int8_t, uint16_t, int16_t, double, string
#define VECTOR_TYPES vector<uint8_t>, vector<int8_t>, vector<uint16_t>, vector<int16_t>, vector<double>, vector<string>

#define VARIANT_TYPENAMES SCALAR_TYPES, VECTOR_TYPES

, чтобы VARIANT_TYPENAMES расширился до списка, связывающего объединение двух ранее определенных макросов. Использование флага -E показывает, что макросы не раскрываются внутри VARIANT_TYPENAMES, и мне нужно, чтобы они были определены таким образом, потому что VECTOR_TYPES будет сгенерирован из SCALAR_TYPES, используя что-то похожее на то, что описано здесь .

Is Есть ли способ заставить препроцессор расширять макросы внутри макросов?

РЕДАКТИРОВАТЬ

Чтобы немного разъяснить мое намерение. Я пытаюсь реализовать общий контейнер данных, который можно использовать из сгенерированного C кода алгоритмов Matlab. Взаимодействие с этим контейнером должно осуществляться с использованием только интерфейса C, поскольку сгенерированный код является только C, но в коде интерфейса я могу использовать код C ++.

Этот контейнер может содержать различные скалярные типы и векторы этих скаляров, и это можно сделать с помощью std :: variable.

. На стороне интерфейса C я должен предоставить функции для получения и помещения данных из / в контейнер примерно так:

extern "C"{
int getter_for_int(const char *key);
void put_for_intt(const char * key, int val);
void put_for_int_array(const char * key, int *val, uint32_t size);
}

Это лишь некоторые из них, но реализация всех этих методов получения и установки очень похожа от одного типа данных к другому, и эти функции могут генерироваться препроцессором, если я могу заставить препроцессор расширяться. макросы внутри #define.

Ответы [ 2 ]

1 голос
/ 04 мая 2020

Если вы не возражаете против синтаксиса в стиле барокко, вы можете достичь желаемого с помощью X Macro . Создайте два отдельных списка скалярных и векторных типов:

#define SCALAR_TYPES(_)     \
    _(u32,      uint32_t)   \
    _(double,   double)     \
    _(cstr,     char *)

#define VECTOR_TYPES(_)     \
    _(u32,      uint32_t)   \
    _(double,   double)     \
    _(cstr,     char *)

Эти два макроса являются макросами gerenator. Они принимают другой макрос _ в качестве параметра. Этот макрос должен принимать два аргумента: NAME для создания подходящих имен функций и TYPE, который описывает скалярный тип или тип элементов массива.

Чтобы создать интерфейс в примере, сначала создайте необходимые макросы:

#define SCALAR_GET(N, T) T get_##N(const char *key);
#define VECTOR_GET(N, T) T *get_##N##_array(const char *key);
#define SCALAR_PUT(N, T) void put_##N(const char * key, T val);
#define VECTOR_PUT(N, T) void put_##N##_array(const char * key, T *val, uint32_t size);

Затем передайте их двум генераторам макросов:

extern "C"{
    SCALAR_TYPES(SCALAR_GET)
    SCALAR_TYPES(SCALAR_PUT)
    VECTOR_TYPES(VECTOR_GET)
    VECTOR_TYPES(VECTOR_PUT)
}

Это приведет к:

extern "C" {
    uint32_t get_u32(const char *key);
    double get_double(const char *key);
    char *get_cstr(const char *key);
    void put_u32(const char *key, uint32_t val);
    void put_double(const char *key, double val);
    void put_cstr(const char *key, char *val);
    uint32_t *get_u32_array(const char *key);
    double *get_double_array(const char *key);
    char **get_cstr_array(const char *key);
    void put_u32_array(const char *key, uint32_t * val, uint32_t size);
    void put_double_array(const char *key, double *val, uint32_t size);
    void put_cstr_array(const char *key, char **val, uint32_t size);
}

Чтобы получить список для std::variant, используйте:

#define SCALAR_COMMA(N, T) T,
#define VECTOR_COMMA(N, T) T *,

#define VARIANT_TYPENAMES \
    SCALAR_TYPES(SCALAR_COMMA) VECTOR_TYPES(VECTOR_COMMA)

Однако есть загвоздка: VARIANT_TYPPENAMES приведет к запятой. В инициализаторах массива допускаются конечные запятые. В enum s вы можете определить «максимальное» значение после последнего перечислимого значения.

Но для этого есть и макрос-решение, показанное в конце поста.


Вы также можете включить «класс» данных - скаляр или вектор & nda sh в макрос gererator.

#define TYPES(_)                            \
    _(SCALAR,   u32,            uint32_t)   \
    _(SCALAR,   double,         double)     \
    _(SCALAR,   cstr,           char *)     \
    _(VECTOR,   u32_array,      uint32_t)   \
    _(VECTOR,   double_array,   double)     \
    _(VECTOR,   cstr_array,     char *)

Они определяют набор макросов, которые имеют либо SCALAR_, либо ´ VECTOR_` в качестве префикса, так что вы можете создавать их имена с вставкой токена:

#define SCALAR_TYPE(T) T
#define VECTOR_TYPE(T) T *

#define SCALAR_ARG(T) T val
#define VECTOR_ARG(T) T* val, uint32_t size

Теперь ваши маро выглядят так:

#define GET(C, N, T) C##_TYPE(T) get_##N(const char *key);
#define PUT(C, N, T) void put_##N(const char * key, C##_ARG(T));

extern "C"{
    TYPES(GET)
    TYPES(PUT)
}

#define COMMA(C, N, T) C##_TYPE(T),

#define VARIANT_TYPENAMES TYPES(COMMA)

Они дают тот же результат, что и выше.


Наконец, по поводу этой запятой в VARIANT_TYPENAMES: Вы можете избавиться от запятой, превратив запятую в каждом макросе в ведущую запятую, а затем отбросив запятую в голове.

#define COMMA(C, N, T) , C##_TYPE(T)

#define TAIL_(A, ...) __VA_ARGS__
#define TAIL(...) TAIL_(__VA_ARGS__)

#define VARIANT_TYPENAMES TAIL(TYPES(COMMA))

Это работает, потому что аргументы макроса могут быть пустыми, но для преобразования TAIL(TYPES(COMMA)) в TAIL_(, T1, T2, T3, ...).


требуется 10-шаговое расширение. Это решение требует некоторого времени, чтобы начать работать, особенно потому, что расширенные макросы имеют свои пробелы c и не очень читабельны, но когда у вас есть система, вы можете легко добавлять новые типы.

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

1 голос
/ 04 мая 2020

Ваш вопрос не завершен, так как включенные макросы только работают:

#include <iostream>

#define SCALAR_TYPES uint8_t, int8_t, uint16_t, int16_t, double, string
#define VECTOR_TYPES vector<uint8_t>, vector<int8_t>, vector<uint16_t>, vector<int16_t>, vector<double>, vector<string>

#define VARIANT_TYPENAMES SCALAR_TYPES, VECTOR_TYPES

#define STR(X) STRINGIFY(X)
#define STRINGIFY(X) #X

int main()
{
    std::cout << STR((SCALAR_TYPES)) << '\n';
    std::cout << STR((VECTOR_TYPES)) << '\n';
    std::cout << STR((VARIANT_TYPENAMES)) << '\n';
    return 0;
}

производит вывод:

(uint8_t, int8_t, uint16_t, int16_t, double, string)
(vector<uint8_t>, vector<int8_t>, vector<uint16_t>, vector<int16_t>, vector<double>, vector<string>)
(uint8_t, int8_t, uint16_t, int16_t, double, string, vector<uint8_t>, vector<int8_t>, vector<uint16_t>, vector<int16_t>, vector<double>, vector<string>)

https://wandbox.org/permlink/1JJE838O2CNOOpuT
Здесь вы можете найти объяснение использованных макросов .

Также, пожалуйста, избегайте использования макросов. Это должно быть последним средством, так как имеет много недостатков.

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