#c #fmt
Вопрос:
Я использую библиотеку fmt на C (c -17). В этой конкретной библиотеке значения с типами единиц измерения (углы, длины и т.д.) хранятся как двойные в наборе определенных единиц измерения (рад, км и т.д.). В различных функциях печати / ведения журнала они печатаются как тип единицы измерения, где пользователь имеет контроль над форматом вывода (печать всех углов в градусах). В настоящее время мы делаем что-то вроде этого:
double value = 0.1234;
out << "angle = " << Unit::format( value, Unit::angle ) << "n";
Я хотел бы использовать библиотеку fmt для форматирования, расширив средство форматирования для double, чтобы понять некоторые пользовательские коды форматирования. Например, я хотел бы сказать:
out << fmt::format( "angle = {:.3f[angle]}n", value );
и пусть пользовательский форматировщик проанализирует спецификацию angle и выполнит любые преобразования единиц измерения, прежде чем перейти к обычному двойному форматированию. Я пытался следовать документам пользовательского типа здесь и использовать fmt::formatter<double>
, но, похоже, он никогда не запускает этот код, а тем более не позволяет мне анализировать, выполнять некоторый синтаксический анализ формата, а затем переходить к «реальному» двойному форматированию. Выполнение чего-то вроде следующего компилируется нормально и просто выводит стандартный формат double (это просто быстрый взлом / тест, чтобы увидеть, смогу ли я заставить double работать через мой код). Пользовательский форматировщик структуры вызывает новый форматировщик double, но переменная double этого не делает.
Есть идеи о том, как переопределить / расширить объект форматирования по умолчанию для double?
#include <iostream>
#include <fmt/format.h>
#include <fmt/core.h>
struct Foo { double a; };
template <> struct fmt::formatter<double> {
constexpr auto parse(format_parse_contextamp; ctx) -> decltype(ctx.begin()) {
return ctx.begin();
}
template <typename FormatContext>
auto format(const doubleamp; v, FormatContextamp; ctx) -> decltype(ctx.out()) {
std::cerr << "formatter<double>::formatn";
return format_to( ctx.out(), "custom: {}", v );
}
};
template <> struct fmt::formatter<Foo> : fmt::formatter<double> {
template <typename FormatContext>
auto format(const Fooamp; v, FormatContextamp; ctx) -> decltype(ctx.out()) {
std::cerr << "formatter<Foo>::formatn";
return formatter<double>::format(v.a, ctx );
}
};
int main()
{
Foo a;
a.a = 1.12345;
std::cout << fmt::format( "Foo : {}n", a );
double b = 1.2345;
std::cout << fmt::format( "dbl : {}n", b );
return 0;
}
Дает:
formatter<foo>::format
formatter<double>::format
foo : custom: 1.12345
dbl : 1.2345
Комментарии:
1. Может быть, пойти на
fmt::format("{}", Angle(42))
? Должно быть проще.2. Да, спасибо. Это мой запасной вариант. трудно иметь один тип для unit, поскольку любая комбинация степеней длины, времени, массы и т. Д. Может быть единицей. Таким образом, вызывающий seq для этого get в некоторых случаях длинный.
Ответ №1:
Вы не можете переопределить форматирование встроенных типов, но вы можете создать новый тип, представляющий угол или любую другую единицу, и сделать его форматируемым:
struct Angle {
double value;
};
template <> struct fmt::formatter<Angle> : fmt::formatter<double> {
auto format(Angle angle, format_contextamp; ctx) {
// Implement custom formatting here.
return ctx.out();
}
};
std::string s = fmt::format("{}", Angle{42});