diff --git a/ChangeLog.rst b/ChangeLog.rst index 423f329282d9..d43d0699e315 100644 --- a/ChangeLog.rst +++ b/ChangeLog.rst @@ -467,7 +467,7 @@ * Implemented ``constexpr`` parsing of format strings and `compile-time format string checks - `_. For + `_. For example .. code:: c++ @@ -528,7 +528,7 @@ throw format_error("invalid specifier"); * Added `iterator support - `_: + `_: .. code:: c++ @@ -539,7 +539,7 @@ fmt::format_to(std::back_inserter(out), "{}", 42); * Added the `format_to_n - `_ + `_ function that restricts the output to the specified number of characters (`#298 `_): @@ -550,7 +550,7 @@ // out == "1234" (without terminating '\0') * Added the `formatted_size - `_ + `_ function for computing the output size: .. code:: c++ @@ -560,7 +560,7 @@ auto size = fmt::formatted_size("{}", 12345); // size == 5 * Improved compile times by reducing dependencies on standard headers and - providing a lightweight `core API `_: + providing a lightweight `core API `_: .. code:: c++ @@ -572,7 +572,7 @@ `_. * Added the `make_format_args - `_ + `_ function for capturing formatting arguments: .. code:: c++ @@ -654,7 +654,7 @@ fmt::format("{} {two}", 1, fmt::arg("two", 2)); * Removed the write API in favor of the `format API - `_ with compile-time handling of + `_ with compile-time handling of format strings. * Disallowed formatting of multibyte strings into a wide character target @@ -1115,10 +1115,10 @@ Including ``format.h`` from the ``cppformat`` directory is deprecated but works via a proxy header which will be removed in the next major version. - The documentation is now available at http://fmtlib.net. + The documentation is now available at https://fmt.dev. * Added support for `strftime `_-like - `date and time formatting `_ + `date and time formatting `_ (`#283 `_): .. code:: c++ @@ -1150,7 +1150,7 @@ // s == "The date is 2012-12-9" * Added support for `custom argument formatters - `_ + `_ (`#235 `_). * Added support for locale-specific integer formatting with the ``n`` specifier @@ -1530,7 +1530,7 @@ Documentation * Added `Building the documentation - `_ + `_ section to the documentation. * Documentation build script is now compatible with Python 3 and newer pip versions. diff --git a/README.rst b/README.rst index c82291445ab5..40ac8a443419 100644 --- a/README.rst +++ b/README.rst @@ -14,23 +14,23 @@ **{fmt}** is an open-source formatting library for C++. It can be used as a safe and fast alternative to (s)printf and iostreams. -`Documentation `__ +`Documentation `__ Q&A: ask questions on `StackOverflow with the tag fmt `_. Features -------- -* Replacement-based `format API `_ with +* Replacement-based `format API `_ with positional arguments for localization. -* `Format string syntax `_ similar to the one +* `Format string syntax `_ similar to the one of `str.format `_ in Python. * Safe `printf implementation - `_ including + `_ including the POSIX extension for positional arguments. * Implementation of the ISO C++ standards proposal `P0645 - Text Formatting `__. + Text Formatting `__. * Support for user-defined types. * High performance: faster than common standard library implementations of `printf `_ and @@ -47,14 +47,14 @@ Features * Ease of use: small self-contained code base, no external dependencies, permissive BSD `license `_ -* `Portability `_ with +* `Portability `_ with consistent output across platforms and support for older compilers. * Clean warning-free codebase even on high warning levels (``-Wall -Wextra -pedantic``). * Support for wide strings. * Optional header-only configuration enabled with the ``FMT_HEADER_ONLY`` macro. -See the `documentation `_ for more details. +See the `documentation `_ for more details. Examples -------- @@ -107,7 +107,7 @@ Use {fmt} as a safe portable replacement for ``itoa`` // access the string with to_string(buf) or buf.data() Format objects of user-defined types via a simple `extension API -`_: +`_: .. code:: c++ @@ -132,8 +132,8 @@ Format objects of user-defined types via a simple `extension API // s == "The date is 2012-12-9" Create your own functions similar to `format -`_ and -`print `_ +`_ and +`print `_ which take arbitrary arguments (`godbolt `_): .. code:: c++ @@ -185,7 +185,7 @@ formatting (`dtoa-benchmark `_) and as fast as `double-conversion `_: .. image:: https://user-images.githubusercontent.com/576385/54883977-9fe8c000-4e28-11e9-8bde-272d122e7c52.jpg - :target: http://fmtlib.net/unknown_mac64_clang10.0.html + :target: https://fmt.dev/unknown_mac64_clang10.0.html Compile time and code bloat ~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -244,7 +244,7 @@ Running the tests Please refer to `Building the library`__ for the instructions on how to build the library and run the unit tests. -__ http://fmtlib.net/latest/usage.html#building-the-library +__ https://fmt.dev/latest/usage.html#building-the-library Benchmarks reside in a separate repository, `format-benchmarks `_, @@ -474,7 +474,7 @@ License `_. The `Format String Syntax -`_ +`_ section in the documentation is based on the one from Python `string module documentation `_ adapted for the current library. For this reason the documentation is diff --git a/doc/_templates/layout.html b/doc/_templates/layout.html index 0ac2dec783e4..ed19bcd38425 100644 --- a/doc/_templates/layout.html +++ b/doc/_templates/layout.html @@ -58,7 +58,7 @@ diff --git a/doc/api.rst b/doc/api.rst index 329e79fad41f..ed2a1e4e5754 100644 --- a/doc/api.rst +++ b/doc/api.rst @@ -51,14 +51,14 @@ participate in an overload resolution if the latter is not a string. .. doxygenfunction:: vprint(std::FILE *, string_view, format_args) .. doxygenfunction:: vprint(std::FILE *, wstring_view, wformat_args) -Named arguments +Named Arguments --------------- .. doxygenfunction:: fmt::arg(const S&, const T&) Named arguments are not supported in compile-time checks at the moment. -Argument lists +Argument Lists -------------- .. doxygenfunction:: fmt::make_format_args(const Args&...) @@ -91,13 +91,13 @@ Format API ``fmt/format.h`` defines the full format API providing compile-time format string checks, output iterator and user-defined type support. -Compile-time format string checks +Compile-time Format String Checks --------------------------------- .. doxygendefine:: FMT_STRING .. doxygendefine:: fmt -Formatting user-defined types +Formatting User-defined Types ----------------------------- To make a user-defined type formattable, specialize the ``formatter`` struct @@ -184,10 +184,10 @@ This section shows how to define a custom format function for a user-defined type. The next section describes how to get ``fmt`` to use a conventional stream output ``operator<<`` when one is defined for a user-defined type. -Output iterator support +Output Iterator Support ----------------------- -.. doxygenfunction:: fmt::format_to(OutputIt, const S&, const Args&...) +.. doxygenfunction:: fmt::format_to(OutputIt, const S &, const Args &...) .. doxygenfunction:: fmt::format_to_n(OutputIt, std::size_t, string_view, const Args&...) .. doxygenstruct:: fmt::format_to_n_result :members: @@ -218,7 +218,7 @@ Utilities :protected-members: :members: -System errors +System Errors ------------- fmt does not use ``errno`` to communicate errors to the user, but it may call @@ -235,7 +235,7 @@ the value of ``errno`` being preserved by library functions. .. _formatstrings: -Custom allocators +Custom Allocators ----------------- The {fmt} library supports custom dynamic memory allocators. @@ -270,7 +270,7 @@ arguments, the container that stores pointers to them will be allocated using the default allocator. Also floating-point formatting falls back on ``sprintf`` which may do allocations. -Custom formatting of built-in types +Custom Formatting of Built-in Types ----------------------------------- It is possible to change the way arguments are formatted by providing a @@ -316,7 +316,7 @@ custom argument formatter class:: .. _chrono-api: -Date and time formatting +Date and Time Formatting ======================== The library supports `strftime @@ -334,7 +334,7 @@ The format string syntax is described in the documentation of .. _ostream-api: -``std::ostream`` support +``std::ostream`` Support ======================== ``fmt/ostream.h`` provides ``std::ostream`` support including formatting of @@ -359,7 +359,7 @@ user-defined types that have overloaded ``operator<<``:: .. _printf-api: -``printf`` formatting +``printf`` Formatting ===================== The header ``fmt/printf.h`` provides ``printf``-like formatting functionality. diff --git a/doc/build.py b/doc/build.py index 924563f07d00..078f2238d951 100755 --- a/doc/build.py +++ b/doc/build.py @@ -94,7 +94,7 @@ def build_docs(version='dev', **kwargs): "FMT_BEGIN_NAMESPACE=namespace fmt {{" \ "FMT_END_NAMESPACE=}}" \ "FMT_STRING_ALIAS=1" \ - "FMT_ENABLE_IF_T(B, T)=T" + "FMT_ENABLE_IF_T(B)=int" EXCLUDE_SYMBOLS = fmt::internal::* StringValue write_str '''.format(include_dir, doxyxml_dir).encode('UTF-8')) if p.returncode != 0: diff --git a/doc/index.rst b/doc/index.rst index d978b7157c2b..57140d6644ec 100644 --- a/doc/index.rst +++ b/doc/index.rst @@ -130,7 +130,7 @@ its numeric value being written to the stream (i.e. 1070 instead of letter 'ю' which is represented by ``L'\x42e'`` if we use Unicode) which is rarely what is needed. -Compact binary code +Compact Binary Code ------------------- The library is designed to produce compact per-call compiled code. For example diff --git a/doc/syntax.rst b/doc/syntax.rst index 4f3d5e5c6983..973dc4b4d8d2 100644 --- a/doc/syntax.rst +++ b/doc/syntax.rst @@ -305,7 +305,7 @@ The available presentation types for pointers are: .. _formatexamples: -Format examples +Format Examples =============== This section contains examples of the format syntax and comparison with diff --git a/doc/usage.rst b/doc/usage.rst index ca925162f28b..35247dbda5ff 100644 --- a/doc/usage.rst +++ b/doc/usage.rst @@ -8,16 +8,9 @@ from a `release archive `_ or the `Git repository `_ to your project. Alternatively, you can :ref:`build the library with CMake `. -If you are using Visual C++ with precompiled headers, you might need to add -the line :: - - #include "stdafx.h" - -before other includes in :file:`format.cc`. - .. _building: -Building the library +Building the Library ==================== The included `CMake build script`__ can be used to build the fmt @@ -59,8 +52,8 @@ To build a `shared library`__ set the ``BUILD_SHARED_LIBS`` CMake variable to __ http://en.wikipedia.org/wiki/Library_%28computing%29#Shared_libraries -Installing the library -==================== +Installing the Library +====================== After building the library you can install it on a Unix-like system by running :command:`sudo make install`. @@ -90,7 +83,7 @@ Setting up your target to use a header-only version of ``fmt`` is equaly easy:: target_link_libraries( PRIVATE fmt-header-only) -Building the documentation +Building the Documentation ========================== To build the documentation you need the following software installed on your diff --git a/include/fmt/printf.h b/include/fmt/printf.h index f75f10af0531..6c73b1d8aced 100644 --- a/include/fmt/printf.h +++ b/include/fmt/printf.h @@ -632,7 +632,6 @@ template ::value)> inline std::basic_string sprintf(const S& format, const Args&... args) { - internal::check_format_string(format); typedef internal::buffer buffer; typedef typename basic_printf_context_t::type context; format_arg_store as{args...}; @@ -665,7 +664,6 @@ inline int vfprintf( template ::value)> inline int fprintf(std::FILE* f, const S& format, const Args&... args) { - internal::check_format_string(format); typedef internal::buffer buffer; typedef typename basic_printf_context_t::type context; format_arg_store as{args...}; diff --git a/support/rtd/index.rst b/support/rtd/index.rst index 4a59e9be8e2a..7c88322f6246 100644 --- a/support/rtd/index.rst +++ b/support/rtd/index.rst @@ -1,2 +1,2 @@ If you are not redirected automatically, follow the -`link to the fmt documentation `_. +`link to the fmt documentation `_. diff --git a/support/rtd/theme/layout.html b/support/rtd/theme/layout.html index ee140868b0c0..29ebc55bb42e 100644 --- a/support/rtd/theme/layout.html +++ b/support/rtd/theme/layout.html @@ -2,15 +2,15 @@ {% block extrahead %} - + Page Redirection {% endblock %} {% block document %} -If you are not redirected automatically, follow the link to the fmt documentation. +If you are not redirected automatically, follow the link to the fmt documentation. {% endblock %} {% block footer %} diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 69c760850a82..02d6a8588f97 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -107,12 +107,6 @@ add_fmt_test(custom-formatter-test) add_fmt_test(ranges-test) add_fmt_test(scan-test) -# MSVC fails to compile GMock when C++17 is enabled. -if (FMT_HAS_VARIANT AND NOT MSVC) - add_fmt_test(std-format-test) - set_property(TARGET std-format-test PROPERTY CXX_STANDARD 17) -endif () - if (HAVE_OPEN) add_fmt_executable(posix-mock-test posix-mock-test.cc ../src/format.cc ${TEST_MAIN_SRC}) @@ -143,6 +137,12 @@ endif () message(STATUS "FMT_PEDANTIC: ${FMT_PEDANTIC}") if (FMT_PEDANTIC) + # MSVC fails to compile GMock when C++17 is enabled. + if (FMT_HAS_VARIANT AND NOT MSVC) + add_fmt_test(std-format-test) + set_property(TARGET std-format-test PROPERTY CXX_STANDARD 17) + endif () + # Test that the library can be compiled with exceptions disabled. # -fno-exception is broken in icc: https://github.com/fmtlib/fmt/issues/822. if (NOT CMAKE_CXX_COMPILER_ID STREQUAL "Intel") diff --git a/test/format b/test/format index 8c0bef7527b7..f411c8c9ceb0 100644 --- a/test/format +++ b/test/format @@ -25,7 +25,7 @@ template using iter_difference_t = ptrdiff_t; } -// http://fmtlib.net/Text%20Formatting.html#format.syn +// https://fmt.dev/Text%20Formatting.html#format.syn namespace std { // [format.error], class format_error class format_error; @@ -105,7 +105,7 @@ namespace std { size_t formatted_size(wstring_view fmt, const Args&... args); } -// http://fmtlib.net/Text%20Formatting.html#format.error +// https://fmt.dev/Text%20Formatting.html#format.error namespace std { class format_error : public runtime_error { public: @@ -125,7 +125,7 @@ struct error_handler { } } -// http://fmtlib.net/Text%20Formatting.html#format.parse_context +// https://fmt.dev/Text%20Formatting.html#format.parse_context namespace std { template class basic_format_parse_context { @@ -199,7 +199,7 @@ constexpr void basic_format_parse_context::check_arg_id(size_t id) { } } -// http://fmtlib.net/Text%20Formatting.html#format.context +// https://fmt.dev/Text%20Formatting.html#format.context namespace std { template class basic_format_context { @@ -261,7 +261,7 @@ template struct formatter; } } -// http://fmtlib.net/Text%20Formatting.html#format.arg +// https://fmt.dev/Text%20Formatting.html#format.arg namespace std { template class basic_format_arg { @@ -422,14 +422,14 @@ void basic_format_arg::handle::format(basic_format_parse_context auto visit_format_arg(Visitor&& vis, basic_format_arg arg) { return visit(vis, get_value(arg)); } } -// http://fmtlib.net/Text%20Formatting.html#format.store +// https://fmt.dev/Text%20Formatting.html#format.store namespace std { template struct format_arg_store { // exposition only @@ -437,7 +437,7 @@ namespace std { }; } -// http://fmtlib.net/Text%20Formatting.html#format.basic_args +// https://fmt.dev/Text%20Formatting.html#format.basic_args namespace std { template class basic_format_args { @@ -471,13 +471,13 @@ basic_format_arg basic_format_args::get(size_t i) const noexce } namespace std { -// http://fmtlib.net/Text%20Formatting.html#format.make_args +// https://fmt.dev/Text%20Formatting.html#format.make_args template format_arg_store make_format_args(const Args&... args) { return {basic_format_arg(args)...}; } -// http://fmtlib.net/Text%20Formatting.html#format.make_wargs +// https://fmt.dev/Text%20Formatting.html#format.make_wargs template format_arg_store make_wformat_args(const Args&... args) { return make_format_args(args...); @@ -708,7 +708,7 @@ struct formatter { }; } // namespace detail -// http://fmtlib.net/Text%20Formatting.html#format.functions +// https://fmt.dev/Text%20Formatting.html#format.functions template string format(string_view fmt, const Args&... args) { return vformat(fmt, make_format_args(args...)); diff --git a/test/scan-test.cc b/test/scan-test.cc index e3efba7bbfdc..42417748211e 100644 --- a/test/scan-test.cc +++ b/test/scan-test.cc @@ -14,23 +14,24 @@ FMT_BEGIN_NAMESPACE namespace internal { - struct scan_arg { - int* value; - // TODO: more types -}; + type arg_type; + union { + int* int_value; + unsigned* uint_value; + long long* long_long_value; + unsigned long long* ulong_long_value; + // TODO: more types + }; -template -void parse_scan_format(string_view format_str, Handler&& handler) { - const char* p = format_str.data(); - const char* end = p + format_str.size(); - while (p != end) { - char c = *p++; - if (c != '{' || p == end || *p++ != '}') - throw format_error("invalid format string"); - handler.on_arg(); - } -} + scan_arg() : arg_type(none_type) {} + scan_arg(int& value) : arg_type(int_type), int_value(&value) {} + scan_arg(unsigned& value) : arg_type(uint_type), uint_value(&value) {} + scan_arg(long long& value) + : arg_type(long_long_type), long_long_value(&value) {} + scan_arg(unsigned long long& value) + : arg_type(ulong_long_type), ulong_long_value(&value) {} +}; } // namespace internal struct scan_args { @@ -45,51 +46,137 @@ struct scan_args { }; namespace internal { -struct scan_handler { - const char* begin; - const char* end; - scan_args args; - int next_arg_id; - scan_handler(string_view input, scan_args args) - : begin(input.data()), - end(begin + input.size()), - args(args), - next_arg_id(0) {} - - void on_arg() { - int value = 0; - while (begin != end) { - char c = *begin++; - if (c < '0' || c > '9') throw format_error("invalid input"); +struct scan_handler : error_handler { + private: + const char* begin_; + const char* end_; + scan_args args_; + int next_arg_id_; + scan_arg arg_; + + template T read_uint() { + T value = 0; + while (begin_ != end_) { + char c = *begin_++; + if (c < '0' || c > '9') on_error("invalid input"); + // TODO: check overflow value = value * 10 + (c - '0'); } - if (next_arg_id >= args.size) - throw format_error("argument index out of range"); - *args.data[0].value = value; + return value; + } + + template T read_int() { + T value = 0; + bool negative = begin_ != end_ && *begin_ == '-'; + if (negative) ++begin_; + value = read_uint::type>(); + if (negative) value = -value; + return value; + } + + public: + scan_handler(string_view input, scan_args args) + : begin_(input.data()), + end_(begin_ + input.size()), + args_(args), + next_arg_id_(0) {} + + const char* pos() const { return begin_; } + + void on_text(const char* begin, const char* end) { + auto size = end - begin; + if (begin_ + size > end_ || !std::equal(begin, end, begin_)) + on_error("invalid input"); + begin_ += size; + } + + void on_arg_id() { + if (next_arg_id_ >= args_.size) on_error("argument index out of range"); + arg_ = args_.data[next_arg_id_++]; + } + void on_arg_id(unsigned) { on_error("invalid format"); } + void on_arg_id(string_view) { on_error("invalid format"); } + + void on_replacement_field(const char*) { + switch (arg_.arg_type) { + case int_type: + *arg_.int_value = read_int(); + break; + case uint_type: + *arg_.uint_value = read_uint(); + break; + case long_long_type: + *arg_.long_long_value = read_int(); + break; + case ulong_long_type: + *arg_.ulong_long_value = read_uint(); + break; + default: + assert(false); + } } + + const char* on_format_specs(const char* begin, const char*) { return begin; } }; } // namespace internal -void vscan(string_view input, string_view format_str, scan_args args) { - internal::parse_scan_format(format_str, internal::scan_handler(input, args)); -} - template std::array make_scan_args(Args&... args) { - return std::array{&args...}; + return std::array{args...}; +} + +string_view::iterator vscan(string_view input, string_view format_str, + scan_args args) { + internal::scan_handler h(input, args); + internal::parse_format_string(format_str, h); + return input.begin() + (h.pos() - &*input.begin()); } template -void scan(string_view input, string_view format_str, Args&... args) { - vscan(input, format_str, make_scan_args(args...)); +string_view::iterator scan(string_view input, string_view format_str, + Args&... args) { + return vscan(input, format_str, make_scan_args(args...)); } FMT_END_NAMESPACE +TEST(ScanTest, ReadText) { + fmt::string_view s = "foo"; + auto end = fmt::scan(s, "foo"); + EXPECT_EQ(end, s.end()); + EXPECT_THROW_MSG(fmt::scan("fob", "foo"), fmt::format_error, "invalid input"); +} + TEST(ScanTest, ReadInt) { int n = 0; fmt::scan("42", "{}", n); EXPECT_EQ(n, 42); + fmt::scan("-42", "{}", n); + EXPECT_EQ(n, -42); +} + +TEST(ScanTest, ReadLongLong) { + long long n = 0; + fmt::scan("42", "{}", n); + EXPECT_EQ(n, 42); + fmt::scan("-42", "{}", n); + EXPECT_EQ(n, -42); +} + +TEST(ScanTest, ReadUInt) { + unsigned n = 0; + fmt::scan("42", "{}", n); + EXPECT_EQ(n, 42); + EXPECT_THROW_MSG(fmt::scan("-42", "{}", n), fmt::format_error, + "invalid input"); +} + +TEST(ScanTest, ReadULongLong) { + unsigned long long n = 0; + fmt::scan("42", "{}", n); + EXPECT_EQ(n, 42); + EXPECT_THROW_MSG(fmt::scan("-42", "{}", n), fmt::format_error, + "invalid input"); } TEST(ScanTest, InvalidFormat) {