我曾经能够将 Eigen3 数组/矩阵传递给 spdlog,它内部使用 libfmt。从 libfmt 9.0.0 开始,这些类型不再由 libfmt 格式化,无需进一步的代码。
\nfmt 通过专门化类型来支持自定义fmt::formatter<T>类型;fmt9 文档进一步解释说,派生此专业化ostream_formatter将利用现有的operator<<,Eigen 类可以方便地提供这些。
Eigen使用 CRTP 继承,我将专门研究Eigen::DenseBase<T>这样的所有类型:
#include<fmt/ostream.h>\n#include<eigen3/Eigen/Core>\n#include<iostream>\n\ntemplate <typename T> struct fmt::formatter<T,std::enable_if_t<std::is_base_of_v<Eigen::DenseBase<T>,T>>>: ostream_formatter {};\n\nint main(){\n Eigen::Array3f a(1,2,3);\n std::cout<<fmt::format("{}",a)<<std::endl;\n}\nRun Code Online (Sandbox Code Playgroud)\n自从我发帖以来,很明显这并不顺利:
\nIn file included from /usr/include/fmt/format.h:48,\n from /usr/include/fmt/ostream.h:18,\n from /tmp/aa.cpp:1:\n/usr/include/fmt/core.h: In instantiation of \xe2\x80\x98constexpr 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>&]\xe2\x80\x99:\n/usr/include/fmt/core.h:1771:29: required from \xe2\x80\x98constexpr 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]\xe2\x80\x99\n/usr/include/fmt/core.h:1895:77: required from \xe2\x80\x98constexpr 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>}]\xe2\x80\x99\n/usr/include/fmt/core.h:1912:31: required from \xe2\x80\x98constexpr 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>&}]\xe2\x80\x99\n/usr/include/fmt/core.h:3184:44: required from \xe2\x80\x98std::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>&>]\xe2\x80\x99\n/tmp/aa.cpp:7:24: required from here\n/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\n 1751 | formattable,\n | ^~~~~~~~~~~\n/usr/include/fmt/core.h:1751:7: note: \xe2\x80\x98formattable\xe2\x80\x99 evaluates to false\nRun Code Online (Sandbox Code Playgroud)\n我可以获得一些如何正确执行此操作的解释吗?我想既了解这里 C++ 的情况,又想有一个基于此的可行解决方案。谢谢!
\n如果你有 c++20,你也可以使用一个概念,它可以防止陷入这个 SFINAE / void 陷阱:
template <typename T>
requires std::is_base_of_v<Eigen::DenseBase<T>, T>
struct fmt::formatter<T> : ostream_formatter {};
Run Code Online (Sandbox Code Playgroud)
https://godbolt.org/z/WGG77vGEr
问题在于,formatter将字符(代码单元)类型作为第二个模板参数,并且您void通过它传递到那里enable_if_t,这是不可能工作的。修复方法是传递char或其他代码单元类型:
template <typename T>
struct fmt::formatter<
T,
std::enable_if_t<
std::is_base_of_v<Eigen::DenseBase<T>, T>,
char>> : ostream_formatter {};
// ^ code unit type
Run Code Online (Sandbox Code Playgroud)
上帝螺栓: https: //godbolt.org/z/x1Tr3MPYY