共计 3922 个字符,预计需要花费 10 分钟才能阅读完成。
前言
不晓得大家在调试的时候,是否常常须要将一些类型变量的名称打印进去呢。这里提供了一个对立的办法,在应用时只须要很少的代码量,就能够把宏定义、枚举的变量名称打印进去了。
上面先上代码。
实现
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<total; i++) {if(enumerate[i].val == val) {return enumerate[i].name;
}
}
return "EnumUnknow";
}
老规矩,示例:
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 = (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
这篇文章还喜爱吗,咱们下次见。