如何使用 XMACRO 将宏参数值转换为字符串,而不是转换传递的参数本身

How to convert macro argument value to string using XMACROs instead of converting the passed argument itself

提问人:Partha 提问时间:6/2/2021 最后编辑:Partha 更新时间:6/2/2021 访问量:149

问:

在我的社区内做了足够的研究后,我在这里发布了这个问题。但是,我还找不到适合我的问题的解决方案。所以,我在这里发布我的问题。

#define EXPAND_AS_ENUMERATION(a) CODE_##a,
#define EXPAND_AS_ARRAY(a) a,

#define CODE_TABLE(EXPAND)\
    EXPAND(0x00E30054uL)\
    EXPAND(0x00ED3581uL)\
    EXPAND(0x00ED3983uL)\
    EXPAND(0x00EE0368uL)\
    EXPAND(0x00EE0368uL)\
    EXPAND(0x00D01087uL)\
    EXPAND(0x00ED4181uL)\
    EXPAND(0x00505602uL)\


// Actual Event IDs which need to be selected at run time
#define SWC_FAULT_CODE_0x00E30054_EVENT 113
#define SWC_FAULT_CODE_0x00ED3581_EVENT 213
#define SWC_FAULT_CODE_0x00ED3983_EVENT 432
#define SWC_FAULT_CODE_0x00EE0368_EVENT 411
#define SWC_FAULT_CODE_0x00EE0368_EVENT 311
#define SWC_FAULT_CODE_0x00D01087_EVENT 231
#define SWC_FAULT_CODE_0x00ED4181_EVENT 471
#define SWC_FAULT_CODE_0x00505602_EVENT 419

#define prefix_str SWC_FAULT_CODE
#define postfix_str _EVENT

#define Get_EventID_FROM_CODE(code)         ?????   // ex: when code= 0x00ED3581, then it should return SWC_FAULT_CODE_0x00ED3581_EVENT macro or it's value 213
#define Get_EventID_FROM_ENUM(code_enum)    ?????   // ex: when code_enum= CODE_0x00ED3581, then it should return SWC_FAULT_CODE_0x00ED3581_EVENT macro or it's value 213
#define Get_CodeEnumName(code)              ?????   // ex: when code = 0x00ED3581, then it should return DTC_0x00ED3581 which is enum element

typedef enum
{
    CODE_TABLE(EXPAND_AS_ENUMERATION)
    CODE_COUNT
}codeList_t ;


void Cycle_1ms(const uint32 code)
{
    
    // Based on code value, I want to get the macro value of SWC_FAULT_CODE_0xZZZZZZZZ_EVENT. 
    // ex: when code = 0x00ED3581, then eventID = SWC_FAULT_CODE_0x00ED3581_EVENT = 213
    
    uint16 eventID_FromCode = Get_EventID(code);
    
}

void Cycle_1ms_anotherFunc(codeList_t  code_enum)
{
// Based on code value, I want to get the macro value of SWC_FAULT_CODE_0xZZZZZZZZ_EVENT. 
    // ex: when code = 0x00ED3581, then eventID = SWC_FAULT_CODE_0x00ED3581_EVENT = 213
    uint16 eventID_FromCodeEnum = Get_EventID_FROM_ENUM(code_enum);
}

void callback_func(uint32 code, uint8* buf)
{
    
    // here, based on code value Get_CodeEnumName() should provide the enum element for that code. ex: when code= 0x00ED3581, enum_var = DTC_0x00ED3581
    codeList_t enum_var = Get_CodeEnumName(code);
}

在这里,CODE_TABLE将因项目而异,并且此表中的元素数量将发生变化。在阅读了几篇关于 Xmacros 的文章和主题后,我在这里使用 Xmacros 概念来避免循环和大量使用 RAM 内存

C for 循环 自动化 X-Macros

评论

3赞 M Oehm 6/2/2021
在函数中,是一个变量。预处理器无法知道其值。你也许可以把你的宏变成一个查找表,或者变成一个函数,其主体是你的 X 宏的另一个扩展,例如 .codeGet_case X: return SWC_FAULT_CODE_##X##_EVENT;

答:

0赞 Lundin 6/2/2021 #1

不能将 X 宏与运行时输入一起使用,它们在编译时会展开。因此,如果您需要根据运行时值进行表查找,它们不是解决方案。

但是,您可以使用 X 宏生成查找表,然后使用幻数编译时常量扩展为枚举,而枚举又对应于查找表索引:

#define GET_EVENT_ID(code) LOOKUP[CODE_##code]

GET_EVENT_ID扩展为类似数组访问的内容。LOOKUP[CODE_0x00ED3581]

此外,“事件”可以与同一 X 宏列表中的代码捆绑在一起,即使您不需要在每个宏中使用它们。另请注意,您的 X 宏列表中有重复的项目,这些项目将不起作用,因为您计划使用它来生成唯一的枚举常量。我们可以这样改变它:

#define CODE_TABLE(X)      \
/*  code          event */ \
  X(0x00E30054uL, 113)     \
  X(0x00ED3581uL, 213)     \
  X(0x00ED3983uL, 432)     \
  X(0x00EE0368uL, 411)     \
  X(0x00D01087uL, 231)     \
  X(0x00ED4181uL, 471)     \
  X(0x00505602uL, 419)     \

然后创建SWC_FAULTS作为枚举常量而不是 #defines:

// create SWC_FAULT_CODE_0x00ED3581_EVENT constants:
#define SWC_FAULT(code,event) SWC_FAULT_CODE_##code##_EVENT = event,
enum 
{
  CODE_TABLE(SWC_FAULT)
};

