How to ensure that the enumeration and array have the same record at compilation?

This is for an embedded application that runs low on UC. Another part of the system requires parameters to be set, and the local UC must maintain a list of parameters. Each parameter consists of an 8-bit identifier and an 8-bit value. The identifier starts at 0x70 due to memory limitations at the other end.

To support the maximum possible memory usage, I implemented the parameter store as an array containing identifiers and values ​​for all writable parameters. After that, a list of these parameters is listed in the header file so that other parts of the application can access the parameters.

Is there a way to make sure that the list of enumerations and the array of parameters have the same entries in the same order? I have documented the code carefully enough (not all are included in the excerpts), but I would like to include some form of verification at compile time to make sure the list and array match.

Another thing I'm not sure about is the most effective way to implement it. I need to be able to iterate over the parameters in order to transfer them to another part of the system, and I need to use as little memory as possible.

From parameters.h :

 /*******************************************************************************/ /* IDs for all parameters must be defined */ /* Defaults only need to be defined for rut-time writable parameters */ /* All parameters must have an ID define */ /* Writable parameters must also have: */ /* * DefaultValue define */ /* * Entry in ParamIndex */ /* * Initialesed default entry in Parameters (contained in C file) */ /* * If min and max values are not 0x00 and 0xFF then define those too */ /*******************************************************************************/ // Parameter IDs - All parameters need this defining #define Param_ActualTemp_ID 0xE0 #define Param_OperationMode_ID 0xE1 #define Param_MaintenanceModePID0_ID 0xE5 #define Param_MaintenanceModePID1_ID 0xE6 #define Param_FirmwareVersionL_ID 0xEB #define Param_FirmwareVersionH_ID 0xEC #define Param_SerialNumberL_ID 0xED #define Param_SerialNumberH_ID 0xEE #define Param_SerialNumberHH_ID 0xEF #define Param_MaxTemperature_ID 0xFC #define Param_NULL_ID 0xFF // Parameter Default Values - All writable parameters need this defining #define Param_NULL_DefaultValue 0xFF #define Param_OperationMode_DefaultValue 0 #define Param_MaintenanceModePID0_DefaultValue 0xFF #define Param_MaintenanceModePID1_DefaultValue 0xFF #define Param_MaxTemperature_DefaultValue 0x54 typedef struct { const uint8 id; uint8 value; } PARAMETER; // Parameter Index, any writable parameters need an entry here // Used as array index for parameters[], do not edit existing values typedef enum { Param_NULL = 0, Param_OperationMode, Param_MaintenanceModePID0, Param_MaintenanceModePID1, Param_MaxTemperature, /* Additional values must be entered above this line */ Param_NUMBER_OF_IMPLEMENTED_PARAMS } ParamIndex; extern PARAMETER parameters[]; 

From parameters.c :

 PARAMETER parameters[] = { { .id = Param_NULL_ID, .value = Param_NULL_DefaultValue}, { .id = Param_OperationMode_ID, .value = Param_OperationMode_DefaultValue}, { .id = Param_MaintenanceModePID0_ID, .value = Param_MaintenanceModePID0_DefaultValue}, { .id = Param_MaintenanceModePID1_ID, .value = Param_MaintenanceModePID1_DefaultValue}, { .id = Param_MaxTemperature_ID, .value = Param_MaxTemperature_DefaultValue} }; 
+5
source share
3 answers

You are on the right track with Param_NUMBER_OF_IMPLEMENTED_PARAMS . Unfortunately, you cannot use this as the size of an array, because it ensures that there are no more initializers in the array than an enumeration. It does not protect against an array having too few initializers.

The way to ensure this is to make a static statement of the size of the enumeration compared to the size of the array. Save the array declaration as PARAMETER parameters[] , and then do

 _Static_assert(Param_NUMBER_OF_IMPLEMENTED_PARAMS == sizeof(parameters)/sizeof(*parameters), "Array initializer list does not match enum"); 

The standard _Static_assert keyword is available only in C11, and the standard static_assert macro is available only in C11 and C ++. On older compilers you have to come up with this yourself. For instance:

 #define STATIC_ASSERT(expr) {typedef uint8_t S_ASSERT[(expr) ? 1 : 0];} 

This will give a cryptic error "I cannot declare an array with a zero size" if an error is generated.


The order can be secured using the assigned initializers for the array elements:

 PARAMETER parameters[] = { [Param_NULL_DefaultValue] = { .id = Param_NULL_ID, .value = Param_NULL_DefaultValue}, ... 

Assigned initializers do not prevent duplication, but in the case of duplicate entries, only the latter will be used (and you usually get a compiler warning). Such duplicate entries do not affect the size of the array.

+4
source

You can use Param_NUMBER_OF_IMPLEMENTED_PARAMS as the size of the array. This will at least lead to a compiler reaction if you have many elements in the list of array initializers.

However, there is no (standard and portable) way to warn that you are not initializing all elements. All elements that you do not initialize will be initialized to zero.

There is definitely no way to ensure order, not compilation time.

There may be static analyzer tools that can help you. And, of course, strict code reviews.

+1
source

You can use generator macros like these.
When you change PARAM_BLOCK, the enumeration and array are built automatically.

 #define PARAM_BLOCK(GEN_FUNC) \ GEN_FUNC(Param_NULL_ID, Param_NULL_DefaultValue) \ GEN_FUNC(Param_OperationMode_ID, Param_OperationMode_DefaultValue) \ GEN_FUNC(Param_MaintenanceModePID0_ID, Param_MaintenanceModePID0_DefaultValue) \ GEN_FUNC(Param_MaintenanceModePID1_ID, Param_MaintenanceModePID1_DefaultValue) \ GEN_FUNC(Param_MaxTemperature_ID, Param_MaxTemperature_DefaultValue) \ #define GENERATE_PARAM_ARRAY(paramId,paramValue) { .id = paramId, .value = paramValue }, #define GENERATE_PARAM_ENUM(paramId,paramValue) paramId, typedef enum { GENERATE_PARAM_ENUM(PARAM_BLOCK) /* Additional values must be entered above this line */ Param_NUMBER_OF_IMPLEMENTED_PARAMS } ParamIndex; PARAMETER parameters[] = { GENERATE_PARAM_ARRAY(PARAM_BLOCK) }; 

But perhaps you could simplify your array of parameters, since you have an ordered array by id, you don't need an identifier at all.

 #define GENERATE_WRITE_PARAM_ARRAY(paramId,paramValue) paramValue, uint8 writeable_parameters[] = { GENERATE_WRITE_PARAM_ARRAY(PARAM_BLOCK) }; 
+1
source

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


All Articles