宏定义在C中实现"面向对象", 在DPDK VPP中应用

最近做一个VPP(Vector Packet Processing)相关的项目,有很多重复性的框架代码。突发奇想,能否用面向对象的编译期多态思想替换框架代码。编译期展开自然联想到宏定义。

原理:

当预处理器遇到宏定义时,会在代码中将定义的标识符替换为其对应的内容。这种替换发生在编译之前的预处理阶段。比如Clang 中的 用宏实现的MIN:

c 复制代码
#define __NSX_PASTE__(A,B) A##B
#define MIN(A,B) __NSMIN_IMPL__(A,B,__COUNTER__)
#define __NSMIN_IMPL__(A,B,L) ({ \
__typeof__(A) __NSX_PASTE__(__a,L) = (A); \
__typeof__(B) __NSX_PASTE__(__b,L) = (B); \
(__NSX_PASTE__(__a,L) < __NSX_PASTE__(__b,L)) ? __NSX_PASTE__(__a,L) : __NSX_PASTE__(__b,L); })

经常搬砖的人,应该不陌生,MIN 是一个宏定义。不多介绍了。

创建函数宏:

1. 定义模块:

定义函数和结构体模块。 C /* 定义字符串 */ #define MODE_MOUDLE_STR2(str) #str #define MODE_MOUDLE_STR(str) MODE_MOUDLE_STR2(str) /* 定义函数 */ #define NODE_MOUDLE_FUN2(pre, node) pre##_##node #define NODE_MOUDLE_FUN(pre, node) NODE_MOUDLE_FUN2(pre, node) /* 定义结构体 */ #define NODE_MOUDLE_STRUCT2(pre, node, tail) pre##_##node##_##tail #define NODE_MOUDLE_STRUCT(pre, node, tail) NODE_MOUDLE_STRUCT2(pre, node, tail)

2. 业务流程:

重复的框架处理。不同模块,直接调用MODULE_NODE_PROC传入模块名token即可。下面代码是各模块都有的处理逻辑: 1.查询缓存,缓存匹配,进行业务处理。 2. 缓存匹配不上,查询数据把本次缓存,业务处理

C 复制代码
#define MODULE_NODE_PROC(module_name) \
static inline u16\
NODE_MOUDLE_FUN(_hpf_proc, CUR_MODULE_NODE)(const vlib_main_t *vm,\
                vlib_node_runtime_t *pstNodeRun, vlib_buffer_t **ppstVbuf,\ 
                NODE_MOUDLE_STRUCT(HPF,CUR_MODULE_NODE,CACHE_S) *pstCache) \
{\
 u16 usNext = 0; u32 uiRet ; vlib_buffer_t* pstVbuf  = *ppstVbuf; \
 NODE_MOUDLE_STRUCT(HPF,CUR_MODULE_NODE,CACHE_KEY_S) stKey; \ 
 NODE_MOUDLE_FUN(_hpf_fillKey, CUR_MODULE_NODE)(pstVbuf, &stKey);\
 /*3 命中缓存 */  \
 if(true == NODE_MOUDLE_FUN(_hpf_compareKey, CUR_MODULE_NODE)(pstCache, &stKey)){\
     /* 根据查到数据做业务 */\
     uiRet = NODE_MOUDLE_FUN(_hpf_work, CUR_MODULE_NODE)(vm, pstNodeRun, ppstVbuf, pstCache);\
     if (uiRet == 0)\
         usNext = pstCache->stData.usNext;\       
     return usNext;\
 }\
 uiRet = NODE_MOUDLE_FUN(_hpf_getDataByVbuf, CUR_MODULE_NODE)(vm, pstVbuf, pstCache);\
 if (uiRet != 0) \
     return usNext;\
 uiRet = NODE_MOUDLE_FUN(_hpf_work, CUR_MODULE_NODE)(vm, pstNodeRun, ppstVbuf, pstCache);\
 if (uiRet == 0)\
     usNext = pstCache->stData.usNext;\
 return usNext;\
 }

上述代码使用宏定义产生的函数,不同的模块直接修改下面宏定义即可,比如udp4模块。

