将枚举转换为c

时间:2017-05-15 11:28:35

标签: c types macros arm c-preprocessor

我试了一个星期但没有成功。

我正在两个处理器之间创建一个记录器接口,我需要帮助定义自动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

它运作正常,但......你知道......

3 个答案:

答案 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_tuint16_t等小整数类型会隐式类型提升为int。这意味着C语言将大部分尝试获得正确的类型,并以int替换所有内容。
  • 结果类型的宏将为int,这不是您想要的。