2016-12-09 1 views
5

Это для встроенного приложения, которое работает на низком уровне на UC. Другая часть системы требует, чтобы параметры были установлены, а локальный UC должен поддерживать список параметров. Каждый параметр состоит из 8-битного идентификатора и 8-битного значения. Идентификаторы начинаются с 0x70 из-за ограничений памяти на другом конце.Как обеспечить, чтобы перечисление и массив имели одинаковые записи при компиляции?

Для обеспечения максимально возможного использования памяти я реализовал хранилище параметров как массив, содержащий идентификаторы и значения для всех доступных для записи параметров. После этого перечисляется список этих параметров в файле заголовка, чтобы другие части приложения могли обращаться к параметрам.

Есть ли способ гарантировать, что список перечислений и массив параметров имеют одни и те же записи в одном порядке? Я достаточно тщательно документировал код (не все включены в выдержки), но я хотел бы включить некоторую форму проверки во время компиляции, чтобы убедиться, что список и массив совпадают.

Другое дело, что я не уверен в том, что это самый эффективный способ реализации этого. Мне нужно иметь возможность перебирать параметры, чтобы передать их в другую часть системы, и мне нужно использовать как можно меньше памяти.

От 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[]; 

От 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} 
}; 

ответ

4

Вы находитесь на правильном пути с Param_NUMBER_OF_IMPLEMENTED_PARAMS. К сожалению, вы не можете использовать это как размер массива, потому что это гарантирует, что в массиве не будет больше инициализаторов, чем перечисление. Он не защищает от массива, имеющего слишком мало инициализаторов.

Способ обеспечения статического утверждения размера перечисления по сравнению с размером массива. Держите объявление массива в качестве PARAMETER parameters[], а затем сделать

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

Стандартное ключевое слово _Static_assert однако доступны только в С11, а стандартный макрос static_assert доступен только в C11 и C++. На старших компиляторах вам придется самому это придумать. Например:

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

Это даст загадочное «не может объявить массив с размером ноль» ошибка компилятора, если не удается утверждают.


Заказ может быть обеспечено с помощью назначенных инициализаторами для элементов массива:

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

Назначенные Инициализаторы не мешает дубликатов, но в случае повторяющихся записей будет использоваться только последняя (и вы обычно получают предупреждение компилятора). Такие повторяющиеся записи не будут влиять на размер массива.

+0

Я не могу заставить статический макрос assert работать с моим компилятором (полученный из gcc 4.1), но наличие назначенных инициализаторов - отличная идея, о которой я не знал. Я добавил это, и я просто сделаю так, чтобы другой разработчик этого проекта знал о том, что я сделал. Вероятно, я буду тем, кто все время будет поддерживать этот код в будущем. – RobbG

+0

@ RobbG Статический утверждение было введено с C11, который, в свою очередь, был представлен где-то около gcc 4.8. Тем не менее, вы должны использовать мой статический макрос home-brewn на любом компиляторе C. – Lundin

1

Вы можете использовать Param_NUMBER_OF_IMPLEMENTED_PARAMS как размер массива. Это, по крайней мере, приведет к реакции компилятора, если у вас есть много элементов в списке инициализаторов массива.

Однако нет (стандартного и портативного) способа предупредить, что вы не инициализируете все элементы. Все элементы, которые вы не инициализируете, будут инициализированы нулем.

определенно нет способа обеспечения порядка, а не во время компиляции.

Могут быть инструменты статического анализатора, которые могут вам помочь. И, конечно, строгие кодовые обзоры.

+0

Спасибо, я в основном искал, если бы был встроенный способ сделать это. Я думаю, что с документацией должно быть достаточно ясно, что происходит, и что нужно добавить, если нам нужно изменить список в будущем. Я буду смотреть на инструменты статического анализа в будущем, поэтому я мог бы добавить это в список вещей, которые я ищу в них. – RobbG

+1

Конечно, вы можете обеспечить это во время компиляции со статическими утверждениями. Это хорошо известный сценарий, и вокруг него существовали десятки десятилетий, прежде чем статические утверждения были включены в стандарт. – Lundin

+1

@RobbG См. Мой ответ, как обеспечить это автоматически в стандарте C. Нет необходимости в ручном просмотре или статических анализаторах. – Lundin

1

Вы можете использовать генераторные макросы, подобные этим.
При изменении PARAM_BLOCK перечисление и массив создаются автоматически.

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

Но, возможно, вы могли бы упростить массив параметров, как и у вас есть упорядоченный массив по идентификатору, вам не нужен идентификатор вообще.

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

Я избегал этого решения специально, так как так называемые «макросы X» производят ужасно уродливый код. В этом случае не должно быть необходимости использовать их. Решения «X macro» всегда являются самыми последними, и их следует избегать, если это возможно. – Lundin

Смежные вопросы