Easylogging的封装使用二
来源:互联网 发布:大专网络教育怎么样 编辑:程序博客网 时间:2024/05/21 13:59
一.对Char类型的处理
在Easylogging的封装使用一:http://blog.csdn.net/woshichenweixian/article/details/77278488 中讲到,对char类型的输出需要进行一些特殊处理,最简单的处理是在最终输出char类型数据时进行一个强制转换,转成整形数据。
Easylogging中log数据的输出是在其类class Writer中实现的。在该类中对各种数据类型进行了<<符号重载中,这里截几个数据类型的输出如下:
inline Writer& operator<<(const std::string& log_) { if (!proceed_) { return *this; } _ELPP_STREAM(logger_) << log_; return *this; } inline Writer& operator<<(char log_) { if (!proceed_) { return *this; } _ELPP_STREAM(logger_) << log_; return *this; } inline Writer& operator<<(bool log_) { if (!proceed_) { return *this; } _ELPP_STREAM(logger_) << log_; return *this; } inline Writer& operator<<(signed short log_) { if (!proceed_) { return *this; } _ELPP_STREAM(logger_) << log_; return *this; }
分别将std::string,char,bool,signed short数据类型输出到_ELPP_STREAM(logger_)中,展开该宏可以看到是返回logger_的stream成员:
#define _ELPP_STREAM(l) (*(l->stream()))
logger_的stream是std::stringstream指针类型:
std::stringstream* stream_;
所以,我们的log数据最终都被放进标准库的string流中,在这之前,可改变要输出的数据的类型。例如,将char转成整形:
inline Writer& operator<<(char log_) { if (!proceed_) { return *this; } _ELPP_STREAM(logger_) << static_cast<int>(log_); return *this; }
通过static_cast将char数据转成int类型。
二.对数组的处理
在《Easylogging的封装使用一》中给出了输出数组的封装,其同样有char类型乱码的问题,同样可通过强转避免:
template<class _TYPE>void logDump(char *info,_TYPE *buff ,int size,LEVEL logLevel) { std::string str(info) ; str += ": " ; if(buff == NULL || size <=0) str+="buff is null!" ; else { std::stringstream ss ; for(int _dumpArrSize = 0; _dumpArrSize < size ; ++_dumpArrSize) ss << static_cast<int>(buff[_dumpArrSize]) << "," ; str += ss.str() ; } logStrOut(size , str ,logLevel) ; }
这种方式太过粗暴了,任何数据类型都被强转,特别是不管是unsigned还是unsigned的类型都被强转成int。要解决这个问题,可使用C++的模板特例化特性,只对char *,unsigned char *,const char * ,const unsigned char * 这四个类型进行特例化处理:
template<>void logDump(char *info,char *buff ,int size,LEVEL logLevel); template<>void logDump(char *info,unsigned char *buff ,int size,LEVEL logLevel); template<>void logDump(char *info,const char *buff ,int size,LEVEL logLevel); template<>void logDump(char *info,const unsigned char *buff ,int size,LEVEL logLevel);
然后在各自的特例化函数中对数据进行特殊处理。这样就避免了任何数据类型都直接进行int强转的粗暴处理,而只对几个char类型进行强转处理。
三.对struct,class类类型的封装
对Easylogging最满意的特性就是支持类类型的数据输出。例如,要输出下面的一个数据类型:
struct tagTest{int aa ;char cc ;};
在以往,只能对其所有的数据成员一个一个输出:
tagTest test ;LogDebug<<test.aa ;LogDebug<<test.cc ;
如果这个数据类型有十几二十个数据成员,那每次输出都必定是很麻烦的。为了解决这个麻烦,我们可以为该类型定义一个<<符号重载函数,然后就可以像内置数据类型一样,直接输出该数据类型的对象了:
struct tagTest{int aa ;char cc ;friend std::ostream& operator<<(std::ostream &out_ , const tagTest &obj_){out_ <<"aa:" <<obj_.aa <<",cc:"<<obj_.cc;return out_ ;}};
为什么多了一个符号重载函数就可以直接输出类类型的对象呢?其实是因为在log输出的实现之处,Writer类中实现了一个模板:
template <class Class> inline Writer& operator<<(const Class& class_) { if (!proceed_) { return *this; } _ELPP_STREAM(logger_) << class_; return *this; }
最终,自定义的类的输出将会进入该模板函数的一个特例中,然后经过_ELPP_STREAM(logger_) << class_找到自定义类的<<操作符函数中。
现在,我们可以这样输出我们定义的类类型对象了:
tagTest test ;test.aa = 1 ;test.cc = 2 ;LogDebug<<"test:"<<test ;
输出结果如下:
[19/08/2017 16:50:59.142] :test:aa:1,cc:
aa的值为1,咦,怎么cc的值没有输出?注意看cc的类型为char,所以其实这里也有char类型的问题,导致输出了ascii值为1的字符,其实是乱码来的。现在把cc的类型改成int类型,就可以看到输出的值是正常的了:struct tagTest{int aa ;int cc ;friend std::ostream& operator<<(std::ostream &out_ , const tagTest &obj_){out_ <<"aa:" <<obj_.aa <<",cc:"<<obj_.cc;return out_ ;}};
[19/08/2017 17:13:32.232] :test:aa:1,cc:2
所以,我们还需要解决类成员数据中char类型的输出问题。这个问题在后面会继续讲解怎么解决。现在先来看看如何对<<重载函数的定义进行封装,使得定义类的<<重载函数更简单:
//用于重载类的log输出#define OUTER out_ #define OBJ obj_#define BEGIN_OPERATOR_LOG_OUT(ObjType) \friend std::ostream& operator<<(std::ostream &OUTER , const ObjType &OBJ) \{ #define END_OPERATOR_LOG_OUT \return OUTER ; \}#define LOG_FOR_MEMBER(_mem) MYLOG::logOut(OUTER , OBJ._mem);
template<class _TYPE>void logOut(std::ostream &outer , _TYPE var){outer << var << ",";}
现在定义类的输出就更简单了:
struct tagTest{int aa ;int cc ;BEGIN_OPERATOR_LOG_OUT(tagTest)LOG_FOR_MEMBER(aa)LOG_FOR_MEMBER(bb)END_OPERATOR_LOG_OUT};
如果类的成员变量类型也是类类型,那怎么办?其实只需对该类型也进行<<重载就可以了。现在给tagTest添加一个类类型成员变量:
struct tagTT{int t1 ;int t2BEGIN_OPERATOR_LOG_OUT(tagTT)LOG_FOR_MEMBER(t1)LOG_FOR_MEMBER(t2)END_OPERATOR_LOG_OUT};struct tagTest{int aa ;int cc ;tagTT tt ;BEGIN_OPERATOR_LOG_OUT(tagTest)LOG_FOR_MEMBER(aa)LOG_FOR_MEMBER(bb)LOG_FOR_MEMBER(tt)END_OPERATOR_LOG_OUT};
可以看到,只需对新的类类型也进行 <<操作符的定义,就可以实现类类型的嵌套。
如果类的数据成员是数组呢?为了解决这个问题,需要再封装一个对数组的输出:
#define LOG_FOR_ARRAY_CONSTANT(_count,_arr) MYLOG::logArrOut(OUTER , _count , OBJ._arr) ;
template<class _TYPE>void logArrOut(std::ostream &outer, int count , _TYPE *arr){outer<<"<"; for(int _iArrSize = 0 ; _iArrSize < count ; ++_iArrSize) outer <<arr[_iArrSize] << ","; outer<<">" ; }
给tagTest再添加一个数组成员,然后看看其怎么输出的:
struct tagTest{int aa ;int cc ;tagTT tt ;int ary_[5] ;BEGIN_OPERATOR_LOG_OUT(tagTest)LOG_FOR_MEMBER(aa)LOG_FOR_MEMBER(bb)LOG_FOR_MEMBER(tt)LOG_FOR_ARRAY_CONSTANT(5,ary_)END_OPERATOR_LOG_OUT};
也是只需再添加一行代码就可以输出数组成员了,所以还是很方便的。还有一点也很实用的,数组的成员也可以是类类型,只需对该类类型进行<<重载的实现。所以,目前来说,普通的数据包所包含的数据类型都可以很方便的输出。
最后,来看看上面遗留的问题:char数据乱码的问题。我们不能直接对数据成员进行强转成int类型,因为有一些数据成员是类类型,不能直接进行强转;如果图方便,可以再定义一个宏专门处理char类型的,当这样就需要多记住一个宏了,使用起来不是很方便。
最好的解决方式是和前面的数组一样,对char类型进行模板特例化,即我们只需对这两个模板函数:
template<class _TYPE>void logOut(std::ostream &outer , _TYPE var)
template<class _TYPE>void logArrOut(std::ostream &outer, int count , _TYPE *arr)
然后在特例化函数中对特殊的数据类型进行特殊处理:
//下面的几个函数用来特化char类型,因为char的输出是其字符,但实际上数据包中期望的是输出数值。template<>void logOut(std::ostream &outer ,char var);template<>void logOut(std::ostream &outer ,unsigned char var);
template<>void logArrOut(std::ostream &outer, int count , char *arr);template<>void logArrOut(std::ostream &outer, int count , unsigned char *arr);template<>void logArrOut(std::ostream &outer, int count , const char *arr);template<>void logArrOut(std::ostream &outer, int count , const unsigned char *arr);
对Easylogging的使用经过自己的封装后,会方便很多,特别是在不同的项目中,根据项目的使用要求可以进行不同的封装。
如果对这份封装代码感兴趣的,可以联系我。
下一节将会对Easylogging的源码进行分析。
阅读全文
0 0
- Easylogging的封装使用二
- Easylogging的封装使用一
- Nohttp的使用(二)封装
- 简单好用的Log类,easylogging++
- Picasso的封装(二)
- jrtplib的简单封装类及使用(二)
- C3P0和dbutils数据库连接池封装的使用(二)
- webrtc封装sdk(二)call api的使用
- 在VS2015中使用easylogging++添加支持Unicode
- 解决在MFC中使用Easylogging++导致TRACE未定义错误
- 简单的分页封装(二)
- CAsyncSocket的封装使用
- AFNetworking的封装使用
- Retrofit的使用封装
- Otto的封装使用
- listview的封装使用
- volley的封装使用
- popupWindow的封装使用
- GDKOI2018&GDOI2018精英培训计划
- 串口电平转换及SCI 总线
- pandas df.drop()
- 菜鸟的myeclipse快捷之路
- 4052: [Cerc2013]Magical GCD/4488: [Jsoi2015]最大公约数
- Easylogging的封装使用二
- 水果机遥控器是真的吗
- Javascript第八天学习总结offset家族及简单轮播图
- HDU-2516-取石子游戏【 斐波那契博弈】
- jQuery Ajax 上传文件处理方式介绍
- 机房合作总结
- lintcode:二叉树的前序遍历
- Java中的工厂模式
- shell学习(三)-crontab命令