C/C++结构体序列化配置模板化

C/C++写后台服务程序的工程师也许经常会遇到这样的问题:总有一些重要的数据是通过字符串的方式输出,比如jsonpxmlpvlog等等。比如日志格式,往往都是通过snprintf/std::cout的接口,将格式化的信息输出到文件或终端。

 

这样做确实方便,但随着业务日渐复杂,又很容易出问题。我们能否将日志格式做成可配置的,但对性能影响又很小?

我将这个问题转换为:能否让程序员将要输出的内容填充到一个结构体里,然后通过配置中指定的方式将这个结构体序列化为字符串?

例如:

struct A
{
   int i;
   char* s;
};

 

假如,结构体A的对象就是我们要序列化的pvlog,我们能否按照这样的定义:

int=%x

char*=%s

A=${i}|${s}

 

这样,当有一个对象A a = {10, "123"};时,我们能按上述定义,将其序列化为字符串:

a|123

(整数‘10’被%x格式化为‘a’)

 

当然业务日志可能要复杂得多,里面可能:

(1)      对于某些int类型的变量以%d方式输出,而另外一些int类型的变量却要以%x的方式输出;

(2)      有指向其他结构体的指针,甚至是指针的指针(……N重指针);

(3)      含有链表、数组等容器,需要遍历、挨个序列化;

(4)      ……

(5)      (其是没列出来的,就是没有考虑到的,也没有实现的:)

 

于是我设计了一套接口,用于封装上述功能。但对于程序员来说,只需要完成这么几步:

(1)      注册数据结构(register_struct):告诉程序你的数据结构里有哪些成员,这些成员是什么类型的。

(2)      注册序列化格式(register_typing_format):告诉程序一种类型的序列化方式。

(3)      也可以将所有类型的序列化方式写在一个文本文件里,由load_typing_format接口逐行进行(2)号接口调用。

(4)      接下来,就可以将需要用到的“序列化句柄”保存下来(get_typing_handler),以备后续重复使用。

(5)      最后,处理业务时,便可将构造好的结构体对向,按照上述句柄指定的方式进行序列化了(object_snprintf)。

 

举一个例子:

struct A
{
   int i;
   char* s;
};

struct B
{
   double* d;          // 指针
   string  str;
};

struct S
{
   int a;
   string b;            // stl string
   char* c;
   A** sA;            // 2重指针
   vector<B> vB;       // 数组容器
};

 

我们可以进行如下序列化的格式定义:

int:1=%x

char:1=%c

double:1=%f

string:1=%s

char*:1=%s           # 此处为char*类型定义了2种序列化格式

char*:2=%6s          # 可以按需要使用

A:1=${i}|${s:1}

B:1=${d:1}

B:2=${str:1}

S:1=${a:1}|${b:1}|${c:1}|${sA:1}|$<vB:1,",">|$<vB:2,";">

(其中${sA:1}表示将S对象中的sA成员(类型A的对象),按照A:1定义的格式进行序列化;$<vB:1,",">表示将对象S对象中的vB成员(类型Bvector)中的每一个元素,按照B:1定义的格式进行序列化,vector元素之间用”,”号分割。)

 

这样,当程序有这样的S结构体的对象时:

    A a = {1, "hello!"};
    A* pA = &a;
    double d1 = 3.1415f;
    double d2 = 6.1853f;
    Bb1 = {&d1, "world!"};
    Bb2 = {&d2, "china!"};
    vector<B> vb;
    vb.push_back(b1);
    vb.push_back(b2);
    S sObj = {3,"4", "5", &pA, vb};

 

可以这样进行序列化:

    const size_t MAXLENTH = 1024;
    char buff[MAXLENTH] = {0};
    ret = object_snprintf(buff, MAXLENTH, pvHandl, (void *)&sObj);
    std::cout<< buff << std::endl;

 

输出为:

3|4|5|1|hello!|3.141500,6.185300|world!;china!

 

