由Google Log库glog循环打印到一行引发的C++知识点思考

原创 2016年08月29日 19:34:24

Google Log库glog

glog是Google的一个C++日志库,原文见http://google-glog.googlecode.com/svn/trunk/doc/glog.html。其使用方法类似于:

LOG(INFO) << "Found " << num_cookies << " cookies";

使用相当方便!输出会类似于:

xxxxxxxx] Found 123456 cookies

前面的前缀我偷懒了,实际会包含输出log的时间、文件,以及行号!

我遇到的问题

上面的一行代码可以输出一行日志,但是我有时候需要遍历一个数组然后把数组里的东西输出到一行里。

比如我期望输出一个学生的所有课程id:

xxxxxxxxxx] Classes for student 10086: 135 137 134

假设我这么写代码(请原谅我用了c++11,因为语法比较简洁啦!):

vector<int> classIds = findClasses(studentId);
LOG(INFO) << "Classes for student " << studentId << ": ";
for(int classId: classIds) {
    LOG(INFO) << classId << " ";
}

实际输出会是介个样纸:

xxxxxxxxxx]Classes for student 10086:
xxxxxxxxxx]135
xxxxxxxxxx]137
xxxxxxxxxx]134

啊,每个LOG(INFO)都会输出一个前缀,还有换行!肿么达到我期望的输出呢!

我的解决方案

先撇出我经过一番搜寻得到的解决方案:

{
    auto&& log = COMPACT_GOOGLE_LOG_INFO;

    vector<int> classIds = findClasses(studentId);
    log.stream() << "Classes for student " << studentId << ": ";
    for(int classId: classIds) {
        log.stream() << classId << " ";
    }
}

这是神马东东!?

这个要解释起来还不是那么好解释,因为问题太多了,涉及的知识点也太多了!

关于Google log库的问题:

  1. LOG(INFO) 到底是神马东东?为什么它可以像cout一样使用?
  2. 为什么LOG(INFO)自动输出了换行?我明明没有让他输出换行来的。

关于我的解决方案的问题:

  1. COMPACT_GOOGLE_LOG_INFO是神马东东?
  2. auto 是神马东东?
  3. auto 后面的 && 是神马东东?
  4. auto 后面的 & 又是神马东东?
  5. for(int classId: classIds) 为啥跟学过的c++语法不一样啊?
  6. 为什么要在最外面加一层花括号?

关于Google log的问题

LOG(INFO) 到底是神马东东?为什么它可以像cout一样使用?

经过一番搜寻,最终还是通过查看源码得到了答案:

LOG(INFO)做了两件事情:
1. 创建了google::LogMessage类的一个临时对象
2. 调用临时对象的stream()方法,返回std::ostream的对象

看到这里有些盆友应该已经知道了!我再来做进一步解释:

cout 其实是一个全局变量,它也是std::ostream类的对象。

所以它们的使用方法一样,都可以用<<来输出东东!

为什么LOG(INFO)自动输出了换行?

根据上面的分析,有两种猜测,而且似乎只有这两种可能的解释:

  1. LogMessage的临时对象析构时,会输出换行
  2. LogMessage里存的std::ostream析构时,会输出换行

如果第2种猜测是对的,那代码写起来就简单啦!

{
    std::ostream& logStream = LOG(INFO);
    vector<int> classIds = findClasses(studentId);
    logStream << "Classes for student " << studentId << ": ";
    for(int classId: classIds) {
        logStream << classId << " ";
    }
}

我们用一个引用保存了std::ostream对象,让它暂时不要被析构,等我们用完再让它析构。

那么输出如何呢?现实很骨感。。。什么都没输出TAT。

于是只剩下第1种猜测了!
那么,我们只要自己构造一个LogMessage对象,并在使用完后让它自动析构!

{
    google::LogMessage log(__FILE__, __LINE__);
    vector<int> classIds = findClasses(studentId);
    log.stream() << "Classes for student " << studentId << ": ";
    for(int classId: classIds) {
        log.stream() << classId << " ";
    }
}

结果很满意!!!!跟预想的完全一致!

然后,我高高兴兴地在很多很多地方都用上了这个方案!

程序效率突然降低,老板说暂时不要打Log了,看看效率如何,如果没有影响再恢复Log

我的天啊!
我的天啊!

这么多地方都用了,肿么办!!!总不能一个一个注释掉,然后又删掉注释吧!太麻烦了!以后要是有类似的要求,我就要去屎了!

为了和命运斗争,我决定再仔细研究一下Google log的源码!下载了glog源码,并进行./configure之后,我们就可以做做简单的分析啦!

