我曾经能够将Eigen3数组/矩阵传递给spdlog,而spdlog内部使用libfmt。从libfmt 9.0.0开始,这些类型不再由libfmt格式化,而无需进一步的代码。
自定义类型在fmt中是通过对类型进行专门化fmt::formatter<T>来支持的;fmt9文档进一步解释说,从ostream_formatter派生这种专门化将使用现有的operator<<,而特征类可以方便地提供这些operator<<。
Eigen 使用CRTP继承和我将专门针对以下所有Eigen::DenseBase<T>类型:
#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我能解释一下如何正确地做这件事吗?我想在这里理解c++的含义,并在此基础上给出一个可行的解决方案。谢谢!
发布于 2022-09-17 14:51:48
问题是,formatter将字符(代码单元)类型作为第二个模板参数,并通过可能无法工作的enable_if_t传递void。修复方法是传递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发布于 2022-09-17 20:29:01
如果您有c++20,您也可以使用一个概念,以防止进入这个SFINAE / void陷阱:
template <typename T>
requires std::is_base_of_v<Eigen::DenseBase<T>, T>
struct fmt::formatter<T> : ostream_formatter {};发布于 2022-11-14 22:25:04
Eigen::EigenBase<Derived>的其他解决方案在大多数情况下运行良好,但我发现它们没有涵盖来自Eigen::DenseBase<Derived>::format()的格式化Eigen::WithFormat<ExpressionType>输出。
为此,您需要添加
template <typename T> struct fmt::formatter<Eigen::WithFormat<T>> : ostream_formatter {};在正确的解决方案不太想工作的情况下,还值得一提的是设置FMT_DEPRECATED_OSTREAM定义的快速而肮脏的选项,该选项恢复了预v9行为。
https://stackoverflow.com/questions/73748856
复制相似问题