我试了一个星期但没有成功。
我正在两个处理器之间创建一个记录器接口,我需要帮助定义自动MACROS。
我的意思是什么?
假设我有一个定义为LOGGER_MSG_ID_2
的记录器消息,它带有两个uint8和uint16类型的参数。
我将枚举定义为:
typedef enum{
PARAM_NONE,
PARAM_SIZE_UINT8,
PARAM_SIZE_UINT16,
PARAM_SIZE_UINT32
}paramSize_e;
因此LOGGER_MSG_ID_2
将位图定义为:
#define LOGGER_MSG_ID_2_BITMAP (PARAM_SIZE_UINT16 << 2 | PARAM_SIZE_UINT8)
此位图的大小为1字节,因此最大参数数为4。 后来我有一个列表,根据消息ID定义所有参数类型:
#define ID_2_P0_TYPE uint8 // first parameter
#define ID_2_P1_TYPE uint16 // 2nd parameter
#define ID_2_P2_TYPE 0 // 3rd parameter
#define ID_2_P3_TYPE 0 // 4th parameter
正如我所说,我有4个参数的限制,所以我想定义它们并让MACRO决定使用它们的天气。我将它们定义为0,但它可以是任何有效的。
我有其他MACROS使用位图来获取所有类型的属性,例如参数数量和消息大小。
现在这是棘手的部分。我想构建一个从类型创建位图的MACRO。原因是我不希望位图和参数定义之间存在冗余。 我的问题是我尝试的所有内容都无法编译。
最终我想要一个MACRO,如:
#define GET_ENUM_FROM_TYPE(_type)
根据类型给出了PARAM_SIZE_UINT8,PARAM_SIZE_UINT16或PARAM_SIZE_UINT32。
限制:我在windows(armcl.exe)和C99上使用arm编译器。我不能使用C11 Generic()
。
我尝试了以下内容:
#define GET_ENUM_FROM_TYPE(_type) \
(_type == uint8) ? PARAM_SIZE_UINT8 : \
((_type == uint16) ? PARAM_SIZE_UINT16 : \
((_type == uint32) ? PARAM_SIZE_UINT32 : PARAM_NONE))
最终我想使用它:
#define LOGGER_MSG_ID_2_BITMAP \
(GET_ENUM_FROM_TYPE(ID_2_P3_TYPE) << 6 | \
GET_ENUM_FROM_TYPE(ID_2_P2_TYPE) << 4 | \
GET_ENUM_FROM_TYPE(ID_2_P1_TYPE) << 2 | \
GET_ENUM_FROM_TYPE(ID_2_P0_TYPE))
但是当我使用它时,它不会编译。
我有一张位图表:
uint8 paramsSizeBitmap [] = {
LOGGER_MSG_ID_1_BITMAP, /* LOGGER_MSG_ID_1 */
LOGGER_MSG_ID_2_BITMAP, /* LOGGER_MSG_ID_2 */
LOGGER_MSG_ID_3_BITMAP, /* LOGGER_MSG_ID_3 */
LOGGER_MSG_ID_4_BITMAP, /* LOGGER_MSG_ID_4 */
LOGGER_MSG_ID_5_BITMAP, /* LOGGER_MSG_ID_5 */
LOGGER_MSG_ID_6_BITMAP, /* LOGGER_MSG_ID_6 */
LOGGER_MSG_ID_7_BITMAP, /* LOGGER_MSG_ID_7 */
LOGGER_MSG_ID_8_BITMAP, /* LOGGER_MSG_ID_8 */
LOGGER_MSG_ID_9_BITMAP, /* LOGGER_MSG_ID_9 */
LOGGER_MSG_ID_10_BITMAP, /* LOGGER_MSG_ID_10 */
};
我收到了这个错误:
line 39: error #18: expected a ")"
line 39: error #29: expected an expression
(第39行是LOGGER_MSG_ID_2_BITMAP
)
我哪里出错了?
-----编辑-----
目前我有一个我不太喜欢的解决方法。
我没有使用uint64所以我使用了sizeof()
MACRO,现在我的MACRO看起来像这样:
#define GET_ENUM_FROM_TYPE(_type) \
(sizeof(_type) == sizeof(uint8)) ? PARAM_SIZE_UINT8 : \
((sizeof(_type) == sizeof(uint16)) ? PARAM_SIZE_UINT16 : \
((sizeof(_type) == sizeof(uint32)) ? PARAM_SIZE_UINT32 : PARAM_NONE))
我的参赛者名单是:
#define NO_PARAM uint64
#define ID_2_P0_TYPE uint8
#define ID_2_P1_TYPE uint16
#define ID_2_P2_TYPE NO_PARAM
#define ID_2_P3_TYPE NO_PARAM
它运作正常,但......你知道......
答案 0 :(得分:3)
我认为解决方案是使用连接运算符##
,并且帮助程序定义。
// These must match your enum
#define HELPER_0 PARAM_NONE
#define HELPER_uint8 PARAM_SIZE_UINT8
#define HELPER_uint16 PARAM_SIZE_UINT16
#define HELPER_uint32 PARAM_SIZE_UINT32
// Secondary macro to avoid expansion to HELPER__type
#define CONCAT(a, b) a ## b
// Outer parenthesis not strictly necessary here
#define GET_ENUM_FROM_TYPE(_type) (CONCAT(HELPER_, _type))
预处理后,GET_ENUM_FROM_TYPE(ID_2_P1_TYPE)
会扩展为(PARAM_SIZE_UINT16)
。
请注意,HELPER_***
定义中的后缀必须与ID_*_P*_TYPE
宏的内容完全匹配。例如,HELPER_UINT8
无法工作(无效案例)。 (谢谢@cxw)
答案 1 :(得分:2)
基本问题是类型不支持==
,仅适用于值。给定
uint8 foo;
您可以说foo==42
但不能foo == uint8
。这是因为类型不是C中的第一类。
一个hack就是使用C预处理器字符串化运算符#
(gcc docs)。但是,这会将您的所有计算都移动到运行时,并且可能不适合嵌入式环境。例如:
#define GET_ENUM_FROM_TYPE(_type) ( \
(strcmp(#_type, "uint8")==0) ? PARAM_SIZE_UINT8 : \
((strcmp(#_type, "uint16")==0) ? PARAM_SIZE_UINT16 : \
((strcmp(#_type, "uint32")==0) ? PARAM_SIZE_UINT32 : PARAM_NONE)) \
)
根据该定义,
GET_ENUM_FROM_TYPE(uint8)
扩展为
( (strcmp("uint8", "uint8")==0) ? PARAM_SIZE_UINT8 : ((strcmp("uint8", "uint16")==0) ? PARAM_SIZE_UINT16 : ((strcmp("uint8", "uint32")==0) ? PARAM_SIZE_UINT32 : PARAM_NONE)) )
哪个应该做你想要的,尽管在运行时。
答案 2 :(得分:0)
对不起,这并没有直接回答这个问题。但是你应该重新考虑整个代码。
首先,_Generic
会优雅地解决这个问题。
解开这些宏组的脏替代方法是使用所谓的X macros,这对于诸如&#34;我不想在位图和参数定义&#34;。您可以使用X宏重写代码,并删除许多多余的定义和宏。最终的可读性是另一个故事。
然而,每当你发现自己深入到一些宏观元编程丛林中时,它几乎总是表明程序设计不佳。所有这些都闻起来像是一个人工解决方案,可以用更好的方式解决问题 - 它是"XY problem"。 (不要与X宏混淆:))。最好的解决方案最有可能是以更简单的方式重写。你的情况在任何方面听起来并不独特,似乎你只想生成一堆位掩码。
优秀的程序员总是试图让他们的代码更简单,而不是让它变得更复杂。
此外,由于C语言类型系统的原因,您可能会在代码中出现或多或少的严重错误。它可以概括为:
int
类型,已签名。你应该避免使用按位算术混合它们。完全避免枚举这样的程序。uint8_t
或uint16_t
等小整数类型会隐式类型提升为int
。这意味着C语言将大部分尝试获得正确的类型,并以int
替换所有内容。int
,这不是您想要的。