You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

问题原因

  1. 模板特化参数位置错误: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>,两者不匹配,特化并未生效。
  2. 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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.19 13:15:32