C 复制代码
#define CUR_MODULE_NODE udp4

3. 继承实现:

定义模块,定义具体结构体,定义所需要的函数具体实现。

C 复制代码
#define CUR_MODULE_NODE udp4
#define CUR_MODULE_NODE_NAME_STR MODE_MOUDLE_STR(CUR_MODULE_NODE)
/* 报文特征,key */
 typedef struct{
    unsigned int uiAdjIndex;
 } NODE_MOUDLE_STRUCT(HPF,CUR_MODULE_NODE,CACHE_KEY_S);
 /* data */
 typedef struct{   
     unsigned short usNext;
     unsigned char  ucL3Type;
 }NODE_MOUDLE_STRUCT(HPF,CUR_MODULE_NODE,CACHE_DATA_S);
 typedef struct{
     NODE_MOUDLE_STRUCT(HPF,CUR_MODULE_NODE,CACHE_KEY_S) stKey;
     NODE_MOUDLE_STRUCT(HPF,CUR_MODULE_NODE,CACHE_DATA_S) stData;
 }NODE_MOUDLE_STRUCT(HPF, CUR_MODULE_NODE, CACHE_S) ;
 
 /* rewrite  */
 static inline void
 NODE_MOUDLE_FUN(_hpf_initCache, CUR_MODULE_NODE)(NODE_MOUDLE_STRUCT(HPF,CUR_MODULE_NODE,CACHE_S) *pstData)
 {
     memset(pstData, 0, sizeof(*pstData));
     return ;
 }
 /* rewrite */
 static inline unsigned int 
 NODE_MOUDLE_FUN(_hpf_compareKey, CUR_MODULE_NODE)(IN const NODE_MOUDLE_STRUCT(HPF,CUR_MODULE_NODE,CACHE_S)* pstData,
                                              IN const NODE_MOUDLE_STRUCT(HPF,CUR_MODULE_NODE,CACHE_KEY_S) *pstKey)
 {             
     return memcmp(pstKey, pstKey2, sizeof(*pstKey));    
 }

4. 调度流程:

上述是定义和实现,下面代码使用上述实现。通过编译阶段也能检测是否有实现错误。

C 复制代码
MODULE_NODE_PROC(CUR_MODULE_NODE)
MODULE_NODE_BATCH(CUR_MODULE_NODE)
/* 要去掉宏定义 */
#undef CUR_MODULE_NODE

最佳实践:

通过宏定义,它允许通过宏来定义模块、函数和结构体,并在编译期利用这些宏定义的多态性质来实现不同模块的特定业务处理。这种方式能够简化代码、减少重复工作,并且在编译时提供错误检测的功能。 但其实并不是最佳事件。C++才是真正利器。

相关推荐
A懿轩A1 小时前
C/C++ 数据结构与算法【数组】 数组详细解析【日常学习,考研必备】带图+详细代码
c语言·数据结构·c++·学习·考研·算法·数组
机器视觉知识推荐、就业指导1 小时前
C++设计模式:享元模式 (附文字处理系统中的字符对象案例)
c++
半盏茶香1 小时前
在21世纪的我用C语言探寻世界本质 ——编译和链接(编译环境和运行环境)
c语言·开发语言·c++·算法
Ronin3052 小时前
11.vector的介绍及模拟实现
开发语言·c++
✿ ༺ ོIT技术༻2 小时前
C++11:新特性&右值引用&移动语义
linux·数据结构·c++
字节高级特工2 小时前
【C++】深入剖析默认成员函数3:拷贝构造函数
c语言·c++
唐诺8 小时前
几种广泛使用的 C++ 编译器
c++·编译器
冷眼看人间恩怨9 小时前
【Qt笔记】QDockWidget控件详解
c++·笔记·qt·qdockwidget
红龙创客9 小时前
某狐畅游24校招-C++开发岗笔试(单选题)
开发语言·c++
Lenyiin9 小时前
第146场双周赛:统计符合条件长度为3的子数组数目、统计异或值为给定值的路径数目、判断网格图能否被切割成块、唯一中间众数子序列 Ⅰ
c++·算法·leetcode·周赛·lenyiin