2016-12-09 30 views
5

Điều này dành cho ứng dụng nhúng chạy ở mức thấp trên UC. Một phần khác của hệ thống yêu cầu các tham số được thiết lập và UC cục bộ cần duy trì một danh sách các tham số. Mỗi tham số bao gồm một ID 8 bit và một giá trị 8 bit. ID bắt đầu ở 0x70 do giới hạn bộ nhớ ở đầu kia.Làm thế nào để đảm bảo enum và mảng có cùng một mục tại biên dịch?

Để giữ mức sử dụng bộ nhớ càng thấp càng tốt, tôi đã triển khai kho tham số dưới dạng mảng chứa ID và Giá trị cho tất cả các tham số có thể ghi. Sau đó có một danh sách liệt kê các tham số này trong tệp tiêu đề để cho phép các phần khác của ứng dụng truy cập các tham số.

Có cách nào để đảm bảo rằng danh sách enum và mảng tham số có cùng mục nhập trong cùng một thứ tự không? Tôi đã ghi lại mã một cách khá kỹ lưỡng (không phải tất cả trong các phần chiết xuất) nhưng tôi muốn đưa vào một số hình thức kiểm tra tại thời gian biên dịch để đảm bảo rằng danh sách và mảng khớp với nhau.

Một điều khác mà tôi không chắc là nếu đây là cách hiệu quả nhất để thực hiện điều này. Tôi cần để có thể lặp qua các tham số để truyền chúng đến một phần khác của hệ thống và tôi cần sử dụng ít bộ nhớ nhất có thể.

Từ 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[]; 

Từ 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} 
}; 

Trả lời

4

Bạn đang đi đúng hướng với Param_NUMBER_OF_IMPLEMENTED_PARAMS. Thật không may bạn không thể sử dụng điều này như là một kích thước mảng, bởi vì đó chỉ đảm bảo rằng mảng không có nhiều initializers hơn enum. Nó không bảo vệ chống lại các mảng có quá ít initializers.

Cách để đảm bảo điều này là thực hiện xác nhận tĩnh kích thước enum so với kích thước mảng. Giữ khai mảng như PARAMETER parameters[] và sau đó làm

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

Từ khóa tiêu chuẩn _Static_assert là tuy nhiên chỉ có sẵn trong C11, và các tiêu chuẩn vĩ mô static_assert chỉ có sẵn trong C11 và C++. Trên các trình biên dịch cũ hơn, bạn sẽ phải tự mình phát minh ra nó. Ví dụ:

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

Điều này sẽ gây ra lỗi "không thể khai báo mảng có kích thước bằng không" nếu trình xác nhận không thành công.


Thứ tự có thể được đảm bảo bằng cách sử dụng initializers dành riêng cho các mục mảng:

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

initializers Designated không ngăn cản bản sao, nhưng trong trường hợp các mục trùng lặp chỉ người cuối cùng sẽ được sử dụng (và bạn thường nhận được cảnh báo trình biên dịch). Các mục trùng lặp như vậy sẽ không ảnh hưởng đến kích thước mảng.

+0

Tôi không thể có được một macro khẳng định tĩnh làm việc với trình biên dịch của tôi (bắt nguồn từ gcc 4.1) nhưng có các trình khởi tạo được chỉ định là một ý tưởng tuyệt vời mà tôi không biết. Tôi đã thêm vào đó và tôi sẽ chỉ đảm bảo rằng nhà phát triển khác trong dự án này biết được những gì tôi đã làm. Có lẽ tôi sẽ là người duy trì mã này trong tương lai. – RobbG

+0

@RobbG khẳng định tĩnh đã được giới thiệu với C11, lần lượt được giới thiệu ở đâu đó xung quanh gcc 4.8. Bạn sẽ có thể sử dụng macro khẳng định tĩnh nhà của tôi trên bất kỳ trình biên dịch C mặc dù. – Lundin

1

Bạn có thể sử dụng Param_NUMBER_OF_IMPLEMENTED_PARAMS như kích thước mảng. Điều đó ít nhất sẽ dẫn đến trình biên dịch phản ứng nếu bạn có nhiều phần tử trong danh sách khởi tạo mảng.

Tuy nhiên, không có cách nào (chuẩn và di động) để cảnh báo rằng bạn không khởi tạo tất cả các thành phần. Tất cả các yếu tố bạn không khởi tạo sẽ được "không" khởi tạo.

chắc chắn không có cách nào đảm bảo trật tự, không phải lúc biên dịch.

Có thể có các công cụ phân tích tĩnh có thể giúp bạn. Và tất nhiên là đánh giá mã nghiêm ngặt.

+0

Xin cảm ơn, tôi chủ yếu tìm kiếm nếu có cách xây dựng để làm điều đó. Tôi nghĩ rằng với các tài liệu hướng dẫn nó phải được khá rõ ràng những gì đang xảy ra và những gì cần thêm nếu chúng ta cần phải thay đổi danh sách trong tương lai. Tôi sẽ xem xét các công cụ phân tích tĩnh trong tương lai để tôi có thể thêm nó vào danh sách những thứ tôi tìm kiếm trong chúng. – RobbG

+1

Tất nhiên bạn có thể đảm bảo điều này tại thời gian biên dịch, với các xác nhận tĩnh. Đây là một kịch bản nổi tiếng và công việc xung quanh đã tồn tại trong nhiều thập kỷ trước khi các khẳng định tĩnh được đưa vào tiêu chuẩn. – Lundin

+1

@RobbG Xem câu trả lời của tôi về cách đảm bảo điều này tự động theo tiêu chuẩn C. Không cần xem xét thủ công cũng như các máy phân tích tĩnh. – Lundin

1

Bạn có thể sử dụng macro máy phát, giống như các macro này.
Khi bạn sửa đổi PARAM_BLOCK, enum và mảng được tạo tự động.

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

Nhưng có lẽ bạn có thể đơn giản hóa mảng tham số của mình, vì bạn có mảng được sắp xếp theo id, bạn không cần id.

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

Tôi tránh giải pháp này với mục đích, vì cái gọi là "X macro" sản xuất mã xấu xí khủng khiếp. Không nên sử dụng chúng trong trường hợp này. Giải pháp "X macro" luôn là phương sách cuối cùng và nên tránh nếu có thể. – Lundin

Các vấn đề liên quan