I am trying the following (link):
#include <string>
#include <format> // For std::format
#include <fmt/format.h> // For fmt::format
#include <ranges>
#include <functional>
struct Point {
int x;
int y;
};
#ifdef USE_FMT
template<typename T>
struct Formatter : public fmt::formatter<T> {};
using context = fmt::format_context;
#else
template<typename T>
struct Formatter : public std::formatter<T> {};
using context = std::format_context;
#endif
template <>
struct Formatter<Point>: Formatter<std::string>
{
auto format(Point x,
context& ctx) const
{
return Formatter<std::string>::format(std::to_string(x.x) + "us", ctx);
}
};
int main() {
Point xx {2,3};
std::cout << std::format("{}", xx); // assuming using std
return 0;
}
But it fails to compile. As indicated in the above code, I want to let the developer to custom format using the struct Formatter where the backend can then choose which backend solution to employ.
Any reasons why this fails and possible solutions to this?
Compiler error:
In file included from /opt/compiler-explorer/gcc-trunk-20240207/include/c++/14.0.1/ostream:43, from /opt/compiler-explorer/gcc-trunk-20240207/include/c++/14.0.1/iostream:41, from :1: /opt/compiler-explorer/gcc-trunk-20240207/include/c++/14.0.1/format: In instantiation of 'class std::__format::_Checking_scanner<char, Point>': /opt/compiler-explorer/gcc-trunk-20240207/include/c++/14.0.1/format:4174:4: required from 'consteval std::basic_format_string<_CharT, _Args>::basic_format_string(const _Tp&) [with _Tp = char [3]; _CharT = char; _Args = {Point&}]' 4174 | __scanner(_M_str); | ^~~~~~~~~ :38:29: required from here 38 | std::cout << std::format("{}", xx); // assuming using std | ~~~~~~~~~~~^~~~~~~~~~ /opt/compiler-explorer/gcc-trunk-20240207/include/c++/14.0.1/format:4030:10: error: static assertion failed: std::formatter must be specialized for each type being formatted 4030 |
(is_default_constructible_v<formatter<_Args, _CharT>> && ...),
The default for a non-specialized formatter will raise these kinds of errors, indicating that you have not actually specialized a formatter for that type. This is documented in
std::formatter:The problem here is that you are not specializing
std::formatter, but instead defining your own class template that derives fromstd::formatterand specializing that. When you invokestd::formatit has no way of finding yourFormatterspecializations.The same applies to
fmt::format.Additionally, your program is always using
std::formathere, despite having logic to choose the format library based on the macroUSE_FMT. If you define that macro, you'll run into further issues becausestd::formatdoes not look forfmt::formatterspecializations.You need to directly specialize
std::formatter(orfmt::formatter). Here's how you could do it cleanly: