嵌入式项目中打造自己的utils库-ENUM转字符串

前言
不知道大家在调试的时候,是否经常需要将一些类型变量的名称打印出来呢。这里提供了一个统一的方法,在使用时只需要很少的代码量,就可以把宏定义、枚举的变量名称打印出来了。
下面先上代码。
实现
utils.h

typedef struct { int32_t val; char* name; } Enumerate; char* enumerate_to_string(Enumerate* enumerate, uint8_t total, int32_t val); #define ENUMERATE_DEF(object) static Enumerate _ENUM_##object[] #define ENUMERATE_ITEM(val) {val, #val}#define ENUM_TO_STRING(object, val) enumerate_to_string(_ENUM_##object, ARRAY_LENGTH(_ENUM_##object), val)#define ARRAY_LENGTH(array) (sizeof(array) / sizeof(array[0]))

这次的代码因为要遍历数组,已经不能用简单的宏定义来完全实现了。
utils.c
char* enumerate_to_string(Enumerate* enumerate, uint8_t total, int32_t val) { for(uint8_t i=0; i

老规矩,示例:
enum { UNDEFINED = 0, OBJECT, ARRAY, STRING, NUMBER, FLOAT, BOOL, }; ENUMERATE_DEF(JsonTypeStr) = { ENUMERATE_ITEM(UNDEFINED), ENUMERATE_ITEM(OBJECT), ENUMERATE_ITEM(ARRAY), ENUMERATE_ITEM(STRING), ENUMERATE_ITEM(NUMBER), ENUMERATE_ITEM(FLOAT), ENUMERATE_ITEM(BOOL), }; uint8_t json_type = ARRAY; printf("json_type:%s(%d)\n", ENUM_TO_STRING(JsonTypeStr, json_type), json_type);

结果:
json_type:ARRAY(2)

代码分析
这里的思路,是重新定义一个结构体数组,在这个结构体里保存枚举值,和值的名字。在使用时,遍历整个数组,找到对应值的名字。
同时为了减少手写代码的数量,利用宏定义的特性,将值的文本名称直接转成了字符串,就不用手动再去写一遍名称的字符串了。
使用上保留了原来c语言中定义枚举的语法,用 xxx={a1,a2,a3}; 的方式,使代码阅读更符合习惯。
在计算数组长度方面,c语言不比其他语言,java的array.length(),python的len(list),都可以快速准确得到数组长度。c语言中则需要这样来计算:
size_t array_len = sizeof(array) / sizeof(item);

用数组占的内存大小,除以每个项占的内存大小,就能得到一共有多少个项,就是数组长度了。在传递的参数都是数组的前提下,使用第一个元素可以简化代码:
size_t array_len = sizeof(array) / sizeof(array[0]);

这种用法有一定的隐患,没有对传入的参数做检查,所以也是没有大面积推广使用的原因吧。
同时要注意的,sizeof是一个操作符,和=,+这些是一样的,并不是函数,如果要计算一个数组的大小,需要传入正确的名字,通过传入指向数组的指针是不行的。
小知识:
在声明时,宏定义展开后增加了特定的前缀,这样可以有效避免和其他变量命名重复,使本变量更接近业务,更方便输入。
应用
就拿蓝牙断开原因来举例吧,运行在ESP32平台。
蓝牙断开原因在 idf\components\bt\host\bluedroid\api\include\api\esp_gatt_defs.h 中定义了:
/** * @brief Gatt Connection reason enum */ typedef enum { ESP_GATT_CONN_UNKNOWN = 0,/*!< Gatt connection unknown *//* relate to BTA_GATT_CONN_UNKNOWN in bta/bta_gatt_api.h */ ESP_GATT_CONN_L2C_FAILURE = 1,/*!< General L2cap failure*//* relate to BTA_GATT_CONN_L2C_FAILURE in bta/bta_gatt_api.h */ ESP_GATT_CONN_TIMEOUT = 0x08,/*!< Connection timeout*//* relate to BTA_GATT_CONN_TIMEOUT in bta/bta_gatt_api.h */ ESP_GATT_CONN_TERMINATE_PEER_USER = 0x13,/*!< Connection terminate by peer user*//* relate to BTA_GATT_CONN_TERMINATE_PEER_USER in bta/bta_gatt_api.h */ ESP_GATT_CONN_TERMINATE_LOCAL_HOST = 0x16,/*!< Connection terminated by local host *//* relate to BTA_GATT_CONN_TERMINATE_LOCAL_HOST in bta/bta_gatt_api.h */ ESP_GATT_CONN_FAIL_ESTABLISH = 0x3e,/*!< Connection fail to establish*//* relate to BTA_GATT_CONN_FAIL_ESTABLISH in bta/bta_gatt_api.h */ ESP_GATT_CONN_LMP_TIMEOUT = 0x22,/*!< Connection fail for LMP response tout */ /* relate to BTA_GATT_CONN_LMP_TIMEOUT in bta/bta_gatt_api.h */ ESP_GATT_CONN_CONN_CANCEL = 0x0100,/*!< L2CAP connection cancelled*//* relate to BTA_GATT_CONN_CONN_CANCEL in bta/bta_gatt_api.h */ ESP_GATT_CONN_NONE = 0x0101/*!< No connection to cancel*//* relate to BTA_GATT_CONN_NONE in bta/bta_gatt_api.h */ } esp_gatt_conn_reason_t;

我们来为它添加字符串:(上面定义原样复制出来,再做少许替换删减即可)
ENUMERATE_DEF(BleConnReasonStr) = { ENUMERATE_ITEM(ESP_GATT_CONN_UNKNOWN), ENUMERATE_ITEM(ESP_GATT_CONN_L2C_FAILURE), ENUMERATE_ITEM(ESP_GATT_CONN_TIMEOUT), ENUMERATE_ITEM(ESP_GATT_CONN_TERMINATE_PEER_USER), ENUMERATE_ITEM(ESP_GATT_CONN_TERMINATE_LOCAL_HOST), ENUMERATE_ITEM(ESP_GATT_CONN_FAIL_ESTABLISH), ENUMERATE_ITEM(ESP_GATT_CONN_LMP_TIMEOUT), ENUMERATE_ITEM(ESP_GATT_CONN_CONN_CANCEL), ENUMERATE_ITEM(ESP_GATT_CONN_NONE), };

在工程中应用:
// 蓝牙事件回调函数 static void gattc_profile_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, esp_ble_gattc_cb_param_t *param) { esp_ble_gattc_cb_param_t *p_data = https://www.it610.com/article/(esp_ble_gattc_cb_param_t *)param; switch (event) { case ESP_GATTC_DISCONNECT_EVT: //断连事件 //打印事件原因 ESP_LOGI(TAG,"ESP_GATTC_DISCONNECT_EVT, reason = %s(%x)", ENUM_TO_STRING(BleConnReasonStr, p_data->disconnect.reason), p_data->disconnect.reason); break; default: break; } }

实际打印会长这样:
I (1126378) BLE: ESP_GATTC_DISCONNECT_EVT, reason = ESP_GATT_CONN_TIMEOUT(8)

是不是很方便,这样看日志就一目了然,不用对着错误码再去对照定义。使用时也不需要额外再写数字转字符串的函数,在很多地方添加就能使用。
End
【嵌入式项目中打造自己的utils库-ENUM转字符串】这篇文章还喜欢吗,我们下次见。

    推荐阅读