1 // Formatting library for C++ - formatters for standard library types
3 // Copyright (c) 2012 - present, Victor Zverovich
4 // All rights reserved.
6 // For the license information refer to format.h.
17 #include <type_traits>
25 #if FMT_HAS_INCLUDE(<version>)
28 // Checking FMT_CPLUSPLUS for warning suppression in MSVC.
29 #if FMT_CPLUSPLUS >= 201703L
30 # if FMT_HAS_INCLUDE(<filesystem>)
31 # include <filesystem>
33 # if FMT_HAS_INCLUDE(<variant>)
36 # if FMT_HAS_INCLUDE(<optional>)
41 // GCC 4 does not support FMT_HAS_INCLUDE.
42 #if FMT_HAS_INCLUDE(<cxxabi.h>) || defined(__GLIBCXX__)
44 // Android NDK with gabi++ library on some architectures does not implement
45 // abi::__cxa_demangle().
46 # ifndef __GABIXX_CXXABI_H__
47 # define FMT_HAS_ABI_CXA_DEMANGLE
51 // Check if typeid is available.
52 #ifndef FMT_USE_TYPEID
53 // __RTTI is for EDG compilers. In MSVC typeid is available without RTTI.
54 # if defined(__GXX_RTTI) || FMT_HAS_FEATURE(cxx_rtti) || FMT_MSC_VERSION || \
55 defined(__INTEL_RTTI__) || defined(__RTTI)
56 # define FMT_USE_TYPEID 1
58 # define FMT_USE_TYPEID 0
62 #ifdef __cpp_lib_filesystem
67 template <typename Char
> auto get_path_string(const std::filesystem::path
& p
) {
68 return p
.string
<Char
>();
71 template <typename Char
>
72 void write_escaped_path(basic_memory_buffer
<Char
>& quoted
,
73 const std::filesystem::path
& p
) {
74 write_escaped_string
<Char
>(std::back_inserter(quoted
), p
.string
<Char
>());
79 inline auto get_path_string
<char>(const std::filesystem::path
& p
) {
80 return to_utf8
<wchar_t>(p
.native(), to_utf8_error_policy::replace
);
84 inline void write_escaped_path
<char>(memory_buffer
& quoted
,
85 const std::filesystem::path
& p
) {
86 auto buf
= basic_memory_buffer
<wchar_t>();
87 write_escaped_string
<wchar_t>(std::back_inserter(buf
), p
.native());
88 bool valid
= to_utf8
<wchar_t>::convert(quoted
, {buf
.data(), buf
.size()});
89 FMT_ASSERT(valid
, "invalid utf16");
94 inline void write_escaped_path
<std::filesystem::path::value_type
>(
95 basic_memory_buffer
<std::filesystem::path::value_type
>& quoted
,
96 const std::filesystem::path
& p
) {
97 write_escaped_string
<std::filesystem::path::value_type
>(
98 std::back_inserter(quoted
), p
.native());
101 } // namespace detail
104 template <typename Char
> struct formatter
<std::filesystem::path
, Char
> {
106 format_specs
<Char
> specs_
;
107 detail::arg_ref
<Char
> width_ref_
;
111 FMT_CONSTEXPR
void set_debug_format(bool set
= true) { debug_
= set
; }
113 template <typename ParseContext
> FMT_CONSTEXPR
auto parse(ParseContext
& ctx
) {
114 auto it
= ctx
.begin(), end
= ctx
.end();
115 if (it
== end
) return it
;
117 it
= detail::parse_align(it
, end
, specs_
);
118 if (it
== end
) return it
;
120 it
= detail::parse_dynamic_spec(it
, end
, specs_
.width
, width_ref_
, ctx
);
121 if (it
!= end
&& *it
== '?') {
128 template <typename FormatContext
>
129 auto format(const std::filesystem::path
& p
, FormatContext
& ctx
) const {
131 detail::handle_dynamic_spec
<detail::width_checker
>(specs
.width
, width_ref_
,
134 auto s
= detail::get_path_string
<Char
>(p
);
135 return detail::write(ctx
.out(), basic_string_view
<Char
>(s
), specs
);
137 auto quoted
= basic_memory_buffer
<Char
>();
138 detail::write_escaped_path(quoted
, p
);
139 return detail::write(ctx
.out(),
140 basic_string_view
<Char
>(quoted
.data(), quoted
.size()),
149 template <typename Char
>
150 struct formatter
<std::thread::id
, Char
> : basic_ostream_formatter
<Char
> {};
153 #ifdef __cpp_lib_optional
156 template <typename T
, typename Char
>
157 struct formatter
<std::optional
<T
>, Char
,
158 std::enable_if_t
<is_formattable
<T
, Char
>::value
>> {
160 formatter
<T
, Char
> underlying_
;
161 static constexpr basic_string_view
<Char
> optional
=
162 detail::string_literal
<Char
, 'o', 'p', 't', 'i', 'o', 'n', 'a', 'l',
164 static constexpr basic_string_view
<Char
> none
=
165 detail::string_literal
<Char
, 'n', 'o', 'n', 'e'>{};
168 FMT_CONSTEXPR
static auto maybe_set_debug_format(U
& u
, bool set
)
169 -> decltype(u
.set_debug_format(set
)) {
170 u
.set_debug_format(set
);
174 FMT_CONSTEXPR
static void maybe_set_debug_format(U
&, ...) {}
177 template <typename ParseContext
> FMT_CONSTEXPR
auto parse(ParseContext
& ctx
) {
178 maybe_set_debug_format(underlying_
, true);
179 return underlying_
.parse(ctx
);
182 template <typename FormatContext
>
183 auto format(std::optional
<T
> const& opt
, FormatContext
& ctx
) const
184 -> decltype(ctx
.out()) {
185 if (!opt
) return detail::write
<Char
>(ctx
.out(), none
);
187 auto out
= ctx
.out();
188 out
= detail::write
<Char
>(out
, optional
);
190 out
= underlying_
.format(*opt
, ctx
);
191 return detail::write(out
, ')');
195 #endif // __cpp_lib_optional
197 #ifdef __cpp_lib_variant
201 template <typename T
>
202 using variant_index_sequence
=
203 std::make_index_sequence
<std::variant_size
<T
>::value
>;
205 template <typename
> struct is_variant_like_
: std::false_type
{};
206 template <typename
... Types
>
207 struct is_variant_like_
<std::variant
<Types
...>> : std::true_type
{};
209 // formattable element check.
210 template <typename T
, typename C
> class is_variant_formattable_
{
211 template <std::size_t... Is
>
212 static std::conjunction
<
213 is_formattable
<std::variant_alternative_t
<Is
, T
>, C
>...>
214 check(std::index_sequence
<Is
...>);
217 static constexpr const bool value
=
218 decltype(check(variant_index_sequence
<T
>{}))::value
;
221 template <typename Char
, typename OutputIt
, typename T
>
222 auto write_variant_alternative(OutputIt out
, const T
& v
) -> OutputIt
{
223 if constexpr (is_string
<T
>::value
)
224 return write_escaped_string
<Char
>(out
, detail::to_string_view(v
));
225 else if constexpr (std::is_same_v
<T
, Char
>)
226 return write_escaped_char(out
, v
);
228 return write
<Char
>(out
, v
);
231 } // namespace detail
233 template <typename T
> struct is_variant_like
{
234 static constexpr const bool value
= detail::is_variant_like_
<T
>::value
;
237 template <typename T
, typename C
> struct is_variant_formattable
{
238 static constexpr const bool value
=
239 detail::is_variant_formattable_
<T
, C
>::value
;
243 template <typename Char
> struct formatter
<std::monostate
, Char
> {
244 template <typename ParseContext
>
245 FMT_CONSTEXPR
auto parse(ParseContext
& ctx
) -> decltype(ctx
.begin()) {
249 template <typename FormatContext
>
250 auto format(const std::monostate
&, FormatContext
& ctx
) const
251 -> decltype(ctx
.out()) {
252 return detail::write
<Char
>(ctx
.out(), "monostate");
257 template <typename Variant
, typename Char
>
260 std::enable_if_t
<std::conjunction_v
<
261 is_variant_like
<Variant
>, is_variant_formattable
<Variant
, Char
>>>> {
262 template <typename ParseContext
>
263 FMT_CONSTEXPR
auto parse(ParseContext
& ctx
) -> decltype(ctx
.begin()) {
267 template <typename FormatContext
>
268 auto format(const Variant
& value
, FormatContext
& ctx
) const
269 -> decltype(ctx
.out()) {
270 auto out
= ctx
.out();
272 out
= detail::write
<Char
>(out
, "variant(");
276 out
= detail::write_variant_alternative
<Char
>(out
, v
);
280 FMT_CATCH(const std::bad_variant_access
&) {
281 detail::write
<Char
>(out
, "valueless by exception");
288 #endif // __cpp_lib_variant
292 template <typename Char
> struct formatter
<std::error_code
, Char
> {
293 template <typename ParseContext
>
294 FMT_CONSTEXPR
auto parse(ParseContext
& ctx
) -> decltype(ctx
.begin()) {
298 template <typename FormatContext
>
299 FMT_CONSTEXPR
auto format(const std::error_code
& ec
, FormatContext
& ctx
) const
300 -> decltype(ctx
.out()) {
301 auto out
= ctx
.out();
302 out
= detail::write_bytes(out
, ec
.category().name(), format_specs
<Char
>());
303 out
= detail::write
<Char
>(out
, Char(':'));
304 out
= detail::write
<Char
>(out
, ec
.value());
310 template <typename T
, typename Char
>
313 typename
std::enable_if
<std::is_base_of
<std::exception
, T
>::value
>::type
> {
315 bool with_typename_
= false;
318 FMT_CONSTEXPR
auto parse(basic_format_parse_context
<Char
>& ctx
)
319 -> decltype(ctx
.begin()) {
320 auto it
= ctx
.begin();
321 auto end
= ctx
.end();
322 if (it
== end
|| *it
== '}') return it
;
325 with_typename_
= FMT_USE_TYPEID
!= 0;
330 template <typename OutputIt
>
331 auto format(const std::exception
& ex
,
332 basic_format_context
<OutputIt
, Char
>& ctx
) const -> OutputIt
{
333 format_specs
<Char
> spec
;
334 auto out
= ctx
.out();
336 return detail::write_bytes(out
, string_view(ex
.what()), spec
);
339 const std::type_info
& ti
= typeid(ex
);
340 # ifdef FMT_HAS_ABI_CXA_DEMANGLE
342 std::size_t size
= 0;
343 std::unique_ptr
<char, decltype(&std::free
)> demangled_name_ptr(
344 abi::__cxa_demangle(ti
.name(), nullptr, &size
, &status
), &std::free
);
346 string_view demangled_name_view
;
347 if (demangled_name_ptr
) {
348 demangled_name_view
= demangled_name_ptr
.get();
350 // Normalization of stdlib inline namespace names.
351 // libc++ inline namespaces.
352 // std::__1::* -> std::*
353 // std::__1::__fs::* -> std::*
354 // libstdc++ inline namespaces.
355 // std::__cxx11::* -> std::*
356 // std::filesystem::__cxx11::* -> std::filesystem::*
357 if (demangled_name_view
.starts_with("std::")) {
358 char* begin
= demangled_name_ptr
.get();
359 char* to
= begin
+ 5; // std::
360 for (char *from
= to
, *end
= begin
+ demangled_name_view
.size();
362 // This is safe, because demangled_name is NUL-terminated.
363 if (from
[0] == '_' && from
[1] == '_') {
364 char* next
= from
+ 1;
365 while (next
< end
&& *next
!= ':') next
++;
366 if (next
[0] == ':' && next
[1] == ':') {
373 demangled_name_view
= {begin
, detail::to_unsigned(to
- begin
)};
376 demangled_name_view
= string_view(ti
.name());
378 out
= detail::write_bytes(out
, demangled_name_view
, spec
);
379 # elif FMT_MSC_VERSION
380 string_view
demangled_name_view(ti
.name());
381 if (demangled_name_view
.starts_with("class "))
382 demangled_name_view
.remove_prefix(6);
383 else if (demangled_name_view
.starts_with("struct "))
384 demangled_name_view
.remove_prefix(7);
385 out
= detail::write_bytes(out
, demangled_name_view
, spec
);
387 out
= detail::write_bytes(out
, string_view(ti
.name()), spec
);
391 return detail::write_bytes(out
, string_view(ex
.what()), spec
);
398 template <typename T
, typename Enable
= void>
399 struct has_flip
: std::false_type
{};
401 template <typename T
>
402 struct has_flip
<T
, void_t
<decltype(std::declval
<T
>().flip())>>
405 template <typename T
> struct is_bit_reference_like
{
406 static constexpr const bool value
=
407 std::is_convertible
<T
, bool>::value
&&
408 std::is_nothrow_assignable
<T
, bool>::value
&& has_flip
<T
>::value
;
411 #ifdef _LIBCPP_VERSION
413 // Workaround for libc++ incompatibility with C++ standard.
414 // According to the Standard, `bitset::operator[] const` returns bool.
415 template <typename C
>
416 struct is_bit_reference_like
<std::__bit_const_reference
<C
>> {
417 static constexpr const bool value
= true;
422 } // namespace detail
424 // We can't use std::vector<bool, Allocator>::reference and
425 // std::bitset<N>::reference because the compiler can't deduce Allocator and N
426 // in partial specialization.
428 template <typename BitRef
, typename Char
>
429 struct formatter
<BitRef
, Char
,
430 enable_if_t
<detail::is_bit_reference_like
<BitRef
>::value
>>
431 : formatter
<bool, Char
> {
432 template <typename FormatContext
>
433 FMT_CONSTEXPR
auto format(const BitRef
& v
, FormatContext
& ctx
) const
434 -> decltype(ctx
.out()) {
435 return formatter
<bool, Char
>::format(v
, ctx
);
440 template <typename T
, typename Char
>
441 struct formatter
<std::atomic
<T
>, Char
,
442 enable_if_t
<is_formattable
<T
, Char
>::value
>>
443 : formatter
<T
, Char
> {
444 template <typename FormatContext
>
445 auto format(const std::atomic
<T
>& v
, FormatContext
& ctx
) const
446 -> decltype(ctx
.out()) {
447 return formatter
<T
, Char
>::format(v
.load(), ctx
);
451 #ifdef __cpp_lib_atomic_flag_test
453 template <typename Char
>
454 struct formatter
<std::atomic_flag
, Char
>
455 : formatter
<bool, Char
> {
456 template <typename FormatContext
>
457 auto format(const std::atomic_flag
& v
, FormatContext
& ctx
) const
458 -> decltype(ctx
.out()) {
459 return formatter
<bool, Char
>::format(v
.test(), ctx
);
462 #endif // __cpp_lib_atomic_flag_test