From 7c21d567d59153b37978756a5bf02daf6998e665 Mon Sep 17 00:00:00 2001 From: Kargatum Date: Sun, 11 Jul 2021 16:39:13 +0700 Subject: [PATCH] feat(Deps/Fmt): update to 8.0.1 (#6859) --- deps/fmt/CMakeLists.txt | 39 +- deps/fmt/ChangeLog.rst | 700 +++++++++- deps/fmt/README.rst | 10 +- deps/fmt/include/fmt/chrono.h | 271 ++-- deps/fmt/include/fmt/color.h | 8 +- deps/fmt/include/fmt/compile.h | 51 +- deps/fmt/include/fmt/core.h | 1510 ++++++++++++--------- deps/fmt/include/fmt/format-inl.h | 78 +- deps/fmt/include/fmt/format.h | 2051 +++++++++++------------------ deps/fmt/include/fmt/locale.h | 69 +- deps/fmt/include/fmt/os.h | 39 +- deps/fmt/include/fmt/printf.h | 389 +++--- deps/fmt/include/fmt/ranges.h | 81 +- deps/fmt/include/fmt/xchar.h | 236 ++++ deps/fmt/src/fmt.cc | 20 +- deps/fmt/src/format.cc | 39 +- deps/fmt/src/os.cc | 10 +- 17 files changed, 3136 insertions(+), 2465 deletions(-) create mode 100644 deps/fmt/include/fmt/xchar.h diff --git a/deps/fmt/CMakeLists.txt b/deps/fmt/CMakeLists.txt index 464dfde77..447d8e8a3 100644 --- a/deps/fmt/CMakeLists.txt +++ b/deps/fmt/CMakeLists.txt @@ -28,27 +28,34 @@ else() check_symbol_exists(strtod_l "${strtod_l_headers}" HAVE_STRTOD_L) endif() -set(FMT_HEADERS - include/fmt/args.h - include/fmt/chrono.h - include/fmt/color.h - include/fmt/compile.h - include/fmt/core.h - include/fmt/format.h - include/fmt/format-inl.h - include/fmt/locale.h - include/fmt/os.h - include/fmt/ostream.h - include/fmt/printf.h - include/fmt/ranges.h) +function(add_headers VAR) + set(headers ${${VAR}}) + foreach (header ${ARGN}) + set(headers ${headers} include/fmt/${header}) + endforeach() + set(${VAR} ${headers} PARENT_SCOPE) +endfunction() + +# Define the fmt library, its includes and the needed defines. +add_headers(FMT_HEADERS + args.h + chrono.h + color.h + compile.h + core.h + format.h + format-inl.h + locale.h os.h + ostream.h + printf.h + ranges.h + xchar.h) set(FMT_SOURCES src/format.cc src/os.cc) -add_library(fmt STATIC - ${FMT_SOURCES} - ${FMT_HEADERS}) +add_library(fmt STATIC ${FMT_SOURCES} ${FMT_HEADERS}) if (HAVE_STRTOD_L) target_compile_definitions(fmt diff --git a/deps/fmt/ChangeLog.rst b/deps/fmt/ChangeLog.rst index 9c171af05..1988283ca 100644 --- a/deps/fmt/ChangeLog.rst +++ b/deps/fmt/ChangeLog.rst @@ -1,3 +1,699 @@ +8.0.1 - 2021-07-02 +------------------ + +* Fixed the version number in the inline namespace + (`#2374 `_). + +* Added a missing presentation type check for ``std::string`` + (`#2402 `_). + +* Fixed a linkage error when mixing code built with clang and gcc + (`#2377 `_). + +* Fixed documentation issues + (`#2396 `_, + `#2403 `_, + `#2406 `_). + Thanks `@mkurdej (Marek Kurdej) `_. + +* Removed dead code in FP formatter ( + `#2398 `_). + Thanks `@javierhonduco (Javier Honduvilla Coto) + `_. + +* Fixed various warnings and compilation issues + (`#2351 `_, + `#2359 `_, + `#2365 `_, + `#2368 `_, + `#2370 `_, + `#2376 `_, + `#2381 `_, + `#2382 `_, + `#2386 `_, + `#2389 `_, + `#2395 `_, + `#2397 `_, + `#2400 `_ + `#2401 `_, + `#2407 `_). + Thanks `@zx2c4 (Jason A. Donenfeld) `_, + `@AidanSun05 (Aidan Sun) `_, + `@mattiasljungstrom (Mattias Ljungström) + `_, + `@joemmett (Jonathan Emmett) `_, + `@erengy (Eren Okka) `_, + `@patlkli (Patrick Geltinger) `_, + `@gsjaardema (Greg Sjaardema) `_, + `@phprus (Vladislav Shchapov) `_. + +8.0.0 - 2021-06-21 +------------------ + +* Enabled compile-time format string check by default. + For example (`godbolt `__): + + .. code:: c++ + + #include + + int main() { + fmt::print("{:d}", "I am not a number"); + } + + gives a compile-time error on compilers with C++20 ``consteval`` support + (gcc 10+, clang 11+) because ``d`` is not a valid format specifier for a + string. + + To pass a runtime string wrap it in ``fmt::runtime``: + + .. code:: c++ + + fmt::print(fmt::runtime("{:d}"), "I am not a number"); + +* Added compile-time formatting + (`#2019 `_, + `#2044 `_, + `#2056 `_, + `#2072 `_, + `#2075 `_, + `#2078 `_, + `#2129 `_, + `#2326 `_). + For example (`godbolt `__): + + .. code:: c++ + + #include + + consteval auto compile_time_itoa(int value) -> std::array { + auto result = std::array(); + fmt::format_to(result.data(), FMT_COMPILE("{}"), value); + return result; + } + + constexpr auto answer = compile_time_itoa(42); + + Most of the formatting functionality is available at compile time with a + notable exception of floating-point numbers and pointers. + Thanks `@alexezeder (Alexey Ochapov) `_. + +* Optimized handling of format specifiers during format string compilation. + For example, hexadecimal formatting (``"{:x}"``) is now 3-7x faster than + before when using ``format_to`` with format string compilation and a + stack-allocated buffer (`#1944 `_). + + Before (7.1.3):: + + ---------------------------------------------------------------------------- + Benchmark Time CPU Iterations + ---------------------------------------------------------------------------- + FMTCompileOld/0 15.5 ns 15.5 ns 43302898 + FMTCompileOld/42 16.6 ns 16.6 ns 43278267 + FMTCompileOld/273123 18.7 ns 18.6 ns 37035861 + FMTCompileOld/9223372036854775807 19.4 ns 19.4 ns 35243000 + ---------------------------------------------------------------------------- + + After (8.x):: + + ---------------------------------------------------------------------------- + Benchmark Time CPU Iterations + ---------------------------------------------------------------------------- + FMTCompileNew/0 1.99 ns 1.99 ns 360523686 + FMTCompileNew/42 2.33 ns 2.33 ns 279865664 + FMTCompileNew/273123 3.72 ns 3.71 ns 190230315 + FMTCompileNew/9223372036854775807 5.28 ns 5.26 ns 130711631 + ---------------------------------------------------------------------------- + + It is even faster than ``std::to_chars`` from libc++ compiled with clang on + macOS:: + + ---------------------------------------------------------------------------- + Benchmark Time CPU Iterations + ---------------------------------------------------------------------------- + ToChars/0 4.42 ns 4.41 ns 160196630 + ToChars/42 5.00 ns 4.98 ns 140735201 + ToChars/273123 7.26 ns 7.24 ns 95784130 + ToChars/9223372036854775807 8.77 ns 8.75 ns 75872534 + ---------------------------------------------------------------------------- + + In other cases, especially involving ``std::string`` construction, the + speed up is usually lower because handling format specifiers takes a smaller + fraction of the total time. + +* Added the ``_cf`` user-defined literal to represent a compiled format string. + It can be used instead of the ``FMT_COMPILE`` macro + (`#2043 `_, + `#2242 `_): + + .. code:: c++ + + #include + + using namespace fmt::literals; + auto s = fmt::format(FMT_COMPILE("{}"), 42); // 🙁 not modern + auto s = fmt::format("{}"_cf, 42); // 🙂 modern as hell + + It requires compiler support for class types in non-type template parameters + (a C++20 feature) which is available in GCC 9.3+. + Thanks `@alexezeder (Alexey Ochapov) `_. + +* Format string compilation now requires ``format`` functions of ``formatter`` + specializations for user-defined types to be ``const``: + + .. code:: c++ + + template <> struct fmt::formatter: formatter { + template + auto format(my_type obj, FormatContext& ctx) const { // Note const here. + // ... + } + }; + +* Added UDL-based named argument support to format string compilation + (`#2243 `_, + `#2281 `_). For example: + + .. code:: c++ + + #include + + using namespace fmt::literals; + auto s = fmt::format(FMT_COMPILE("{answer}"), "answer"_a = 42); + + Here the argument named "answer" is resolved at compile time with no + runtime overhead. + Thanks `@alexezeder (Alexey Ochapov) `_. + +* Added format string compilation support to ``fmt::print`` + (`#2280 `_, + `#2304 `_). + Thanks `@alexezeder (Alexey Ochapov) `_. + +* Added initial support for compiling {fmt} as a C++20 module + (`#2235 `_, + `#2240 `_, + `#2260 `_, + `#2282 `_, + `#2283 `_, + `#2288 `_, + `#2298 `_, + `#2306 `_, + `#2307 `_, + `#2309 `_, + `#2318 `_, + `#2324 `_, + `#2332 `_, + `#2340 `_). + Thanks `@DanielaE (Daniela Engert) `_. + +* Made symbols private by default reducing shared library size + (`#2301 `_). For example there was + a ~15% reported reduction on one platform. + Thanks `@sergiud (Sergiu Deitsch) `_. + +* Optimized includes making the result of preprocessing ``fmt/format.h`` + ~20% smaller with libstdc++/C++20 and slightly improving build times + (`#1998 `_). + +* Added support of ranges with non-const ``begin`` / ``end`` + (`#1953 `_). + Thanks `@kitegi (sarah) `_. + +* Added support of ``std::byte`` and other formattable types to ``fmt::join`` + (`#1981 `_, + `#2040 `_, + `#2050 `_, + `#2262 `_). For example: + + .. code:: c++ + + #include + #include + #include + + int main() { + auto bytes = std::vector{std::byte(4), std::byte(2)}; + fmt::print("{}", fmt::join(bytes, "")); + } + + prints "42". + + Thanks `@kamibo (Camille Bordignon) `_. + +* Implemented the default format for ``std::chrono::system_clock`` + (`#2319 `_, + `#2345 `_). For example: + + .. code:: c++ + + #include + + int main() { + fmt::print("{}", std::chrono::system_clock::now()); + } + + prints "2021-06-18 15:22:00" (the output depends on the current date and + time). Thanks `@sunmy2019 `_. + +* Made more chrono specifiers locale independent by default. Use the ``'L'`` + specifier to get localized formatting. For example: + + .. code:: c++ + + #include + + int main() { + std::locale::global(std::locale("ru_RU.UTF-8")); + auto monday = std::chrono::weekday(1); + fmt::print("{}\n", monday); // prints "Mon" + fmt::print("{:L}\n", monday); // prints "пн" + } + +* Improved locale handling in chrono formatting + (`#2337 `_, + `#2349 `_, + `#2350 `_). + Thanks `@phprus (Vladislav Shchapov) `_. + +* Deprecated ``fmt/locale.h`` moving the formatting functions that take a + locale to ``fmt/format.h`` (``char``) and ``fmt/xchar`` (other overloads). + This doesn't introduce a dependency on ```` so there is virtually no + compile time effect. + +* Made parameter order in ``vformat_to`` consistent with ``format_to`` + (`#2327 `_). + +* Added support for time points with arbitrary durations + (`#2208 `_). For example: + + .. code:: c++ + + #include + + int main() { + using tp = std::chrono::time_point< + std::chrono::system_clock, std::chrono::seconds>; + fmt::print("{:%S}", tp(std::chrono::seconds(42))); + } + + prints "42". + +* Formatting floating-point numbers no longer produces trailing zeros by default + for consistency with ``std::format``. For example: + + .. code:: c++ + + #include + + int main() { + fmt::print("{0:.3}", 1.1); + } + + prints "1.1". Use the ``'#'`` specifier to keep trailing zeros. + +* Dropped a limit on the number of elements in a range and replaced ``{}`` with + ``[]`` as range delimiters for consistency with Python's ``str.format``. + +* The ``'L'`` specifier for locale-specific numeric formatting can now be + combined with presentation specifiers as in ``std::format``. For example: + + .. code:: c++ + + #include + #include + + int main() { + std::locale::global(std::locale("fr_FR.UTF-8")); + fmt::print("{0:.2Lf}", 0.42); + } + + prints "0,42". The deprecated ``'n'`` specifier has been removed. + +* Made the ``0`` specifier ignored for infinity and NaN + (`#2305 `_, + `#2310 `_). + Thanks `@Liedtke (Matthias Liedtke) `_. + +* Made the hexfloat formatting use the right alignment by default + (`#2308 `_, + `#2317 `_). + Thanks `@Liedtke (Matthias Liedtke) `_. + +* Removed the deprecated numeric alignment (``'='``). Use the ``'0'`` specifier + instead. + +* Removed the deprecated ``fmt/posix.h`` header that has been replaced with + ``fmt/os.h``. + +* Removed the deprecated ``format_to_n_context``, ``format_to_n_args`` and + ``make_format_to_n_args``. They have been replaced with ``format_context``, + ``format_args` and ``make_format_args`` respectively. + +* Moved ``wchar_t``-specific functions and types to ``fmt/xchar.h``. + You can define ``FMT_DEPRECATED_INCLUDE_XCHAR`` to automatically include + ``fmt/xchar.h`` from ``fmt/format.h`` but this will be disabled in the next + major release. + +* Fixed handling of the ``'+'`` specifier in localized formatting + (`#2133 `_). + +* Added support for the ``'s'`` format specifier that gives textual + representation of ``bool`` + (`#2094 `_, + `#2109 `_). For example: + + .. code:: c++ + + #include + + int main() { + fmt::print("{:s}", true); + } + + prints "true". + Thanks `@powercoderlol (Ivan Polyakov) `_. + +* Made ``fmt::ptr`` work with function pointers + (`#2131 `_). For example: + + .. code:: c++ + + #include + + int main() { + fmt::print("My main: {}\n", fmt::ptr(main)); + } + + Thanks `@mikecrowe (Mike Crowe) `_. + +* Fixed ``fmt::formatted_size`` with format string compilation + (`#2141 `_, + `#2161 `_). + Thanks `@alexezeder (Alexey Ochapov) `_. + +* Fixed handling of empty format strings during format string compilation + (`#2042 `_): + + .. code:: c++ + + auto s = fmt::format(FMT_COMPILE("")); + + Thanks `@alexezeder (Alexey Ochapov) `_. + +* Fixed handling of enums in ``fmt::to_string`` + (`#2036 `_). + +* Improved width computation + (`#2033 `_, + `#2091 `_). For example: + + .. code:: c++ + + #include + + int main() { + fmt::print("{:-<10}{}\n", "你好", "世界"); + fmt::print("{:-<10}{}\n", "hello", "world"); + } + + prints + + .. image:: https://user-images.githubusercontent.com/576385/ + 119840373-cea3ca80-beb9-11eb-91e0-54266c48e181.png + + on a modern terminal. + +* The experimental fast output stream (``fmt::ostream``) is now truncated by + default for consistency with ``fopen`` + (`#2018 `_). For example: + + .. code:: c++ + + #include + + int main() { + fmt::ostream out1 = fmt::output_file("guide"); + out1.print("Zaphod"); + out1.close(); + fmt::ostream out2 = fmt::output_file("guide"); + out2.print("Ford"); + } + + writes "Ford" to the file "guide". To preserve the old file content if any + pass ``fmt::file::WRONLY | fmt::file::CREATE`` flags to ``fmt::output_file``. + +* Fixed moving of ``fmt::ostream`` that holds buffered data + (`#2197 `_, + `#2198 `_). + Thanks `@vtta `_. + +* Replaced the ``fmt::system_error`` exception with a function of the same + name that constructs ``std::system_error`` + (`#2266 `_). + +* Replaced the ``fmt::windows_error`` exception with a function of the same + name that constructs ``std::system_error`` with the category returned by + ``fmt::system_category()`` + (`#2274 `_, + `#2275 `_). + The latter is similar to ``std::sytem_category`` but correctly handles UTF-8. + Thanks `@phprus (Vladislav Shchapov) `_. + +* Replaced ``fmt::error_code`` with ``std::error_code`` and made it formattable + (`#2269 `_, + `#2270 `_, + `#2273 `_). + Thanks `@phprus (Vladislav Shchapov) `_. + +* Added speech synthesis support + (`#2206 `_). + +* Made ``format_to`` work with a memory buffer that has a custom allocator + (`#2300 `_). + Thanks `@voxmea `_. + +* Added ``Allocator::max_size`` support to ``basic_memory_buffer``. + (`#1960 `_). + Thanks `@phprus (Vladislav Shchapov) `_. + +* Added wide string support to ``fmt::join`` + (`#2236 `_). + Thanks `@crbrz `_. + +* Made iterators passed to ``formatter`` specializations via a format context + satisfy C++20 ``std::output_iterator`` requirements + (`#2156 `_, + `#2158 `_, + `#2195 `_, + `#2204 `_). + Thanks `@randomnetcat (Jason Cobb) `_. + +* Optimized the ``printf`` implementation + (`#1982 `_, + `#1984 `_, + `#2016 `_, + `#2164 `_). + Thanks `@rimathia `_ and + `@moiwi `_. + +* Improved detection of ``constexpr`` ``char_traits`` + (`#2246 `_, + `#2257 `_). + Thanks `@phprus (Vladislav Shchapov) `_. + +* Fixed writing to ``stdout`` when it is redirected to ``NUL`` on Windows + (`#2080 `_). + +* Fixed exception propagation from iterators + (`#2097 `_). + +* Improved ``strftime`` error handling + (`#2238 `_, + `#2244 `_). + Thanks `@yumeyao `_. + +* Stopped using deprecated GCC UDL template extension. + +* Added ``fmt/args.h`` to the install target + (`#2096 `_). + +* Error messages are now passed to assert when exceptions are disabled + (`#2145 `_). + Thanks `@NobodyXu (Jiahao XU) `_. + +* Added the ``FMT_MASTER_PROJECT`` CMake option to control build and install + targets when {fmt} is included via ``add_subdirectory`` + (`#2098 `_, + `#2100 `_). + Thanks `@randomizedthinking `_. + +* Improved build configuration + (`#2026 `_, + `#2122 `_). + Thanks `@luncliff (Park DongHa) `_ and + `@ibaned (Dan Ibanez) `_. + +* Fixed various warnings and compilation issues + (`#1947 `_, + `#1959 `_, + `#1963 `_, + `#1965 `_, + `#1966 `_, + `#1974 `_, + `#1975 `_, + `#1990 `_, + `#2000 `_, + `#2001 `_, + `#2002 `_, + `#2004 `_, + `#2006 `_, + `#2009 `_, + `#2010 `_, + `#2038 `_, + `#2039 `_, + `#2047 `_, + `#2053 `_, + `#2059 `_, + `#2065 `_, + `#2067 `_, + `#2068 `_, + `#2073 `_, + `#2103 `_ + `#2105 `_ + `#2106 `_, + `#2107 `_, + `#2116 `_ + `#2117 `_, + `#2118 `_ + `#2119 `_, + `#2127 `_, + `#2128 `_, + `#2140 `_, + `#2142 `_, + `#2143 `_, + `#2144 `_, + `#2147 `_, + `#2148 `_, + `#2149 `_, + `#2152 `_, + `#2160 `_, + `#2170 `_, + `#2175 `_, + `#2176 `_, + `#2177 `_, + `#2178 `_, + `#2179 `_, + `#2180 `_, + `#2181 `_, + `#2183 `_, + `#2184 `_, + `#2185 `_, + `#2186 `_, + `#2187 `_, + `#2190 `_, + `#2192 `_, + `#2194 `_, + `#2205 `_, + `#2210 `_, + `#2211 `_, + `#2215 `_, + `#2216 `_, + `#2218 `_, + `#2220 `_, + `#2228 `_, + `#2229 `_, + `#2230 `_, + `#2233 `_, + `#2239 `_, + `#2248 `_, + `#2252 `_, + `#2253 `_, + `#2255 `_, + `#2261 `_, + `#2278 `_, + `#2284 `_, + `#2287 `_, + `#2289 `_, + `#2290 `_, + `#2293 `_, + `#2295 `_, + `#2296 `_, + `#2297 `_, + `#2311 `_, + `#2313 `_, + `#2315 `_, + `#2320 `_, + `#2321 `_, + `#2323 `_, + `#2328 `_, + `#2329 `_, + `#2333 `_, + `#2338 `_, + `#2341 `_). + Thanks `@darklukee `_, + `@fagg (Ashton Fagg) `_, + `@killerbot242 (Lieven de Cock) `_, + `@jgopel (Jonathan Gopel) `_, + `@yeswalrus (Walter Gray) `_, + `@Finkman `_, + `@HazardyKnusperkeks (Björn Schäpers) `_, + `@dkavolis (Daumantas Kavolis) `_ + `@concatime (Issam Maghni) `_, + `@chronoxor (Ivan Shynkarenka) `_, + `@summivox (Yin Zhong) `_, + `@yNeo `_, + `@Apache-HB (Elliot) `_, + `@alexezeder (Alexey Ochapov) `_, + `@toojays (John Steele Scott) `_, + `@Brainy0207 `_, + `@vadz (VZ) `_, + `@imsherlock (Ryan Sherlock) `_, + `@phprus (Vladislav Shchapov) `_, + `@white238 (Chris White) `_, + `@yafshar (Yaser Afshar) `_, + `@BillyDonahue (Billy Donahue) `_, + `@jstaahl `_, + `@denchat `_, + `@DanielaE (Daniela Engert) `_, + `@ilyakurdyukov (Ilya Kurdyukov) `_, + `@ilmai `_, + `@JessyDL (Jessy De Lannoit) `_, + `@sergiud (Sergiu Deitsch) `_, + `@mwinterb `_, + `@sven-herrmann `_, + `@jmelas (John Melas) `_, + `@twoixter (Jose Miguel Pérez) `_, + `@crbrz `_, + `@upsj (Tobias Ribizel) `_. + +* Improved documentation + (`#1986 `_, + `#2051 `_, + `#2057 `_, + `#2081 `_, + `#2084 `_, + `#2312 `_). + Thanks `@imba-tjd (谭九鼎) `_, + `@0x416c69 (AlιAѕѕaѕѕιN) `_, + `@mordante `_. + +* Continuous integration and test improvements + (`#1969 `_, + `#1991 `_, + `#2020 `_, + `#2110 `_, + `#2114 `_, + `#2196 `_, + `#2217 `_, + `#2247 `_, + `#2256 `_, + `#2336 `_, + `#2346 `_). + Thanks `@jgopel (Jonathan Gopel) `_, + `@alexezeder (Alexey Ochapov) `_ and + `@DanielaE (Daniela Engert) `_. + 7.1.3 - 2020-11-24 ------------------ @@ -269,8 +965,8 @@ Thanks `@Naios (Denis Blank) `_. -* Made the ``#`` specifier emit trailing zeros in addition to the decimal point - (`#1797 `_). For example +* Made the ``'#'`` specifier emit trailing zeros in addition to the decimal + point (`#1797 `_). For example (`godbolt `__): .. code:: c++ diff --git a/deps/fmt/README.rst b/deps/fmt/README.rst index 7cf794e4a..02c849c78 100644 --- a/deps/fmt/README.rst +++ b/deps/fmt/README.rst @@ -26,9 +26,9 @@ **{fmt}** is an open-source formatting library providing a fast and safe alternative to C stdio and C++ iostreams. -If you like this project, please consider donating to BY_Help, -an initiative to help victims of political repressions in Belarus: -https://www.facebook.com/donate/199475051809330/. +If you like this project, please consider donating to the BYSOL +Foundation that helps victims of political repressions in Belarus: +https://bysol.org/en/bs/general/. `Documentation `__ @@ -341,6 +341,10 @@ Projects using this library * `Folly `_: Facebook open-source library +* `Grand Mountain Adventure + `_: + A beautiful open-world ski & snowboarding game + * `HarpyWar/pvpgn `_: Player vs Player Gaming Network with tweaks diff --git a/deps/fmt/include/fmt/chrono.h b/deps/fmt/include/fmt/chrono.h index e26e51903..c024fd710 100644 --- a/deps/fmt/include/fmt/chrono.h +++ b/deps/fmt/include/fmt/chrono.h @@ -15,7 +15,6 @@ #include #include "format.h" -#include "locale.h" FMT_BEGIN_NAMESPACE @@ -283,10 +282,80 @@ To safe_duration_cast(std::chrono::duration from, #define FMT_NOMACRO namespace detail { +template struct null {}; inline null<> localtime_r FMT_NOMACRO(...) { return null<>(); } inline null<> localtime_s(...) { return null<>(); } inline null<> gmtime_r(...) { return null<>(); } inline null<> gmtime_s(...) { return null<>(); } + +inline auto do_write(const std::tm& time, const std::locale& loc, char format, + char modifier) -> std::string { + auto&& os = std::ostringstream(); + os.imbue(loc); + using iterator = std::ostreambuf_iterator; + const auto& facet = std::use_facet>(loc); + auto end = facet.put(os, os, ' ', &time, format, modifier); + if (end.failed()) FMT_THROW(format_error("failed to format time")); + auto str = os.str(); + if (!detail::is_utf8() || loc == std::locale::classic()) return str; + // char16_t and char32_t codecvts are broken in MSVC (linkage errors) and + // gcc-4. +#if FMT_MSC_VER != 0 || \ + (defined(__GLIBCXX__) && !defined(_GLIBCXX_USE_DUAL_ABI)) + // The _GLIBCXX_USE_DUAL_ABI macro is always defined in libstdc++ from gcc-5 + // and newer. + using code_unit = wchar_t; +#else + using code_unit = char32_t; +#endif + auto& f = std::use_facet>(loc); + auto mb = std::mbstate_t(); + const char* from_next = nullptr; + code_unit* to_next = nullptr; + constexpr size_t buf_size = 32; + code_unit buf[buf_size] = {}; + auto result = f.in(mb, str.data(), str.data() + str.size(), from_next, buf, + buf + buf_size, to_next); + if (result != std::codecvt_base::ok) + FMT_THROW(format_error("failed to format time")); + str.clear(); + for (code_unit* p = buf; p != to_next; ++p) { + uint32_t c = static_cast(*p); + if (sizeof(code_unit) == 2 && c >= 0xd800 && c <= 0xdfff) { + // surrogate pair + ++p; + if (p == to_next || (c & 0xfc00) != 0xd800 || (*p & 0xfc00) != 0xdc00) { + FMT_THROW(format_error("failed to format time")); + } + c = (c << 10) + static_cast(*p) - 0x35fdc00; + } + if (c < 0x80) { + str.push_back(static_cast(c)); + } else if (c < 0x800) { + str.push_back(static_cast(0xc0 | (c >> 6))); + str.push_back(static_cast(0x80 | (c & 0x3f))); + } else if ((c >= 0x800 && c <= 0xd7ff) || (c >= 0xe000 && c <= 0xffff)) { + str.push_back(static_cast(0xe0 | (c >> 12))); + str.push_back(static_cast(0x80 | ((c & 0xfff) >> 6))); + str.push_back(static_cast(0x80 | (c & 0x3f))); + } else if (c >= 0x10000 && c <= 0x10ffff) { + str.push_back(static_cast(0xf0 | (c >> 18))); + str.push_back(static_cast(0x80 | ((c & 0x3ffff) >> 12))); + str.push_back(static_cast(0x80 | ((c & 0xfff) >> 6))); + str.push_back(static_cast(0x80 | (c & 0x3f))); + } else { + FMT_THROW(format_error("failed to format time")); + } + } + return str; +} + +template +auto write(OutputIt out, const std::tm& time, const std::locale& loc, + char format, char modifier = 0) -> OutputIt { + auto str = do_write(time, loc, format, modifier); + return std::copy(str.begin(), str.end(), out); +} } // namespace detail FMT_MODULE_EXPORT_BEGIN @@ -408,14 +477,37 @@ FMT_END_DETAIL_NAMESPACE template struct formatter, Char> : formatter { + FMT_CONSTEXPR formatter() { + this->specs = {default_specs, sizeof(default_specs) / sizeof(Char)}; + } + + template + FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) { + auto it = ctx.begin(); + if (it != ctx.end() && *it == ':') ++it; + auto end = it; + while (end != ctx.end() && *end != '}') ++end; + if (end != it) this->specs = {it, detail::to_unsigned(end - it)}; + return end; + } + template auto format(std::chrono::time_point val, FormatContext& ctx) -> decltype(ctx.out()) { std::tm time = localtime(val); return formatter::format(time, ctx); } + + static constexpr Char default_specs[] = {'%', 'Y', '-', '%', 'm', '-', + '%', 'd', ' ', '%', 'H', ':', + '%', 'M', ':', '%', 'S'}; }; +template +constexpr Char + formatter, + Char>::default_specs[]; + template struct formatter { template FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) { @@ -458,66 +550,28 @@ template struct formatter { FMT_BEGIN_DETAIL_NAMESPACE -template FMT_CONSTEXPR const char* get_units() { +template FMT_CONSTEXPR inline const char* get_units() { + if (std::is_same::value) return "as"; + if (std::is_same::value) return "fs"; + if (std::is_same::value) return "ps"; + if (std::is_same::value) return "ns"; + if (std::is_same::value) return "µs"; + if (std::is_same::value) return "ms"; + if (std::is_same::value) return "cs"; + if (std::is_same::value) return "ds"; + if (std::is_same>::value) return "s"; + if (std::is_same::value) return "das"; + if (std::is_same::value) return "hs"; + if (std::is_same::value) return "ks"; + if (std::is_same::value) return "Ms"; + if (std::is_same::value) return "Gs"; + if (std::is_same::value) return "Ts"; + if (std::is_same::value) return "Ps"; + if (std::is_same::value) return "Es"; + if (std::is_same>::value) return "m"; + if (std::is_same>::value) return "h"; return nullptr; } -template <> FMT_CONSTEXPR inline const char* get_units() { - return "as"; -} -template <> FMT_CONSTEXPR inline const char* get_units() { - return "fs"; -} -template <> FMT_CONSTEXPR inline const char* get_units() { - return "ps"; -} -template <> FMT_CONSTEXPR inline const char* get_units() { - return "ns"; -} -template <> FMT_CONSTEXPR inline const char* get_units() { - return "µs"; -} -template <> FMT_CONSTEXPR inline const char* get_units() { - return "ms"; -} -template <> FMT_CONSTEXPR inline const char* get_units() { - return "cs"; -} -template <> FMT_CONSTEXPR inline const char* get_units() { - return "ds"; -} -template <> FMT_CONSTEXPR inline const char* get_units>() { - return "s"; -} -template <> FMT_CONSTEXPR inline const char* get_units() { - return "das"; -} -template <> FMT_CONSTEXPR inline const char* get_units() { - return "hs"; -} -template <> FMT_CONSTEXPR inline const char* get_units() { - return "ks"; -} -template <> FMT_CONSTEXPR inline const char* get_units() { - return "Ms"; -} -template <> FMT_CONSTEXPR inline const char* get_units() { - return "Gs"; -} -template <> FMT_CONSTEXPR inline const char* get_units() { - return "Ts"; -} -template <> FMT_CONSTEXPR inline const char* get_units() { - return "Ps"; -} -template <> FMT_CONSTEXPR inline const char* get_units() { - return "Es"; -} -template <> FMT_CONSTEXPR inline const char* get_units>() { - return "m"; -} -template <> FMT_CONSTEXPR inline const char* get_units>() { - return "h"; -} enum class numeric_system { standard, @@ -683,34 +737,50 @@ FMT_CONSTEXPR const Char* parse_chrono_format(const Char* begin, return ptr; } -struct chrono_format_checker { - FMT_NORETURN void report_no_date() { FMT_THROW(format_error("no date")); } +template struct null_chrono_spec_handler { + FMT_CONSTEXPR void unsupported() { + static_cast(this)->unsupported(); + } + FMT_CONSTEXPR void on_abbr_weekday() { unsupported(); } + FMT_CONSTEXPR void on_full_weekday() { unsupported(); } + FMT_CONSTEXPR void on_dec0_weekday(numeric_system) { unsupported(); } + FMT_CONSTEXPR void on_dec1_weekday(numeric_system) { unsupported(); } + FMT_CONSTEXPR void on_abbr_month() { unsupported(); } + FMT_CONSTEXPR void on_full_month() { unsupported(); } + FMT_CONSTEXPR void on_24_hour(numeric_system) { unsupported(); } + FMT_CONSTEXPR void on_12_hour(numeric_system) { unsupported(); } + FMT_CONSTEXPR void on_minute(numeric_system) { unsupported(); } + FMT_CONSTEXPR void on_second(numeric_system) { unsupported(); } + FMT_CONSTEXPR void on_datetime(numeric_system) { unsupported(); } + FMT_CONSTEXPR void on_loc_date(numeric_system) { unsupported(); } + FMT_CONSTEXPR void on_loc_time(numeric_system) { unsupported(); } + FMT_CONSTEXPR void on_us_date() { unsupported(); } + FMT_CONSTEXPR void on_iso_date() { unsupported(); } + FMT_CONSTEXPR void on_12_hour_time() { unsupported(); } + FMT_CONSTEXPR void on_24_hour_time() { unsupported(); } + FMT_CONSTEXPR void on_iso_time() { unsupported(); } + FMT_CONSTEXPR void on_am_pm() { unsupported(); } + FMT_CONSTEXPR void on_duration_value() { unsupported(); } + FMT_CONSTEXPR void on_duration_unit() { unsupported(); } + FMT_CONSTEXPR void on_utc_offset() { unsupported(); } + FMT_CONSTEXPR void on_tz_name() { unsupported(); } +}; + +struct chrono_format_checker : null_chrono_spec_handler { + FMT_NORETURN void unsupported() { FMT_THROW(format_error("no date")); } template FMT_CONSTEXPR void on_text(const Char*, const Char*) {} - FMT_NORETURN void on_abbr_weekday() { report_no_date(); } - FMT_NORETURN void on_full_weekday() { report_no_date(); } - FMT_NORETURN void on_dec0_weekday(numeric_system) { report_no_date(); } - FMT_NORETURN void on_dec1_weekday(numeric_system) { report_no_date(); } - FMT_NORETURN void on_abbr_month() { report_no_date(); } - FMT_NORETURN void on_full_month() { report_no_date(); } FMT_CONSTEXPR void on_24_hour(numeric_system) {} FMT_CONSTEXPR void on_12_hour(numeric_system) {} FMT_CONSTEXPR void on_minute(numeric_system) {} FMT_CONSTEXPR void on_second(numeric_system) {} - FMT_NORETURN void on_datetime(numeric_system) { report_no_date(); } - FMT_NORETURN void on_loc_date(numeric_system) { report_no_date(); } - FMT_NORETURN void on_loc_time(numeric_system) { report_no_date(); } - FMT_NORETURN void on_us_date() { report_no_date(); } - FMT_NORETURN void on_iso_date() { report_no_date(); } FMT_CONSTEXPR void on_12_hour_time() {} FMT_CONSTEXPR void on_24_hour_time() {} FMT_CONSTEXPR void on_iso_time() {} FMT_CONSTEXPR void on_am_pm() {} FMT_CONSTEXPR void on_duration_value() {} FMT_CONSTEXPR void on_duration_unit() {} - FMT_NORETURN void on_utc_offset() { report_no_date(); } - FMT_NORETURN void on_tz_name() { report_no_date(); } }; template ::value)> @@ -957,14 +1027,9 @@ struct chrono_formatter { void format_localized(const tm& time, char format, char modifier = 0) { if (isnan(val)) return write_nan(); - auto locale = localized ? context.locale().template get() - : std::locale::classic(); - auto& facet = std::use_facet>(locale); - std::basic_ostringstream os; - os.imbue(locale); - facet.put(os, os, ' ', &time, format, modifier); - auto str = os.str(); - std::copy(str.begin(), str.end(), out); + const auto& loc = localized ? context.locale().template get() + : std::locale::classic(); + out = detail::write(out, time, loc, format, modifier); } void on_text(const char_type* begin, const char_type* end) { @@ -1080,6 +1145,46 @@ struct chrono_formatter { FMT_END_DETAIL_NAMESPACE +#if defined(__cpp_lib_chrono) && __cpp_lib_chrono >= 201907 +using weekday = std::chrono::weekday; +#else +// A fallback version of weekday. +class weekday { + private: + unsigned char value; + + public: + weekday() = default; + explicit constexpr weekday(unsigned wd) noexcept + : value(static_cast(wd != 7 ? wd : 0)) {} + constexpr unsigned c_encoding() const noexcept { return value; } +}; +#endif + +// A rudimentary weekday formatter. +template <> struct formatter { + private: + bool localized = false; + + public: + FMT_CONSTEXPR auto parse(format_parse_context& ctx) -> decltype(ctx.begin()) { + auto begin = ctx.begin(), end = ctx.end(); + if (begin != end && *begin == 'L') { + ++begin; + localized = true; + } + return begin; + } + + auto format(weekday wd, format_context& ctx) -> decltype(ctx.out()) { + auto time = std::tm(); + time.tm_wday = static_cast(wd.c_encoding()); + const auto& loc = localized ? ctx.locale().template get() + : std::locale::classic(); + return detail::write(ctx.out(), time, loc, 'a'); + } +}; + template struct formatter, Char> { private: @@ -1190,7 +1295,7 @@ struct formatter, Char> { ctx, out, d); f.precision = precision_copy; f.localized = localized; - parse_chrono_format(begin, end, f); + detail::parse_chrono_format(begin, end, f); } return detail::write( ctx.out(), basic_string_view(buf.data(), buf.size()), specs_copy); diff --git a/deps/fmt/include/fmt/color.h b/deps/fmt/include/fmt/color.h index 8cddbfe19..3d5490e87 100644 --- a/deps/fmt/include/fmt/color.h +++ b/deps/fmt/include/fmt/color.h @@ -507,7 +507,7 @@ void vformat_to(buffer& buf, const text_style& ts, auto background = detail::make_background_color(ts.get_background()); buf.append(background.begin(), background.end()); } - detail::vformat_to(buf, format_str, args); + detail::vformat_to(buf, format_str, args, {}); if (has_style) detail::reset_color(buf); } @@ -582,8 +582,8 @@ inline std::basic_string vformat( template > inline std::basic_string format(const text_style& ts, const S& format_str, const Args&... args) { - return vformat(ts, to_string_view(format_str), - fmt::make_args_checked(format_str, args...)); + return fmt::vformat(ts, to_string_view(format_str), + fmt::make_args_checked(format_str, args...)); } /** @@ -594,7 +594,7 @@ template format_str, basic_format_args>> args) { - decltype(detail::get_buffer(out)) buf(detail::get_buffer_init(out)); + auto&& buf = detail::get_buffer(out); detail::vformat_to(buf, ts, format_str, args); return detail::get_iterator(buf); } diff --git a/deps/fmt/include/fmt/compile.h b/deps/fmt/include/fmt/compile.h index 128004bc5..00000c92e 100644 --- a/deps/fmt/include/fmt/compile.h +++ b/deps/fmt/include/fmt/compile.h @@ -157,13 +157,15 @@ struct is_compiled_string : std::is_base_of {}; \endrst */ #ifdef __cpp_if_constexpr -# define FMT_COMPILE(s) FMT_STRING_IMPL(s, fmt::detail::compiled_string) +# define FMT_COMPILE(s) \ + FMT_STRING_IMPL(s, fmt::detail::compiled_string, explicit) #else # define FMT_COMPILE(s) FMT_STRING(s) #endif #if FMT_USE_NONTYPE_TEMPLATE_PARAMETERS -template Str> +template Str> struct udl_compiled_string : compiled_string { using char_type = Char; constexpr operator basic_string_view() const { @@ -382,24 +384,22 @@ constexpr parse_specs_result parse_specs(basic_string_view str, } template struct arg_id_handler { - constexpr void on_error(const char* message) { throw format_error(message); } + arg_ref arg_id; - constexpr int on_arg_id() { + constexpr int operator()() { FMT_ASSERT(false, "handler cannot be used with automatic indexing"); return 0; } - - constexpr int on_arg_id(int id) { + constexpr int operator()(int id) { + arg_id = arg_ref(id); + return 0; + } + constexpr int operator()(basic_string_view id) { arg_id = arg_ref(id); return 0; } - constexpr int on_arg_id(basic_string_view id) { - arg_id = arg_ref(id); - return 0; - } - - arg_ref arg_id; + constexpr void on_error(const char* message) { throw format_error(message); } }; template struct parse_arg_id_result { @@ -410,8 +410,7 @@ template struct parse_arg_id_result { template constexpr auto parse_arg_id(const Char* begin, const Char* end) { auto handler = arg_id_handler{arg_ref{}}; - auto adapter = id_adapter, Char>{handler, 0}; - auto arg_id_end = parse_arg_id(begin, end, adapter); + auto arg_id_end = parse_arg_id(begin, end, handler); return parse_arg_id_result{handler.arg_id, arg_id_end}; } @@ -428,7 +427,7 @@ template constexpr auto parse_replacement_field_then_tail(S format_str) { using char_type = typename S::char_type; - constexpr basic_string_view str = format_str; + constexpr auto str = basic_string_view(format_str); constexpr char_type c = END_POS != str.size() ? str[END_POS] : char_type(); if constexpr (c == '}') { return parse_tail( @@ -449,7 +448,7 @@ constexpr auto parse_replacement_field_then_tail(S format_str) { template constexpr auto compile_format_string(S format_str) { using char_type = typename S::char_type; - constexpr basic_string_view str = format_str; + constexpr auto str = basic_string_view(format_str); if constexpr (str[POS] == '{') { if constexpr (POS + 1 == str.size()) throw format_error("unmatched '{' in format string"); @@ -518,7 +517,7 @@ constexpr auto compile_format_string(S format_str) { template ::value)> constexpr auto compile(S format_str) { - constexpr basic_string_view str = format_str; + constexpr auto str = basic_string_view(format_str); if constexpr (str.size() == 0) { return detail::make_text(str, 0, 0); } else { @@ -557,7 +556,7 @@ template format(const S&, Args&&... args) { if constexpr (std::is_same::value) { - constexpr basic_string_view str = S(); + constexpr auto str = basic_string_view(S()); if constexpr (str.size() == 2 && str[0] == '{' && str[1] == '}') { const auto& first = detail::first(args...); if constexpr (detail::is_named_arg< @@ -608,9 +607,23 @@ size_t formatted_size(const S& format_str, const Args&... args) { return format_to(detail::counting_iterator(), format_str, args...).count(); } +template ::value)> +void print(std::FILE* f, const S& format_str, const Args&... args) { + memory_buffer buffer; + format_to(std::back_inserter(buffer), format_str, args...); + detail::print(f, {buffer.data(), buffer.size()}); +} + +template ::value)> +void print(const S& format_str, const Args&... args) { + print(stdout, format_str, args...); +} + #if FMT_USE_NONTYPE_TEMPLATE_PARAMETERS inline namespace literals { -template +template constexpr detail::udl_compiled_string< remove_cvref_t, sizeof(Str.data) / sizeof(decltype(Str.data[0])), Str> diff --git a/deps/fmt/include/fmt/core.h b/deps/fmt/include/fmt/core.h index 293d82425..d058398ac 100644 --- a/deps/fmt/include/fmt/core.h +++ b/deps/fmt/include/fmt/core.h @@ -1,4 +1,4 @@ -// Formatting library for C++ - the core API +// Formatting library for C++ - the core API for char/UTF-8 // // Copyright (c) 2012 - present, Victor Zverovich // All rights reserved. @@ -8,15 +8,15 @@ #ifndef FMT_CORE_H_ #define FMT_CORE_H_ -#include // INT_MAX -#include // std::FILE +#include // std::FILE #include #include +#include #include #include // The fmt library version in the form major * 10000 + minor * 100 + patch. -#define FMT_VERSION 70104 +#define FMT_VERSION 80001 #ifdef __clang__ # define FMT_CLANG_VERSION (__clang_major__ * 100 + __clang_minor__) @@ -24,7 +24,7 @@ # define FMT_CLANG_VERSION 0 #endif -#if defined(__GNUC__) && !defined(__clang__) +#if defined(__GNUC__) && !defined(__clang__) && !defined(__INTEL_COMPILER) # define FMT_GCC_VERSION (__GNUC__ * 100 + __GNUC_MINOR__) # define FMT_GCC_PRAGMA(arg) _Pragma(arg) #else @@ -32,35 +32,16 @@ # define FMT_GCC_PRAGMA(arg) #endif -#if defined(__INTEL_COMPILER) -# define FMT_ICC_VERSION __INTEL_COMPILER -#else -# define FMT_ICC_VERSION 0 -#endif - #if __cplusplus >= 201103L || defined(__GXX_EXPERIMENTAL_CXX0X__) # define FMT_HAS_GXX_CXX11 FMT_GCC_VERSION #else # define FMT_HAS_GXX_CXX11 0 #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) -# if __cplusplus >= 201703L && _LIBCPP_VERSION >= 4000 -# define FMT_CONSTEXPR_CHAR_TRAITS constexpr -# endif -#elif defined(_MSC_VER) -# if _MSVC_LANG >= 201703L && _MSC_VER >= 1914 -# define FMT_CONSTEXPR_CHAR_TRAITS constexpr -# endif -#endif -#ifndef FMT_CONSTEXPR_CHAR_TRAITS -# define FMT_CONSTEXPR_CHAR_TRAITS +#if defined(__INTEL_COMPILER) +# define FMT_ICC_VERSION __INTEL_COMPILER +#else +# define FMT_ICC_VERSION 0 #endif #ifdef __NVCC__ @@ -83,7 +64,8 @@ # define FMT_HAS_FEATURE(x) 0 #endif -#if defined(__has_include) && !defined(__INTELLISENSE__) && \ +#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 @@ -118,6 +100,22 @@ # define FMT_CONSTEXPR_DECL #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 + #ifndef FMT_OVERRIDE # if FMT_HAS_FEATURE(cxx_override_control) || \ (FMT_GCC_VERSION >= 408 && FMT_HAS_GXX_CXX11) || FMT_MSC_VER >= 1900 @@ -176,25 +174,32 @@ # endif #endif -#ifndef FMT_DEPRECATED -# if FMT_HAS_CPP14_ATTRIBUTE(deprecated) || FMT_MSC_VER >= 1900 -# define FMT_DEPRECATED [[deprecated]] +#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 -# 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 +# define FMT_FALLTHROUGH # endif +#elif FMT_HAS_CPP17_ATTRIBUTE(fallthrough) || \ + (defined(_MSVC_LANG) && _MSVC_LANG >= 201703L) +# define FMT_FALLTHROUGH [[fallthrough]] +#else +# define FMT_FALLTHROUGH #endif -// Workaround broken [[deprecated]] in the Intel, PGI and NVCC compilers. -#if FMT_ICC_VERSION || defined(__PGI) || FMT_NVCC -# define FMT_DEPRECATED_ALIAS -#else -# define FMT_DEPRECATED_ALIAS FMT_DEPRECATED +#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 @@ -224,30 +229,20 @@ # define FMT_INLINE_NAMESPACE namespace # define FMT_END_NAMESPACE \ } \ - using namespace v7; \ + using namespace v8; \ } # endif # define FMT_BEGIN_NAMESPACE \ namespace fmt { \ - FMT_INLINE_NAMESPACE v7 { + FMT_INLINE_NAMESPACE v8 { #endif #ifndef FMT_MODULE_EXPORT # define FMT_MODULE_EXPORT -#endif -#ifndef FMT_MODULE_EXPORT_BEGIN # define FMT_MODULE_EXPORT_BEGIN -#endif -#ifndef FMT_MODULE_EXPORT_END # define FMT_MODULE_EXPORT_END -#endif -#ifndef FMT_BEGIN_DETAIL_NAMESPACE -# define FMT_BEGIN_DETAIL_NAMESPACE \ - namespace detail { -#endif -#ifndef FMT_END_DETAIL_NAMESPACE -# define FMT_END_DETAIL_NAMESPACE \ - } +# define FMT_BEGIN_DETAIL_NAMESPACE namespace detail { +# define FMT_END_DETAIL_NAMESPACE } #endif #if !defined(FMT_HEADER_ONLY) && defined(_WIN32) @@ -259,6 +254,11 @@ # 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 @@ -285,8 +285,16 @@ # define FMT_UNICODE !FMT_MSC_VER #endif -#ifndef FMT_COMPILE_TIME_CHECKS -# define FMT_COMPILE_TIME_CHECKS 0 +#ifndef FMT_CONSTEVAL +# if ((FMT_GCC_VERSION >= 1000 || FMT_CLANG_VERSION >= 1101) && \ + __cplusplus > 201703L) || \ + (defined(__cpp_consteval) && \ + !FMT_MSC_VER) // consteval is broken in MSVC. +# define FMT_CONSTEVAL consteval +# define FMT_HAS_CONSTEVAL +# else +# define FMT_CONSTEVAL +# endif #endif #ifndef FMT_USE_NONTYPE_TEMPLATE_PARAMETERS @@ -309,21 +317,26 @@ FMT_BEGIN_NAMESPACE FMT_MODULE_EXPORT_BEGIN // Implementations of enable_if_t and other metafunctions for older systems. -template +template using enable_if_t = typename std::enable_if::type; -template +template using conditional_t = typename std::conditional::type; template using bool_constant = std::integral_constant; template using remove_reference_t = typename std::remove_reference::type; template -using remove_const_t = typename std::remove_const::type; -template using remove_cvref_t = typename std::remove_cv>::type; template struct type_identity { using type = T; }; template using type_identity_t = typename type_identity::type; -struct monostate {}; +struct monostate { + constexpr monostate() {} +}; + +// 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 FMT_CONSTEXPR void ignore_unused(const T&...) {} // 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 @@ -336,7 +349,7 @@ struct monostate {}; FMT_BEGIN_DETAIL_NAMESPACE -constexpr FMT_INLINE bool is_constant_evaluated() FMT_NOEXCEPT { +constexpr FMT_INLINE auto is_constant_evaluated() FMT_NOEXCEPT -> bool { #ifdef __cpp_lib_is_constant_evaluated return std::is_constant_evaluated(); #else @@ -344,8 +357,8 @@ constexpr FMT_INLINE bool is_constant_evaluated() FMT_NOEXCEPT { #endif } -// A helper function to suppress "conditional expression is constant" warnings. -template constexpr T const_check(T value) { return value; } +// A function to suppress "conditional expression is constant" warnings. +template constexpr auto const_check(T value) -> T { return value; } FMT_NORETURN FMT_API void assert_fail(const char* file, int line, const char* message); @@ -353,7 +366,8 @@ FMT_NORETURN FMT_API void assert_fail(const char* file, int line, #ifndef FMT_ASSERT # ifdef NDEBUG // FMT_ASSERT is not empty to avoid -Werror=empty-body. -# define FMT_ASSERT(condition, message) ((void)0) +# define FMT_ASSERT(condition, message) \ + ::fmt::ignore_unused((condition), (message)) # else # define FMT_ASSERT(condition, message) \ ((condition) /* void() fails with -Winvalid-constexpr on clang 4.0.1 */ \ @@ -378,28 +392,38 @@ template struct std_string_view {}; # define FMT_USE_INT128 1 using int128_t = __int128_t; using uint128_t = __uint128_t; +template inline auto convert_for_visit(T value) -> T { + return value; +} #else # define FMT_USE_INT128 0 #endif #if !FMT_USE_INT128 -struct int128_t {}; -struct uint128_t {}; +enum class int128_t {}; +enum class uint128_t {}; +// Reduce template instantiations. +template inline auto convert_for_visit(T) -> monostate { + return {}; +} #endif // Casts a nonnegative integer to unsigned. template -FMT_CONSTEXPR typename std::make_unsigned::type to_unsigned(Int value) { +FMT_CONSTEXPR auto to_unsigned(Int value) -> + typename std::make_unsigned::type { FMT_ASSERT(value >= 0, "negative value"); return static_cast::type>(value); } FMT_MSC_WARNING(suppress : 4566) constexpr unsigned char micro[] = "\u00B5"; -template constexpr bool is_unicode() { - return FMT_UNICODE || sizeof(Char) != 1 || - (sizeof(micro) == 3 && micro[0] == 0xC2 && micro[1] == 0xB5); +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 /** @@ -454,15 +478,17 @@ template class basic_string_view { size_(s.size()) {} /** Returns a pointer to the string data. */ - constexpr const Char* data() const { return data_; } + constexpr auto data() const -> const Char* { return data_; } /** Returns the string size. */ - constexpr size_t size() const { return size_; } + constexpr auto size() const -> size_t { return size_; } - constexpr iterator begin() const { return data_; } - constexpr iterator end() const { return data_ + size_; } + constexpr auto begin() const -> iterator { return data_; } + constexpr auto end() const -> iterator { return data_ + size_; } - constexpr const Char& operator[](size_t pos) const { return data_[pos]; } + constexpr auto operator[](size_t pos) const -> const Char& { + return data_[pos]; + } FMT_CONSTEXPR void remove_prefix(size_t n) { data_ += n; @@ -470,7 +496,7 @@ template class basic_string_view { } // Lexicographically compare this string reference to other. - FMT_CONSTEXPR_CHAR_TRAITS int compare(basic_string_view other) const { + 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::compare(data_, other.data_, str_size); if (result == 0) @@ -478,70 +504,53 @@ template class basic_string_view { return result; } - FMT_CONSTEXPR_CHAR_TRAITS friend bool operator==(basic_string_view lhs, - basic_string_view rhs) { + FMT_CONSTEXPR_CHAR_TRAITS friend auto operator==(basic_string_view lhs, + basic_string_view rhs) + -> bool { return lhs.compare(rhs) == 0; } - friend bool operator!=(basic_string_view lhs, basic_string_view rhs) { + friend auto operator!=(basic_string_view lhs, basic_string_view rhs) -> bool { return lhs.compare(rhs) != 0; } - friend bool operator<(basic_string_view lhs, basic_string_view rhs) { + friend auto operator<(basic_string_view lhs, basic_string_view rhs) -> bool { return lhs.compare(rhs) < 0; } - friend bool operator<=(basic_string_view lhs, basic_string_view rhs) { + friend auto operator<=(basic_string_view lhs, basic_string_view rhs) -> bool { return lhs.compare(rhs) <= 0; } - friend bool operator>(basic_string_view lhs, basic_string_view rhs) { + friend auto operator>(basic_string_view lhs, basic_string_view rhs) -> bool { return lhs.compare(rhs) > 0; } - friend bool operator>=(basic_string_view lhs, basic_string_view rhs) { + friend auto operator>=(basic_string_view lhs, basic_string_view rhs) -> bool { return lhs.compare(rhs) >= 0; } }; using string_view = basic_string_view; -using wstring_view = basic_string_view; /** Specifies if ``T`` is a character type. Can be specialized by users. */ template struct is_char : std::false_type {}; template <> struct is_char : std::true_type {}; -template <> struct is_char : std::true_type {}; -/** - \rst - Returns a string view of `s`. In order to add custom string type support to - {fmt} provide an overload of `to_string_view` for it in the same namespace as - the type for the argument-dependent lookup to work. - - **Example**:: - - namespace my_ns { - inline string_view to_string_view(const my_string& s) { - return {s.data(), s.length()}; - } - } - std::string message = fmt::format(my_string("The answer is {}"), 42); - \endrst - */ +// Returns a string view of `s`. template ::value)> -FMT_INLINE basic_string_view to_string_view(const Char* s) { +FMT_INLINE auto to_string_view(const Char* s) -> basic_string_view { return s; } - template -inline basic_string_view to_string_view( - const std::basic_string& s) { +inline auto to_string_view(const std::basic_string& s) + -> basic_string_view { return s; } - template -constexpr basic_string_view to_string_view(basic_string_view s) { +constexpr auto to_string_view(basic_string_view s) + -> basic_string_view { return s; } - template >::value)> -inline basic_string_view to_string_view(detail::std_string_view s) { +inline auto to_string_view(detail::std_string_view s) + -> basic_string_view { return s; } @@ -553,14 +562,15 @@ template struct is_compile_string : std::is_base_of {}; template ::value)> -constexpr basic_string_view to_string_view(const S& s) { - return s; +constexpr auto to_string_view(const S& s) + -> basic_string_view { + return basic_string_view(s); } FMT_BEGIN_DETAIL_NAMESPACE void to_string_view(...); -using fmt::v7::to_string_view; +using fmt::v8::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 @@ -594,7 +604,6 @@ struct error_handler { // 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. */ @@ -604,16 +613,7 @@ template using char_t = typename detail::char_t_impl::type; \rst Parsing context consisting of a format string range being parsed and an argument counter for automatic indexing. - - You can use one of the following type aliases for common character types: - - +-----------------------+-------------------------------------+ - | Type | Definition | - +=======================+=====================================+ - | format_parse_context | basic_format_parse_context | - +-----------------------+-------------------------------------+ - | wformat_parse_context | basic_format_parse_context | - +-----------------------+-------------------------------------+ + You can use the ``format_parse_context`` type alias for ``char`` instead. \endrst */ template @@ -635,12 +635,16 @@ class basic_format_parse_context : private ErrorHandler { Returns an iterator to the beginning of the format string range being parsed. */ - constexpr iterator begin() const FMT_NOEXCEPT { return format_str_.begin(); } + 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 iterator end() const FMT_NOEXCEPT { return format_str_.end(); } + constexpr auto end() const FMT_NOEXCEPT -> iterator { + return format_str_.end(); + } /** Advances the begin iterator to ``it``. */ FMT_CONSTEXPR void advance_to(iterator it) { @@ -651,7 +655,7 @@ class basic_format_parse_context : private ErrorHandler { Reports an error if using the manual argument indexing; otherwise returns the next argument index and switches to the automatic indexing. */ - FMT_CONSTEXPR int next_arg_id() { + 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_++; @@ -676,11 +680,10 @@ class basic_format_parse_context : private ErrorHandler { ErrorHandler::on_error(message); } - constexpr ErrorHandler error_handler() const { return *this; } + constexpr auto error_handler() const -> ErrorHandler { return *this; } }; using format_parse_context = basic_format_parse_context; -using wformat_parse_context = basic_format_parse_context; template class basic_format_arg; template class basic_format_args; @@ -693,7 +696,6 @@ struct formatter { formatter() = delete; }; -// DEPRECATED! // 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 @@ -705,11 +707,14 @@ template struct is_contiguous : std::false_type {}; template struct is_contiguous> : std::true_type {}; +class appender; + FMT_BEGIN_DETAIL_NAMESPACE // Extracts a reference to the container from back_insert_iterator. template -inline Container& get_container(std::back_insert_iterator it) { +inline auto get_container(std::back_insert_iterator it) + -> Container& { using bi_iterator = std::back_insert_iterator; struct accessor : bi_iterator { accessor(bi_iterator iter) : bi_iterator(iter) {} @@ -718,6 +723,23 @@ inline Container& get_container(std::back_insert_iterator it) { return *accessor(it).container; } +template +FMT_CONSTEXPR auto copy_str(InputIt begin, InputIt end, OutputIt out) + -> OutputIt { + while (begin != end) *out++ = static_cast(*begin++); + return out; +} + +template ::value)> +FMT_CONSTEXPR auto copy_str(const Char* begin, const Char* end, Char* out) + -> Char* { + if (is_constant_evaluated()) + return copy_str(begin, end, out); + auto size = to_unsigned(end - begin); + memcpy(out, begin, size); + return out + size; +} + /** \rst A contiguous memory buffer with an optional growing ability. It is an internal @@ -741,6 +763,7 @@ template class buffer { capacity_(cap) {} ~buffer() = default; + buffer(buffer&&) = default; /** Sets the buffer data and capacity. */ void set(T* buf_data, size_t buf_capacity) FMT_NOEXCEPT { @@ -758,23 +781,23 @@ template class buffer { buffer(const buffer&) = delete; void operator=(const buffer&) = delete; - T* begin() FMT_NOEXCEPT { return ptr_; } - T* end() FMT_NOEXCEPT { return ptr_ + size_; } + auto begin() FMT_NOEXCEPT -> T* { return ptr_; } + auto end() FMT_NOEXCEPT -> T* { return ptr_ + size_; } - const T* begin() const FMT_NOEXCEPT { return ptr_; } - const T* end() const FMT_NOEXCEPT { 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. */ - size_t size() const FMT_NOEXCEPT { return size_; } + auto size() const FMT_NOEXCEPT -> size_t { return size_; } /** Returns the capacity of this buffer. */ - size_t capacity() const FMT_NOEXCEPT { return capacity_; } + auto capacity() const FMT_NOEXCEPT -> size_t { return capacity_; } /** Returns a pointer to the buffer data. */ - T* data() FMT_NOEXCEPT { return ptr_; } + auto data() FMT_NOEXCEPT -> T* { return ptr_; } /** Returns a pointer to the buffer data. */ - const T* data() const FMT_NOEXCEPT { return ptr_; } + auto data() const FMT_NOEXCEPT -> const T* { return ptr_; } /** Clears this buffer. */ void clear() { size_ = 0; } @@ -802,16 +825,16 @@ template class buffer { /** Appends data to the end of the buffer. */ template void append(const U* begin, const U* end); - template T& operator[](I index) { return ptr_[index]; } - template const T& operator[](I index) const { + template auto operator[](I index) -> T& { return ptr_[index]; } + template auto operator[](I index) const -> const T& { return ptr_[index]; } }; struct buffer_traits { explicit buffer_traits(size_t) {} - size_t count() const { return 0; } - size_t limit(size_t size) { return size; } + auto count() const -> size_t { return 0; } + auto limit(size_t size) -> size_t { return size; } }; class fixed_buffer_traits { @@ -821,8 +844,8 @@ class fixed_buffer_traits { public: explicit fixed_buffer_traits(size_t limit) : limit_(limit) {} - size_t count() const { return count_; } - size_t limit(size_t size) { + 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; @@ -841,18 +864,25 @@ class iterator_buffer final : public Traits, public buffer { void grow(size_t) final FMT_OVERRIDE { if (this->size() == buffer_size) flush(); } - void flush(); + + void flush() { + auto size = this->size(); + this->clear(); + out_ = copy_str(data_, data_ + this->limit(size), out_); + } public: explicit iterator_buffer(OutputIt out, size_t n = buffer_size) : Traits(n), buffer(data_, 0, buffer_size), out_(out) {} + iterator_buffer(iterator_buffer&& other) + : Traits(other), buffer(data_, 0, buffer_size), out_(other.out_) {} ~iterator_buffer() { flush(); } - OutputIt out() { + auto out() -> OutputIt { flush(); return out_; } - size_t count() const { return Traits::count() + this->size(); } + auto count() const -> size_t { return Traits::count() + this->size(); } }; template class iterator_buffer final : public buffer { @@ -862,7 +892,7 @@ template class iterator_buffer final : public buffer { public: explicit iterator_buffer(T* out, size_t = 0) : buffer(out, 0, ~size_t()) {} - T* out() { return &*this->end(); } + auto out() -> T* { return &*this->end(); } }; // A buffer that writes to a container with the contiguous storage. @@ -885,7 +915,7 @@ class iterator_buffer, : buffer(c.size()), container_(c) {} explicit iterator_buffer(std::back_insert_iterator out, size_t = 0) : iterator_buffer(get_container(out)) {} - std::back_insert_iterator out() { + auto out() -> std::back_insert_iterator { return std::back_inserter(container_); } }; @@ -907,49 +937,24 @@ template class counting_buffer final : public buffer { public: counting_buffer() : buffer(data_, 0, buffer_size) {} - size_t count() { return count_ + this->size(); } + auto count() -> size_t { return count_ + this->size(); } }; -// An output iterator that appends to the buffer. -// It is used to reduce symbol sizes for the common case. template -class buffer_appender : public std::back_insert_iterator> { - using base = std::back_insert_iterator>; +using buffer_appender = conditional_t::value, appender, + std::back_insert_iterator>>; - public: - using std::back_insert_iterator>::back_insert_iterator; - buffer_appender(base it) : base(it) {} - using _Unchecked_type = buffer_appender; // Mark iterator as checked. - - buffer_appender& operator++() { - base::operator++(); - return *this; - } - - buffer_appender operator++(int) { - buffer_appender tmp = *this; - ++*this; - return tmp; - } -}; - -// Maps an output iterator into a buffer. +// Maps an output iterator to a buffer. template -iterator_buffer get_buffer(OutputIt); -template buffer& get_buffer(buffer_appender); - -template OutputIt get_buffer_init(OutputIt out) { - return out; -} -template buffer& get_buffer_init(buffer_appender out) { - return get_container(out); +auto get_buffer(OutputIt out) -> iterator_buffer { + return iterator_buffer(out); } template auto get_iterator(Buffer& buf) -> decltype(buf.out()) { return buf.out(); } -template buffer_appender get_iterator(buffer& buf) { +template auto get_iterator(buffer& buf) -> buffer_appender { return buffer_appender(buf); } @@ -986,8 +991,8 @@ struct arg_data { template arg_data(const U&... init) : args_{T(named_args_, NUM_NAMED_ARGS), init...} {} arg_data(const arg_data& other) = delete; - const T* args() const { return args_ + 1; } - named_arg_info* named_args() { return named_args_; } + auto args() const -> const T* { return args_ + 1; } + auto named_args() -> named_arg_info* { return named_args_; } }; template @@ -997,8 +1002,10 @@ struct arg_data { template FMT_CONSTEXPR FMT_INLINE arg_data(const U&... init) : args_{init...} {} - FMT_CONSTEXPR FMT_INLINE const T* args() const { return args_; } - FMT_CONSTEXPR FMT_INLINE std::nullptr_t named_args() { return nullptr; } + FMT_CONSTEXPR FMT_INLINE auto args() const -> const T* { return args_; } + FMT_CONSTEXPR FMT_INLINE auto named_args() -> std::nullptr_t { + return nullptr; + } }; template @@ -1029,12 +1036,12 @@ template FMT_CONSTEXPR FMT_INLINE void init_named_args(std::nullptr_t, int, int, const Args&...) {} -template constexpr size_t count() { return B ? 1 : 0; } -template constexpr size_t count() { +template constexpr auto count() -> size_t { return B ? 1 : 0; } +template constexpr auto count() -> size_t { return (B1 ? 1 : 0) + count(); } -template constexpr size_t count_named_args() { +template constexpr auto count_named_args() -> size_t { return count::value...>(); } @@ -1115,6 +1122,7 @@ template class value { using char_type = typename Context::char_type; union { + monostate no_value; int int_value; unsigned uint_value; long long long_long_value; @@ -1132,7 +1140,8 @@ template class value { named_arg_value named_args; }; - constexpr FMT_INLINE value(int val = 0) : int_value(val) {} + 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) {} @@ -1155,7 +1164,7 @@ template class value { FMT_INLINE value(const named_arg_info* args, size_t size) : named_args{args, size} {} - template FMT_INLINE value(const T& val) { + template FMT_CONSTEXPR FMT_INLINE value(const T& val) { custom.value = &val; // Get the formatter type through the context to allow different contexts // have different extension points, e.g. `formatter` for `format` and @@ -1179,7 +1188,7 @@ template class value { }; template -FMT_CONSTEXPR basic_format_arg make_arg(const T& value); +FMT_CONSTEXPR auto make_arg(const T& value) -> basic_format_arg; // 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. @@ -1193,40 +1202,52 @@ struct unformattable {}; template struct arg_mapper { using char_type = typename Context::char_type; - FMT_CONSTEXPR FMT_INLINE int map(signed char val) { return val; } - FMT_CONSTEXPR FMT_INLINE unsigned map(unsigned char val) { return val; } - FMT_CONSTEXPR FMT_INLINE int map(short val) { return val; } - FMT_CONSTEXPR FMT_INLINE unsigned map(unsigned short val) { return val; } - FMT_CONSTEXPR FMT_INLINE int map(int val) { return val; } - FMT_CONSTEXPR FMT_INLINE unsigned map(unsigned val) { return val; } - FMT_CONSTEXPR FMT_INLINE long_type map(long val) { return val; } - FMT_CONSTEXPR FMT_INLINE ulong_type map(unsigned long val) { return val; } - FMT_CONSTEXPR FMT_INLINE long long map(long long val) { return val; } - FMT_CONSTEXPR FMT_INLINE unsigned long long map(unsigned long long val) { + 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 int128_t map(int128_t val) { return val; } - FMT_CONSTEXPR FMT_INLINE uint128_t map(uint128_t val) { return val; } - FMT_CONSTEXPR FMT_INLINE bool map(bool val) { 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 ::value)> - FMT_CONSTEXPR FMT_INLINE char_type map(T val) { + FMT_CONSTEXPR FMT_INLINE auto map(T val) -> char_type { static_assert( std::is_same::value || std::is_same::value, "mixing character types is disallowed"); return val; } - FMT_CONSTEXPR FMT_INLINE float map(float val) { return val; } - FMT_CONSTEXPR FMT_INLINE double map(double val) { return val; } - FMT_CONSTEXPR FMT_INLINE long double map(long double val) { return val; } + 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 const char_type* map(char_type* val) { return val; } - FMT_CONSTEXPR FMT_INLINE const char_type* map(const char_type* 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 ::value)> - FMT_CONSTEXPR FMT_INLINE basic_string_view map(const T& val) { + FMT_CONSTEXPR FMT_INLINE auto map(const T& val) + -> basic_string_view { static_assert(std::is_same>::value, "mixing character types is disallowed"); return to_string_view(val); @@ -1236,7 +1257,8 @@ template struct arg_mapper { std::is_constructible, T>::value && !is_string::value && !has_formatter::value && !has_fallback_formatter::value)> - FMT_CONSTEXPR FMT_INLINE basic_string_view map(const T& val) { + FMT_CONSTEXPR FMT_INLINE auto map(const T& val) + -> basic_string_view { return basic_string_view(val); } template < @@ -1246,29 +1268,34 @@ template struct arg_mapper { !std::is_constructible, T>::value && !is_string::value && !has_formatter::value && !has_fallback_formatter::value)> - FMT_CONSTEXPR FMT_INLINE basic_string_view map(const T& val) { + FMT_CONSTEXPR FMT_INLINE auto map(const T& val) + -> basic_string_view { return std_string_view(val); } - FMT_CONSTEXPR FMT_INLINE const char* map(const signed char* val) { + FMT_CONSTEXPR FMT_INLINE auto map(const signed char* val) -> const char* { static_assert(std::is_same::value, "invalid string type"); return reinterpret_cast(val); } - FMT_CONSTEXPR FMT_INLINE const char* map(const unsigned char* val) { + FMT_CONSTEXPR FMT_INLINE auto map(const unsigned char* val) -> const char* { static_assert(std::is_same::value, "invalid string type"); return reinterpret_cast(val); } - FMT_CONSTEXPR FMT_INLINE const char* map(signed char* val) { + FMT_CONSTEXPR FMT_INLINE auto map(signed char* val) -> const char* { const auto* const_val = val; return map(const_val); } - FMT_CONSTEXPR FMT_INLINE const char* map(unsigned char* val) { + FMT_CONSTEXPR FMT_INLINE auto map(unsigned char* val) -> const char* { const auto* const_val = val; return map(const_val); } - FMT_CONSTEXPR FMT_INLINE const void* map(void* val) { return val; } - FMT_CONSTEXPR FMT_INLINE const void* map(const void* val) { return val; } - FMT_CONSTEXPR FMT_INLINE const void* map(std::nullptr_t val) { return 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. @@ -1300,7 +1327,7 @@ template struct arg_mapper { FMT_ENABLE_IF(!is_string::value && !is_char::value && (has_formatter::value || has_fallback_formatter::value))> - FMT_CONSTEXPR FMT_INLINE const T& map(const T& val) { + FMT_CONSTEXPR FMT_INLINE auto map(const T& val) -> const T& { return val; } @@ -1310,7 +1337,7 @@ template struct arg_mapper { return map(named_arg.value); } - unformattable map(...) { return {}; } + auto map(...) -> unformattable { return {}; } }; // A type constant after applying arg_mapper. @@ -1327,6 +1354,33 @@ 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> { + using base = std::back_insert_iterator>; + + template + friend auto get_buffer(appender out) -> detail::buffer& { + return detail::get_container(out); + } + + public: + using std::back_insert_iterator>::back_insert_iterator; + appender(base it) : base(it) {} + using _Unchecked_type = appender; // Mark iterator as checked. + + auto operator++() -> appender& { + base::operator++(); + return *this; + } + + auto operator++(int) -> appender { + auto tmp = *this; + ++*this; + return tmp; + } +}; + // A formatting argument. It is a trivially copyable/constructible type to // allow storage in basic_memory_buffer. template class basic_format_arg { @@ -1335,8 +1389,8 @@ template class basic_format_arg { detail::type type_; template - friend FMT_CONSTEXPR basic_format_arg detail::make_arg( - const T& value); + friend FMT_CONSTEXPR auto detail::make_arg(const T& value) + -> basic_format_arg; template friend FMT_CONSTEXPR auto visit_format_arg(Visitor&& vis, @@ -1374,10 +1428,12 @@ template class basic_format_arg { return type_ != detail::type::none_type; } - detail::type type() const { return type_; } + auto type() const -> detail::type { return type_; } - bool is_integral() const { return detail::is_integral_type(type_); } - bool is_arithmetic() const { return detail::is_arithmetic_type(type_); } + auto is_integral() const -> bool { return detail::is_integral_type(type_); } + auto is_arithmetic() const -> bool { + return detail::is_arithmetic_type(type_); + } }; /** @@ -1388,9 +1444,8 @@ template class basic_format_arg { \endrst */ template -FMT_CONSTEXPR_DECL FMT_INLINE auto visit_format_arg( +FMT_CONSTEXPR FMT_INLINE auto visit_format_arg( Visitor&& vis, const basic_format_arg& arg) -> decltype(vis(0)) { - using char_type = typename Context::char_type; switch (arg.type_) { case detail::type::none_type: break; @@ -1402,16 +1457,10 @@ FMT_CONSTEXPR_DECL FMT_INLINE auto visit_format_arg( return vis(arg.value_.long_long_value); case detail::type::ulong_long_type: return vis(arg.value_.ulong_long_value); -#if FMT_USE_INT128 case detail::type::int128_type: - return vis(arg.value_.int128_value); + return vis(detail::convert_for_visit(arg.value_.int128_value)); case detail::type::uint128_type: - return vis(arg.value_.uint128_value); -#else - case detail::type::int128_type: - case detail::type::uint128_type: - break; -#endif + 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: @@ -1425,8 +1474,8 @@ FMT_CONSTEXPR_DECL FMT_INLINE auto visit_format_arg( case detail::type::cstring_type: return vis(arg.value_.string.data); case detail::type::string_type: - return vis(basic_string_view(arg.value_.string.data, - arg.value_.string.size)); + using sv = basic_string_view; + 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: @@ -1437,6 +1486,12 @@ FMT_CONSTEXPR_DECL FMT_INLINE auto visit_format_arg( FMT_BEGIN_DETAIL_NAMESPACE +template +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 struct void_t_impl { using type = void; }; @@ -1467,9 +1522,8 @@ struct is_contiguous_back_insert_iterator : std::false_type {}; template struct is_contiguous_back_insert_iterator> : is_contiguous {}; -template -struct is_contiguous_back_insert_iterator> - : std::true_type {}; +template <> +struct is_contiguous_back_insert_iterator : std::true_type {}; // A type-erased reference to an std::locale to avoid heavy include. class locale_ref { @@ -1482,19 +1536,21 @@ class locale_ref { explicit operator bool() const FMT_NOEXCEPT { return locale_ != nullptr; } - template Locale get() const; + template auto get() const -> Locale; }; -template constexpr unsigned long long encode_types() { return 0; } +template constexpr auto encode_types() -> unsigned long long { + return 0; +} template -constexpr unsigned long long encode_types() { +constexpr auto encode_types() -> unsigned long long { return static_cast(mapped_type_constant::value) | (encode_types() << packed_arg_bits); } template -FMT_CONSTEXPR basic_format_arg make_arg(const T& value) { +FMT_CONSTEXPR auto make_arg(const T& value) -> basic_format_arg { basic_format_arg arg; arg.type_ = mapped_type_constant::value; arg.value_ = arg_mapper().map(value); @@ -1506,21 +1562,20 @@ FMT_CONSTEXPR basic_format_arg make_arg(const T& value) { // another (not recommended). template -FMT_CONSTEXPR FMT_INLINE value make_arg(const T& val) { +FMT_CONSTEXPR FMT_INLINE auto make_arg(const T& val) -> value { const auto& arg = arg_mapper().map(val); static_assert( !std::is_same::value, "Cannot format an argument. To make type T formattable provide a " "formatter specialization: https://fmt.dev/latest/api.html#udt"); - return arg; + return {arg}; } template -inline basic_format_arg make_arg(const T& value) { +inline auto make_arg(const T& value) -> basic_format_arg { return make_arg(value); } - FMT_END_DETAIL_NAMESPACE // Formatting context. @@ -1552,32 +1607,35 @@ template class basic_format_context { detail::locale_ref loc = detail::locale_ref()) : out_(out), args_(ctx_args), loc_(loc) {} - constexpr format_arg arg(int id) const { return args_.get(id); } - FMT_CONSTEXPR format_arg arg(basic_string_view name) { + constexpr auto arg(int id) const -> format_arg { return args_.get(id); } + FMT_CONSTEXPR auto arg(basic_string_view name) -> format_arg { return args_.get(name); } - int arg_id(basic_string_view name) { return args_.get_id(name); } - const basic_format_args& args() const { return args_; } + FMT_CONSTEXPR auto arg_id(basic_string_view name) -> int { + return args_.get_id(name); + } + auto args() const -> const basic_format_args& { + return args_; + } - FMT_CONSTEXPR detail::error_handler error_handler() { return {}; } + 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 iterator out() { return out_; } + FMT_CONSTEXPR auto out() -> iterator { return out_; } // Advances the begin iterator to ``it``. void advance_to(iterator it) { if (!detail::is_back_insert_iterator()) out_ = it; } - FMT_CONSTEXPR detail::locale_ref locale() { return loc_; } + FMT_CONSTEXPR auto locale() -> detail::locale_ref { return loc_; } }; template using buffer_context = basic_format_context, Char>; using format_context = buffer_context; -using wformat_context = buffer_context; // Workaround an alias issue: https://stackoverflow.com/q/62767544/471164. #define FMT_BUFFER_CONTEXT(Char) \ @@ -1647,29 +1705,8 @@ class format_arg_store \endrst */ template -constexpr format_arg_store make_format_args( - const Args&... args) { - return {args...}; -} - -/** - \rst - Constructs a `~fmt::format_arg_store` object that contains references - to arguments and can be implicitly converted to `~fmt::format_args`. - If ``format_str`` is a compile-time string then `make_args_checked` checks - its validity at compile time. - \endrst - */ -template > -FMT_INLINE auto make_args_checked(const S& format_str, - const remove_reference_t&... args) - -> format_arg_store, remove_reference_t...> { - static_assert( - detail::count<( - std::is_base_of>::value && - std::is_reference::value)...>() == 0, - "passing views as lvalues is disallowed"); - detail::check_format_string(format_str); +constexpr auto make_format_args(const Args&... args) + -> format_arg_store { return {args...}; } @@ -1685,7 +1722,7 @@ FMT_INLINE auto make_args_checked(const S& format_str, \endrst */ template -inline detail::named_arg arg(const Char* name, const T& arg) { +inline auto arg(const Char* name, const T& arg) -> detail::named_arg { static_assert(!detail::is_named_arg(), "nested named arguments"); return {name, arg}; } @@ -1721,14 +1758,14 @@ template class basic_format_args { const format_arg* args_; }; - constexpr bool is_packed() const { + constexpr auto is_packed() const -> bool { return (desc_ & detail::is_unpacked_bit) == 0; } - bool has_named_args() const { + auto has_named_args() const -> bool { return (desc_ & detail::has_named_args_bit) != 0; } - FMT_CONSTEXPR detail::type type(int index) const { + 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((desc_ >> shift) & mask); @@ -1774,7 +1811,7 @@ template class basic_format_args { args) {} /** Returns the argument with the specified id. */ - FMT_CONSTEXPR format_arg get(int id) const { + FMT_CONSTEXPR auto get(int id) const -> format_arg { format_arg arg; if (!is_packed()) { if (id < max_size()) arg = args_[id]; @@ -1787,12 +1824,14 @@ template class basic_format_args { return arg; } - template format_arg get(basic_string_view name) const { + template + auto get(basic_string_view name) const -> format_arg { int id = get_id(name); return id >= 0 ? get(id) : format_arg(); } - template int get_id(basic_string_view name) const { + template + auto get_id(basic_string_view name) const -> int { if (!has_named_args()) return -1; const auto& named_args = (is_packed() ? values_[-1] : args_[-1].value_).named_args; @@ -1802,7 +1841,7 @@ template class basic_format_args { return -1; } - int max_size() const { + auto max_size() const -> int { unsigned long long max_packed = detail::max_packed_args; return static_cast(is_packed() ? max_packed : desc_ & ~detail::is_unpacked_bit); @@ -1810,10 +1849,9 @@ template class basic_format_args { }; /** An alias to ``basic_format_args``. */ -// Separate types would result in shorter symbols but break ABI compatibility +// 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; -using wformat_args = basic_format_args; // We cannot use enum classes as bit fields because of a gcc bug // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=61414. @@ -1845,15 +1883,14 @@ template struct fill_t { size_ = static_cast(size); } - constexpr size_t size() const { return size_; } - constexpr const Char* data() const { return data_; } + constexpr auto size() const -> size_t { return size_; } + constexpr auto data() const -> const Char* { return data_; } - FMT_CONSTEXPR Char& operator[](size_t index) { return data_[index]; } - FMT_CONSTEXPR const Char& operator[](size_t index) const { + 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 // Format specifiers for built-in and string types. @@ -1892,7 +1929,7 @@ template struct arg_ref { FMT_CONSTEXPR explicit arg_ref(basic_string_view name) : kind(arg_id_kind::name), val(name) {} - FMT_CONSTEXPR arg_ref& operator=(int idx) { + FMT_CONSTEXPR auto operator=(int idx) -> arg_ref& { kind = arg_id_kind::index; val.index = idx; return *this; @@ -1921,6 +1958,9 @@ struct auto_id {}; // A format specifier handler that sets fields in basic_format_specs. template class specs_setter { + protected: + basic_format_specs& specs_; + public: explicit FMT_CONSTEXPR specs_setter(basic_format_specs& specs) : specs_(specs) {} @@ -1932,14 +1972,12 @@ template class specs_setter { FMT_CONSTEXPR void on_fill(basic_string_view fill) { specs_.fill = fill; } - FMT_CONSTEXPR void on_plus() { specs_.sign = sign::plus; } - FMT_CONSTEXPR void on_minus() { specs_.sign = sign::minus; } - FMT_CONSTEXPR void on_space() { specs_.sign = sign::space; } + 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() { - specs_.align = align::numeric; + if (specs_.align == align::none) specs_.align = align::numeric; specs_.fill[0] = Char('0'); } @@ -1952,9 +1990,6 @@ template class specs_setter { FMT_CONSTEXPR void on_type(Char type) { specs_.type = static_cast(type); } - - protected: - basic_format_specs& specs_; }; // Format spec handler that saves references to arguments representing dynamic @@ -1987,26 +2022,27 @@ class dynamic_specs_handler } private: + dynamic_format_specs& specs_; + ParseContext& context_; + using arg_ref_type = arg_ref; - FMT_CONSTEXPR arg_ref_type make_arg_ref(int arg_id) { + 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 arg_ref_type make_arg_ref(auto_id) { + FMT_CONSTEXPR auto make_arg_ref(auto_id) -> arg_ref_type { return arg_ref_type(context_.next_arg_id()); } - FMT_CONSTEXPR arg_ref_type make_arg_ref(basic_string_view arg_id) { + FMT_CONSTEXPR auto make_arg_ref(basic_string_view arg_id) + -> arg_ref_type { context_.check_arg_id(arg_id); basic_string_view format_str( context_.begin(), to_unsigned(context_.end() - context_.begin())); return arg_ref_type(arg_id); } - - dynamic_format_specs& specs_; - ParseContext& context_; }; template constexpr bool is_ascii_letter(Char c) { @@ -2015,16 +2051,17 @@ template constexpr bool is_ascii_letter(Char c) { // Converts a character to ASCII. Returns a number > 127 on conversion failure. template ::value)> -constexpr Char to_ascii(Char value) { +constexpr auto to_ascii(Char value) -> Char { return value; } template ::value)> -constexpr typename std::underlying_type::type to_ascii(Char value) { +constexpr auto to_ascii(Char value) -> + typename std::underlying_type::type { return value; } template -FMT_CONSTEXPR int code_point_length(const Char* begin) { +FMT_CONSTEXPR auto code_point_length(const Char* begin) -> int { if (const_check(sizeof(Char) != 1)) return 1; constexpr char 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, 0}; @@ -2036,33 +2073,52 @@ FMT_CONSTEXPR int code_point_length(const Char* begin) { return len + !len; } +// Return the result via the out param to workaround gcc bug 77539. +template +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(const char* first, const char* last, char value, + const char*& out) -> bool { + out = static_cast( + 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 -FMT_CONSTEXPR int parse_nonnegative_int(const Char*& begin, const Char* end, - ErrorHandler&& eh) { +template +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; - // Convert to unsigned to prevent a warning. - const unsigned max_int = to_unsigned(INT_MAX); - unsigned big = max_int / 10; + unsigned value = 0, prev = 0; + auto p = begin; do { - // Check for overflow. - if (value > big) { - value = max_int + 1; - break; - } - value = value * 10 + unsigned(*begin - '0'); - ++begin; - } while (begin != end && '0' <= *begin && *begin <= '9'); - if (value > max_int) eh.on_error("number is too big"); - return static_cast(value); + 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::digits10) + return static_cast(value); + // Check for overflow. + const unsigned max = to_unsigned((std::numeric_limits::max)()); + return num_digits == std::numeric_limits::digits10 + 1 && + prev * 10ull + unsigned(p[-1] - '0') <= max + ? static_cast(value) + : error_value; } // Parses fill and alignment. template -FMT_CONSTEXPR const Char* parse_align(const Char* begin, const Char* end, - Handler&& 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); @@ -2105,14 +2161,15 @@ template FMT_CONSTEXPR bool is_name_start(Char c) { } template -FMT_CONSTEXPR const Char* do_parse_arg_id(const Char* begin, const Char* end, - IDHandler&& handler) { +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, handler); + index = + parse_nonnegative_int(begin, end, (std::numeric_limits::max)()); else ++begin; if (begin == end || (*begin != '}' && *begin != ':')) @@ -2134,42 +2191,41 @@ FMT_CONSTEXPR const Char* do_parse_arg_id(const Char* begin, const Char* end, } template -FMT_CONSTEXPR_DECL FMT_INLINE const Char* parse_arg_id(const Char* begin, - const Char* end, - IDHandler&& handler) { +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; } -// Adapts SpecHandler to IDHandler API for dynamic width. -template struct width_adapter { - explicit FMT_CONSTEXPR width_adapter(SpecHandler& h) : handler(h) {} - - 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 id) { - handler.on_dynamic_width(id); - } - - FMT_CONSTEXPR void on_error(const char* message) { - handler.on_error(message); - } - - SpecHandler& handler; -}; - template -FMT_CONSTEXPR const Char* parse_width(const Char* begin, const Char* end, - Handler&& 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 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') { - handler.on_width(parse_nonnegative_int(begin, end, handler)); + 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 = parse_arg_id(begin, end, width_adapter{handler}); if (begin == end || *begin != '}') return handler.on_error("invalid format string"), begin; ++begin; @@ -2177,36 +2233,35 @@ FMT_CONSTEXPR const Char* parse_width(const Char* begin, const Char* end, return begin; } -// Adapts SpecHandler to IDHandler API for dynamic precision. -template struct precision_adapter { - explicit FMT_CONSTEXPR precision_adapter(SpecHandler& h) : handler(h) {} - - 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 id) { - handler.on_dynamic_precision(id); - } - - FMT_CONSTEXPR void on_error(const char* message) { - handler.on_error(message); - } - - SpecHandler& handler; -}; - template -FMT_CONSTEXPR const Char* parse_precision(const Char* begin, const Char* end, - Handler&& 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 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') { - handler.on_precision(parse_nonnegative_int(begin, end, handler)); + 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 = parse_arg_id(begin, end, precision_adapter{handler}); if (begin == end || *begin++ != '}') return handler.on_error("invalid format string"), begin; } else { @@ -2219,8 +2274,10 @@ FMT_CONSTEXPR const Char* parse_precision(const Char* begin, const Char* end, // Parses standard format specifiers and sends notifications about parsed // components to handler. template -FMT_CONSTEXPR_DECL FMT_INLINE const Char* parse_format_specs( - const Char* begin, const Char* end, SpecHandler&& handler) { +FMT_CONSTEXPR FMT_INLINE auto parse_format_specs(const Char* begin, + const Char* end, + SpecHandler&& handler) + -> const Char* { if (begin + 1 < end && begin[1] == '}' && is_ascii_letter(*begin) && *begin != 'L') { handler.on_type(*begin++); @@ -2235,15 +2292,15 @@ FMT_CONSTEXPR_DECL FMT_INLINE const Char* parse_format_specs( // Parse sign. switch (to_ascii(*begin)) { case '+': - handler.on_plus(); + handler.on_sign(sign::plus); ++begin; break; case '-': - handler.on_minus(); + handler.on_sign(sign::minus); ++begin; break; case ' ': - handler.on_space(); + handler.on_sign(sign::space); ++begin; break; default: @@ -2281,41 +2338,23 @@ FMT_CONSTEXPR_DECL FMT_INLINE const Char* parse_format_specs( return begin; } -// Return the result via the out param to workaround gcc bug 77539. -template -FMT_CONSTEXPR bool find(Ptr first, Ptr last, T value, Ptr& out) { - for (out = first; out != last; ++out) { - if (*out == value) return true; - } - return false; -} - -template <> -inline bool find(const char* first, const char* last, char value, - const char*& out) { - out = static_cast( - std::memchr(first, value, to_unsigned(last - first))); - return out != nullptr; -} - -template 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 id) { - arg_id = handler.on_arg_id(id); - } - FMT_CONSTEXPR void on_error(const char* message) { - handler.on_error(message); - } -}; - template -FMT_CONSTEXPR const Char* parse_replacement_field(const Char* begin, - const Char* end, - Handler&& 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 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 == '}') { @@ -2323,7 +2362,7 @@ FMT_CONSTEXPR const Char* parse_replacement_field(const Char* begin, } else if (*begin == '{') { handler.on_text(begin, begin + 1); } else { - auto adapter = id_adapter{handler, 0}; + auto adapter = id_adapter{handler, 0}; begin = parse_arg_id(begin, end, adapter); Char c = begin != end ? *begin : Char(); if (c == '}') { @@ -2340,7 +2379,7 @@ FMT_CONSTEXPR const Char* parse_replacement_field(const Char* begin, } template -FMT_CONSTEXPR_DECL FMT_INLINE void parse_format_string( +FMT_CONSTEXPR FMT_INLINE void parse_format_string( basic_string_view format_str, Handler&& handler) { // this is most likely a name-lookup defect in msvc's modules implementation using detail::find; @@ -2370,7 +2409,7 @@ FMT_CONSTEXPR_DECL FMT_INLINE void parse_format_string( if (pbegin == pend) return; for (;;) { const Char* p = nullptr; - if (!find(pbegin, pend, '}', p)) + if (!find(pbegin, pend, Char('}'), p)) return handler_.on_text(pbegin, pend); ++p; if (p == pend || *p != '}') @@ -2385,7 +2424,7 @@ FMT_CONSTEXPR_DECL FMT_INLINE void parse_format_string( // 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(begin + 1, end, '{', p)) + if (*begin != '{' && !find(begin + 1, end, Char('{'), p)) return write(begin, end); write(begin, p); begin = parse_replacement_field(p, end, handler); @@ -2393,8 +2432,8 @@ FMT_CONSTEXPR_DECL FMT_INLINE void parse_format_string( } template -FMT_CONSTEXPR const typename ParseContext::char_type* parse_format_specs( - ParseContext& ctx) { +FMT_CONSTEXPR auto parse_format_specs(ParseContext& ctx) + -> decltype(ctx.begin()) { using char_type = typename ParseContext::char_type; using context = buffer_context; using mapped_type = conditional_t< @@ -2419,11 +2458,11 @@ class compile_parse_context public: explicit FMT_CONSTEXPR compile_parse_context( - basic_string_view format_str, int num_args = INT_MAX, - ErrorHandler eh = {}) + basic_string_view format_str, + int num_args = (std::numeric_limits::max)(), ErrorHandler eh = {}) : base(format_str, eh), num_args_(num_args) {} - FMT_CONSTEXPR int next_arg_id() { + 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; @@ -2436,32 +2475,212 @@ class compile_parse_context using base::check_arg_id; }; +template +FMT_CONSTEXPR void check_int_type_spec(char spec, ErrorHandler&& eh) { + switch (spec) { + case 0: + case 'd': + case 'x': + case 'X': + case 'b': + case 'B': + case 'o': + case 'c': + break; + default: + eh.on_error("invalid type specifier"); + break; + } +} + +// Checks char specs and returns true if the type spec is char (and not int). +template +FMT_CONSTEXPR auto check_char_specs(const basic_format_specs& specs, + ErrorHandler&& eh = {}) -> bool { + if (specs.type && specs.type != 'c') { + 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 use_grisu : 1; + bool showpoint : 1; +}; + +template +FMT_CONSTEXPR auto parse_float_type_spec(const basic_format_specs& specs, + ErrorHandler&& eh = {}) + -> float_specs { + auto result = float_specs(); + result.showpoint = specs.alt; + result.locale = specs.localized; + switch (specs.type) { + case 0: + result.format = float_format::general; + break; + case 'G': + result.upper = true; + FMT_FALLTHROUGH; + case 'g': + result.format = float_format::general; + break; + case 'E': + result.upper = true; + FMT_FALLTHROUGH; + case 'e': + result.format = float_format::exp; + result.showpoint |= specs.precision != 0; + break; + case 'F': + result.upper = true; + FMT_FALLTHROUGH; + case 'f': + result.format = float_format::fixed; + result.showpoint |= specs.precision != 0; + break; + case 'A': + result.upper = true; + FMT_FALLTHROUGH; + case 'a': + result.format = float_format::hex; + break; + default: + eh.on_error("invalid type specifier"); + break; + } + return result; +} + +template +FMT_CONSTEXPR auto check_cstring_type_spec(Char spec, ErrorHandler&& eh = {}) + -> bool { + if (spec == 0 || spec == 's') return true; + if (spec != 'p') eh.on_error("invalid type specifier"); + return false; +} + +template +FMT_CONSTEXPR void check_string_type_spec(Char spec, ErrorHandler&& eh = {}) { + if (spec != 0 && spec != 's') eh.on_error("invalid type specifier"); +} + +template +FMT_CONSTEXPR void check_pointer_type_spec(Char spec, ErrorHandler&& eh) { + if (spec != 0 && spec != 'p') eh.on_error("invalid type specifier"); +} + +// A parse_format_specs handler that checks if specifiers are consistent with +// the argument type. +template 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 -constexpr int get_arg_index_by_name(basic_string_view name) { +constexpr auto get_arg_index_by_name(basic_string_view name) -> int { if constexpr (detail::is_statically_named_arg()) { if (name == T::name) return N; } - if constexpr (sizeof...(Args) > 0) + if constexpr (sizeof...(Args) > 0) { return get_arg_index_by_name(name); - return invalid_arg_index; + } else { + (void)name; // Workaround an MSVC bug about "unused" parameter. + return invalid_arg_index; + } } #endif template -FMT_CONSTEXPR int get_arg_index_by_name(basic_string_view name) { +FMT_CONSTEXPR auto get_arg_index_by_name(basic_string_view name) -> int { #if FMT_USE_NONTYPE_TEMPLATE_PARAMETERS - if constexpr (sizeof...(Args) > 0) + if constexpr (sizeof...(Args) > 0) { return get_arg_index_by_name<0, Args...>(name); -#endif + } else { + (void)name; + return invalid_arg_index; + } +#else (void)name; return invalid_arg_index; +#endif } template class format_string_checker { + private: + using parse_context_type = compile_parse_context; + 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 format_str, ErrorHandler eh) @@ -2470,9 +2689,11 @@ class format_string_checker { FMT_CONSTEXPR void on_text(const Char*, const Char*) {} - FMT_CONSTEXPR int on_arg_id() { return context_.next_arg_id(); } - FMT_CONSTEXPR int on_arg_id(int id) { return context_.check_arg_id(id), id; } - FMT_CONSTEXPR int on_arg_id(basic_string_view id) { + 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 id) -> int { #if FMT_USE_NONTYPE_TEMPLATE_PARAMETERS auto index = get_arg_index_by_name(id); if (index == invalid_arg_index) on_error("named argument is not found"); @@ -2486,9 +2707,9 @@ class format_string_checker { FMT_CONSTEXPR void on_replacement_field(int, const Char*) {} - FMT_CONSTEXPR const Char* on_format_specs(int id, const Char* begin, - const Char*) { - advance_to(context_, begin); + 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; } @@ -2496,127 +2717,202 @@ class format_string_checker { FMT_CONSTEXPR void on_error(const char* message) { context_.on_error(message); } - - private: - using parse_context_type = compile_parse_context; - 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]; }; template ::value), int>> void check_format_string(S format_str) { - FMT_CONSTEXPR_DECL auto s = to_string_view(format_str); + FMT_CONSTEXPR auto s = to_string_view(format_str); using checker = format_string_checker...>; - FMT_CONSTEXPR_DECL bool invalid_format = + FMT_CONSTEXPR bool invalid_format = (parse_format_string(s, checker(s, {})), true); - (void)invalid_format; + ignore_unused(invalid_format); } -// Converts string literals to basic_string_view. -template -FMT_CONSTEXPR basic_string_view compile_string_to_view( - const Char (&s)[N]) { - // Remove trailing null character if needed. Won't be present if this is used - // with raw character array (i.e. not defined as a string). - return {s, - N - ((std::char_traits::to_int_type(s[N - 1]) == 0) ? 1 : 0)}; -} - -// Converts string_view to basic_string_view. -template -FMT_CONSTEXPR basic_string_view compile_string_to_view( - const std_string_view& s) { - return {s.data(), s.size()}; -} - -#define FMT_STRING_IMPL(s, base) \ - [] { \ - /* Use the hidden visibility as a workaround for a GCC bug (#1973). */ \ - /* Use a macro-like name to avoid shadowing warnings. */ \ - struct FMT_GCC_VISIBILITY_HIDDEN FMT_COMPILE_STRING : base { \ - using char_type = fmt::remove_cvref_t; \ - FMT_MAYBE_UNUSED FMT_CONSTEXPR \ - operator fmt::basic_string_view() const { \ - return fmt::detail::compile_string_to_view(s); \ - } \ - }; \ - return FMT_COMPILE_STRING(); \ - }() - -/** - \rst - Constructs a compile-time format string from a string literal *s*. - - **Example**:: - - // A compile-time error because 'd' is an invalid specifier for strings. - std::string s = fmt::format(FMT_STRING("{:d}"), "foo"); - \endrst - */ -#define FMT_STRING(s) FMT_STRING_IMPL(s, fmt::compile_string) - -template ::value)> -std::basic_string vformat( - basic_string_view format_str, - basic_format_args>> args); - -FMT_API std::string vformat(string_view format_str, format_args args); - template void vformat_to( - buffer& buf, basic_string_view format_str, + buffer& buf, basic_string_view fmt, basic_format_args)> args, - detail::locale_ref loc = {}); - -template ::value)> -inline void vprint_mojibake(std::FILE*, basic_string_view, const 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 +struct formatter::value != + detail::type::custom_type>> { + private: + detail::dynamic_format_specs specs_; + + public: + // Parses format specifiers stopping either at the end of the range or at the + // terminating '}'. + template + 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; + auto type = detail::type_constant::value; + auto checker = + detail::specs_checker(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 || specs_.type == 's') 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 + FMT_CONSTEXPR auto format(const T& val, FormatContext& ctx) const + -> decltype(ctx.out()); +}; + +template struct basic_runtime { basic_string_view str; }; + +template class basic_format_string { + private: + basic_string_view str_; + + public: + template >::value)> + FMT_CONSTEVAL basic_format_string(const S& s) : str_(s) { + static_assert( + detail::count< + (std::is_base_of>::value && + std::is_reference::value)...>() == 0, + "passing views as lvalues is disallowed"); +#ifdef FMT_HAS_CONSTEVAL + if constexpr (detail::count_named_args() == 0) { + using checker = detail::format_string_checker...>; + detail::parse_format_string(str_, checker(s, {})); + } +#else + detail::check_format_string(s); +#endif + } + basic_format_string(basic_runtime r) : str_(r.str) {} + + FMT_INLINE operator basic_string_view() const { return str_; } +}; + +#if FMT_GCC_VERSION && FMT_GCC_VERSION < 409 +// Workaround broken conversion on older gcc. +template using format_string = string_view; +template auto runtime(const S& s) -> basic_string_view> { + return s; +} +#else +template +using format_string = basic_format_string...>; +// Creates a runtime format string. +template auto runtime(const S& s) -> basic_runtime> { + 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 + std::string message = fmt::format("The answer is {}", 42); + \endrst +*/ +template +FMT_INLINE auto format(format_string fmt, T&&... args) -> std::string { + return vformat(fmt, fmt::make_format_args(args...)); +} + /** Formats a string and writes the output to ``out``. */ -// GCC 8 and earlier cannot handle std::back_insert_iterator with -// vformat_to(...) overload, so SFINAE on iterator type instead. -template , - bool enable = detail::is_output_iterator::value> -auto vformat_to(OutputIt out, const S& format_str, - basic_format_args>> args) - -> typename std::enable_if::type { - decltype(detail::get_buffer(out)) buf(detail::get_buffer_init(out)); - detail::vformat_to(buf, to_string_view(format_str), args); +template ::value)> +auto vformat_to(OutputIt out, string_view fmt, format_args args) -> OutputIt { + using detail::get_buffer; + auto&& buf = get_buffer(out); + detail::vformat_to(buf, string_view(fmt), args, {}); return detail::get_iterator(buf); } /** \rst - Formats arguments, writes the result to the output iterator ``out`` and returns - the iterator past the end of the output range. + 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. **Example**:: - std::vector out; + auto out = std::vector(); fmt::format_to(std::back_inserter(out), "{}", 42); \endrst */ -// We cannot use FMT_ENABLE_IF because of a bug in gcc 8.3. -template >::value> -inline auto format_to(OutputIt out, const S& format_str, Args&&... args) -> - typename std::enable_if::type { - const auto& vargs = fmt::make_args_checked(format_str, args...); - return vformat_to(out, to_string_view(format_str), vargs); +template ::value)> +FMT_INLINE auto format_to(OutputIt out, format_string fmt, T&&... args) + -> OutputIt { + return vformat_to(out, fmt, fmt::make_format_args(args...)); } template struct format_to_n_result { @@ -2626,112 +2922,74 @@ template struct format_to_n_result { size_t size; }; -template ::value)> -inline format_to_n_result vformat_to_n( - OutputIt out, size_t n, basic_string_view format_str, - basic_format_args>> args) { - detail::iterator_buffer buf(out, - n); - detail::vformat_to(buf, format_str, args); +template ::value)> +auto vformat_to_n(OutputIt out, size_t n, string_view fmt, format_args args) + -> format_to_n_result { + using buffer = + detail::iterator_buffer; + auto buf = buffer(out, n); + detail::vformat_to(buf, fmt, args, {}); return {buf.out(), buf.count()}; } /** - \rst - Formats arguments, writes up to ``n`` characters of the result to the output - iterator ``out`` and returns the total output size and the iterator past the - end of the output range. - \endrst + \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. + \endrst */ -template >::value> -inline auto format_to_n(OutputIt out, size_t n, const S& format_str, - const Args&... args) -> - typename std::enable_if>::type { - const auto& vargs = fmt::make_args_checked(format_str, args...); - return vformat_to_n(out, n, to_string_view(format_str), vargs); +template ::value)> +FMT_INLINE auto format_to_n(OutputIt out, size_t n, format_string fmt, + const T&... args) -> format_to_n_result { + return vformat_to_n(out, n, fmt, fmt::make_format_args(args...)); } -/** - Returns the number of characters in the output of - ``format(format_str, args...)``. - */ -template > -inline size_t formatted_size(const S& format_str, Args&&... args) { - const auto& vargs = fmt::make_args_checked(format_str, args...); - detail::counting_buffer<> buf; - detail::vformat_to(buf, to_string_view(format_str), vargs); +/** Returns the number of chars in the output of ``format(fmt, args...)``. */ +template +FMT_INLINE auto formatted_size(format_string 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(); } -template > -FMT_INLINE std::basic_string vformat( - const S& format_str, - basic_format_args>> args) { - return detail::vformat(to_string_view(format_str), args); -} +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 arguments and returns the result as a string. - - **Example**:: - - #include - std::string message = fmt::format("The answer is {}", 42); - \endrst -*/ -// Pass char_t as a default template parameter instead of using -// std::basic_string> to reduce the symbol size. -template , - FMT_ENABLE_IF(!FMT_COMPILE_TIME_CHECKS || - !std::is_same::value)> -FMT_INLINE std::basic_string format(const S& format_str, Args&&... args) { - const auto& vargs = fmt::make_args_checked(format_str, args...); - return detail::vformat(to_string_view(format_str), vargs); -} - -FMT_API void vprint(string_view, format_args); -FMT_API void vprint(std::FILE*, string_view, format_args); - -/** - \rst - Formats ``args`` according to specifications in ``format_str`` and writes the - output to the file ``f``. Strings are assumed to be Unicode-encoded unless the - ``FMT_UNICODE`` macro is set to 0. - - **Example**:: - - fmt::print(stderr, "Don't {}!", "panic"); - \endrst - */ -template > -inline void print(std::FILE* f, const S& format_str, Args&&... args) { - const auto& vargs = fmt::make_args_checked(format_str, args...); - return detail::is_unicode() - ? vprint(f, to_string_view(format_str), vargs) - : detail::vprint_mojibake(f, to_string_view(format_str), vargs); -} - -/** - \rst - Formats ``args`` according to specifications in ``format_str`` and writes - the output to ``stdout``. Strings are assumed to be Unicode-encoded unless - the ``FMT_UNICODE`` macro is set to 0. + 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 > -inline void print(const S& format_str, Args&&... args) { - const auto& vargs = fmt::make_args_checked(format_str, args...); - return detail::is_unicode() - ? vprint(to_string_view(format_str), vargs) - : detail::vprint_mojibake(stdout, to_string_view(format_str), - vargs); +template +FMT_INLINE void print(format_string 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 +FMT_INLINE void print(std::FILE* f, format_string 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 diff --git a/deps/fmt/include/fmt/format-inl.h b/deps/fmt/include/fmt/format-inl.h index 02a4e7c3a..94a36d1bc 100644 --- a/deps/fmt/include/fmt/format-inl.h +++ b/deps/fmt/include/fmt/format-inl.h @@ -10,6 +10,7 @@ #include #include +#include // errno #include #include #include @@ -102,23 +103,21 @@ template Locale locale_ref::get() const { return locale_ ? *static_cast(locale_) : std::locale(); } -template FMT_FUNC std::string grouping_impl(locale_ref loc) { - return std::use_facet>(loc.get()).grouping(); -} -template FMT_FUNC Char thousands_sep_impl(locale_ref loc) { - return std::use_facet>(loc.get()) - .thousands_sep(); +template +FMT_FUNC auto thousands_sep_impl(locale_ref loc) -> thousands_sep_result { + auto& facet = std::use_facet>(loc.get()); + auto grouping = facet.grouping(); + auto thousands_sep = grouping.empty() ? Char() : facet.thousands_sep(); + return {std::move(grouping), thousands_sep}; } template FMT_FUNC Char decimal_point_impl(locale_ref loc) { return std::use_facet>(loc.get()) .decimal_point(); } #else -template FMT_FUNC std::string grouping_impl(locale_ref) { - return "\03"; -} -template FMT_FUNC Char thousands_sep_impl(locale_ref) { - return FMT_STATIC_THOUSANDS_SEPARATOR; +template +FMT_FUNC auto thousands_sep_impl(locale_ref) -> thousands_sep_result { + return {"\03", FMT_STATIC_THOUSANDS_SEPARATOR}; } template FMT_FUNC Char decimal_point_impl(locale_ref) { return '.'; @@ -147,8 +146,7 @@ template <> FMT_FUNC int count_digits<4>(detail::fallback_uintptr n) { } #if __cplusplus < 201703L -template -constexpr const typename basic_data::digit_pair basic_data::digits[]; +template constexpr const char basic_data::digits[][2]; template constexpr const char basic_data::hex_digits[]; template constexpr const char basic_data::signs[]; template constexpr const unsigned basic_data::prefixes[]; @@ -565,7 +563,6 @@ class bigint { (*this)[bigit_index] = static_cast(sum); sum >>= bits::value; } - --num_result_bigits; remove_leading_zeros(); exp_ *= 2; } @@ -640,8 +637,8 @@ inline uint64_t power_of_10_64(int exp) { // error: the size of the region (lower, upper) outside of which numbers // definitely do not round to value (Delta in Grisu3). template -FMT_ALWAYS_INLINE digits::result grisu_gen_digits(fp value, uint64_t error, - int& exp, Handler& handler) { +FMT_INLINE digits::result grisu_gen_digits(fp value, uint64_t error, int& exp, + Handler& handler) { const fp one(1ULL << -value.e, value.e); // The integral part of scaled value (p1 in Grisu) = value / one. It cannot be // zero because it contains a product of two 64-bit numbers with MSB set (due @@ -1919,7 +1916,7 @@ bool is_center_integer(typename float_info::carrier_uint two_f, int exponent, } // Remove trailing zeros from n and return the number of zeros removed (float) -FMT_ALWAYS_INLINE int remove_trailing_zeros(uint32_t& n) FMT_NOEXCEPT { +FMT_INLINE int remove_trailing_zeros(uint32_t& n) FMT_NOEXCEPT { #ifdef FMT_BUILTIN_CTZ int t = FMT_BUILTIN_CTZ(n); #else @@ -1947,7 +1944,7 @@ FMT_ALWAYS_INLINE int remove_trailing_zeros(uint32_t& n) FMT_NOEXCEPT { } // Removes trailing zeros and returns the number of zeros removed (double) -FMT_ALWAYS_INLINE int remove_trailing_zeros(uint64_t& n) FMT_NOEXCEPT { +FMT_INLINE int remove_trailing_zeros(uint64_t& n) FMT_NOEXCEPT { #ifdef FMT_BUILTIN_CTZLL int t = FMT_BUILTIN_CTZLL(n); #else @@ -2033,8 +2030,7 @@ FMT_ALWAYS_INLINE int remove_trailing_zeros(uint64_t& n) FMT_NOEXCEPT { // The main algorithm for shorter interval case template -FMT_ALWAYS_INLINE decimal_fp shorter_interval_case(int exponent) - FMT_NOEXCEPT { +FMT_INLINE decimal_fp shorter_interval_case(int exponent) FMT_NOEXCEPT { decimal_fp ret_value; // Compute k and beta const int minus_k = floor_log10_pow2_minus_log10_4_over_3(exponent); @@ -2500,19 +2496,6 @@ int snprintf_float(T value, int precision, float_specs specs, return exp - fraction_size; } } - -struct stringifier { - template FMT_INLINE std::string operator()(T value) const { - return to_string(value); - } - std::string operator()(basic_format_arg::handle h) const { - memory_buffer buf; - format_parse_context parse_ctx({}); - format_context format_ctx(buffer_appender(buf), {}, {}); - h.format(parse_ctx, format_ctx); - return to_string(buf); - } -}; } // namespace detail template <> struct formatter { @@ -2575,14 +2558,11 @@ FMT_FUNC void report_system_error(int error_code, report_error(format_system_error, error_code, message); } -FMT_FUNC std::string detail::vformat(string_view format_str, format_args args) { - if (format_str.size() == 2 && equal2(format_str.data(), "{}")) { - auto arg = args.get(0); - if (!arg) error_handler().on_error("argument not found"); - return visit_format_arg(stringifier(), arg); - } - memory_buffer buffer; - detail::vformat_to(buffer, format_str, args); +FMT_FUNC std::string vformat(string_view fmt, format_args args) { + // Don't optimize the "{}" case to keep the binary size small and because it + // can be better optimized in fmt::format anyway. + auto buffer = memory_buffer(); + detail::vformat_to(buffer, fmt, args); return to_string(buffer); } @@ -2594,13 +2574,12 @@ extern "C" __declspec(dllimport) int __stdcall WriteConsoleW( // } // namespace detail #endif -FMT_FUNC void vprint(std::FILE* f, string_view format_str, format_args args) { - memory_buffer buffer; - detail::vformat_to(buffer, format_str, args); +namespace detail { +FMT_FUNC void print(std::FILE* f, string_view text) { #ifdef _WIN32 auto fd = _fileno(f); if (_isatty(fd)) { - detail::utf8_to_utf16 u16(string_view(buffer.data(), buffer.size())); + detail::utf8_to_utf16 u16(string_view(text.data(), text.size())); auto written = detail::dword(); if (detail::WriteConsoleW(reinterpret_cast(_get_osfhandle(fd)), u16.c_str(), static_cast(u16.size()), @@ -2611,7 +2590,14 @@ FMT_FUNC void vprint(std::FILE* f, string_view format_str, format_args args) { // redirected to NUL. } #endif - detail::fwrite_fully(buffer.data(), 1, buffer.size(), f); + detail::fwrite_fully(text.data(), 1, text.size(), f); +} +} // namespace detail + +FMT_FUNC void vprint(std::FILE* f, string_view format_str, format_args args) { + memory_buffer buffer; + detail::vformat_to(buffer, format_str, args); + detail::print(f, {buffer.data(), buffer.size()}); } #ifdef _WIN32 diff --git a/deps/fmt/include/fmt/format.h b/deps/fmt/include/fmt/format.h index a6dbd819f..5398a23a8 100644 --- a/deps/fmt/include/fmt/format.h +++ b/deps/fmt/include/fmt/format.h @@ -33,10 +33,8 @@ #ifndef FMT_FORMAT_H_ #define FMT_FORMAT_H_ -#include // errno -#include // std::signbit -#include -#include +#include // std::signbit +#include // uint32_t #include // std::numeric_limits #include // std::uninitialized_copy #include // std::runtime_error @@ -77,24 +75,6 @@ # define FMT_MSC_DEFAULT #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) || \ - (defined(_MSVC_LANG) && _MSVC_LANG >= 201703L) -# define FMT_FALLTHROUGH [[fallthrough]] -#else -# define FMT_FALLTHROUGH -#endif - #ifndef FMT_THROW # if FMT_EXCEPTIONS # if FMT_MSC_VER || FMT_NVCC @@ -128,6 +108,27 @@ FMT_END_NAMESPACE # define FMT_CATCH(x) if (false) #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 + +// Workaround broken [[deprecated]] in the Intel, PGI and NVCC compilers. +#if FMT_ICC_VERSION || defined(__PGI) || FMT_NVCC +# define FMT_DEPRECATED_ALIAS +#else +# define FMT_DEPRECATED_ALIAS FMT_DEPRECATED +#endif + #ifndef FMT_USE_USER_DEFINED_LITERALS // EDG based compilers (Intel, NVIDIA, Elbrus, etc), GCC and MSVC support UDLs. # if (FMT_HAS_FEATURE(cxx_user_literals) || FMT_GCC_VERSION >= 407 || \ @@ -139,18 +140,6 @@ FMT_END_NAMESPACE # 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 - // Defining FMT_REDUCE_INT_INSTANTIATIONS to 1, will reduce the number of // integer formatter template instantiations to just one by only using the // largest integer type. This results in a reduction in binary size but will @@ -195,7 +184,7 @@ namespace detail { # endif # endif -inline int clz(uint32_t x) { +inline auto clz(uint32_t x) -> int { unsigned long r = 0; _BitScanReverse(&r, x); FMT_ASSERT(x != 0, ""); @@ -207,7 +196,7 @@ inline int clz(uint32_t x) { } # define FMT_BUILTIN_CLZ(n) detail::clz(n) -inline int clzll(uint64_t x) { +inline auto clzll(uint64_t x) -> int { unsigned long r = 0; # ifdef _WIN64 _BitScanReverse64(&r, x); @@ -223,7 +212,7 @@ inline int clzll(uint64_t x) { } # define FMT_BUILTIN_CLZLL(n) detail::clzll(n) -inline int ctz(uint32_t x) { +inline auto ctz(uint32_t x) -> int { unsigned long r = 0; _BitScanForward(&r, x); FMT_ASSERT(x != 0, ""); @@ -232,7 +221,7 @@ inline int ctz(uint32_t x) { } # define FMT_BUILTIN_CTZ(n) detail::ctz(n) -inline int ctzll(uint64_t x) { +inline auto ctzll(uint64_t x) -> int { unsigned long r = 0; FMT_ASSERT(x != 0, ""); FMT_MSC_WARNING(suppress : 6102) // Suppress a bogus static analysis warning. @@ -269,14 +258,14 @@ namespace detail { // undefined behavior (e.g. due to type aliasing). // Example: uint64_t d = bit_cast(2.718); template -inline Dest bit_cast(const Source& source) { +inline auto bit_cast(const Source& source) -> Dest { static_assert(sizeof(Dest) == sizeof(Source), "size mismatch"); Dest dest; std::memcpy(&dest, &source, sizeof(dest)); return dest; } -inline bool is_big_endian() { +inline auto is_big_endian() -> bool { const auto u = 1u; struct bytes { char data[sizeof(u)]; @@ -299,26 +288,28 @@ struct fallback_uintptr { }; #ifdef UINTPTR_MAX using uintptr_t = ::uintptr_t; -inline uintptr_t to_uintptr(const void* p) { return bit_cast(p); } +inline auto to_uintptr(const void* p) -> uintptr_t { + return bit_cast(p); +} #else using uintptr_t = fallback_uintptr; -inline fallback_uintptr to_uintptr(const void* p) { +inline auto to_uintptr(const void* p) -> fallback_uintptr { return fallback_uintptr(p); } #endif // Returns the largest possible value for type T. Same as // std::numeric_limits::max() but shorter and not affected by the max macro. -template constexpr T max_value() { +template constexpr auto max_value() -> T { return (std::numeric_limits::max)(); } -template constexpr int num_bits() { +template constexpr auto num_bits() -> int { return std::numeric_limits::digits; } // std::numeric_limits::digits may return 0 for 128-bit ints. -template <> constexpr int num_bits() { return 128; } -template <> constexpr int num_bits() { return 128; } -template <> constexpr int num_bits() { +template <> constexpr auto num_bits() -> int { return 128; } +template <> constexpr auto num_bits() -> int { return 128; } +template <> constexpr auto num_bits() -> int { return static_cast(sizeof(void*) * std::numeric_limits::digits); } @@ -336,31 +327,35 @@ using iterator_t = decltype(std::begin(std::declval())); template using sentinel_t = decltype(std::end(std::declval())); // A workaround for std::string not having mutable data() until C++17. -template inline Char* get_data(std::basic_string& s) { +template +inline auto get_data(std::basic_string& s) -> Char* { return &s[0]; } template -inline typename Container::value_type* get_data(Container& c) { +inline auto get_data(Container& c) -> typename Container::value_type* { return c.data(); } #if defined(_SECURE_SCL) && _SECURE_SCL // Make a checked iterator to avoid MSVC warnings. template using checked_ptr = stdext::checked_array_iterator; -template checked_ptr make_checked(T* p, size_t size) { +template auto make_checked(T* p, size_t size) -> checked_ptr { return {p, size}; } #else template using checked_ptr = T*; -template inline T* make_checked(T* p, size_t) { return p; } +template inline auto make_checked(T* p, size_t) -> T* { return p; } #endif +// Attempts to reserve space for n extra characters in the output range. +// Returns a pointer to the reserved range or a reference to it. template ::value)> #if FMT_CLANG_VERSION >= 307 && !FMT_ICC_VERSION __attribute__((no_sanitize("undefined"))) #endif -inline checked_ptr -reserve(std::back_insert_iterator it, size_t n) { +inline auto +reserve(std::back_insert_iterator it, size_t n) + -> checked_ptr { Container& c = get_container(it); size_t size = c.size(); c.resize(size + n); @@ -368,13 +363,14 @@ reserve(std::back_insert_iterator it, size_t n) { } template -inline buffer_appender reserve(buffer_appender it, size_t n) { +inline auto reserve(buffer_appender it, size_t n) -> buffer_appender { buffer& buf = get_container(it); buf.try_reserve(buf.size() + n); return it; } -template constexpr Iterator& reserve(Iterator& it, size_t) { +template +constexpr auto reserve(Iterator& it, size_t) -> Iterator& { return it; } @@ -383,10 +379,10 @@ using reserve_iterator = remove_reference_t(), 0))>; template -constexpr T* to_pointer(OutputIt, size_t) { +constexpr auto to_pointer(OutputIt, size_t) -> T* { return nullptr; } -template T* to_pointer(buffer_appender it, size_t n) { +template auto to_pointer(buffer_appender it, size_t n) -> T* { buffer& buf = get_container(it); auto size = buf.size(); if (buf.capacity() < size + n) return nullptr; @@ -395,26 +391,27 @@ template T* to_pointer(buffer_appender it, size_t n) { } template ::value)> -inline std::back_insert_iterator base_iterator( - std::back_insert_iterator& it, - checked_ptr) { +inline auto base_iterator(std::back_insert_iterator& it, + checked_ptr) + -> std::back_insert_iterator { return it; } template -constexpr Iterator base_iterator(Iterator, Iterator it) { +constexpr auto base_iterator(Iterator, Iterator it) -> Iterator { return it; } // is spectacularly slow to compile in C++20 so use a simple fill_n // instead (#1998). template -FMT_CONSTEXPR OutputIt fill_n(OutputIt out, Size count, const T& value) { +FMT_CONSTEXPR auto fill_n(OutputIt out, Size count, const T& value) + -> OutputIt { for (Size i = 0; i < count; ++i) *out++ = value; return out; } template -FMT_CONSTEXPR20 T* fill_n(T* out, Size count, char value) { +FMT_CONSTEXPR20 auto fill_n(T* out, Size count, char value) -> T* { if (is_constant_evaluated()) { return fill_n(out, count, value); } @@ -428,43 +425,10 @@ using char8_type = char8_t; enum char8_type : unsigned char {}; #endif -template -using needs_conversion = bool_constant< - std::is_same::value_type, - char>::value && - std::is_same::value>; - -template ::value)> -FMT_CONSTEXPR OutputIt copy_str(InputIt begin, InputIt end, OutputIt it) { - while (begin != end) *it++ = *begin++; - return it; -} - -template ::value)> -FMT_CONSTEXPR20 OutChar* copy_str(InputIt begin, InputIt end, OutChar* out) { - if (is_constant_evaluated()) { - return copy_str(begin, end, out); - } - auto size = to_unsigned(end - begin); - std::uninitialized_copy(begin, end, make_checked(out, size)); - return out + size; -} - -template ::value)> -OutputIt copy_str(InputIt begin, InputIt end, OutputIt it) { - while (begin != end) *it++ = static_cast(*begin++); - return it; -} - -template ::value)> -buffer_appender copy_str(InputIt begin, InputIt end, - buffer_appender out) { - get_container(out).append(begin, end); - return out; +template +FMT_CONSTEXPR FMT_NOINLINE auto copy_str_noinline(InputIt begin, InputIt end, + OutputIt out) -> OutputIt { + return copy_str(begin, end, out); } // A public domain branchless UTF-8 decoder by Christopher Wellons: @@ -484,8 +448,8 @@ buffer_appender copy_str(InputIt begin, InputIt end, * occurs, this pointer will be a guess that depends on the particular * error, but it will always advance at least one byte. */ -FMT_CONSTEXPR inline const char* utf8_decode(const char* s, uint32_t* c, - int* e) { +FMT_CONSTEXPR inline auto utf8_decode(const char* s, uint32_t* c, int* e) + -> const char* { constexpr const int masks[] = {0x00, 0x7f, 0x1f, 0x0f, 0x07}; constexpr const uint32_t mins[] = {4194304, 0, 128, 2048, 65536}; constexpr const int shiftc[] = {0, 18, 12, 6, 0}; @@ -541,7 +505,7 @@ FMT_CONSTEXPR void for_each_codepoint(string_view s, F f) { } template -inline size_t compute_width(basic_string_view s) { +inline auto compute_width(basic_string_view s) -> size_t { return s.size(); } @@ -552,13 +516,13 @@ FMT_CONSTEXPR inline size_t compute_width(string_view s) { struct count_code_points { size_t* count; FMT_CONSTEXPR void operator()(uint32_t cp, int error) const { - *count += + *count += detail::to_unsigned( 1 + (error == 0 && cp >= 0x1100 && (cp <= 0x115f || // Hangul Jamo init. consonants - cp == 0x2329 || // LEFT-POINTING ANGLE BRACKET〈 - cp == 0x232a || // RIGHT-POINTING ANGLE BRACKET 〉 - // CJK ... Yi except Unicode Character “〿”: + cp == 0x2329 || // LEFT-POINTING ANGLE BRACKET + cp == 0x232a || // RIGHT-POINTING ANGLE BRACKET + // CJK ... Yi except IDEOGRAPHIC HALF FILL SPACE: (cp >= 0x2e80 && cp <= 0xa4cf && cp != 0x303f) || (cp >= 0xac00 && cp <= 0xd7a3) || // Hangul Syllables (cp >= 0xf900 && cp <= 0xfaff) || // CJK Compatibility Ideographs @@ -571,26 +535,27 @@ FMT_CONSTEXPR inline size_t compute_width(string_view s) { // Miscellaneous Symbols and Pictographs + Emoticons: (cp >= 0x1f300 && cp <= 0x1f64f) || // Supplemental Symbols and Pictographs: - (cp >= 0x1f900 && cp <= 0x1f9ff))); + (cp >= 0x1f900 && cp <= 0x1f9ff)))); } }; for_each_codepoint(s, count_code_points{&num_code_points}); return num_code_points; } -inline size_t compute_width(basic_string_view s) { +inline auto compute_width(basic_string_view s) -> size_t { return compute_width(basic_string_view( reinterpret_cast(s.data()), s.size())); } template -inline size_t code_point_index(basic_string_view s, size_t n) { +inline auto code_point_index(basic_string_view s, size_t n) -> size_t { size_t size = s.size(); return n < size ? n : size; } // Calculates the index of the nth code point in a UTF-8 string. -inline size_t code_point_index(basic_string_view s, size_t n) { +inline auto code_point_index(basic_string_view s, size_t n) + -> size_t { const char8_type* data = s.data(); size_t num_code_points = 0; for (size_t i = 0, size = s.size(); i != size; ++i) { @@ -621,20 +586,14 @@ void buffer::append(const U* begin, const U* end) { } } -template -void iterator_buffer::flush() { - auto size = this->size(); - this->clear(); - out_ = copy_str(data_, data_ + this->limit(size), out_); -} +template +struct is_locale : std::false_type {}; +template +struct is_locale> : std::true_type {}; } // namespace detail FMT_MODULE_EXPORT_BEGIN -template <> struct is_char : std::true_type {}; -template <> struct is_char : std::true_type {}; -template <> struct is_char : std::true_type {}; - // The number of characters to store in the basic_memory_buffer object itself // to avoid dynamic memory allocation. enum { inline_buffer_size = 500 }; @@ -644,15 +603,7 @@ enum { inline_buffer_size = 500 }; A dynamically growing memory buffer for trivially copyable/constructible types with the first ``SIZE`` elements stored in the object itself. - You can use one of the following type aliases for common character types: - - +----------------+------------------------------+ - | Type | Definition | - +================+==============================+ - | memory_buffer | basic_memory_buffer | - +----------------+------------------------------+ - | wmemory_buffer | basic_memory_buffer | - +----------------+------------------------------+ + You can use the ``memory_buffer`` type alias for ``char`` instead. **Example**:: @@ -729,7 +680,8 @@ class basic_memory_buffer final : public detail::buffer { Moves the content of the other ``basic_memory_buffer`` object to this one. \endrst */ - basic_memory_buffer& operator=(basic_memory_buffer&& other) FMT_NOEXCEPT { + auto operator=(basic_memory_buffer&& other) FMT_NOEXCEPT + -> basic_memory_buffer& { FMT_ASSERT(this != &other, ""); deallocate(); move(other); @@ -737,7 +689,7 @@ class basic_memory_buffer final : public detail::buffer { } // Returns a copy of the allocator associated with this buffer. - Allocator get_allocator() const { return alloc_; } + auto get_allocator() const -> Allocator { return alloc_; } /** Resizes the buffer to contain *count* elements. If T is a POD type new @@ -782,12 +734,15 @@ void basic_memory_buffer::grow(size_t size) { } using memory_buffer = basic_memory_buffer; -using wmemory_buffer = basic_memory_buffer; template struct is_contiguous> : std::true_type { }; +namespace detail { +FMT_API void print(std::FILE*, string_view); +} + /** A formatting error such as invalid format string. */ FMT_CLASS_API class FMT_API format_error : public std::runtime_error { @@ -802,6 +757,54 @@ class FMT_API format_error : public std::runtime_error { ~format_error() FMT_NOEXCEPT FMT_OVERRIDE FMT_MSC_DEFAULT; }; +/** + \rst + Constructs a `~fmt::format_arg_store` object that contains references + to arguments and can be implicitly converted to `~fmt::format_args`. + If ``fmt`` is a compile-time string then `make_args_checked` checks + its validity at compile time. + \endrst + */ +template > +FMT_INLINE auto make_args_checked(const S& fmt, + const remove_reference_t&... args) + -> format_arg_store, remove_reference_t...> { + static_assert( + detail::count<( + std::is_base_of>::value && + std::is_reference::value)...>() == 0, + "passing views as lvalues is disallowed"); + detail::check_format_string(fmt); + return {args...}; +} + +// compile-time support +namespace detail_exported { +#if FMT_USE_NONTYPE_TEMPLATE_PARAMETERS +template struct fixed_string { + constexpr fixed_string(const Char (&str)[N]) { + detail::copy_str(static_cast(str), + str + N, data); + } + Char data[N]{}; +}; +#endif + +// Converts a compile-time string to basic_string_view. +template +constexpr auto compile_string_to_view(const Char (&s)[N]) + -> basic_string_view { + // Remove trailing NUL character if needed. Won't be present if this is used + // with a raw character array (i.e. not defined as a string). + return {s, N - (std::char_traits::to_int_type(s[N - 1]) == 0 ? 1 : 0)}; +} +template +constexpr auto compile_string_to_view(detail::std_string_view s) + -> basic_string_view { + return {s.data(), s.size()}; +} +} // namespace detail_exported + FMT_BEGIN_DETAIL_NAMESPACE inline void throw_format_error(const char* message) { @@ -820,16 +823,16 @@ using is_signed = // Returns true if value is negative, false otherwise. // Same as `value < 0` but doesn't produce warnings if T is an unsigned type. template ::value)> -FMT_CONSTEXPR bool is_negative(T value) { +FMT_CONSTEXPR auto is_negative(T value) -> bool { return value < 0; } template ::value)> -FMT_CONSTEXPR bool is_negative(T) { +FMT_CONSTEXPR auto is_negative(T) -> bool { return false; } template ::value)> -FMT_CONSTEXPR bool is_supported_floating_point(T) { +FMT_CONSTEXPR auto is_supported_floating_point(T) -> uint16_t { return (std::is_same::value && FMT_USE_FLOAT) || (std::is_same::value && FMT_USE_DOUBLE) || (std::is_same::value && FMT_USE_LONG_DOUBLE); @@ -856,8 +859,7 @@ template struct basic_data { static const uint64_t log10_2_significand = 0x4d104d427de7fbcc; // GCC generates slightly better code for pairs than chars. - using digit_pair = char[2]; - static constexpr const digit_pair digits[] = { + FMT_API static constexpr const char digits[100][2] = { {'0', '0'}, {'0', '1'}, {'0', '2'}, {'0', '3'}, {'0', '4'}, {'0', '5'}, {'0', '6'}, {'0', '7'}, {'0', '8'}, {'0', '9'}, {'1', '0'}, {'1', '1'}, {'1', '2'}, {'1', '3'}, {'1', '4'}, {'1', '5'}, {'1', '6'}, {'1', '7'}, @@ -876,29 +878,25 @@ template struct basic_data { {'9', '0'}, {'9', '1'}, {'9', '2'}, {'9', '3'}, {'9', '4'}, {'9', '5'}, {'9', '6'}, {'9', '7'}, {'9', '8'}, {'9', '9'}}; - static constexpr const char hex_digits[] = "0123456789abcdef"; - static constexpr const char signs[] = {0, '-', '+', ' '}; - static constexpr const unsigned prefixes[4] = {0, 0, 0x1000000u | '+', - 0x1000000u | ' '}; - static constexpr const char left_padding_shifts[] = {31, 31, 0, 1, 0}; - static constexpr const char right_padding_shifts[] = {0, 31, 0, 1, 0}; + FMT_API static constexpr const char hex_digits[] = "0123456789abcdef"; + FMT_API static constexpr const char signs[4] = {0, '-', '+', ' '}; + FMT_API static constexpr const unsigned prefixes[4] = {0, 0, 0x1000000u | '+', + 0x1000000u | ' '}; + FMT_API static constexpr const char left_padding_shifts[5] = {31, 31, 0, 1, + 0}; + FMT_API static constexpr const char right_padding_shifts[5] = {0, 31, 0, 1, + 0}; }; +#ifdef FMT_SHARED +// Required for -flto, -fivisibility=hidden and -shared to work +extern template struct basic_data; +#endif + // This is a struct rather than an alias to avoid shadowing warnings in gcc. struct data : basic_data<> {}; -// Maps bsr(n) to ceil(log10(pow(2, bsr(n) + 1) - 1)). -// This is a function instead of an array to workaround a bug in GCC10 (#1810). -FMT_INLINE uint16_t bsr2log10(int bsr) { - static constexpr uint16_t data[] = { - 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, - 6, 6, 6, 7, 7, 7, 7, 8, 8, 8, 9, 9, 9, 10, 10, 10, - 10, 11, 11, 11, 12, 12, 12, 13, 13, 13, 13, 14, 14, 14, 15, 15, - 15, 16, 16, 16, 16, 17, 17, 17, 18, 18, 18, 19, 19, 19, 19, 20}; - return data[bsr]; -} - -template FMT_CONSTEXPR int count_digits_fallback(T n) { +template FMT_CONSTEXPR auto count_digits_fallback(T n) -> int { int count = 1; for (;;) { // Integer division is slow so do it for a group of four digits instead @@ -913,31 +911,36 @@ template FMT_CONSTEXPR int count_digits_fallback(T n) { } } #if FMT_USE_INT128 -FMT_CONSTEXPR inline int count_digits(uint128_t n) { +FMT_CONSTEXPR inline auto count_digits(uint128_t n) -> int { return count_digits_fallback(n); } #endif // Returns the number of decimal digits in n. Leading zeros are not counted // except for n == 0 in which case count_digits returns 1. -FMT_CONSTEXPR20 inline int count_digits(uint64_t n) { - if (is_constant_evaluated()) { - return count_digits_fallback(n); - } +FMT_CONSTEXPR20 inline auto count_digits(uint64_t n) -> int { #ifdef FMT_BUILTIN_CLZLL - // https://github.com/fmtlib/format-benchmark/blob/master/digits10 - auto t = bsr2log10(FMT_BUILTIN_CLZLL(n | 1) ^ 63); - constexpr const uint64_t zero_or_powers_of_10[] = { - 0, 0, FMT_POWERS_OF_10(1U), FMT_POWERS_OF_10(1000000000ULL), - 10000000000000000000ULL}; - return t - (n < zero_or_powers_of_10[t]); -#else - return count_digits_fallback(n); + if (!is_constant_evaluated()) { + // https://github.com/fmtlib/format-benchmark/blob/master/digits10 + // Maps bsr(n) to ceil(log10(pow(2, bsr(n) + 1) - 1)). + constexpr uint16_t bsr2log10[] = { + 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, + 6, 6, 6, 7, 7, 7, 7, 8, 8, 8, 9, 9, 9, 10, 10, 10, + 10, 11, 11, 11, 12, 12, 12, 13, 13, 13, 13, 14, 14, 14, 15, 15, + 15, 16, 16, 16, 16, 17, 17, 17, 18, 18, 18, 19, 19, 19, 19, 20}; + auto t = bsr2log10[FMT_BUILTIN_CLZLL(n | 1) ^ 63]; + constexpr const uint64_t zero_or_powers_of_10[] = { + 0, 0, FMT_POWERS_OF_10(1U), FMT_POWERS_OF_10(1000000000ULL), + 10000000000000000000ULL}; + return t - (n < zero_or_powers_of_10[t]); + } #endif + return count_digits_fallback(n); } // Counts the number of digits in n. BITS = log2(radix). -template FMT_CONSTEXPR int count_digits(UInt n) { +template +FMT_CONSTEXPR auto count_digits(UInt n) -> int { #ifdef FMT_BUILTIN_CLZ if (num_bits() == 32) return (FMT_BUILTIN_CLZ(static_cast(n) | 1) ^ 31) / BITS + 1; @@ -949,65 +952,82 @@ template FMT_CONSTEXPR int count_digits(UInt n) { return num_digits; } -template <> int count_digits<4>(detail::fallback_uintptr n); +template <> auto count_digits<4>(detail::fallback_uintptr n) -> int; -#if FMT_GCC_VERSION || FMT_CLANG_VERSION -# define FMT_ALWAYS_INLINE inline __attribute__((always_inline)) -#elif FMT_MSC_VER -# define FMT_ALWAYS_INLINE __forceinline -#else -# define FMT_ALWAYS_INLINE inline -#endif - -#ifdef FMT_BUILTIN_CLZ -// Optional version of count_digits for better performance on 32-bit platforms. -FMT_CONSTEXPR20 inline int count_digits(uint32_t n) { - if (is_constant_evaluated()) { - return count_digits_fallback(n); - } - auto t = bsr2log10(FMT_BUILTIN_CLZ(n | 1) ^ 31); - constexpr const uint32_t zero_or_powers_of_10[] = {0, 0, - FMT_POWERS_OF_10(1U)}; - return t - (n < zero_or_powers_of_10[t]); +// It is a separate function rather than a part of count_digits to workaround +// the lack of static constexpr in constexpr functions. +FMT_INLINE uint64_t count_digits_inc(int n) { + // An optimization by Kendall Willets from https://bit.ly/3uOIQrB. + // This increments the upper 32 bits (log10(T) - 1) when >= T is added. +#define FMT_INC(T) (((sizeof(#T) - 1ull) << 32) - T) + static constexpr uint64_t table[] = { + FMT_INC(0), FMT_INC(0), FMT_INC(0), // 8 + FMT_INC(10), FMT_INC(10), FMT_INC(10), // 64 + FMT_INC(100), FMT_INC(100), FMT_INC(100), // 512 + FMT_INC(1000), FMT_INC(1000), FMT_INC(1000), // 4096 + FMT_INC(10000), FMT_INC(10000), FMT_INC(10000), // 32k + FMT_INC(100000), FMT_INC(100000), FMT_INC(100000), // 256k + FMT_INC(1000000), FMT_INC(1000000), FMT_INC(1000000), // 2048k + FMT_INC(10000000), FMT_INC(10000000), FMT_INC(10000000), // 16M + FMT_INC(100000000), FMT_INC(100000000), FMT_INC(100000000), // 128M + FMT_INC(1000000000), FMT_INC(1000000000), FMT_INC(1000000000), // 1024M + FMT_INC(1000000000), FMT_INC(1000000000) // 4B + }; + return table[n]; } -#endif -template constexpr int digits10() FMT_NOEXCEPT { +// Optional version of count_digits for better performance on 32-bit platforms. +FMT_CONSTEXPR20 inline auto count_digits(uint32_t n) -> int { +#ifdef FMT_BUILTIN_CLZ + if (!is_constant_evaluated()) { + auto inc = count_digits_inc(FMT_BUILTIN_CLZ(n | 1) ^ 31); + return static_cast((n + inc) >> 32); + } +#endif + return count_digits_fallback(n); +} + +template constexpr auto digits10() FMT_NOEXCEPT -> int { return std::numeric_limits::digits10; } -template <> constexpr int digits10() FMT_NOEXCEPT { return 38; } -template <> constexpr int digits10() FMT_NOEXCEPT { return 38; } - -// DEPRECATED! grouping will be merged into thousands_sep. -template FMT_API std::string grouping_impl(locale_ref loc); -template inline std::string grouping(locale_ref loc) { - return grouping_impl(loc); +template <> constexpr auto digits10() FMT_NOEXCEPT -> int { + return 38; } -template <> inline std::string grouping(locale_ref loc) { - return grouping_impl(loc); +template <> constexpr auto digits10() FMT_NOEXCEPT -> int { + return 38; } -template FMT_API Char thousands_sep_impl(locale_ref loc); -template inline Char thousands_sep(locale_ref loc) { - return Char(thousands_sep_impl(loc)); +template struct thousands_sep_result { + std::string grouping; + Char thousands_sep; +}; + +template +FMT_API auto thousands_sep_impl(locale_ref loc) -> thousands_sep_result; +template +inline auto thousands_sep(locale_ref loc) -> thousands_sep_result { + auto result = thousands_sep_impl(loc); + return {result.grouping, Char(result.thousands_sep)}; } -template <> inline wchar_t thousands_sep(locale_ref loc) { +template <> +inline auto thousands_sep(locale_ref loc) -> thousands_sep_result { return thousands_sep_impl(loc); } -template FMT_API Char decimal_point_impl(locale_ref loc); -template inline Char decimal_point(locale_ref loc) { +template +FMT_API auto decimal_point_impl(locale_ref loc) -> Char; +template inline auto decimal_point(locale_ref loc) -> Char { return Char(decimal_point_impl(loc)); } -template <> inline wchar_t decimal_point(locale_ref loc) { +template <> inline auto decimal_point(locale_ref loc) -> wchar_t { return decimal_point_impl(loc); } // Compares two characters for equality. -template bool equal2(const Char* lhs, const char* rhs) { - return lhs[0] == rhs[0] && lhs[1] == rhs[1]; +template auto equal2(const Char* lhs, const char* rhs) -> bool { + return lhs[0] == Char(rhs[0]) && lhs[1] == Char(rhs[1]); } -inline bool equal2(const char* lhs, const char* rhs) { +inline auto equal2(const char* lhs, const char* rhs) -> bool { return memcmp(lhs, rhs, 2) == 0; } @@ -1027,9 +1047,8 @@ template struct format_decimal_result { // buffer of specified size. The caller must ensure that the buffer is large // enough. template -FMT_CONSTEXPR20 format_decimal_result format_decimal(Char* out, - UInt value, - int size) { +FMT_CONSTEXPR20 auto format_decimal(Char* out, UInt value, int size) + -> format_decimal_result { FMT_ASSERT(size >= count_digits(value), "invalid digit count"); out += size; Char* end = out; @@ -1060,17 +1079,17 @@ FMT_CONSTEXPR20 format_decimal_result format_decimal(Char* out, template >::value)> -inline format_decimal_result format_decimal(Iterator out, UInt value, - int size) { +inline auto format_decimal(Iterator out, UInt value, int size) + -> format_decimal_result { // Buffer is large enough to hold all digits (digits10 + 1). Char buffer[digits10() + 1]; auto end = format_decimal(buffer, value, size).end; - return {out, detail::copy_str(buffer, end, out)}; + return {out, detail::copy_str_noinline(buffer, end, out)}; } template -FMT_CONSTEXPR Char* format_uint(Char* buffer, UInt value, int num_digits, - bool upper = false) { +FMT_CONSTEXPR auto format_uint(Char* buffer, UInt value, int num_digits, + bool upper = false) -> Char* { buffer += num_digits; Char* end = buffer; do { @@ -1083,8 +1102,8 @@ FMT_CONSTEXPR Char* format_uint(Char* buffer, UInt value, int num_digits, } template -Char* format_uint(Char* buffer, detail::fallback_uintptr n, int num_digits, - bool = false) { +auto format_uint(Char* buffer, detail::fallback_uintptr n, int num_digits, + bool = false) -> Char* { auto char_digits = std::numeric_limits::digits / 4; int start = (num_digits + char_digits - 1) / char_digits - 1; if (int start_digits = num_digits % char_digits) { @@ -1105,7 +1124,8 @@ Char* format_uint(Char* buffer, detail::fallback_uintptr n, int num_digits, } template -inline It format_uint(It out, UInt value, int num_digits, bool upper = false) { +inline auto format_uint(It out, UInt value, int num_digits, bool upper = false) + -> It { if (auto ptr = to_pointer(out, to_unsigned(num_digits))) { format_uint(ptr, value, num_digits, upper); return out; @@ -1113,24 +1133,22 @@ inline It format_uint(It out, UInt value, int num_digits, bool upper = false) { // Buffer should be large enough to hold all digits (digits / BASE_BITS + 1). char buffer[num_bits() / BASE_BITS + 1]; format_uint(buffer, value, num_digits, upper); - return detail::copy_str(buffer, buffer + num_digits, out); + return detail::copy_str_noinline(buffer, buffer + num_digits, out); } // A converter from UTF-8 to UTF-16. class utf8_to_utf16 { private: - wmemory_buffer buffer_; + basic_memory_buffer buffer_; public: FMT_API explicit utf8_to_utf16(string_view s); - operator wstring_view() const { return {&buffer_[0], size()}; } - size_t size() const { return buffer_.size() - 1; } - const wchar_t* c_str() const { return &buffer_[0]; } - std::wstring str() const { return {&buffer_[0], size()}; } + operator basic_string_view() const { return {&buffer_[0], size()}; } + auto size() const -> size_t { return buffer_.size() - 1; } + auto c_str() const -> const wchar_t* { return &buffer_[0]; } + auto str() const -> std::wstring { return {&buffer_[0], size()}; } }; -template struct null {}; - namespace dragonbox { // Type-specific information that Dragonbox uses. @@ -1194,37 +1212,21 @@ template struct decimal_fp { int exponent; }; -template FMT_API decimal_fp to_decimal(T x) FMT_NOEXCEPT; +template +FMT_API auto to_decimal(T x) FMT_NOEXCEPT -> decimal_fp; } // namespace dragonbox template -constexpr typename dragonbox::float_info::carrier_uint exponent_mask() { +constexpr auto exponent_mask() -> + typename dragonbox::float_info::carrier_uint { using uint = typename dragonbox::float_info::carrier_uint; return ((uint(1) << dragonbox::float_info::exponent_bits) - 1) << dragonbox::float_info::significand_bits; } -// 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 use_grisu : 1; - bool showpoint : 1; -}; - // Writes the exponent exp in the form "[+-]d{2,3}" to buffer. -template It write_exponent(int exp, It it) { +template +auto write_exponent(int exp, It it) -> It { FMT_ASSERT(-10000 < exp && exp < 10000, "exponent out of range"); if (exp < 0) { *it++ = static_cast('-'); @@ -1245,132 +1247,22 @@ template It write_exponent(int exp, It it) { } template -int format_float(T value, int precision, float_specs specs, buffer& buf); +auto format_float(T value, int precision, float_specs specs, buffer& buf) + -> int; // Formats a floating-point number with snprintf. template -int snprintf_float(T value, int precision, float_specs specs, - buffer& buf); +auto snprintf_float(T value, int precision, float_specs specs, + buffer& buf) -> int; -template T promote_float(T value) { return value; } -inline double promote_float(float value) { return static_cast(value); } - -template -FMT_CONSTEXPR float_specs parse_float_type_spec( - const basic_format_specs& specs, ErrorHandler&& eh = {}) { - auto result = float_specs(); - result.showpoint = specs.alt; - result.locale = specs.localized; - switch (specs.type) { - case 0: - result.format = float_format::general; - break; - case 'G': - result.upper = true; - FMT_FALLTHROUGH; - case 'g': - result.format = float_format::general; - break; - case 'E': - result.upper = true; - FMT_FALLTHROUGH; - case 'e': - result.format = float_format::exp; - result.showpoint |= specs.precision != 0; - break; - case 'F': - result.upper = true; - FMT_FALLTHROUGH; - case 'f': - result.format = float_format::fixed; - result.showpoint |= specs.precision != 0; - break; - case 'A': - result.upper = true; - FMT_FALLTHROUGH; - case 'a': - result.format = float_format::hex; - break; - default: - eh.on_error("invalid type specifier"); - break; - } - return result; +template auto promote_float(T value) -> T { return value; } +inline auto promote_float(float value) -> double { + return static_cast(value); } -template -FMT_CONSTEXPR void check_int_type_spec(char spec, ErrorHandler&& eh) { - switch (spec) { - case 0: - case 'd': - case 'x': - case 'X': - case 'b': - case 'B': - case 'o': - case 'c': - break; - default: - eh.on_error("invalid type specifier"); - break; - } -} - -template -FMT_CONSTEXPR void handle_char_specs(const basic_format_specs& specs, - Handler&& handler) { - if (specs.type && specs.type != 'c') return handler.on_int(); - if (specs.align == align::numeric || specs.sign != sign::none || specs.alt) - handler.on_error("invalid format specifier for char"); - handler.on_char(); -} - -template -FMT_CONSTEXPR void handle_cstring_type_spec(Char spec, Handler&& handler) { - if (spec == 0 || spec == 's') - handler.on_string(); - else if (spec == 'p') - handler.on_pointer(); - else - handler.on_error("invalid type specifier"); -} - -template -FMT_CONSTEXPR void check_string_type_spec(Char spec, ErrorHandler&& eh) { - if (spec != 0 && spec != 's') eh.on_error("invalid type specifier"); -} - -template -FMT_CONSTEXPR void check_pointer_type_spec(Char spec, ErrorHandler&& eh) { - if (spec != 0 && spec != 'p') eh.on_error("invalid type specifier"); -} - -template -class char_specs_checker : public ErrorHandler { - private: - char type_; - - public: - FMT_CONSTEXPR char_specs_checker(char type, ErrorHandler eh) - : ErrorHandler(eh), type_(type) {} - - FMT_CONSTEXPR void on_int() { check_int_type_spec(type_, *this); } - FMT_CONSTEXPR void on_char() {} -}; - -template -class cstring_type_checker : public ErrorHandler { - public: - FMT_CONSTEXPR explicit cstring_type_checker(ErrorHandler eh) - : ErrorHandler(eh) {} - - FMT_CONSTEXPR void on_string() {} - FMT_CONSTEXPR void on_pointer() {} -}; - template -FMT_NOINLINE FMT_CONSTEXPR OutputIt fill(OutputIt it, size_t n, - const fill_t& fill) { +FMT_NOINLINE FMT_CONSTEXPR auto fill(OutputIt it, size_t n, + const fill_t& fill) -> OutputIt { auto fill_size = fill.size(); if (fill_size == 1) return detail::fill_n(it, n, fill[0]); auto data = fill.data(); @@ -1384,9 +1276,9 @@ FMT_NOINLINE FMT_CONSTEXPR OutputIt fill(OutputIt it, size_t n, // width: output display width in (terminal) column positions. template -FMT_CONSTEXPR OutputIt write_padded(OutputIt out, - const basic_format_specs& specs, - size_t size, size_t width, F&& f) { +FMT_CONSTEXPR auto write_padded(OutputIt out, + const basic_format_specs& specs, + size_t size, size_t width, F&& f) -> OutputIt { static_assert(align == align::left || align == align::right, ""); unsigned spec_width = to_unsigned(specs.width); size_t padding = spec_width > width ? spec_width - width : 0; @@ -1403,35 +1295,50 @@ FMT_CONSTEXPR OutputIt write_padded(OutputIt out, template -constexpr OutputIt write_padded(OutputIt out, - const basic_format_specs& specs, - size_t size, F&& f) { +constexpr auto write_padded(OutputIt out, const basic_format_specs& specs, + size_t size, F&& f) -> OutputIt { return write_padded(out, specs, size, size, f); } -template -OutputIt write_bytes(OutputIt out, string_view bytes, - const basic_format_specs& specs) { - return write_padded(out, specs, bytes.size(), - [bytes](reserve_iterator it) { - const char* data = bytes.data(); - return copy_str(data, data + bytes.size(), it); - }); +template +FMT_CONSTEXPR auto write_bytes(OutputIt out, string_view bytes, + const basic_format_specs& specs) + -> OutputIt { + return write_padded( + out, specs, bytes.size(), [bytes](reserve_iterator it) { + const char* data = bytes.data(); + return copy_str(data, data + bytes.size(), it); + }); +} + +template +auto write_ptr(OutputIt out, UIntPtr value, + const basic_format_specs* specs) -> OutputIt { + int num_digits = count_digits<4>(value); + auto size = to_unsigned(num_digits) + size_t(2); + auto write = [=](reserve_iterator it) { + *it++ = static_cast('0'); + *it++ = static_cast('x'); + return format_uint<4, Char>(it, value, num_digits); + }; + return specs ? write_padded(out, *specs, size, write) + : base_iterator(out, write(reserve(out, size))); } template -FMT_CONSTEXPR OutputIt write_char(OutputIt out, Char value, - const basic_format_specs& specs) { +FMT_CONSTEXPR auto write_char(OutputIt out, Char value, + const basic_format_specs& specs) + -> OutputIt { return write_padded(out, specs, 1, [=](reserve_iterator it) { *it++ = value; return it; }); } template -FMT_CONSTEXPR OutputIt write(OutputIt out, Char value, - const basic_format_specs& specs, - locale_ref loc = {}) { - return !specs.type || specs.type == 'c' +FMT_CONSTEXPR auto write(OutputIt out, Char value, + const basic_format_specs& specs, + locale_ref loc = {}) -> OutputIt { + return check_char_specs(specs) ? write_char(out, value, specs) : write(out, static_cast(value), specs, loc); } @@ -1463,9 +1370,10 @@ template struct write_int_data { // where are written by write_digits(it). // prefix contains chars in three lower bytes and the size in the fourth byte. template -FMT_CONSTEXPR FMT_INLINE OutputIt -write_int(OutputIt out, int num_digits, unsigned prefix, - const basic_format_specs& specs, W write_digits) { +FMT_CONSTEXPR FMT_INLINE auto write_int(OutputIt out, int num_digits, + unsigned prefix, + const basic_format_specs& specs, + W write_digits) -> OutputIt { // Slightly faster check for specs.width == 0 && specs.precision == -1. if ((specs.width | (specs.precision + 1)) == 0) { auto it = reserve(out, to_unsigned(num_digits) + (prefix >> 24)); @@ -1486,17 +1394,16 @@ write_int(OutputIt out, int num_digits, unsigned prefix, } template -bool write_int_localized(OutputIt& out, UInt value, unsigned prefix, - const basic_format_specs& specs, - locale_ref loc) { +auto write_int_localized(OutputIt& out, UInt value, unsigned prefix, + const basic_format_specs& specs, locale_ref loc) + -> bool { static_assert(std::is_same, UInt>::value, ""); const auto sep_size = 1; - std::string groups = grouping(loc); - if (groups.empty()) return false; - auto sep = thousands_sep(loc); - if (!sep) return false; + auto ts = thousands_sep(loc); + if (!ts.thousands_sep) return false; int num_digits = count_digits(value); int size = num_digits, n = num_digits; + const std::string& groups = ts.grouping; std::string::const_iterator group = groups.cbegin(); while (group != groups.cend() && n > *group && *group > 0 && *group != max_value()) { @@ -1511,7 +1418,7 @@ bool write_int_localized(OutputIt& out, UInt value, unsigned prefix, if (prefix != 0) ++size; const auto usize = to_unsigned(size); buffer.resize(usize); - basic_string_view s(&sep, sep_size); + basic_string_view s(&ts.thousands_sep, sep_size); // Index of a decimal digit with the least significant digit having index 0. int digit_index = 0; group = groups.cbegin(); @@ -1544,19 +1451,32 @@ FMT_CONSTEXPR inline void prefix_append(unsigned& prefix, unsigned value) { prefix += (1u + (value > 0xff ? 1 : 0)) << 24; } -template ::value && !std::is_same::value)> -FMT_CONSTEXPR FMT_INLINE OutputIt -write_int(OutputIt out, T value, const basic_format_specs& specs, - locale_ref loc) { +template struct write_int_arg { + UInt abs_value; + unsigned prefix; +}; + +template +FMT_CONSTEXPR auto make_write_int_arg(T value, sign_t sign) + -> write_int_arg> { auto prefix = 0u; auto abs_value = static_cast>(value); if (is_negative(value)) { prefix = 0x01000000 | '-'; abs_value = 0 - abs_value; } else { - prefix = data::prefixes[specs.sign]; + prefix = data::prefixes[sign]; } + return {abs_value, prefix}; +} + +template +FMT_CONSTEXPR FMT_INLINE auto write_int(OutputIt out, write_int_arg arg, + const basic_format_specs& specs, + locale_ref loc) -> OutputIt { + static_assert(std::is_same>::value, ""); + auto abs_value = arg.abs_value; + auto prefix = arg.prefix; auto utype = static_cast(specs.type); switch (specs.type) { case 0: @@ -1614,54 +1534,65 @@ template ::value && !std::is_same::value && std::is_same>::value)> -FMT_CONSTEXPR OutputIt write(OutputIt out, T value, - const basic_format_specs& specs, - locale_ref loc) { - return write_int(out, value, specs, loc); +FMT_CONSTEXPR auto write(OutputIt out, T value, + const basic_format_specs& specs, locale_ref loc) + -> OutputIt { + return write_int(out, make_write_int_arg(value, specs.sign), specs, loc); } // An inlined version of write used in format string compilation. template ::value && !std::is_same::value && !std::is_same>::value)> -FMT_CONSTEXPR FMT_INLINE OutputIt write(OutputIt out, T value, - const basic_format_specs& specs, - locale_ref loc) { - return write_int(out, value, specs, loc); +FMT_CONSTEXPR FMT_INLINE auto write(OutputIt out, T value, + const basic_format_specs& specs, + locale_ref loc) -> OutputIt { + return write_int(out, make_write_int_arg(value, specs.sign), specs, loc); } -template -FMT_CONSTEXPR OutputIt write(OutputIt out, basic_string_view s, - const basic_format_specs& specs) { +template +FMT_CONSTEXPR auto write(OutputIt out, basic_string_view s, + const basic_format_specs& specs) -> OutputIt { auto data = s.data(); auto size = s.size(); if (specs.precision >= 0 && to_unsigned(specs.precision) < size) size = code_point_index(s, to_unsigned(specs.precision)); - auto width = specs.width != 0 - ? compute_width(basic_string_view(data, size)) - : 0; + auto width = + specs.width != 0 ? compute_width(basic_string_view(data, size)) : 0; return write_padded(out, specs, size, width, [=](reserve_iterator it) { return copy_str(data, data + size, it); }); } template -FMT_CONSTEXPR OutputIt write(OutputIt out, - basic_string_view> s, - const basic_format_specs& specs, - locale_ref) { - return write(out, s, specs); // Adapt write to formatter::format. +FMT_CONSTEXPR auto write(OutputIt out, + basic_string_view> s, + const basic_format_specs& specs, locale_ref) + -> OutputIt { + check_string_type_spec(specs.type); + return write(out, s, specs); +} +template +FMT_CONSTEXPR auto write(OutputIt out, const Char* s, + const basic_format_specs& specs, locale_ref) + -> OutputIt { + return check_cstring_type_spec(specs.type) + ? write(out, basic_string_view(s), specs, {}) + : write_ptr(out, to_uintptr(s), &specs); } template -OutputIt write_nonfinite(OutputIt out, bool isinf, - const basic_format_specs& specs, - const float_specs& fspecs) { +auto write_nonfinite(OutputIt out, bool isinf, basic_format_specs specs, + const float_specs& fspecs) -> OutputIt { auto str = isinf ? (fspecs.upper ? "INF" : "inf") : (fspecs.upper ? "NAN" : "nan"); constexpr size_t str_size = 3; auto sign = fspecs.sign; auto size = str_size + (sign ? 1 : 0); + // Replace '0'-padding with space for non-finite values. + const bool is_zero_fill = + specs.fill.size() == 1 && *specs.fill.data() == static_cast('0'); + if (is_zero_fill) specs.fill[0] = static_cast(' '); return write_padded(out, specs, size, [=](reserve_iterator it) { if (sign) *it++ = static_cast(data::signs[sign]); return copy_str(str, str + str_size, it); @@ -1675,30 +1606,29 @@ struct big_decimal_fp { int exponent; }; -inline int get_significand_size(const big_decimal_fp& fp) { +inline auto get_significand_size(const big_decimal_fp& fp) -> int { return fp.significand_size; } template -inline int get_significand_size(const dragonbox::decimal_fp& fp) { +inline auto get_significand_size(const dragonbox::decimal_fp& fp) -> int { return count_digits(fp.significand); } template -inline OutputIt write_significand(OutputIt out, const char* significand, - int& significand_size) { +inline auto write_significand(OutputIt out, const char* significand, + int& significand_size) -> OutputIt { return copy_str(significand, significand + significand_size, out); } template -inline OutputIt write_significand(OutputIt out, UInt significand, - int significand_size) { +inline auto write_significand(OutputIt out, UInt significand, + int significand_size) -> OutputIt { return format_decimal(out, significand, significand_size).end; } template ::value)> -inline Char* write_significand(Char* out, UInt significand, - int significand_size, int integral_size, - Char decimal_point) { +inline auto write_significand(Char* out, UInt significand, int significand_size, + int integral_size, Char decimal_point) -> Char* { if (!decimal_point) return format_decimal(out, significand, significand_size).end; auto end = format_decimal(out + 1, significand, significand_size).end; @@ -1714,31 +1644,32 @@ inline Char* write_significand(Char* out, UInt significand, template >::value)> -inline OutputIt write_significand(OutputIt out, UInt significand, - int significand_size, int integral_size, - Char decimal_point) { +inline auto write_significand(OutputIt out, UInt significand, + int significand_size, int integral_size, + Char decimal_point) -> OutputIt { // Buffer is large enough to hold digits (digits10 + 1) and a decimal point. Char buffer[digits10() + 2]; auto end = write_significand(buffer, significand, significand_size, integral_size, decimal_point); - return detail::copy_str(buffer, end, out); + return detail::copy_str_noinline(buffer, end, out); } template -inline OutputIt write_significand(OutputIt out, const char* significand, - int significand_size, int integral_size, - Char decimal_point) { - out = detail::copy_str(significand, significand + integral_size, out); +inline auto write_significand(OutputIt out, const char* significand, + int significand_size, int integral_size, + Char decimal_point) -> OutputIt { + out = detail::copy_str_noinline(significand, + significand + integral_size, out); if (!decimal_point) return out; *out++ = decimal_point; - return detail::copy_str(significand + integral_size, - significand + significand_size, out); + return detail::copy_str_noinline(significand + integral_size, + significand + significand_size, out); } template -OutputIt write_float(OutputIt out, const DecimalFP& fp, - const basic_format_specs& specs, float_specs fspecs, - Char decimal_point) { +auto write_float(OutputIt out, const DecimalFP& fp, + const basic_format_specs& specs, float_specs fspecs, + Char decimal_point) -> OutputIt { auto significand = fp.significand; int significand_size = get_significand_size(fp); static const Char zero = static_cast('0'); @@ -1836,8 +1767,8 @@ OutputIt write_float(OutputIt out, const DecimalFP& fp, template ::value)> -OutputIt write(OutputIt out, T value, basic_format_specs specs, - locale_ref loc = {}) { +auto write(OutputIt out, T value, basic_format_specs specs, + locale_ref loc = {}) -> OutputIt { if (const_check(!is_supported_floating_point(value))) return out; float_specs fspecs = parse_float_type_spec(specs); fspecs.sign = specs.sign; @@ -1863,7 +1794,8 @@ OutputIt write(OutputIt out, T value, basic_format_specs specs, if (fspecs.format == float_format::hex) { if (fspecs.sign) buffer.push_back(data::signs[fspecs.sign]); snprintf_float(promote_float(value), specs.precision, fspecs, buffer); - return write_bytes(out, {buffer.data(), buffer.size()}, specs); + return write_bytes(out, {buffer.data(), buffer.size()}, + specs); } int precision = specs.precision >= 0 || !specs.type ? specs.precision : 6; if (fspecs.format == float_format::exp) { @@ -1884,7 +1816,7 @@ OutputIt write(OutputIt out, T value, basic_format_specs specs, template ::value)> -OutputIt write(OutputIt out, T value) { +auto write(OutputIt out, T value) -> OutputIt { if (const_check(!is_supported_floating_point(value))) return out; using floaty = conditional_t::value, double, T>; @@ -1910,48 +1842,28 @@ OutputIt write(OutputIt out, T value) { template ::value && !is_fast_float::value)> -inline OutputIt write(OutputIt out, T value) { +inline auto write(OutputIt out, T value) -> OutputIt { return write(out, value, basic_format_specs()); } -template -OutputIt write_ptr(OutputIt out, UIntPtr value, - const basic_format_specs* specs) { - int num_digits = count_digits<4>(value); - auto size = to_unsigned(num_digits) + size_t(2); - auto write = [=](reserve_iterator it) { - *it++ = static_cast('0'); - *it++ = static_cast('x'); - return format_uint<4, Char>(it, value, num_digits); - }; - return specs ? write_padded(out, *specs, size, write) - : base_iterator(out, write(reserve(out, size))); -} - template -OutputIt write(OutputIt out, monostate) { +auto write(OutputIt out, monostate, basic_format_specs = {}, + locale_ref = {}) -> OutputIt { FMT_ASSERT(false, ""); return out; } -template ::value)> -OutputIt write(OutputIt out, string_view value) { - auto it = reserve(out, value.size()); - it = copy_str(value.begin(), value.end(), it); - return base_iterator(out, it); -} - template -FMT_CONSTEXPR OutputIt write(OutputIt out, basic_string_view value) { +FMT_CONSTEXPR auto write(OutputIt out, basic_string_view value) + -> OutputIt { auto it = reserve(out, value.size()); - it = copy_str(value.begin(), value.end(), it); + it = copy_str_noinline(value.begin(), value.end(), it); return base_iterator(out, it); } template ::value)> -constexpr OutputIt write(OutputIt out, const T& value) { +constexpr auto write(OutputIt out, const T& value) -> OutputIt { return write(out, to_string_view(value)); } @@ -1959,7 +1871,7 @@ template ::value && !std::is_same::value && !std::is_same::value)> -FMT_CONSTEXPR OutputIt write(OutputIt out, T value) { +FMT_CONSTEXPR auto write(OutputIt out, T value) -> OutputIt { auto abs_value = static_cast>(value); bool negative = is_negative(value); // Don't do -abs_value since it trips unsigned-integer-overflow sanitizer. @@ -1985,30 +1897,31 @@ template < mapped_type_constant>::value != type::custom_type, FMT_ENABLE_IF(check)> -FMT_CONSTEXPR OutputIt write(OutputIt out, T value) { +FMT_CONSTEXPR auto write(OutputIt out, T value) -> OutputIt { return write( out, static_cast::type>(value)); } template ::value)> -FMT_CONSTEXPR OutputIt write(OutputIt out, T value, - const basic_format_specs& specs = {}, - locale_ref = {}) { +FMT_CONSTEXPR auto write(OutputIt out, T value, + const basic_format_specs& specs = {}, + locale_ref = {}) -> OutputIt { return specs.type && specs.type != 's' ? write(out, value ? 1 : 0, specs, {}) - : write(out, string_view(value ? "true" : "false"), specs); + : write_bytes(out, value ? "true" : "false", specs); } template -FMT_CONSTEXPR OutputIt write(OutputIt out, Char value) { +FMT_CONSTEXPR auto write(OutputIt out, Char value) -> OutputIt { auto it = reserve(out, 1); *it++ = value; return base_iterator(out, it); } template -FMT_CONSTEXPR_CHAR_TRAITS OutputIt write(OutputIt out, const Char* value) { +FMT_CONSTEXPR_CHAR_TRAITS auto write(OutputIt out, const Char* value) + -> OutputIt { if (!value) { FMT_THROW(format_error("string pointer is null")); } else { @@ -2020,16 +1933,19 @@ FMT_CONSTEXPR_CHAR_TRAITS OutputIt write(OutputIt out, const Char* value) { template ::value)> -OutputIt write(OutputIt out, const T* value, - const basic_format_specs& specs = {}, locale_ref = {}) { +auto write(OutputIt out, const T* value, + const basic_format_specs& specs = {}, locale_ref = {}) + -> OutputIt { + check_pointer_type_spec(specs.type, error_handler()); return write_ptr(out, to_uintptr(value), &specs); } template -auto write(OutputIt out, const T& value) -> typename std::enable_if< - mapped_type_constant>::value == - type::custom_type, - OutputIt>::type { +FMT_CONSTEXPR auto write(OutputIt out, const T& value) -> + typename std::enable_if< + mapped_type_constant>::value == + type::custom_type, + OutputIt>::type { using context_type = basic_format_context; using formatter_type = conditional_t::value, @@ -2041,231 +1957,52 @@ auto write(OutputIt out, const T& value) -> typename std::enable_if< // An argument visitor that formats the argument and writes it via the output // iterator. It's a class and not a generic lambda for compatibility with C++11. -template struct default_arg_formatter { - using context = basic_format_context; +template struct default_arg_formatter { + using iterator = buffer_appender; + using context = buffer_context; - OutputIt out; + iterator out; basic_format_args args; locale_ref loc; - template OutputIt operator()(T value) { + template auto operator()(T value) -> iterator { return write(out, value); } - - OutputIt operator()(typename basic_format_arg::handle handle) { + auto operator()(typename basic_format_arg::handle h) -> iterator { basic_format_parse_context parse_ctx({}); - basic_format_context format_ctx(out, args, loc); - handle.format(parse_ctx, format_ctx); + context format_ctx(out, args, loc); + h.format(parse_ctx, format_ctx); return format_ctx.out(); } }; -template -class arg_formatter_base { - public: - using iterator = OutputIt; - using char_type = Char; - using format_specs = basic_format_specs; +template struct arg_formatter { + using iterator = buffer_appender; + using context = buffer_context; - private: - iterator out_; - const format_specs& specs_; - locale_ref locale_; + iterator out; + const basic_format_specs& specs; + locale_ref locale; - // Attempts to reserve space for n extra characters in the output range. - // Returns a pointer to the reserved range or a reference to out_. - auto reserve(size_t n) -> decltype(detail::reserve(out_, n)) { - return detail::reserve(out_, n); + template + FMT_CONSTEXPR FMT_INLINE auto operator()(T value) -> iterator { + return detail::write(out, value, specs, locale); } - - void write(char value) { - auto&& it = reserve(1); - *it++ = value; - } - - template ::value)> - void write(Ch value) { - out_ = detail::write(out_, value); - } - - void write(string_view value) { - auto&& it = reserve(value.size()); - it = copy_str(value.begin(), value.end(), it); - } - void write(wstring_view value) { - static_assert(std::is_same::value, ""); - auto&& it = reserve(value.size()); - it = copy_str(value.begin(), value.end(), it); - } - - template - void write(const Ch* s, size_t size, const format_specs& specs) { - auto width = - specs.width != 0 ? compute_width(basic_string_view(s, size)) : 0; - out_ = write_padded(out_, specs, size, width, - [=](reserve_iterator it) { - return copy_str(s, s + size, it); - }); - } - - template - FMT_CONSTEXPR void write(basic_string_view s, - const format_specs& specs = {}) { - out_ = detail::write(out_, s, specs); - } - - void write_pointer(const void* p) { - out_ = write_ptr(out_, to_uintptr(p), &specs_); - } - - struct char_spec_handler : ErrorHandler { - arg_formatter_base& formatter; - Char value; - - constexpr char_spec_handler(arg_formatter_base& f, Char val) - : formatter(f), value(val) {} - - FMT_CONSTEXPR void on_int() { - // char is only formatted as int if there are specs. - formatter.out_ = detail::write(formatter.out_, static_cast(value), - formatter.specs_, formatter.locale_); - } - FMT_CONSTEXPR void on_char() { - formatter.out_ = - detail::write(formatter.out_, value, formatter.specs_); - } - }; - - struct cstring_spec_handler : error_handler { - arg_formatter_base& formatter; - const Char* value; - - cstring_spec_handler(arg_formatter_base& f, const Char* val) - : formatter(f), value(val) {} - - void on_string() { formatter.write(value); } - void on_pointer() { formatter.write_pointer(value); } - }; - - protected: - iterator out() { return out_; } - const format_specs& specs() { return specs_; } - - FMT_CONSTEXPR void write(bool value) { - write(string_view(value ? "true" : "false"), specs_); - } - - void write(const Char* value) { - if (value) - write(basic_string_view(value), specs_); - else - FMT_THROW(format_error("string pointer is null")); - } - - public: - constexpr arg_formatter_base(OutputIt out, const format_specs& s, - locale_ref loc) - : out_(out), specs_(s), locale_(loc) {} - - iterator operator()(monostate) { - FMT_ASSERT(false, "invalid argument type"); - return out_; - } - - template ::value)> - FMT_CONSTEXPR FMT_INLINE iterator operator()(T value) { - return out_ = detail::write(out_, value, specs_, locale_); - } - - FMT_CONSTEXPR iterator operator()(Char value) { - handle_char_specs(specs_, - char_spec_handler(*this, static_cast(value))); - return out_; - } - - FMT_CONSTEXPR iterator operator()(bool value) { - if (specs_.type && specs_.type != 's') return (*this)(value ? 1 : 0); - write(value != 0); - return out_; - } - - template ::value)> - iterator operator()(T value) { - if (const_check(is_supported_floating_point(value))) - out_ = detail::write(out_, value, specs_, locale_); - else - FMT_ASSERT(false, "unsupported float argument type"); - return out_; - } - - iterator operator()(const Char* value) { - handle_cstring_type_spec(specs_.type, cstring_spec_handler(*this, value)); - return out_; - } - - FMT_CONSTEXPR iterator operator()(basic_string_view value) { - check_string_type_spec(specs_.type, error_handler()); - write(value, specs_); - return out_; - } - - iterator operator()(const void* value) { - check_pointer_type_spec(specs_.type, error_handler()); - write_pointer(value); - return out_; + auto operator()(typename basic_format_arg::handle) -> iterator { + // User-defined types are handled separately because they require access + // to the parse context. + return out; } }; -/** The default argument formatter. */ -template -class arg_formatter : public arg_formatter_base { - private: - using char_type = Char; - using base = arg_formatter_base; - using context_type = basic_format_context; +template struct custom_formatter { + basic_format_parse_context& parse_ctx; + buffer_context& ctx; - context_type& ctx_; - - public: - using iterator = typename base::iterator; - using format_specs = typename base::format_specs; - - /** - \rst - Constructs an argument formatter object. - *ctx* is a reference to the formatting context, - *specs* contains format specifier information for standard argument types. - \endrst - */ - constexpr explicit arg_formatter(context_type& ctx, const format_specs& specs) - : base(ctx.out(), specs, ctx.locale()), ctx_(ctx) {} - - using base::operator(); - - iterator operator()(typename basic_format_arg::handle) { - // User-defined types are handled separately because they require access to - // the parse context. - return ctx_.out(); + void operator()( + typename basic_format_arg>::handle h) const { + h.format(parse_ctx, ctx); } -}; - -template class custom_formatter { - private: - using char_type = typename Context::char_type; - - basic_format_parse_context& parse_ctx_; - Context& ctx_; - - public: - explicit custom_formatter(basic_format_parse_context& parse_ctx, - Context& ctx) - : parse_ctx_(parse_ctx), ctx_(ctx) {} - - void operator()(typename basic_format_arg::handle h) const { - h.format(parse_ctx_, ctx_); - } - template void operator()(T) const {} }; @@ -2280,13 +2017,13 @@ template class width_checker { explicit FMT_CONSTEXPR width_checker(ErrorHandler& eh) : handler_(eh) {} template ::value)> - FMT_CONSTEXPR unsigned long long operator()(T value) { + FMT_CONSTEXPR auto operator()(T value) -> unsigned long long { if (is_negative(value)) handler_.on_error("negative width"); return static_cast(value); } template ::value)> - FMT_CONSTEXPR unsigned long long operator()(T) { + FMT_CONSTEXPR auto operator()(T) -> unsigned long long { handler_.on_error("width is not integer"); return 0; } @@ -2300,13 +2037,13 @@ template class precision_checker { explicit FMT_CONSTEXPR precision_checker(ErrorHandler& eh) : handler_(eh) {} template ::value)> - FMT_CONSTEXPR unsigned long long operator()(T value) { + FMT_CONSTEXPR auto operator()(T value) -> unsigned long long { if (is_negative(value)) handler_.on_error("negative precision"); return static_cast(value); } template ::value)> - FMT_CONSTEXPR unsigned long long operator()(T) { + FMT_CONSTEXPR auto operator()(T) -> unsigned long long { handler_.on_error("precision is not integer"); return 0; } @@ -2315,114 +2052,50 @@ template class precision_checker { ErrorHandler& handler_; }; -template class numeric_specs_checker { - public: - FMT_CONSTEXPR numeric_specs_checker(ErrorHandler& eh, detail::type arg_type) - : error_handler_(eh), arg_type_(arg_type) {} - - FMT_CONSTEXPR void require_numeric_argument() { - if (!is_arithmetic_type(arg_type_)) - error_handler_.on_error("format specifier requires numeric argument"); - } - - FMT_CONSTEXPR void check_sign() { - 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) { - error_handler_.on_error("format specifier requires signed argument"); - } - } - - FMT_CONSTEXPR void check_precision() { - if (is_integral_type(arg_type_) || arg_type_ == type::pointer_type) - error_handler_.on_error("precision not allowed for this argument type"); - } - - private: - ErrorHandler& error_handler_; - detail::type arg_type_; -}; - -// A format specifier handler that checks if specifiers are consistent with the -// argument type. -template class specs_checker : public Handler { - private: - numeric_specs_checker checker_; - - // Suppress an MSVC warning about using this in initializer list. - FMT_CONSTEXPR Handler& error_handler() { return *this; } - - public: - FMT_CONSTEXPR specs_checker(const Handler& handler, detail::type arg_type) - : Handler(handler), checker_(error_handler(), arg_type) {} - - FMT_CONSTEXPR specs_checker(const specs_checker& other) - : Handler(other), checker_(error_handler(), other.arg_type_) {} - - FMT_CONSTEXPR void on_align(align_t align) { - if (align == align::numeric) checker_.require_numeric_argument(); - Handler::on_align(align); - } - - FMT_CONSTEXPR void on_plus() { - checker_.check_sign(); - Handler::on_plus(); - } - - FMT_CONSTEXPR void on_minus() { - checker_.check_sign(); - Handler::on_minus(); - } - - FMT_CONSTEXPR void on_space() { - checker_.check_sign(); - Handler::on_space(); - } - - FMT_CONSTEXPR void on_hash() { - checker_.require_numeric_argument(); - Handler::on_hash(); - } - - FMT_CONSTEXPR void on_localized() { - checker_.require_numeric_argument(); - Handler::on_localized(); - } - - FMT_CONSTEXPR void on_zero() { - checker_.require_numeric_argument(); - Handler::on_zero(); - } - - FMT_CONSTEXPR void end_precision() { checker_.check_precision(); } -}; - template