Using ENUM as raster images, how to check in C

I am developing firmware for an embedded application with limited memory. I have a set of commands that need to be processed as they are received. Each team falls under different "buckets", and each "bucket" receives a number of valid team numbers. For this, I created two ENUMs, as shown below.

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 

When my command handler function receives a command code, it needs to check if the command is enabled before processing it. I plan to use a bitmap for this. Commands can be enabled or disabled during processing at runtime. I can use int for each group (giving me 32 commands per group, I understand that 0xN00 to 0xN20 are valid command codes and that other codes in the range are lost). Despite the fact that the command codes are wasted, the choice of design makes it easy to tell the group the command code when viewing raw data on the console.

Since many developers can add commands to the cmd_codes enumeration (even new buckets can be added as necessary to the cmd_buckets enumeration), I want to make sure that the number of command codes in each bucket does not exceed 32 (bitmap is int). I want to catch this at compile time, not run time. Besides checking each BUCKET_N_CMD_MAX value as shown below and throwing a compile-time error, is there a better solution?

 #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 

Please also suggest if there is a more elegant way to develop it.

Thank you, I appreciate your time and patience.

+5
source share
2 answers

First correct the error in the code. As mentioned in the comments, you have the constant BUCKET_1 = 0x100 , which you then assign CMD_BUCKET_1_START = BUCKET_1 . Thus, the final enumerations will receive the values ​​0x101, 0x102, ... and BUCKET_1_CMD_MAX will be 0x106. Since 0x106 is always greater than 0x20, your static assert will always run.

Correct it so that it actually checks the total number of elements in the enumeration, for example:

 #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) ... 

Assuming the above is fixed, you can replace multiple checks with a single macro. Not a big improvement, but at least it reduces code repetition:

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

Exit gcc if one constant is too large:

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

EDIT

If you combine the bug fix with a validation macro, you get the following:

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

First of all, preprocessor commands do not work. The C preprocessor is only able to "see" names entered by the #define or passed as compiler flags. It cannot see constants defined as part of enum or with the const keyword. You should use _Static_assert to test the commands instead of the preprocessor.

As for the commands, I would suggest having all the commands numbered in the range 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, }; 

Then you need only one protective value to check if all the commands are in the valid range:

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

To use bit-wise commands or them along with bucket offset:

 enum { BUCKET_1 = 0x100, BUCKET_2 = 0x200, }; ... int cmd = BUCKET_2 | BUCKET_2_CMD_1; 
+1
source

Source: https://habr.com/ru/post/1240064/


All Articles