使用ENUM作为位图,如何在C中验证

CCo*_*der 5 c embedded enums bitmap c-preprocessor

我正在为具有内存限制的嵌入式应用程序开发固件.我有一组需要在收到时处理的命令.每个命令都属于不同的"桶",每个"桶"都有一系列有效的命令号.我创建了两个ENUM,如下所示,以实现此目的.

enum
{
  BUCKET_1 = 0x100, // Range of 0x100 to 0x1FF
  BUCKET_2 = 0x200, // Range of 0x200 to 0x2FF
  BUCKET_3 = 0x300, // Range of 0x300 to 0x3FF
  ...
  ...
  BUCKET_N = 0xN00 // Range of 0xN00 to 0xNFF
} cmd_buckets;

enum 
{
  //BUCKET_1 commands
  CMD_BUCKET_1_START = BUCKET_1,
  BUCKET_1_CMD_1,
  BUCKET_1_CMD_2,
  BUCKET_1_CMD_3,
  BUCKET_1_CMD_4,
  //Add new commands above this line
  BUCKET_1_CMD_MAX,

  //BUCKET_2 commands
  CMD_BUCKET_2_START = BUCKET_2,
  BUCKET_2_CMD_1,
  BUCKET_2_CMD_2,
  BUCKET_2_CMD_3,
  //Add new commands above this line
  BUCKET_2_CMD_MAX,

  //BUCKET_3 commands
  ...
  ...
  ...

  //BUCKET_N commands
  CMD_BUCKET_N_START = BUCKET_N
  BUCKET_N_CMD_1,
  BUCKET_N_CMD_2,
  BUCKET_N_CMD_3,
  BUCKET_N_CMD_4,
  //Add new commands above this line
  BUCKET_N_CMD_MAX,
}cmd_codes
Run Code Online (Sandbox Code Playgroud)

当我的命令处理程序函数收到命令代码时,它需要在处理命令之前检查命令是否已启用.我计划为此使用位图.可以在运行时启用或禁用处理命令.我可以为每个组使用一个int(每组给出32个命令,我意识到0xN00到0xN20是有效的命令代码,并且该范围内的其他代码都被浪费了).即使命令代码被浪费,设计选择的好处是可以在控制台上查看原始数据时轻松告知组命令代码.

由于许多开发人员可以在'cmd_codes'枚举中添加命令(甚至可以根据需要在'cmd_buckets'枚举中添加新桶),我想确保每个桶中的命令代码数不超过32(位图是INT).我想在编译时而不是运行时捕获它.除了检查下面的每个BUCKET_N_CMD_MAX值并抛出编译时错误之外,还有更好的解决方案吗?

#if (BUCKET_1_CMD_MAX > 0x20)
#error ("Number of commands in BUCKET_1 exceeded 32")
#endif

#if (BUCKET_2_CMD_MAX > 0x20)
#error ("Number of commands in BUCKET_2 exceeded 32")
#endif

#if (BUCKET_3_CMD_MAX > 0x20)
#error ("Number of commands in BUCKET_3 exceeded 32")
#endif
...
...
...
#if (BUCKET_N_CMD_MAX > 0x20)
#error ("Number of commands in BUCKET_N exceeded 32")
#endif
Run Code Online (Sandbox Code Playgroud)

还请建议是否有更优雅的设计方法.

谢谢,我感谢您的时间和耐心.

Lun*_*din 3

首先修复代码中的bug。正如评论中提到的,您有一个常量BUCKET_1 = 0x100,然后可以分配它CMD_BUCKET_1_START = BUCKET_1。因此,尾随枚举将得到值 0x101、0x102、...,并且BUCKET_1_CMD_MAX将是 0x106。由于 0x106 始终大于 0x20,因此您的静态断言将始终触发。

修复这个问题,以便它实际上检查枚举中的项目总数,如下所示:

#define BUCKET_1_CMD_N (BUCKET_1_CMD_MAX - CMD_BUCKET_1_START)
#define BUCKET_2_CMD_N (BUCKET_2_CMD_MAX - CMD_BUCKET_2_START)
...
Run Code Online (Sandbox Code Playgroud)

假设上述问题已修复,那么您可以用单个宏替换大量检查。虽然不是很大的改进,但至少减少了代码重复:

#define BUCKET_MAX 32 // use a defined constant instead of a magic number

// some helper macros:    
#define CHECK(n) BUCKET_ ## n ## _CMD_N
#define STRINGIFY(n) #n

// the actual macro:
#define BUCKET_CHECK(n) \
  _Static_assert(CHECK(n) <= BUCKET_MAX, \
                 "Number of commands in BUCKET_" STRINGIFY(n) "_CMD_N exceeds BUCKET_MAX.");


// usage:
int main (void)
{
  BUCKET_CHECK(1);
  BUCKET_CHECK(2);
}
Run Code Online (Sandbox Code Playgroud)

如果一个常量太大,gcc 的输出:

error: static assertion failed: "Number of commands in BUCKET_1_CMD_N exceeds BUCKET_MAX."
note: in expansion of macro 'BUCKET_CHECK'
Run Code Online (Sandbox Code Playgroud)

编辑

如果将错误修复与检查宏结合起来,您将得到以下结果:

#define BUCKET_MAX 32

#define CHECK(n) (BUCKET_##n##_CMD_MAX - CMD_BUCKET_##n##_START)
#define STRINGIFY(n) #n
#define BUCKET_CHECK(n) \
  _Static_assert(CHECK(n) <= BUCKET_MAX, \
                 "Number of commands in BUCKET " STRINGIFY(n) " exceeds BUCKET_MAX.");

int main (void)
{
  BUCKET_CHECK(1);
  BUCKET_CHECK(2);
}
Run Code Online (Sandbox Code Playgroud)