然后是用于查找表索引的枚举:

// create enums like CODE__0x00ED3581:
#define EXPAND_AS_ENUMERATION(code, event) CODE_##code,
typedef enum
{
  CODE_TABLE(EXPAND_AS_ENUMERATION)
  CODE_COUNT
}code_list_t ;

一个完整的例子:

#include <inttypes.h>
#include <stdio.h>

#define CODE_TABLE(X)      \
/*  code          event */ \
  X(0x00E30054uL, 113)     \
  X(0x00ED3581uL, 213)     \
  X(0x00ED3983uL, 432)     \
  X(0x00EE0368uL, 411)     \
  X(0x00D01087uL, 231)     \
  X(0x00ED4181uL, 471)     \
  X(0x00505602uL, 419)     \


// create SWC_FAULT_CODE_0x00ED3581_EVENT constants:
#define SWC_FAULT(code,event) SWC_FAULT_CODE_##code##_EVENT = event,
enum 
{
  CODE_TABLE(SWC_FAULT)
};

// create enums like CODE__0x00ED3581:
#define EXPAND_AS_ENUMERATION(code, event) CODE_##code,
typedef enum
{
  CODE_TABLE(EXPAND_AS_ENUMERATION)
  CODE_COUNT
}code_list_t ;

// create look-up table containing SWC_FAULT_CODE_0x00ED3581_EVENT
// use designated initializers to get [CODE__0x00ED3581] = SWC_FAULT_CODE_0x00ED3581_EVENT
#define LOOKUP_TABLE(code,event) [CODE_##code] = SWC_FAULT_CODE_##code##_EVENT,
static const uint32_t LOOKUP[CODE_COUNT] = 
{
  CODE_TABLE(LOOKUP_TABLE)
};
_Static_assert(sizeof LOOKUP/sizeof *LOOKUP == CODE_COUNT, "LOOKUP table corrupt");


#define GET_EVENT_ID(code) LOOKUP[CODE_##code]

int main()
{
  printf("0x%.8" PRIX32 " = %"PRIu32 "\n", 0x00ED3581uL, GET_EVENT_ID(0x00ED3581uL));

  return 0;
}

输出:。0x00ED3581 = 213

在 gcc x86 上,整个表可以得到优化,只剩下这个:

    mov     edx, 213
    mov     esi, 15545729
    xor     eax, eax
    mov     edi, OFFSET FLAT:.LC0

评论

0赞 tstanisl 6/2/2021
但它仅在是文字而不是变量时才有效code
0赞 Lundin 6/2/2021
@tstanisl 是的,这个答案的前两句话确实是这么说的。
0赞 tstanisl 6/2/2021
但是,即使对输入进行轻微的修改,它也会失败,例如:vs vs vs 或GET_EVENT_ID(0x00ED3581uL)GET_EVENT_ID(0x00ED3581u)GET_EVENT_ID(0x00ED3581ul)GET_EVENT_ID(0x00ED3581)GET_EVENT_ID(0xED3581)
0赞 tstanisl 6/2/2021
我实际上会更简单,因为它会扩展到真正的常量,而不是查找表中的一些条目#define GET_EVENT_ID(code) SWC_FAULT_CODE_##code##_EVENT
0赞 Lundin 6/2/2021
@tstanisl 是的,所以如果你希望调用方使用各种版本,可以删除 ul 并从宏内部调用。有各种各样的改进可以做,我只是演示了粗略的概念。UINT32_C(0x00ED3581)
0赞 tstanisl 6/2/2021 #2

您可以扩展为非常长的三元运算符链 ()。XMacro 可以在另一个宏中扩展,从而实现此功能。Get_EventID_FROM_CODE?:

这样可以避免使用函数。此外,该解决方案还接受任何格式的整数文本和变量。如果可能的话,它会扩展到一个真正的 C 常数。唯一的缺点是对参数表达式的计算,但这是宏的常见问题。

#include <inttypes.h>
#include <stdio.h>

#define CODE_TABLE(X, ...)              \
  X(0x00E30054uL, 113, __VA_ARGS__)     \
  X(0x00ED3581uL, 213, __VA_ARGS__)     \
  X(0x00ED3983uL, 432, __VA_ARGS__)     \
  X(0x00EE0368uL, 411, __VA_ARGS__)     \
  X(0x00D01087uL, 231, __VA_ARGS__)     \
  X(0x00ED4181uL, 471, __VA_ARGS__)     \
  X(0x00505602uL, 419, __VA_ARGS__)     \

// expands to -1 for invalid code
#define GET_EVENT_ID_(CODE,ENUM,VAR) (VAR) == (CODE) ? ENUM :
#define GET_EVENT_ID(code_var) (CODE_TABLE(GET_EVENT_ID_, code_var) -1)

int main()
{
#define DBG(CODE) \
  printf("%s 0x%08lx = %d\n", #CODE, (unsigned long)(CODE), GET_EVENT_ID(CODE))

  DBG(0x00ED3983uL);
  DBG(0x00ED3983);
  DBG(0xED3983);
  int32_t x = 0x00D01087uL;
  DBG(x);

  // expand to a true constant if possible
  struct X { char x[GET_EVENT_ID(0xED3983)]; };
  printf("%zi\n", sizeof(struct X));

  return 0;
}

生成以下预期输出:

0x00ED3983uL 0x00ed3983 = 432
0x00ED3983 0x00ed3983 = 432
0xED3983 0x00ed3983 = 432
x 0x00d01087 = 231
432