#Checking whether type is formattable with `std::format`
10 messages · Page 1 of 1 (latest)
When your question is answered use !solved or the button below to mark the question as resolved.
Remember to ask specific questions, provide necessary details, and reduce your question to its simplest form. For tips on how to ask a good question use !howto ask.
;compile -std=c++26
#include <format>
#include <string>
#include <cassert>
std::string to_string(auto value) {
if constexpr (requires{std::format("{}", value);}) {
return std::format("{}", value);
}
return "dunno";
}
struct U {};
struct R {};
template <>
struct std::formatter<R, char> {
template <class ParseContext>
constexpr ParseContext::iterator parse(ParseContext &ctx) {
std::string arg;
auto it = ctx.begin();
for (; it != ctx.end() && *it != '}'; ++it) {
arg.push_back(*it);
}
if (arg != "") {
throw std::format_error{"Invalid format specifier \"" + arg + "\" for R"};
}
return it;
}
template <class FmtContext>
FmtContext::iterator format(const R &, FmtContext &ctx) const {
return std::format_to(ctx.out(), "R");
}
};
struct S {};
template <>
struct std::formatter<S, char> {
template <class ParseContext>
constexpr ParseContext::iterator parse(ParseContext &ctx) {
std::string arg;
auto it = ctx.begin();
for (; it != ctx.end() && *it != '}'; ++it) {
arg.push_back(*it);
}
if (arg != "cool") {
throw std::format_error{"Invalid format specifier \"" + arg + "\" for S"};
}
return it;
}
template <class FmtContext>
FmtContext::iterator format(const S &, FmtContext &ctx) const {
return std::format_to(ctx.out(), "S");
}
};
int main() {
assert(to_string(42) == "42"); //has a formatter
assert(to_string(U{}) == "dunno"); //does not have a formatter
assert(to_string(R{}) == "R"); //has a formatter
assert(to_string(S{}) == "dunno"); //has a formatter, but does not support "{}"
}
In file included from <source>:1:
/cefs/22/22e6cdc013c8541ce3d1548e_consolidated/compilers_c++_x86_gcc_15.2.0/include/c++/15.2.0/format: In instantiation of 'class std::__format::_Checking_scanner<char, U>':
/cefs/22/22e6cdc013c8541ce3d1548e_consolidated/compilers_c++_x86_gcc_15.2.0/include/c++/15.2.0/format:4809:4: required from 'consteval std::basic_format_string<_CharT, _Args>::basic_format_string(const _Tp&) [with _Tp = char [3]; _CharT = char; _Args = {U&}]'
4809 | __scanner(_M_str);
| ^~~~~~~~~
<source>:7:28: required from 'std::string to_string(auto:11) [with auto:11 = U; std::string = std::__cxx11::basic_string<char>]'
7 | return std::format("{}", value);
| ^~~~
<source>:62:5: required from here
62 | assert(to_string(U{}) == "dunno"); //does not have a formatter
| ~~~~~~~~~^~~~~
/cefs/22/22e6cdc013c8541ce3d1548e_consolidated/compilers_c++_x86_gcc_15.2.0/include/c++/15.2.0/fo
I've also tried using std::formatter<T, char> and std::format_context, but it always results in a hard error saying the formatter is not a constant expression due to the thrown exception.
do you just want std::formattable?
oh I see, you want to check specifically that it's formattable with {}; I'm not sure that's possible, because any such errors are outside of the immediate context
Yeah, std::formattable essentially fails the S case.
Ah, actually, I think by combining std::formattable with a requires on std::format_string it should work
template <typename T>
std::string to_string(T value) {
if constexpr (std::formattable<T, char>) {
if constexpr (requires {
typename std::integral_constant<int, (std::format_string<T>("{}"), 1)>;
}) {
return std::format("{}", value);
}
}
return "dunno";
}
``` works for me