libfmt 9.0+下Eigen3数组/矩阵格式化失败,求正确实现方案
Eigen3类型在libfmt 9.0.0+中的格式化问题解决
问题背景
之前可直接将Eigen3数组/矩阵传入依赖libfmt的spdlog,但升级至libfmt 9.0.0后,这些类型无法直接被格式化。按照fmt官方文档,尝试通过特化fmt::formatter<T>并继承ostream_formatter来复用Eigen已实现的operator<<,结合Eigen的CRTP继承特性,编写了如下代码:
#include<fmt/ostream.h> #include<eigen3/Eigen/Core> #include<iostream> template <typename T> struct fmt::formatter<T,std::enable_if_t<std::is_base_of_v<Eigen::DenseBase<T>,T>>>: ostream_formatter {}; int main(){ Eigen::Array3f a(1,2,3); std::cout<<fmt::format("{}",a)<<std::endl; }
但编译时触发如下错误:
In file included from /usr/include/fmt/format.h:48, from /usr/include/fmt/ostream.h:18, from /tmp/aa.cpp:1: /usr/include/fmt/core.h: In instantiation of ‘constexpr fmt::v9::detail::value<Context> fmt::v9::detail::make_value(T&&) [with Context = fmt::v9::basic_format_context<fmt::v9::appender, char>; T = Eigen::Array<float, 3, 1>&]’: /usr/include/fmt/core.h:1771:29: required from ‘constexpr fmt::v9::detail::value<Context> fmt::v9::detail::make_arg(T&&) [with bool IS_PACKED = true; Context = fmt::v9::basic_format_context<fmt::v9::appender, char>; fmt::v9::detail::type <anonymous> = fmt::v9::detail::type::custom_type; T = Eigen::Array<float, 3, 1>&; typename std::enable_if<IS_PACKED, int>::type <anonymous> = 0]’: /usr/include/fmt/core.h:1895:77: required from ‘constexpr fmt::v9::format_arg_store<Context, Args>::format_arg_store(T&& ...) [with T = {Eigen::Array<float, 3, 1, 0, 3, 1>&}; Context = fmt::v9::basic_format_context<fmt::v9::appender, char>; Args = {Eigen::Array<float, 3, 1, 0, 3, 1>}]’: /usr/include/fmt/core.h:1912:31: required from ‘constexpr fmt::v9::format_arg_store<Context, typename std::remove_cv<typename std::remove_reference<Args>::type>::type ...> fmt::v9::make_format_args(Args&& ...) [with Context = fmt::v9::basic_format_context<fmt::v9::appender, char>; Args = {Eigen::Array<float, 3, 1, 0, 3, 1>&}]’: /usr/include/fmt/core.h:3184:44: required from ‘std::string fmt::v9::format(fmt::v9::format_string<T ...>, T&& ...) [with T = {Eigen::Array<float, 3, 1, 0, 3, 1>&}; std::string = std::__cxx11::basic_string<char>; fmt::v9::format_string<T ...> = fmt::v9::basic_format_string<char, Eigen::Array<float, 3, 1, 0, 3, 1>&>]’: /tmp/aa.cpp:7:24: required from here /usr/include/fmt/core.h:1751:7: error: static assertion failed: Cannot format an argument. To make type T formattable provide a formatter<T> specialization: https://fmt.dev/latest/api.html#udt 1751 | formattable, | ^~~~~~~~~~~ /usr/include/fmt/core.h:1751:7: note: ‘formattable’ evaluates to false
问题原因
- 模板特化参数位置错误:
fmt::formatter的模板定义为template <typename T, typename Char = char> class formatter,第二个参数是字符类型(默认char)。原代码将std::enable_if_t作为第二个参数,导致特化的是fmt::formatter<T, void>,而实际调用fmt::format时使用的是默认的fmt::formatter<T, char>,两者不匹配,特化并未生效。 - CRTP类型判断未触发:虽然
std::is_base_of_v<Eigen::DenseBase<T>, T>逻辑上符合Eigen的CRTP继承(如Array3f继承自DenseBase<Array3f>),但因参数位置错误,该条件从未被正确触发。
解决方法
方法1:利用模板默认参数添加约束
将std::enable_if作为fmt::formatter特化的第三个默认参数,确保特化匹配默认的字符类型参数,同时处理引用和cv限定:
#include <fmt/ostream.h> #include <eigen3/Eigen/Core> #include <iostream> #include <type_traits> template <typename T, typename Char = char> struct fmt::formatter<T, Char, std::enable_if_t<std::is_base_of_v<Eigen::DenseBase<std::decay_t<T>>, std::decay_t<T>>>> : fmt::ostream_formatter<Char> {}; int main() { Eigen::Array3f a(1, 2, 3); std::cout << fmt::format("Array: {}", a) << std::endl; Eigen::Matrix2d m; m << 1, 2, 3, 4; std::cout << fmt::format("Matrix: {}", m) << std::endl; }
方法2:使用C++20 Concept约束(更简洁)
如果项目支持C++20,可通过Concept明确约束Eigen密集类型:
#include <fmt/ostream.h> #include <eigen3/Eigen/Core> #include <iostream> #include <type_traits> template <typename T> concept EigenDenseType = std::is_base_of_v<Eigen::DenseBase<std::decay_t<T>>, std::decay_t<T>>; template <EigenDenseType T, typename Char> struct fmt::formatter<T, Char> : fmt::ostream_formatter<Char> {}; int main() { Eigen::Array3f a(1, 2, 3); std::cout << fmt::format("{}", a) << std::endl; }
原理说明
两种方法均正确匹配fmt::formatter<T, char>的特化,通过继承fmt::ostream_formatter直接复用Eigen已实现的operator<<,完成自定义类型的格式化,同时覆盖所有Eigen密集类型(包括Array、Matrix等)。
内容的提问来源于stack exchange,提问作者eudoxos
相关产品推荐
相关产品推荐

