dynarmic/include/fmt/core.h
Merry 6633089a44 Squashed 'externals/fmt/' changes from 9e8b86fd2..b6f4ceaed
b6f4ceaed Update version
15f812dae Update changelog
6884aab49 Update changelog
88ec4e706 Bump version
dd3d2490e Update changelog
739055ae7 Fix apidocs
dbbd711f4 Suppress a warning
98cbb6a43 Fix ABI compatiblity issue
214cf13f1 Fix endianness bug in write_digit2_separated (#2699)
17a5c808d Restore FMT_API on error_handler::on_error() (#2696)
fc1783fcc Avoid undefined symbols with mingw-w64 (#2692)
1b193e7b3 Deprecate more
8e59744b8 Switch to new github auth mechanism
7081a6aa3 Update version
64dc8fbad Bump version
fc8e3de7d Fix manage.py script
57bee9fcd Fix formating
dce52e491 Update changelog
9405a4724 Update changelog
495b8bf12 Update changelog
e221166fa Update changelog
035cab8da Update changelog
89c6ed12b Clarify in comments (for now) deprecated map functions
e462da828 Add some noexcept (#2684)
79c66d66b Update changelog
5d37f705f Update changelog
6bb370cec Update changelog
bb6920157 Fix tuple join
4fac7daae Cleanup bit_cast
3617c2795 Update changelog
9c0c1bcdb Simplify tuple formatting
187e8db1b Update changelog
c7f88180f add tests for format string compile-time checks
8a2c3fb88 add reverse tests to compile-error-test
1164eda5a disable compile-error-test on Windows
4482f6f1f rewrite compile-error-test to use non-header-only library
796662a61 Escape range items convertible to std::string_view
33ee4cc51 Improve noexception test
3bbf2c673 Fix throw with exceptions disabled
074c9c52e Update changelog
3110ec5a2 Update changelog
3014b3d77 Clarify that C strings must be null-terminated
eab2ea9fc Replace an assert with an exception
21ed92a6e Update changelog
04111dd1e Fix issue #2670 (#2671)
817788fbf remove incorrect C++20 check from test/CMakeLists.txt (#2663)
4511030af Minor code style tweaks for consistency
7812813a3 Don't explicitly delete copy ctor of dynamic_format_arg_store (#2664)
664cd6067 Remove std-format-test
784e2a7b4 Fix an overflow when formatting very large durations
fc2a376d8 Remove two expressions which had no effect (reported by LGTM)
c5aafd8f9 expose headers as SYSTEM depending on special configuration option
eaddd1e3c Fix handling of byte
2d4457758 Try fixing byte regression
e46392ea2 deprecate _format UDL in code using FMT_DEPRECATED
c882790a2 Add a set formatter
121002d70 Add a map formatter
be51ee1ce Disable broken copy ctor of dynamic_format_arg_store
659de779e Fix a UB in parse_format_specs when begin is null
51b14b6c0 remove commented out lines
223a0fa55 move gtest-specific check into gtest/CMakeLists.txt
ef72b471f enable named arguments check in compile-time checks (#2649)
82246b876 fix throw with exceptions disabled (#2647)
35f60377a Update ChangeLog.rst
3a951a66c Avoid qualifying by inline namespace. Fixes #2642. (#2643)
e0136fc8b Qualify calls to make_wformat_args. Fixes #2639. (#2641)
ac1b5f3da Refactor problematic trailing returns in arg_mapper
fd62fba98 Don't convert scoped enums to integers
c652f8243 Make header guard consistent with header name
a9c7b9b8f Clarify that _format is deprecated
e4f0564aa Disable is_streamable for string[_view]
91533d3c3 Minor tweaks to chrono subsecond formatting
0bbc9708f Implement c++20 std::chrono::duration subsecond formatting (#2623)
9d5b9defd Enable tzset only on Windows desktop app (#2633)
215f21a03 Detect overflow on large precision
c240d98ff Optimize tm formatting (Non C-locales and %Z) (#2617)
6ab73113f Mark grow as FMT_CONSTEXPR20 (#2630)
713c7c7c6 Cleanup os.cc
9b1807a8a fix int -> uint warning (#2611)
ec3b097cb [doc] FMT_STRING supports C++14 and no-op in C++11 (#2620)
c472a2781 Fix handling of very large precision in fixed format
201971e29 Make MSVC use [[nodiscard]] (#2615)
acad8cfab Reformat all source code; no functional changes
491ba2dda Annotate fmt::format and fmt::formatted_size as [[nodiscard]]
5abe9e826 Add platform-specific 'z' formatter
be3a3a5ae Use predefined formats for C-locale
a3ab36c80 Formatting of function pointers, member function pointers, member object pointers... (#2610)
19cac63fe Broken link in README.rst
43419a4ad Workaround a bug in gcc
c089f7d49 Simplify std::tm formatter
aa5517f6b Reuse tm_writer in chrono_formatter
50140be7a Reuse tm_writer in weekday formatter
8b8945499 Improve consistency
5380ff4d8 Detect types convertible to unformattable pointers
094b66e81 changed locale retrieval way to a fancy one
b69ae4854 Reorder classes (#2591)
0b843af56 sped up chrono.h formatting for cases without providing locale (#2576)
12b1d8b14 Fix precision 0 with std::chrono::duration and added additional tests. (#2588)
e67f92c55 Cleanup warnings with nvhpc/21.9. (#2582)
812733cc9 const qualify format function for systen_clock
028f22775 Handle implicit conversions in write
5b0aa638c Minor grammar fix
6eaceb5f7 Fix incompatible between docutils 1.18.0 and sphinx 3.3.0 (#2575)
0697c5edb FMT_USE_FCNTL can be predefined (#2573)
1031eedf2 Replacing strftime with std::time_put (#2550)
90034e4c4 Add FMT_ASSERT and validation of values of struct tm members (#2564)
df40e9467 Upgrade `module-test` to msvc 16.11.5 and 17.0-pre5 (#2558)
e6d5059cb Simplify js tag in basic-bootstrap theme (#2562)
3b6e409cd Enable `consteval` for msvc 17.0-pre5 (#2559)
249f03bbb do not detect LLVM based IBMXL compiler (on ppc) as clang (#2555)
7463c8320 Fix overflow for very bigger years (>2*10^9) (#2551)
1266c2b60 Fix handling of exotic character types
684e2fdc9 Minor cleanup
a1d586302 Minor cleanup
7a604cdd9 Cleanup
aeb54b0dd Fix bug on '%Y' and '%C' formats with negative years Requested changes
f88c020fc Generalization of strftime/wcsftime function calls in tests
2eeddba75 Renaming, splitting of functions
275454608 Fix errors in ISO week-base-year formatter
218cecb6d Fix error in test
e9f4453b0 Fix Microsoft Visual Studio 14.0 build
27c3674ce Improve performance
5dc3dd3d4 New tests
f8542cd98 Unified formatters for std::chrono::time_point<std::chrono::system_clock, Duration> and std::tm
4707373d3 Fix year formatter
79c00ad8f Improve ISO week-base-year formatter
fbaaa5906 Improve week of the year formatter
cde44ddb7 Improve year formatter
b04601b91 Switch from std::strftime/std::wcsftime to internal implementation for locale independent formats
d3d30a46f New tests
7911d8d3f Add format spec checker
fbbfc3b03 Reorder formatters
509eac957 Workarounds for implementation-defined std::strftime behavior
85b38190d New tests for all C++11 std::strftime format specifiers
7aca36bca Extending fmt::join to support C++20-only ranges. (#2549)
f5371a75f locale.h -> format.h
febdef43f fix: add workaround for intel parameter pack bug
f56756986 fix: check to make sure both 'if constexpr' and return type deduction are available
dcd282bb2 Namespace qualify calls to get
9c14474d3 Include `<bit>` when using `std::bit_cast`
1e96e0176 Fix compiler flag check (#2540)
7e4bc9451 Speeding up write_significand() (#2499)
26c1ca4c3 Replaced default spec with equivalent one, which is potentially more optimizable (#2537)
1e865b353 Fix docs
4a85db1ce Change default open mode to -rw-r--r-- (#2530)
0a985fd4c Move size_ initialization to initializer list (#2529)
012cc709d Workaround gcc _Pragma bug 59884
d6590e3bd Fix compiler check
134aec40f Fix search in docs
48a476ae0 Update example (#2522)
023c2018f Don't use strlen in constexpr
800d4c8ac Refactor Windows workarounds
32865aeaa changed detection of Intel Compiler Classic to distinguish MS-Windows (#2510)
7b339795a Describe a better approach of how to use {fmt} as a depency in a Bazel project (#2516)
ae9bbe116 Suppress warning C4127 in chrono.h (conditional expression is constant) (#2518)
927dbd134 Misplaced comma in README.rst (#2515)
2a9a77dd8 Remove misplaced comment
1aee4bc90 Refactor FP formatting
e1bd6cc91 Refactor FP formatting
027fcaf05 Replace use_grisu with fallback since Grisu is only one of multiple implemented algorithms
716d69f27 Refactor FP formatting
ff7e73af6 Always run grisu_gen_digits before fallback_format
2976e31ac Refactor format_float
807ee5ec3 Disable consteval in Apple clang
d9a731d48 Add basic support for Bazel (#2505)
9c57357e0 Add `static` to a table (#2509)
2742611ca Fix formatting
5092b198b Document group_digits
b4d9d82e1 make FP formatting available to be used at compile-time (#2426)
d9fd695ac Fix wchar_t tm formatting
92614ecbf Optimize %T in tm formatting
aaeca12d8 Move FMT_MAYBE_UNUSED to format.h where it is used
3d0c7ae38 Move data to format.cc
04e3a79f7 Use memcpy in more cases in copy2
e47e99bb0 Simplify format_decimal (#2498)
9b6b0e403 Remove data
4d1c6034e Deprecate basic_data
a3348eccd Deprecate most of basic_data
3a0448148 Remove data::hex_digits
ad77331c0 Move log10_2_significand to format-inl.h
d9ebc4e82 Add a function to get sign char
c00eb4f4c Add missing inline
25af02f21 positive -> nonnegative (#2493)
67cb2dad3 Optimize %F in tm formatting
1aa98f8b9 Eliminate double copying in vformat_to_n (#2489)
a58c13382 Improve code_point_length codegen on older gcc
aeee70a81 Remove unnecessary cast
c771ba361 Fix build for the clang-10 / libstdc++-9 couple (#2491)
ab6e2272c Clarify shifts encoding
e4728409e Use throw_format_error in more places to reduce bloat
e3ebf366a Inline padding shifts
894faf3fe Refactor presentation types
4eb97fa4e Reduce code bloat
6b55c8325 is_const_formattable -> has_const_formatter
2fe94ad7e Make specifiers support in tuple_join an opt-in
3940de595 thousands -> group_digits
c4d0f96a6 Implement format specs in fmt::thousands
3b9c44268 Implement thousands separators without locales
08f98c7fa Simplify get_arg_index_by_name
a151f955a Remove FMT_OVERRIDE
42a225cbd Remove redundand final
bf20d1990 Simplify the core API
fc0884037 Move FMT_GCC_VISIBILITY_HIDDEN to format.h
1aeed2dbc Require inline namespaces
799bea473 Remove FMT_HAS_GXX_CXX11
60cd5ea3f Add support for more formattable types in ranges
4fd9a00f3 Simplify ostream interface
568156389 Cleanup ostream interface
20931baf1 Disable fallback_formatter for arrays
d58d19ba3 Fix an odr violation in ranges.h (#2483)
ee0659f8b Fix formatting of abstract classes via ostream
8029bf955 Fix copy_str performance (#2477)
2520f410c Workaround for #2478 (#2482)
ee63f5f04 Workaround to MSVC bug (#2474) (#2476)
1aaf72fb6 Add an example to fmt::runtime
c1313c205 Clarify that format_to[_n] do not append a terminating null
cb0f177c3 Improve docs
71677e520 Improve docs
4db572352 add fuzzers for chrono timepoint and localtime,gmtime (#2469)
dc7f3ef2b Fix header name
419ba86a9 Improve docs
6a5b4d5fa Document format_string
2599163b8 Document format_string
8ef22f774 Update docs
c0c4d1ada Update docs
729a44e67 Depreate strtod and remove problematic tests
74c111896 Apply force inline
596508a92 Cleanup
043e3b342 Remove static_assert from arg_mapper
8b0cb944d Fix error reporting when mixing character types
117fc6707 CI: replace g++ C++20 build to test FP formatting at compile-time
c79a3841e make detail::fp and detail::bigit constexpr
5888de9f3 make detail::make_checked() constexpr
04b4b69b1 make detail::bit_cast() constexpr with C++20
fd34a3d24 make detail::basic_memory_buffer constexpr with C++20
6d597e39c Fix overload ambiguity in arg_mapper
b9ce56d93 Improve comments
f889e52a1 Improve error reporting
34caecd6b Use consistent initialization style
a44c8f651 reimplement `formatter<tuple_join_view>` (#2457)
4b8bda25c Fix 2462
6b5e6119e set clang in one place
7af1dc1d2 fix UB in fuzzer common (memcpy on nullptr)
e77686f7a clang format
2207ea0b3 More escaping
a212ff757 Escape invalid code points
a76031e11 check -> is_printable
a7f280765 Improve naming
07d033ecb Fix is_printable
cdb4299ac Add Unicode support to is_printable
7df2c82a8 Rewrite printable.py codegen to emit C++
6cf90d7ce Add script license and fix python version
2f1ad8ed3 Add printable codegen from Rust
371d8e2ee Escape Unicode
6397095ca More escaping
f69a57253 Don't overescape wide strings
11b07a56b We should escape
b559cfd4c Implement basic escaping
11d49491c Handle global locale
6ea6cf946 Add decimal separator support to float
9730a2af0 Update ChangeLog.rst
c2ed5f686 Update ChangeLog.rst
7b66e72e2 Use builtin intrinsics on intel (#2450)
d57b2a652 Suppress a warning
bdfbd794e Cleanup begin/end usage
111de881f Don't copy non-const-iterable ranges
d6e882ed8 Undo the move because the doc is not a GH template
f488eed10 Resolve default constructor error in Xcode 7.2.1 and 8.2.1
652c3653b Move CONTRIBUTING.md to .github
fb19faa31 Improves README with svg badge (#2446)
07211701f Disable the -Wstringop-overflow warning from GCC 7 (#2442)
bba0a9d96 Make flush public
f1794a885 Switch to threadsafe death test style
0544a2279 Exclude fallback functions when FMT_BUILTIN_CLZ(LL) is not defined (#2434)
5c222f056 Add support for nonconst formattable types
3def950b8 Set FMT_CAN_MODULE=OFF for MSVC 19.29.30035+
63fe2d5bd Add copy constructor for dynamic_format_arg_store, and test
561834650 Improve digit count
f20f50368 Replace `throw` with `FMT_THROW` (#2427)
00235d8a9 fix module test odr violations (#2414)
2038bf618 Update format_to usage
e41ac1f87 Don't use deprecated API in docs
8465869d7 Move ignore_unused to detail
3d53d1539 Warning removals in test code (#2399)
20e4ef8b4 Pass significand_size by value
c4a3c2342 Refactor locale handling
7a0d30175 Update README.rst
f2b03facd Include test sources to pick up functions and classes from the module rather than from the non-modular library which is baked into the `test-main` library. (#2356)
02ad5e11d Add faint, blink, reverse and conceal to the emphases (#2394)
d141cdbeb Update version
cfc05e05f Bump version
8ea312633 Update changelog
e461f3dbb Minor consitency and comment tweaks
54014e42e silence warning C4100 on MSVC 2019 when exceptions are disabled (#2397)
3e7a29cc9 Workaround clang/gcc incompatibility
00a57a9f8 Update changelog
1d7384530 Add missing presentation type checks for std::string (#2402)
889bbf27a Fix missing std::get overload in MSVC (#2407)
5f8473914 Remove outdated apidoc
785908ee3 Fix warnings
fbb70eec5 suppress unused variable warnings (#2381)
002bb759f Remove unneeded `num_result_bigits` decrement
a3f762c5a [doc] Minor: fix ``code``.
c3c27e5ab Fix MSVC warning C4819
c6b1f181a Fix docs
94564b058 Fix docs
0fc73a2a8 Merge branch 'master' of github.com:fmtlib/fmt
3156fcf5f Switch to older breathe version
f85fb9fdf Adjust definition for FMT_HAS_INCLUDE
0bc3d664e Fix docs
e5c46e13e Fix docs: breathe 18 and earlier corrupts trailing return type
49a3b58c8 Specify size for static data arrays
d0c8d45a2 apt update before install
c9a10631c format: do not use udl_{arg,formatter} return types when UDL is not in use
3bd806f12 Eliminate intel compiler warning
fd16bcb20 Fix bug in cmake join function
5221242f6 Instruct msvc to report the _true_ value in `__cplusplus` and force _full_ C++ conformance
31a5f0d39 Bump version in inline namespace
102a4d492 Bump version in inline namespace
f68508b6c Update ChangeLog.rst

git-subtree-dir: externals/fmt
git-subtree-split: b6f4ceaed0a0a24ccf575fab6c56dd50ccf6f1a9
2022-02-15 11:15:34 +00:00

3236 lines
105 KiB
C++

// Formatting library for C++ - the core API for char/UTF-8
//
// Copyright (c) 2012 - present, Victor Zverovich
// All rights reserved.
//
// For the license information refer to format.h.
#ifndef FMT_CORE_H_
#define FMT_CORE_H_
#include <cstddef> // std::byte
#include <cstdio> // std::FILE
#include <cstring>
#include <iterator>
#include <limits>
#include <string>
#include <type_traits>
// The fmt library version in the form major * 10000 + minor * 100 + patch.
#define FMT_VERSION 80101
#if defined(__clang__) && !defined(__ibmxl__)
# define FMT_CLANG_VERSION (__clang_major__ * 100 + __clang_minor__)
#else
# define FMT_CLANG_VERSION 0
#endif
#if defined(__GNUC__) && !defined(__clang__) && !defined(__INTEL_COMPILER) && \
!defined(__NVCOMPILER)
# define FMT_GCC_VERSION (__GNUC__ * 100 + __GNUC_MINOR__)
#else
# define FMT_GCC_VERSION 0
#endif
#ifndef FMT_GCC_PRAGMA
// Workaround _Pragma bug https://gcc.gnu.org/bugzilla/show_bug.cgi?id=59884.
# if FMT_GCC_VERSION >= 504
# define FMT_GCC_PRAGMA(arg) _Pragma(arg)
# else
# define FMT_GCC_PRAGMA(arg)
# endif
#endif
#ifdef __ICL
# define FMT_ICC_VERSION __ICL
#elif defined(__INTEL_COMPILER)
# define FMT_ICC_VERSION __INTEL_COMPILER
#else
# define FMT_ICC_VERSION 0
#endif
#ifdef __NVCC__
# define FMT_NVCC __NVCC__
#else
# define FMT_NVCC 0
#endif
#ifdef _MSC_VER
# define FMT_MSC_VER _MSC_VER
# define FMT_MSC_WARNING(...) __pragma(warning(__VA_ARGS__))
#else
# define FMT_MSC_VER 0
# define FMT_MSC_WARNING(...)
#endif
#ifdef __has_feature
# define FMT_HAS_FEATURE(x) __has_feature(x)
#else
# define FMT_HAS_FEATURE(x) 0
#endif
#if defined(__has_include) && \
(!defined(__INTELLISENSE__) || FMT_MSC_VER > 1900) && \
(!FMT_ICC_VERSION || FMT_ICC_VERSION >= 1600)
# define FMT_HAS_INCLUDE(x) __has_include(x)
#else
# define FMT_HAS_INCLUDE(x) 0
#endif
#ifdef __has_cpp_attribute
# define FMT_HAS_CPP_ATTRIBUTE(x) __has_cpp_attribute(x)
#else
# define FMT_HAS_CPP_ATTRIBUTE(x) 0
#endif
#ifdef _MSVC_LANG
# define FMT_CPLUSPLUS _MSVC_LANG
#else
# define FMT_CPLUSPLUS __cplusplus
#endif
#define FMT_HAS_CPP14_ATTRIBUTE(attribute) \
(FMT_CPLUSPLUS >= 201402L && FMT_HAS_CPP_ATTRIBUTE(attribute))
#define FMT_HAS_CPP17_ATTRIBUTE(attribute) \
(FMT_CPLUSPLUS >= 201703L && FMT_HAS_CPP_ATTRIBUTE(attribute))
// Check if relaxed C++14 constexpr is supported.
// GCC doesn't allow throw in constexpr until version 6 (bug 67371).
#ifndef FMT_USE_CONSTEXPR
# define FMT_USE_CONSTEXPR \
(FMT_HAS_FEATURE(cxx_relaxed_constexpr) || FMT_MSC_VER >= 1912 || \
(FMT_GCC_VERSION >= 600 && __cplusplus >= 201402L)) && \
!FMT_NVCC && !FMT_ICC_VERSION
#endif
#if FMT_USE_CONSTEXPR
# define FMT_CONSTEXPR constexpr
# define FMT_CONSTEXPR_DECL constexpr
#else
# define FMT_CONSTEXPR
# define FMT_CONSTEXPR_DECL
#endif
#if ((__cplusplus >= 202002L) && \
(!defined(_GLIBCXX_RELEASE) || _GLIBCXX_RELEASE > 9)) || \
(__cplusplus >= 201709L && FMT_GCC_VERSION >= 1002)
# define FMT_CONSTEXPR20 constexpr
#else
# define FMT_CONSTEXPR20
#endif
// Check if constexpr std::char_traits<>::compare,length is supported.
#if defined(__GLIBCXX__)
# if __cplusplus >= 201703L && defined(_GLIBCXX_RELEASE) && \
_GLIBCXX_RELEASE >= 7 // GCC 7+ libstdc++ has _GLIBCXX_RELEASE.
# define FMT_CONSTEXPR_CHAR_TRAITS constexpr
# endif
#elif defined(_LIBCPP_VERSION) && __cplusplus >= 201703L && \
_LIBCPP_VERSION >= 4000
# define FMT_CONSTEXPR_CHAR_TRAITS constexpr
#elif FMT_MSC_VER >= 1914 && _MSVC_LANG >= 201703L
# define FMT_CONSTEXPR_CHAR_TRAITS constexpr
#endif
#ifndef FMT_CONSTEXPR_CHAR_TRAITS
# define FMT_CONSTEXPR_CHAR_TRAITS
#endif
// Check if exceptions are disabled.
#ifndef FMT_EXCEPTIONS
# if (defined(__GNUC__) && !defined(__EXCEPTIONS)) || \
FMT_MSC_VER && !_HAS_EXCEPTIONS
# define FMT_EXCEPTIONS 0
# else
# define FMT_EXCEPTIONS 1
# endif
#endif
// Define FMT_USE_NOEXCEPT to make fmt use noexcept (C++11 feature).
#ifndef FMT_USE_NOEXCEPT
# define FMT_USE_NOEXCEPT 0
#endif
#if FMT_USE_NOEXCEPT || FMT_HAS_FEATURE(cxx_noexcept) || \
FMT_GCC_VERSION >= 408 || FMT_MSC_VER >= 1900
# define FMT_DETECTED_NOEXCEPT noexcept
# define FMT_HAS_CXX11_NOEXCEPT 1
#else
# define FMT_DETECTED_NOEXCEPT throw()
# define FMT_HAS_CXX11_NOEXCEPT 0
#endif
#ifndef FMT_NOEXCEPT
# if FMT_EXCEPTIONS || FMT_HAS_CXX11_NOEXCEPT
# define FMT_NOEXCEPT FMT_DETECTED_NOEXCEPT
# else
# define FMT_NOEXCEPT
# endif
#endif
// [[noreturn]] is disabled on MSVC and NVCC because of bogus unreachable code
// warnings.
#if FMT_EXCEPTIONS && FMT_HAS_CPP_ATTRIBUTE(noreturn) && !FMT_MSC_VER && \
!FMT_NVCC
# define FMT_NORETURN [[noreturn]]
#else
# define FMT_NORETURN
#endif
#if __cplusplus == 201103L || __cplusplus == 201402L
# if defined(__INTEL_COMPILER) || defined(__PGI)
# define FMT_FALLTHROUGH
# elif defined(__clang__)
# define FMT_FALLTHROUGH [[clang::fallthrough]]
# elif FMT_GCC_VERSION >= 700 && \
(!defined(__EDG_VERSION__) || __EDG_VERSION__ >= 520)
# define FMT_FALLTHROUGH [[gnu::fallthrough]]
# else
# define FMT_FALLTHROUGH
# endif
#elif FMT_HAS_CPP17_ATTRIBUTE(fallthrough)
# define FMT_FALLTHROUGH [[fallthrough]]
#else
# define FMT_FALLTHROUGH
#endif
#ifndef FMT_NODISCARD
# if FMT_HAS_CPP17_ATTRIBUTE(nodiscard)
# define FMT_NODISCARD [[nodiscard]]
# else
# define FMT_NODISCARD
# endif
#endif
#ifndef FMT_USE_FLOAT
# define FMT_USE_FLOAT 1
#endif
#ifndef FMT_USE_DOUBLE
# define FMT_USE_DOUBLE 1
#endif
#ifndef FMT_USE_LONG_DOUBLE
# define FMT_USE_LONG_DOUBLE 1
#endif
#ifndef FMT_INLINE
# if FMT_GCC_VERSION || FMT_CLANG_VERSION
# define FMT_INLINE inline __attribute__((always_inline))
# else
# define FMT_INLINE inline
# endif
#endif
#ifndef FMT_DEPRECATED
# if FMT_HAS_CPP14_ATTRIBUTE(deprecated) || FMT_MSC_VER >= 1900
# define FMT_DEPRECATED [[deprecated]]
# else
# if (defined(__GNUC__) && !defined(__LCC__)) || defined(__clang__)
# define FMT_DEPRECATED __attribute__((deprecated))
# elif FMT_MSC_VER
# define FMT_DEPRECATED __declspec(deprecated)
# else
# define FMT_DEPRECATED /* deprecated */
# endif
# endif
#endif
#ifndef FMT_BEGIN_NAMESPACE
# define FMT_BEGIN_NAMESPACE \
namespace fmt { \
inline namespace v8 {
# define FMT_END_NAMESPACE \
} \
}
#endif
#ifndef FMT_MODULE_EXPORT
# define FMT_MODULE_EXPORT
# define FMT_MODULE_EXPORT_BEGIN
# define FMT_MODULE_EXPORT_END
# define FMT_BEGIN_DETAIL_NAMESPACE namespace detail {
# define FMT_END_DETAIL_NAMESPACE }
#endif
#if !defined(FMT_HEADER_ONLY) && defined(_WIN32)
# define FMT_CLASS_API FMT_MSC_WARNING(suppress : 4275)
# ifdef FMT_EXPORT
# define FMT_API __declspec(dllexport)
# elif defined(FMT_SHARED)
# define FMT_API __declspec(dllimport)
# endif
#else
# define FMT_CLASS_API
# if defined(FMT_EXPORT) || defined(FMT_SHARED)
# if defined(__GNUC__) || defined(__clang__)
# define FMT_API __attribute__((visibility("default")))
# endif
# endif
#endif
#ifndef FMT_API
# define FMT_API
#endif
// libc++ supports string_view in pre-c++17.
#if (FMT_HAS_INCLUDE(<string_view>) && \
(__cplusplus > 201402L || defined(_LIBCPP_VERSION))) || \
(defined(_MSVC_LANG) && _MSVC_LANG > 201402L && _MSC_VER >= 1910)
# include <string_view>
# define FMT_USE_STRING_VIEW
#elif FMT_HAS_INCLUDE("experimental/string_view") && __cplusplus >= 201402L
# include <experimental/string_view>
# define FMT_USE_EXPERIMENTAL_STRING_VIEW
#endif
#ifndef FMT_UNICODE
# define FMT_UNICODE !FMT_MSC_VER
#endif
#ifndef FMT_CONSTEVAL
# if ((FMT_GCC_VERSION >= 1000 || FMT_CLANG_VERSION >= 1101) && \
__cplusplus > 201703L && !defined(__apple_build_version__)) || \
(defined(__cpp_consteval) && \
(!FMT_MSC_VER || _MSC_FULL_VER >= 193030704))
// consteval is broken in MSVC before VS2022 and Apple clang 13.
# define FMT_CONSTEVAL consteval
# define FMT_HAS_CONSTEVAL
# else
# define FMT_CONSTEVAL
# endif
#endif
#ifndef FMT_USE_NONTYPE_TEMPLATE_PARAMETERS
# if defined(__cpp_nontype_template_args) && \
((FMT_GCC_VERSION >= 903 && __cplusplus >= 201709L) || \
__cpp_nontype_template_args >= 201911L)
# define FMT_USE_NONTYPE_TEMPLATE_PARAMETERS 1
# else
# define FMT_USE_NONTYPE_TEMPLATE_PARAMETERS 0
# endif
#endif
// Enable minimal optimizations for more compact code in debug mode.
FMT_GCC_PRAGMA("GCC push_options")
#ifndef __OPTIMIZE__
FMT_GCC_PRAGMA("GCC optimize(\"Og\")")
#endif
FMT_BEGIN_NAMESPACE
FMT_MODULE_EXPORT_BEGIN
// Implementations of enable_if_t and other metafunctions for older systems.
template <bool B, typename T = void>
using enable_if_t = typename std::enable_if<B, T>::type;
template <bool B, typename T, typename F>
using conditional_t = typename std::conditional<B, T, F>::type;
template <bool B> using bool_constant = std::integral_constant<bool, B>;
template <typename T>
using remove_reference_t = typename std::remove_reference<T>::type;
template <typename T>
using remove_const_t = typename std::remove_const<T>::type;
template <typename T>
using remove_cvref_t = typename std::remove_cv<remove_reference_t<T>>::type;
template <typename T> struct type_identity { using type = T; };
template <typename T> using type_identity_t = typename type_identity<T>::type;
struct monostate {
constexpr monostate() {}
};
// An enable_if helper to be used in template parameters which results in much
// shorter symbols: https://godbolt.org/z/sWw4vP. Extra parentheses are needed
// to workaround a bug in MSVC 2019 (see #1140 and #1186).
#ifdef FMT_DOC
# define FMT_ENABLE_IF(...)
#else
# define FMT_ENABLE_IF(...) enable_if_t<(__VA_ARGS__), int> = 0
#endif
FMT_BEGIN_DETAIL_NAMESPACE
// Suppress "unused variable" warnings with the method described in
// https://herbsutter.com/2009/10/18/mailbag-shutting-up-compiler-warnings/.
// (void)var does not work on many Intel compilers.
template <typename... T> FMT_CONSTEXPR void ignore_unused(const T&...) {}
constexpr FMT_INLINE auto is_constant_evaluated(bool default_value = false)
FMT_NOEXCEPT -> bool {
#ifdef __cpp_lib_is_constant_evaluated
ignore_unused(default_value);
return std::is_constant_evaluated();
#else
return default_value;
#endif
}
// A function to suppress "conditional expression is constant" warnings.
template <typename T> constexpr FMT_INLINE auto const_check(T value) -> T {
return value;
}
FMT_NORETURN FMT_API void assert_fail(const char* file, int line,
const char* message);
#ifndef FMT_ASSERT
# ifdef NDEBUG
// FMT_ASSERT is not empty to avoid -Werror=empty-body.
# define FMT_ASSERT(condition, message) \
::fmt::detail::ignore_unused((condition), (message))
# else
# define FMT_ASSERT(condition, message) \
((condition) /* void() fails with -Winvalid-constexpr on clang 4.0.1 */ \
? (void)0 \
: ::fmt::detail::assert_fail(__FILE__, __LINE__, (message)))
# endif
#endif
#ifdef __cpp_lib_byte
using byte = std::byte;
#else
enum class byte : unsigned char {};
#endif
#if defined(FMT_USE_STRING_VIEW)
template <typename Char> using std_string_view = std::basic_string_view<Char>;
#elif defined(FMT_USE_EXPERIMENTAL_STRING_VIEW)
template <typename Char>
using std_string_view = std::experimental::basic_string_view<Char>;
#else
template <typename T> struct std_string_view {};
#endif
#ifdef FMT_USE_INT128
// Do nothing.
#elif defined(__SIZEOF_INT128__) && !FMT_NVCC && \
!(FMT_CLANG_VERSION && FMT_MSC_VER)
# define FMT_USE_INT128 1
using int128_t = __int128_t;
using uint128_t = __uint128_t;
template <typename T> inline auto convert_for_visit(T value) -> T {
return value;
}
#else
# define FMT_USE_INT128 0
#endif
#if !FMT_USE_INT128
enum class int128_t {};
enum class uint128_t {};
// Reduce template instantiations.
template <typename T> inline auto convert_for_visit(T) -> monostate {
return {};
}
#endif
// Casts a nonnegative integer to unsigned.
template <typename Int>
FMT_CONSTEXPR auto to_unsigned(Int value) ->
typename std::make_unsigned<Int>::type {
FMT_ASSERT(value >= 0, "negative value");
return static_cast<typename std::make_unsigned<Int>::type>(value);
}
FMT_MSC_WARNING(suppress : 4566) constexpr unsigned char micro[] = "\u00B5";
constexpr auto is_utf8() -> bool {
// Avoid buggy sign extensions in MSVC's constant evaluation mode.
// https://developercommunity.visualstudio.com/t/C-difference-in-behavior-for-unsigned/1233612
using uchar = unsigned char;
return FMT_UNICODE || (sizeof(micro) == 3 && uchar(micro[0]) == 0xC2 &&
uchar(micro[1]) == 0xB5);
}
FMT_END_DETAIL_NAMESPACE
/**
An implementation of ``std::basic_string_view`` for pre-C++17. It provides a
subset of the API. ``fmt::basic_string_view`` is used for format strings even
if ``std::string_view`` is available to prevent issues when a library is
compiled with a different ``-std`` option than the client code (which is not
recommended).
*/
template <typename Char> class basic_string_view {
private:
const Char* data_;
size_t size_;
public:
using value_type = Char;
using iterator = const Char*;
constexpr basic_string_view() FMT_NOEXCEPT : data_(nullptr), size_(0) {}
/** Constructs a string reference object from a C string and a size. */
constexpr basic_string_view(const Char* s, size_t count) FMT_NOEXCEPT
: data_(s),
size_(count) {}
/**
\rst
Constructs a string reference object from a C string computing
the size with ``std::char_traits<Char>::length``.
\endrst
*/
FMT_CONSTEXPR_CHAR_TRAITS
FMT_INLINE
basic_string_view(const Char* s)
: data_(s),
size_(detail::const_check(std::is_same<Char, char>::value &&
!detail::is_constant_evaluated(true))
? std::strlen(reinterpret_cast<const char*>(s))
: std::char_traits<Char>::length(s)) {}
/** Constructs a string reference from a ``std::basic_string`` object. */
template <typename Traits, typename Alloc>
FMT_CONSTEXPR basic_string_view(
const std::basic_string<Char, Traits, Alloc>& s) FMT_NOEXCEPT
: data_(s.data()),
size_(s.size()) {}
template <typename S, FMT_ENABLE_IF(std::is_same<
S, detail::std_string_view<Char>>::value)>
FMT_CONSTEXPR basic_string_view(S s) FMT_NOEXCEPT : data_(s.data()),
size_(s.size()) {}
/** Returns a pointer to the string data. */
constexpr auto data() const FMT_NOEXCEPT -> const Char* { return data_; }
/** Returns the string size. */
constexpr auto size() const FMT_NOEXCEPT -> size_t { return size_; }
constexpr auto begin() const FMT_NOEXCEPT -> iterator { return data_; }
constexpr auto end() const FMT_NOEXCEPT -> iterator { return data_ + size_; }
constexpr auto operator[](size_t pos) const FMT_NOEXCEPT -> const Char& {
return data_[pos];
}
FMT_CONSTEXPR void remove_prefix(size_t n) FMT_NOEXCEPT {
data_ += n;
size_ -= n;
}
// Lexicographically compare this string reference to other.
FMT_CONSTEXPR_CHAR_TRAITS auto compare(basic_string_view other) const -> int {
size_t str_size = size_ < other.size_ ? size_ : other.size_;
int result = std::char_traits<Char>::compare(data_, other.data_, str_size);
if (result == 0)
result = size_ == other.size_ ? 0 : (size_ < other.size_ ? -1 : 1);
return result;
}
FMT_CONSTEXPR_CHAR_TRAITS friend auto operator==(basic_string_view lhs,
basic_string_view rhs)
-> bool {
return lhs.compare(rhs) == 0;
}
friend auto operator!=(basic_string_view lhs, basic_string_view rhs) -> bool {
return lhs.compare(rhs) != 0;
}
friend auto operator<(basic_string_view lhs, basic_string_view rhs) -> bool {
return lhs.compare(rhs) < 0;
}
friend auto operator<=(basic_string_view lhs, basic_string_view rhs) -> bool {
return lhs.compare(rhs) <= 0;
}
friend auto operator>(basic_string_view lhs, basic_string_view rhs) -> bool {
return lhs.compare(rhs) > 0;
}
friend auto operator>=(basic_string_view lhs, basic_string_view rhs) -> bool {
return lhs.compare(rhs) >= 0;
}
};
using string_view = basic_string_view<char>;
/** Specifies if ``T`` is a character type. Can be specialized by users. */
template <typename T> struct is_char : std::false_type {};
template <> struct is_char<char> : std::true_type {};
// Returns a string view of `s`.
template <typename Char, FMT_ENABLE_IF(is_char<Char>::value)>
FMT_INLINE auto to_string_view(const Char* s) -> basic_string_view<Char> {
return s;
}
template <typename Char, typename Traits, typename Alloc>
inline auto to_string_view(const std::basic_string<Char, Traits, Alloc>& s)
-> basic_string_view<Char> {
return s;
}
template <typename Char>
constexpr auto to_string_view(basic_string_view<Char> s)
-> basic_string_view<Char> {
return s;
}
template <typename Char,
FMT_ENABLE_IF(!std::is_empty<detail::std_string_view<Char>>::value)>
inline auto to_string_view(detail::std_string_view<Char> s)
-> basic_string_view<Char> {
return s;
}
// A base class for compile-time strings. It is defined in the fmt namespace to
// make formatting functions visible via ADL, e.g. format(FMT_STRING("{}"), 42).
struct compile_string {};
template <typename S>
struct is_compile_string : std::is_base_of<compile_string, S> {};
template <typename S, FMT_ENABLE_IF(is_compile_string<S>::value)>
constexpr auto to_string_view(const S& s)
-> basic_string_view<typename S::char_type> {
return basic_string_view<typename S::char_type>(s);
}
FMT_BEGIN_DETAIL_NAMESPACE
void to_string_view(...);
using fmt::to_string_view;
// Specifies whether S is a string type convertible to fmt::basic_string_view.
// It should be a constexpr function but MSVC 2017 fails to compile it in
// enable_if and MSVC 2015 fails to compile it as an alias template.
template <typename S>
struct is_string : std::is_class<decltype(to_string_view(std::declval<S>()))> {
};
template <typename S, typename = void> struct char_t_impl {};
template <typename S> struct char_t_impl<S, enable_if_t<is_string<S>::value>> {
using result = decltype(to_string_view(std::declval<S>()));
using type = typename result::value_type;
};
// Reports a compile-time error if S is not a valid format string.
template <typename..., typename S, FMT_ENABLE_IF(!is_compile_string<S>::value)>
FMT_INLINE void check_format_string(const S&) {
#ifdef FMT_ENFORCE_COMPILE_STRING
static_assert(is_compile_string<S>::value,
"FMT_ENFORCE_COMPILE_STRING requires all format strings to use "
"FMT_STRING.");
#endif
}
template <typename..., typename S, FMT_ENABLE_IF(is_compile_string<S>::value)>
void check_format_string(S);
FMT_NORETURN FMT_API void throw_format_error(const char* message);
struct error_handler {
constexpr error_handler() = default;
constexpr error_handler(const error_handler&) = default;
// This function is intentionally not constexpr to give a compile-time error.
FMT_NORETURN FMT_API void on_error(const char* message);
};
FMT_END_DETAIL_NAMESPACE
/** String's character type. */
template <typename S> using char_t = typename detail::char_t_impl<S>::type;
/**
\rst
Parsing context consisting of a format string range being parsed and an
argument counter for automatic indexing.
You can use the ``format_parse_context`` type alias for ``char`` instead.
\endrst
*/
template <typename Char, typename ErrorHandler = detail::error_handler>
class basic_format_parse_context : private ErrorHandler {
private:
basic_string_view<Char> format_str_;
int next_arg_id_;
public:
using char_type = Char;
using iterator = typename basic_string_view<Char>::iterator;
explicit constexpr basic_format_parse_context(
basic_string_view<Char> format_str, ErrorHandler eh = {},
int next_arg_id = 0)
: ErrorHandler(eh), format_str_(format_str), next_arg_id_(next_arg_id) {}
/**
Returns an iterator to the beginning of the format string range being
parsed.
*/
constexpr auto begin() const FMT_NOEXCEPT -> iterator {
return format_str_.begin();
}
/**
Returns an iterator past the end of the format string range being parsed.
*/
constexpr auto end() const FMT_NOEXCEPT -> iterator {
return format_str_.end();
}
/** Advances the begin iterator to ``it``. */
FMT_CONSTEXPR void advance_to(iterator it) {
format_str_.remove_prefix(detail::to_unsigned(it - begin()));
}
/**
Reports an error if using the manual argument indexing; otherwise returns
the next argument index and switches to the automatic indexing.
*/
FMT_CONSTEXPR auto next_arg_id() -> int {
// Don't check if the argument id is valid to avoid overhead and because it
// will be checked during formatting anyway.
if (next_arg_id_ >= 0) return next_arg_id_++;
on_error("cannot switch from manual to automatic argument indexing");
return 0;
}
/**
Reports an error if using the automatic argument indexing; otherwise
switches to the manual indexing.
*/
FMT_CONSTEXPR void check_arg_id(int) {
if (next_arg_id_ > 0)
on_error("cannot switch from automatic to manual argument indexing");
else
next_arg_id_ = -1;
}
FMT_CONSTEXPR void check_arg_id(basic_string_view<Char>) {}
FMT_CONSTEXPR void on_error(const char* message) {
ErrorHandler::on_error(message);
}
constexpr auto error_handler() const -> ErrorHandler { return *this; }
};
using format_parse_context = basic_format_parse_context<char>;
template <typename Context> class basic_format_arg;
template <typename Context> class basic_format_args;
template <typename Context> class dynamic_format_arg_store;
// A formatter for objects of type T.
template <typename T, typename Char = char, typename Enable = void>
struct formatter {
// A deleted default constructor indicates a disabled formatter.
formatter() = delete;
};
// Specifies if T has an enabled formatter specialization. A type can be
// formattable even if it doesn't have a formatter e.g. via a conversion.
template <typename T, typename Context>
using has_formatter =
std::is_constructible<typename Context::template formatter_type<T>>;
// Checks whether T is a container with contiguous storage.
template <typename T> struct is_contiguous : std::false_type {};
template <typename Char>
struct is_contiguous<std::basic_string<Char>> : std::true_type {};
class appender;
FMT_BEGIN_DETAIL_NAMESPACE
template <typename Context, typename T>
constexpr auto has_const_formatter_impl(T*)
-> decltype(typename Context::template formatter_type<T>().format(
std::declval<const T&>(), std::declval<Context&>()),
true) {
return true;
}
template <typename Context>
constexpr auto has_const_formatter_impl(...) -> bool {
return false;
}
template <typename T, typename Context>
constexpr auto has_const_formatter() -> bool {
return has_const_formatter_impl<Context>(static_cast<T*>(nullptr));
}
// Extracts a reference to the container from back_insert_iterator.
template <typename Container>
inline auto get_container(std::back_insert_iterator<Container> it)
-> Container& {
using bi_iterator = std::back_insert_iterator<Container>;
struct accessor : bi_iterator {
accessor(bi_iterator iter) : bi_iterator(iter) {}
using bi_iterator::container;
};
return *accessor(it).container;
}
template <typename Char, typename InputIt, typename OutputIt>
FMT_CONSTEXPR auto copy_str(InputIt begin, InputIt end, OutputIt out)
-> OutputIt {
while (begin != end) *out++ = static_cast<Char>(*begin++);
return out;
}
template <typename Char, typename T, typename U,
FMT_ENABLE_IF(
std::is_same<remove_const_t<T>, U>::value&& is_char<U>::value)>
FMT_CONSTEXPR auto copy_str(T* begin, T* end, U* out) -> U* {
if (is_constant_evaluated()) return copy_str<Char, T*, U*>(begin, end, out);
auto size = to_unsigned(end - begin);
memcpy(out, begin, size * sizeof(U));
return out + size;
}
/**
\rst
A contiguous memory buffer with an optional growing ability. It is an internal
class and shouldn't be used directly, only via `~fmt::basic_memory_buffer`.
\endrst
*/
template <typename T> class buffer {
private:
T* ptr_;
size_t size_;
size_t capacity_;
protected:
// Don't initialize ptr_ since it is not accessed to save a few cycles.
FMT_MSC_WARNING(suppress : 26495)
buffer(size_t sz) FMT_NOEXCEPT : size_(sz), capacity_(sz) {}
FMT_CONSTEXPR20 buffer(T* p = nullptr, size_t sz = 0,
size_t cap = 0) FMT_NOEXCEPT : ptr_(p),
size_(sz),
capacity_(cap) {}
FMT_CONSTEXPR20 ~buffer() = default;
buffer(buffer&&) = default;
/** Sets the buffer data and capacity. */
FMT_CONSTEXPR void set(T* buf_data, size_t buf_capacity) FMT_NOEXCEPT {
ptr_ = buf_data;
capacity_ = buf_capacity;
}
/** Increases the buffer capacity to hold at least *capacity* elements. */
virtual FMT_CONSTEXPR20 void grow(size_t capacity) = 0;
public:
using value_type = T;
using const_reference = const T&;
buffer(const buffer&) = delete;
void operator=(const buffer&) = delete;
auto begin() FMT_NOEXCEPT -> T* { return ptr_; }
auto end() FMT_NOEXCEPT -> T* { return ptr_ + size_; }
auto begin() const FMT_NOEXCEPT -> const T* { return ptr_; }
auto end() const FMT_NOEXCEPT -> const T* { return ptr_ + size_; }
/** Returns the size of this buffer. */
constexpr auto size() const FMT_NOEXCEPT -> size_t { return size_; }
/** Returns the capacity of this buffer. */
constexpr auto capacity() const FMT_NOEXCEPT -> size_t { return capacity_; }
/** Returns a pointer to the buffer data. */
FMT_CONSTEXPR auto data() FMT_NOEXCEPT -> T* { return ptr_; }
/** Returns a pointer to the buffer data. */
FMT_CONSTEXPR auto data() const FMT_NOEXCEPT -> const T* { return ptr_; }
/** Clears this buffer. */
void clear() { size_ = 0; }
// Tries resizing the buffer to contain *count* elements. If T is a POD type
// the new elements may not be initialized.
FMT_CONSTEXPR20 void try_resize(size_t count) {
try_reserve(count);
size_ = count <= capacity_ ? count : capacity_;
}
// Tries increasing the buffer capacity to *new_capacity*. It can increase the
// capacity by a smaller amount than requested but guarantees there is space
// for at least one additional element either by increasing the capacity or by
// flushing the buffer if it is full.
FMT_CONSTEXPR20 void try_reserve(size_t new_capacity) {
if (new_capacity > capacity_) grow(new_capacity);
}
FMT_CONSTEXPR20 void push_back(const T& value) {
try_reserve(size_ + 1);
ptr_[size_++] = value;
}
/** Appends data to the end of the buffer. */
template <typename U> void append(const U* begin, const U* end);
template <typename I> FMT_CONSTEXPR auto operator[](I index) -> T& {
return ptr_[index];
}
template <typename I>
FMT_CONSTEXPR auto operator[](I index) const -> const T& {
return ptr_[index];
}
};
struct buffer_traits {
explicit buffer_traits(size_t) {}
auto count() const -> size_t { return 0; }
auto limit(size_t size) -> size_t { return size; }
};
class fixed_buffer_traits {
private:
size_t count_ = 0;
size_t limit_;
public:
explicit fixed_buffer_traits(size_t limit) : limit_(limit) {}
auto count() const -> size_t { return count_; }
auto limit(size_t size) -> size_t {
size_t n = limit_ > count_ ? limit_ - count_ : 0;
count_ += size;
return size < n ? size : n;
}
};
// A buffer that writes to an output iterator when flushed.
template <typename OutputIt, typename T, typename Traits = buffer_traits>
class iterator_buffer final : public Traits, public buffer<T> {
private:
OutputIt out_;
enum { buffer_size = 256 };
T data_[buffer_size];
protected:
FMT_CONSTEXPR20 void grow(size_t) override {
if (this->size() == buffer_size) flush();
}
void flush() {
auto size = this->size();
this->clear();
out_ = copy_str<T>(data_, data_ + this->limit(size), out_);
}
public:
explicit iterator_buffer(OutputIt out, size_t n = buffer_size)
: Traits(n), buffer<T>(data_, 0, buffer_size), out_(out) {}
iterator_buffer(iterator_buffer&& other)
: Traits(other), buffer<T>(data_, 0, buffer_size), out_(other.out_) {}
~iterator_buffer() { flush(); }
auto out() -> OutputIt {
flush();
return out_;
}
auto count() const -> size_t { return Traits::count() + this->size(); }
};
template <typename T>
class iterator_buffer<T*, T, fixed_buffer_traits> final
: public fixed_buffer_traits,
public buffer<T> {
private:
T* out_;
enum { buffer_size = 256 };
T data_[buffer_size];
protected:
FMT_CONSTEXPR20 void grow(size_t) override {
if (this->size() == this->capacity()) flush();
}
void flush() {
size_t n = this->limit(this->size());
if (this->data() == out_) {
out_ += n;
this->set(data_, buffer_size);
}
this->clear();
}
public:
explicit iterator_buffer(T* out, size_t n = buffer_size)
: fixed_buffer_traits(n), buffer<T>(out, 0, n), out_(out) {}
iterator_buffer(iterator_buffer&& other)
: fixed_buffer_traits(other),
buffer<T>(std::move(other)),
out_(other.out_) {
if (this->data() != out_) {
this->set(data_, buffer_size);
this->clear();
}
}
~iterator_buffer() { flush(); }
auto out() -> T* {
flush();
return out_;
}
auto count() const -> size_t {
return fixed_buffer_traits::count() + this->size();
}
};
template <typename T> class iterator_buffer<T*, T> final : public buffer<T> {
protected:
FMT_CONSTEXPR20 void grow(size_t) override {}
public:
explicit iterator_buffer(T* out, size_t = 0) : buffer<T>(out, 0, ~size_t()) {}
auto out() -> T* { return &*this->end(); }
};
// A buffer that writes to a container with the contiguous storage.
template <typename Container>
class iterator_buffer<std::back_insert_iterator<Container>,
enable_if_t<is_contiguous<Container>::value,
typename Container::value_type>>
final : public buffer<typename Container::value_type> {
private:
Container& container_;
protected:
FMT_CONSTEXPR20 void grow(size_t capacity) override {
container_.resize(capacity);
this->set(&container_[0], capacity);
}
public:
explicit iterator_buffer(Container& c)
: buffer<typename Container::value_type>(c.size()), container_(c) {}
explicit iterator_buffer(std::back_insert_iterator<Container> out, size_t = 0)
: iterator_buffer(get_container(out)) {}
auto out() -> std::back_insert_iterator<Container> {
return std::back_inserter(container_);
}
};
// A buffer that counts the number of code units written discarding the output.
template <typename T = char> class counting_buffer final : public buffer<T> {
private:
enum { buffer_size = 256 };
T data_[buffer_size];
size_t count_ = 0;
protected:
FMT_CONSTEXPR20 void grow(size_t) override {
if (this->size() != buffer_size) return;
count_ += this->size();
this->clear();
}
public:
counting_buffer() : buffer<T>(data_, 0, buffer_size) {}
auto count() -> size_t { return count_ + this->size(); }
};
template <typename T>
using buffer_appender = conditional_t<std::is_same<T, char>::value, appender,
std::back_insert_iterator<buffer<T>>>;
// Maps an output iterator to a buffer.
template <typename T, typename OutputIt>
auto get_buffer(OutputIt out) -> iterator_buffer<OutputIt, T> {
return iterator_buffer<OutputIt, T>(out);
}
template <typename Buffer>
auto get_iterator(Buffer& buf) -> decltype(buf.out()) {
return buf.out();
}
template <typename T> auto get_iterator(buffer<T>& buf) -> buffer_appender<T> {
return buffer_appender<T>(buf);
}
template <typename T, typename Char = char, typename Enable = void>
struct fallback_formatter {
fallback_formatter() = delete;
};
// Specifies if T has an enabled fallback_formatter specialization.
template <typename T, typename Char>
using has_fallback_formatter =
std::is_constructible<fallback_formatter<T, Char>>;
struct view {};
template <typename Char, typename T> struct named_arg : view {
const Char* name;
const T& value;
named_arg(const Char* n, const T& v) : name(n), value(v) {}
};
template <typename Char> struct named_arg_info {
const Char* name;
int id;
};
template <typename T, typename Char, size_t NUM_ARGS, size_t NUM_NAMED_ARGS>
struct arg_data {
// args_[0].named_args points to named_args_ to avoid bloating format_args.
// +1 to workaround a bug in gcc 7.5 that causes duplicated-branches warning.
T args_[1 + (NUM_ARGS != 0 ? NUM_ARGS : +1)];
named_arg_info<Char> named_args_[NUM_NAMED_ARGS];
template <typename... U>
arg_data(const U&... init) : args_{T(named_args_, NUM_NAMED_ARGS), init...} {}
arg_data(const arg_data& other) = delete;
auto args() const -> const T* { return args_ + 1; }
auto named_args() -> named_arg_info<Char>* { return named_args_; }
};
template <typename T, typename Char, size_t NUM_ARGS>
struct arg_data<T, Char, NUM_ARGS, 0> {
// +1 to workaround a bug in gcc 7.5 that causes duplicated-branches warning.
T args_[NUM_ARGS != 0 ? NUM_ARGS : +1];
template <typename... U>
FMT_CONSTEXPR FMT_INLINE arg_data(const U&... init) : args_{init...} {}
FMT_CONSTEXPR FMT_INLINE auto args() const -> const T* { return args_; }
FMT_CONSTEXPR FMT_INLINE auto named_args() -> std::nullptr_t {
return nullptr;
}
};
template <typename Char>
inline void init_named_args(named_arg_info<Char>*, int, int) {}
template <typename T> struct is_named_arg : std::false_type {};
template <typename T> struct is_statically_named_arg : std::false_type {};
template <typename T, typename Char>
struct is_named_arg<named_arg<Char, T>> : std::true_type {};
template <typename Char, typename T, typename... Tail,
FMT_ENABLE_IF(!is_named_arg<T>::value)>
void init_named_args(named_arg_info<Char>* named_args, int arg_count,
int named_arg_count, const T&, const Tail&... args) {
init_named_args(named_args, arg_count + 1, named_arg_count, args...);
}
template <typename Char, typename T, typename... Tail,
FMT_ENABLE_IF(is_named_arg<T>::value)>
void init_named_args(named_arg_info<Char>* named_args, int arg_count,
int named_arg_count, const T& arg, const Tail&... args) {
named_args[named_arg_count++] = {arg.name, arg_count};
init_named_args(named_args, arg_count + 1, named_arg_count, args...);
}
template <typename... Args>
FMT_CONSTEXPR FMT_INLINE void init_named_args(std::nullptr_t, int, int,
const Args&...) {}
template <bool B = false> constexpr auto count() -> size_t { return B ? 1 : 0; }
template <bool B1, bool B2, bool... Tail> constexpr auto count() -> size_t {
return (B1 ? 1 : 0) + count<B2, Tail...>();
}
template <typename... Args> constexpr auto count_named_args() -> size_t {
return count<is_named_arg<Args>::value...>();
}
template <typename... Args>
constexpr auto count_statically_named_args() -> size_t {
return count<is_statically_named_arg<Args>::value...>();
}
enum class type {
none_type,
// Integer types should go first,
int_type,
uint_type,
long_long_type,
ulong_long_type,
int128_type,
uint128_type,
bool_type,
char_type,
last_integer_type = char_type,
// followed by floating-point types.
float_type,
double_type,
long_double_type,
last_numeric_type = long_double_type,
cstring_type,
string_type,
pointer_type,
custom_type
};
// Maps core type T to the corresponding type enum constant.
template <typename T, typename Char>
struct type_constant : std::integral_constant<type, type::custom_type> {};
#define FMT_TYPE_CONSTANT(Type, constant) \
template <typename Char> \
struct type_constant<Type, Char> \
: std::integral_constant<type, type::constant> {}
FMT_TYPE_CONSTANT(int, int_type);
FMT_TYPE_CONSTANT(unsigned, uint_type);
FMT_TYPE_CONSTANT(long long, long_long_type);
FMT_TYPE_CONSTANT(unsigned long long, ulong_long_type);
FMT_TYPE_CONSTANT(int128_t, int128_type);
FMT_TYPE_CONSTANT(uint128_t, uint128_type);
FMT_TYPE_CONSTANT(bool, bool_type);
FMT_TYPE_CONSTANT(Char, char_type);
FMT_TYPE_CONSTANT(float, float_type);
FMT_TYPE_CONSTANT(double, double_type);
FMT_TYPE_CONSTANT(long double, long_double_type);
FMT_TYPE_CONSTANT(const Char*, cstring_type);
FMT_TYPE_CONSTANT(basic_string_view<Char>, string_type);
FMT_TYPE_CONSTANT(const void*, pointer_type);
constexpr bool is_integral_type(type t) {
return t > type::none_type && t <= type::last_integer_type;
}
constexpr bool is_arithmetic_type(type t) {
return t > type::none_type && t <= type::last_numeric_type;
}
struct unformattable {};
struct unformattable_char : unformattable {};
struct unformattable_const : unformattable {};
struct unformattable_pointer : unformattable {};
template <typename Char> struct string_value {
const Char* data;
size_t size;
};
template <typename Char> struct named_arg_value {
const named_arg_info<Char>* data;
size_t size;
};
template <typename Context> struct custom_value {
using parse_context = typename Context::parse_context_type;
void* value;
void (*format)(void* arg, parse_context& parse_ctx, Context& ctx);
};
// A formatting argument value.
template <typename Context> class value {
public:
using char_type = typename Context::char_type;
union {
monostate no_value;
int int_value;
unsigned uint_value;
long long long_long_value;
unsigned long long ulong_long_value;
int128_t int128_value;
uint128_t uint128_value;
bool bool_value;
char_type char_value;
float float_value;
double double_value;
long double long_double_value;
const void* pointer;
string_value<char_type> string;
custom_value<Context> custom;
named_arg_value<char_type> named_args;
};
constexpr FMT_INLINE value() : no_value() {}
constexpr FMT_INLINE value(int val) : int_value(val) {}
constexpr FMT_INLINE value(unsigned val) : uint_value(val) {}
constexpr FMT_INLINE value(long long val) : long_long_value(val) {}
constexpr FMT_INLINE value(unsigned long long val) : ulong_long_value(val) {}
FMT_INLINE value(int128_t val) : int128_value(val) {}
FMT_INLINE value(uint128_t val) : uint128_value(val) {}
constexpr FMT_INLINE value(float val) : float_value(val) {}
constexpr FMT_INLINE value(double val) : double_value(val) {}
FMT_INLINE value(long double val) : long_double_value(val) {}
constexpr FMT_INLINE value(bool val) : bool_value(val) {}
constexpr FMT_INLINE value(char_type val) : char_value(val) {}
FMT_CONSTEXPR FMT_INLINE value(const char_type* val) {
string.data = val;
if (is_constant_evaluated()) string.size = {};
}
FMT_CONSTEXPR FMT_INLINE value(basic_string_view<char_type> val) {
string.data = val.data();
string.size = val.size();
}
FMT_INLINE value(const void* val) : pointer(val) {}
FMT_INLINE value(const named_arg_info<char_type>* args, size_t size)
: named_args{args, size} {}
template <typename T> FMT_CONSTEXPR FMT_INLINE value(T& val) {
using value_type = remove_cvref_t<T>;
custom.value = const_cast<value_type*>(&val);
// Get the formatter type through the context to allow different contexts
// have different extension points, e.g. `formatter<T>` for `format` and
// `printf_formatter<T>` for `printf`.
custom.format = format_custom_arg<
value_type,
conditional_t<has_formatter<value_type, Context>::value,
typename Context::template formatter_type<value_type>,
fallback_formatter<value_type, char_type>>>;
}
value(unformattable);
value(unformattable_char);
value(unformattable_const);
value(unformattable_pointer);
private:
// Formats an argument of a custom type, such as a user-defined class.
template <typename T, typename Formatter>
static void format_custom_arg(void* arg,
typename Context::parse_context_type& parse_ctx,
Context& ctx) {
auto f = Formatter();
parse_ctx.advance_to(f.parse(parse_ctx));
using qualified_type =
conditional_t<has_const_formatter<T, Context>(), const T, T>;
ctx.advance_to(f.format(*static_cast<qualified_type*>(arg), ctx));
}
};
template <typename Context, typename T>
FMT_CONSTEXPR auto make_arg(const T& value) -> basic_format_arg<Context>;
// To minimize the number of types we need to deal with, long is translated
// either to int or to long long depending on its size.
enum { long_short = sizeof(long) == sizeof(int) };
using long_type = conditional_t<long_short, int, long long>;
using ulong_type = conditional_t<long_short, unsigned, unsigned long long>;
// Maps formatting arguments to core types.
// arg_mapper reports errors by returning unformattable instead of using
// static_assert because it's used in the is_formattable trait.
template <typename Context> struct arg_mapper {
using char_type = typename Context::char_type;
FMT_CONSTEXPR FMT_INLINE auto map(signed char val) -> int { return val; }
FMT_CONSTEXPR FMT_INLINE auto map(unsigned char val) -> unsigned {
return val;
}
FMT_CONSTEXPR FMT_INLINE auto map(short val) -> int { return val; }
FMT_CONSTEXPR FMT_INLINE auto map(unsigned short val) -> unsigned {
return val;
}
FMT_CONSTEXPR FMT_INLINE auto map(int val) -> int { return val; }
FMT_CONSTEXPR FMT_INLINE auto map(unsigned val) -> unsigned { return val; }
FMT_CONSTEXPR FMT_INLINE auto map(long val) -> long_type { return val; }
FMT_CONSTEXPR FMT_INLINE auto map(unsigned long val) -> ulong_type {
return val;
}
FMT_CONSTEXPR FMT_INLINE auto map(long long val) -> long long { return val; }
FMT_CONSTEXPR FMT_INLINE auto map(unsigned long long val)
-> unsigned long long {
return val;
}
FMT_CONSTEXPR FMT_INLINE auto map(int128_t val) -> int128_t { return val; }
FMT_CONSTEXPR FMT_INLINE auto map(uint128_t val) -> uint128_t { return val; }
FMT_CONSTEXPR FMT_INLINE auto map(bool val) -> bool { return val; }
template <typename T, FMT_ENABLE_IF(std::is_same<T, char>::value ||
std::is_same<T, char_type>::value)>
FMT_CONSTEXPR FMT_INLINE auto map(T val) -> char_type {
return val;
}
template <typename T, enable_if_t<(std::is_same<T, wchar_t>::value ||
#ifdef __cpp_char8_t
std::is_same<T, char8_t>::value ||
#endif
std::is_same<T, char16_t>::value ||
std::is_same<T, char32_t>::value) &&
!std::is_same<T, char_type>::value,
int> = 0>
FMT_CONSTEXPR FMT_INLINE auto map(T) -> unformattable_char {
return {};
}
FMT_CONSTEXPR FMT_INLINE auto map(float val) -> float { return val; }
FMT_CONSTEXPR FMT_INLINE auto map(double val) -> double { return val; }
FMT_CONSTEXPR FMT_INLINE auto map(long double val) -> long double {
return val;
}
FMT_CONSTEXPR FMT_INLINE auto map(char_type* val) -> const char_type* {
return val;
}
FMT_CONSTEXPR FMT_INLINE auto map(const char_type* val) -> const char_type* {
return val;
}
template <typename T,
FMT_ENABLE_IF(is_string<T>::value && !std::is_pointer<T>::value &&
std::is_same<char_type, char_t<T>>::value)>
FMT_CONSTEXPR FMT_INLINE auto map(const T& val)
-> basic_string_view<char_type> {
return to_string_view(val);
}
template <typename T,
FMT_ENABLE_IF(is_string<T>::value && !std::is_pointer<T>::value &&
!std::is_same<char_type, char_t<T>>::value)>
FMT_CONSTEXPR FMT_INLINE auto map(const T&) -> unformattable_char {
return {};
}
template <typename T,
FMT_ENABLE_IF(
std::is_constructible<basic_string_view<char_type>, T>::value &&
!is_string<T>::value && !has_formatter<T, Context>::value &&
!has_fallback_formatter<T, char_type>::value)>
FMT_CONSTEXPR FMT_INLINE auto map(const T& val)
-> basic_string_view<char_type> {
return basic_string_view<char_type>(val);
}
template <
typename T,
FMT_ENABLE_IF(
std::is_constructible<std_string_view<char_type>, T>::value &&
!std::is_constructible<basic_string_view<char_type>, T>::value &&
!is_string<T>::value && !has_formatter<T, Context>::value &&
!has_fallback_formatter<T, char_type>::value)>
FMT_CONSTEXPR FMT_INLINE auto map(const T& val)
-> basic_string_view<char_type> {
return std_string_view<char_type>(val);
}
using cstring_result = conditional_t<std::is_same<char_type, char>::value,
const char*, unformattable_pointer>;
FMT_DEPRECATED FMT_CONSTEXPR FMT_INLINE auto map(const signed char* val)
-> cstring_result {
return map(reinterpret_cast<const char*>(val));
}
FMT_DEPRECATED FMT_CONSTEXPR FMT_INLINE auto map(const unsigned char* val)
-> cstring_result {
return map(reinterpret_cast<const char*>(val));
}
FMT_DEPRECATED FMT_CONSTEXPR FMT_INLINE auto map(signed char* val)
-> cstring_result {
return map(reinterpret_cast<const char*>(val));
}
FMT_DEPRECATED FMT_CONSTEXPR FMT_INLINE auto map(unsigned char* val)
-> cstring_result {
return map(reinterpret_cast<const char*>(val));
}
FMT_CONSTEXPR FMT_INLINE auto map(void* val) -> const void* { return val; }
FMT_CONSTEXPR FMT_INLINE auto map(const void* val) -> const void* {
return val;
}
FMT_CONSTEXPR FMT_INLINE auto map(std::nullptr_t val) -> const void* {
return val;
}
// We use SFINAE instead of a const T* parameter to avoid conflicting with
// the C array overload.
template <
typename T,
FMT_ENABLE_IF(
std::is_member_pointer<T>::value ||
std::is_function<typename std::remove_pointer<T>::type>::value ||
(std::is_convertible<const T&, const void*>::value &&
!std::is_convertible<const T&, const char_type*>::value))>
FMT_CONSTEXPR auto map(const T&) -> unformattable_pointer {
return {};
}
template <typename T, std::size_t N,
FMT_ENABLE_IF(!std::is_same<T, wchar_t>::value)>
FMT_CONSTEXPR FMT_INLINE auto map(const T (&values)[N]) -> const T (&)[N] {
return values;
}
template <typename T,
FMT_ENABLE_IF(
std::is_enum<T>::value&& std::is_convertible<T, int>::value &&
!has_formatter<T, Context>::value &&
!has_fallback_formatter<T, char_type>::value)>
FMT_CONSTEXPR FMT_INLINE auto map(const T& val)
-> decltype(std::declval<arg_mapper>().map(
static_cast<typename std::underlying_type<T>::type>(val))) {
return map(static_cast<typename std::underlying_type<T>::type>(val));
}
FMT_CONSTEXPR FMT_INLINE auto map(detail::byte val) -> unsigned {
return map(static_cast<unsigned char>(val));
}
template <typename T, typename U = remove_cvref_t<T>>
struct formattable
: bool_constant<has_const_formatter<U, Context>() ||
!std::is_const<remove_reference_t<T>>::value ||
has_fallback_formatter<U, char_type>::value> {};
#if FMT_MSC_VER != 0 && FMT_MSC_VER < 1910
// Workaround a bug in MSVC.
template <typename T> FMT_CONSTEXPR FMT_INLINE auto do_map(T&& val) -> T& {
return val;
}
#else
template <typename T, FMT_ENABLE_IF(formattable<T>::value)>
FMT_CONSTEXPR FMT_INLINE auto do_map(T&& val) -> T& {
return val;
}
template <typename T, FMT_ENABLE_IF(!formattable<T>::value)>
FMT_CONSTEXPR FMT_INLINE auto do_map(T&&) -> unformattable_const {
return {};
}
#endif
template <typename T, typename U = remove_cvref_t<T>,
FMT_ENABLE_IF(!is_string<U>::value && !is_char<U>::value &&
!std::is_array<U>::value &&
(has_formatter<U, Context>::value ||
has_fallback_formatter<U, char_type>::value))>
FMT_CONSTEXPR FMT_INLINE auto map(T&& val)
-> decltype(this->do_map(std::forward<T>(val))) {
return do_map(std::forward<T>(val));
}
template <typename T, FMT_ENABLE_IF(is_named_arg<T>::value)>
FMT_CONSTEXPR FMT_INLINE auto map(const T& named_arg)
-> decltype(std::declval<arg_mapper>().map(named_arg.value)) {
return map(named_arg.value);
}
auto map(...) -> unformattable { return {}; }
};
// A type constant after applying arg_mapper<Context>.
template <typename T, typename Context>
using mapped_type_constant =
type_constant<decltype(arg_mapper<Context>().map(std::declval<const T&>())),
typename Context::char_type>;
enum { packed_arg_bits = 4 };
// Maximum number of arguments with packed types.
enum { max_packed_args = 62 / packed_arg_bits };
enum : unsigned long long { is_unpacked_bit = 1ULL << 63 };
enum : unsigned long long { has_named_args_bit = 1ULL << 62 };
FMT_END_DETAIL_NAMESPACE
// An output iterator that appends to a buffer.
// It is used to reduce symbol sizes for the common case.
class appender : public std::back_insert_iterator<detail::buffer<char>> {
using base = std::back_insert_iterator<detail::buffer<char>>;
template <typename T>
friend auto get_buffer(appender out) -> detail::buffer<char>& {
return detail::get_container(out);
}
public:
using std::back_insert_iterator<detail::buffer<char>>::back_insert_iterator;
appender(base it) FMT_NOEXCEPT : base(it) {}
using _Unchecked_type = appender; // Mark iterator as checked.
auto operator++() FMT_NOEXCEPT -> appender& { return *this; }
auto operator++(int) FMT_NOEXCEPT -> appender { return *this; }
};
// A formatting argument. It is a trivially copyable/constructible type to
// allow storage in basic_memory_buffer.
template <typename Context> class basic_format_arg {
private:
detail::value<Context> value_;
detail::type type_;
template <typename ContextType, typename T>
friend FMT_CONSTEXPR auto detail::make_arg(const T& value)
-> basic_format_arg<ContextType>;
template <typename Visitor, typename Ctx>
friend FMT_CONSTEXPR auto visit_format_arg(Visitor&& vis,
const basic_format_arg<Ctx>& arg)
-> decltype(vis(0));
friend class basic_format_args<Context>;
friend class dynamic_format_arg_store<Context>;
using char_type = typename Context::char_type;
template <typename T, typename Char, size_t NUM_ARGS, size_t NUM_NAMED_ARGS>
friend struct detail::arg_data;
basic_format_arg(const detail::named_arg_info<char_type>* args, size_t size)
: value_(args, size) {}
public:
class handle {
public:
explicit handle(detail::custom_value<Context> custom) : custom_(custom) {}
void format(typename Context::parse_context_type& parse_ctx,
Context& ctx) const {
custom_.format(custom_.value, parse_ctx, ctx);
}
private:
detail::custom_value<Context> custom_;
};
constexpr basic_format_arg() : type_(detail::type::none_type) {}
constexpr explicit operator bool() const FMT_NOEXCEPT {
return type_ != detail::type::none_type;
}
auto type() const -> detail::type { return type_; }
auto is_integral() const -> bool { return detail::is_integral_type(type_); }
auto is_arithmetic() const -> bool {
return detail::is_arithmetic_type(type_);
}
};
/**
\rst
Visits an argument dispatching to the appropriate visit method based on
the argument type. For example, if the argument type is ``double`` then
``vis(value)`` will be called with the value of type ``double``.
\endrst
*/
template <typename Visitor, typename Context>
FMT_CONSTEXPR FMT_INLINE auto visit_format_arg(
Visitor&& vis, const basic_format_arg<Context>& arg) -> decltype(vis(0)) {
switch (arg.type_) {
case detail::type::none_type:
break;
case detail::type::int_type:
return vis(arg.value_.int_value);
case detail::type::uint_type:
return vis(arg.value_.uint_value);
case detail::type::long_long_type:
return vis(arg.value_.long_long_value);
case detail::type::ulong_long_type:
return vis(arg.value_.ulong_long_value);
case detail::type::int128_type:
return vis(detail::convert_for_visit(arg.value_.int128_value));
case detail::type::uint128_type:
return vis(detail::convert_for_visit(arg.value_.uint128_value));
case detail::type::bool_type:
return vis(arg.value_.bool_value);
case detail::type::char_type:
return vis(arg.value_.char_value);
case detail::type::float_type:
return vis(arg.value_.float_value);
case detail::type::double_type:
return vis(arg.value_.double_value);
case detail::type::long_double_type:
return vis(arg.value_.long_double_value);
case detail::type::cstring_type:
return vis(arg.value_.string.data);
case detail::type::string_type:
using sv = basic_string_view<typename Context::char_type>;
return vis(sv(arg.value_.string.data, arg.value_.string.size));
case detail::type::pointer_type:
return vis(arg.value_.pointer);
case detail::type::custom_type:
return vis(typename basic_format_arg<Context>::handle(arg.value_.custom));
}
return vis(monostate());
}
FMT_BEGIN_DETAIL_NAMESPACE
template <typename Char, typename InputIt>
auto copy_str(InputIt begin, InputIt end, appender out) -> appender {
get_container(out).append(begin, end);
return out;
}
#if FMT_GCC_VERSION && FMT_GCC_VERSION < 500
// A workaround for gcc 4.8 to make void_t work in a SFINAE context.
template <typename... Ts> struct void_t_impl { using type = void; };
template <typename... Ts>
using void_t = typename detail::void_t_impl<Ts...>::type;
#else
template <typename...> using void_t = void;
#endif
template <typename It, typename T, typename Enable = void>
struct is_output_iterator : std::false_type {};
template <typename It, typename T>
struct is_output_iterator<
It, T,
void_t<typename std::iterator_traits<It>::iterator_category,
decltype(*std::declval<It>() = std::declval<T>())>>
: std::true_type {};
template <typename OutputIt>
struct is_back_insert_iterator : std::false_type {};
template <typename Container>
struct is_back_insert_iterator<std::back_insert_iterator<Container>>
: std::true_type {};
template <typename OutputIt>
struct is_contiguous_back_insert_iterator : std::false_type {};
template <typename Container>
struct is_contiguous_back_insert_iterator<std::back_insert_iterator<Container>>
: is_contiguous<Container> {};
template <>
struct is_contiguous_back_insert_iterator<appender> : std::true_type {};
// A type-erased reference to an std::locale to avoid heavy <locale> include.
class locale_ref {
private:
const void* locale_; // A type-erased pointer to std::locale.
public:
constexpr locale_ref() : locale_(nullptr) {}
template <typename Locale> explicit locale_ref(const Locale& loc);
explicit operator bool() const FMT_NOEXCEPT { return locale_ != nullptr; }
template <typename Locale> auto get() const -> Locale;
};
template <typename> constexpr auto encode_types() -> unsigned long long {
return 0;
}
template <typename Context, typename Arg, typename... Args>
constexpr auto encode_types() -> unsigned long long {
return static_cast<unsigned>(mapped_type_constant<Arg, Context>::value) |
(encode_types<Context, Args...>() << packed_arg_bits);
}
template <typename Context, typename T>
FMT_CONSTEXPR auto make_arg(const T& value) -> basic_format_arg<Context> {
basic_format_arg<Context> arg;
arg.type_ = mapped_type_constant<T, Context>::value;
arg.value_ = arg_mapper<Context>().map(value);
return arg;
}
// The type template parameter is there to avoid an ODR violation when using
// a fallback formatter in one translation unit and an implicit conversion in
// another (not recommended).
template <bool IS_PACKED, typename Context, type, typename T,
FMT_ENABLE_IF(IS_PACKED)>
FMT_CONSTEXPR FMT_INLINE auto make_arg(T&& val) -> value<Context> {
const auto& arg = arg_mapper<Context>().map(std::forward<T>(val));
constexpr bool formattable_char =
!std::is_same<decltype(arg), const unformattable_char&>::value;
static_assert(formattable_char, "Mixing character types is disallowed.");
constexpr bool formattable_const =
!std::is_same<decltype(arg), const unformattable_const&>::value;
static_assert(formattable_const, "Cannot format a const argument.");
// Formatting of arbitrary pointers is disallowed. If you want to output
// a pointer cast it to "void *" or "const void *". In particular, this
// forbids formatting of "[const] volatile char *" which is printed as bool
// by iostreams.
constexpr bool formattable_pointer =
!std::is_same<decltype(arg), const unformattable_pointer&>::value;
static_assert(formattable_pointer,
"Formatting of non-void pointers is disallowed.");
constexpr bool formattable =
!std::is_same<decltype(arg), const unformattable&>::value;
static_assert(
formattable,
"Cannot format an argument. To make type T formattable provide a "
"formatter<T> specialization: https://fmt.dev/latest/api.html#udt");
return {arg};
}
template <bool IS_PACKED, typename Context, type, typename T,
FMT_ENABLE_IF(!IS_PACKED)>
inline auto make_arg(const T& value) -> basic_format_arg<Context> {
return make_arg<Context>(value);
}
FMT_END_DETAIL_NAMESPACE
// Formatting context.
template <typename OutputIt, typename Char> class basic_format_context {
public:
/** The character type for the output. */
using char_type = Char;
private:
OutputIt out_;
basic_format_args<basic_format_context> args_;
detail::locale_ref loc_;
public:
using iterator = OutputIt;
using format_arg = basic_format_arg<basic_format_context>;
using parse_context_type = basic_format_parse_context<Char>;
template <typename T> using formatter_type = formatter<T, char_type>;
basic_format_context(basic_format_context&&) = default;
basic_format_context(const basic_format_context&) = delete;
void operator=(const basic_format_context&) = delete;
/**
Constructs a ``basic_format_context`` object. References to the arguments are
stored in the object so make sure they have appropriate lifetimes.
*/
constexpr basic_format_context(
OutputIt out, basic_format_args<basic_format_context> ctx_args,
detail::locale_ref loc = detail::locale_ref())
: out_(out), args_(ctx_args), loc_(loc) {}
constexpr auto arg(int id) const -> format_arg { return args_.get(id); }
FMT_CONSTEXPR auto arg(basic_string_view<char_type> name) -> format_arg {
return args_.get(name);
}
FMT_CONSTEXPR auto arg_id(basic_string_view<char_type> name) -> int {
return args_.get_id(name);
}
auto args() const -> const basic_format_args<basic_format_context>& {
return args_;
}
FMT_CONSTEXPR auto error_handler() -> detail::error_handler { return {}; }
void on_error(const char* message) { error_handler().on_error(message); }
// Returns an iterator to the beginning of the output range.
FMT_CONSTEXPR auto out() -> iterator { return out_; }
// Advances the begin iterator to ``it``.
void advance_to(iterator it) {
if (!detail::is_back_insert_iterator<iterator>()) out_ = it;
}
FMT_CONSTEXPR auto locale() -> detail::locale_ref { return loc_; }
};
template <typename Char>
using buffer_context =
basic_format_context<detail::buffer_appender<Char>, Char>;
using format_context = buffer_context<char>;
// Workaround an alias issue: https://stackoverflow.com/q/62767544/471164.
#define FMT_BUFFER_CONTEXT(Char) \
basic_format_context<detail::buffer_appender<Char>, Char>
template <typename T, typename Char = char>
using is_formattable = bool_constant<
!std::is_base_of<detail::unformattable,
decltype(detail::arg_mapper<buffer_context<Char>>().map(
std::declval<T>()))>::value &&
!detail::has_fallback_formatter<T, Char>::value>;
/**
\rst
An array of references to arguments. It can be implicitly converted into
`~fmt::basic_format_args` for passing into type-erased formatting functions
such as `~fmt::vformat`.
\endrst
*/
template <typename Context, typename... Args>
class format_arg_store
#if FMT_GCC_VERSION && FMT_GCC_VERSION < 409
// Workaround a GCC template argument substitution bug.
: public basic_format_args<Context>
#endif
{
private:
static const size_t num_args = sizeof...(Args);
static const size_t num_named_args = detail::count_named_args<Args...>();
static const bool is_packed = num_args <= detail::max_packed_args;
using value_type = conditional_t<is_packed, detail::value<Context>,
basic_format_arg<Context>>;
detail::arg_data<value_type, typename Context::char_type, num_args,
num_named_args>
data_;
friend class basic_format_args<Context>;
static constexpr unsigned long long desc =
(is_packed ? detail::encode_types<Context, Args...>()
: detail::is_unpacked_bit | num_args) |
(num_named_args != 0
? static_cast<unsigned long long>(detail::has_named_args_bit)
: 0);
public:
template <typename... T>
FMT_CONSTEXPR FMT_INLINE format_arg_store(T&&... args)
:
#if FMT_GCC_VERSION && FMT_GCC_VERSION < 409
basic_format_args<Context>(*this),
#endif
data_{detail::make_arg<
is_packed, Context,
detail::mapped_type_constant<remove_cvref_t<T>, Context>::value>(
std::forward<T>(args))...} {
detail::init_named_args(data_.named_args(), 0, 0, args...);
}
};
/**
\rst
Constructs a `~fmt::format_arg_store` object that contains references to
arguments and can be implicitly converted to `~fmt::format_args`. `Context`
can be omitted in which case it defaults to `~fmt::context`.
See `~fmt::arg` for lifetime considerations.
\endrst
*/
template <typename Context = format_context, typename... Args>
constexpr auto make_format_args(Args&&... args)
-> format_arg_store<Context, remove_cvref_t<Args>...> {
return {std::forward<Args>(args)...};
}
/**
\rst
Returns a named argument to be used in a formatting function.
It should only be used in a call to a formatting function or
`dynamic_format_arg_store::push_back`.
**Example**::
fmt::print("Elapsed time: {s:.2f} seconds", fmt::arg("s", 1.23));
\endrst
*/
template <typename Char, typename T>
inline auto arg(const Char* name, const T& arg) -> detail::named_arg<Char, T> {
static_assert(!detail::is_named_arg<T>(), "nested named arguments");
return {name, arg};
}
/**
\rst
A view of a collection of formatting arguments. To avoid lifetime issues it
should only be used as a parameter type in type-erased functions such as
``vformat``::
void vlog(string_view format_str, format_args args); // OK
format_args args = make_format_args(42); // Error: dangling reference
\endrst
*/
template <typename Context> class basic_format_args {
public:
using size_type = int;
using format_arg = basic_format_arg<Context>;
private:
// A descriptor that contains information about formatting arguments.
// If the number of arguments is less or equal to max_packed_args then
// argument types are passed in the descriptor. This reduces binary code size
// per formatting function call.
unsigned long long desc_;
union {
// If is_packed() returns true then argument values are stored in values_;
// otherwise they are stored in args_. This is done to improve cache
// locality and reduce compiled code size since storing larger objects
// may require more code (at least on x86-64) even if the same amount of
// data is actually copied to stack. It saves ~10% on the bloat test.
const detail::value<Context>* values_;
const format_arg* args_;
};
constexpr auto is_packed() const -> bool {
return (desc_ & detail::is_unpacked_bit) == 0;
}
auto has_named_args() const -> bool {
return (desc_ & detail::has_named_args_bit) != 0;
}
FMT_CONSTEXPR auto type(int index) const -> detail::type {
int shift = index * detail::packed_arg_bits;
unsigned int mask = (1 << detail::packed_arg_bits) - 1;
return static_cast<detail::type>((desc_ >> shift) & mask);
}
constexpr FMT_INLINE basic_format_args(unsigned long long desc,
const detail::value<Context>* values)
: desc_(desc), values_(values) {}
constexpr basic_format_args(unsigned long long desc, const format_arg* args)
: desc_(desc), args_(args) {}
public:
constexpr basic_format_args() : desc_(0), args_(nullptr) {}
/**
\rst
Constructs a `basic_format_args` object from `~fmt::format_arg_store`.
\endrst
*/
template <typename... Args>
constexpr FMT_INLINE basic_format_args(
const format_arg_store<Context, Args...>& store)
: basic_format_args(format_arg_store<Context, Args...>::desc,
store.data_.args()) {}
/**
\rst
Constructs a `basic_format_args` object from
`~fmt::dynamic_format_arg_store`.
\endrst
*/
constexpr FMT_INLINE basic_format_args(
const dynamic_format_arg_store<Context>& store)
: basic_format_args(store.get_types(), store.data()) {}
/**
\rst
Constructs a `basic_format_args` object from a dynamic set of arguments.
\endrst
*/
constexpr basic_format_args(const format_arg* args, int count)
: basic_format_args(detail::is_unpacked_bit | detail::to_unsigned(count),
args) {}
/** Returns the argument with the specified id. */
FMT_CONSTEXPR auto get(int id) const -> format_arg {
format_arg arg;
if (!is_packed()) {
if (id < max_size()) arg = args_[id];
return arg;
}
if (id >= detail::max_packed_args) return arg;
arg.type_ = type(id);
if (arg.type_ == detail::type::none_type) return arg;
arg.value_ = values_[id];
return arg;
}
template <typename Char>
auto get(basic_string_view<Char> name) const -> format_arg {
int id = get_id(name);
return id >= 0 ? get(id) : format_arg();
}
template <typename Char>
auto get_id(basic_string_view<Char> name) const -> int {
if (!has_named_args()) return -1;
const auto& named_args =
(is_packed() ? values_[-1] : args_[-1].value_).named_args;
for (size_t i = 0; i < named_args.size; ++i) {
if (named_args.data[i].name == name) return named_args.data[i].id;
}
return -1;
}
auto max_size() const -> int {
unsigned long long max_packed = detail::max_packed_args;
return static_cast<int>(is_packed() ? max_packed
: desc_ & ~detail::is_unpacked_bit);
}
};
/** An alias to ``basic_format_args<format_context>``. */
// A separate type would result in shorter symbols but break ABI compatibility
// between clang and gcc on ARM (#1919).
using format_args = basic_format_args<format_context>;
// We cannot use enum classes as bit fields because of a gcc bug
// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=61414.
namespace align {
enum type { none, left, right, center, numeric };
}
using align_t = align::type;
namespace sign {
enum type { none, minus, plus, space };
}
using sign_t = sign::type;
FMT_BEGIN_DETAIL_NAMESPACE
// Workaround an array initialization issue in gcc 4.8.
template <typename Char> struct fill_t {
private:
enum { max_size = 4 };
Char data_[max_size] = {Char(' '), Char(0), Char(0), Char(0)};
unsigned char size_ = 1;
public:
FMT_CONSTEXPR void operator=(basic_string_view<Char> s) {
auto size = s.size();
if (size > max_size) return throw_format_error("invalid fill");
for (size_t i = 0; i < size; ++i) data_[i] = s[i];
size_ = static_cast<unsigned char>(size);
}
constexpr auto size() const -> size_t { return size_; }
constexpr auto data() const -> const Char* { return data_; }
FMT_CONSTEXPR auto operator[](size_t index) -> Char& { return data_[index]; }
FMT_CONSTEXPR auto operator[](size_t index) const -> const Char& {
return data_[index];
}
};
FMT_END_DETAIL_NAMESPACE
enum class presentation_type : unsigned char {
none,
// Integer types should go first,
dec, // 'd'
oct, // 'o'
hex_lower, // 'x'
hex_upper, // 'X'
bin_lower, // 'b'
bin_upper, // 'B'
hexfloat_lower, // 'a'
hexfloat_upper, // 'A'
exp_lower, // 'e'
exp_upper, // 'E'
fixed_lower, // 'f'
fixed_upper, // 'F'
general_lower, // 'g'
general_upper, // 'G'
chr, // 'c'
string, // 's'
pointer // 'p'
};
// Format specifiers for built-in and string types.
template <typename Char> struct basic_format_specs {
int width;
int precision;
presentation_type type;
align_t align : 4;
sign_t sign : 3;
bool alt : 1; // Alternate form ('#').
bool localized : 1;
detail::fill_t<Char> fill;
constexpr basic_format_specs()
: width(0),
precision(-1),
type(presentation_type::none),
align(align::none),
sign(sign::none),
alt(false),
localized(false) {}
};
using format_specs = basic_format_specs<char>;
FMT_BEGIN_DETAIL_NAMESPACE
enum class arg_id_kind { none, index, name };
// An argument reference.
template <typename Char> struct arg_ref {
FMT_CONSTEXPR arg_ref() : kind(arg_id_kind::none), val() {}
FMT_CONSTEXPR explicit arg_ref(int index)
: kind(arg_id_kind::index), val(index) {}
FMT_CONSTEXPR explicit arg_ref(basic_string_view<Char> name)
: kind(arg_id_kind::name), val(name) {}
FMT_CONSTEXPR auto operator=(int idx) -> arg_ref& {
kind = arg_id_kind::index;
val.index = idx;
return *this;
}
arg_id_kind kind;
union value {
FMT_CONSTEXPR value(int id = 0) : index{id} {}
FMT_CONSTEXPR value(basic_string_view<Char> n) : name(n) {}
int index;
basic_string_view<Char> name;
} val;
};
// Format specifiers with width and precision resolved at formatting rather
// than parsing time to allow re-using the same parsed specifiers with
// different sets of arguments (precompilation of format strings).
template <typename Char>
struct dynamic_format_specs : basic_format_specs<Char> {
arg_ref<Char> width_ref;
arg_ref<Char> precision_ref;
};
struct auto_id {};
// A format specifier handler that sets fields in basic_format_specs.
template <typename Char> class specs_setter {
protected:
basic_format_specs<Char>& specs_;
public:
explicit FMT_CONSTEXPR specs_setter(basic_format_specs<Char>& specs)
: specs_(specs) {}
FMT_CONSTEXPR specs_setter(const specs_setter& other)
: specs_(other.specs_) {}
FMT_CONSTEXPR void on_align(align_t align) { specs_.align = align; }
FMT_CONSTEXPR void on_fill(basic_string_view<Char> fill) {
specs_.fill = fill;
}
FMT_CONSTEXPR void on_sign(sign_t s) { specs_.sign = s; }
FMT_CONSTEXPR void on_hash() { specs_.alt = true; }
FMT_CONSTEXPR void on_localized() { specs_.localized = true; }
FMT_CONSTEXPR void on_zero() {
if (specs_.align == align::none) specs_.align = align::numeric;
specs_.fill[0] = Char('0');
}
FMT_CONSTEXPR void on_width(int width) { specs_.width = width; }
FMT_CONSTEXPR void on_precision(int precision) {
specs_.precision = precision;
}
FMT_CONSTEXPR void end_precision() {}
FMT_CONSTEXPR void on_type(presentation_type type) { specs_.type = type; }
};
// Format spec handler that saves references to arguments representing dynamic
// width and precision to be resolved at formatting time.
template <typename ParseContext>
class dynamic_specs_handler
: public specs_setter<typename ParseContext::char_type> {
public:
using char_type = typename ParseContext::char_type;
FMT_CONSTEXPR dynamic_specs_handler(dynamic_format_specs<char_type>& specs,
ParseContext& ctx)
: specs_setter<char_type>(specs), specs_(specs), context_(ctx) {}
FMT_CONSTEXPR dynamic_specs_handler(const dynamic_specs_handler& other)
: specs_setter<char_type>(other),
specs_(other.specs_),
context_(other.context_) {}
template <typename Id> FMT_CONSTEXPR void on_dynamic_width(Id arg_id) {
specs_.width_ref = make_arg_ref(arg_id);
}
template <typename Id> FMT_CONSTEXPR void on_dynamic_precision(Id arg_id) {
specs_.precision_ref = make_arg_ref(arg_id);
}
FMT_CONSTEXPR void on_error(const char* message) {
context_.on_error(message);
}
private:
dynamic_format_specs<char_type>& specs_;
ParseContext& context_;
using arg_ref_type = arg_ref<char_type>;
FMT_CONSTEXPR auto make_arg_ref(int arg_id) -> arg_ref_type {
context_.check_arg_id(arg_id);
return arg_ref_type(arg_id);
}
FMT_CONSTEXPR auto make_arg_ref(auto_id) -> arg_ref_type {
return arg_ref_type(context_.next_arg_id());
}
FMT_CONSTEXPR auto make_arg_ref(basic_string_view<char_type> arg_id)
-> arg_ref_type {
context_.check_arg_id(arg_id);
basic_string_view<char_type> format_str(
context_.begin(), to_unsigned(context_.end() - context_.begin()));
return arg_ref_type(arg_id);
}
};
template <typename Char> constexpr bool is_ascii_letter(Char c) {
return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z');
}
// Converts a character to ASCII. Returns a number > 127 on conversion failure.
template <typename Char, FMT_ENABLE_IF(std::is_integral<Char>::value)>
constexpr auto to_ascii(Char value) -> Char {
return value;
}
template <typename Char, FMT_ENABLE_IF(std::is_enum<Char>::value)>
constexpr auto to_ascii(Char value) ->
typename std::underlying_type<Char>::type {
return value;
}
template <typename Char>
FMT_CONSTEXPR auto code_point_length(const Char* begin) -> int {
if (const_check(sizeof(Char) != 1)) return 1;
auto lengths =
"\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\0\0\0\0\0\0\0\0\2\2\2\2\3\3\4";
int len = lengths[static_cast<unsigned char>(*begin) >> 3];
// Compute the pointer to the next character early so that the next
// iteration can start working on the next character. Neither Clang
// nor GCC figure out this reordering on their own.
return len + !len;
}
// Return the result via the out param to workaround gcc bug 77539.
template <bool IS_CONSTEXPR, typename T, typename Ptr = const T*>
FMT_CONSTEXPR auto find(Ptr first, Ptr last, T value, Ptr& out) -> bool {
for (out = first; out != last; ++out) {
if (*out == value) return true;
}
return false;
}
template <>
inline auto find<false, char>(const char* first, const char* last, char value,
const char*& out) -> bool {
out = static_cast<const char*>(
std::memchr(first, value, to_unsigned(last - first)));
return out != nullptr;
}
// Parses the range [begin, end) as an unsigned integer. This function assumes
// that the range is non-empty and the first character is a digit.
template <typename Char>
FMT_CONSTEXPR auto parse_nonnegative_int(const Char*& begin, const Char* end,
int error_value) noexcept -> int {
FMT_ASSERT(begin != end && '0' <= *begin && *begin <= '9', "");
unsigned value = 0, prev = 0;
auto p = begin;
do {
prev = value;
value = value * 10 + unsigned(*p - '0');
++p;
} while (p != end && '0' <= *p && *p <= '9');
auto num_digits = p - begin;
begin = p;
if (num_digits <= std::numeric_limits<int>::digits10)
return static_cast<int>(value);
// Check for overflow.
const unsigned max = to_unsigned((std::numeric_limits<int>::max)());
return num_digits == std::numeric_limits<int>::digits10 + 1 &&
prev * 10ull + unsigned(p[-1] - '0') <= max
? static_cast<int>(value)
: error_value;
}
// Parses fill and alignment.
template <typename Char, typename Handler>
FMT_CONSTEXPR auto parse_align(const Char* begin, const Char* end,
Handler&& handler) -> const Char* {
FMT_ASSERT(begin != end, "");
auto align = align::none;
auto p = begin + code_point_length(begin);
if (p >= end) p = begin;
for (;;) {
switch (to_ascii(*p)) {
case '<':
align = align::left;
break;
case '>':
align = align::right;
break;
case '^':
align = align::center;
break;
default:
break;
}
if (align != align::none) {
if (p != begin) {
auto c = *begin;
if (c == '{')
return handler.on_error("invalid fill character '{'"), begin;
handler.on_fill(basic_string_view<Char>(begin, to_unsigned(p - begin)));
begin = p + 1;
} else
++begin;
handler.on_align(align);
break;
} else if (p == begin) {
break;
}
p = begin;
}
return begin;
}
template <typename Char> FMT_CONSTEXPR bool is_name_start(Char c) {
return ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || '_' == c;
}
template <typename Char, typename IDHandler>
FMT_CONSTEXPR auto do_parse_arg_id(const Char* begin, const Char* end,
IDHandler&& handler) -> const Char* {
FMT_ASSERT(begin != end, "");
Char c = *begin;
if (c >= '0' && c <= '9') {
int index = 0;
if (c != '0')
index =
parse_nonnegative_int(begin, end, (std::numeric_limits<int>::max)());
else
++begin;
if (begin == end || (*begin != '}' && *begin != ':'))
handler.on_error("invalid format string");
else
handler(index);
return begin;
}
if (!is_name_start(c)) {
handler.on_error("invalid format string");
return begin;
}
auto it = begin;
do {
++it;
} while (it != end && (is_name_start(c = *it) || ('0' <= c && c <= '9')));
handler(basic_string_view<Char>(begin, to_unsigned(it - begin)));
return it;
}
template <typename Char, typename IDHandler>
FMT_CONSTEXPR FMT_INLINE auto parse_arg_id(const Char* begin, const Char* end,
IDHandler&& handler) -> const Char* {
Char c = *begin;
if (c != '}' && c != ':') return do_parse_arg_id(begin, end, handler);
handler();
return begin;
}
template <typename Char, typename Handler>
FMT_CONSTEXPR auto parse_width(const Char* begin, const Char* end,
Handler&& handler) -> const Char* {
using detail::auto_id;
struct width_adapter {
Handler& handler;
FMT_CONSTEXPR void operator()() { handler.on_dynamic_width(auto_id()); }
FMT_CONSTEXPR void operator()(int id) { handler.on_dynamic_width(id); }
FMT_CONSTEXPR void operator()(basic_string_view<Char> id) {
handler.on_dynamic_width(id);
}
FMT_CONSTEXPR void on_error(const char* message) {
if (message) handler.on_error(message);
}
};
FMT_ASSERT(begin != end, "");
if ('0' <= *begin && *begin <= '9') {
int width = parse_nonnegative_int(begin, end, -1);
if (width != -1)
handler.on_width(width);
else
handler.on_error("number is too big");
} else if (*begin == '{') {
++begin;
if (begin != end) begin = parse_arg_id(begin, end, width_adapter{handler});
if (begin == end || *begin != '}')
return handler.on_error("invalid format string"), begin;
++begin;
}
return begin;
}
template <typename Char, typename Handler>
FMT_CONSTEXPR auto parse_precision(const Char* begin, const Char* end,
Handler&& handler) -> const Char* {
using detail::auto_id;
struct precision_adapter {
Handler& handler;
FMT_CONSTEXPR void operator()() { handler.on_dynamic_precision(auto_id()); }
FMT_CONSTEXPR void operator()(int id) { handler.on_dynamic_precision(id); }
FMT_CONSTEXPR void operator()(basic_string_view<Char> id) {
handler.on_dynamic_precision(id);
}
FMT_CONSTEXPR void on_error(const char* message) {
if (message) handler.on_error(message);
}
};
++begin;
auto c = begin != end ? *begin : Char();
if ('0' <= c && c <= '9') {
auto precision = parse_nonnegative_int(begin, end, -1);
if (precision != -1)
handler.on_precision(precision);
else
handler.on_error("number is too big");
} else if (c == '{') {
++begin;
if (begin != end)
begin = parse_arg_id(begin, end, precision_adapter{handler});
if (begin == end || *begin++ != '}')
return handler.on_error("invalid format string"), begin;
} else {
return handler.on_error("missing precision specifier"), begin;
}
handler.end_precision();
return begin;
}
template <typename Char>
FMT_CONSTEXPR auto parse_presentation_type(Char type) -> presentation_type {
switch (to_ascii(type)) {
case 'd':
return presentation_type::dec;
case 'o':
return presentation_type::oct;
case 'x':
return presentation_type::hex_lower;
case 'X':
return presentation_type::hex_upper;
case 'b':
return presentation_type::bin_lower;
case 'B':
return presentation_type::bin_upper;
case 'a':
return presentation_type::hexfloat_lower;
case 'A':
return presentation_type::hexfloat_upper;
case 'e':
return presentation_type::exp_lower;
case 'E':
return presentation_type::exp_upper;
case 'f':
return presentation_type::fixed_lower;
case 'F':
return presentation_type::fixed_upper;
case 'g':
return presentation_type::general_lower;
case 'G':
return presentation_type::general_upper;
case 'c':
return presentation_type::chr;
case 's':
return presentation_type::string;
case 'p':
return presentation_type::pointer;
default:
return presentation_type::none;
}
}
// Parses standard format specifiers and sends notifications about parsed
// components to handler.
template <typename Char, typename SpecHandler>
FMT_CONSTEXPR FMT_INLINE auto parse_format_specs(const Char* begin,
const Char* end,
SpecHandler&& handler)
-> const Char* {
if (1 < end - begin && begin[1] == '}' && is_ascii_letter(*begin) &&
*begin != 'L') {
presentation_type type = parse_presentation_type(*begin++);
if (type == presentation_type::none)
handler.on_error("invalid type specifier");
handler.on_type(type);
return begin;
}
if (begin == end) return begin;
begin = parse_align(begin, end, handler);
if (begin == end) return begin;
// Parse sign.
switch (to_ascii(*begin)) {
case '+':
handler.on_sign(sign::plus);
++begin;
break;
case '-':
handler.on_sign(sign::minus);
++begin;
break;
case ' ':
handler.on_sign(sign::space);
++begin;
break;
default:
break;
}
if (begin == end) return begin;
if (*begin == '#') {
handler.on_hash();
if (++begin == end) return begin;
}
// Parse zero flag.
if (*begin == '0') {
handler.on_zero();
if (++begin == end) return begin;
}
begin = parse_width(begin, end, handler);
if (begin == end) return begin;
// Parse precision.
if (*begin == '.') {
begin = parse_precision(begin, end, handler);
if (begin == end) return begin;
}
if (*begin == 'L') {
handler.on_localized();
++begin;
}
// Parse type.
if (begin != end && *begin != '}') {
presentation_type type = parse_presentation_type(*begin++);
if (type == presentation_type::none)
handler.on_error("invalid type specifier");
handler.on_type(type);
}
return begin;
}
template <typename Char, typename Handler>
FMT_CONSTEXPR auto parse_replacement_field(const Char* begin, const Char* end,
Handler&& handler) -> const Char* {
struct id_adapter {
Handler& handler;
int arg_id;
FMT_CONSTEXPR void operator()() { arg_id = handler.on_arg_id(); }
FMT_CONSTEXPR void operator()(int id) { arg_id = handler.on_arg_id(id); }
FMT_CONSTEXPR void operator()(basic_string_view<Char> id) {
arg_id = handler.on_arg_id(id);
}
FMT_CONSTEXPR void on_error(const char* message) {
if (message) handler.on_error(message);
}
};
++begin;
if (begin == end) return handler.on_error("invalid format string"), end;
if (*begin == '}') {
handler.on_replacement_field(handler.on_arg_id(), begin);
} else if (*begin == '{') {
handler.on_text(begin, begin + 1);
} else {
auto adapter = id_adapter{handler, 0};
begin = parse_arg_id(begin, end, adapter);
Char c = begin != end ? *begin : Char();
if (c == '}') {
handler.on_replacement_field(adapter.arg_id, begin);
} else if (c == ':') {
begin = handler.on_format_specs(adapter.arg_id, begin + 1, end);
if (begin == end || *begin != '}')
return handler.on_error("unknown format specifier"), end;
} else {
return handler.on_error("missing '}' in format string"), end;
}
}
return begin + 1;
}
template <bool IS_CONSTEXPR, typename Char, typename Handler>
FMT_CONSTEXPR FMT_INLINE void parse_format_string(
basic_string_view<Char> format_str, Handler&& handler) {
// Workaround a name-lookup bug in MSVC's modules implementation.
using detail::find;
auto begin = format_str.data();
auto end = begin + format_str.size();
if (end - begin < 32) {
// Use a simple loop instead of memchr for small strings.
const Char* p = begin;
while (p != end) {
auto c = *p++;
if (c == '{') {
handler.on_text(begin, p - 1);
begin = p = parse_replacement_field(p - 1, end, handler);
} else if (c == '}') {
if (p == end || *p != '}')
return handler.on_error("unmatched '}' in format string");
handler.on_text(begin, p);
begin = ++p;
}
}
handler.on_text(begin, end);
return;
}
struct writer {
FMT_CONSTEXPR void operator()(const Char* pbegin, const Char* pend) {
if (pbegin == pend) return;
for (;;) {
const Char* p = nullptr;
if (!find<IS_CONSTEXPR>(pbegin, pend, Char('}'), p))
return handler_.on_text(pbegin, pend);
++p;
if (p == pend || *p != '}')
return handler_.on_error("unmatched '}' in format string");
handler_.on_text(pbegin, p);
pbegin = p + 1;
}
}
Handler& handler_;
} write{handler};
while (begin != end) {
// Doing two passes with memchr (one for '{' and another for '}') is up to
// 2.5x faster than the naive one-pass implementation on big format strings.
const Char* p = begin;
if (*begin != '{' && !find<IS_CONSTEXPR>(begin + 1, end, Char('{'), p))
return write(begin, end);
write(begin, p);
begin = parse_replacement_field(p, end, handler);
}
}
template <typename T, typename ParseContext>
FMT_CONSTEXPR auto parse_format_specs(ParseContext& ctx)
-> decltype(ctx.begin()) {
using char_type = typename ParseContext::char_type;
using context = buffer_context<char_type>;
using mapped_type = conditional_t<
mapped_type_constant<T, context>::value != type::custom_type,
decltype(arg_mapper<context>().map(std::declval<const T&>())), T>;
auto f = conditional_t<has_formatter<mapped_type, context>::value,
formatter<mapped_type, char_type>,
fallback_formatter<T, char_type>>();
return f.parse(ctx);
}
// A parse context with extra argument id checks. It is only used at compile
// time because adding checks at runtime would introduce substantial overhead
// and would be redundant since argument ids are checked when arguments are
// retrieved anyway.
template <typename Char, typename ErrorHandler = error_handler>
class compile_parse_context
: public basic_format_parse_context<Char, ErrorHandler> {
private:
int num_args_;
using base = basic_format_parse_context<Char, ErrorHandler>;
public:
explicit FMT_CONSTEXPR compile_parse_context(
basic_string_view<Char> format_str,
int num_args = (std::numeric_limits<int>::max)(), ErrorHandler eh = {})
: base(format_str, eh), num_args_(num_args) {}
FMT_CONSTEXPR auto next_arg_id() -> int {
int id = base::next_arg_id();
if (id >= num_args_) this->on_error("argument not found");
return id;
}
FMT_CONSTEXPR void check_arg_id(int id) {
base::check_arg_id(id);
if (id >= num_args_) this->on_error("argument not found");
}
using base::check_arg_id;
};
template <typename ErrorHandler>
FMT_CONSTEXPR void check_int_type_spec(presentation_type type,
ErrorHandler&& eh) {
if (type > presentation_type::bin_upper && type != presentation_type::chr)
eh.on_error("invalid type specifier");
}
// Checks char specs and returns true if the type spec is char (and not int).
template <typename Char, typename ErrorHandler = error_handler>
FMT_CONSTEXPR auto check_char_specs(const basic_format_specs<Char>& specs,
ErrorHandler&& eh = {}) -> bool {
if (specs.type != presentation_type::none &&
specs.type != presentation_type::chr) {
check_int_type_spec(specs.type, eh);
return false;
}
if (specs.align == align::numeric || specs.sign != sign::none || specs.alt)
eh.on_error("invalid format specifier for char");
return true;
}
// A floating-point presentation format.
enum class float_format : unsigned char {
general, // General: exponent notation or fixed point based on magnitude.
exp, // Exponent notation with the default precision of 6, e.g. 1.2e-3.
fixed, // Fixed point with the default precision of 6, e.g. 0.0012.
hex
};
struct float_specs {
int precision;
float_format format : 8;
sign_t sign : 8;
bool upper : 1;
bool locale : 1;
bool binary32 : 1;
bool fallback : 1;
bool showpoint : 1;
};
template <typename ErrorHandler = error_handler, typename Char>
FMT_CONSTEXPR auto parse_float_type_spec(const basic_format_specs<Char>& specs,
ErrorHandler&& eh = {})
-> float_specs {
auto result = float_specs();
result.showpoint = specs.alt;
result.locale = specs.localized;
switch (specs.type) {
case presentation_type::none:
result.format = float_format::general;
break;
case presentation_type::general_upper:
result.upper = true;
FMT_FALLTHROUGH;
case presentation_type::general_lower:
result.format = float_format::general;
break;
case presentation_type::exp_upper:
result.upper = true;
FMT_FALLTHROUGH;
case presentation_type::exp_lower:
result.format = float_format::exp;
result.showpoint |= specs.precision != 0;
break;
case presentation_type::fixed_upper:
result.upper = true;
FMT_FALLTHROUGH;
case presentation_type::fixed_lower:
result.format = float_format::fixed;
result.showpoint |= specs.precision != 0;
break;
case presentation_type::hexfloat_upper:
result.upper = true;
FMT_FALLTHROUGH;
case presentation_type::hexfloat_lower:
result.format = float_format::hex;
break;
default:
eh.on_error("invalid type specifier");
break;
}
return result;
}
template <typename ErrorHandler = error_handler>
FMT_CONSTEXPR auto check_cstring_type_spec(presentation_type type,
ErrorHandler&& eh = {}) -> bool {
if (type == presentation_type::none || type == presentation_type::string)
return true;
if (type != presentation_type::pointer) eh.on_error("invalid type specifier");
return false;
}
template <typename ErrorHandler = error_handler>
FMT_CONSTEXPR void check_string_type_spec(presentation_type type,
ErrorHandler&& eh = {}) {
if (type != presentation_type::none && type != presentation_type::string)
eh.on_error("invalid type specifier");
}
template <typename ErrorHandler>
FMT_CONSTEXPR void check_pointer_type_spec(presentation_type type,
ErrorHandler&& eh) {
if (type != presentation_type::none && type != presentation_type::pointer)
eh.on_error("invalid type specifier");
}
// A parse_format_specs handler that checks if specifiers are consistent with
// the argument type.
template <typename Handler> class specs_checker : public Handler {
private:
detail::type arg_type_;
FMT_CONSTEXPR void require_numeric_argument() {
if (!is_arithmetic_type(arg_type_))
this->on_error("format specifier requires numeric argument");
}
public:
FMT_CONSTEXPR specs_checker(const Handler& handler, detail::type arg_type)
: Handler(handler), arg_type_(arg_type) {}
FMT_CONSTEXPR void on_align(align_t align) {
if (align == align::numeric) require_numeric_argument();
Handler::on_align(align);
}
FMT_CONSTEXPR void on_sign(sign_t s) {
require_numeric_argument();
if (is_integral_type(arg_type_) && arg_type_ != type::int_type &&
arg_type_ != type::long_long_type && arg_type_ != type::char_type) {
this->on_error("format specifier requires signed argument");
}
Handler::on_sign(s);
}
FMT_CONSTEXPR void on_hash() {
require_numeric_argument();
Handler::on_hash();
}
FMT_CONSTEXPR void on_localized() {
require_numeric_argument();
Handler::on_localized();
}
FMT_CONSTEXPR void on_zero() {
require_numeric_argument();
Handler::on_zero();
}
FMT_CONSTEXPR void end_precision() {
if (is_integral_type(arg_type_) || arg_type_ == type::pointer_type)
this->on_error("precision not allowed for this argument type");
}
};
constexpr int invalid_arg_index = -1;
#if FMT_USE_NONTYPE_TEMPLATE_PARAMETERS
template <int N, typename T, typename... Args, typename Char>
constexpr auto get_arg_index_by_name(basic_string_view<Char> name) -> int {
if constexpr (detail::is_statically_named_arg<T>()) {
if (name == T::name) return N;
}
if constexpr (sizeof...(Args) > 0)
return get_arg_index_by_name<N + 1, Args...>(name);
(void)name; // Workaround an MSVC bug about "unused" parameter.
return invalid_arg_index;
}
#endif
template <typename... Args, typename Char>
FMT_CONSTEXPR auto get_arg_index_by_name(basic_string_view<Char> name) -> int {
#if FMT_USE_NONTYPE_TEMPLATE_PARAMETERS
if constexpr (sizeof...(Args) > 0)
return get_arg_index_by_name<0, Args...>(name);
#endif
(void)name;
return invalid_arg_index;
}
template <typename Char, typename ErrorHandler, typename... Args>
class format_string_checker {
private:
using parse_context_type = compile_parse_context<Char, ErrorHandler>;
enum { num_args = sizeof...(Args) };
// Format specifier parsing function.
using parse_func = const Char* (*)(parse_context_type&);
parse_context_type context_;
parse_func parse_funcs_[num_args > 0 ? num_args : 1];
public:
explicit FMT_CONSTEXPR format_string_checker(
basic_string_view<Char> format_str, ErrorHandler eh)
: context_(format_str, num_args, eh),
parse_funcs_{&parse_format_specs<Args, parse_context_type>...} {}
FMT_CONSTEXPR void on_text(const Char*, const Char*) {}
FMT_CONSTEXPR auto on_arg_id() -> int { return context_.next_arg_id(); }
FMT_CONSTEXPR auto on_arg_id(int id) -> int {
return context_.check_arg_id(id), id;
}
FMT_CONSTEXPR auto on_arg_id(basic_string_view<Char> id) -> int {
#if FMT_USE_NONTYPE_TEMPLATE_PARAMETERS
auto index = get_arg_index_by_name<Args...>(id);
if (index == invalid_arg_index) on_error("named argument is not found");
return context_.check_arg_id(index), index;
#else
(void)id;
on_error("compile-time checks for named arguments require C++20 support");
return 0;
#endif
}
FMT_CONSTEXPR void on_replacement_field(int, const Char*) {}
FMT_CONSTEXPR auto on_format_specs(int id, const Char* begin, const Char*)
-> const Char* {
context_.advance_to(context_.begin() + (begin - &*context_.begin()));
// id >= 0 check is a workaround for gcc 10 bug (#2065).
return id >= 0 && id < num_args ? parse_funcs_[id](context_) : begin;
}
FMT_CONSTEXPR void on_error(const char* message) {
context_.on_error(message);
}
};
template <typename... Args, typename S,
enable_if_t<(is_compile_string<S>::value), int>>
void check_format_string(S format_str) {
FMT_CONSTEXPR auto s = to_string_view(format_str);
using checker = format_string_checker<typename S::char_type, error_handler,
remove_cvref_t<Args>...>;
FMT_CONSTEXPR bool invalid_format =
(parse_format_string<true>(s, checker(s, {})), true);
ignore_unused(invalid_format);
}
template <typename Char>
void vformat_to(
buffer<Char>& buf, basic_string_view<Char> fmt,
basic_format_args<FMT_BUFFER_CONTEXT(type_identity_t<Char>)> args,
locale_ref loc = {});
FMT_API void vprint_mojibake(std::FILE*, string_view, format_args);
#ifndef _WIN32
inline void vprint_mojibake(std::FILE*, string_view, format_args) {}
#endif
FMT_END_DETAIL_NAMESPACE
// A formatter specialization for the core types corresponding to detail::type
// constants.
template <typename T, typename Char>
struct formatter<T, Char,
enable_if_t<detail::type_constant<T, Char>::value !=
detail::type::custom_type>> {
private:
detail::dynamic_format_specs<Char> specs_;
public:
// Parses format specifiers stopping either at the end of the range or at the
// terminating '}'.
template <typename ParseContext>
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
auto begin = ctx.begin(), end = ctx.end();
if (begin == end) return begin;
using handler_type = detail::dynamic_specs_handler<ParseContext>;
auto type = detail::type_constant<T, Char>::value;
auto checker =
detail::specs_checker<handler_type>(handler_type(specs_, ctx), type);
auto it = detail::parse_format_specs(begin, end, checker);
auto eh = ctx.error_handler();
switch (type) {
case detail::type::none_type:
FMT_ASSERT(false, "invalid argument type");
break;
case detail::type::bool_type:
if (specs_.type == presentation_type::none ||
specs_.type == presentation_type::string) {
break;
}
FMT_FALLTHROUGH;
case detail::type::int_type:
case detail::type::uint_type:
case detail::type::long_long_type:
case detail::type::ulong_long_type:
case detail::type::int128_type:
case detail::type::uint128_type:
detail::check_int_type_spec(specs_.type, eh);
break;
case detail::type::char_type:
detail::check_char_specs(specs_, eh);
break;
case detail::type::float_type:
if (detail::const_check(FMT_USE_FLOAT))
detail::parse_float_type_spec(specs_, eh);
else
FMT_ASSERT(false, "float support disabled");
break;
case detail::type::double_type:
if (detail::const_check(FMT_USE_DOUBLE))
detail::parse_float_type_spec(specs_, eh);
else
FMT_ASSERT(false, "double support disabled");
break;
case detail::type::long_double_type:
if (detail::const_check(FMT_USE_LONG_DOUBLE))
detail::parse_float_type_spec(specs_, eh);
else
FMT_ASSERT(false, "long double support disabled");
break;
case detail::type::cstring_type:
detail::check_cstring_type_spec(specs_.type, eh);
break;
case detail::type::string_type:
detail::check_string_type_spec(specs_.type, eh);
break;
case detail::type::pointer_type:
detail::check_pointer_type_spec(specs_.type, eh);
break;
case detail::type::custom_type:
// Custom format specifiers are checked in parse functions of
// formatter specializations.
break;
}
return it;
}
template <typename FormatContext>
FMT_CONSTEXPR auto format(const T& val, FormatContext& ctx) const
-> decltype(ctx.out());
};
template <typename Char> struct basic_runtime { basic_string_view<Char> str; };
/** A compile-time format string. */
template <typename Char, typename... Args> class basic_format_string {
private:
basic_string_view<Char> str_;
public:
template <typename S,
FMT_ENABLE_IF(
std::is_convertible<const S&, basic_string_view<Char>>::value)>
FMT_CONSTEVAL FMT_INLINE basic_format_string(const S& s) : str_(s) {
static_assert(
detail::count<
(std::is_base_of<detail::view, remove_reference_t<Args>>::value &&
std::is_reference<Args>::value)...>() == 0,
"passing views as lvalues is disallowed");
#ifdef FMT_HAS_CONSTEVAL
if constexpr (detail::count_named_args<Args...>() ==
detail::count_statically_named_args<Args...>()) {
using checker = detail::format_string_checker<Char, detail::error_handler,
remove_cvref_t<Args>...>;
detail::parse_format_string<true>(str_, checker(s, {}));
}
#else
detail::check_format_string<Args...>(s);
#endif
}
basic_format_string(basic_runtime<Char> r) : str_(r.str) {}
FMT_INLINE operator basic_string_view<Char>() const { return str_; }
};
#if FMT_GCC_VERSION && FMT_GCC_VERSION < 409
// Workaround broken conversion on older gcc.
template <typename... Args> using format_string = string_view;
template <typename S> auto runtime(const S& s) -> basic_string_view<char_t<S>> {
return s;
}
#else
template <typename... Args>
using format_string = basic_format_string<char, type_identity_t<Args>...>;
/**
\rst
Creates a runtime format string.
**Example**::
// Check format string at runtime instead of compile-time.
fmt::print(fmt::runtime("{:d}"), "I am not a number");
\endrst
*/
template <typename S> auto runtime(const S& s) -> basic_runtime<char_t<S>> {
return {{s}};
}
#endif
FMT_API auto vformat(string_view fmt, format_args args) -> std::string;
/**
\rst
Formats ``args`` according to specifications in ``fmt`` and returns the result
as a string.
**Example**::
#include <fmt/core.h>
std::string message = fmt::format("The answer is {}.", 42);
\endrst
*/
template <typename... T>
FMT_NODISCARD FMT_INLINE auto format(format_string<T...> fmt, T&&... args)
-> std::string {
return vformat(fmt, fmt::make_format_args(args...));
}
/** Formats a string and writes the output to ``out``. */
template <typename OutputIt,
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, char>::value)>
auto vformat_to(OutputIt out, string_view fmt, format_args args) -> OutputIt {
using detail::get_buffer;
auto&& buf = get_buffer<char>(out);
detail::vformat_to(buf, fmt, args, {});
return detail::get_iterator(buf);
}
/**
\rst
Formats ``args`` according to specifications in ``fmt``, writes the result to
the output iterator ``out`` and returns the iterator past the end of the output
range. `format_to` does not append a terminating null character.
**Example**::
auto out = std::vector<char>();
fmt::format_to(std::back_inserter(out), "{}", 42);
\endrst
*/
template <typename OutputIt, typename... T,
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, char>::value)>
FMT_INLINE auto format_to(OutputIt out, format_string<T...> fmt, T&&... args)
-> OutputIt {
return vformat_to(out, fmt, fmt::make_format_args(args...));
}
template <typename OutputIt> struct format_to_n_result {
/** Iterator past the end of the output range. */
OutputIt out;
/** Total (not truncated) output size. */
size_t size;
};
template <typename OutputIt, typename... T,
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, char>::value)>
auto vformat_to_n(OutputIt out, size_t n, string_view fmt, format_args args)
-> format_to_n_result<OutputIt> {
using traits = detail::fixed_buffer_traits;
auto buf = detail::iterator_buffer<OutputIt, char, traits>(out, n);
detail::vformat_to(buf, fmt, args, {});
return {buf.out(), buf.count()};
}
/**
\rst
Formats ``args`` according to specifications in ``fmt``, writes up to ``n``
characters of the result to the output iterator ``out`` and returns the total
(not truncated) output size and the iterator past the end of the output range.
`format_to_n` does not append a terminating null character.
\endrst
*/
template <typename OutputIt, typename... T,
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, char>::value)>
FMT_INLINE auto format_to_n(OutputIt out, size_t n, format_string<T...> fmt,
T&&... args) -> format_to_n_result<OutputIt> {
return vformat_to_n(out, n, fmt, fmt::make_format_args(args...));
}
/** Returns the number of chars in the output of ``format(fmt, args...)``. */
template <typename... T>
FMT_NODISCARD FMT_INLINE auto formatted_size(format_string<T...> fmt,
T&&... args) -> size_t {
auto buf = detail::counting_buffer<>();
detail::vformat_to(buf, string_view(fmt), fmt::make_format_args(args...), {});
return buf.count();
}
FMT_API void vprint(string_view fmt, format_args args);
FMT_API void vprint(std::FILE* f, string_view fmt, format_args args);
/**
\rst
Formats ``args`` according to specifications in ``fmt`` and writes the output
to ``stdout``.
**Example**::
fmt::print("Elapsed time: {0:.2f} seconds", 1.23);
\endrst
*/
template <typename... T>
FMT_INLINE void print(format_string<T...> fmt, T&&... args) {
const auto& vargs = fmt::make_format_args(args...);
return detail::is_utf8() ? vprint(fmt, vargs)
: detail::vprint_mojibake(stdout, fmt, vargs);
}
/**
\rst
Formats ``args`` according to specifications in ``fmt`` and writes the
output to the file ``f``.
**Example**::
fmt::print(stderr, "Don't {}!", "panic");
\endrst
*/
template <typename... T>
FMT_INLINE void print(std::FILE* f, format_string<T...> fmt, T&&... args) {
const auto& vargs = fmt::make_format_args(args...);
return detail::is_utf8() ? vprint(f, fmt, vargs)
: detail::vprint_mojibake(f, fmt, vargs);
}
FMT_MODULE_EXPORT_END
FMT_GCC_PRAGMA("GCC pop_options")
FMT_END_NAMESPACE
#ifdef FMT_HEADER_ONLY
# include "format.h"
#endif
#endif // FMT_CORE_H_