Loading... # 问题 在使用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 ``` 代码如下: ```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(); return 0; } ``` # 结论 解决该显示问题有如下两种方式 1. 在`CMakeLists.txt`文件中增加如下配置,取消输出对齐;优点为不需要修改任何代码,缺点为`GLOG`输出不能对齐矩阵的每个元素 ``` set(EigenFormat "Eigen::IOFormat(6, DontAlignCols)") add_compile_definitions(EIGEN_DEFAULT_IO_FORMAT=${EigenFormat}) ``` 2. 变更`GLOG`使用方式,可以使用宏进行一层包裹进行适配;优点为可以进行矩阵元素的对齐,缺点就是需要修改代码(无论是修改宏还是修改调用点) ``` LOG(INFO) << std::setfill(' ') << matrix ``` # 问题排查 这种问题不是`Eigen`就是`GLOG`里面的问题。先看一下`Eigen` ## `Eigen`方向 `Eigen`的输出实际上是对`<<`进行了重载,代码如下: ```cpp 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](https://eigen.tuxfamily.org/dox/structEigen_1_1IOFormat.html) 默认的构造参数为: ```cpp 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="") ``` 我们可以在include`Eigen`之前设置`EIGEN_DEFAULT_IO_FORMAT`为我们想要的参数,比如: ```cpp #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`输出的padding`0` 所以问题出在`GLOG`中 ## `GLOG`方向 翻了翻`GLOG`的头文件,发现实际实现日志记录的类是`LogMessage`。 `LOG(INFO) << xxx`实际的调用情况大概是下面的这个代码: ```cpp google::LogMessage(__FILE__, __LINE__, google::INFO).stream() << xxx ``` 在`github`上找到了源码[glog/logging.cc at master · google/glog](https://github.com/google/glog/blob/master/src/logging.cc#L1588)。`GLOG`的初始化代码大致如下 ```cpp 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 ```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`可以避免padding`0`的问题是因为: `Eigen`输出的矩阵元素对齐会用到`std::setw`,该操作增加的padding会使用`std::setfill`或`ostream::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 25th, 2021 at 09:56 am © 允许规范转载
2 comments
猛
全是技术文,不明觉历啊。