C++ C/C++;宏从类型的定义中定义类型

C++ C/C++;宏从类型的定义中定义类型,c++,c,macros,C++,C,Macros,我想在另一个定义中展开一个定义,如下所示: #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 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
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);
}

这些只是其中的一小部分,但所有这些getter和put er的实现在不同的数据类型之间非常相似,如果我能让预处理器在#define中展开宏,那么这些函数可以由预处理器生成。

您的问题不完整,因为包含的宏正在工作:

#包括
#定义标量类型uint8、int8、uint16、int16、双精度、字符串
#定义向量类型向量,向量,向量,向量,向量,向量,向量
#定义变量类型名称标量类型、向量类型
#定义STR(X)字符串化(X)
#定义STRINGIFY(X)#X
int main()
{

std::cout您的问题不完整,因为包含的宏正在工作:

#包括
#定义标量类型uint8、int8、uint16、int16、双精度、字符串
#定义向量类型向量,向量,向量,向量,向量,向量,向量
#定义变量类型名称标量类型、向量类型
#定义STR(X)字符串化(X)
#定义STRINGIFY(X)#X
int main()
{

std::cout如果您不介意巴洛克式语法,您可以使用。创建标量和向量类型的两个单独列表:

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

#define VECTOR_TYPES(_)     \
    _(u32,      uint32_t)   \
    _(double,   double)     \
    _(cstr,     char *)
这两个宏是“gerenator MACROSS”。它们将另一个宏
\uuuu
作为参数。该宏必须具有两个参数,用于创建合适的函数名的
名称和描述标量类型或数组元素类型的
类型

要在示例中创建接口,请首先创建所需的宏:

#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\u typenames
将产生一个尾随逗号。在数组初始值设定项中,尾随逗号是允许的。在
enum
s中,您可以在最后一个枚举值之后定义一个“max”值

但在文章的结尾,也有一个宏观的解决方案


还可以在gererator宏中包含数据的“类”——标量或向量&ndash

#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 *)
它们定义了一组以
标量
或`向量`为前缀的宏,以便您可以通过标记粘贴来创建它们的名称:

#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
现在你的MARO看起来像:

#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(类型(逗号))
转换为
TAIL(,T1,T2,T3,…)


这个解决方案需要一些时间才能开始工作,特别是因为扩展宏的空格收缩,可读性不强,但是一旦有了系统,就可以轻松地添加新类型


使用宏的常见注意事项适用。我还想向您指出另一种可能的解决方案:编写脚本或程序,根据比X宏更好的定义为您生成接口,并将其包含在构建过程中。

如果您不介意巴洛克语法,您可以使用。创建两个单独的标量和向量类型列表:

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

#define VECTOR_TYPES(_)     \
    _(u32,      uint32_t)   \
    _(double,   double)     \
    _(cstr,     char *)
这两个宏是“gerenator MACROSS”。它们将另一个宏
\uuuu
作为参数。该宏必须具有两个参数,用于创建合适的函数名的
名称和描述标量类型或数组元素类型的
类型

要在示例中创建接口,请首先创建所需的宏:

#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\u typenames
将产生一个尾随逗号。在数组初始值设定项中,尾随逗号是允许的。在
enum
s中,您可以在最后一个枚举值之后定义一个“max”值

但在文章的结尾,也有一个宏观的解决方案


还可以在gererator宏中包含数据的“类”——标量或向量&ndash

#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 *)
它们定义了一组以
标量
或`向量`为前缀的宏,以便您可以通过标记粘贴来创建它们的名称:

#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
现在你的MARO看起来像:

#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(类型(逗号))
转换为
TAIL(,T1,T2,T3,…)


这个解决方案需要一些时间才能开始工作,特别是因为扩展宏的空格收缩,可读性不强,但是一旦有了系统,就可以轻松地添加新类型


使用宏的常见注意事项也适用。我还想向您指出另一种可能的解决方案:编写脚本或程序,根据比X宏更好的定义为您生成接口,并将其包含在构建过程中。

不要使用宏。这似乎是
std::variant
或类似的类型列表,所以可以使用简单的TyPulf:<代码>使用MyValue= STD::<代码> > <代码>定义EVAL(x)x??但是,您已经在“代码> vector < /CODE >中使用模板。如果您想要C++代码的C API(包在<代码>外)C“{< /代码>和<代码> } /代码>)然后你被限制在C中可用的类型。仅供参考:也许你应该编辑你的问题以显示宏应该如何使用。目前还不清楚——对我来说,