问题

在使用GLOG的时候经常会输出Eigen矩阵,但是输出的矩阵每个值前面都会有很多的0出现,如下

WARNING: Logging before InitGoogleLogging() is written to STDERR
I0806 19:17:13.466074 21812 test1.cpp:8] 00.680375 -0.211234 00.566198

代码如下:

#include <iostream>
#include <iomanip>
#include <Eigen/Core>
#include <glog/logging.h>

int main(int /*argc*/, char** /*argv*/) {
  Eigen::Vector3d a = Eigen::Vector3d::Random();
  LOG(INFO) << a.transpose();
  return 0;
}

结论

解决该显示问题有如下两种方式

  1. CMakeLists.txt文件中增加如下配置,取消输出对齐;优点为不需要修改任何代码,缺点为GLOG输出不能对齐矩阵的每个元素
set(EigenFormat "Eigen::IOFormat(6, DontAlignCols)")
add_compile_definitions(EIGEN_DEFAULT_IO_FORMAT=${EigenFormat})
  1. 变更GLOG使用方式,可以使用宏进行一层包裹进行适配;优点为可以进行矩阵元素的对齐,缺点就是需要修改代码(无论是修改宏还是修改调用点)
LOG(INFO) << std::setfill(' ') << matrix

问题排查

这种问题不是Eigen就是GLOG里面的问题。先看一下Eigen

Eigen方向

Eigen的输出实际上是对<<进行了重载,代码如下:

template<typename Derived>
std::ostream & operator <<
(std::ostream & s,
 const DenseBase<Derived> & m)
{
  return internal::print_matrix(s, m.eval(), EIGEN_DEFAULT_IO_FORMAT);
}

我们可以看到一个宏EIGEN_DEFAULT_IO_FORMAT字面意思是Eigen默认的IO格式。

参考https://eigen.tuxfamily.org/dox/TopicPreprocessorDirectives.html,可以知道EIGEN_DEFAULT_IO_FORMAT默认设置为IOFormat::IOFormat()。

IOFormat的函数参数说明可以在这里找到:Eigen: Eigen::IOFormat Class Reference

默认的构造参数为:

IOFormat (int _precision=StreamPrecision, int _flags=0, const std::string &_coeffSeparator=" ", const std::string &_rowSeparator="\n", const std::string &_rowPrefix="", const std::string &_rowSuffix="", const std::string &_matPrefix="", const std::string &_matSuffix="")

我们可以在includeEigen之前设置EIGEN_DEFAULT_IO_FORMAT为我们想要的参数,比如:

#include <iostream>
#include <iomanip>
#define EIGEN_DEFAULT_IO_FORMAT Eigen::IOFormat(6, DontAlignCols)
#include <Eigen/Core>
#include <glog/logging.h>

int main(int /*argc*/, char** /*argv*/) {
  Eigen::Vector3d a = Eigen::Vector3d::Random();
  LOG(INFO) << a.transpose();
  return 0;
}

经过测试,发现:

flagsDontAlignCols时,GLOG输出正常,但是矩阵元素和元素之间的对齐就没有了

flags0时,无论将_coeffSeparator_rowPrefix等参数修改为任何值,都没有办法去掉GLOG输出的padding0

所以问题出在GLOG

GLOG方向

翻了翻GLOG的头文件,发现实际实现日志记录的类是LogMessage

LOG(INFO) << xxx实际的调用情况大概是下面的这个代码:

google::LogMessage(__FILE__, __LINE__, google::INFO).stream() << xxx

github上找到了源码glog/logging.cc at master · google/glogGLOG的初始化代码大致如下

void LogMessage::Init(const char* file,
                      int line,
                      LogSeverity severity,
                      void (LogMessage::*send_method)()) {
  // some initialization code
  stream().fill('0');
  // some initialization code

这里实际是把GLOGostream的padding字符设置为0。比较奇怪为什么这么做。目前已经在github上提了issuehttps://github.com/google/glog/issues/695

最后附上测试代码

test1.cpp

#include <iostream>
#include <iomanip>
#include <Eigen/Core>
#include <glog/logging.h>

int main(int /*argc*/, char** /*argv*/) {
  Eigen::Vector3d a = Eigen::Vector3d::Random();
  LOG(INFO) << a.transpose();
  LOG(INFO) << std::setfill(' ') << a.transpose();
  std::cout << a.transpose() << std::endl;
  LOG(INFO) << std::setw(4) << 1.0;
  LOG(INFO) << std::setfill(' ') << std::setw(4) << 1.0;
  google::LogMessage l(__FILE__, __LINE__, google::INFO);
  l.stream().fill(' ');
  l.stream() << a.transpose();

  return 0;
}

CMakeLists.txt

project(test)
cmake_minimum_required(VERSION 3.17)
set(CMAKE_CXX_STANDARD 14)
# set(CMAKE_CXX_FLAGS "-march=native")
set(CMAKE_CXX_FLAGS "-O2 -march=native -no-pie")
find_package(Eigen3 REQUIRED)
find_package(Boost COMPONENTS system REQUIRED)

add_definitions(-DEIGEN_NO_DEBUG)
include_directories(${EIGEN3_INCLUDE_DIR})

add_executable(test1 test1.cpp)
target_link_libraries(test1 
    glog)

执行结果

$ ./test1
WARNING: Logging before InitGoogleLogging() is written to STDERR
I0806 19:17:13.466074 21812 test1.cpp:8] 00.680375 -0.211234 00.566198
I0806 19:17:13.466220 21812 test1.cpp:9]  0.680375 -0.211234  0.566198
 0.680375 -0.211234  0.566198
I0806 19:17:13.466259 21812 test1.cpp:11] 0001
I0806 19:17:13.466267 21812 test1.cpp:12]    1
I0806 19:17:13.466276 21812 test1.cpp:13]  0.680375 -0.211234  0.566198

总结

Eigen输出使用DontAlignCols可以避免padding0的问题是因为:

Eigen输出的矩阵元素对齐会用到std::setw,该操作增加的padding会使用std::setfillostream::fill设置的值;相反,不进行输出的矩阵元素对齐就不会用到std::setw

Ref

  1. https://eigen.tuxfamily.org/dox/TopicPreprocessorDirectives.html
  2. https://eigen.tuxfamily.org/dox/structEigen_1_1IOFormat.html#a840cac6401adc4de421260d63dc3d861
  3. https://eigen.tuxfamily.org/dox/structEigen_1_1IOFormat.html
  4. https://github.com/google/glog/blob/master/src/logging.cc#L1588
Last modification:August 25, 2021
If you think my article is useful to you, please feel free to appreciate