具体的接口定义如下:

typedef void* TYPINGHANDLER;
typedef int (*ObjectFormatFunc)(char*,size_t, TYPINGHANDLER, void*, const char*);

// 类型描述
struct StructMemberDescription
{
   const char*       memberName;    // 成员名称
   DATATYPEENUM       dataType;      // 类型名称
   size_t             offset;        // 成员偏移
   int                plevel;        // 指针层级
                                      //     0:非指针或char*;
                                      //     1:有1层指针或char**
                                      //     n:有n层指针或char*(*n*)
   const char*       structName;    // 结构名称
                                      //     dataType=DATATYPE_STRUCT 时表示类型名称
                                      //     dataType=DATATYPE_CONTAINER 时表示容器内元素的名称
   ObjectFormatFunc  formatFunc;    // 对象格式化打印函数(dataType=DATATYPE_CONTAINER 时有效)
};

 

/*
 * 类型注册
 * param:
 *     name    :  struct name
 *     stDesc  :  member description of the struct
 *     n       :  number of member description in stDesc
 * return:
 *     0(SUCESS), -1(FAIL)
 * note:
 *     1.非线程安全函数
 */
int register_struct(const char* name,const StructMemberDescription* stDesc, const size_t n);

 
/*
 * 注册类型的序列化格式
 * param:
 *     typingname   :  typing name, "type:name"
 *     typingfmt    :  typing format string, "format"
 * return:
 *     0(SUCESS), -1(FAIL)
 * note:
 *     1.需要先注册类型(register_struct),后注册类型的序列化格式(register_typing_format)
 *     2.序列化格式串中涉及到的数据类型及对应的序列化格式必须已定义
 */
int register_typing_format(const char*typingname, const char* typingfmt);

/*
 * 加载格式化定义文件
 * note:
 *     1.按行注册类型的序列化格式(register_typing_format)
 */
int load_typing_format(const char*conf);

/*
 * 获取处理句柄
 * param:
 *      typingname :  typing format name
 * return:
 *     return the TYPINGHANDLER of typingname
 */
TYPINGHANDLER get_typing_handler(constchar* typingname);

 
/*
 * 对象序列化
 * param:
 *     buff           :  destination buffer
 *     bufflen        :  size of buffer
 *     typinghandler  :  typing format handler
 *     pData          :  object's address
 *     rc             :  err code
 * return:
 *     return the number of characters printed
*/
int object_snprintf(char *buff, size_tbufflen, TYPINGHANDLER typinghandler, void *pData, int *rc=NULL);

 

来看一个具体的使用示例:

//
// file: main.cpp
//
#include <iostream>
#include <vector>
#include <stddef.h>
#include <sys/time.h>
 
#include "inifile.h"
#include "template_format.h"

 
struct A
{
   int i;
   char* s;
};
 

struct B
{
   double* d;
   string  str;
};

struct S
{
   int a;
   string b;
   char* c;
   A** sA;
   vector<B> vB;
};

struct X
{
   S* pS;
   A  a;
   B* pB;
};

StructMemberDescriptiong_struct_member_desc_A[] = {
   //   memberName, dataType, offset, plevel, structName, template_snprint
   {"i",  DATATYPE_INT,      offsetof(A,i),      0,  NULL,     NULL},
   {"s",  DATATYPE_CHARP,    offsetof(A,s),      0,  NULL,     NULL},
};

StructMemberDescription g_struct_member_desc_B[]= {
   //   memberName, dataType, offset, plevel, structName, template_snprint
   {"d",  DATATYPE_DOUBLE,      offsetof(B,d),   1, NULL,      NULL},
   {"str", DATATYPE_STLSTRING,  offsetof(B, str), 0,  NULL,      NULL},
};

 

