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

CCoder

我正在为具有内存限制的嵌入式应用程序开发固件。我有一组需要在接收时处理的命令。每个命令都属于不同的“存储桶”,并且每个“存储桶”都有一定范围的有效命令编号。我创建了两个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

当我的命令处理程序函数接收到命令代码时,它需要在处理命令之前检查该命令是否已启用。我打算为此使用位图。可以在运行时启用或禁用命令处理。我可以为每个组使用一个整数(每组给我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

还请提出是否有一种更优雅的设计方法。

谢谢,感谢您的宝贵时间和耐心等待。

伦丁

首先修复代码中的错误。如注释中所述,您有一个常量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)
...

假设以上内容已解决,则可以用单个宏替换大量检查。这不是一个很大的改进,但至少可以减少代码重复:

#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);
}

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

error: static assertion failed: "Number of commands in BUCKET_1_CMD_N exceeds BUCKET_MAX."
note: in expansion of macro 'BUCKET_CHECK'

编辑

如果将错误修复与check宏结合使用,则会得到以下结果:

#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);
}

本文收集自互联网,转载请注明来源。

如有侵权,请联系 [email protected] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章