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
:
For all types T and CharT for which no specialization
std::formatter<T, CharT>
is enabled, that specialization is a complete type and is disabled.Disabled specializations do not meet the Formatter requirements, and the following are all
false
:
The problem here is that you are not specializing std::formatter
, but instead defining your own class template that derives from std::formatter
and specializing that. When you invoke std::format
it has no way of finding your Formatter
specializations.
The same applies to fmt::format
.
Additionally, your program is always using std::format
here, despite having logic to choose the format library based on the macro USE_FMT
. If you define that macro, you'll run into further issues because std::format
does not look for fmt::formatter
specializations.
You need to directly specialize std::formatter
(or fmt::formatter
). Here's how you could do it cleanly:
#include <iostream>
#include <string>
#ifdef USE_FMT
# include <fmt/format.h> // For fmt::format
# define FMT_NAMESPACE fmt
#else
# include <format> // For std::format
# define FMT_NAMESPACE std
#endif
using FMT_NAMESPACE::format;
struct Point {
int x;
int y;
};
template <>
struct FMT_NAMESPACE::formatter<Point> : formatter<std::string>
{
auto format(Point x, format_context& ctx) const
{
return formatter<std::string>::format(std::to_string(x.x) + "us", ctx);
}
};
int main()
{
Point xx {2,3};
std::cout << format("{}", xx);
}