fmt::format_to + FMT_STRING with char16_t/char32_t characters fails to compile
Closed this issue · 2 comments
It's failing when using char16_t
or char32_t
with FMT_STRING in fmt 11.x
, but it was working in fmt 10.x
.
// Using a char32_t buffer fails to compile:
std::u32string u32buf;
fmt::format_to(std::back_inserter(u32buf), FMT_STRING(U"{}"), 2);
Similar to #3925, PR #3931 only fixed wchar_t
.
godbolt repro:
https://godbolt.org/z/q4vqbTEc5
godbolt success for fmt 10.x:
https://godbolt.org/z/sT6aKjfjj
Thanks for reporting but compile-time checks are only supported for char
and wchar_t
strings and there are no current plans to implement them for other code unit types. We could make legacy FMT_STRING
be a noop for these but it's probably better to just remove them from your code.
We also hit this while upgrading from 10.2.1 to 11.x.
We try pretty hard to use only char{8,16}_t in our data structures, avoiding things with unspecified/non-portable encoding like std::string and std::wstring, since we build in really interesting environments like winelib (where wchar_t might be libstdc++'s ucs-4, but there's still lots of windows WCHAR
running around that is utf-16 like wchar_t is on MSVC.
compile-time checks are only supported for char and wchar_t strings and there are no current plans to implement them for other code unit types
Interesting. I do see that even where it compiled in fmt 10.x the compile-time check was not actually performed. I.e. a totally incorrect usage like
#include <fmt/format.h>
#include <fmt/xchar.h>
int main() {
(void)fmt::format(FMT_STRING(u8"hello {:x}"),u8"world");
}
compiles just fine in fmt 10.2.x, but crashes at runtime with
terminate called after throwing an instance of 'fmt::v10::format_error'
what(): invalid format specifier
whereas changing it to wchar_t detects the problem at compile time
int main() {
(void)fmt::format(FMT_STRING(L"hello {:x}"),L"world");
}
/opt/compiler-explorer/libs/fmt/10.2.1/include/fmt/core.h:2340:27: error: call to non-'constexpr' function 'void fmt::v10::detail::throw_format_error(const char*)'
throw_format_error("invalid format specifier");
Interestingly, these did work with char16_t
when we first introduced the use of FMT_STRING
back with fmt 7.1.2
So, just to make the change somewhat more searchable:
- In fmt 8.1.1 and below,
FMT_STRING
seems to have actually worked to get compile-time checks withchar16_t
, including in c++14 mode. But you did need the macro even in c++20 mode - onlychar
(not evenwchar_t
) seems to get compile-time checking automatically. - In fmt 9.0-10.2.1,
char
andwchar_t
both get compile time checking even withoutFMT_STRING
.FMT_STRING
with other types (likechar16_t
) still compiles, but quietly became a no-op. Wrong format string/argument pairing for other char types is detected only at runtime.
Seemingly this was intentional, and the functionality to check other kinds of format strings was removed on purpose (might be good documentation update to least mention that in the release notes for 9.0?). - In fmt 11.0+,
FMT_STRING
no longer compiles except withchar
orwchar_t
(where it actually works, but is unnecessary since you get that by default in c++20 mode anyway).