问题
在使用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;
}
结论
解决该显示问题有如下两种方式
- 在
CMakeLists.txt
文件中增加如下配置,取消输出对齐;优点为不需要修改任何代码,缺点为GLOG
输出不能对齐矩阵的每个元素
set(EigenFormat "Eigen::IOFormat(6, DontAlignCols)")
add_compile_definitions(EIGEN_DEFAULT_IO_FORMAT=${EigenFormat})
- 变更
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;
}
经过测试,发现:
当flags
为DontAlignCols
时,GLOG
输出正常,但是矩阵元素和元素之间的对齐就没有了
当flags
为0
时,无论将_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/glog。GLOG
的初始化代码大致如下
void LogMessage::Init(const char* file,
int line,
LogSeverity severity,
void (LogMessage::*send_method)()) {
// some initialization code
stream().fill('0');
// some initialization code
这里实际是把GLOG
的ostream
的padding字符设置为0
。比较奇怪为什么这么做。目前已经在github
上提了issue
:https://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::setfill
或ostream::fill
设置的值;相反,不进行输出的矩阵元素对齐就不会用到std::setw
2 comments
猛
全是技术文,不明觉历啊。