C语言中简化increase_log_info函数,实现无判断动态数据类型更新
技术问询
需求背景:需要实现一个LOG参数更新接口,传入参数索引和待增加值,将值累加到对应参数上,要求实现简洁,无复杂判断。
我的实现思路:创建映射表,记录每个参数在T_LOG_INFO结构体中的偏移量和数据类型。调用接口时,根据参数索引获取参数地址,强制转换为对应类型后执行加法操作。
现存问题:当前实现的increase_log_info函数包含较多if...else条件判断,过于复杂,希望简化该函数,实现参数类型的直接映射,而非通过条件判断。
注意事项:
T_LOG_INFO结构体定义不可修改;- 外部提供的
LOG_INFO_INCREASE更新接口不可修改; - 其他代码可调整。
相关代码:
#pragma once /********************************can not be change, begin**********************************/ /* T_LOG_INFO is data structure definition and cannot be changed. */ typedef struct { unsigned short tx_num; unsigned int tx_bytes; unsigned short rx_num; unsigned int rx_bytes; unsigned char discard_num; unsigned int discard_bytes; // There are many parameters behind, not listed }T_LOG_INFO; T_LOG_INFO g_log_info; /* This macro is called very frequently, and efficiency needs to be considered. ** LOG_INFO_INCREASE is an update parameter interface provided externally and cannot be changed. */ //#define LOG_INFO_INCREASE(para_idx, inc_val) /********************************can not be change, end**********************************/ /********************************an alternative, begin**********************************/ enum { LOG_PARA_IDX_TX_NUM, LOG_PARA_IDX_TX_BYTES, LOG_PARA_IDX_RX_NUM, LOG_PARA_IDX_RX_BYTES, LOG_PARA_IDX_DISCARD_NUM, LOG_PARA_IDX_DISCARD_BYTES, LOG_PARA_IDX_MAX }; enum { DATA_TYPE_U8, DATA_TYPE_U16, DATA_TYPE_U32 }; typedef struct { /* Indicates the offset of this parameter in the structure. */ unsigned char offset; /* Indicates the data type of the parameter. */ unsigned char data_type; }T_PARA_MAPPING; /* This table can also be calculated during system initialization. */ T_PARA_MAPPING g_para_mapping_table[LOG_PARA_IDX_MAX] = { {0, DATA_TYPE_U16}, // LOG_PARA_IDX_TX_NUM {4, DATA_TYPE_U32}, // LOG_PARA_IDX_TX_BYTES {8, DATA_TYPE_U16}, // LOG_PARA_IDX_RX_NUM {12, DATA_TYPE_U32}, // LOG_PARA_IDX_RX_BYTES {16, DATA_TYPE_U8}, // LOG_PARA_IDX_DISCARD_NUM {20, DATA_TYPE_U32} // LOG_PARA_IDX_DISCARD_BYTES }; /* How to simplify the function??? especially to remove the judgment. */ static inline void increase_log_info(unsigned int para_idx, unsigned inc_val) { unsigned int data_type = g_para_mapping_table[para_idx].data_type; /* Get the parameter address and cast it to the corresponding type pointer before adding. */ if (data_type == DATA_TYPE_U8) { *((unsigned char*)(((unsigned char*)&g_log_info) + g_para_mapping_table[para_idx].offset)) += inc_val; } else if (data_type == DATA_TYPE_U16) { *((unsigned short*)(((unsigned char*)&g_log_info) + g_para_mapping_table[para_idx].offset)) += inc_val; } else { *((unsigned int*)(((unsigned char*)&g_log_info) + g_para_mapping_table[para_idx].offset)) += inc_val; } } /* This macro is called very frequently, and efficiency needs to be considered. */ #define LOG_INFO_INCREASE(para_idx, inc_val) increase_log_info(para_idx, inc_val) /********************************an alternative, end**********************************/ /********************************test case, begin**********************************/ void increase_log_info_test() { LOG_INFO_INCREASE(LOG_PARA_IDX_TX_NUM, 1); LOG_INFO_INCREASE(LOG_PARA_IDX_TX_NUM, 2); LOG_INFO_INCREASE(LOG_PARA_IDX_TX_NUM, 3); LOG_INFO_INCREASE(LOG_PARA_IDX_RX_BYTES, 10); LOG_INFO_INCREASE(LOG_PARA_IDX_RX_BYTES, 20); LOG_INFO_INCREASE(LOG_PARA_IDX_RX_BYTES, 30); } /********************************test case, end**********************************/
解决方案
要消除increase_log_info里的if...else判断,有两种高效且简洁的实现方式,均符合你的约束条件:
方案1:函数指针映射表
修改映射表结构,直接绑定对应类型的累加函数,运行时无需条件判断,直接调用对应函数:
// 提前定义各类型的累加函数 static inline void inc_u8(unsigned char* ptr, unsigned val) { *ptr += val; } static inline void inc_u16(unsigned short* ptr, unsigned val) { *ptr += val; } static inline void inc_u32(unsigned int* ptr, unsigned val) { *ptr += val; } // 重新定义映射表,用函数指针替代类型枚举 typedef void (*IncFunc)(void*, unsigned); typedef struct { unsigned char offset; IncFunc inc_func; }T_PARA_MAPPING; // 初始化映射表,用offsetof宏自动计算偏移,避免手动出错 T_PARA_MAPPING g_para_mapping_table[LOG_PARA_IDX_MAX] = { {offsetof(T_LOG_INFO, tx_num), (IncFunc)inc_u16}, {offsetof(T_LOG_INFO, tx_bytes), (IncFunc)inc_u32}, {offsetof(T_LOG_INFO, rx_num), (IncFunc)inc_u16}, {offsetof(T_LOG_INFO, rx_bytes), (IncFunc)inc_u32}, {offsetof(T_LOG_INFO, discard_num), (IncFunc)inc_u8}, {offsetof(T_LOG_INFO, discard_bytes), (IncFunc)inc_u32} }; // 简化后的累加函数,无任何条件判断 static inline void increase_log_info(unsigned int para_idx, unsigned inc_val) { T_PARA_MAPPING* map = &g_para_mapping_table[para_idx]; map->inc_func((unsigned char*)&g_log_info + map->offset, inc_val); }
这种方式把类型判断转移到初始化阶段,运行时仅需一次指针查找和函数调用,效率稳定,同时避免了手动维护偏移量的错误。
方案2:编译期宏优化(零运行时开销)
如果调用LOG_INFO_INCREASE时的参数索引是编译期常量(比如测试用例里的枚举值),可以用宏在编译期直接生成对应类型的累加代码,完全消除运行时判断:
// 定义通用分支生成宏,简化代码重复 #define _LOG_INFO_INC_CASE(idx, member, type) \ case idx: *((type*)((unsigned char*)&g_log_info + offsetof(T_LOG_INFO, member))) += val; break // 重构LOG_INFO_INCREASE宏,保持原有接口不变 #define LOG_INFO_INCREASE(para_idx, inc_val) do { \ unsigned val = inc_val; \ switch(para_idx) { \ _LOG_INFO_INC_CASE(LOG_PARA_IDX_TX_NUM, tx_num, unsigned short); \ _LOG_INFO_INC_CASE(LOG_PARA_IDX_TX_BYTES, tx_bytes, unsigned int); \ _LOG_INFO_INC_CASE(LOG_PARA_IDX_RX_NUM, rx_num, unsigned short); \ _LOG_INFO_INC_CASE(LOG_PARA_IDX_RX_BYTES, rx_bytes, unsigned int); \ _LOG_INFO_INC_CASE(LOG_PARA_IDX_DISCARD_NUM, discard_num, unsigned char); \ _LOG_INFO_INC_CASE(LOG_PARA_IDX_DISCARD_BYTES, discard_bytes, unsigned int); \ } \ } while(0)
编译器会识别para_idx为常量,自动将switch分支优化为直接的内存访问和加法指令,没有任何运行时判断开销,特别适合接口被频繁调用的场景。
两种方案均满足要求:无复杂条件判断,实现简洁,且不修改T_LOG_INFO和LOG_INFO_INCREASE的对外接口。如果参数索引可能是运行时变量,选方案1;如果索引都是编译期常量,方案2性能最优。
内容的提问来源于stack exchange,提问作者nn huang
相关产品推荐
相关产品推荐