StructMemberDescriptiong_struct_member_desc_S[] = {
   //   memberName, dataType, offset, plevel, structName, template_snprint
   {"a",  DATATYPE_INT,         offsetof(S,a),   0,  NULL,      NULL},
   {"b",  DATATYPE_STLSTRING,   offsetof(S,b),   0,  NULL,      NULL},
   {"c",  DATATYPE_CHARP,       offsetof(S,c),   0,  NULL,      NULL},
   {"sA", DATATYPE_STRUCT,      offsetof(S,sA),  2,  "A",       NULL},
   {"vB", DATATYPE_CONTAINER,   offsetof(S,vB),  0,  "B",      VectorFormaterFunc(vector<B>)},
};

StructMemberDescriptiong_struct_member_desc_X[] = {
   //   memberName, dataType, offset, plevel, structName, template_snprint
   {"pS", DATATYPE_STRUCT,      offsetof(X,pS),  1,  "S",       NULL},
   {"pB", DATATYPE_STRUCT,      offsetof(X,pB),  1,  "B",       NULL},
};

struct StructDesc {
   const char* name;
   const StructMemberDescription* stDesc;
   const size_t n;
};
 

StructDesc g_self_def_struct[] = {
   {"A", g_struct_member_desc_A, sizeof(g_struct_member_desc_A)/sizeof(StructMemberDescription)},
   {"B", g_struct_member_desc_B, sizeof(g_struct_member_desc_B)/sizeof(StructMemberDescription)},
   {"S", g_struct_member_desc_S, sizeof(g_struct_member_desc_S)/sizeof(StructMemberDescription)},
   {"X", g_struct_member_desc_X, sizeof(g_struct_member_desc_X)/sizeof(StructMemberDescription)},
};

int reg_my_structs() {
   size_t i = 0;
   size_t n = sizeof(g_self_def_struct)/sizeof(StructDesc);
   for(i=0; i<n; i++){
       if(0!=register_struct(g_self_def_struct[i].name,
                             g_self_def_struct[i].stDesc,
                             g_self_def_struct[i].n)){
            return -1;
       }
   }
   return 0;
};

int main(int argc, char* argv[])
{
   int ret = 0;
 
   ret = reg_my_structs();                               // 注册结构体
   ret = load_typing_format( “format.ini” );            // 定义各种类型的序列化格式
 
   TYPINGHANDLER pvHandl = get_typing_handler(“S:1”);  // 根据序列化名称获取序列化句柄
   TYPINGHANDLER clkHandl = get_typing_handler(“X”);
   TYPINGHANDLER shHandl = get_typing_handler(“shlog”);

   A a = {1, "hello!"};
   A* pA = &a;
   double d1 = 3.1415f;
   double d2 = 6.1853f;
   B b1 = {&d1, "world!"};
   B b2 = {&d2, "china!"};
   vector<B> vb;
   vb.push_back(b1);
   vb.push_back(b2);
   S sObj = {3, "4", "5", &pA, vb};
   X xObj = {&sObj, a, &b2};

   const size_t MAXLENTH = 1024;

   char buff[MAXLENTH] = {0};
   // 按照指定句柄序列化
   ret = object_snprintf(buff, MAXLENTH, pvHandl, (void *)&sObj);
   std::cout << buff << std::endl;
//  std::cout <<3|4|5|1|hello!|3.141500,6.185300|world!;china!

 
   ret = object_snprintf(buff, MAXLENTH, clkHandl, (void *)&xObj);
   std::cout << buff << std::endl;
// std::cout << 3|4|5|1|hello!|3.141500,6.185300|world!;china!|china!
}

 

其中format.ini:

int:1=%x

char:1=%c

double:1=%f

string:1=%s

char*:1=%s

A:1=${i}|${s:1}

B:1=${d:1}

B:2=${str:1}

S:1=${a:1}|${b:1}|${c:1}|${sA:1}|$<vB:1,",">|$<vB:2,";">

X=${pS:1}|${pB:2}

 

(完)

完整代码示例,点击这里下载,g++下编译、测试通过。

 

  • 8
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值