diff options
Diffstat (limited to 'deps')
-rw-r--r-- | deps/fmt/CMakeLists.txt | 39 | ||||
-rw-r--r-- | deps/fmt/ChangeLog.rst | 700 | ||||
-rw-r--r-- | deps/fmt/README.rst | 10 | ||||
-rw-r--r-- | deps/fmt/include/fmt/chrono.h | 271 | ||||
-rw-r--r-- | deps/fmt/include/fmt/color.h | 8 | ||||
-rw-r--r-- | deps/fmt/include/fmt/compile.h | 45 | ||||
-rw-r--r-- | deps/fmt/include/fmt/core.h | 1490 | ||||
-rw-r--r-- | deps/fmt/include/fmt/format-inl.h | 78 | ||||
-rw-r--r-- | deps/fmt/include/fmt/format.h | 2013 | ||||
-rw-r--r-- | deps/fmt/include/fmt/locale.h | 69 | ||||
-rw-r--r-- | deps/fmt/include/fmt/os.h | 39 | ||||
-rw-r--r-- | deps/fmt/include/fmt/printf.h | 389 | ||||
-rw-r--r-- | deps/fmt/include/fmt/ranges.h | 81 | ||||
-rw-r--r-- | deps/fmt/include/fmt/xchar.h | 236 | ||||
-rw-r--r-- | deps/fmt/src/fmt.cc | 20 | ||||
-rw-r--r-- | deps/fmt/src/format.cc | 39 | ||||
-rw-r--r-- | deps/fmt/src/os.cc | 10 |
17 files changed, 3104 insertions, 2433 deletions
diff --git a/deps/fmt/CMakeLists.txt b/deps/fmt/CMakeLists.txt index 464dfde771..447d8e8a34 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 9c171af050..1988283caf 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 <https://github.com/fmtlib/fmt/issues/2374>`_). + +* Added a missing presentation type check for ``std::string`` + (`#2402 <https://github.com/fmtlib/fmt/issues/2402>`_). + +* Fixed a linkage error when mixing code built with clang and gcc + (`#2377 <https://github.com/fmtlib/fmt/issues/2377>`_). + +* Fixed documentation issues + (`#2396 <https://github.com/fmtlib/fmt/pull/2396>`_, + `#2403 <https://github.com/fmtlib/fmt/issues/2403>`_, + `#2406 <https://github.com/fmtlib/fmt/issues/2406>`_). + Thanks `@mkurdej (Marek Kurdej) <https://github.com/mkurdej>`_. + +* Removed dead code in FP formatter ( + `#2398 <https://github.com/fmtlib/fmt/pull/2398>`_). + Thanks `@javierhonduco (Javier Honduvilla Coto) + <https://github.com/javierhonduco>`_. + +* Fixed various warnings and compilation issues + (`#2351 <https://github.com/fmtlib/fmt/issues/2351>`_, + `#2359 <https://github.com/fmtlib/fmt/issues/2359>`_, + `#2365 <https://github.com/fmtlib/fmt/pull/2365>`_, + `#2368 <https://github.com/fmtlib/fmt/issues/2368>`_, + `#2370 <https://github.com/fmtlib/fmt/pull/2370>`_, + `#2376 <https://github.com/fmtlib/fmt/pull/2376>`_, + `#2381 <https://github.com/fmtlib/fmt/pull/2381>`_, + `#2382 <https://github.com/fmtlib/fmt/pull/2382>`_, + `#2386 <https://github.com/fmtlib/fmt/issues/2386>`_, + `#2389 <https://github.com/fmtlib/fmt/pull/2389>`_, + `#2395 <https://github.com/fmtlib/fmt/pull/2395>`_, + `#2397 <https://github.com/fmtlib/fmt/pull/2397>`_, + `#2400 <https://github.com/fmtlib/fmt/issues/2400>`_ + `#2401 <https://github.com/fmtlib/fmt/issues/2401>`_, + `#2407 <https://github.com/fmtlib/fmt/pull/2407>`_). + Thanks `@zx2c4 (Jason A. Donenfeld) <https://github.com/zx2c4>`_, + `@AidanSun05 (Aidan Sun) <https://github.com/AidanSun05>`_, + `@mattiasljungstrom (Mattias Ljungström) + <https://github.com/mattiasljungstrom>`_, + `@joemmett (Jonathan Emmett) <https://github.com/joemmett>`_, + `@erengy (Eren Okka) <https://github.com/erengy>`_, + `@patlkli (Patrick Geltinger) <https://github.com/patlkli>`_, + `@gsjaardema (Greg Sjaardema) <https://github.com/gsjaardema>`_, + `@phprus (Vladislav Shchapov) <https://github.com/phprus>`_. + +8.0.0 - 2021-06-21 +------------------ + +* Enabled compile-time format string check by default. + For example (`godbolt <https://godbolt.org/z/sMxcohGjz>`__): + + .. code:: c++ + + #include <fmt/core.h> + + 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 <https://github.com/fmtlib/fmt/pull/2019>`_, + `#2044 <https://github.com/fmtlib/fmt/pull/2044>`_, + `#2056 <https://github.com/fmtlib/fmt/pull/2056>`_, + `#2072 <https://github.com/fmtlib/fmt/pull/2072>`_, + `#2075 <https://github.com/fmtlib/fmt/pull/2075>`_, + `#2078 <https://github.com/fmtlib/fmt/issues/2078>`_, + `#2129 <https://github.com/fmtlib/fmt/pull/2129>`_, + `#2326 <https://github.com/fmtlib/fmt/pull/2326>`_). + For example (`godbolt <https://godbolt.org/z/Mxx9d89jM>`__): + + .. code:: c++ + + #include <fmt/compile.h> + + consteval auto compile_time_itoa(int value) -> std::array<char, 10> { + auto result = std::array<char, 10>(); + 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) <https://github.com/alexezeder>`_. + +* 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 <https://github.com/fmtlib/fmt/issues/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 <https://github.com/fmtlib/fmt/pull/2043>`_, + `#2242 <https://github.com/fmtlib/fmt/pull/2242>`_): + + .. code:: c++ + + #include <fmt/compile.h> + + 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) <https://github.com/alexezeder>`_. + +* Format string compilation now requires ``format`` functions of ``formatter`` + specializations for user-defined types to be ``const``: + + .. code:: c++ + + template <> struct fmt::formatter<my_type>: formatter<string_view> { + template <typename FormatContext> + auto format(my_type obj, FormatContext& ctx) const { // Note const here. + // ... + } + }; + +* Added UDL-based named argument support to format string compilation + (`#2243 <https://github.com/fmtlib/fmt/pull/2243>`_, + `#2281 <https://github.com/fmtlib/fmt/pull/2281>`_). For example: + + .. code:: c++ + + #include <fmt/compile.h> + + 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) <https://github.com/alexezeder>`_. + +* Added format string compilation support to ``fmt::print`` + (`#2280 <https://github.com/fmtlib/fmt/issues/2280>`_, + `#2304 <https://github.com/fmtlib/fmt/pull/2304>`_). + Thanks `@alexezeder (Alexey Ochapov) <https://github.com/alexezeder>`_. + +* Added initial support for compiling {fmt} as a C++20 module + (`#2235 <https://github.com/fmtlib/fmt/pull/2235>`_, + `#2240 <https://github.com/fmtlib/fmt/pull/2240>`_, + `#2260 <https://github.com/fmtlib/fmt/pull/2260>`_, + `#2282 <https://github.com/fmtlib/fmt/pull/2282>`_, + `#2283 <https://github.com/fmtlib/fmt/pull/2283>`_, + `#2288 <https://github.com/fmtlib/fmt/pull/2288>`_, + `#2298 <https://github.com/fmtlib/fmt/pull/2298>`_, + `#2306 <https://github.com/fmtlib/fmt/pull/2306>`_, + `#2307 <https://github.com/fmtlib/fmt/pull/2307>`_, + `#2309 <https://github.com/fmtlib/fmt/pull/2309>`_, + `#2318 <https://github.com/fmtlib/fmt/pull/2318>`_, + `#2324 <https://github.com/fmtlib/fmt/pull/2324>`_, + `#2332 <https://github.com/fmtlib/fmt/pull/2332>`_, + `#2340 <https://github.com/fmtlib/fmt/pull/2340>`_). + Thanks `@DanielaE (Daniela Engert) <https://github.com/DanielaE>`_. + +* Made symbols private by default reducing shared library size + (`#2301 <https://github.com/fmtlib/fmt/pull/2301>`_). For example there was + a ~15% reported reduction on one platform. + Thanks `@sergiud (Sergiu Deitsch) <https://github.com/sergiud>`_. + +* Optimized includes making the result of preprocessing ``fmt/format.h`` + ~20% smaller with libstdc++/C++20 and slightly improving build times + (`#1998 <https://github.com/fmtlib/fmt/issues/1998>`_). + +* Added support of ranges with non-const ``begin`` / ``end`` + (`#1953 <https://github.com/fmtlib/fmt/pull/1953>`_). + Thanks `@kitegi (sarah) <https://github.com/kitegi>`_. + +* Added support of ``std::byte`` and other formattable types to ``fmt::join`` + (`#1981 <https://github.com/fmtlib/fmt/issues/1981>`_, + `#2040 <https://github.com/fmtlib/fmt/issues/2040>`_, + `#2050 <https://github.com/fmtlib/fmt/pull/2050>`_, + `#2262 <https://github.com/fmtlib/fmt/issues/2262>`_). For example: + + .. code:: c++ + + #include <fmt/format.h> + #include <cstddef> + #include <vector> + + int main() { + auto bytes = std::vector{std::byte(4), std::byte(2)}; + fmt::print("{}", fmt::join(bytes, "")); + } + + prints "42". + + Thanks `@kamibo (Camille Bordignon) <https://github.com/kamibo>`_. + +* Implemented the default format for ``std::chrono::system_clock`` + (`#2319 <https://github.com/fmtlib/fmt/issues/2319>`_, + `#2345 <https://github.com/fmtlib/fmt/pull/2345>`_). For example: + + .. code:: c++ + + #include <fmt/chrono.h> + + 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 <https://github.com/sunmy2019>`_. + +* Made more chrono specifiers locale independent by default. Use the ``'L'`` + specifier to get localized formatting. For example: + + .. code:: c++ + + #include <fmt/chrono.h> + + 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 <https://github.com/fmtlib/fmt/issues/2337>`_, + `#2349 <https://github.com/fmtlib/fmt/pull/2349>`_, + `#2350 <https://github.com/fmtlib/fmt/pull/2350>`_). + Thanks `@phprus (Vladislav Shchapov) <https://github.com/phprus>`_. + +* 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 ``<locale>`` so there is virtually no + compile time effect. + +* Made parameter order in ``vformat_to`` consistent with ``format_to`` + (`#2327 <https://github.com/fmtlib/fmt/issues/2327>`_). + +* Added support for time points with arbitrary durations + (`#2208 <https://github.com/fmtlib/fmt/issues/2208>`_). For example: + + .. code:: c++ + + #include <fmt/chrono.h> + + 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 <fmt/core.h> + + 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 <fmt/core.h> + #include <locale> + + 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 <https://github.com/fmtlib/fmt/issues/2305>`_, + `#2310 <https://github.com/fmtlib/fmt/pull/2310>`_). + Thanks `@Liedtke (Matthias Liedtke) <https://github.com/Liedtke>`_. + +* Made the hexfloat formatting use the right alignment by default + (`#2308 <https://github.com/fmtlib/fmt/issues/2308>`_, + `#2317 <https://github.com/fmtlib/fmt/pull/2317>`_). + Thanks `@Liedtke (Matthias Liedtke) <https://github.com/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 <https://github.com/fmtlib/fmt/issues/2133>`_). + +* Added support for the ``'s'`` format specifier that gives textual + representation of ``bool`` + (`#2094 <https://github.com/fmtlib/fmt/issues/2094>`_, + `#2109 <https://github.com/fmtlib/fmt/pull/2109>`_). For example: + + .. code:: c++ + + #include <fmt/core.h> + + int main() { + fmt::print("{:s}", true); + } + + prints "true". + Thanks `@powercoderlol (Ivan Polyakov) <https://github.com/powercoderlol>`_. + +* Made ``fmt::ptr`` work with function pointers + (`#2131 <https://github.com/fmtlib/fmt/pull/2131>`_). For example: + + .. code:: c++ + + #include <fmt/format.h> + + int main() { + fmt::print("My main: {}\n", fmt::ptr(main)); + } + + Thanks `@mikecrowe (Mike Crowe) <https://github.com/mikecrowe>`_. + +* Fixed ``fmt::formatted_size`` with format string compilation + (`#2141 <https://github.com/fmtlib/fmt/pull/2141>`_, + `#2161 <https://github.com/fmtlib/fmt/pull/2161>`_). + Thanks `@alexezeder (Alexey Ochapov) <https://github.com/alexezeder>`_. + +* Fixed handling of empty format strings during format string compilation + (`#2042 <https://github.com/fmtlib/fmt/issues/2042>`_): + + .. code:: c++ + + auto s = fmt::format(FMT_COMPILE("")); + + Thanks `@alexezeder (Alexey Ochapov) <https://github.com/alexezeder>`_. + +* Fixed handling of enums in ``fmt::to_string`` + (`#2036 <https://github.com/fmtlib/fmt/issues/2036>`_). + +* Improved width computation + (`#2033 <https://github.com/fmtlib/fmt/issues/2033>`_, + `#2091 <https://github.com/fmtlib/fmt/issues/2091>`_). For example: + + .. code:: c++ + + #include <fmt/core.h> + + 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 <https://github.com/fmtlib/fmt/issues/2018>`_). For example: + + .. code:: c++ + + #include <fmt/os.h> + + 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 <https://github.com/fmtlib/fmt/issues/2197>`_, + `#2198 <https://github.com/fmtlib/fmt/pull/2198>`_). + Thanks `@vtta <https://github.com/vtta>`_. + +* Replaced the ``fmt::system_error`` exception with a function of the same + name that constructs ``std::system_error`` + (`#2266 <https://github.com/fmtlib/fmt/issues/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 <https://github.com/fmtlib/fmt/issues/2274>`_, + `#2275 <https://github.com/fmtlib/fmt/pull/2275>`_). + The latter is similar to ``std::sytem_category`` but correctly handles UTF-8. + Thanks `@phprus (Vladislav Shchapov) <https://github.com/phprus>`_. + +* Replaced ``fmt::error_code`` with ``std::error_code`` and made it formattable + (`#2269 <https://github.com/fmtlib/fmt/issues/2269>`_, + `#2270 <https://github.com/fmtlib/fmt/pull/2270>`_, + `#2273 <https://github.com/fmtlib/fmt/pull/2273>`_). + Thanks `@phprus (Vladislav Shchapov) <https://github.com/phprus>`_. + +* Added speech synthesis support + (`#2206 <https://github.com/fmtlib/fmt/pull/2206>`_). + +* Made ``format_to`` work with a memory buffer that has a custom allocator + (`#2300 <https://github.com/fmtlib/fmt/pull/2300>`_). + Thanks `@voxmea <https://github.com/voxmea>`_. + +* Added ``Allocator::max_size`` support to ``basic_memory_buffer``. + (`#1960 <https://github.com/fmtlib/fmt/pull/1960>`_). + Thanks `@phprus (Vladislav Shchapov) <https://github.com/phprus>`_. + +* Added wide string support to ``fmt::join`` + (`#2236 <https://github.com/fmtlib/fmt/pull/2236>`_). + Thanks `@crbrz <https://github.com/crbrz>`_. + +* Made iterators passed to ``formatter`` specializations via a format context + satisfy C++20 ``std::output_iterator`` requirements + (`#2156 <https://github.com/fmtlib/fmt/issues/2156>`_, + `#2158 <https://github.com/fmtlib/fmt/pull/2158>`_, + `#2195 <https://github.com/fmtlib/fmt/issues/2195>`_, + `#2204 <https://github.com/fmtlib/fmt/pull/2204>`_). + Thanks `@randomnetcat (Jason Cobb) <https://github.com/randomnetcat>`_. + +* Optimized the ``printf`` implementation + (`#1982 <https://github.com/fmtlib/fmt/pull/1982>`_, + `#1984 <https://github.com/fmtlib/fmt/pull/1984>`_, + `#2016 <https://github.com/fmtlib/fmt/pull/2016>`_, + `#2164 <https://github.com/fmtlib/fmt/pull/2164>`_). + Thanks `@rimathia <https://github.com/rimathia>`_ and + `@moiwi <https://github.com/moiwi>`_. + +* Improved detection of ``constexpr`` ``char_traits`` + (`#2246 <https://github.com/fmtlib/fmt/pull/2246>`_, + `#2257 <https://github.com/fmtlib/fmt/pull/2257>`_). + Thanks `@phprus (Vladislav Shchapov) <https://github.com/phprus>`_. + +* Fixed writing to ``stdout`` when it is redirected to ``NUL`` on Windows + (`#2080 <https://github.com/fmtlib/fmt/issues/2080>`_). + +* Fixed exception propagation from iterators + (`#2097 <https://github.com/fmtlib/fmt/issues/2097>`_). + +* Improved ``strftime`` error handling + (`#2238 <https://github.com/fmtlib/fmt/issues/2238>`_, + `#2244 <https://github.com/fmtlib/fmt/pull/2244>`_). + Thanks `@yumeyao <https://github.com/yumeyao>`_. + +* Stopped using deprecated GCC UDL template extension. + +* Added ``fmt/args.h`` to the install target + (`#2096 <https://github.com/fmtlib/fmt/issues/2096>`_). + +* Error messages are now passed to assert when exceptions are disabled + (`#2145 <https://github.com/fmtlib/fmt/pull/2145>`_). + Thanks `@NobodyXu (Jiahao XU) <https://github.com/NobodyXu>`_. + +* Added the ``FMT_MASTER_PROJECT`` CMake option to control build and install + targets when {fmt} is included via ``add_subdirectory`` + (`#2098 <https://github.com/fmtlib/fmt/issues/2098>`_, + `#2100 <https://github.com/fmtlib/fmt/pull/2100>`_). + Thanks `@randomizedthinking <https://github.com/randomizedthinking>`_. + +* Improved build configuration + (`#2026 <https://github.com/fmtlib/fmt/pull/2026>`_, + `#2122 <https://github.com/fmtlib/fmt/pull/2122>`_). + Thanks `@luncliff (Park DongHa) <https://github.com/luncliff>`_ and + `@ibaned (Dan Ibanez) <https://github.com/ibaned>`_. + +* Fixed various warnings and compilation issues + (`#1947 <https://github.com/fmtlib/fmt/issues/1947>`_, + `#1959 <https://github.com/fmtlib/fmt/pull/1959>`_, + `#1963 <https://github.com/fmtlib/fmt/pull/1963>`_, + `#1965 <https://github.com/fmtlib/fmt/pull/1965>`_, + `#1966 <https://github.com/fmtlib/fmt/issues/1966>`_, + `#1974 <https://github.com/fmtlib/fmt/pull/1974>`_, + `#1975 <https://github.com/fmtlib/fmt/pull/1975>`_, + `#1990 <https://github.com/fmtlib/fmt/pull/1990>`_, + `#2000 <https://github.com/fmtlib/fmt/issues/2000>`_, + `#2001 <https://github.com/fmtlib/fmt/pull/2001>`_, + `#2002 <https://github.com/fmtlib/fmt/issues/2002>`_, + `#2004 <https://github.com/fmtlib/fmt/issues/2004>`_, + `#2006 <https://github.com/fmtlib/fmt/pull/2006>`_, + `#2009 <https://github.com/fmtlib/fmt/pull/2009>`_, + `#2010 <https://github.com/fmtlib/fmt/pull/2010>`_, + `#2038 <https://github.com/fmtlib/fmt/issues/2038>`_, + `#2039 <https://github.com/fmtlib/fmt/issues/2039>`_, + `#2047 <https://github.com/fmtlib/fmt/issues/2047>`_, + `#2053 <https://github.com/fmtlib/fmt/pull/2053>`_, + `#2059 <https://github.com/fmtlib/fmt/issues/2059>`_, + `#2065 <https://github.com/fmtlib/fmt/pull/2065>`_, + `#2067 <https://github.com/fmtlib/fmt/pull/2067>`_, + `#2068 <https://github.com/fmtlib/fmt/pull/2068>`_, + `#2073 <https://github.com/fmtlib/fmt/pull/2073>`_, + `#2103 <https://github.com/fmtlib/fmt/issues/2103>`_ + `#2105 <https://github.com/fmtlib/fmt/issues/2105>`_ + `#2106 <https://github.com/fmtlib/fmt/pull/2106>`_, + `#2107 <https://github.com/fmtlib/fmt/pull/2107>`_, + `#2116 <https://github.com/fmtlib/fmt/issues/2116>`_ + `#2117 <https://github.com/fmtlib/fmt/pull/2117>`_, + `#2118 <https://github.com/fmtlib/fmt/issues/2118>`_ + `#2119 <https://github.com/fmtlib/fmt/pull/2119>`_, + `#2127 <https://github.com/fmtlib/fmt/issues/2127>`_, + `#2128 <https://github.com/fmtlib/fmt/pull/2128>`_, + `#2140 <https://github.com/fmtlib/fmt/issues/2140>`_, + `#2142 <https://github.com/fmtlib/fmt/issues/2142>`_, + `#2143 <https://github.com/fmtlib/fmt/pull/2143>`_, + `#2144 <https://github.com/fmtlib/fmt/pull/2144>`_, + `#2147 <https://github.com/fmtlib/fmt/issues/2147>`_, + `#2148 <https://github.com/fmtlib/fmt/issues/2148>`_, + `#2149 <https://github.com/fmtlib/fmt/issues/2149>`_, + `#2152 <https://github.com/fmtlib/fmt/pull/2152>`_, + `#2160 <https://github.com/fmtlib/fmt/pull/2160>`_, + `#2170 <https://github.com/fmtlib/fmt/issues/2170>`_, + `#2175 <https://github.com/fmtlib/fmt/issues/2175>`_, + `#2176 <https://github.com/fmtlib/fmt/issues/2176>`_, + `#2177 <https://github.com/fmtlib/fmt/pull/2177>`_, + `#2178 <https://github.com/fmtlib/fmt/issues/2178>`_, + `#2179 <https://github.com/fmtlib/fmt/pull/2179>`_, + `#2180 <https://github.com/fmtlib/fmt/issues/2180>`_, + `#2181 <https://github.com/fmtlib/fmt/issues/2181>`_, + `#2183 <https://github.com/fmtlib/fmt/pull/2183>`_, + `#2184 <https://github.com/fmtlib/fmt/issues/2184>`_, + `#2185 <https://github.com/fmtlib/fmt/issues/2185>`_, + `#2186 <https://github.com/fmtlib/fmt/pull/2186>`_, + `#2187 <https://github.com/fmtlib/fmt/pull/2187>`_, + `#2190 <https://github.com/fmtlib/fmt/pull/2190>`_, + `#2192 <https://github.com/fmtlib/fmt/pull/2192>`_, + `#2194 <https://github.com/fmtlib/fmt/pull/2194>`_, + `#2205 <https://github.com/fmtlib/fmt/pull/2205>`_, + `#2210 <https://github.com/fmtlib/fmt/issues/2210>`_, + `#2211 <https://github.com/fmtlib/fmt/pull/2211>`_, + `#2215 <https://github.com/fmtlib/fmt/pull/2215>`_, + `#2216 <https://github.com/fmtlib/fmt/pull/2216>`_, + `#2218 <https://github.com/fmtlib/fmt/pull/2218>`_, + `#2220 <https://github.com/fmtlib/fmt/pull/2220>`_, + `#2228 <https://github.com/fmtlib/fmt/issues/2228>`_, + `#2229 <https://github.com/fmtlib/fmt/pull/2229>`_, + `#2230 <https://github.com/fmtlib/fmt/pull/2230>`_, + `#2233 <https://github.com/fmtlib/fmt/issues/2233>`_, + `#2239 <https://github.com/fmtlib/fmt/pull/2239>`_, + `#2248 <https://github.com/fmtlib/fmt/issues/2248>`_, + `#2252 <https://github.com/fmtlib/fmt/issues/2252>`_, + `#2253 <https://github.com/fmtlib/fmt/pull/2253>`_, + `#2255 <https://github.com/fmtlib/fmt/pull/2255>`_, + `#2261 <https://github.com/fmtlib/fmt/issues/2261>`_, + `#2278 <https://github.com/fmtlib/fmt/issues/2278>`_, + `#2284 <https://github.com/fmtlib/fmt/issues/2284>`_, + `#2287 <https://github.com/fmtlib/fmt/pull/2287>`_, + `#2289 <https://github.com/fmtlib/fmt/pull/2289>`_, + `#2290 <https://github.com/fmtlib/fmt/pull/2290>`_, + `#2293 <https://github.com/fmtlib/fmt/pull/2293>`_, + `#2295 <https://github.com/fmtlib/fmt/issues/2295>`_, + `#2296 <https://github.com/fmtlib/fmt/pull/2296>`_, + `#2297 <https://github.com/fmtlib/fmt/pull/2297>`_, + `#2311 <https://github.com/fmtlib/fmt/issues/2311>`_, + `#2313 <https://github.com/fmtlib/fmt/pull/2313>`_, + `#2315 <https://github.com/fmtlib/fmt/pull/2315>`_, + `#2320 <https://github.com/fmtlib/fmt/issues/2320>`_, + `#2321 <https://github.com/fmtlib/fmt/pull/2321>`_, + `#2323 <https://github.com/fmtlib/fmt/pull/2323>`_, + `#2328 <https://github.com/fmtlib/fmt/issues/2328>`_, + `#2329 <https://github.com/fmtlib/fmt/pull/2329>`_, + `#2333 <https://github.com/fmtlib/fmt/pull/2333>`_, + `#2338 <https://github.com/fmtlib/fmt/pull/2338>`_, + `#2341 <https://github.com/fmtlib/fmt/pull/2341>`_). + Thanks `@darklukee <https://github.com/darklukee>`_, + `@fagg (Ashton Fagg) <https://github.com/fagg>`_, + `@killerbot242 (Lieven de Cock) <https://github.com/killerbot242>`_, + `@jgopel (Jonathan Gopel) <https://github.com/jgopel>`_, + `@yeswalrus (Walter Gray) <https://github.com/yeswalrus>`_, + `@Finkman <https://github.com/Finkman>`_, + `@HazardyKnusperkeks (Björn Schäpers) <https://github.com/HazardyKnusperkeks>`_, + `@dkavolis (Daumantas Kavolis) <https://github.com/dkavolis>`_ + `@concatime (Issam Maghni) <https://github.com/concatime>`_, + `@chronoxor (Ivan Shynkarenka) <https://github.com/chronoxor>`_, + `@summivox (Yin Zhong) <https://github.com/summivox>`_, + `@yNeo <https://github.com/yNeo>`_, + `@Apache-HB (Elliot) <https://github.com/Apache-HB>`_, + `@alexezeder (Alexey Ochapov) <https://github.com/alexezeder>`_, + `@toojays (John Steele Scott) <https://github.com/toojays>`_, + `@Brainy0207 <https://github.com/Brainy0207>`_, + `@vadz (VZ) <https://github.com/vadz>`_, + `@imsherlock (Ryan Sherlock) <https://github.com/imsherlock>`_, + `@phprus (Vladislav Shchapov) <https://github.com/phprus>`_, + `@white238 (Chris White) <https://github.com/white238>`_, + `@yafshar (Yaser Afshar) <https://github.com/yafshar>`_, + `@BillyDonahue (Billy Donahue) <https://github.com/BillyDonahue>`_, + `@jstaahl <https://github.com/jstaahl>`_, + `@denchat <https://github.com/denchat>`_, + `@DanielaE (Daniela Engert) <https://github.com/DanielaE>`_, + `@ilyakurdyukov (Ilya Kurdyukov) <https://github.com/ilyakurdyukov>`_, + `@ilmai <https://github.com/ilmai>`_, + `@JessyDL (Jessy De Lannoit) <https://github.com/JessyDL>`_, + `@sergiud (Sergiu Deitsch) <https://github.com/sergiud>`_, + `@mwinterb <https://github.com/mwinterb>`_, + `@sven-herrmann <https://github.com/sven-herrmann>`_, + `@jmelas (John Melas) <https://github.com/jmelas>`_, + `@twoixter (Jose Miguel Pérez) <https://github.com/twoixter>`_, + `@crbrz <https://github.com/crbrz>`_, + `@upsj (Tobias Ribizel) <https://github.com/upsj>`_. + +* Improved documentation + (`#1986 <https://github.com/fmtlib/fmt/issues/1986>`_, + `#2051 <https://github.com/fmtlib/fmt/pull/2051>`_, + `#2057 <https://github.com/fmtlib/fmt/issues/2057>`_, + `#2081 <https://github.com/fmtlib/fmt/pull/2081>`_, + `#2084 <https://github.com/fmtlib/fmt/issues/2084>`_, + `#2312 <https://github.com/fmtlib/fmt/pull/2312>`_). + Thanks `@imba-tjd (谭九鼎) <https://github.com/imba-tjd>`_, + `@0x416c69 (AlιAѕѕaѕѕιN) <https://github.com/0x416c69>`_, + `@mordante <https://github.com/mordante>`_. + +* Continuous integration and test improvements + (`#1969 <https://github.com/fmtlib/fmt/issues/1969>`_, + `#1991 <https://github.com/fmtlib/fmt/pull/1991>`_, + `#2020 <https://github.com/fmtlib/fmt/pull/2020>`_, + `#2110 <https://github.com/fmtlib/fmt/pull/2110>`_, + `#2114 <https://github.com/fmtlib/fmt/pull/2114>`_, + `#2196 <https://github.com/fmtlib/fmt/issues/2196>`_, + `#2217 <https://github.com/fmtlib/fmt/pull/2217>`_, + `#2247 <https://github.com/fmtlib/fmt/pull/2247>`_, + `#2256 <https://github.com/fmtlib/fmt/pull/2256>`_, + `#2336 <https://github.com/fmtlib/fmt/pull/2336>`_, + `#2346 <https://github.com/fmtlib/fmt/pull/2346>`_). + Thanks `@jgopel (Jonathan Gopel) <https://github.com/jgopel>`_, + `@alexezeder (Alexey Ochapov) <https://github.com/alexezeder>`_ and + `@DanielaE (Daniela Engert) <https://github.com/DanielaE>`_. + 7.1.3 - 2020-11-24 ------------------ @@ -269,8 +965,8 @@ Thanks `@Naios (Denis Blank) <https://github.com/Naios>`_. -* Made the ``#`` specifier emit trailing zeros in addition to the decimal point - (`#1797 <https://github.com/fmtlib/fmt/issues/1797>`_). For example +* Made the ``'#'`` specifier emit trailing zeros in addition to the decimal + point (`#1797 <https://github.com/fmtlib/fmt/issues/1797>`_). For example (`godbolt <https://godbolt.org/z/bhdcW9>`__): .. code:: c++ diff --git a/deps/fmt/README.rst b/deps/fmt/README.rst index 7cf794e4ac..02c849c78f 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 <https://fmt.dev>`__ @@ -341,6 +341,10 @@ Projects using this library * `Folly <https://github.com/facebook/folly>`_: Facebook open-source library +* `Grand Mountain Adventure + <https://store.steampowered.com/app/1247360/Grand_Mountain_Adventure/>`_: + A beautiful open-world ski & snowboarding game + * `HarpyWar/pvpgn <https://github.com/pvpgn/pvpgn-server>`_: Player vs Player Gaming Network with tweaks diff --git a/deps/fmt/include/fmt/chrono.h b/deps/fmt/include/fmt/chrono.h index e26e519039..c024fd710c 100644 --- a/deps/fmt/include/fmt/chrono.h +++ b/deps/fmt/include/fmt/chrono.h @@ -15,7 +15,6 @@ #include <sstream> #include "format.h" -#include "locale.h" FMT_BEGIN_NAMESPACE @@ -283,10 +282,80 @@ To safe_duration_cast(std::chrono::duration<FromRep, FromPeriod> from, #define FMT_NOMACRO namespace detail { +template <typename T = void> 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<char>; + const auto& facet = std::use_facet<std::time_put<char, iterator>>(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<std::codecvt<code_unit, char, std::mbstate_t>>(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<uint32_t>(*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<uint32_t>(*p) - 0x35fdc00; + } + if (c < 0x80) { + str.push_back(static_cast<char>(c)); + } else if (c < 0x800) { + str.push_back(static_cast<char>(0xc0 | (c >> 6))); + str.push_back(static_cast<char>(0x80 | (c & 0x3f))); + } else if ((c >= 0x800 && c <= 0xd7ff) || (c >= 0xe000 && c <= 0xffff)) { + str.push_back(static_cast<char>(0xe0 | (c >> 12))); + str.push_back(static_cast<char>(0x80 | ((c & 0xfff) >> 6))); + str.push_back(static_cast<char>(0x80 | (c & 0x3f))); + } else if (c >= 0x10000 && c <= 0x10ffff) { + str.push_back(static_cast<char>(0xf0 | (c >> 18))); + str.push_back(static_cast<char>(0x80 | ((c & 0x3ffff) >> 12))); + str.push_back(static_cast<char>(0x80 | ((c & 0xfff) >> 6))); + str.push_back(static_cast<char>(0x80 | (c & 0x3f))); + } else { + FMT_THROW(format_error("failed to format time")); + } + } + return str; +} + +template <typename OutputIt> +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 <typename Char, typename Duration> struct formatter<std::chrono::time_point<std::chrono::system_clock, Duration>, Char> : formatter<std::tm, Char> { + FMT_CONSTEXPR formatter() { + this->specs = {default_specs, sizeof(default_specs) / sizeof(Char)}; + } + + template <typename ParseContext> + 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 <typename FormatContext> auto format(std::chrono::time_point<std::chrono::system_clock> val, FormatContext& ctx) -> decltype(ctx.out()) { std::tm time = localtime(val); return formatter<std::tm, Char>::format(time, ctx); } + + static constexpr Char default_specs[] = {'%', 'Y', '-', '%', 'm', '-', + '%', 'd', ' ', '%', 'H', ':', + '%', 'M', ':', '%', 'S'}; }; +template <typename Char, typename Duration> +constexpr Char + formatter<std::chrono::time_point<std::chrono::system_clock, Duration>, + Char>::default_specs[]; + template <typename Char> struct formatter<std::tm, Char> { template <typename ParseContext> FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) { @@ -458,66 +550,28 @@ template <typename Char> struct formatter<std::tm, Char> { FMT_BEGIN_DETAIL_NAMESPACE -template <typename Period> FMT_CONSTEXPR const char* get_units() { +template <typename Period> FMT_CONSTEXPR inline const char* get_units() { + if (std::is_same<Period, std::atto>::value) return "as"; + if (std::is_same<Period, std::femto>::value) return "fs"; + if (std::is_same<Period, std::pico>::value) return "ps"; + if (std::is_same<Period, std::nano>::value) return "ns"; + if (std::is_same<Period, std::micro>::value) return "µs"; + if (std::is_same<Period, std::milli>::value) return "ms"; + if (std::is_same<Period, std::centi>::value) return "cs"; + if (std::is_same<Period, std::deci>::value) return "ds"; + if (std::is_same<Period, std::ratio<1>>::value) return "s"; + if (std::is_same<Period, std::deca>::value) return "das"; + if (std::is_same<Period, std::hecto>::value) return "hs"; + if (std::is_same<Period, std::kilo>::value) return "ks"; + if (std::is_same<Period, std::mega>::value) return "Ms"; + if (std::is_same<Period, std::giga>::value) return "Gs"; + if (std::is_same<Period, std::tera>::value) return "Ts"; + if (std::is_same<Period, std::peta>::value) return "Ps"; + if (std::is_same<Period, std::exa>::value) return "Es"; + if (std::is_same<Period, std::ratio<60>>::value) return "m"; + if (std::is_same<Period, std::ratio<3600>>::value) return "h"; return nullptr; } -template <> FMT_CONSTEXPR inline const char* get_units<std::atto>() { - return "as"; -} -template <> FMT_CONSTEXPR inline const char* get_units<std::femto>() { - return "fs"; -} -template <> FMT_CONSTEXPR inline const char* get_units<std::pico>() { - return "ps"; -} -template <> FMT_CONSTEXPR inline const char* get_units<std::nano>() { - return "ns"; -} -template <> FMT_CONSTEXPR inline const char* get_units<std::micro>() { - return "µs"; -} -template <> FMT_CONSTEXPR inline const char* get_units<std::milli>() { - return "ms"; -} -template <> FMT_CONSTEXPR inline const char* get_units<std::centi>() { - return "cs"; -} -template <> FMT_CONSTEXPR inline const char* get_units<std::deci>() { - return "ds"; -} -template <> FMT_CONSTEXPR inline const char* get_units<std::ratio<1>>() { - return "s"; -} -template <> FMT_CONSTEXPR inline const char* get_units<std::deca>() { - return "das"; -} -template <> FMT_CONSTEXPR inline const char* get_units<std::hecto>() { - return "hs"; -} -template <> FMT_CONSTEXPR inline const char* get_units<std::kilo>() { - return "ks"; -} -template <> FMT_CONSTEXPR inline const char* get_units<std::mega>() { - return "Ms"; -} -template <> FMT_CONSTEXPR inline const char* get_units<std::giga>() { - return "Gs"; -} -template <> FMT_CONSTEXPR inline const char* get_units<std::tera>() { - return "Ts"; -} -template <> FMT_CONSTEXPR inline const char* get_units<std::peta>() { - return "Ps"; -} -template <> FMT_CONSTEXPR inline const char* get_units<std::exa>() { - return "Es"; -} -template <> FMT_CONSTEXPR inline const char* get_units<std::ratio<60>>() { - return "m"; -} -template <> FMT_CONSTEXPR inline const char* get_units<std::ratio<3600>>() { - 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 <typename Derived> struct null_chrono_spec_handler { + FMT_CONSTEXPR void unsupported() { + static_cast<Derived*>(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<chrono_format_checker> { + FMT_NORETURN void unsupported() { FMT_THROW(format_error("no date")); } template <typename Char> 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 <typename T, FMT_ENABLE_IF(std::is_integral<T>::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>() - : std::locale::classic(); - auto& facet = std::use_facet<std::time_put<char_type>>(locale); - std::basic_ostringstream<char_type> 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>() + : 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<unsigned char>(wd != 7 ? wd : 0)) {} + constexpr unsigned c_encoding() const noexcept { return value; } +}; +#endif + +// A rudimentary weekday formatter. +template <> struct formatter<weekday> { + 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<int>(wd.c_encoding()); + const auto& loc = localized ? ctx.locale().template get<std::locale>() + : std::locale::classic(); + return detail::write(ctx.out(), time, loc, 'a'); + } +}; + template <typename Rep, typename Period, typename Char> struct formatter<std::chrono::duration<Rep, Period>, Char> { private: @@ -1190,7 +1295,7 @@ struct formatter<std::chrono::duration<Rep, Period>, 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<Char>(buf.data(), buf.size()), specs_copy); diff --git a/deps/fmt/include/fmt/color.h b/deps/fmt/include/fmt/color.h index 8cddbfe192..3d5490e87f 100644 --- a/deps/fmt/include/fmt/color.h +++ b/deps/fmt/include/fmt/color.h @@ -507,7 +507,7 @@ void vformat_to(buffer<Char>& buf, const text_style& ts, auto background = detail::make_background_color<Char>(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<Char>(buf); } @@ -582,8 +582,8 @@ inline std::basic_string<Char> vformat( template <typename S, typename... Args, typename Char = char_t<S>> inline std::basic_string<Char> format(const text_style& ts, const S& format_str, const Args&... args) { - return vformat(ts, to_string_view(format_str), - fmt::make_args_checked<Args...>(format_str, args...)); + return fmt::vformat(ts, to_string_view(format_str), + fmt::make_args_checked<Args...>(format_str, args...)); } /** @@ -594,7 +594,7 @@ template <typename OutputIt, typename Char, OutputIt vformat_to( OutputIt out, const text_style& ts, basic_string_view<Char> format_str, basic_format_args<buffer_context<type_identity_t<Char>>> args) { - decltype(detail::get_buffer<Char>(out)) buf(detail::get_buffer_init(out)); + auto&& buf = detail::get_buffer<Char>(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 128004bc5a..00000c92e3 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<compiled_string, S> {}; \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 <typename Char, size_t N, fixed_string<Char, N> Str> +template <typename Char, size_t N, + fmt::detail_exported::fixed_string<Char, N> Str> struct udl_compiled_string : compiled_string { using char_type = Char; constexpr operator basic_string_view<char_type>() const { @@ -382,24 +384,22 @@ constexpr parse_specs_result<T, Char> parse_specs(basic_string_view<Char> str, } template <typename Char> struct arg_id_handler { - constexpr void on_error(const char* message) { throw format_error(message); } + arg_ref<Char> 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<Char>(id); return 0; } - - constexpr int on_arg_id(basic_string_view<Char> id) { + constexpr int operator()(basic_string_view<Char> id) { arg_id = arg_ref<Char>(id); return 0; } - arg_ref<Char> arg_id; + constexpr void on_error(const char* message) { throw format_error(message); } }; template <typename Char> struct parse_arg_id_result { @@ -410,8 +410,7 @@ template <typename Char> struct parse_arg_id_result { template <int ID, typename Char> constexpr auto parse_arg_id(const Char* begin, const Char* end) { auto handler = arg_id_handler<Char>{arg_ref<Char>{}}; - auto adapter = id_adapter<arg_id_handler<Char>, 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<Char>{handler.arg_id, arg_id_end}; } @@ -428,7 +427,7 @@ template <typename T, typename Args, size_t END_POS, int ARG_INDEX, int NEXT_ID, typename S> constexpr auto parse_replacement_field_then_tail(S format_str) { using char_type = typename S::char_type; - constexpr basic_string_view<char_type> str = format_str; + constexpr auto str = basic_string_view<char_type>(format_str); constexpr char_type c = END_POS != str.size() ? str[END_POS] : char_type(); if constexpr (c == '}') { return parse_tail<Args, END_POS + 1, NEXT_ID>( @@ -449,7 +448,7 @@ constexpr auto parse_replacement_field_then_tail(S format_str) { template <typename Args, size_t POS, int ID, typename S> constexpr auto compile_format_string(S format_str) { using char_type = typename S::char_type; - constexpr basic_string_view<char_type> str = format_str; + constexpr auto str = basic_string_view<char_type>(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 <typename... Args, typename S, FMT_ENABLE_IF(detail::is_compiled_string<S>::value)> constexpr auto compile(S format_str) { - constexpr basic_string_view<typename S::char_type> str = format_str; + constexpr auto str = basic_string_view<typename S::char_type>(format_str); if constexpr (str.size() == 0) { return detail::make_text(str, 0, 0); } else { @@ -557,7 +556,7 @@ template <typename S, typename... Args, FMT_INLINE std::basic_string<typename S::char_type> format(const S&, Args&&... args) { if constexpr (std::is_same<typename S::char_type, char>::value) { - constexpr basic_string_view<typename S::char_type> str = S(); + constexpr auto str = basic_string_view<typename S::char_type>(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 <typename S, typename... Args, + FMT_ENABLE_IF(detail::is_compiled_string<S>::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 <typename S, typename... Args, + FMT_ENABLE_IF(detail::is_compiled_string<S>::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 <detail::fixed_string Str> +template <detail_exported::fixed_string Str> constexpr detail::udl_compiled_string< remove_cvref_t<decltype(Str.data[0])>, 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 293d824250..d058398ac9 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 <climits> // INT_MAX -#include <cstdio> // std::FILE +#include <cstdio> // std::FILE #include <cstring> #include <iterator> +#include <limits> #include <string> #include <type_traits> // The fmt library version in the form major * 10000 + minor * 100 + patch. -#define FMT_VERSION 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 <bool B, class T = void> +template <bool B, typename T = void> using enable_if_t = typename std::enable_if<B, T>::type; -template <bool B, class T, class F> +template <bool B, typename T, typename F> using conditional_t = typename std::conditional<B, T, F>::type; template <bool B> using bool_constant = std::integral_constant<bool, B>; template <typename T> using remove_reference_t = typename std::remove_reference<T>::type; template <typename T> -using remove_const_t = typename std::remove_const<T>::type; -template <typename T> using remove_cvref_t = typename std::remove_cv<remove_reference_t<T>>::type; template <typename T> struct type_identity { using type = T; }; template <typename T> using type_identity_t = typename type_identity<T>::type; -struct monostate {}; +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 <typename... T> 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 <typename T> constexpr T const_check(T value) { return value; } +// A function to suppress "conditional expression is constant" warnings. +template <typename T> 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 <typename T> struct std_string_view {}; # define FMT_USE_INT128 1 using int128_t = __int128_t; using uint128_t = __uint128_t; +template <typename T> inline auto convert_for_visit(T value) -> T { + return value; +} #else # define FMT_USE_INT128 0 #endif #if !FMT_USE_INT128 -struct int128_t {}; -struct uint128_t {}; +enum class int128_t {}; +enum class uint128_t {}; +// Reduce template instantiations. +template <typename T> inline auto convert_for_visit(T) -> monostate { + return {}; +} #endif // Casts a nonnegative integer to unsigned. template <typename Int> -FMT_CONSTEXPR typename std::make_unsigned<Int>::type to_unsigned(Int value) { +FMT_CONSTEXPR auto to_unsigned(Int value) -> + typename std::make_unsigned<Int>::type { FMT_ASSERT(value >= 0, "negative value"); return static_cast<typename std::make_unsigned<Int>::type>(value); } FMT_MSC_WARNING(suppress : 4566) constexpr unsigned char micro[] = "\u00B5"; -template <typename Char> 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 <typename Char> 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 <typename Char> 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<Char>::compare(data_, other.data_, str_size); if (result == 0) @@ -478,70 +504,53 @@ template <typename Char> 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<char>; -using wstring_view = basic_string_view<wchar_t>; /** Specifies if ``T`` is a character type. Can be specialized by users. */ template <typename T> struct is_char : std::false_type {}; template <> struct is_char<char> : std::true_type {}; -template <> struct is_char<wchar_t> : 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 <typename Char, FMT_ENABLE_IF(is_char<Char>::value)> -FMT_INLINE basic_string_view<Char> to_string_view(const Char* s) { +FMT_INLINE auto to_string_view(const Char* s) -> basic_string_view<Char> { return s; } - template <typename Char, typename Traits, typename Alloc> -inline basic_string_view<Char> to_string_view( - const std::basic_string<Char, Traits, Alloc>& s) { +inline auto to_string_view(const std::basic_string<Char, Traits, Alloc>& s) + -> basic_string_view<Char> { return s; } - template <typename Char> -constexpr basic_string_view<Char> to_string_view(basic_string_view<Char> s) { +constexpr auto to_string_view(basic_string_view<Char> s) + -> basic_string_view<Char> { return s; } - template <typename Char, FMT_ENABLE_IF(!std::is_empty<detail::std_string_view<Char>>::value)> -inline basic_string_view<Char> to_string_view(detail::std_string_view<Char> s) { +inline auto to_string_view(detail::std_string_view<Char> s) + -> basic_string_view<Char> { return s; } @@ -553,14 +562,15 @@ template <typename S> struct is_compile_string : std::is_base_of<compile_string, S> {}; template <typename S, FMT_ENABLE_IF(is_compile_string<S>::value)> -constexpr basic_string_view<typename S::char_type> to_string_view(const S& s) { - return s; +constexpr auto to_string_view(const S& s) + -> basic_string_view<typename S::char_type> { + return basic_string_view<typename S::char_type>(s); } FMT_BEGIN_DETAIL_NAMESPACE void to_string_view(...); -using fmt::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 <typename S> using char_t = typename detail::char_t_impl<S>::type; \rst Parsing context consisting of a format string range being parsed and an argument counter for automatic indexing. - - You can use one of the following type aliases for common character types: - - +-----------------------+-------------------------------------+ - | Type | Definition | - +=======================+=====================================+ - | format_parse_context | basic_format_parse_context<char> | - +-----------------------+-------------------------------------+ - | wformat_parse_context | basic_format_parse_context<wchar_t> | - +-----------------------+-------------------------------------+ + You can use the ``format_parse_context`` type alias for ``char`` instead. \endrst */ template <typename Char, typename ErrorHandler = detail::error_handler> @@ -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<char>; -using wformat_parse_context = basic_format_parse_context<wchar_t>; template <typename Context> class basic_format_arg; template <typename Context> 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 <typename T, typename Context> @@ -705,11 +707,14 @@ template <typename T> struct is_contiguous : std::false_type {}; template <typename Char> struct is_contiguous<std::basic_string<Char>> : std::true_type {}; +class appender; + FMT_BEGIN_DETAIL_NAMESPACE // Extracts a reference to the container from back_insert_iterator. template <typename Container> -inline Container& get_container(std::back_insert_iterator<Container> it) { +inline auto get_container(std::back_insert_iterator<Container> it) + -> Container& { using bi_iterator = std::back_insert_iterator<Container>; struct accessor : bi_iterator { accessor(bi_iterator iter) : bi_iterator(iter) {} @@ -718,6 +723,23 @@ inline Container& get_container(std::back_insert_iterator<Container> it) { return *accessor(it).container; } +template <typename Char, typename InputIt, typename OutputIt> +FMT_CONSTEXPR auto copy_str(InputIt begin, InputIt end, OutputIt out) + -> OutputIt { + while (begin != end) *out++ = static_cast<Char>(*begin++); + return out; +} + +template <typename Char, FMT_ENABLE_IF(std::is_same<Char, char>::value)> +FMT_CONSTEXPR auto copy_str(const Char* begin, const Char* end, Char* out) + -> Char* { + if (is_constant_evaluated()) + return copy_str<Char, const Char*, Char*>(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 <typename T> 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 <typename T> 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 <typename T> class buffer { /** Appends data to the end of the buffer. */ template <typename U> void append(const U* begin, const U* end); - template <typename I> T& operator[](I index) { return ptr_[index]; } - template <typename I> const T& operator[](I index) const { + template <typename I> auto operator[](I index) -> T& { return ptr_[index]; } + template <typename I> 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<T> { 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<T>(data_, data_ + this->limit(size), out_); + } public: explicit iterator_buffer(OutputIt out, size_t n = buffer_size) : Traits(n), buffer<T>(data_, 0, buffer_size), out_(out) {} + iterator_buffer(iterator_buffer&& other) + : Traits(other), buffer<T>(data_, 0, buffer_size), out_(other.out_) {} ~iterator_buffer() { flush(); } - 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 <typename T> class iterator_buffer<T*, T> final : public buffer<T> { @@ -862,7 +892,7 @@ template <typename T> class iterator_buffer<T*, T> final : public buffer<T> { public: explicit iterator_buffer(T* out, size_t = 0) : buffer<T>(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<std::back_insert_iterator<Container>, : buffer<typename Container::value_type>(c.size()), container_(c) {} explicit iterator_buffer(std::back_insert_iterator<Container> out, size_t = 0) : iterator_buffer(get_container(out)) {} - std::back_insert_iterator<Container> out() { + auto out() -> std::back_insert_iterator<Container> { return std::back_inserter(container_); } }; @@ -907,49 +937,24 @@ template <typename T = char> class counting_buffer final : public buffer<T> { public: counting_buffer() : buffer<T>(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 <typename T> -class buffer_appender : public std::back_insert_iterator<buffer<T>> { - using base = std::back_insert_iterator<buffer<T>>; - - public: - using std::back_insert_iterator<buffer<T>>::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; - } -}; +using buffer_appender = conditional_t<std::is_same<T, char>::value, appender, + std::back_insert_iterator<buffer<T>>>; -// Maps an output iterator into a buffer. +// Maps an output iterator to a buffer. template <typename T, typename OutputIt> -iterator_buffer<OutputIt, T> get_buffer(OutputIt); -template <typename T> buffer<T>& get_buffer(buffer_appender<T>); - -template <typename OutputIt> OutputIt get_buffer_init(OutputIt out) { - return out; -} -template <typename T> buffer<T>& get_buffer_init(buffer_appender<T> out) { - return get_container(out); +auto get_buffer(OutputIt out) -> iterator_buffer<OutputIt, T> { + return iterator_buffer<OutputIt, T>(out); } template <typename Buffer> auto get_iterator(Buffer& buf) -> decltype(buf.out()) { return buf.out(); } -template <typename T> buffer_appender<T> get_iterator(buffer<T>& buf) { +template <typename T> auto get_iterator(buffer<T>& buf) -> buffer_appender<T> { return buffer_appender<T>(buf); } @@ -986,8 +991,8 @@ struct arg_data { template <typename... U> 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<Char>* named_args() { return named_args_; } + auto args() const -> const T* { return args_ + 1; } + auto named_args() -> named_arg_info<Char>* { return named_args_; } }; template <typename T, typename Char, size_t NUM_ARGS> @@ -997,8 +1002,10 @@ struct arg_data<T, Char, NUM_ARGS, 0> { template <typename... U> 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 <typename Char> @@ -1029,12 +1036,12 @@ template <typename... Args> FMT_CONSTEXPR FMT_INLINE void init_named_args(std::nullptr_t, int, int, const Args&...) {} -template <bool B = false> constexpr size_t count() { return B ? 1 : 0; } -template <bool B1, bool B2, bool... Tail> constexpr size_t count() { +template <bool B = false> constexpr auto count() -> size_t { return B ? 1 : 0; } +template <bool B1, bool B2, bool... Tail> constexpr auto count() -> size_t { return (B1 ? 1 : 0) + count<B2, Tail...>(); } -template <typename... Args> constexpr size_t count_named_args() { +template <typename... Args> constexpr auto count_named_args() -> size_t { return count<is_named_arg<Args>::value...>(); } @@ -1115,6 +1122,7 @@ template <typename Context> 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 <typename Context> class value { named_arg_value<char_type> 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 <typename Context> class value { FMT_INLINE value(const named_arg_info<char_type>* args, size_t size) : named_args{args, size} {} - template <typename T> FMT_INLINE value(const T& val) { + template <typename T> 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<T>` for `format` and @@ -1179,7 +1188,7 @@ template <typename Context> class value { }; template <typename Context, typename T> -FMT_CONSTEXPR basic_format_arg<Context> make_arg(const T& value); +FMT_CONSTEXPR auto make_arg(const T& value) -> basic_format_arg<Context>; // To minimize the number of types we need to deal with, long is translated // either to int or to long long depending on its size. @@ -1193,40 +1202,52 @@ struct unformattable {}; template <typename Context> 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 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 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(int128_t val) -> int128_t { return val; } + FMT_CONSTEXPR FMT_INLINE auto map(uint128_t val) -> uint128_t { return val; } + FMT_CONSTEXPR FMT_INLINE auto map(bool val) -> bool { return val; } template <typename T, FMT_ENABLE_IF(is_char<T>::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<T, char>::value || std::is_same<T, char_type>::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 <typename T, FMT_ENABLE_IF(is_string<T>::value)> - FMT_CONSTEXPR FMT_INLINE basic_string_view<char_type> map(const T& val) { + FMT_CONSTEXPR FMT_INLINE auto map(const T& val) + -> basic_string_view<char_type> { static_assert(std::is_same<char_type, char_t<T>>::value, "mixing character types is disallowed"); return to_string_view(val); @@ -1236,7 +1257,8 @@ template <typename Context> struct arg_mapper { std::is_constructible<basic_string_view<char_type>, T>::value && !is_string<T>::value && !has_formatter<T, Context>::value && !has_fallback_formatter<T, char_type>::value)> - FMT_CONSTEXPR FMT_INLINE basic_string_view<char_type> map(const T& val) { + FMT_CONSTEXPR FMT_INLINE auto map(const T& val) + -> basic_string_view<char_type> { return basic_string_view<char_type>(val); } template < @@ -1246,29 +1268,34 @@ template <typename Context> struct arg_mapper { !std::is_constructible<basic_string_view<char_type>, T>::value && !is_string<T>::value && !has_formatter<T, Context>::value && !has_fallback_formatter<T, char_type>::value)> - FMT_CONSTEXPR FMT_INLINE basic_string_view<char_type> map(const T& val) { + FMT_CONSTEXPR FMT_INLINE auto map(const T& val) + -> basic_string_view<char_type> { return std_string_view<char_type>(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<char_type, char>::value, "invalid string type"); return reinterpret_cast<const char*>(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<char_type, char>::value, "invalid string type"); return reinterpret_cast<const char*>(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 <typename Context> struct arg_mapper { FMT_ENABLE_IF(!is_string<T>::value && !is_char<T>::value && (has_formatter<T, Context>::value || has_fallback_formatter<T, char_type>::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 <typename Context> struct arg_mapper { return map(named_arg.value); } - unformattable map(...) { return {}; } + auto map(...) -> unformattable { return {}; } }; // A type constant after applying arg_mapper<Context>. @@ -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<detail::buffer<char>> { + using base = std::back_insert_iterator<detail::buffer<char>>; + + template <typename T> + friend auto get_buffer(appender out) -> detail::buffer<char>& { + return detail::get_container(out); + } + + public: + using std::back_insert_iterator<detail::buffer<char>>::back_insert_iterator; + appender(base it) : 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 <typename Context> class basic_format_arg { @@ -1335,8 +1389,8 @@ template <typename Context> class basic_format_arg { detail::type type_; template <typename ContextType, typename T> - friend FMT_CONSTEXPR basic_format_arg<ContextType> detail::make_arg( - const T& value); + friend FMT_CONSTEXPR auto detail::make_arg(const T& value) + -> basic_format_arg<ContextType>; template <typename Visitor, typename Ctx> friend FMT_CONSTEXPR auto visit_format_arg(Visitor&& vis, @@ -1374,10 +1428,12 @@ template <typename Context> 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 <typename Context> class basic_format_arg { \endrst */ template <typename Visitor, typename Context> -FMT_CONSTEXPR_DECL FMT_INLINE auto visit_format_arg( +FMT_CONSTEXPR FMT_INLINE auto visit_format_arg( Visitor&& vis, const basic_format_arg<Context>& 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); - case detail::type::uint128_type: - return vis(arg.value_.uint128_value); -#else case detail::type::int128_type: + return vis(detail::convert_for_visit(arg.value_.int128_value)); 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<char_type>(arg.value_.string.data, - arg.value_.string.size)); + using sv = basic_string_view<typename Context::char_type>; + return vis(sv(arg.value_.string.data, arg.value_.string.size)); case detail::type::pointer_type: return vis(arg.value_.pointer); case detail::type::custom_type: @@ -1437,6 +1486,12 @@ FMT_CONSTEXPR_DECL FMT_INLINE auto visit_format_arg( FMT_BEGIN_DETAIL_NAMESPACE +template <typename Char, typename InputIt> +auto copy_str(InputIt begin, InputIt end, appender out) -> appender { + get_container(out).append(begin, end); + return out; +} + #if FMT_GCC_VERSION && FMT_GCC_VERSION < 500 // A workaround for gcc 4.8 to make void_t work in a SFINAE context. template <typename... Ts> struct void_t_impl { using type = void; }; @@ -1467,9 +1522,8 @@ struct is_contiguous_back_insert_iterator : std::false_type {}; template <typename Container> struct is_contiguous_back_insert_iterator<std::back_insert_iterator<Container>> : is_contiguous<Container> {}; -template <typename Char> -struct is_contiguous_back_insert_iterator<buffer_appender<Char>> - : std::true_type {}; +template <> +struct is_contiguous_back_insert_iterator<appender> : std::true_type {}; // A type-erased reference to an std::locale to avoid heavy <locale> include. class locale_ref { @@ -1482,19 +1536,21 @@ class locale_ref { explicit operator bool() const FMT_NOEXCEPT { return locale_ != nullptr; } - template <typename Locale> Locale get() const; + template <typename Locale> auto get() const -> Locale; }; -template <typename> constexpr unsigned long long encode_types() { return 0; } +template <typename> constexpr auto encode_types() -> unsigned long long { + return 0; +} template <typename Context, typename Arg, typename... Args> -constexpr unsigned long long encode_types() { +constexpr auto encode_types() -> unsigned long long { return static_cast<unsigned>(mapped_type_constant<Arg, Context>::value) | (encode_types<Context, Args...>() << packed_arg_bits); } template <typename Context, typename T> -FMT_CONSTEXPR basic_format_arg<Context> make_arg(const T& value) { +FMT_CONSTEXPR auto make_arg(const T& value) -> basic_format_arg<Context> { basic_format_arg<Context> arg; arg.type_ = mapped_type_constant<T, Context>::value; arg.value_ = arg_mapper<Context>().map(value); @@ -1506,21 +1562,20 @@ FMT_CONSTEXPR basic_format_arg<Context> make_arg(const T& value) { // another (not recommended). template <bool IS_PACKED, typename Context, type, typename T, FMT_ENABLE_IF(IS_PACKED)> -FMT_CONSTEXPR FMT_INLINE value<Context> make_arg(const T& val) { +FMT_CONSTEXPR FMT_INLINE auto make_arg(const T& val) -> value<Context> { const auto& arg = arg_mapper<Context>().map(val); static_assert( !std::is_same<decltype(arg), const unformattable&>::value, "Cannot format an argument. To make type T formattable provide a " "formatter<T> specialization: https://fmt.dev/latest/api.html#udt"); - return arg; + return {arg}; } template <bool IS_PACKED, typename Context, type, typename T, FMT_ENABLE_IF(!IS_PACKED)> -inline basic_format_arg<Context> make_arg(const T& value) { +inline auto make_arg(const T& value) -> basic_format_arg<Context> { return make_arg<Context>(value); } - FMT_END_DETAIL_NAMESPACE // Formatting context. @@ -1552,32 +1607,35 @@ template <typename OutputIt, typename Char> 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<char_type> name) { + constexpr auto arg(int id) const -> format_arg { return args_.get(id); } + FMT_CONSTEXPR auto arg(basic_string_view<char_type> name) -> format_arg { return args_.get(name); } - int arg_id(basic_string_view<char_type> name) { return args_.get_id(name); } - const basic_format_args<basic_format_context>& args() const { return args_; } + FMT_CONSTEXPR auto arg_id(basic_string_view<char_type> name) -> int { + return args_.get_id(name); + } + auto args() const -> const basic_format_args<basic_format_context>& { + return args_; + } - FMT_CONSTEXPR 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<iterator>()) out_ = it; } - FMT_CONSTEXPR detail::locale_ref locale() { return loc_; } + FMT_CONSTEXPR auto locale() -> detail::locale_ref { return loc_; } }; template <typename Char> using buffer_context = basic_format_context<detail::buffer_appender<Char>, Char>; using format_context = buffer_context<char>; -using wformat_context = buffer_context<wchar_t>; // 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 <typename Context = format_context, typename... Args> -constexpr format_arg_store<Context, Args...> 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 <typename... Args, typename S, typename Char = char_t<S>> -FMT_INLINE auto make_args_checked(const S& format_str, - const remove_reference_t<Args>&... args) - -> format_arg_store<buffer_context<Char>, remove_reference_t<Args>...> { - static_assert( - detail::count<( - std::is_base_of<detail::view, remove_reference_t<Args>>::value && - std::is_reference<Args>::value)...>() == 0, - "passing views as lvalues is disallowed"); - detail::check_format_string<Args...>(format_str); +constexpr auto make_format_args(const Args&... args) + -> format_arg_store<Context, Args...> { return {args...}; } @@ -1685,7 +1722,7 @@ FMT_INLINE auto make_args_checked(const S& format_str, \endrst */ template <typename Char, typename T> -inline detail::named_arg<Char, T> arg(const Char* name, const T& arg) { +inline auto arg(const Char* name, const T& arg) -> detail::named_arg<Char, T> { static_assert(!detail::is_named_arg<T>(), "nested named arguments"); return {name, arg}; } @@ -1721,14 +1758,14 @@ template <typename Context> 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<detail::type>((desc_ >> shift) & mask); @@ -1774,7 +1811,7 @@ template <typename Context> 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 <typename Context> class basic_format_args { return arg; } - template <typename Char> format_arg get(basic_string_view<Char> name) const { + template <typename Char> + auto get(basic_string_view<Char> name) const -> format_arg { int id = get_id(name); return id >= 0 ? get(id) : format_arg(); } - template <typename Char> int get_id(basic_string_view<Char> name) const { + template <typename Char> + auto get_id(basic_string_view<Char> name) const -> int { if (!has_named_args()) return -1; const auto& named_args = (is_packed() ? values_[-1] : args_[-1].value_).named_args; @@ -1802,7 +1841,7 @@ template <typename Context> 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<int>(is_packed() ? max_packed : desc_ & ~detail::is_unpacked_bit); @@ -1810,10 +1849,9 @@ template <typename Context> class basic_format_args { }; /** An alias to ``basic_format_args<format_context>``. */ -// 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<format_context>; -using wformat_args = basic_format_args<wformat_context>; // 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 <typename Char> struct fill_t { size_ = static_cast<unsigned char>(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 <typename Char> struct arg_ref { FMT_CONSTEXPR explicit arg_ref(basic_string_view<Char> 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 <typename Char> class specs_setter { + protected: + basic_format_specs<Char>& specs_; + public: explicit FMT_CONSTEXPR specs_setter(basic_format_specs<Char>& specs) : specs_(specs) {} @@ -1932,14 +1972,12 @@ template <typename Char> class specs_setter { FMT_CONSTEXPR void on_fill(basic_string_view<Char> 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 <typename Char> class specs_setter { FMT_CONSTEXPR void on_type(Char type) { specs_.type = static_cast<char>(type); } - - protected: - basic_format_specs<Char>& specs_; }; // Format spec handler that saves references to arguments representing dynamic @@ -1987,26 +2022,27 @@ class dynamic_specs_handler } private: + dynamic_format_specs<char_type>& specs_; + ParseContext& context_; + using arg_ref_type = arg_ref<char_type>; - 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<char_type> arg_id) { + FMT_CONSTEXPR auto make_arg_ref(basic_string_view<char_type> arg_id) + -> arg_ref_type { context_.check_arg_id(arg_id); basic_string_view<char_type> format_str( context_.begin(), to_unsigned(context_.end() - context_.begin())); return arg_ref_type(arg_id); } - - dynamic_format_specs<char_type>& specs_; - ParseContext& context_; }; template <typename Char> constexpr bool is_ascii_letter(Char c) { @@ -2015,16 +2051,17 @@ template <typename Char> constexpr bool is_ascii_letter(Char c) { // Converts a character to ASCII. Returns a number > 127 on conversion failure. template <typename Char, FMT_ENABLE_IF(std::is_integral<Char>::value)> -constexpr Char to_ascii(Char value) { +constexpr auto to_ascii(Char value) -> Char { return value; } template <typename Char, FMT_ENABLE_IF(std::is_enum<Char>::value)> -constexpr typename std::underlying_type<Char>::type to_ascii(Char value) { +constexpr auto to_ascii(Char value) -> + typename std::underlying_type<Char>::type { return value; } template <typename Char> -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 <bool IS_CONSTEXPR, typename T, typename Ptr = const T*> +FMT_CONSTEXPR auto find(Ptr first, Ptr last, T value, Ptr& out) -> bool { + for (out = first; out != last; ++out) { + if (*out == value) return true; + } + return false; +} + +template <> +inline auto find<false, char>(const char* first, const char* last, char value, + const char*& out) -> bool { + out = static_cast<const char*>( + std::memchr(first, value, to_unsigned(last - first))); + return out != nullptr; +} + // Parses the range [begin, end) as an unsigned integer. This function assumes // that the range is non-empty and the first character is a digit. -template <typename Char, typename ErrorHandler> -FMT_CONSTEXPR int parse_nonnegative_int(const Char*& begin, const Char* end, - ErrorHandler&& eh) { +template <typename Char> +FMT_CONSTEXPR auto parse_nonnegative_int(const Char*& begin, const Char* end, + int error_value) noexcept -> int { FMT_ASSERT(begin != end && '0' <= *begin && *begin <= '9', ""); - unsigned value = 0; - // 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<int>(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<int>::digits10) + return static_cast<int>(value); + // Check for overflow. + const unsigned max = to_unsigned((std::numeric_limits<int>::max)()); + return num_digits == std::numeric_limits<int>::digits10 + 1 && + prev * 10ull + unsigned(p[-1] - '0') <= max + ? static_cast<int>(value) + : error_value; } // Parses fill and alignment. template <typename Char, typename Handler> -FMT_CONSTEXPR 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 <typename Char> FMT_CONSTEXPR bool is_name_start(Char c) { } template <typename Char, typename IDHandler> -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<int>::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 <typename Char, typename IDHandler> -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 <typename SpecHandler, typename Char> 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<Char> id) { - handler.on_dynamic_width(id); - } - - FMT_CONSTEXPR void on_error(const char* message) { - handler.on_error(message); - } - - SpecHandler& handler; -}; - template <typename Char, typename Handler> -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<Char> id) { + handler.on_dynamic_width(id); + } + FMT_CONSTEXPR void on_error(const char* message) { + if (message) handler.on_error(message); + } + }; + FMT_ASSERT(begin != end, ""); if ('0' <= *begin && *begin <= '9') { - 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, Char>(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 <typename SpecHandler, typename Char> 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<Char> id) { - handler.on_dynamic_precision(id); - } - - FMT_CONSTEXPR void on_error(const char* message) { - handler.on_error(message); - } - - SpecHandler& handler; -}; - template <typename Char, typename Handler> -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<Char> id) { + handler.on_dynamic_precision(id); + } + FMT_CONSTEXPR void on_error(const char* message) { + if (message) handler.on_error(message); + } + }; + ++begin; auto c = begin != end ? *begin : Char(); if ('0' <= c && c <= '9') { - 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, Char>(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 <typename Char, typename SpecHandler> -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 <bool IS_CONSTEXPR, typename T, typename Ptr = const T*> -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<false, char>(const char* first, const char* last, char value, - const char*& out) { - out = static_cast<const char*>( - std::memchr(first, value, to_unsigned(last - first))); - return out != nullptr; -} - -template <typename Handler, typename Char> struct id_adapter { - Handler& handler; - int arg_id; - - FMT_CONSTEXPR void operator()() { arg_id = handler.on_arg_id(); } - FMT_CONSTEXPR void operator()(int id) { arg_id = handler.on_arg_id(id); } - FMT_CONSTEXPR void operator()(basic_string_view<Char> id) { - arg_id = handler.on_arg_id(id); - } - FMT_CONSTEXPR void on_error(const char* message) { - handler.on_error(message); - } -}; - template <typename Char, typename Handler> -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<Char> id) { + arg_id = handler.on_arg_id(id); + } + FMT_CONSTEXPR void on_error(const char* message) { + if (message) handler.on_error(message); + } + }; + ++begin; if (begin == end) return handler.on_error("invalid format string"), end; if (*begin == '}') { @@ -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, Char>{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 <bool IS_CONSTEXPR, typename Char, typename Handler> -FMT_CONSTEXPR_DECL FMT_INLINE void parse_format_string( +FMT_CONSTEXPR FMT_INLINE void parse_format_string( basic_string_view<Char> 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<IS_CONSTEXPR>(pbegin, pend, '}', p)) + if (!find<IS_CONSTEXPR>(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<IS_CONSTEXPR>(begin + 1, end, '{', p)) + if (*begin != '{' && !find<IS_CONSTEXPR>(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 <typename T, typename ParseContext> -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<char_type>; using mapped_type = conditional_t< @@ -2419,11 +2458,11 @@ class compile_parse_context public: explicit FMT_CONSTEXPR compile_parse_context( - basic_string_view<Char> format_str, int num_args = INT_MAX, - ErrorHandler eh = {}) + basic_string_view<Char> format_str, + int num_args = (std::numeric_limits<int>::max)(), ErrorHandler eh = {}) : base(format_str, eh), num_args_(num_args) {} - FMT_CONSTEXPR 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 <typename ErrorHandler> +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 <typename Char, typename ErrorHandler = error_handler> +FMT_CONSTEXPR auto check_char_specs(const basic_format_specs<Char>& 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 <typename ErrorHandler = error_handler, typename Char> +FMT_CONSTEXPR auto parse_float_type_spec(const basic_format_specs<Char>& specs, + ErrorHandler&& eh = {}) + -> float_specs { + auto result = float_specs(); + result.showpoint = specs.alt; + result.locale = specs.localized; + switch (specs.type) { + case 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 <typename Char, typename ErrorHandler = error_handler> +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 <typename Char, typename ErrorHandler = error_handler> +FMT_CONSTEXPR void check_string_type_spec(Char spec, ErrorHandler&& eh = {}) { + if (spec != 0 && spec != 's') eh.on_error("invalid type specifier"); +} + +template <typename Char, typename ErrorHandler> +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 <typename Handler> class specs_checker : public Handler { + private: + detail::type arg_type_; + + FMT_CONSTEXPR void require_numeric_argument() { + if (!is_arithmetic_type(arg_type_)) + this->on_error("format specifier requires numeric argument"); + } + + public: + FMT_CONSTEXPR specs_checker(const Handler& handler, detail::type arg_type) + : Handler(handler), arg_type_(arg_type) {} + + FMT_CONSTEXPR void on_align(align_t align) { + if (align == align::numeric) require_numeric_argument(); + Handler::on_align(align); + } + + FMT_CONSTEXPR void on_sign(sign_t s) { + require_numeric_argument(); + if (is_integral_type(arg_type_) && arg_type_ != type::int_type && + arg_type_ != type::long_long_type && arg_type_ != type::char_type) { + this->on_error("format specifier requires signed argument"); + } + Handler::on_sign(s); + } + + FMT_CONSTEXPR void on_hash() { + require_numeric_argument(); + Handler::on_hash(); + } + + FMT_CONSTEXPR void on_localized() { + require_numeric_argument(); + Handler::on_localized(); + } + + FMT_CONSTEXPR void on_zero() { + require_numeric_argument(); + Handler::on_zero(); + } + + FMT_CONSTEXPR void end_precision() { + if (is_integral_type(arg_type_) || arg_type_ == type::pointer_type) + this->on_error("precision not allowed for this argument type"); + } +}; + constexpr int invalid_arg_index = -1; #if FMT_USE_NONTYPE_TEMPLATE_PARAMETERS template <int N, typename T, typename... Args, typename Char> -constexpr int get_arg_index_by_name(basic_string_view<Char> name) { +constexpr auto get_arg_index_by_name(basic_string_view<Char> name) -> int { if constexpr (detail::is_statically_named_arg<T>()) { if (name == T::name) return N; } - if constexpr (sizeof...(Args) > 0) + if constexpr (sizeof...(Args) > 0) { return get_arg_index_by_name<N + 1, Args...>(name); - return invalid_arg_index; + } else { + (void)name; // Workaround an MSVC bug about "unused" parameter. + return invalid_arg_index; + } } #endif template <typename... Args, typename Char> -FMT_CONSTEXPR int get_arg_index_by_name(basic_string_view<Char> name) { +FMT_CONSTEXPR auto get_arg_index_by_name(basic_string_view<Char> name) -> int { #if FMT_USE_NONTYPE_TEMPLATE_PARAMETERS - if constexpr (sizeof...(Args) > 0) + 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 <typename Char, typename ErrorHandler, typename... Args> class format_string_checker { + private: + using parse_context_type = compile_parse_context<Char, ErrorHandler>; + enum { num_args = sizeof...(Args) }; + + // Format specifier parsing function. + using parse_func = const Char* (*)(parse_context_type&); + + parse_context_type context_; + parse_func parse_funcs_[num_args > 0 ? num_args : 1]; + public: explicit FMT_CONSTEXPR format_string_checker( basic_string_view<Char> format_str, ErrorHandler eh) @@ -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<Char> 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<Char> id) -> int { #if FMT_USE_NONTYPE_TEMPLATE_PARAMETERS auto index = get_arg_index_by_name<Args...>(id); if (index == invalid_arg_index) on_error("named argument is not found"); @@ -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<Char, ErrorHandler>; - enum { num_args = sizeof...(Args) }; - - // Format specifier parsing function. - using parse_func = const Char* (*)(parse_context_type&); - - parse_context_type context_; - parse_func parse_funcs_[num_args > 0 ? num_args : 1]; }; template <typename... Args, typename S, enable_if_t<(is_compile_string<S>::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<typename S::char_type, error_handler, remove_cvref_t<Args>...>; - FMT_CONSTEXPR_DECL bool invalid_format = + FMT_CONSTEXPR bool invalid_format = (parse_format_string<true>(s, checker(s, {})), true); - (void)invalid_format; -} - -// Converts string literals to basic_string_view. -template <typename Char, size_t N> -FMT_CONSTEXPR basic_string_view<Char> 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<Char>::to_int_type(s[N - 1]) == 0) ? 1 : 0)}; + ignore_unused(invalid_format); } -// Converts string_view to basic_string_view. template <typename Char> -FMT_CONSTEXPR basic_string_view<Char> compile_string_to_view( - const std_string_view<Char>& 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<decltype(s[0])>; \ - FMT_MAYBE_UNUSED FMT_CONSTEXPR \ - operator fmt::basic_string_view<char_type>() const { \ - return fmt::detail::compile_string_to_view<char_type>(s); \ - } \ - }; \ - return FMT_COMPILE_STRING(); \ - }() +void vformat_to( + buffer<Char>& buf, basic_string_view<Char> fmt, + basic_format_args<FMT_BUFFER_CONTEXT(type_identity_t<Char>)> args, + locale_ref loc = {}); -/** - \rst - Constructs a compile-time format string from a string literal *s*. +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 - **Example**:: +// A formatter specialization for the core types corresponding to detail::type +// constants. +template <typename T, typename Char> +struct formatter<T, Char, + enable_if_t<detail::type_constant<T, Char>::value != + detail::type::custom_type>> { + private: + detail::dynamic_format_specs<Char> specs_; - // 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) + public: + // Parses format specifiers stopping either at the end of the range or at the + // terminating '}'. + template <typename ParseContext> + FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) { + auto begin = ctx.begin(), end = ctx.end(); + if (begin == end) return begin; + using handler_type = detail::dynamic_specs_handler<ParseContext>; + auto type = detail::type_constant<T, Char>::value; + auto checker = + detail::specs_checker<handler_type>(handler_type(specs_, ctx), type); + auto it = detail::parse_format_specs(begin, end, checker); + auto eh = ctx.error_handler(); + switch (type) { + case detail::type::none_type: + FMT_ASSERT(false, "invalid argument type"); + break; + case detail::type::bool_type: + if (!specs_.type || 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 <typename Char, FMT_ENABLE_IF(!std::is_same<Char, char>::value)> -std::basic_string<Char> vformat( - basic_string_view<Char> format_str, - basic_format_args<buffer_context<type_identity_t<Char>>> args); + template <typename FormatContext> + FMT_CONSTEXPR auto format(const T& val, FormatContext& ctx) const + -> decltype(ctx.out()); +}; -FMT_API std::string vformat(string_view format_str, format_args args); +template <typename Char> struct basic_runtime { basic_string_view<Char> str; }; -template <typename Char> -void vformat_to( - buffer<Char>& buf, basic_string_view<Char> format_str, - basic_format_args<FMT_BUFFER_CONTEXT(type_identity_t<Char>)> args, - detail::locale_ref loc = {}); +template <typename Char, typename... Args> class basic_format_string { + private: + basic_string_view<Char> str_; -template <typename Char, typename Args, - FMT_ENABLE_IF(!std::is_same<Char, char>::value)> -inline void vprint_mojibake(std::FILE*, basic_string_view<Char>, const Args&) {} + public: + template <typename S, + FMT_ENABLE_IF( + std::is_convertible<const S&, basic_string_view<Char>>::value)> + FMT_CONSTEVAL basic_format_string(const S& s) : str_(s) { + static_assert( + detail::count< + (std::is_base_of<detail::view, remove_reference_t<Args>>::value && + std::is_reference<Args>::value)...>() == 0, + "passing views as lvalues is disallowed"); +#ifdef FMT_HAS_CONSTEVAL + if constexpr (detail::count_named_args<Args...>() == 0) { + using checker = detail::format_string_checker<Char, detail::error_handler, + remove_cvref_t<Args>...>; + detail::parse_format_string<true>(str_, checker(s, {})); + } +#else + detail::check_format_string<Args...>(s); +#endif + } + basic_format_string(basic_runtime<Char> r) : str_(r.str) {} -FMT_API void vprint_mojibake(std::FILE*, string_view, format_args); -#ifndef _WIN32 -inline void vprint_mojibake(std::FILE*, string_view, format_args) {} + FMT_INLINE operator basic_string_view<Char>() const { return str_; } +}; + +#if FMT_GCC_VERSION && FMT_GCC_VERSION < 409 +// Workaround broken conversion on older gcc. +template <typename... Args> using format_string = string_view; +template <typename S> auto runtime(const S& s) -> basic_string_view<char_t<S>> { + return s; +} +#else +template <typename... Args> +using format_string = basic_format_string<char, type_identity_t<Args>...>; +// Creates a runtime format string. +template <typename S> auto runtime(const S& s) -> basic_runtime<char_t<S>> { + return {{s}}; +} #endif -FMT_END_DETAIL_NAMESPACE +FMT_API auto vformat(string_view fmt, format_args args) -> std::string; + +/** + \rst + Formats ``args`` according to specifications in ``fmt`` and returns the result + as a string. + + **Example**:: + + #include <fmt/core.h> + std::string message = fmt::format("The answer is {}", 42); + \endrst +*/ +template <typename... T> +FMT_INLINE auto format(format_string<T...> fmt, T&&... args) -> std::string { + return vformat(fmt, fmt::make_format_args(args...)); +} /** Formats a string and writes the output to ``out``. */ -// GCC 8 and earlier cannot handle std::back_insert_iterator<Container> with -// vformat_to<ArgFormatter>(...) overload, so SFINAE on iterator type instead. -template <typename OutputIt, typename S, typename Char = char_t<S>, - bool enable = detail::is_output_iterator<OutputIt, Char>::value> -auto vformat_to(OutputIt out, const S& format_str, - basic_format_args<buffer_context<type_identity_t<Char>>> args) - -> typename std::enable_if<enable, OutputIt>::type { - decltype(detail::get_buffer<Char>(out)) buf(detail::get_buffer_init(out)); - detail::vformat_to(buf, to_string_view(format_str), args); +template <typename OutputIt, + FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, char>::value)> +auto vformat_to(OutputIt out, string_view fmt, format_args args) -> OutputIt { + using detail::get_buffer; + auto&& buf = get_buffer<char>(out); + detail::vformat_to(buf, 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<char> out; + auto out = std::vector<char>(); fmt::format_to(std::back_inserter(out), "{}", 42); \endrst */ -// We cannot use FMT_ENABLE_IF because of a bug in gcc 8.3. -template <typename OutputIt, typename S, typename... Args, - bool enable = detail::is_output_iterator<OutputIt, char_t<S>>::value> -inline auto format_to(OutputIt out, const S& format_str, Args&&... args) -> - typename std::enable_if<enable, OutputIt>::type { - const auto& vargs = fmt::make_args_checked<Args...>(format_str, args...); - return vformat_to(out, to_string_view(format_str), vargs); +template <typename OutputIt, typename... T, + FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, char>::value)> +FMT_INLINE auto format_to(OutputIt out, format_string<T...> fmt, T&&... args) + -> OutputIt { + return vformat_to(out, fmt, fmt::make_format_args(args...)); } template <typename OutputIt> struct format_to_n_result { @@ -2626,112 +2922,74 @@ template <typename OutputIt> struct format_to_n_result { size_t size; }; -template <typename OutputIt, typename Char, typename... Args, - FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value)> -inline format_to_n_result<OutputIt> vformat_to_n( - OutputIt out, size_t n, basic_string_view<Char> format_str, - basic_format_args<buffer_context<type_identity_t<Char>>> args) { - detail::iterator_buffer<OutputIt, Char, detail::fixed_buffer_traits> buf(out, - n); - detail::vformat_to(buf, format_str, args); +template <typename OutputIt, typename... T, + FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, char>::value)> +auto vformat_to_n(OutputIt out, size_t n, string_view fmt, format_args args) + -> format_to_n_result<OutputIt> { + using buffer = + detail::iterator_buffer<OutputIt, char, detail::fixed_buffer_traits>; + 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 <typename OutputIt, typename S, typename... Args, - bool enable = detail::is_output_iterator<OutputIt, char_t<S>>::value> -inline auto format_to_n(OutputIt out, size_t n, const S& format_str, - const Args&... args) -> - typename std::enable_if<enable, format_to_n_result<OutputIt>>::type { - const auto& vargs = fmt::make_args_checked<Args...>(format_str, args...); - return vformat_to_n(out, n, to_string_view(format_str), vargs); +template <typename OutputIt, typename... T, + FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, char>::value)> +FMT_INLINE auto format_to_n(OutputIt out, size_t n, format_string<T...> fmt, + const T&... args) -> format_to_n_result<OutputIt> { + 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 <typename S, typename... Args, typename Char = char_t<S>> -inline size_t formatted_size(const S& format_str, Args&&... args) { - const auto& vargs = fmt::make_args_checked<Args...>(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 <typename... T> +FMT_INLINE auto formatted_size(format_string<T...> fmt, T&&... args) -> size_t { + auto buf = detail::counting_buffer<>(); + detail::vformat_to(buf, string_view(fmt), fmt::make_format_args(args...), {}); return buf.count(); } -template <typename S, typename Char = char_t<S>> -FMT_INLINE std::basic_string<Char> vformat( - const S& format_str, - basic_format_args<buffer_context<type_identity_t<Char>>> 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. + Formats ``args`` according to specifications in ``fmt`` and writes the output + to ``stdout``. **Example**:: - #include <fmt/core.h> - std::string message = fmt::format("The answer is {}", 42); - \endrst -*/ -// Pass char_t as a default template parameter instead of using -// std::basic_string<char_t<S>> to reduce the symbol size. -template <typename S, typename... Args, typename Char = char_t<S>, - FMT_ENABLE_IF(!FMT_COMPILE_TIME_CHECKS || - !std::is_same<Char, char>::value)> -FMT_INLINE std::basic_string<Char> format(const S& format_str, Args&&... args) { - const auto& vargs = fmt::make_args_checked<Args...>(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"); + fmt::print("Elapsed time: {0:.2f} seconds", 1.23); \endrst */ -template <typename S, typename... Args, typename Char = char_t<S>> -inline void print(std::FILE* f, const S& format_str, Args&&... args) { - const auto& vargs = fmt::make_args_checked<Args...>(format_str, args...); - return detail::is_unicode<Char>() - ? vprint(f, to_string_view(format_str), vargs) - : detail::vprint_mojibake(f, to_string_view(format_str), vargs); +template <typename... T> +FMT_INLINE void print(format_string<T...> fmt, T&&... args) { + const auto& vargs = fmt::make_format_args(args...); + return detail::is_utf8() ? vprint(fmt, vargs) + : detail::vprint_mojibake(stdout, fmt, vargs); } /** \rst - Formats ``args`` according to specifications in ``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 the file ``f``. **Example**:: - fmt::print("Elapsed time: {0:.2f} seconds", 1.23); + fmt::print(stderr, "Don't {}!", "panic"); \endrst */ -template <typename S, typename... Args, typename Char = char_t<S>> -inline void print(const S& format_str, Args&&... args) { - const auto& vargs = fmt::make_args_checked<Args...>(format_str, args...); - return detail::is_unicode<Char>() - ? vprint(to_string_view(format_str), vargs) - : detail::vprint_mojibake(stdout, to_string_view(format_str), - vargs); +template <typename... T> +FMT_INLINE void print(std::FILE* f, format_string<T...> fmt, T&&... args) { + const auto& vargs = fmt::make_format_args(args...); + return detail::is_utf8() ? vprint(f, fmt, vargs) + : detail::vprint_mojibake(f, fmt, vargs); } FMT_MODULE_EXPORT_END diff --git a/deps/fmt/include/fmt/format-inl.h b/deps/fmt/include/fmt/format-inl.h index 02a4e7c3af..94a36d1bc4 100644 --- a/deps/fmt/include/fmt/format-inl.h +++ b/deps/fmt/include/fmt/format-inl.h @@ -10,6 +10,7 @@ #include <algorithm> #include <cctype> +#include <cerrno> // errno #include <climits> #include <cmath> #include <cstdarg> @@ -102,23 +103,21 @@ template <typename Locale> Locale locale_ref::get() const { return locale_ ? *static_cast<const std::locale*>(locale_) : std::locale(); } -template <typename Char> FMT_FUNC std::string grouping_impl(locale_ref loc) { - return std::use_facet<std::numpunct<Char>>(loc.get<std::locale>()).grouping(); -} -template <typename Char> FMT_FUNC Char thousands_sep_impl(locale_ref loc) { - return std::use_facet<std::numpunct<Char>>(loc.get<std::locale>()) - .thousands_sep(); +template <typename Char> +FMT_FUNC auto thousands_sep_impl(locale_ref loc) -> thousands_sep_result<Char> { + auto& facet = std::use_facet<std::numpunct<Char>>(loc.get<std::locale>()); + auto grouping = facet.grouping(); + auto thousands_sep = grouping.empty() ? Char() : facet.thousands_sep(); + return {std::move(grouping), thousands_sep}; } template <typename Char> FMT_FUNC Char decimal_point_impl(locale_ref loc) { return std::use_facet<std::numpunct<Char>>(loc.get<std::locale>()) .decimal_point(); } #else -template <typename Char> FMT_FUNC std::string grouping_impl(locale_ref) { - return "\03"; -} -template <typename Char> FMT_FUNC Char thousands_sep_impl(locale_ref) { - return FMT_STATIC_THOUSANDS_SEPARATOR; +template <typename Char> +FMT_FUNC auto thousands_sep_impl(locale_ref) -> thousands_sep_result<Char> { + return {"\03", FMT_STATIC_THOUSANDS_SEPARATOR}; } template <typename Char> 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 <typename T> -constexpr const typename basic_data<T>::digit_pair basic_data<T>::digits[]; +template <typename T> constexpr const char basic_data<T>::digits[][2]; template <typename T> constexpr const char basic_data<T>::hex_digits[]; template <typename T> constexpr const char basic_data<T>::signs[]; template <typename T> constexpr const unsigned basic_data<T>::prefixes[]; @@ -565,7 +563,6 @@ class bigint { (*this)[bigit_index] = static_cast<bigit>(sum); sum >>= bits<bigit>::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 <typename Handler> -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<T>::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 <class T> -FMT_ALWAYS_INLINE decimal_fp<T> shorter_interval_case(int exponent) - FMT_NOEXCEPT { +FMT_INLINE decimal_fp<T> shorter_interval_case(int exponent) FMT_NOEXCEPT { decimal_fp<T> 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 <typename T> FMT_INLINE std::string operator()(T value) const { - return to_string(value); - } - std::string operator()(basic_format_arg<format_context>::handle h) const { - memory_buffer buf; - format_parse_context parse_ctx({}); - format_context format_ctx(buffer_appender<char>(buf), {}, {}); - h.format(parse_ctx, format_ctx); - return to_string(buf); - } -}; } // namespace detail template <> struct formatter<detail::bigint> { @@ -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<void*>(_get_osfhandle(fd)), u16.c_str(), static_cast<uint32_t>(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 a6dbd819fd..5398a23a82 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 <cerrno> // errno -#include <cmath> // std::signbit -#include <cstdint> -#include <cwchar> +#include <cmath> // std::signbit +#include <cstdint> // uint32_t #include <limits> // std::numeric_limits #include <memory> // std::uninitialized_copy #include <stdexcept> // 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<uint64_t>(2.718); template <typename Dest, typename Source> -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<uintptr_t>(p); } +inline auto to_uintptr(const void* p) -> uintptr_t { + return bit_cast<uintptr_t>(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<T>::max() but shorter and not affected by the max macro. -template <typename T> constexpr T max_value() { +template <typename T> constexpr auto max_value() -> T { return (std::numeric_limits<T>::max)(); } -template <typename T> constexpr int num_bits() { +template <typename T> constexpr auto num_bits() -> int { return std::numeric_limits<T>::digits; } // std::numeric_limits<T>::digits may return 0 for 128-bit ints. -template <> constexpr int num_bits<int128_t>() { return 128; } -template <> constexpr int num_bits<uint128_t>() { return 128; } -template <> constexpr int num_bits<fallback_uintptr>() { +template <> constexpr auto num_bits<int128_t>() -> int { return 128; } +template <> constexpr auto num_bits<uint128_t>() -> int { return 128; } +template <> constexpr auto num_bits<fallback_uintptr>() -> int { return static_cast<int>(sizeof(void*) * std::numeric_limits<unsigned char>::digits); } @@ -336,31 +327,35 @@ using iterator_t = decltype(std::begin(std::declval<T&>())); template <typename T> using sentinel_t = decltype(std::end(std::declval<T&>())); // A workaround for std::string not having mutable data() until C++17. -template <typename Char> inline Char* get_data(std::basic_string<Char>& s) { +template <typename Char> +inline auto get_data(std::basic_string<Char>& s) -> Char* { return &s[0]; } template <typename Container> -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 <typename T> using checked_ptr = stdext::checked_array_iterator<T*>; -template <typename T> checked_ptr<T> make_checked(T* p, size_t size) { +template <typename T> auto make_checked(T* p, size_t size) -> checked_ptr<T> { return {p, size}; } #else template <typename T> using checked_ptr = T*; -template <typename T> inline T* make_checked(T* p, size_t) { return p; } +template <typename T> 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 <typename Container, FMT_ENABLE_IF(is_contiguous<Container>::value)> #if FMT_CLANG_VERSION >= 307 && !FMT_ICC_VERSION __attribute__((no_sanitize("undefined"))) #endif -inline checked_ptr<typename Container::value_type> -reserve(std::back_insert_iterator<Container> it, size_t n) { +inline auto +reserve(std::back_insert_iterator<Container> it, size_t n) + -> checked_ptr<typename Container::value_type> { Container& c = get_container(it); size_t size = c.size(); c.resize(size + n); @@ -368,13 +363,14 @@ reserve(std::back_insert_iterator<Container> it, size_t n) { } template <typename T> -inline buffer_appender<T> reserve(buffer_appender<T> it, size_t n) { +inline auto reserve(buffer_appender<T> it, size_t n) -> buffer_appender<T> { buffer<T>& buf = get_container(it); buf.try_reserve(buf.size() + n); return it; } -template <typename Iterator> constexpr Iterator& reserve(Iterator& it, size_t) { +template <typename Iterator> +constexpr auto reserve(Iterator& it, size_t) -> Iterator& { return it; } @@ -383,10 +379,10 @@ using reserve_iterator = remove_reference_t<decltype(reserve(std::declval<OutputIt&>(), 0))>; template <typename T, typename OutputIt> -constexpr T* to_pointer(OutputIt, size_t) { +constexpr auto to_pointer(OutputIt, size_t) -> T* { return nullptr; } -template <typename T> T* to_pointer(buffer_appender<T> it, size_t n) { +template <typename T> auto to_pointer(buffer_appender<T> it, size_t n) -> T* { buffer<T>& buf = get_container(it); auto size = buf.size(); if (buf.capacity() < size + n) return nullptr; @@ -395,26 +391,27 @@ template <typename T> T* to_pointer(buffer_appender<T> it, size_t n) { } template <typename Container, FMT_ENABLE_IF(is_contiguous<Container>::value)> -inline std::back_insert_iterator<Container> base_iterator( - std::back_insert_iterator<Container>& it, - checked_ptr<typename Container::value_type>) { +inline auto base_iterator(std::back_insert_iterator<Container>& it, + checked_ptr<typename Container::value_type>) + -> std::back_insert_iterator<Container> { return it; } template <typename Iterator> -constexpr Iterator base_iterator(Iterator, Iterator it) { +constexpr auto base_iterator(Iterator, Iterator it) -> Iterator { return it; } // <algorithm> is spectacularly slow to compile in C++20 so use a simple fill_n // instead (#1998). template <typename OutputIt, typename Size, typename T> -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 <typename T, typename Size> -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<T*, Size, T>(out, count, value); } @@ -428,43 +425,10 @@ using char8_type = char8_t; enum char8_type : unsigned char {}; #endif -template <typename InputIt, typename OutChar> -using needs_conversion = bool_constant< - std::is_same<typename std::iterator_traits<InputIt>::value_type, - char>::value && - std::is_same<OutChar, char8_type>::value>; - -template <typename OutChar, typename InputIt, typename OutputIt, - FMT_ENABLE_IF(!needs_conversion<InputIt, OutChar>::value)> -FMT_CONSTEXPR OutputIt copy_str(InputIt begin, InputIt end, OutputIt it) { - while (begin != end) *it++ = *begin++; - return it; -} - -template <typename OutChar, typename InputIt, - FMT_ENABLE_IF(!needs_conversion<InputIt, OutChar>::value)> -FMT_CONSTEXPR20 OutChar* copy_str(InputIt begin, InputIt end, OutChar* out) { - if (is_constant_evaluated()) { - return copy_str<OutChar, InputIt, OutChar*>(begin, end, out); - } - auto size = to_unsigned(end - begin); - std::uninitialized_copy(begin, end, make_checked(out, size)); - return out + size; -} - -template <typename OutChar, typename InputIt, typename OutputIt, - FMT_ENABLE_IF(needs_conversion<InputIt, OutChar>::value)> -OutputIt copy_str(InputIt begin, InputIt end, OutputIt it) { - while (begin != end) *it++ = static_cast<char8_type>(*begin++); - return it; -} - -template <typename OutChar, typename InputIt, - FMT_ENABLE_IF(!needs_conversion<InputIt, OutChar>::value)> -buffer_appender<OutChar> copy_str(InputIt begin, InputIt end, - buffer_appender<OutChar> out) { - get_container(out).append(begin, end); - return out; +template <typename OutChar, typename InputIt, typename OutputIt> +FMT_CONSTEXPR FMT_NOINLINE auto copy_str_noinline(InputIt begin, InputIt end, + OutputIt out) -> OutputIt { + return copy_str<OutChar>(begin, end, out); } // A public domain branchless UTF-8 decoder by Christopher Wellons: @@ -484,8 +448,8 @@ buffer_appender<OutChar> 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 <typename Char> -inline size_t compute_width(basic_string_view<Char> s) { +inline auto compute_width(basic_string_view<Char> 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<char8_type> s) { +inline auto compute_width(basic_string_view<char8_type> s) -> size_t { return compute_width(basic_string_view<char>( reinterpret_cast<const char*>(s.data()), s.size())); } template <typename Char> -inline size_t code_point_index(basic_string_view<Char> s, size_t n) { +inline auto code_point_index(basic_string_view<Char> 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<char8_type> s, size_t n) { +inline auto code_point_index(basic_string_view<char8_type> 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<T>::append(const U* begin, const U* end) { } } -template <typename OutputIt, typename T, typename Traits> -void iterator_buffer<OutputIt, T, Traits>::flush() { - auto size = this->size(); - this->clear(); - out_ = copy_str<T>(data_, data_ + this->limit(size), out_); -} +template <typename T, typename Enable = void> +struct is_locale : std::false_type {}; +template <typename T> +struct is_locale<T, void_t<decltype(T::classic())>> : std::true_type {}; } // namespace detail FMT_MODULE_EXPORT_BEGIN -template <> struct is_char<detail::char8_type> : std::true_type {}; -template <> struct is_char<char16_t> : std::true_type {}; -template <> struct is_char<char32_t> : 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<char> | - +----------------+------------------------------+ - | wmemory_buffer | basic_memory_buffer<wchar_t> | - +----------------+------------------------------+ + You can use the ``memory_buffer`` type alias for ``char`` instead. **Example**:: @@ -729,7 +680,8 @@ class basic_memory_buffer final : public detail::buffer<T> { 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<T> { } // 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<T, SIZE, Allocator>::grow(size_t size) { } using memory_buffer = basic_memory_buffer<char>; -using wmemory_buffer = basic_memory_buffer<wchar_t>; template <typename T, size_t SIZE, typename Allocator> struct is_contiguous<basic_memory_buffer<T, SIZE, Allocator>> : 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 <typename... Args, typename S, typename Char = char_t<S>> +FMT_INLINE auto make_args_checked(const S& fmt, + const remove_reference_t<Args>&... args) + -> format_arg_store<buffer_context<Char>, remove_reference_t<Args>...> { + static_assert( + detail::count<( + std::is_base_of<detail::view, remove_reference_t<Args>>::value && + std::is_reference<Args>::value)...>() == 0, + "passing views as lvalues is disallowed"); + detail::check_format_string<Args...>(fmt); + return {args...}; +} + +// compile-time support +namespace detail_exported { +#if FMT_USE_NONTYPE_TEMPLATE_PARAMETERS +template <typename Char, size_t N> struct fixed_string { + constexpr fixed_string(const Char (&str)[N]) { + detail::copy_str<Char, const Char*, Char*>(static_cast<const Char*>(str), + str + N, data); + } + Char data[N]{}; +}; +#endif + +// Converts a compile-time string to basic_string_view. +template <typename Char, size_t N> +constexpr auto compile_string_to_view(const Char (&s)[N]) + -> basic_string_view<Char> { + // 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<Char>::to_int_type(s[N - 1]) == 0 ? 1 : 0)}; +} +template <typename Char> +constexpr auto compile_string_to_view(detail::std_string_view<Char> s) + -> basic_string_view<Char> { + 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 <typename T, FMT_ENABLE_IF(is_signed<T>::value)> -FMT_CONSTEXPR bool is_negative(T value) { +FMT_CONSTEXPR auto is_negative(T value) -> bool { return value < 0; } template <typename T, FMT_ENABLE_IF(!is_signed<T>::value)> -FMT_CONSTEXPR bool is_negative(T) { +FMT_CONSTEXPR auto is_negative(T) -> bool { return false; } template <typename T, FMT_ENABLE_IF(std::is_floating_point<T>::value)> -FMT_CONSTEXPR bool is_supported_floating_point(T) { +FMT_CONSTEXPR auto is_supported_floating_point(T) -> uint16_t { return (std::is_same<T, float>::value && FMT_USE_FLOAT) || (std::is_same<T, double>::value && FMT_USE_DOUBLE) || (std::is_same<T, long double>::value && FMT_USE_LONG_DOUBLE); @@ -856,8 +859,7 @@ template <typename T = void> 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 <typename T = void> 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<void>; +#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 <typename T> FMT_CONSTEXPR int count_digits_fallback(T n) { +template <typename T> 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 <typename T> 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 <int BITS, typename UInt> FMT_CONSTEXPR int count_digits(UInt n) { +template <int BITS, typename UInt> +FMT_CONSTEXPR auto count_digits(UInt n) -> int { #ifdef FMT_BUILTIN_CLZ if (num_bits<UInt>() == 32) return (FMT_BUILTIN_CLZ(static_cast<uint32_t>(n) | 1) ^ 31) / BITS + 1; @@ -949,65 +952,82 @@ template <int BITS, typename UInt> FMT_CONSTEXPR int count_digits(UInt n) { return num_digits; } -template <> int count_digits<4>(detail::fallback_uintptr n); - -#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 +template <> auto count_digits<4>(detail::fallback_uintptr n) -> int; + +// 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]; +} -#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); +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<int>((n + inc) >> 32); } - 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]); -} #endif + return count_digits_fallback(n); +} -template <typename Int> constexpr int digits10() FMT_NOEXCEPT { +template <typename Int> constexpr auto digits10() FMT_NOEXCEPT -> int { return std::numeric_limits<Int>::digits10; } -template <> constexpr int digits10<int128_t>() FMT_NOEXCEPT { return 38; } -template <> constexpr int digits10<uint128_t>() FMT_NOEXCEPT { return 38; } - -// DEPRECATED! grouping will be merged into thousands_sep. -template <typename Char> FMT_API std::string grouping_impl(locale_ref loc); -template <typename Char> inline std::string grouping(locale_ref loc) { - return grouping_impl<char>(loc); +template <> constexpr auto digits10<int128_t>() FMT_NOEXCEPT -> int { + return 38; } -template <> inline std::string grouping<wchar_t>(locale_ref loc) { - return grouping_impl<wchar_t>(loc); +template <> constexpr auto digits10<uint128_t>() FMT_NOEXCEPT -> int { + return 38; } -template <typename Char> FMT_API Char thousands_sep_impl(locale_ref loc); -template <typename Char> inline Char thousands_sep(locale_ref loc) { - return Char(thousands_sep_impl<char>(loc)); +template <typename Char> struct thousands_sep_result { + std::string grouping; + Char thousands_sep; +}; + +template <typename Char> +FMT_API auto thousands_sep_impl(locale_ref loc) -> thousands_sep_result<Char>; +template <typename Char> +inline auto thousands_sep(locale_ref loc) -> thousands_sep_result<Char> { + auto result = thousands_sep_impl<char>(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<wchar_t> { return thousands_sep_impl<wchar_t>(loc); } -template <typename Char> FMT_API Char decimal_point_impl(locale_ref loc); -template <typename Char> inline Char decimal_point(locale_ref loc) { +template <typename Char> +FMT_API auto decimal_point_impl(locale_ref loc) -> Char; +template <typename Char> inline auto decimal_point(locale_ref loc) -> Char { return Char(decimal_point_impl<char>(loc)); } -template <> inline wchar_t decimal_point(locale_ref loc) { +template <> inline auto decimal_point(locale_ref loc) -> wchar_t { return decimal_point_impl<wchar_t>(loc); } // Compares two characters for equality. -template <typename Char> bool equal2(const Char* lhs, const char* rhs) { - return lhs[0] == rhs[0] && lhs[1] == rhs[1]; +template <typename Char> 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 <typename Iterator> struct format_decimal_result { // buffer of specified size. The caller must ensure that the buffer is large // enough. template <typename Char, typename UInt> -FMT_CONSTEXPR20 format_decimal_result<Char*> format_decimal(Char* out, - UInt value, - int size) { +FMT_CONSTEXPR20 auto format_decimal(Char* out, UInt value, int size) + -> format_decimal_result<Char*> { FMT_ASSERT(size >= count_digits(value), "invalid digit count"); out += size; Char* end = out; @@ -1060,17 +1079,17 @@ FMT_CONSTEXPR20 format_decimal_result<Char*> format_decimal(Char* out, template <typename Char, typename UInt, typename Iterator, FMT_ENABLE_IF(!std::is_pointer<remove_cvref_t<Iterator>>::value)> -inline format_decimal_result<Iterator> format_decimal(Iterator out, UInt value, - int size) { +inline auto format_decimal(Iterator out, UInt value, int size) + -> format_decimal_result<Iterator> { // Buffer is large enough to hold all digits (digits10 + 1). Char buffer[digits10<UInt>() + 1]; auto end = format_decimal(buffer, value, size).end; - return {out, detail::copy_str<Char>(buffer, end, out)}; + return {out, detail::copy_str_noinline<Char>(buffer, end, out)}; } template <unsigned BASE_BITS, typename Char, typename UInt> -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 <unsigned BASE_BITS, typename Char> -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<unsigned char>::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 <unsigned BASE_BITS, typename Char, typename It, typename UInt> -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<Char>(out, to_unsigned(num_digits))) { format_uint<BASE_BITS>(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<UInt>() / BASE_BITS + 1]; format_uint<BASE_BITS>(buffer, value, num_digits, upper); - return detail::copy_str<Char>(buffer, buffer + num_digits, out); + return detail::copy_str_noinline<Char>(buffer, buffer + num_digits, out); } // A converter from UTF-8 to UTF-16. class utf8_to_utf16 { private: - wmemory_buffer buffer_; + basic_memory_buffer<wchar_t> 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<wchar_t>() 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 <typename T = void> struct null {}; - namespace dragonbox { // Type-specific information that Dragonbox uses. @@ -1194,37 +1212,21 @@ template <typename T> struct decimal_fp { int exponent; }; -template <typename T> FMT_API decimal_fp<T> to_decimal(T x) FMT_NOEXCEPT; +template <typename T> +FMT_API auto to_decimal(T x) FMT_NOEXCEPT -> decimal_fp<T>; } // namespace dragonbox template <typename T> -constexpr typename dragonbox::float_info<T>::carrier_uint exponent_mask() { +constexpr auto exponent_mask() -> + typename dragonbox::float_info<T>::carrier_uint { using uint = typename dragonbox::float_info<T>::carrier_uint; return ((uint(1) << dragonbox::float_info<T>::exponent_bits) - 1) << dragonbox::float_info<T>::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 <typename Char, typename It> It write_exponent(int exp, It it) { +template <typename Char, typename It> +auto write_exponent(int exp, It it) -> It { FMT_ASSERT(-10000 < exp && exp < 10000, "exponent out of range"); if (exp < 0) { *it++ = static_cast<Char>('-'); @@ -1245,132 +1247,22 @@ template <typename Char, typename It> It write_exponent(int exp, It it) { } template <typename T> -int format_float(T value, int precision, float_specs specs, buffer<char>& buf); +auto format_float(T value, int precision, float_specs specs, buffer<char>& buf) + -> int; // Formats a floating-point number with snprintf. template <typename T> -int snprintf_float(T value, int precision, float_specs specs, - buffer<char>& buf); - -template <typename T> T promote_float(T value) { return value; } -inline double promote_float(float value) { return static_cast<double>(value); } - -template <typename ErrorHandler = error_handler, typename Char> -FMT_CONSTEXPR float_specs parse_float_type_spec( - const basic_format_specs<Char>& 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 <typename ErrorHandler> -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 <typename Char, typename Handler> -FMT_CONSTEXPR void handle_char_specs(const basic_format_specs<Char>& 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 <typename Char, typename Handler> -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 <typename Char, typename ErrorHandler> -FMT_CONSTEXPR void check_string_type_spec(Char spec, ErrorHandler&& eh) { - if (spec != 0 && spec != 's') eh.on_error("invalid type specifier"); -} +auto snprintf_float(T value, int precision, float_specs specs, + buffer<char>& buf) -> int; -template <typename Char, typename ErrorHandler> -FMT_CONSTEXPR void check_pointer_type_spec(Char spec, ErrorHandler&& eh) { - if (spec != 0 && spec != 'p') eh.on_error("invalid type specifier"); +template <typename T> auto promote_float(T value) -> T { return value; } +inline auto promote_float(float value) -> double { + return static_cast<double>(value); } -template <typename ErrorHandler> -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 <typename ErrorHandler> -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 <typename OutputIt, typename Char> -FMT_NOINLINE FMT_CONSTEXPR OutputIt fill(OutputIt it, size_t n, - const fill_t<Char>& fill) { +FMT_NOINLINE FMT_CONSTEXPR auto fill(OutputIt it, size_t n, + const fill_t<Char>& 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 <align::type align = align::left, typename OutputIt, typename Char, typename F> -FMT_CONSTEXPR OutputIt write_padded(OutputIt out, - const basic_format_specs<Char>& specs, - size_t size, size_t width, F&& f) { +FMT_CONSTEXPR auto write_padded(OutputIt out, + const basic_format_specs<Char>& 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 <align::type align = align::left, typename OutputIt, typename Char, typename F> -constexpr OutputIt write_padded(OutputIt out, - const basic_format_specs<Char>& specs, - size_t size, F&& f) { +constexpr auto write_padded(OutputIt out, const basic_format_specs<Char>& specs, + size_t size, F&& f) -> OutputIt { return write_padded<align>(out, specs, size, size, f); } -template <typename Char, typename OutputIt> -OutputIt write_bytes(OutputIt out, string_view bytes, - const basic_format_specs<Char>& specs) { - return write_padded(out, specs, bytes.size(), - [bytes](reserve_iterator<OutputIt> it) { - const char* data = bytes.data(); - return copy_str<Char>(data, data + bytes.size(), it); - }); +template <align::type align = align::left, typename Char, typename OutputIt> +FMT_CONSTEXPR auto write_bytes(OutputIt out, string_view bytes, + const basic_format_specs<Char>& specs) + -> OutputIt { + return write_padded<align>( + out, specs, bytes.size(), [bytes](reserve_iterator<OutputIt> it) { + const char* data = bytes.data(); + return copy_str<Char>(data, data + bytes.size(), it); + }); +} + +template <typename Char, typename OutputIt, typename UIntPtr> +auto write_ptr(OutputIt out, UIntPtr value, + const basic_format_specs<Char>* specs) -> OutputIt { + int num_digits = count_digits<4>(value); + auto size = to_unsigned(num_digits) + size_t(2); + auto write = [=](reserve_iterator<OutputIt> it) { + *it++ = static_cast<Char>('0'); + *it++ = static_cast<Char>('x'); + return format_uint<4, Char>(it, value, num_digits); + }; + return specs ? write_padded<align::right>(out, *specs, size, write) + : base_iterator(out, write(reserve(out, size))); } template <typename Char, typename OutputIt> -FMT_CONSTEXPR OutputIt write_char(OutputIt out, Char value, - const basic_format_specs<Char>& specs) { +FMT_CONSTEXPR auto write_char(OutputIt out, Char value, + const basic_format_specs<Char>& specs) + -> OutputIt { return write_padded(out, specs, 1, [=](reserve_iterator<OutputIt> it) { *it++ = value; return it; }); } template <typename Char, typename OutputIt> -FMT_CONSTEXPR OutputIt write(OutputIt out, Char value, - const basic_format_specs<Char>& specs, - locale_ref loc = {}) { - return !specs.type || specs.type == 'c' +FMT_CONSTEXPR auto write(OutputIt out, Char value, + const basic_format_specs<Char>& specs, + locale_ref loc = {}) -> OutputIt { + return check_char_specs(specs) ? write_char(out, value, specs) : write(out, static_cast<int>(value), specs, loc); } @@ -1463,9 +1370,10 @@ template <typename Char> struct write_int_data { // where <digits> are written by write_digits(it). // prefix contains chars in three lower bytes and the size in the fourth byte. template <typename OutputIt, typename Char, typename W> -FMT_CONSTEXPR FMT_INLINE OutputIt -write_int(OutputIt out, int num_digits, unsigned prefix, - const basic_format_specs<Char>& specs, W write_digits) { +FMT_CONSTEXPR FMT_INLINE auto write_int(OutputIt out, int num_digits, + unsigned prefix, + const basic_format_specs<Char>& 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 <typename OutputIt, typename UInt, typename Char> -bool write_int_localized(OutputIt& out, UInt value, unsigned prefix, - const basic_format_specs<Char>& specs, - locale_ref loc) { +auto write_int_localized(OutputIt& out, UInt value, unsigned prefix, + const basic_format_specs<Char>& specs, locale_ref loc) + -> bool { static_assert(std::is_same<uint64_or_128_t<UInt>, UInt>::value, ""); const auto sep_size = 1; - std::string groups = grouping<Char>(loc); - if (groups.empty()) return false; - auto sep = thousands_sep<Char>(loc); - if (!sep) return false; + auto ts = thousands_sep<Char>(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<char>()) { @@ -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<Char> s(&sep, sep_size); + basic_string_view<Char> 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 <typename Char, typename OutputIt, typename T, - FMT_ENABLE_IF(is_integral<T>::value && !std::is_same<T, bool>::value)> -FMT_CONSTEXPR FMT_INLINE OutputIt -write_int(OutputIt out, T value, const basic_format_specs<Char>& specs, - locale_ref loc) { +template <typename UInt> struct write_int_arg { + UInt abs_value; + unsigned prefix; +}; + +template <typename T> +FMT_CONSTEXPR auto make_write_int_arg(T value, sign_t sign) + -> write_int_arg<uint32_or_64_or_128_t<T>> { auto prefix = 0u; auto abs_value = static_cast<uint32_or_64_or_128_t<T>>(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 <typename Char, typename OutputIt, typename T> +FMT_CONSTEXPR FMT_INLINE auto write_int(OutputIt out, write_int_arg<T> arg, + const basic_format_specs<Char>& specs, + locale_ref loc) -> OutputIt { + static_assert(std::is_same<T, uint32_or_64_or_128_t<T>>::value, ""); + auto abs_value = arg.abs_value; + auto prefix = arg.prefix; auto utype = static_cast<unsigned>(specs.type); switch (specs.type) { case 0: @@ -1614,54 +1534,65 @@ template <typename Char, typename OutputIt, typename T, FMT_ENABLE_IF(is_integral<T>::value && !std::is_same<T, bool>::value && std::is_same<OutputIt, buffer_appender<Char>>::value)> -FMT_CONSTEXPR OutputIt write(OutputIt out, T value, - const basic_format_specs<Char>& specs, - locale_ref loc) { - return write_int(out, value, specs, loc); +FMT_CONSTEXPR auto write(OutputIt out, T value, + const basic_format_specs<Char>& 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 <typename Char, typename OutputIt, typename T, FMT_ENABLE_IF(is_integral<T>::value && !std::is_same<T, bool>::value && !std::is_same<OutputIt, buffer_appender<Char>>::value)> -FMT_CONSTEXPR FMT_INLINE OutputIt write(OutputIt out, T value, - const basic_format_specs<Char>& 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<Char>& specs, + locale_ref loc) -> OutputIt { + return write_int(out, make_write_int_arg(value, specs.sign), specs, loc); } -template <typename OutputIt, typename StrChar, typename Char> -FMT_CONSTEXPR OutputIt write(OutputIt out, basic_string_view<StrChar> s, - const basic_format_specs<Char>& specs) { +template <typename Char, typename OutputIt> +FMT_CONSTEXPR auto write(OutputIt out, basic_string_view<Char> s, + const basic_format_specs<Char>& 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<StrChar>(data, size)) - : 0; + auto width = + specs.width != 0 ? compute_width(basic_string_view<Char>(data, size)) : 0; return write_padded(out, specs, size, width, [=](reserve_iterator<OutputIt> it) { return copy_str<Char>(data, data + size, it); }); } template <typename Char, typename OutputIt> -FMT_CONSTEXPR OutputIt write(OutputIt out, - basic_string_view<type_identity_t<Char>> s, - const basic_format_specs<Char>& specs, - locale_ref) { - return write(out, s, specs); // Adapt write to formatter::format. +FMT_CONSTEXPR auto write(OutputIt out, + basic_string_view<type_identity_t<Char>> s, + const basic_format_specs<Char>& specs, locale_ref) + -> OutputIt { + check_string_type_spec(specs.type); + return write(out, s, specs); +} +template <typename Char, typename OutputIt> +FMT_CONSTEXPR auto write(OutputIt out, const Char* s, + const basic_format_specs<Char>& specs, locale_ref) + -> OutputIt { + return check_cstring_type_spec(specs.type) + ? write(out, basic_string_view<Char>(s), specs, {}) + : write_ptr<Char>(out, to_uintptr(s), &specs); } template <typename Char, typename OutputIt> -OutputIt write_nonfinite(OutputIt out, bool isinf, - const basic_format_specs<Char>& specs, - const float_specs& fspecs) { +auto write_nonfinite(OutputIt out, bool isinf, basic_format_specs<Char> 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<Char>('0'); + if (is_zero_fill) specs.fill[0] = static_cast<Char>(' '); return write_padded(out, specs, size, [=](reserve_iterator<OutputIt> it) { if (sign) *it++ = static_cast<Char>(data::signs[sign]); return copy_str<Char>(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 <typename T> -inline int get_significand_size(const dragonbox::decimal_fp<T>& fp) { +inline auto get_significand_size(const dragonbox::decimal_fp<T>& fp) -> int { return count_digits(fp.significand); } template <typename Char, typename OutputIt> -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<Char>(significand, significand + significand_size, out); } template <typename Char, typename OutputIt, typename UInt> -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<Char>(out, significand, significand_size).end; } template <typename Char, typename UInt, FMT_ENABLE_IF(std::is_integral<UInt>::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 <typename OutputIt, typename UInt, typename Char, FMT_ENABLE_IF(!std::is_pointer<remove_cvref_t<OutputIt>>::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<UInt>() + 2]; auto end = write_significand(buffer, significand, significand_size, integral_size, decimal_point); - return detail::copy_str<Char>(buffer, end, out); + return detail::copy_str_noinline<Char>(buffer, end, out); } template <typename OutputIt, typename Char> -inline OutputIt write_significand(OutputIt out, const char* significand, - int significand_size, int integral_size, - Char decimal_point) { - out = detail::copy_str<Char>(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<Char>(significand, + significand + integral_size, out); if (!decimal_point) return out; *out++ = decimal_point; - return detail::copy_str<Char>(significand + integral_size, - significand + significand_size, out); + return detail::copy_str_noinline<Char>(significand + integral_size, + significand + significand_size, out); } template <typename OutputIt, typename DecimalFP, typename Char> -OutputIt write_float(OutputIt out, const DecimalFP& fp, - const basic_format_specs<Char>& specs, float_specs fspecs, - Char decimal_point) { +auto write_float(OutputIt out, const DecimalFP& fp, + const basic_format_specs<Char>& 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<Char>('0'); @@ -1836,8 +1767,8 @@ OutputIt write_float(OutputIt out, const DecimalFP& fp, template <typename Char, typename OutputIt, typename T, FMT_ENABLE_IF(std::is_floating_point<T>::value)> -OutputIt write(OutputIt out, T value, basic_format_specs<Char> specs, - locale_ref loc = {}) { +auto write(OutputIt out, T value, basic_format_specs<Char> 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<Char> 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<align::right>(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<Char> specs, template <typename Char, typename OutputIt, typename T, FMT_ENABLE_IF(is_fast_float<T>::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<std::is_same<T, long double>::value, double, T>; @@ -1910,48 +1842,28 @@ OutputIt write(OutputIt out, T value) { template <typename Char, typename OutputIt, typename T, FMT_ENABLE_IF(std::is_floating_point<T>::value && !is_fast_float<T>::value)> -inline OutputIt write(OutputIt out, T value) { +inline auto write(OutputIt out, T value) -> OutputIt { return write(out, value, basic_format_specs<Char>()); } -template <typename Char, typename OutputIt, typename UIntPtr> -OutputIt write_ptr(OutputIt out, UIntPtr value, - const basic_format_specs<Char>* specs) { - int num_digits = count_digits<4>(value); - auto size = to_unsigned(num_digits) + size_t(2); - auto write = [=](reserve_iterator<OutputIt> it) { - *it++ = static_cast<Char>('0'); - *it++ = static_cast<Char>('x'); - return format_uint<4, Char>(it, value, num_digits); - }; - return specs ? write_padded<align::right>(out, *specs, size, write) - : base_iterator(out, write(reserve(out, size))); -} - template <typename Char, typename OutputIt> -OutputIt write(OutputIt out, monostate) { +auto write(OutputIt out, monostate, basic_format_specs<Char> = {}, + locale_ref = {}) -> OutputIt { FMT_ASSERT(false, ""); return out; } -template <typename Char, typename OutputIt, - FMT_ENABLE_IF(!std::is_same<Char, char>::value)> -OutputIt write(OutputIt out, string_view value) { - auto it = reserve(out, value.size()); - it = copy_str<Char>(value.begin(), value.end(), it); - return base_iterator(out, it); -} - template <typename Char, typename OutputIt> -FMT_CONSTEXPR OutputIt write(OutputIt out, basic_string_view<Char> value) { +FMT_CONSTEXPR auto write(OutputIt out, basic_string_view<Char> value) + -> OutputIt { auto it = reserve(out, value.size()); - it = copy_str<Char>(value.begin(), value.end(), it); + it = copy_str_noinline<Char>(value.begin(), value.end(), it); return base_iterator(out, it); } template <typename Char, typename OutputIt, typename T, FMT_ENABLE_IF(is_string<T>::value)> -constexpr OutputIt write(OutputIt out, const T& value) { +constexpr auto write(OutputIt out, const T& value) -> OutputIt { return write<Char>(out, to_string_view(value)); } @@ -1959,7 +1871,7 @@ template <typename Char, typename OutputIt, typename T, FMT_ENABLE_IF(is_integral<T>::value && !std::is_same<T, bool>::value && !std::is_same<T, Char>::value)> -FMT_CONSTEXPR OutputIt write(OutputIt out, T value) { +FMT_CONSTEXPR auto write(OutputIt out, T value) -> OutputIt { auto abs_value = static_cast<uint32_or_64_or_128_t<T>>(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<T, basic_format_context<OutputIt, Char>>::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<Char>( out, static_cast<typename std::underlying_type<T>::type>(value)); } template <typename Char, typename OutputIt, typename T, FMT_ENABLE_IF(std::is_same<T, bool>::value)> -FMT_CONSTEXPR OutputIt write(OutputIt out, T value, - const basic_format_specs<Char>& specs = {}, - locale_ref = {}) { +FMT_CONSTEXPR auto write(OutputIt out, T value, + const basic_format_specs<Char>& 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 <typename Char, typename OutputIt> -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 <typename Char, typename OutputIt> -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 <typename Char, typename OutputIt, typename T, FMT_ENABLE_IF(std::is_same<T, void>::value)> -OutputIt write(OutputIt out, const T* value, - const basic_format_specs<Char>& specs = {}, locale_ref = {}) { +auto write(OutputIt out, const T* value, + const basic_format_specs<Char>& specs = {}, locale_ref = {}) + -> OutputIt { + check_pointer_type_spec(specs.type, error_handler()); return write_ptr<Char>(out, to_uintptr(value), &specs); } template <typename Char, typename OutputIt, typename T> -auto write(OutputIt out, const T& value) -> typename std::enable_if< - mapped_type_constant<T, basic_format_context<OutputIt, Char>>::value == - type::custom_type, - OutputIt>::type { +FMT_CONSTEXPR auto write(OutputIt out, const T& value) -> + typename std::enable_if< + mapped_type_constant<T, basic_format_context<OutputIt, Char>>::value == + type::custom_type, + OutputIt>::type { using context_type = basic_format_context<OutputIt, Char>; using formatter_type = conditional_t<has_formatter<T, context_type>::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 <typename OutputIt, typename Char> struct default_arg_formatter { - using context = basic_format_context<OutputIt, Char>; +template <typename Char> struct default_arg_formatter { + using iterator = buffer_appender<Char>; + using context = buffer_context<Char>; - OutputIt out; + iterator out; basic_format_args<context> args; locale_ref loc; - template <typename T> OutputIt operator()(T value) { + template <typename T> auto operator()(T value) -> iterator { return write<Char>(out, value); } - - OutputIt operator()(typename basic_format_arg<context>::handle handle) { + auto operator()(typename basic_format_arg<context>::handle h) -> iterator { basic_format_parse_context<Char> parse_ctx({}); - basic_format_context<OutputIt, Char> 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 <typename OutputIt, typename Char, - typename ErrorHandler = error_handler> -class arg_formatter_base { - public: - using iterator = OutputIt; - using char_type = Char; - using format_specs = basic_format_specs<Char>; - - private: - iterator out_; - const 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); - } - - void write(char value) { - auto&& it = reserve(1); - *it++ = value; - } - - template <typename Ch, FMT_ENABLE_IF(std::is_same<Ch, Char>::value)> - void write(Ch value) { - out_ = detail::write<Char>(out_, value); - } - - void write(string_view value) { - auto&& it = reserve(value.size()); - it = copy_str<Char>(value.begin(), value.end(), it); - } - void write(wstring_view value) { - static_assert(std::is_same<Char, wchar_t>::value, ""); - auto&& it = reserve(value.size()); - it = copy_str<Char>(value.begin(), value.end(), it); - } - - template <typename Ch> - void write(const Ch* s, size_t size, const format_specs& specs) { - auto width = - specs.width != 0 ? compute_width(basic_string_view<Ch>(s, size)) : 0; - out_ = write_padded(out_, specs, size, width, - [=](reserve_iterator<OutputIt> it) { - return copy_str<Char>(s, s + size, it); - }); - } - - template <typename Ch> - FMT_CONSTEXPR void write(basic_string_view<Ch> s, - const format_specs& specs = {}) { - out_ = detail::write(out_, s, specs); - } - - void write_pointer(const void* p) { - out_ = write_ptr<char_type>(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<int>(value), - formatter.specs_, formatter.locale_); - } - FMT_CONSTEXPR void on_char() { - formatter.out_ = - detail::write<Char>(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<char_type>(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 <typename T, FMT_ENABLE_IF(is_integral<T>::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<Char>(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 <typename T, FMT_ENABLE_IF(std::is_floating_point<T>::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_; - } +template <typename Char> struct arg_formatter { + using iterator = buffer_appender<Char>; + using context = buffer_context<Char>; - FMT_CONSTEXPR iterator operator()(basic_string_view<Char> value) { - check_string_type_spec(specs_.type, error_handler()); - write(value, specs_); - return out_; - } + iterator out; + const basic_format_specs<Char>& specs; + locale_ref locale; - iterator operator()(const void* value) { - check_pointer_type_spec(specs_.type, error_handler()); - write_pointer(value); - return out_; + template <typename T> + FMT_CONSTEXPR FMT_INLINE auto operator()(T value) -> iterator { + return detail::write(out, value, specs, locale); } -}; - -/** The default argument formatter. */ -template <typename OutputIt, typename Char> -class arg_formatter : public arg_formatter_base<OutputIt, Char> { - private: - using char_type = Char; - using base = arg_formatter_base<OutputIt, Char>; - using context_type = basic_format_context<OutputIt, Char>; - - 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<context_type>::handle) { - // User-defined types are handled separately because they require access to - // the parse context. - return ctx_.out(); + auto operator()(typename basic_format_arg<context>::handle) -> iterator { + // User-defined types are handled separately because they require access + // to the parse context. + return out; } }; -template <typename Context> class custom_formatter { - private: - using char_type = typename Context::char_type; - - basic_format_parse_context<char_type>& parse_ctx_; - Context& ctx_; - - public: - explicit custom_formatter(basic_format_parse_context<char_type>& parse_ctx, - Context& ctx) - : parse_ctx_(parse_ctx), ctx_(ctx) {} +template <typename Char> struct custom_formatter { + basic_format_parse_context<Char>& parse_ctx; + buffer_context<Char>& ctx; - void operator()(typename basic_format_arg<Context>::handle h) const { - h.format(parse_ctx_, ctx_); + void operator()( + typename basic_format_arg<buffer_context<Char>>::handle h) const { + h.format(parse_ctx, ctx); } - template <typename T> void operator()(T) const {} }; @@ -2280,13 +2017,13 @@ template <typename ErrorHandler> class width_checker { explicit FMT_CONSTEXPR width_checker(ErrorHandler& eh) : handler_(eh) {} template <typename T, FMT_ENABLE_IF(is_integer<T>::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<unsigned long long>(value); } template <typename T, FMT_ENABLE_IF(!is_integer<T>::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 <typename ErrorHandler> class precision_checker { explicit FMT_CONSTEXPR precision_checker(ErrorHandler& eh) : handler_(eh) {} template <typename T, FMT_ENABLE_IF(is_integer<T>::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<unsigned long long>(value); } template <typename T, FMT_ENABLE_IF(!is_integer<T>::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,197 +2052,62 @@ template <typename ErrorHandler> class precision_checker { ErrorHandler& handler_; }; -template <typename ErrorHandler> 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 <typename Handler> class specs_checker : public Handler { - private: - numeric_specs_checker<Handler> 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 <template <typename> class Handler, typename FormatArg, typename ErrorHandler> -FMT_CONSTEXPR int get_dynamic_spec(FormatArg arg, ErrorHandler eh) { +FMT_CONSTEXPR auto get_dynamic_spec(FormatArg arg, ErrorHandler eh) -> int { unsigned long long value = visit_format_arg(Handler<ErrorHandler>(eh), arg); if (value > to_unsigned(max_value<int>())) eh.on_error("number is too big"); return static_cast<int>(value); } template <typename Context, typename ID> -FMT_CONSTEXPR typename Context::format_arg get_arg(Context& ctx, ID id) { +FMT_CONSTEXPR auto get_arg(Context& ctx, ID id) -> + typename Context::format_arg { auto arg = ctx.arg(id); if (!arg) ctx.on_error("argument not found"); return arg; } // The standard format specifier handler with checking. -template <typename ParseContext, typename Context> -class specs_handler : public specs_setter<typename Context::char_type> { - public: - using char_type = typename Context::char_type; - - FMT_CONSTEXPR specs_handler(basic_format_specs<char_type>& specs, - ParseContext& parse_ctx, Context& ctx) - : specs_setter<char_type>(specs), - parse_context_(parse_ctx), - context_(ctx) {} - - template <typename Id> FMT_CONSTEXPR void on_dynamic_width(Id arg_id) { - this->specs_.width = get_dynamic_spec<width_checker>( - get_arg(arg_id), context_.error_handler()); - } - - template <typename Id> FMT_CONSTEXPR void on_dynamic_precision(Id arg_id) { - this->specs_.precision = get_dynamic_spec<precision_checker>( - get_arg(arg_id), context_.error_handler()); - } - - void on_error(const char* message) { context_.on_error(message); } - +template <typename Char> class specs_handler : public specs_setter<Char> { private: + basic_format_parse_context<Char>& parse_context_; + buffer_context<Char>& context_; + // This is only needed for compatibility with gcc 4.4. - using format_arg = typename Context::format_arg; + using format_arg = basic_format_arg<buffer_context<Char>>; - FMT_CONSTEXPR format_arg get_arg(auto_id) { + FMT_CONSTEXPR auto get_arg(auto_id) -> format_arg { return detail::get_arg(context_, parse_context_.next_arg_id()); } - FMT_CONSTEXPR format_arg get_arg(int arg_id) { + FMT_CONSTEXPR auto get_arg(int arg_id) -> format_arg { parse_context_.check_arg_id(arg_id); return detail::get_arg(context_, arg_id); } - FMT_CONSTEXPR format_arg get_arg(basic_string_view<char_type> arg_id) { + FMT_CONSTEXPR auto get_arg(basic_string_view<Char> arg_id) -> format_arg { parse_context_.check_arg_id(arg_id); return detail::get_arg(context_, arg_id); } - ParseContext& parse_context_; - Context& context_; -}; - -template <typename OutputIt, typename Char, typename Context> -struct format_handler : detail::error_handler { - basic_format_parse_context<Char> parse_context; - Context context; - - format_handler(OutputIt out, basic_string_view<Char> str, - basic_format_args<Context> format_args, detail::locale_ref loc) - : parse_context(str), context(out, format_args, loc) {} - - void on_text(const Char* begin, const Char* end) { - auto text = basic_string_view<Char>(begin, to_unsigned(end - begin)); - context.advance_to(write<Char>(context.out(), text)); - } + public: + FMT_CONSTEXPR specs_handler(basic_format_specs<Char>& specs, + basic_format_parse_context<Char>& parse_ctx, + buffer_context<Char>& ctx) + : specs_setter<Char>(specs), parse_context_(parse_ctx), context_(ctx) {} - int on_arg_id() { return parse_context.next_arg_id(); } - int on_arg_id(int id) { return parse_context.check_arg_id(id), id; } - int on_arg_id(basic_string_view<Char> id) { - int arg_id = context.arg_id(id); - if (arg_id < 0) on_error("argument not found"); - return arg_id; + template <typename Id> FMT_CONSTEXPR void on_dynamic_width(Id arg_id) { + this->specs_.width = get_dynamic_spec<width_checker>( + get_arg(arg_id), context_.error_handler()); } - FMT_INLINE void on_replacement_field(int id, const Char*) { - auto arg = get_arg(context, id); - context.advance_to(visit_format_arg( - default_arg_formatter<OutputIt, Char>{context.out(), context.args(), - context.locale()}, - arg)); + template <typename Id> FMT_CONSTEXPR void on_dynamic_precision(Id arg_id) { + this->specs_.precision = get_dynamic_spec<precision_checker>( + get_arg(arg_id), context_.error_handler()); } - const Char* on_format_specs(int id, const Char* begin, const Char* end) { - auto arg = get_arg(context, id); - if (arg.type() == type::custom_type) { - advance_to(parse_context, begin); - visit_format_arg(custom_formatter<Context>(parse_context, context), arg); - return parse_context.begin(); - } - auto specs = basic_format_specs<Char>(); - using parse_context_t = basic_format_parse_context<Char>; - specs_checker<specs_handler<parse_context_t, Context>> handler( - specs_handler<parse_context_t, Context>(specs, parse_context, context), - arg.type()); - begin = parse_format_specs(begin, end, handler); - if (begin == end || *begin != '}') on_error("missing '}' in format string"); - context.advance_to( - visit_format_arg(arg_formatter<OutputIt, Char>(context, specs), arg)); - return begin; - } + void on_error(const char* message) { context_.on_error(message); } }; template <template <typename> class Handler, typename Context> @@ -2526,6 +2128,88 @@ FMT_CONSTEXPR void handle_dynamic_spec(int& value, } } +#define FMT_STRING_IMPL(s, base, explicit) \ + [] { \ + /* 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<decltype(s[0])>; \ + FMT_MAYBE_UNUSED FMT_CONSTEXPR explicit \ + operator fmt::basic_string_view<char_type>() const { \ + return fmt::detail_exported::compile_string_to_view<char_type>(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, ) + +#if FMT_USE_USER_DEFINED_LITERALS +template <typename Char> struct udl_formatter { + basic_string_view<Char> str; + + template <typename... T> + auto operator()(T&&... args) const -> std::basic_string<Char> { + return vformat(str, fmt::make_args_checked<T...>(str, args...)); + } +}; + +# if FMT_USE_NONTYPE_TEMPLATE_PARAMETERS +template <typename T, typename Char, size_t N, + fmt::detail_exported::fixed_string<Char, N> Str> +struct statically_named_arg : view { + static constexpr auto name = Str.data; + + const T& value; + statically_named_arg(const T& v) : value(v) {} +}; + +template <typename T, typename Char, size_t N, + fmt::detail_exported::fixed_string<Char, N> Str> +struct is_named_arg<statically_named_arg<T, Char, N, Str>> : std::true_type {}; + +template <typename T, typename Char, size_t N, + fmt::detail_exported::fixed_string<Char, N> Str> +struct is_statically_named_arg<statically_named_arg<T, Char, N, Str>> + : std::true_type {}; + +template <typename Char, size_t N, + fmt::detail_exported::fixed_string<Char, N> Str> +struct udl_arg { + template <typename T> auto operator=(T&& value) const { + return statically_named_arg<T, Char, N, Str>(std::forward<T>(value)); + } +}; +# else +template <typename Char> struct udl_arg { + const Char* str; + + template <typename T> auto operator=(T&& value) const -> named_arg<Char, T> { + return {str, std::forward<T>(value)}; + } +}; +# endif +#endif // FMT_USE_USER_DEFINED_LITERALS + +template <typename Locale, typename Char> +auto vformat(const Locale& loc, basic_string_view<Char> format_str, + basic_format_args<buffer_context<type_identity_t<Char>>> args) + -> std::basic_string<Char> { + basic_memory_buffer<Char> buffer; + detail::vformat_to(buffer, format_str, args, detail::locale_ref(loc)); + return {buffer.data(), buffer.size()}; +} + using format_func = void (*)(detail::buffer<char>&, int, const char*); FMT_API void format_error_code(buffer<char>& out, int error_code, @@ -2533,20 +2217,15 @@ FMT_API void format_error_code(buffer<char>& out, int error_code, FMT_API void report_error(format_func func, int error_code, const char* message) FMT_NOEXCEPT; - FMT_END_DETAIL_NAMESPACE -template <typename OutputIt, typename Char> -using arg_formatter FMT_DEPRECATED_ALIAS = - detail::arg_formatter<OutputIt, Char>; - -FMT_API std::system_error vsystem_error(int error_code, string_view format_str, - format_args args); +FMT_API auto vsystem_error(int error_code, string_view format_str, + format_args args) -> std::system_error; /** \rst Constructs :class:`std::system_error` with a message formatted with - ``fmt::format(message, args...)``. + ``fmt::format(fmt, args...)``. *error_code* is a system error code as given by ``errno``. **Example**:: @@ -2560,10 +2239,10 @@ FMT_API std::system_error vsystem_error(int error_code, string_view format_str, throw fmt::system_error(errno, "cannot open file '{}'", filename); \endrst */ -template <typename... Args> -std::system_error system_error(int error_code, string_view message, - const Args&... args) { - return vsystem_error(error_code, message, make_format_args(args...)); +template <typename... T> +auto system_error(int error_code, format_string<T...> fmt, T&&... args) + -> std::system_error { + return vsystem_error(error_code, fmt, fmt::make_format_args(args...)); } /** @@ -2599,12 +2278,12 @@ class format_int { mutable char buffer_[buffer_size]; char* str_; - template <typename UInt> char* format_unsigned(UInt value) { + template <typename UInt> auto format_unsigned(UInt value) -> char* { auto n = static_cast<detail::uint32_or_64_or_128_t<UInt>>(value); return detail::format_decimal(buffer_, n, buffer_size - 1).begin; } - template <typename Int> char* format_signed(Int value) { + template <typename Int> auto format_signed(Int value) -> char* { auto abs_value = static_cast<detail::uint32_or_64_or_128_t<Int>>(value); bool negative = value < 0; if (negative) abs_value = 0 - abs_value; @@ -2623,7 +2302,7 @@ class format_int { : str_(format_unsigned(value)) {} /** Returns the number of characters written to the output buffer. */ - size_t size() const { + auto size() const -> size_t { return detail::to_unsigned(buffer_ - str_ + buffer_size - 1); } @@ -2631,13 +2310,13 @@ class format_int { Returns a pointer to the output buffer content. No terminating null character is appended. */ - const char* data() const { return str_; } + auto data() const -> const char* { return str_; } /** Returns a pointer to the output buffer content with terminating null character appended. */ - const char* c_str() const { + auto c_str() const -> const char* { buffer_[buffer_size - 1] = '\0'; return str_; } @@ -2647,102 +2326,28 @@ class format_int { Returns the content of the output buffer as an ``std::string``. \endrst */ - std::string str() const { return std::string(str_, size()); } + auto str() const -> std::string { return std::string(str_, size()); } }; -// A formatter specialization for the core types corresponding to detail::type -// constants. template <typename T, typename Char> -struct formatter<T, Char, - enable_if_t<detail::type_constant<T, Char>::value != - detail::type::custom_type>> { - FMT_CONSTEXPR formatter() = default; - - // Parses format specifiers stopping either at the end of the range or at the - // terminating '}'. - template <typename ParseContext> - FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) { - auto begin = ctx.begin(), end = ctx.end(); - if (begin == end) return begin; - using handler_type = detail::dynamic_specs_handler<ParseContext>; - auto type = detail::type_constant<T, Char>::value; - detail::specs_checker<handler_type> handler(handler_type(specs_, ctx), - type); - auto it = detail::parse_format_specs(begin, end, handler); - 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::handle_char_specs( - specs_, detail::char_specs_checker<decltype(eh)>(specs_.type, 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::handle_cstring_type_spec( - specs_.type, detail::cstring_type_checker<decltype(eh)>(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 should be checked in parse functions of - // formatter specializations. - break; - } - return it; - } - - template <typename FormatContext> - FMT_CONSTEXPR FMT_INLINE auto format(const T& val, FormatContext& ctx) const - -> decltype(ctx.out()) { - if (specs_.width_ref.kind != detail::arg_id_kind::none || - specs_.precision_ref.kind != detail::arg_id_kind::none) { - auto specs = specs_; - detail::handle_dynamic_spec<detail::width_checker>(specs.width, - specs.width_ref, ctx); - detail::handle_dynamic_spec<detail::precision_checker>( - specs.precision, specs.precision_ref, ctx); - return detail::write<Char>(ctx.out(), val, specs, ctx.locale()); - } - return detail::write<Char>(ctx.out(), val, specs_, ctx.locale()); +template <typename FormatContext> +FMT_CONSTEXPR FMT_INLINE auto +formatter<T, Char, + enable_if_t<detail::type_constant<T, Char>::value != + detail::type::custom_type>>::format(const T& val, + FormatContext& ctx) + const -> decltype(ctx.out()) { + if (specs_.width_ref.kind != detail::arg_id_kind::none || + specs_.precision_ref.kind != detail::arg_id_kind::none) { + auto specs = specs_; + detail::handle_dynamic_spec<detail::width_checker>(specs.width, + specs.width_ref, ctx); + detail::handle_dynamic_spec<detail::precision_checker>( + specs.precision, specs.precision_ref, ctx); + return detail::write<Char>(ctx.out(), val, specs, ctx.locale()); } - - private: - detail::dynamic_format_specs<Char> specs_; -}; + return detail::write<Char>(ctx.out(), val, specs_, ctx.locale()); +} #define FMT_FORMAT_AS(Type, Base) \ template <typename Char> \ @@ -2796,14 +2401,22 @@ struct formatter<Char[N], Char> : formatter<basic_string_view<Char>, Char> { // }; template <typename Char = char> class dynamic_formatter { private: + detail::dynamic_format_specs<Char> specs_; + const Char* format_str_; + struct null_handler : detail::error_handler { void on_align(align_t) {} - void on_plus() {} - void on_minus() {} - void on_space() {} + void on_sign(sign_t) {} void on_hash() {} }; + template <typename Context> void handle_specs(Context& ctx) { + detail::handle_dynamic_spec<detail::width_checker>(specs_.width, + specs_.width_ref, ctx); + detail::handle_dynamic_spec<detail::precision_checker>( + specs_.precision, specs_.precision_ref, ctx); + } + public: template <typename ParseContext> FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) { @@ -2819,45 +2432,13 @@ template <typename Char = char> class dynamic_formatter { detail::specs_checker<null_handler> checker( null_handler(), detail::mapped_type_constant<T, FormatContext>::value); checker.on_align(specs_.align); - switch (specs_.sign) { - case sign::none: - break; - case sign::plus: - checker.on_plus(); - break; - case sign::minus: - checker.on_minus(); - break; - case sign::space: - checker.on_space(); - break; - } + if (specs_.sign != sign::none) checker.on_sign(specs_.sign); if (specs_.alt) checker.on_hash(); if (specs_.precision >= 0) checker.end_precision(); - using af = detail::arg_formatter<typename FormatContext::iterator, - typename FormatContext::char_type>; - visit_format_arg(af(ctx, specs_), detail::make_arg<FormatContext>(val)); - return ctx.out(); - } - - private: - template <typename Context> void handle_specs(Context& ctx) { - detail::handle_dynamic_spec<detail::width_checker>(specs_.width, - specs_.width_ref, ctx); - detail::handle_dynamic_spec<detail::precision_checker>( - specs_.precision, specs_.precision_ref, ctx); + return detail::write<Char>(ctx.out(), val, specs_, ctx.locale()); } - - detail::dynamic_format_specs<Char> specs_; - const Char* format_str_; }; -template <typename Char, typename ErrorHandler> -FMT_CONSTEXPR void advance_to( - basic_format_parse_context<Char, ErrorHandler>& ctx, const Char* p) { - ctx.advance_to(ctx.begin() + (p - &*ctx.begin())); -} - /** \rst Converts ``p`` to ``const void*`` for pointer formatting. @@ -2867,14 +2448,14 @@ FMT_CONSTEXPR void advance_to( auto s = fmt::format("{}", fmt::ptr(p)); \endrst */ -template <typename T> const void* ptr(T p) { +template <typename T> auto ptr(T p) -> const void* { static_assert(std::is_pointer<T>::value, ""); return detail::bit_cast<const void*>(p); } -template <typename T> const void* ptr(const std::unique_ptr<T>& p) { +template <typename T> auto ptr(const std::unique_ptr<T>& p) -> const void* { return p.get(); } -template <typename T> const void* ptr(const std::shared_ptr<T>& p) { +template <typename T> auto ptr(const std::shared_ptr<T>& p) -> const void* { return p.get(); } @@ -2912,18 +2493,21 @@ template <> struct formatter<bytes> { } }; -template <typename It, typename Sentinel, typename Char> -struct arg_join : detail::view { +template <typename It, typename Sentinel, typename Char = char> +struct join_view : detail::view { It begin; Sentinel end; basic_string_view<Char> sep; - arg_join(It b, Sentinel e, basic_string_view<Char> s) + join_view(It b, Sentinel e, basic_string_view<Char> s) : begin(b), end(e), sep(s) {} }; template <typename It, typename Sentinel, typename Char> -struct formatter<arg_join<It, Sentinel, Char>, Char> { +using arg_join FMT_DEPRECATED_ALIAS = join_view<It, Sentinel, Char>; + +template <typename It, typename Sentinel, typename Char> +struct formatter<join_view<It, Sentinel, Char>, Char> { private: using value_type = typename std::iterator_traits<It>::value_type; using context = buffer_context<Char>; @@ -2939,9 +2523,9 @@ struct formatter<arg_join<It, Sentinel, Char>, Char> { } using formatter_type = - conditional_t<is_formattable<value_type>::value, - formatter<remove_cvref_t<decltype( - map(std::declval<const value_type&>()))>, + conditional_t<is_formattable<value_type, Char>::value, + formatter<remove_cvref_t<decltype(map( + std::declval<const value_type&>()))>, Char>, detail::fallback_formatter<value_type, Char>>; @@ -2954,7 +2538,7 @@ struct formatter<arg_join<It, Sentinel, Char>, Char> { } template <typename FormatContext> - auto format(const arg_join<It, Sentinel, Char>& value, FormatContext& ctx) + auto format(const join_view<It, Sentinel, Char>& value, FormatContext& ctx) -> decltype(ctx.out()) { auto it = value.begin; auto out = ctx.out(); @@ -2971,16 +2555,11 @@ struct formatter<arg_join<It, Sentinel, Char>, Char> { }; /** - Returns an object that formats the iterator range `[begin, end)` with elements - separated by `sep`. + Returns an object that formats the iterator range `[begin, end)` with + elements separated by `sep`. */ template <typename It, typename Sentinel> -arg_join<It, Sentinel, char> join(It begin, Sentinel end, string_view sep) { - return {begin, end, sep}; -} - -template <typename It, typename Sentinel> -arg_join<It, Sentinel, wchar_t> join(It begin, Sentinel end, wstring_view sep) { +auto join(It begin, Sentinel end, string_view sep) -> join_view<It, Sentinel> { return {begin, end, sep}; } @@ -3001,14 +2580,8 @@ arg_join<It, Sentinel, wchar_t> join(It begin, Sentinel end, wstring_view sep) { \endrst */ template <typename Range> -arg_join<detail::iterator_t<Range>, detail::sentinel_t<Range>, char> join( - Range&& range, string_view sep) { - return join(std::begin(range), std::end(range), sep); -} - -template <typename Range> -arg_join<detail::iterator_t<Range>, detail::sentinel_t<Range>, wchar_t> join( - Range&& range, wstring_view sep) { +auto join(Range&& range, string_view sep) + -> join_view<detail::iterator_t<Range>, detail::sentinel_t<Range>> { return join(std::begin(range), std::end(range), sep); } @@ -3024,231 +2597,165 @@ arg_join<detail::iterator_t<Range>, detail::sentinel_t<Range>, wchar_t> join( \endrst */ template <typename T, FMT_ENABLE_IF(!std::is_integral<T>::value)> -inline std::string to_string(const T& value) { - std::string result; +inline auto to_string(const T& value) -> std::string { + auto result = std::string(); detail::write<char>(std::back_inserter(result), value); return result; } template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)> -inline std::string to_string(T value) { - // The buffer should be large enough to store the number including the sign or - // "false" for bool. +inline auto to_string(T value) -> std::string { + // The buffer should be large enough to store the number including the sign + // or "false" for bool. constexpr int max_size = detail::digits10<T>() + 2; char buffer[max_size > 5 ? static_cast<unsigned>(max_size) : 5]; char* begin = buffer; return std::string(begin, detail::write<char>(begin, value)); } -/** - Converts *value* to ``std::wstring`` using the default format for type *T*. - */ -template <typename T> inline std::wstring to_wstring(const T& value) { - return format(FMT_STRING(L"{}"), value); -} - template <typename Char, size_t SIZE> -std::basic_string<Char> to_string(const basic_memory_buffer<Char, SIZE>& buf) { +auto to_string(const basic_memory_buffer<Char, SIZE>& buf) + -> std::basic_string<Char> { auto size = buf.size(); detail::assume(size < std::basic_string<Char>().max_size()); return std::basic_string<Char>(buf.data(), size); } -FMT_MODULE_EXPORT_END +FMT_BEGIN_DETAIL_NAMESPACE template <typename Char> -void detail::vformat_to( - detail::buffer<Char>& buf, basic_string_view<Char> format_str, - basic_format_args<buffer_context<type_identity_t<Char>>> args, - detail::locale_ref loc) { - using iterator = typename buffer_context<Char>::iterator; +void vformat_to( + buffer<Char>& buf, basic_string_view<Char> fmt, + basic_format_args<FMT_BUFFER_CONTEXT(type_identity_t<Char>)> args, + locale_ref loc) { + // workaround for msvc bug regarding name-lookup in module + // link names into function scope + using detail::arg_formatter; + using detail::buffer_appender; + using detail::custom_formatter; + using detail::default_arg_formatter; + using detail::get_arg; + using detail::locale_ref; + using detail::parse_format_specs; + using detail::specs_checker; + using detail::specs_handler; + using detail::to_unsigned; + using detail::type; + using detail::write; auto out = buffer_appender<Char>(buf); - if (format_str.size() == 2 && equal2(format_str.data(), "{}")) { + if (fmt.size() == 2 && equal2(fmt.data(), "{}")) { auto arg = args.get(0); if (!arg) error_handler().on_error("argument not found"); - visit_format_arg(default_arg_formatter<iterator, Char>{out, args, loc}, - arg); + visit_format_arg(default_arg_formatter<Char>{out, args, loc}, arg); return; } - format_handler<iterator, Char, buffer_context<Char>> h(out, format_str, args, - loc); - parse_format_string<false>(format_str, h); -} - -#ifndef FMT_HEADER_ONLY -extern template void detail::vformat_to(detail::buffer<char>&, string_view, - basic_format_args<format_context>, - detail::locale_ref); -namespace detail { - -extern template FMT_API std::string grouping_impl<char>(locale_ref loc); -extern template FMT_API std::string grouping_impl<wchar_t>(locale_ref loc); -extern template FMT_API char thousands_sep_impl<char>(locale_ref loc); -extern template FMT_API wchar_t thousands_sep_impl<wchar_t>(locale_ref loc); -extern template FMT_API char decimal_point_impl(locale_ref loc); -extern template FMT_API wchar_t decimal_point_impl(locale_ref loc); -extern template int format_float<double>(double value, int precision, - float_specs specs, buffer<char>& buf); -extern template int format_float<long double>(long double value, int precision, - float_specs specs, - buffer<char>& buf); -int snprintf_float(float value, int precision, float_specs specs, - buffer<char>& buf) = delete; -extern template int snprintf_float<double>(double value, int precision, - float_specs specs, - buffer<char>& buf); -extern template int snprintf_float<long double>(long double value, - int precision, - float_specs specs, - buffer<char>& buf); -} // namespace detail -#endif - -FMT_MODULE_EXPORT_BEGIN - -template <typename S, typename Char = char_t<S>, - FMT_ENABLE_IF(detail::is_string<S>::value)> -inline void vformat_to( - detail::buffer<Char>& buf, const S& format_str, - basic_format_args<FMT_BUFFER_CONTEXT(type_identity_t<Char>)> args) { - return detail::vformat_to(buf, to_string_view(format_str), args); -} - -template <typename S, typename... Args, size_t SIZE = inline_buffer_size, - typename Char = enable_if_t<detail::is_string<S>::value, char_t<S>>> -inline typename buffer_context<Char>::iterator format_to( - basic_memory_buffer<Char, SIZE>& buf, const S& format_str, Args&&... args) { - const auto& vargs = fmt::make_args_checked<Args...>(format_str, args...); - detail::vformat_to(buf, to_string_view(format_str), vargs); - return detail::buffer_appender<Char>(buf); -} -template <typename OutputIt, typename Char = char> -using format_context_t = basic_format_context<OutputIt, Char>; + struct format_handler : error_handler { + basic_format_parse_context<Char> parse_context; + buffer_context<Char> context; -template <typename OutputIt, typename Char = char> -using format_args_t = basic_format_args<format_context_t<OutputIt, Char>>; + format_handler(buffer_appender<Char> out, basic_string_view<Char> str, + basic_format_args<buffer_context<Char>> args, locale_ref loc) + : parse_context(str), context(out, args, loc) {} -template <typename OutputIt, typename Char = typename OutputIt::value_type> -using format_to_n_context FMT_DEPRECATED_ALIAS = buffer_context<Char>; - -template <typename OutputIt, typename Char = typename OutputIt::value_type> -using format_to_n_args FMT_DEPRECATED_ALIAS = - basic_format_args<buffer_context<Char>>; - -template <typename OutputIt, typename Char, typename... Args> -FMT_DEPRECATED format_arg_store<buffer_context<Char>, Args...> -make_format_to_n_args(const Args&... args) { - return format_arg_store<buffer_context<Char>, Args...>(args...); -} - -#if FMT_COMPILE_TIME_CHECKS -template <typename... Args> struct format_string { - string_view str; - - template <size_t N> consteval format_string(const char (&s)[N]) : str(s) { - if constexpr (detail::count_named_args<Args...>() == 0) { - using checker = detail::format_string_checker<char, detail::error_handler, - remove_cvref_t<Args>...>; - detail::parse_format_string<true>(string_view(s, N), checker(s, {})); + void on_text(const Char* begin, const Char* end) { + auto text = basic_string_view<Char>(begin, to_unsigned(end - begin)); + context.advance_to(write<Char>(context.out(), text)); } - } - - template <typename T, - FMT_ENABLE_IF(std::is_constructible_v<string_view, const T&>)> - format_string(const T& s) : str(s) {} -}; -template <typename... Args> -FMT_INLINE std::string format( - format_string<std::type_identity_t<Args>...> format_str, Args&&... args) { - return detail::vformat(format_str.str, make_format_args(args...)); -} -#endif - -template <typename Char, enable_if_t<(!std::is_same<Char, char>::value), int>> -std::basic_string<Char> detail::vformat( - basic_string_view<Char> format_str, - basic_format_args<buffer_context<type_identity_t<Char>>> args) { - basic_memory_buffer<Char> buffer; - detail::vformat_to(buffer, format_str, args); - return to_string(buffer); -} + FMT_CONSTEXPR auto on_arg_id() -> int { + return parse_context.next_arg_id(); + } + FMT_CONSTEXPR auto on_arg_id(int id) -> int { + return parse_context.check_arg_id(id), id; + } + FMT_CONSTEXPR auto on_arg_id(basic_string_view<Char> id) -> int { + int arg_id = context.arg_id(id); + if (arg_id < 0) on_error("argument not found"); + return arg_id; + } -template <typename Char, FMT_ENABLE_IF(std::is_same<Char, wchar_t>::value)> -void vprint(std::FILE* f, basic_string_view<Char> format_str, - wformat_args args) { - wmemory_buffer buffer; - detail::vformat_to(buffer, format_str, args); - buffer.push_back(L'\0'); - if (std::fputws(buffer.data(), f) == -1) - FMT_THROW(system_error(errno, "cannot write to file")); -} + FMT_INLINE void on_replacement_field(int id, const Char*) { + auto arg = get_arg(context, id); + context.advance_to(visit_format_arg( + default_arg_formatter<Char>{context.out(), context.args(), + context.locale()}, + arg)); + } -template <typename Char, FMT_ENABLE_IF(std::is_same<Char, wchar_t>::value)> -void vprint(basic_string_view<Char> format_str, wformat_args args) { - vprint(stdout, format_str, args); + auto on_format_specs(int id, const Char* begin, const Char* end) + -> const Char* { + auto arg = get_arg(context, id); + if (arg.type() == type::custom_type) { + parse_context.advance_to(parse_context.begin() + + (begin - &*parse_context.begin())); + visit_format_arg(custom_formatter<Char>{parse_context, context}, arg); + return parse_context.begin(); + } + auto specs = basic_format_specs<Char>(); + specs_checker<specs_handler<Char>> handler( + specs_handler<Char>(specs, parse_context, context), arg.type()); + begin = parse_format_specs(begin, end, handler); + if (begin == end || *begin != '}') + on_error("missing '}' in format string"); + auto f = arg_formatter<Char>{context.out(), specs, context.locale()}; + context.advance_to(visit_format_arg(f, arg)); + return begin; + } + }; + detail::parse_format_string<false>(fmt, format_handler(out, fmt, args, loc)); } -FMT_MODULE_EXPORT_END +#ifndef FMT_HEADER_ONLY +extern template FMT_API auto thousands_sep_impl<char>(locale_ref) + -> thousands_sep_result<char>; +extern template FMT_API auto thousands_sep_impl<wchar_t>(locale_ref) + -> thousands_sep_result<wchar_t>; +extern template FMT_API auto decimal_point_impl(locale_ref) -> char; +extern template FMT_API auto decimal_point_impl(locale_ref) -> wchar_t; +extern template auto format_float<double>(double value, int precision, + float_specs specs, buffer<char>& buf) + -> int; +extern template auto format_float<long double>(long double value, int precision, + float_specs specs, + buffer<char>& buf) -> int; +void snprintf_float(float, int, float_specs, buffer<char>&) = delete; +extern template auto snprintf_float<double>(double value, int precision, + float_specs specs, + buffer<char>& buf) -> int; +extern template auto snprintf_float<long double>(long double value, + int precision, + float_specs specs, + buffer<char>& buf) -> int; +#endif // FMT_HEADER_ONLY -#if FMT_USE_NONTYPE_TEMPLATE_PARAMETERS -namespace detail { -template <typename Char, size_t N> struct fixed_string { - constexpr fixed_string(const Char (&str)[N]) { - copy_str<Char, const Char*, Char*>(static_cast<const Char*>(str), str + N, - data); - } - Char data[N]{}; -}; -} // namespace detail -#endif +FMT_END_DETAIL_NAMESPACE #if FMT_USE_USER_DEFINED_LITERALS -namespace detail { -template <typename Char> struct udl_formatter { - basic_string_view<Char> str; +inline namespace literals { +/** + \rst + User-defined literal equivalent of :func:`fmt::arg`. - template <typename... Args> - std::basic_string<Char> operator()(Args&&... args) const { - return format(str, std::forward<Args>(args)...); - } -}; + **Example**:: + using namespace fmt::literals; + fmt::print("Elapsed time: {s:.2f} seconds", "s"_a=1.23); + \endrst + */ # if FMT_USE_NONTYPE_TEMPLATE_PARAMETERS -template <typename T, typename Char, size_t N, fixed_string<Char, N> Str> -struct statically_named_arg : view { - static constexpr auto name = Str.data; - - const T& value; - statically_named_arg(const T& v) : value(v) {} -}; - -template <typename T, typename Char, size_t N, fixed_string<Char, N> Str> -struct is_named_arg<statically_named_arg<T, Char, N, Str>> : std::true_type {}; - -template <typename T, typename Char, size_t N, fixed_string<Char, N> Str> -struct is_statically_named_arg<statically_named_arg<T, Char, N, Str>> - : std::true_type {}; - -template <typename Char, size_t N, fixed_string<Char, N> Str> struct udl_arg { - template <typename T> auto operator=(T&& value) const { - return statically_named_arg<T, Char, N, Str>(std::forward<T>(value)); - } -}; +template <detail_exported::fixed_string Str> +constexpr auto operator""_a() + -> detail::udl_arg<remove_cvref_t<decltype(Str.data[0])>, + sizeof(Str.data) / sizeof(decltype(Str.data[0])), Str> { + return {}; +} # else -template <typename Char> struct udl_arg { - const Char* str; - - template <typename T> named_arg<Char, T> operator=(T&& value) const { - return {str, std::forward<T>(value)}; - } -}; +constexpr auto operator"" _a(const char* s, size_t) -> detail::udl_arg<char> { + return {s}; +} # endif -} // namespace detail -FMT_MODULE_EXPORT_BEGIN -inline namespace literals { /** \rst User-defined literal equivalent of :func:`fmt::format`. @@ -3259,46 +2766,60 @@ inline namespace literals { std::string message = "The answer is {}"_format(42); \endrst */ -constexpr detail::udl_formatter<char> operator"" _format(const char* s, - size_t n) { - return {{s, n}}; -} -constexpr detail::udl_formatter<wchar_t> operator"" _format(const wchar_t* s, - size_t n) { +constexpr auto operator"" _format(const char* s, size_t n) + -> detail::udl_formatter<char> { return {{s, n}}; } +} // namespace literals +#endif // FMT_USE_USER_DEFINED_LITERALS -/** - \rst - User-defined literal equivalent of :func:`fmt::arg`. +template <typename Locale, FMT_ENABLE_IF(detail::is_locale<Locale>::value)> +inline auto vformat(const Locale& loc, string_view fmt, format_args args) + -> std::string { + return detail::vformat(loc, fmt, args); +} - **Example**:: +template <typename Locale, typename... T, + FMT_ENABLE_IF(detail::is_locale<Locale>::value)> +inline auto format(const Locale& loc, format_string<T...> fmt, T&&... args) + -> std::string { + return vformat(loc, string_view(fmt), fmt::make_format_args(args...)); +} - using namespace fmt::literals; - fmt::print("Elapsed time: {s:.2f} seconds", "s"_a=1.23); - \endrst - */ -# if FMT_USE_NONTYPE_TEMPLATE_PARAMETERS -template <detail::fixed_string Str> -constexpr detail::udl_arg<remove_cvref_t<decltype(Str.data[0])>, - sizeof(Str.data) / sizeof(decltype(Str.data[0])), Str> -operator""_a() { - return {}; +template <typename... T, size_t SIZE, typename Allocator> +FMT_DEPRECATED auto format_to(basic_memory_buffer<char, SIZE, Allocator>& buf, + format_string<T...> fmt, T&&... args) + -> appender { + detail::vformat_to(buf, string_view(fmt), fmt::make_format_args(args...)); + return appender(buf); } -# else -constexpr detail::udl_arg<char> operator"" _a(const char* s, size_t) { - return {s}; + +template <typename OutputIt, typename Locale, + FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, char>::value&& + detail::is_locale<Locale>::value)> +auto vformat_to(OutputIt out, const Locale& loc, string_view fmt, + format_args args) -> OutputIt { + using detail::get_buffer; + auto&& buf = get_buffer<char>(out); + detail::vformat_to(buf, fmt, args, detail::locale_ref(loc)); + return detail::get_iterator(buf); } -constexpr detail::udl_arg<wchar_t> operator"" _a(const wchar_t* s, size_t) { - return {s}; + +template <typename OutputIt, typename Locale, typename... T, + FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, char>::value&& + detail::is_locale<Locale>::value)> +FMT_INLINE auto format_to(OutputIt out, const Locale& loc, + format_string<T...> fmt, T&&... args) -> OutputIt { + return vformat_to(out, loc, fmt, fmt::make_format_args(args...)); } -# endif -} // namespace literals FMT_MODULE_EXPORT_END -#endif // FMT_USE_USER_DEFINED_LITERALS FMT_END_NAMESPACE +#ifdef FMT_DEPRECATED_INCLUDE_XCHAR +# include "xchar.h" +#endif + #ifdef FMT_HEADER_ONLY # define FMT_FUNC inline # include "format-inl.h" diff --git a/deps/fmt/include/fmt/locale.h b/deps/fmt/include/fmt/locale.h index e79140a1a1..7571b5261b 100644 --- a/deps/fmt/include/fmt/locale.h +++ b/deps/fmt/include/fmt/locale.h @@ -1,67 +1,2 @@ -// Formatting library for C++ - std::locale support -// -// Copyright (c) 2012 - present, Victor Zverovich -// All rights reserved. -// -// For the license information refer to format.h. - -#ifndef FMT_LOCALE_H_ -#define FMT_LOCALE_H_ - -#include <locale> - -#include "format.h" - -FMT_BEGIN_NAMESPACE - -namespace detail { -template <typename Char> -std::basic_string<Char> vformat( - const std::locale& loc, basic_string_view<Char> format_str, - basic_format_args<buffer_context<type_identity_t<Char>>> args) { - basic_memory_buffer<Char> buffer; - detail::vformat_to(buffer, format_str, args, detail::locale_ref(loc)); - return fmt::to_string(buffer); -} -} // namespace detail - -FMT_MODULE_EXPORT_BEGIN - -template <typename S, typename Char = char_t<S>> -inline std::basic_string<Char> vformat( - const std::locale& loc, const S& format_str, - basic_format_args<buffer_context<type_identity_t<Char>>> args) { - return detail::vformat(loc, to_string_view(format_str), args); -} - -template <typename S, typename... Args, typename Char = char_t<S>> -inline std::basic_string<Char> format(const std::locale& loc, - const S& format_str, Args&&... args) { - return detail::vformat(loc, to_string_view(format_str), - fmt::make_args_checked<Args...>(format_str, args...)); -} - -template <typename S, typename OutputIt, typename... Args, - typename Char = char_t<S>, - FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value)> -inline OutputIt vformat_to( - OutputIt out, const std::locale& loc, const S& format_str, - basic_format_args<buffer_context<type_identity_t<Char>>> args) { - decltype(detail::get_buffer<Char>(out)) buf(detail::get_buffer_init(out)); - vformat_to(buf, to_string_view(format_str), args, detail::locale_ref(loc)); - return detail::get_iterator(buf); -} - -template <typename OutputIt, typename S, typename... Args, - bool enable = detail::is_output_iterator<OutputIt, char_t<S>>::value> -inline auto format_to(OutputIt out, const std::locale& loc, const S& format_str, - Args&&... args) -> - typename std::enable_if<enable, OutputIt>::type { - const auto& vargs = fmt::make_args_checked<Args...>(format_str, args...); - return vformat_to(out, loc, to_string_view(format_str), vargs); -} - -FMT_MODULE_EXPORT_END -FMT_END_NAMESPACE - -#endif // FMT_LOCALE_H_ +#include "xchar.h" +#warning fmt/locale.h is deprecated, include fmt/format.h or fmt/xchar.h instead diff --git a/deps/fmt/include/fmt/os.h b/deps/fmt/include/fmt/os.h index 1a8833def4..f6c0f32985 100644 --- a/deps/fmt/include/fmt/os.h +++ b/deps/fmt/include/fmt/os.h @@ -70,6 +70,7 @@ #define FMT_RETRY(result, expression) FMT_RETRY_VAL(result, expression, -1) FMT_BEGIN_NAMESPACE +FMT_MODULE_EXPORT_BEGIN /** \rst @@ -128,7 +129,8 @@ template <typename Char> struct formatter<std::error_code, Char> { FMT_CONSTEXPR auto format(const std::error_code& ec, FormatContext& ctx) const -> decltype(ctx.out()) { auto out = ctx.out(); - out = detail::write<Char>(out, to_string_view(ec.category().name())); + out = detail::write_bytes(out, ec.category().name(), + basic_format_specs<Char>()); out = detail::write<Char>(out, Char(':')); out = detail::write<Char>(out, ec.value()); return out; @@ -138,7 +140,7 @@ template <typename Char> struct formatter<std::error_code, Char> { #ifdef _WIN32 FMT_API const std::error_category& system_category() FMT_NOEXCEPT; -namespace detail { +FMT_BEGIN_DETAIL_NAMESPACE // A converter from UTF-16 to UTF-8. // It is only provided for Windows since other systems support UTF-8 natively. class utf16_to_utf8 { @@ -147,7 +149,7 @@ class utf16_to_utf8 { public: utf16_to_utf8() {} - FMT_API explicit utf16_to_utf8(wstring_view s); + FMT_API explicit utf16_to_utf8(basic_string_view<wchar_t> s); operator string_view() const { return string_view(&buffer_[0], size()); } size_t size() const { return buffer_.size() - 1; } const char* c_str() const { return &buffer_[0]; } @@ -156,12 +158,12 @@ class utf16_to_utf8 { // Performs conversion returning a system error code instead of // throwing exception on conversion error. This method may still throw // in case of memory allocation error. - FMT_API int convert(wstring_view s); + FMT_API int convert(basic_string_view<wchar_t> s); }; FMT_API void format_windows_error(buffer<char>& out, int error_code, const char* message) FMT_NOEXCEPT; -} // namespace detail +FMT_END_DETAIL_NAMESPACE FMT_API std::system_error vwindows_error(int error_code, string_view format_str, format_args args); @@ -197,7 +199,7 @@ FMT_API std::system_error vwindows_error(int error_code, string_view format_str, template <typename... Args> std::system_error windows_error(int error_code, string_view message, const Args&... args) { - return vwindows_error(error_code, message, make_format_args(args...)); + return vwindows_error(error_code, message, fmt::make_format_args(args...)); } // Reports a Windows error without throwing an exception. @@ -268,7 +270,7 @@ class buffered_file { template <typename... Args> inline void print(string_view format_str, const Args&... args) { - vprint(format_str, make_format_args(args...)); + vprint(format_str, fmt::make_format_args(args...)); } }; @@ -360,7 +362,7 @@ class file { // Returns the memory page size. long getpagesize(); -namespace detail { +FMT_BEGIN_DETAIL_NAMESPACE struct buffer_size { buffer_size() = default; @@ -389,12 +391,13 @@ struct ostream_params { this->buffer_size = bs.value; } }; -} // namespace detail -static constexpr detail::buffer_size buffer_size; +FMT_END_DETAIL_NAMESPACE + +constexpr detail::buffer_size buffer_size; /** A fast output stream which is not thread-safe. */ -class ostream final : private detail::buffer<char> { +class FMT_API ostream final : private detail::buffer<char> { private: file file_; @@ -404,7 +407,7 @@ class ostream final : private detail::buffer<char> { clear(); } - FMT_API void grow(size_t) override final; + void grow(size_t) override; ostream(cstring_view path, const detail::ostream_params& params) : file_(path, params.oflag) { @@ -432,13 +435,12 @@ class ostream final : private detail::buffer<char> { } /** - Formats ``args`` according to specifications in ``format_str`` and writes - the output to the file. + Formats ``args`` according to specifications in ``fmt`` and writes the + output to the file. */ - template <typename S, typename... Args> - void print(const S& format_str, Args&&... args) { - format_to(detail::buffer_appender<char>(*this), format_str, - std::forward<Args>(args)...); + template <typename... T> void print(format_string<T...> fmt, T&&... args) { + vformat_to(detail::buffer_appender<char>(*this), fmt, + fmt::make_format_args(args...)); } }; @@ -507,6 +509,7 @@ class locale { }; using Locale FMT_DEPRECATED_ALIAS = locale; #endif // FMT_LOCALE +FMT_MODULE_EXPORT_END FMT_END_NAMESPACE #endif // FMT_OS_H_ diff --git a/deps/fmt/include/fmt/printf.h b/deps/fmt/include/fmt/printf.h index c65fe4049f..3a3cd15283 100644 --- a/deps/fmt/include/fmt/printf.h +++ b/deps/fmt/include/fmt/printf.h @@ -15,7 +15,49 @@ #include "format.h" FMT_BEGIN_NAMESPACE -namespace detail { +FMT_MODULE_EXPORT_BEGIN + +template <typename T> struct printf_formatter { printf_formatter() = delete; }; + +template <typename Char> +class basic_printf_parse_context : public basic_format_parse_context<Char> { + using basic_format_parse_context<Char>::basic_format_parse_context; +}; + +template <typename OutputIt, typename Char> class basic_printf_context { + private: + OutputIt out_; + basic_format_args<basic_printf_context> args_; + + public: + using char_type = Char; + using format_arg = basic_format_arg<basic_printf_context>; + using parse_context_type = basic_printf_parse_context<Char>; + template <typename T> using formatter_type = printf_formatter<T>; + + /** + \rst + Constructs a ``printf_context`` object. References to the arguments are + stored in the context object so make sure they have appropriate lifetimes. + \endrst + */ + basic_printf_context(OutputIt out, + basic_format_args<basic_printf_context> args) + : out_(out), args_(args) {} + + OutputIt out() { return out_; } + void advance_to(OutputIt it) { out_ = it; } + + detail::locale_ref locale() { return {}; } + + format_arg arg(int id) const { return args_.get(id); } + + FMT_CONSTEXPR void on_error(const char* message) { + detail::error_handler().on_error(message); + } +}; + +FMT_BEGIN_DETAIL_NAMESPACE // Checks if a value fits in int - used to avoid warnings about comparing // signed and unsigned integers. @@ -179,65 +221,34 @@ template <typename Char> class printf_width_handler { } }; -template <typename Char, typename Context> -void vprintf(buffer<Char>& buf, basic_string_view<Char> format, - basic_format_args<Context> args) { - Context(buffer_appender<Char>(buf), format, args).format(); -} -} // namespace detail - -// For printing into memory_buffer. -template <typename Char, typename Context> -FMT_DEPRECATED void printf(detail::buffer<Char>& buf, - basic_string_view<Char> format, - basic_format_args<Context> args) { - return detail::vprintf(buf, format, args); -} -using detail::vprintf; - -FMT_MODULE_EXPORT_BEGIN - -template <typename Char> -class basic_printf_parse_context : public basic_format_parse_context<Char> { - using basic_format_parse_context<Char>::basic_format_parse_context; -}; -template <typename OutputIt, typename Char> class basic_printf_context; - -FMT_MODULE_EXPORT_END - -/** - \rst - The ``printf`` argument formatter. - \endrst - */ +// The ``printf`` argument formatter. template <typename OutputIt, typename Char> -class printf_arg_formatter : public detail::arg_formatter_base<OutputIt, Char> { +class printf_arg_formatter : public arg_formatter<Char> { private: - using base = detail::arg_formatter_base<OutputIt, Char>; + using base = arg_formatter<Char>; using context_type = basic_printf_context<OutputIt, Char>; - using format_specs = typename base::format_specs; + using format_specs = basic_format_specs<Char>; context_type& context_; OutputIt write_null_pointer(bool is_string = false) { - auto s = this->specs(); + auto s = this->specs; s.type = 0; - return detail::write(this->out(), - string_view(is_string ? "(null)" : "(nil)"), s); + return write_bytes(this->out, is_string ? "(null)" : "(nil)", s); } public: - printf_arg_formatter(OutputIt iter, format_specs& specs, context_type& ctx) - : base(iter, specs, detail::locale_ref()), context_(ctx) {} + printf_arg_formatter(OutputIt iter, format_specs& s, context_type& ctx) + : base{iter, s, locale_ref()}, context_(ctx) {} OutputIt operator()(monostate value) { return base::operator()(value); } - template <typename T, FMT_ENABLE_IF(fmt::detail::is_integral<T>::value)> + template <typename T, FMT_ENABLE_IF(detail::is_integral<T>::value)> OutputIt operator()(T value) { // MSVC2013 fails to compile separate overloads for bool and Char so use // std::is_same instead. if (std::is_same<T, Char>::value) { - format_specs fmt_specs = this->specs(); + format_specs fmt_specs = this->specs; if (fmt_specs.type && fmt_specs.type != 'c') return (*this)(static_cast<int>(value)); fmt_specs.sign = sign::none; @@ -247,8 +258,7 @@ class printf_arg_formatter : public detail::arg_formatter_base<OutputIt, Char> { // ignored for non-numeric types if (fmt_specs.align == align::none || fmt_specs.align == align::numeric) fmt_specs.align = align::right; - return detail::write<Char>(this->out(), static_cast<Char>(value), - fmt_specs); + return write<Char>(this->out, static_cast<Char>(value), fmt_specs); } return base::operator()(value); } @@ -261,13 +271,13 @@ class printf_arg_formatter : public detail::arg_formatter_base<OutputIt, Char> { /** Formats a null-terminated C string. */ OutputIt operator()(const char* value) { if (value) return base::operator()(value); - return write_null_pointer(this->specs().type != 'p'); + return write_null_pointer(this->specs.type != 'p'); } /** Formats a null-terminated wide C string. */ OutputIt operator()(const wchar_t* value) { if (value) return base::operator()(value); - return write_null_pointer(this->specs().type != 'p'); + return write_null_pointer(this->specs.type != 'p'); } OutputIt operator()(basic_string_view<Char> value) { @@ -281,87 +291,16 @@ class printf_arg_formatter : public detail::arg_formatter_base<OutputIt, Char> { /** Formats an argument of a custom (user-defined) type. */ OutputIt operator()(typename basic_format_arg<context_type>::handle handle) { - handle.format(context_.parse_context(), context_); - return this->out(); - } -}; - -template <typename T> struct printf_formatter { - printf_formatter() = delete; - - template <typename ParseContext> - auto parse(ParseContext& ctx) -> decltype(ctx.begin()) { - return ctx.begin(); - } - - template <typename FormatContext> - auto format(const T&, FormatContext& ctx) -> decltype(ctx.out()) { - return ctx.out(); - } -}; - -/** - This template formats data and writes the output through an output iterator. - */ -template <typename OutputIt, typename Char> class basic_printf_context { - public: - /** The character type for the output. */ - using char_type = Char; - using iterator = OutputIt; - using format_arg = basic_format_arg<basic_printf_context>; - using parse_context_type = basic_printf_parse_context<Char>; - template <typename T> using formatter_type = printf_formatter<T>; - - private: - using format_specs = basic_format_specs<char_type>; - - OutputIt out_; - basic_format_args<basic_printf_context> args_; - parse_context_type parse_ctx_; - - static void parse_flags(format_specs& specs, const Char*& it, - const Char* end); - - // Returns the argument with specified index or, if arg_index is -1, the next - // argument. - format_arg get_arg(int arg_index = -1); - - // Parses argument index, flags and width and returns the argument index. - int parse_header(const Char*& it, const Char* end, format_specs& specs); - - public: - /** - \rst - Constructs a ``printf_context`` object. References to the arguments are - stored in the context object so make sure they have appropriate lifetimes. - \endrst - */ - basic_printf_context(OutputIt out, basic_string_view<char_type> format_str, - basic_format_args<basic_printf_context> args) - : out_(out), args_(args), parse_ctx_(format_str) {} - - OutputIt out() { return out_; } - void advance_to(OutputIt it) { out_ = it; } - - detail::locale_ref locale() { return {}; } - - format_arg arg(int id) const { return args_.get(id); } - - parse_context_type& parse_context() { return parse_ctx_; } - - FMT_CONSTEXPR void on_error(const char* message) { - parse_ctx_.on_error(message); + auto parse_ctx = + basic_printf_parse_context<Char>(basic_string_view<Char>()); + handle.format(parse_ctx, context_); + return this->out; } - - /** Formats stored arguments and writes the output to the range. */ - template <typename ArgFormatter = printf_arg_formatter<OutputIt, Char>> - OutputIt format(); }; -template <typename OutputIt, typename Char> -void basic_printf_context<OutputIt, Char>::parse_flags(format_specs& specs, - const Char*& it, - const Char* end) { +template <typename Char> +void parse_flags(basic_format_specs<Char>& specs, const Char*& it, + const Char* end) { for (; it != end; ++it) { switch (*it) { case '-': @@ -387,35 +326,24 @@ void basic_printf_context<OutputIt, Char>::parse_flags(format_specs& specs, } } -template <typename OutputIt, typename Char> -typename basic_printf_context<OutputIt, Char>::format_arg -basic_printf_context<OutputIt, Char>::get_arg(int arg_index) { - if (arg_index < 0) - arg_index = parse_ctx_.next_arg_id(); - else - parse_ctx_.check_arg_id(--arg_index); - return detail::get_arg(*this, arg_index); -} - -template <typename OutputIt, typename Char> -int basic_printf_context<OutputIt, Char>::parse_header(const Char*& it, - const Char* end, - format_specs& specs) { +template <typename Char, typename GetArg> +int parse_header(const Char*& it, const Char* end, + basic_format_specs<Char>& specs, GetArg get_arg) { int arg_index = -1; - char_type c = *it; + Char c = *it; if (c >= '0' && c <= '9') { // Parse an argument index (if followed by '$') or a width possibly // preceded with '0' flag(s). - detail::error_handler eh; - int value = parse_nonnegative_int(it, end, eh); + int value = parse_nonnegative_int(it, end, -1); if (it != end && *it == '$') { // value is an argument index ++it; - arg_index = value; + arg_index = value != -1 ? value : max_value<int>(); } else { if (c == '0') specs.fill[0] = '0'; if (value != 0) { // Nonzero value means that we parsed width and don't need to // parse it or flags again, so return now. + if (value == -1) FMT_THROW(format_error("number is too big")); specs.width = value; return arg_index; } @@ -425,30 +353,44 @@ int basic_printf_context<OutputIt, Char>::parse_header(const Char*& it, // Parse width. if (it != end) { if (*it >= '0' && *it <= '9') { - detail::error_handler eh; - specs.width = parse_nonnegative_int(it, end, eh); + specs.width = parse_nonnegative_int(it, end, -1); + if (specs.width == -1) FMT_THROW(format_error("number is too big")); } else if (*it == '*') { ++it; specs.width = static_cast<int>(visit_format_arg( - detail::printf_width_handler<char_type>(specs), get_arg())); + detail::printf_width_handler<Char>(specs), get_arg(-1))); } } return arg_index; } -template <typename OutputIt, typename Char> -template <typename ArgFormatter> -OutputIt basic_printf_context<OutputIt, Char>::format() { - auto out = this->out(); - const Char* start = parse_ctx_.begin(); - const Char* end = parse_ctx_.end(); +template <typename Char, typename Context> +void vprintf(buffer<Char>& buf, basic_string_view<Char> format, + basic_format_args<Context> args) { + using OutputIt = buffer_appender<Char>; + auto out = OutputIt(buf); + auto context = basic_printf_context<OutputIt, Char>(out, args); + auto parse_ctx = basic_printf_parse_context<Char>(format); + + // Returns the argument with specified index or, if arg_index is -1, the next + // argument. + auto get_arg = [&](int arg_index) { + if (arg_index < 0) + arg_index = parse_ctx.next_arg_id(); + else + parse_ctx.check_arg_id(--arg_index); + return detail::get_arg(context, arg_index); + }; + + const Char* start = parse_ctx.begin(); + const Char* end = parse_ctx.end(); auto it = start; while (it != end) { if (!detail::find<false, Char>(it, end, '%', it)) { it = end; // detail::find leaves it == nullptr if it doesn't find '%' break; } - char_type c = *it++; + Char c = *it++; if (it != end && *it == c) { out = detail::write( out, basic_string_view<Char>(start, detail::to_unsigned(it - start))); @@ -458,30 +400,29 @@ OutputIt basic_printf_context<OutputIt, Char>::format() { out = detail::write(out, basic_string_view<Char>( start, detail::to_unsigned(it - 1 - start))); - format_specs specs; + basic_format_specs<Char> specs; specs.align = align::right; // Parse argument index, flags and width. - int arg_index = parse_header(it, end, specs); - if (arg_index == 0) on_error("argument not found"); + int arg_index = parse_header(it, end, specs, get_arg); + if (arg_index == 0) parse_ctx.on_error("argument not found"); // Parse precision. if (it != end && *it == '.') { ++it; c = it != end ? *it : 0; if ('0' <= c && c <= '9') { - detail::error_handler eh; - specs.precision = parse_nonnegative_int(it, end, eh); + specs.precision = parse_nonnegative_int(it, end, 0); } else if (c == '*') { ++it; specs.precision = static_cast<int>( - visit_format_arg(detail::printf_precision_handler(), get_arg())); + visit_format_arg(detail::printf_precision_handler(), get_arg(-1))); } else { specs.precision = 0; } } - format_arg arg = get_arg(arg_index); + auto arg = get_arg(arg_index); // For d, i, o, u, x, and X conversion specifiers, if a precision is // specified, the '0' flag is ignored if (specs.precision >= 0 && arg.is_integral()) @@ -491,9 +432,10 @@ OutputIt basic_printf_context<OutputIt, Char>::format() { auto str = visit_format_arg(detail::get_cstring<Char>(), arg); auto str_end = str + specs.precision; auto nul = std::find(str, str_end, Char()); - arg = detail::make_arg<basic_printf_context>(basic_string_view<Char>( - str, - detail::to_unsigned(nul != str_end ? nul - str : specs.precision))); + arg = detail::make_arg<basic_printf_context<OutputIt, Char>>( + basic_string_view<Char>( + str, detail::to_unsigned(nul != str_end ? nul - str + : specs.precision))); } if (specs.alt && visit_format_arg(detail::is_zero_int(), arg)) specs.alt = false; @@ -507,7 +449,7 @@ OutputIt basic_printf_context<OutputIt, Char>::format() { // Parse length and convert the argument to the required type. c = it != end ? *it++ : 0; - char_type t = it != end ? *it : 0; + Char t = it != end ? *it : 0; using detail::convert_arg; switch (c) { case 'h': @@ -557,8 +499,9 @@ OutputIt basic_printf_context<OutputIt, Char>::format() { specs.type = 'd'; break; case 'c': - visit_format_arg(detail::char_converter<basic_printf_context>(arg), - arg); + visit_format_arg( + detail::char_converter<basic_printf_context<OutputIt, Char>>(arg), + arg); break; } } @@ -566,13 +509,12 @@ OutputIt basic_printf_context<OutputIt, Char>::format() { start = it; // Format argument. - out = visit_format_arg(ArgFormatter(out, specs, *this), arg); + out = visit_format_arg( + detail::printf_arg_formatter<OutputIt, Char>(out, specs, context), arg); } - return detail::write( - out, basic_string_view<Char>(start, detail::to_unsigned(it - start))); + detail::write(out, basic_string_view<Char>(start, to_unsigned(it - start))); } - -FMT_MODULE_EXPORT_BEGIN +FMT_END_DETAIL_NAMESPACE template <typename Char> using basic_printf_context_t = @@ -590,9 +532,9 @@ using wprintf_args = basic_format_args<wprintf_context>; arguments and can be implicitly converted to `~fmt::printf_args`. \endrst */ -template <typename... Args> -inline format_arg_store<printf_context, Args...> make_printf_args( - const Args&... args) { +template <typename... T> +inline auto make_printf_args(const T&... args) + -> format_arg_store<printf_context, T...> { return {args...}; } @@ -602,18 +544,19 @@ inline format_arg_store<printf_context, Args...> make_printf_args( arguments and can be implicitly converted to `~fmt::wprintf_args`. \endrst */ -template <typename... Args> -inline format_arg_store<wprintf_context, Args...> make_wprintf_args( - const Args&... args) { +template <typename... T> +inline auto make_wprintf_args(const T&... args) + -> format_arg_store<wprintf_context, T...> { return {args...}; } template <typename S, typename Char = char_t<S>> -inline std::basic_string<Char> vsprintf( - const S& format, - basic_format_args<basic_printf_context_t<type_identity_t<Char>>> args) { +inline auto vsprintf( + const S& fmt, + basic_format_args<basic_printf_context_t<type_identity_t<Char>>> args) + -> std::basic_string<Char> { basic_memory_buffer<Char> buffer; - vprintf(buffer, to_string_view(format), args); + vprintf(buffer, to_string_view(fmt), args); return to_string(buffer); } @@ -626,19 +569,20 @@ inline std::basic_string<Char> vsprintf( std::string message = fmt::sprintf("The answer is %d", 42); \endrst */ -template <typename S, typename... Args, +template <typename S, typename... T, typename Char = enable_if_t<detail::is_string<S>::value, char_t<S>>> -inline std::basic_string<Char> sprintf(const S& format, const Args&... args) { +inline auto sprintf(const S& fmt, const T&... args) -> std::basic_string<Char> { using context = basic_printf_context_t<Char>; - return vsprintf(to_string_view(format), make_format_args<context>(args...)); + return vsprintf(to_string_view(fmt), fmt::make_format_args<context>(args...)); } template <typename S, typename Char = char_t<S>> -inline int vfprintf( - std::FILE* f, const S& format, - basic_format_args<basic_printf_context_t<type_identity_t<Char>>> args) { +inline auto vfprintf( + std::FILE* f, const S& fmt, + basic_format_args<basic_printf_context_t<type_identity_t<Char>>> args) + -> int { basic_memory_buffer<Char> buffer; - vprintf(buffer, to_string_view(format), args); + vprintf(buffer, to_string_view(fmt), args); size_t size = buffer.size(); return std::fwrite(buffer.data(), sizeof(Char), size, f) < size ? -1 @@ -654,19 +598,19 @@ inline int vfprintf( fmt::fprintf(stderr, "Don't %s!", "panic"); \endrst */ -template <typename S, typename... Args, - typename Char = enable_if_t<detail::is_string<S>::value, char_t<S>>> -inline int fprintf(std::FILE* f, const S& format, const Args&... args) { +template <typename S, typename... T, typename Char = char_t<S>> +inline auto fprintf(std::FILE* f, const S& fmt, const T&... args) -> int { using context = basic_printf_context_t<Char>; - return vfprintf(f, to_string_view(format), - make_format_args<context>(args...)); + return vfprintf(f, to_string_view(fmt), + fmt::make_format_args<context>(args...)); } template <typename S, typename Char = char_t<S>> -inline int vprintf( - const S& format, - basic_format_args<basic_printf_context_t<type_identity_t<Char>>> args) { - return vfprintf(stdout, to_string_view(format), args); +inline auto vprintf( + const S& fmt, + basic_format_args<basic_printf_context_t<type_identity_t<Char>>> args) + -> int { + return vfprintf(stdout, to_string_view(fmt), args); } /** @@ -678,51 +622,28 @@ inline int vprintf( fmt::printf("Elapsed time: %.2f seconds", 1.23); \endrst */ -template <typename S, typename... Args, - FMT_ENABLE_IF(detail::is_string<S>::value)> -inline int printf(const S& format_str, const Args&... args) { - using context = basic_printf_context_t<char_t<S>>; - return vprintf(to_string_view(format_str), - make_format_args<context>(args...)); +template <typename S, typename... T, FMT_ENABLE_IF(detail::is_string<S>::value)> +inline auto printf(const S& fmt, const T&... args) -> int { + return vprintf( + to_string_view(fmt), + fmt::make_format_args<basic_printf_context_t<char_t<S>>>(args...)); } template <typename S, typename Char = char_t<S>> -inline int vfprintf( - std::basic_ostream<Char>& os, const S& format, - basic_format_args<basic_printf_context_t<type_identity_t<Char>>> args) { +FMT_DEPRECATED auto vfprintf( + std::basic_ostream<Char>& os, const S& fmt, + basic_format_args<basic_printf_context_t<type_identity_t<Char>>> args) + -> int { basic_memory_buffer<Char> buffer; - vprintf(buffer, to_string_view(format), args); + vprintf(buffer, to_string_view(fmt), args); os.write(buffer.data(), static_cast<std::streamsize>(buffer.size())); return static_cast<int>(buffer.size()); } - -/** Formats arguments and writes the output to the range. */ -template <typename ArgFormatter, typename Char, - typename Context = - basic_printf_context<typename ArgFormatter::iterator, Char>> -typename ArgFormatter::iterator vprintf( - detail::buffer<Char>& out, basic_string_view<Char> format_str, - basic_format_args<type_identity_t<Context>> args) { - typename ArgFormatter::iterator iter(out); - Context(iter, format_str, args).template format<ArgFormatter>(); - return iter; -} - -/** - \rst - Prints formatted data to the stream *os*. - - **Example**:: - - fmt::fprintf(cerr, "Don't %s!", "panic"); - \endrst - */ -template <typename S, typename... Args, typename Char = char_t<S>> -inline int fprintf(std::basic_ostream<Char>& os, const S& format_str, - const Args&... args) { - using context = basic_printf_context_t<Char>; - return vfprintf(os, to_string_view(format_str), - make_format_args<context>(args...)); +template <typename S, typename... T, typename Char = char_t<S>> +FMT_DEPRECATED auto fprintf(std::basic_ostream<Char>& os, const S& fmt, + const T&... args) -> int { + return vfprintf(os, to_string_view(fmt), + fmt::make_format_args<basic_printf_context_t<Char>>(args...)); } FMT_MODULE_EXPORT_END diff --git a/deps/fmt/include/fmt/ranges.h b/deps/fmt/include/fmt/ranges.h index 367d7a6ba3..f0390df211 100644 --- a/deps/fmt/include/fmt/ranges.h +++ b/deps/fmt/include/fmt/ranges.h @@ -19,15 +19,7 @@ FMT_BEGIN_NAMESPACE -template <typename Char> struct formatting_base { - template <typename ParseContext> - FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) { - return ctx.begin(); - } -}; - -template <typename Char, typename Enable = void> -struct formatting_range : formatting_base<Char> { +template <typename Char, typename Enable = void> struct formatting_range { #ifdef FMT_DEPRECATED_BRACED_RANGES Char prefix = '{'; Char postfix = '}'; @@ -35,12 +27,21 @@ struct formatting_range : formatting_base<Char> { Char prefix = '['; Char postfix = ']'; #endif + + template <typename ParseContext> + FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) { + return ctx.begin(); + } }; -template <typename Char, typename Enable = void> -struct formatting_tuple : formatting_base<Char> { +template <typename Char, typename Enable = void> struct formatting_tuple { Char prefix = '('; Char postfix = ')'; + + template <typename ParseContext> + FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) { + return ctx.begin(); + } }; namespace detail { @@ -296,8 +297,8 @@ struct formatter<TupleT, Char, enable_if_t<fmt::is_tuple_like<TupleT>::value>> { } formatting_tuple<Char>& formatting; size_t& i; - typename std::add_lvalue_reference<decltype( - std::declval<FormatContext>().out())>::type out; + typename std::add_lvalue_reference< + decltype(std::declval<FormatContext>().out())>::type out; }; public: @@ -362,50 +363,53 @@ struct formatter< } }; -template <typename Char, typename... T> struct tuple_arg_join : detail::view { +template <typename Char, typename... T> struct tuple_join_view : detail::view { const std::tuple<T...>& tuple; basic_string_view<Char> sep; - tuple_arg_join(const std::tuple<T...>& t, basic_string_view<Char> s) + tuple_join_view(const std::tuple<T...>& t, basic_string_view<Char> s) : tuple(t), sep{s} {} }; template <typename Char, typename... T> -struct formatter<tuple_arg_join<Char, T...>, Char> { +using tuple_arg_join = tuple_join_view<Char, T...>; + +template <typename Char, typename... T> +struct formatter<tuple_join_view<Char, T...>, Char> { template <typename ParseContext> FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) { return ctx.begin(); } template <typename FormatContext> - typename FormatContext::iterator format( - const tuple_arg_join<Char, T...>& value, FormatContext& ctx) { + auto format(const tuple_join_view<Char, T...>& value, FormatContext& ctx) -> + typename FormatContext::iterator { return format(value, ctx, detail::make_index_sequence<sizeof...(T)>{}); } private: template <typename FormatContext, size_t... N> - typename FormatContext::iterator format( - const tuple_arg_join<Char, T...>& value, FormatContext& ctx, - detail::index_sequence<N...>) { - return format_args(value, ctx, std::get<N>(value.tuple)...); + auto format(const tuple_join_view<Char, T...>& value, FormatContext& ctx, + detail::index_sequence<N...>) -> + typename FormatContext::iterator { + using std::get; + return format_args(value, ctx, get<N>(value.tuple)...); } template <typename FormatContext> - typename FormatContext::iterator format_args( - const tuple_arg_join<Char, T...>&, FormatContext& ctx) { + auto format_args(const tuple_join_view<Char, T...>&, FormatContext& ctx) -> + typename FormatContext::iterator { // NOTE: for compilers that support C++17, this empty function instantiation // can be replaced with a constexpr branch in the variadic overload. return ctx.out(); } template <typename FormatContext, typename Arg, typename... Args> - typename FormatContext::iterator format_args( - const tuple_arg_join<Char, T...>& value, FormatContext& ctx, - const Arg& arg, const Args&... args) { + auto format_args(const tuple_join_view<Char, T...>& value, FormatContext& ctx, + const Arg& arg, const Args&... args) -> + typename FormatContext::iterator { using base = formatter<typename std::decay<Arg>::type, Char>; - auto out = ctx.out(); - out = base{}.format(arg, ctx); + auto out = base().format(arg, ctx); if (sizeof...(Args) > 0) { out = std::copy(value.sep.begin(), value.sep.end(), out); ctx.advance_to(out); @@ -429,14 +433,15 @@ FMT_MODULE_EXPORT_BEGIN \endrst */ template <typename... T> -FMT_CONSTEXPR tuple_arg_join<char, T...> join(const std::tuple<T...>& tuple, - string_view sep) { +FMT_CONSTEXPR auto join(const std::tuple<T...>& tuple, string_view sep) + -> tuple_join_view<char, T...> { return {tuple, sep}; } template <typename... T> -FMT_CONSTEXPR tuple_arg_join<wchar_t, T...> join(const std::tuple<T...>& tuple, - wstring_view sep) { +FMT_CONSTEXPR auto join(const std::tuple<T...>& tuple, + basic_string_view<wchar_t> sep) + -> tuple_join_view<wchar_t, T...> { return {tuple, sep}; } @@ -452,14 +457,8 @@ FMT_CONSTEXPR tuple_arg_join<wchar_t, T...> join(const std::tuple<T...>& tuple, \endrst */ template <typename T> -arg_join<const T*, const T*, char> join(std::initializer_list<T> list, - string_view sep) { - return join(std::begin(list), std::end(list), sep); -} - -template <typename T> -arg_join<const T*, const T*, wchar_t> join(std::initializer_list<T> list, - wstring_view sep) { +auto join(std::initializer_list<T> list, string_view sep) + -> join_view<const T*, const T*> { return join(std::begin(list), std::end(list), sep); } diff --git a/deps/fmt/include/fmt/xchar.h b/deps/fmt/include/fmt/xchar.h new file mode 100644 index 0000000000..a0dd032f16 --- /dev/null +++ b/deps/fmt/include/fmt/xchar.h @@ -0,0 +1,236 @@ +// Formatting library for C++ - optional wchar_t and exotic character support +// +// Copyright (c) 2012 - present, Victor Zverovich +// All rights reserved. +// +// For the license information refer to format.h. + +#ifndef FMT_WCHAR_H_ +#define FMT_WCHAR_H_ + +#include <cwchar> +#include <tuple> + +#include "format.h" + +FMT_BEGIN_NAMESPACE +namespace detail { +template <typename T> +using is_exotic_char = bool_constant<!std::is_same<T, char>::value>; +} + +FMT_MODULE_EXPORT_BEGIN + +using wstring_view = basic_string_view<wchar_t>; +using wformat_parse_context = basic_format_parse_context<wchar_t>; +using wformat_context = buffer_context<wchar_t>; +using wformat_args = basic_format_args<wformat_context>; +using wmemory_buffer = basic_memory_buffer<wchar_t>; + +#if FMT_GCC_VERSION && FMT_GCC_VERSION < 409 +// Workaround broken conversion on older gcc. +template <typename... Args> using wformat_string = wstring_view; +#else +template <typename... Args> +using wformat_string = basic_format_string<wchar_t, type_identity_t<Args>...>; +#endif + +template <> struct is_char<wchar_t> : std::true_type {}; +template <> struct is_char<detail::char8_type> : std::true_type {}; +template <> struct is_char<char16_t> : std::true_type {}; +template <> struct is_char<char32_t> : std::true_type {}; + +template <typename... Args> +constexpr format_arg_store<wformat_context, Args...> make_wformat_args( + const Args&... args) { + return {args...}; +} + +inline namespace literals { +constexpr auto operator"" _format(const wchar_t* s, size_t n) + -> detail::udl_formatter<wchar_t> { + return {{s, n}}; +} + +#if FMT_USE_USER_DEFINED_LITERALS && !FMT_USE_NONTYPE_TEMPLATE_PARAMETERS +constexpr detail::udl_arg<wchar_t> operator"" _a(const wchar_t* s, size_t) { + return {s}; +} +#endif +} // namespace literals + +template <typename It, typename Sentinel> +auto join(It begin, Sentinel end, wstring_view sep) + -> join_view<It, Sentinel, wchar_t> { + return {begin, end, sep}; +} + +template <typename Range> +auto join(Range&& range, wstring_view sep) + -> join_view<detail::iterator_t<Range>, detail::sentinel_t<Range>, + wchar_t> { + return join(std::begin(range), std::end(range), sep); +} + +template <typename T> +auto join(std::initializer_list<T> list, wstring_view sep) + -> join_view<const T*, const T*, wchar_t> { + return join(std::begin(list), std::end(list), sep); +} + +template <typename Char, FMT_ENABLE_IF(!std::is_same<Char, char>::value)> +auto vformat(basic_string_view<Char> format_str, + basic_format_args<buffer_context<type_identity_t<Char>>> args) + -> std::basic_string<Char> { + basic_memory_buffer<Char> buffer; + detail::vformat_to(buffer, format_str, args); + return to_string(buffer); +} + +// Pass char_t as a default template parameter instead of using +// std::basic_string<char_t<S>> to reduce the symbol size. +template <typename S, typename... Args, typename Char = char_t<S>, + FMT_ENABLE_IF(!std::is_same<Char, char>::value)> +auto format(const S& format_str, Args&&... args) -> std::basic_string<Char> { + const auto& vargs = fmt::make_args_checked<Args...>(format_str, args...); + return vformat(to_string_view(format_str), vargs); +} + +template <typename Locale, typename S, typename Char = char_t<S>, + FMT_ENABLE_IF(detail::is_locale<Locale>::value&& + detail::is_exotic_char<Char>::value)> +inline auto vformat( + const Locale& loc, const S& format_str, + basic_format_args<buffer_context<type_identity_t<Char>>> args) + -> std::basic_string<Char> { + return detail::vformat(loc, to_string_view(format_str), args); +} + +template <typename Locale, typename S, typename... Args, + typename Char = char_t<S>, + FMT_ENABLE_IF(detail::is_locale<Locale>::value&& + detail::is_exotic_char<Char>::value)> +inline auto format(const Locale& loc, const S& format_str, Args&&... args) + -> std::basic_string<Char> { + return detail::vformat(loc, to_string_view(format_str), + fmt::make_args_checked<Args...>(format_str, args...)); +} + +template <typename OutputIt, typename S, typename Char = char_t<S>, + FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value&& + detail::is_exotic_char<Char>::value)> +auto vformat_to(OutputIt out, const S& format_str, + basic_format_args<buffer_context<type_identity_t<Char>>> args) + -> OutputIt { + auto&& buf = detail::get_buffer<Char>(out); + detail::vformat_to(buf, to_string_view(format_str), args); + return detail::get_iterator(buf); +} + +template <typename OutputIt, typename S, typename... Args, + typename Char = char_t<S>, + FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value&& + detail::is_exotic_char<Char>::value)> +inline auto format_to(OutputIt out, const S& fmt, Args&&... args) -> OutputIt { + const auto& vargs = fmt::make_args_checked<Args...>(fmt, args...); + return vformat_to(out, to_string_view(fmt), vargs); +} + +template <typename S, typename... Args, typename Char, size_t SIZE, + typename Allocator, FMT_ENABLE_IF(detail::is_string<S>::value)> +FMT_DEPRECATED auto format_to(basic_memory_buffer<Char, SIZE, Allocator>& buf, + const S& format_str, Args&&... args) -> + typename buffer_context<Char>::iterator { + const auto& vargs = fmt::make_args_checked<Args...>(format_str, args...); + detail::vformat_to(buf, to_string_view(format_str), vargs, {}); + return detail::buffer_appender<Char>(buf); +} + +template <typename Locale, typename S, typename OutputIt, typename... Args, + typename Char = char_t<S>, + FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value&& + detail::is_locale<Locale>::value&& + detail::is_exotic_char<Char>::value)> +inline auto vformat_to( + OutputIt out, const Locale& loc, const S& format_str, + basic_format_args<buffer_context<type_identity_t<Char>>> args) -> OutputIt { + auto&& buf = detail::get_buffer<Char>(out); + vformat_to(buf, to_string_view(format_str), args, detail::locale_ref(loc)); + return detail::get_iterator(buf); +} + +template < + typename OutputIt, typename Locale, typename S, typename... Args, + typename Char = char_t<S>, + bool enable = detail::is_output_iterator<OutputIt, Char>::value&& + detail::is_locale<Locale>::value&& detail::is_exotic_char<Char>::value> +inline auto format_to(OutputIt out, const Locale& loc, const S& format_str, + Args&&... args) -> + typename std::enable_if<enable, OutputIt>::type { + const auto& vargs = fmt::make_args_checked<Args...>(format_str, args...); + return vformat_to(out, loc, to_string_view(format_str), vargs); +} + +template <typename OutputIt, typename Char, typename... Args, + FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value&& + detail::is_exotic_char<Char>::value)> +inline auto vformat_to_n( + OutputIt out, size_t n, basic_string_view<Char> format_str, + basic_format_args<buffer_context<type_identity_t<Char>>> args) + -> format_to_n_result<OutputIt> { + detail::iterator_buffer<OutputIt, Char, detail::fixed_buffer_traits> buf(out, + n); + detail::vformat_to(buf, format_str, args); + return {buf.out(), buf.count()}; +} + +template <typename OutputIt, typename S, typename... Args, + typename Char = char_t<S>, + FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value&& + detail::is_exotic_char<Char>::value)> +inline auto format_to_n(OutputIt out, size_t n, const S& fmt, + const Args&... args) -> format_to_n_result<OutputIt> { + const auto& vargs = fmt::make_args_checked<Args...>(fmt, args...); + return vformat_to_n(out, n, to_string_view(fmt), vargs); +} + +template <typename S, typename... Args, typename Char = char_t<S>, + FMT_ENABLE_IF(detail::is_exotic_char<Char>::value)> +inline auto formatted_size(const S& fmt, Args&&... args) -> size_t { + detail::counting_buffer<Char> buf; + const auto& vargs = fmt::make_args_checked<Args...>(fmt, args...); + detail::vformat_to(buf, to_string_view(fmt), vargs); + return buf.count(); +} + +inline void vprint(std::FILE* f, wstring_view fmt, wformat_args args) { + wmemory_buffer buffer; + detail::vformat_to(buffer, fmt, args); + buffer.push_back(L'\0'); + if (std::fputws(buffer.data(), f) == -1) + FMT_THROW(system_error(errno, FMT_STRING("cannot write to file"))); +} + +inline void vprint(wstring_view fmt, wformat_args args) { + vprint(stdout, fmt, args); +} + +template <typename... T> +void print(std::FILE* f, wformat_string<T...> fmt, T&&... args) { + return vprint(f, wstring_view(fmt), make_wformat_args(args...)); +} + +template <typename... T> void print(wformat_string<T...> fmt, T&&... args) { + return vprint(wstring_view(fmt), make_wformat_args(args...)); +} + +/** + Converts *value* to ``std::wstring`` using the default format for type *T*. + */ +template <typename T> inline auto to_wstring(const T& value) -> std::wstring { + return format(FMT_STRING(L"{}"), value); +} +FMT_MODULE_EXPORT_END +FMT_END_NAMESPACE + +#endif // FMT_WCHAR_H_ diff --git a/deps/fmt/src/fmt.cc b/deps/fmt/src/fmt.cc index b0d805da41..d0d6e7fb34 100644 --- a/deps/fmt/src/fmt.cc +++ b/deps/fmt/src/fmt.cc @@ -1,4 +1,8 @@ module; +#ifndef __cpp_modules +# error Module not supported. +#endif + // put all implementation-provided headers into the global module fragment // to prevent attachment to this module #if !defined(_CRT_SECURE_NO_WARNINGS) && defined(_MSC_VER) @@ -8,8 +12,10 @@ module; # define WIN32_LEAN_AND_MEAN #endif +#include <algorithm> #include <cctype> #include <cerrno> +#include <chrono> #include <climits> #include <clocale> #include <cmath> @@ -21,9 +27,6 @@ module; #include <cstring> #include <ctime> #include <cwchar> - -#include <algorithm> -#include <chrono> #include <exception> #include <functional> #include <iterator> @@ -79,16 +82,19 @@ export module fmt; // all library-provided declarations and definitions // must be in the module purview to be exported -#include "fmt/format.h" #include "fmt/args.h" +#include "fmt/chrono.h" #include "fmt/color.h" #include "fmt/compile.h" -#include "fmt/locale.h" -#include "fmt/chrono.h" -#include "fmt/printf.h" +#include "fmt/format.h" #include "fmt/os.h" +#include "fmt/printf.h" +#include "fmt/xchar.h" +// gcc doesn't yet implement private module fragments +#if !FMT_GCC_VERSION module : private; +#endif #include "format.cc" #include "os.cc" diff --git a/deps/fmt/src/format.cc b/deps/fmt/src/format.cc index 618aa07d0b..66925b4210 100644 --- a/deps/fmt/src/format.cc +++ b/deps/fmt/src/format.cc @@ -28,31 +28,6 @@ template FMT_API dragonbox::decimal_fp<float> dragonbox::to_decimal(float x) FMT_NOEXCEPT; template FMT_API dragonbox::decimal_fp<double> dragonbox::to_decimal(double x) FMT_NOEXCEPT; - -// DEPRECATED! This function exists for ABI compatibility. -template <typename Char> -typename basic_format_context<std::back_insert_iterator<buffer<Char>>, - Char>::iterator -vformat_to(buffer<Char>& buf, basic_string_view<Char> format_str, - basic_format_args<basic_format_context< - std::back_insert_iterator<buffer<type_identity_t<Char>>>, - type_identity_t<Char>>> - args) { - using iterator = std::back_insert_iterator<buffer<char>>; - using context = basic_format_context< - std::back_insert_iterator<buffer<type_identity_t<Char>>>, - type_identity_t<Char>>; - auto out = iterator(buf); - format_handler<iterator, Char, context> h(out, format_str, args, {}); - parse_format_string<false>(format_str, h); - return out; -} -template basic_format_context<std::back_insert_iterator<buffer<char>>, - char>::iterator -vformat_to(buffer<char>&, string_view, - basic_format_args<basic_format_context< - std::back_insert_iterator<buffer<type_identity_t<char>>>, - type_identity_t<char>>>); } // namespace detail // Workaround a bug in MSVC2013 that prevents instantiation of format_float. @@ -66,12 +41,15 @@ template FMT_API std::locale detail::locale_ref::get<std::locale>() const; // Explicit instantiations for char. -template FMT_API std::string detail::grouping_impl<char>(locale_ref); -template FMT_API char detail::thousands_sep_impl(locale_ref); +template FMT_API auto detail::thousands_sep_impl(locale_ref) + -> thousands_sep_result<char>; template FMT_API char detail::decimal_point_impl(locale_ref); template FMT_API void detail::buffer<char>::append(const char*, const char*); +// DEPRECATED! +// There is no correspondent extern template in format.h because of +// incompatibility between clang and gcc (#2377). template FMT_API void detail::vformat_to( detail::buffer<char>&, string_view, basic_format_args<FMT_BUFFER_CONTEXT(char)>, detail::locale_ref); @@ -88,10 +66,13 @@ template FMT_API int detail::format_float(long double, int, detail::float_specs, // Explicit instantiations for wchar_t. -template FMT_API std::string detail::grouping_impl<wchar_t>(locale_ref); -template FMT_API wchar_t detail::thousands_sep_impl(locale_ref); +template FMT_API auto detail::thousands_sep_impl(locale_ref) + -> thousands_sep_result<wchar_t>; template FMT_API wchar_t detail::decimal_point_impl(locale_ref); template FMT_API void detail::buffer<wchar_t>::append(const wchar_t*, const wchar_t*); + +template struct detail::basic_data<void>; + FMT_END_NAMESPACE diff --git a/deps/fmt/src/os.cc b/deps/fmt/src/os.cc index 6474d455e2..934629d71b 100644 --- a/deps/fmt/src/os.cc +++ b/deps/fmt/src/os.cc @@ -72,14 +72,14 @@ inline std::size_t convert_rwcount(std::size_t count) { return count; } FMT_BEGIN_NAMESPACE #ifdef _WIN32 -detail::utf16_to_utf8::utf16_to_utf8(wstring_view s) { +detail::utf16_to_utf8::utf16_to_utf8(basic_string_view<wchar_t> s) { if (int error_code = convert(s)) { FMT_THROW(windows_error(error_code, "cannot convert string from UTF-16 to UTF-8")); } } -int detail::utf16_to_utf8::convert(wstring_view s) { +int detail::utf16_to_utf8::convert(basic_string_view<wchar_t> s) { if (s.size() > INT_MAX) return ERROR_INVALID_PARAMETER; int s_size = static_cast<int>(s.size()); if (s_size == 0) { @@ -129,8 +129,8 @@ class system_message { } ~system_message() { LocalFree(message_); } explicit operator bool() const FMT_NOEXCEPT { return result_ != 0; } - operator wstring_view() const FMT_NOEXCEPT { - return wstring_view(message_, result_); + operator basic_string_view<wchar_t>() const FMT_NOEXCEPT { + return basic_string_view<wchar_t>(message_, result_); } }; @@ -159,7 +159,7 @@ FMT_API const std::error_category& system_category() FMT_NOEXCEPT { std::system_error vwindows_error(int err_code, string_view format_str, format_args args) { auto ec = std::error_code(err_code, system_category()); - throw std::system_error(ec, vformat(format_str, args)); + return std::system_error(ec, vformat(format_str, args)); } void detail::format_windows_error(detail::buffer<char>& out, int error_code, |