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

使用枚举作为位图,如何在C中验证,c,bitmap,enums,embedded,c-preprocessor,C,Bitmap,Enums,Embedded,C Preprocessor,我正在为内存受限的嵌入式应用程序开发固件。我有一组命令需要在接收时进行处理。每个命令位于不同的“bucket”下,每个“bucket”获得一系列有效的命令编号。为了实现这一点,我创建了两个枚举,如下所示 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”下,每个“bucket”获得一系列有效的命令编号。为了实现这一点,我创建了两个枚举,如下所示

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
当我的命令处理函数收到命令代码时,它需要在处理之前检查命令是否已启用。我计划使用位图来实现这一点。在运行时期间,可以启用或禁用处理命令。我可以为每个组使用一个int(每个组给我32个命令,我意识到0xN00到0xN20是有效的命令代码,并且该范围内的其他代码都被浪费了)。即使命令代码被浪费,设计选择的好处是在控制台上看到原始数据时,可以轻松地告诉命令代码组

由于许多开发人员可以向“cmd_代码”枚举添加命令(甚至可以根据需要向“cmd_bucket”枚举添加新的bucket),因此我希望确保每个bucket中的命令代码数不超过32(位图为int)。我想在编译时而不是在运行时捕获它。除了如下检查每个BUCKET\N\u CMD\u 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
请也建议,如果有一个更优雅的方式来设计这个


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

首先,预处理器命令不是这样工作的。C预处理器只能“看到”由
#define
语句指示的名称或作为编译器标志传递的名称。它无法查看定义为
enum
的一部分或使用
const
关键字的常量。您应该使用
\u Static\u assert
来验证命令,而不是预处理器

至于命令,我建议将所有命令编号为
0..0x20

enum {
  BUCKET_1_CMD_1,
  BUCKET_1_CMD_2,
  ...
  BUCKET_1_CMD_MAX,
};
enum {
  BUCKET_2_CMD_1,
  BUCKET_2_CMD_2,
  ...
  BUCKET_2_CMD_MAX,
};
然后,您只需要一个保护值来检查所有命令是否都在有效范围内:

#define MAX_COMMAND 0x20
_Static_assert(BUCKET_1_CMD_MAX <= MAX_COMMAND, "too many bucket 1 commands");
_Static_assert(BUCKET_2_CMD_MAX <= MAX_COMMAND, "too many bucket 2 commands");

首先修复代码中的错误。如注释中所述,您有一个常量
BUCKET\u 1=0x100
,然后将其赋值为
CMD\u BUCKET\u 1\u START=BUCKET\u 1
。因此,后续枚举将获得值0x101、0x102。。。而
BUCKET\u 1\u CMD\u 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);
}

编辑

如果将错误修复与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);
}
#定义BUCKET_MAX 32
#定义检查(n)(BUCKET#n#u CMD#u MAX-CMD#u BUCKET#n#u START)
#定义STRINGIFY(n)#n
#定义BUCKET_CHECK(n)\

_静态断言(检查(n)可以发表评论;但是,我认为你的问题更适合于Stack Overflow的代码审查论坛。有人能把这个问题转移到代码审查吗?@PaulOgilvie这个问题似乎更适合于Stack Overflow。代码审查需要完整的工作代码示例。这个示例没有任何意义。你有一个常量BUCKET_1=0x100wh然后分配
CMD_BUCKET_1_START=BUCKET_1
。因此,后面的枚举将得到值0x101、0x102,…,
BUCKET_1_CMD_MAX
将是0x106。因为0x106总是大于0x20,所以静态断言总是会触发。请发布一个工作示例。你是正确的Lundin,这是我监督的一个代码错误。但基本上,我会屏蔽正确的位来测试每个组的最大计数。您下面的答案似乎就是这样。谢谢。我想您也可以将错误修复与断言宏合并,但是宏会变得更混乱。我不明白引入CHECK helper宏的原因。它通过不添加任何内容来降低可读性。另外,我认为您的解决方案实际上增加了代码重复,因为现在必须同时具有
BUCKET\u 1\u CMD\u N
\35;定义的附加列表和
BUCKET\u CHECK
宏调用的列表。@kfx没有“附加列表”,这是使程序工作所必需的功能。但正如我在上面的评论中所写的,它可以与BUCKET_CHECK宏合并。至于可读性:如果你认为
BUCKET_#n##u cmdn更新为一个集成了bug修复的宏,我确实认为显式编写宏条件比编写ju更可读st
BUCKET\u CHECK(1)
。由于不熟悉该代码,我不知道检查了什么。这是有效的,但有一个副作用,即您丢失
cmd\u代码
类型,以及任何编译器类型检查(这可能是问题,也可能不是问题)。感谢您的回答,我使用的是
\u Static\u assert()
按照建议并使用@Lundin的方法
error: static assertion failed: "Number of commands in BUCKET_1_CMD_N exceeds BUCKET_MAX."
note: in expansion of macro 'BUCKET_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);
}