搜索 #define LOG( 就能找到这个LOG的定义啦!其实它是个宏定义!

具体的定义如下:

#define LOG(severity) COMPACT_GOOGLE_LOG_ ## severity.stream()

好像。。。。看不懂啊!!! ##是几个意思?

再仔细一搜,发现宏定义里,## 用于拼接标识符,也就是说,你调用了LOG(INFO) 的地方,COMPACT_GOOGLE_LOG_ ## severity 都会被展开成COMPACT_GOOGLE_LOG_INFO 。哦!!看出来是怎么拼接的了吗?也就是说最后,整个表达式会被展开成:COMPACT_GOOGLE_LOG_INFO.stream()

那么,这个COMPACT_GOOGLE_LOG_INFO 又是神马东东咧!?

我们搜索一下它,立马就出来啦!

#if GOOGLE_STRIP_LOG == 0
#define COMPACT_GOOGLE_LOG_INFO google::LogMessage( \
      __FILE__, __LINE__)
#define LOG_TO_STRING_INFO(message) google::LogMessage( \
      __FILE__, __LINE__, google::GLOG_INFO, message)
#else
#define COMPACT_GOOGLE_LOG_INFO google::NullStream()
#define LOG_TO_STRING_INFO(message) google::NullStream()
#endif

原来如此哦!这个COMPACT_GOOGLE_LOG_INFO 会根据GOOGLE_STRIP_LOG的不同而不同。如果这个GOOGLE_STRIP_LOG不是0,那么COMPACT_GOOGLE_LOG_INFO就会被定义成google::NullStream哦!根据语义,它什么都不会输出!如果是0,那就被定义成google::LogMessage(__FILE__, __LINE)!这样就会正常输出log!

所以解决方案来啦!我们不要自己构造google::LogMessage,而是使用COMPACT_GOOGLE_LOG_INFO来进行构造!

可是它构造的东西我们怎么用变量存起来呢!?毕竟

// 这是不行的啦!LogMessage里把复制构造函数屏蔽了,我们不能进行对象的复制
auto a = COMPACT_GOOGLE_LOG_INFO;
// 这也是不行的啦!引用必需指向一个有效的对象!当然不能引用一个立马就要消失的对象啦!
auto& b = COMPACT_GOOGLE_LOG_INFO;
// 这是可行的哦(仅c++11和c++14)!那么,这是什么东东呢?
auto&& c = COMPACT_GOOGLE_LOG_INFO;

&&是右值引用操作符,专门用来解决接受一个临时对象的问题!关于右值引用,我就先不多说太多啦!

总之c是一个新的对象,而不是临时对象的别名或者克隆人!它偷偷地把临时对象里的所有内容挪为己用,注意!不是复制!而是直接拿过来了!就好像shell里不是cp而是mv一样!然后临时对象里就就可以悄悄地离开,不带走一片云彩~

版权声明:本文为博主原创文章,未经博主允许不得转载。

相关文章推荐

C++ 日志库 boost::log 以及 glog 的对比

C++ 日志库 boost::log 以及 glog 的对比

C++ 日志库 boost::log 以及 glog 的对比

C++ 日志库 boost::log 以及 glog 的对比

iOS知识点小集一行代码(持续更新......)

1. 打电话 [[UIApplication sharedApplication] openURL:[NSURL URLWithString:[NSString stringWithFormat:@"...

补充知识点实例(Log4net)

  • 2010-02-04 10:39
  • 3.55MB
  • 下载

如何使用Google日志库 (glog)

如何使用Google日志库 (glog) http://google-glog.googlecode.com/svn/trunk/doc/glog.html 介绍 Google glog是一...

如何使用Google日志库 (glog)

如何使用Google日志库 (glog) 介绍 Google glog是一个应用层的库. 它提供基于C++风格的流和多种宏接口.例如: #include int main(int ar...

C++重要知识点整理-思考

C++中构造函数的多种情况。尤其在构造函数初始化列表这个地方,有点小变数,如果简单地顺理一下,就会非常清晰、简单。关于继承,主要考虑不同访问限制符下,访问权限的问题。请看下面我的整理,现在感觉很清晰。...

如何使用google的日志库(glog)

简介 glog是google的一个应用级的日志库

Android的log日志知识点剖析

log类的继承结构Log public final class Log extends Object java.lang.Object ↳ android.util.Loglog日志的常...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:深度学习:神经网络中的前向传播和反向传播算法推导
举报原因:
原因补充:

(最多只允许输入30个字)