diff --git a/CMakeLists.txt b/CMakeLists.txt index 639dc7fc..e0626131 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -95,8 +95,8 @@ function(add_headers VAR) endfunction() # Define the fmt library, its includes and the needed defines. -add_headers(FMT_HEADERS core.h format.h format-inl.h locale.h ostream.h printf.h - time.h ranges.h) +add_headers(FMT_HEADERS core.h format.h format-inl.h ostream.h printf.h time.h + ranges.h) set(FMT_SOURCES src/format.cc) if (HAVE_OPEN) add_headers(FMT_HEADERS posix.h) diff --git a/ChangeLog.rst b/ChangeLog.rst index e27e3d82..b3198ced 100644 --- a/ChangeLog.rst +++ b/ChangeLog.rst @@ -20,6 +20,7 @@ .. code:: c++ #include + std::string s = format(fmt("{:d}"), "foo"); gives a compile-time error because ``d`` is an invalid specifier for strings @@ -133,7 +134,9 @@ vreport_error(format, fmt::make_format_args(args...)); } -* Added the ``make_printf_args`` function for capturing ``printf`` arguments. +* Added the ``make_printf_args`` function for capturing ``printf`` arguments ( + `#687 `_, + `#694 `_). Thanks `@Kronuz (Germán Méndez Bravo) `_. * Added prefix ``v`` to non-variadic functions taking ``format_args`` to @@ -146,6 +149,34 @@ template std::string format(string_view format_str, const Args & ... args); +* Added experimental support for formatting ranges, containers and tuple-like + types in ``fmt/ranges.h`` (`#735 `_): + + .. code:: c++ + + #include + + std::vector v = {1, 2, 3}; + fmt::print("{}", v); // prints {1, 2, 3} + + Thanks `@Remotion (Remo) `_. + +* Implemented ``wchar_t`` date and time formatting + (`#712 `_): + + .. code:: c++ + + #include + + std::time_t t = std::time(nullptr); + auto s = fmt::format(L"The date is {:%Y-%m-%d}.", *std::localtime(&t)); + + Thanks `@DanielaE (Daniela Engert) `_. + +* Provided more wide string overloads + (`#724 `_). + Thanks `@DanielaE (Daniela Engert) `_. + * Switched from a custom null-terminated string view class to ``string_view`` in the format API and provided ``fmt::string_view`` which implements a subset of ``std::string_view`` API for pre-C++17 systems. @@ -188,6 +219,8 @@ * Implemented more efficient handling of large number of format arguments. +* Introduced an inline namespace for symbol versioning. + * Added debug postfix ``d`` to the `fmt`` library name (`#636 `_). @@ -229,12 +262,21 @@ `#656 `_, `#679 `_, `#681 `_, - `#705 `_). + `#705 `_, + `#715 `_, + `#717 `_, + `#720 `_, + `#723 `_, + `#726 `_, + `#730 `_, + `#739 `_). Thanks `@peterbell10 `_, `@LarsGullik `_, `@foonathan (Jonathan Müller) `_, - `@eliaskosunen (Elias Kosunen) `_, and - `@christianparpart (Christian Parpart) `_. + `@eliaskosunen (Elias Kosunen) `_, + `@christianparpart (Christian Parpart) `_, + `@DanielaE (Daniela Engert) `_, + and `@mwinterb `_. * Worked around an MSVC bug and fixed several warnings (`#653 `_). @@ -260,10 +302,17 @@ (`#660 `_). Thanks `@hubslave `_. +* Fixed compilation on FreeBSD 12 + (`#732 `_). + Thanks `@dankm `_. + * Fixed compilation when there is a mismatch between ``-std`` options between the library and user code (`#664 `_). +* Fixed compilation with GCC 7 and ``-std=c++11`` + (`#734 `_). + * Improved generated binary code on GCC 7 and older (`#668 `_). @@ -286,6 +335,17 @@ * Fixed compile checks for mixing narrow and wide strings (`#690 `_). +* Disabled unsafe implicit conversion to ``std::string`` + (`#729 `_). + +* Fixed handling of reused format specs (as in ``fmt::join``) for pointers + (`#725 `_). + Thanks `@mwinterb `_. + +* Fixed installation of ``fmt/ranges.h`` + (`#738 `_). + Thanks `@sv1990 `_. + 4.1.0 - 2017-12-20 ------------------ diff --git a/doc/api.rst b/doc/api.rst index 0eeab744..8bf71c57 100644 --- a/doc/api.rst +++ b/doc/api.rst @@ -164,6 +164,10 @@ Utilities System errors ------------- +fmt does not use ``errno`` to communicate errors to the user, but it may call +system functions which set ``errno``. Users should not make any assumptions about +the value of ``errno`` being preserved by library functions. + .. doxygenclass:: fmt::system_error :members: diff --git a/doc/index.rst b/doc/index.rst index ac463062..17d6b4bb 100644 --- a/doc/index.rst +++ b/doc/index.rst @@ -2,8 +2,7 @@ Overview ======== **fmt** (formerly cppformat) is an open-source formatting library. -It can be used as a safe alternative to printf or as a fast -alternative to C++ IOStreams. +It can be used as a fast and safe alternative to printf and IOStreams. .. raw:: html @@ -30,9 +29,9 @@ in Python: .. code:: c++ - fmt::format("The answer is {}", 42); + fmt::format("The answer is {}.", 42); -The ``fmt::format`` function returns a string "The answer is 42". You can use +The ``fmt::format`` function returns a string "The answer is 42.". You can use ``fmt::memory_buffer`` to avoid constructing ``std::string``: .. code:: c++ @@ -94,19 +93,25 @@ Safety ------ The library is fully type safe, automatic memory management prevents buffer -overflow, errors in format strings are reported using exceptions. For example, -the code +overflow, errors in format strings are reported using exceptions or at compile +tim. For example, the code .. code:: c++ fmt::format("The answer is {:d}", "forty-two"); -throws a ``format_error`` exception with description -"unknown format code 'd' for string", because the argument -``"forty-two"`` is a string while the format code ``d`` -only applies to integers. +throws a ``format_error`` exception with description "unknown format code 'd' for +string", because the argument ``"forty-two"`` is a string while the format code +``d`` only applies to integers, while -Where possible, errors are caught at compile time. For example, the code +.. code:: c++ + + format(fmt("The answer is {:d}"), "forty-two"); + +reports a compile-time error for the same reason on compilers that support +relaxed ``constexpr``. + +The following code .. code:: c++ @@ -124,16 +129,10 @@ 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. -Note that fmt does not use the value of the ``errno`` global to communicate -errors to the user, but it may call system functions which set ``errno``. Since -fmt does not attempt to preserve the value of ``errno``, users should not make -any assumptions about it and always set it to ``0`` before making any system -calls that convey error information via ``errno``. - Compact binary code ------------------- -Each call to a formatting function results in a compact binary code. For example +The library is designed to produce compact per-call compiled code. For example (`godbolt `_), .. code:: c++ @@ -167,33 +166,19 @@ compiles to just Portability ----------- -The library is highly portable. Here is an incomplete list of operating systems -and compilers where it has been tested and known to work: +The library is highly portable and relies only on a small set of C++11 features: -* 64-bit (amd64) GNU/Linux with GCC 4.4.3, - `4.6.3 `_, 4.7.2, 4.8.1, and Intel C++ - Compiler (ICC) 14.0.2 +* variadic templates +* type traits +* rvalue references +* decltype +* trailing return types +* deleted functions -* 32-bit (i386) GNU/Linux with GCC 4.4.3, 4.6.3 - -* Mac OS X with GCC 4.2.1 and Clang 4.2, 5.1.0 - -* 64-bit Windows with Visual C++ 2010, 2013 and - `2015 `_ - -* 32-bit Windows with Visual C++ 2010 - -Although the library uses C++11 features when available, it also works with -older compilers and standard library implementations. The only thing to keep in -mind for C++98 portability: - -* Variadic templates: minimum GCC 4.4, Clang 2.9 or VS2013. This feature allows - the Format API to accept an unlimited number of arguments. With older - compilers the maximum is 15. - -* User-defined literals: minimum GCC 4.7, Clang 3.1 or VS2015. The suffixes - ``_format`` and ``_a`` are functionally equivalent to the functions - ``fmt::format`` and ``fmt::arg``. +These are available since GCC 4.4, Clang 2.9 and MSVC 18.0 (2013). For older +compilers use fmt `version 4.x +`_ which continues to be +maintained and only requires C++98. The output of all formatting functions is consistent across platforms. In particular, formatting a floating-point infinity always gives ``inf`` while the @@ -211,7 +196,7 @@ Ease of Use ----------- fmt has a small self-contained code base with the core library consisting of -a single header file and a single source file and no external dependencies. +just three header files and no external dependencies. A permissive BSD `license `_ allows using the library both in open-source and commercial projects. diff --git a/include/fmt/core.h b/include/fmt/core.h index 77ff089b..0986789f 100644 --- a/include/fmt/core.h +++ b/include/fmt/core.h @@ -616,7 +616,8 @@ FMT_MAKE_VALUE(pointer_type, std::nullptr_t, const void*) // formatting of "[const] volatile char *" which is printed as bool by // iostreams. template -typed_value make_value(const T *) { +typename std::enable_if::value>::type + make_value(const T *) { static_assert(!sizeof(T), "formatting of non-void pointers is disallowed"); } @@ -1240,12 +1241,12 @@ inline std::string format(string_view format_str, const Args & ... args) { // This should be just // return vformat(format_str, make_format_args(args...)); // but gcc has trouble optimizing the latter, so break it down. - format_arg_store as(args...); + format_arg_store as{args...}; return vformat(format_str, as); } template inline std::wstring format(wstring_view format_str, const Args & ... args) { - format_arg_store as(args...); + format_arg_store as{args...}; return vformat(format_str, as); } @@ -1286,7 +1287,7 @@ FMT_API void vprint(wstring_view format_str, wformat_args args); */ template inline void print(string_view format_str, const Args & ... args) { - format_arg_store as(args...); + format_arg_store as{args...}; vprint(format_str, as); } diff --git a/include/fmt/format-inl.h b/include/fmt/format-inl.h index 19d5dd8b..223ed516 100644 --- a/include/fmt/format-inl.h +++ b/include/fmt/format-inl.h @@ -9,7 +9,6 @@ #define FMT_FORMAT_INL_H_ #include "format.h" -#include "locale.h" #include @@ -19,6 +18,7 @@ #include #include #include // for std::ptrdiff_t +#include #if defined(_WIN32) && defined(__MINGW32__) # include @@ -205,6 +205,15 @@ void report_error(FormatFunc func, int error_code, } } // namespace +class locale { + private: + std::locale locale_; + + public: + explicit locale(std::locale loc = std::locale()) : locale_(loc) {} + std::locale get() { return locale_; } +}; + template FMT_FUNC Char internal::thousands_sep(locale_provider *lp) { std::locale loc = lp ? lp->locale().get() : std::locale(); diff --git a/include/fmt/format.h b/include/fmt/format.h index 895cf925..f274586c 100644 --- a/include/fmt/format.h +++ b/include/fmt/format.h @@ -304,7 +304,7 @@ fp operator*(fp x, fp y); // Compute k such that its cached power c_k = c_k.f * pow(2, c_k.e) satisfies // min_exponent <= c_k.e + e <= min_exponent + 3. inline int compute_cached_power_index(int e, int min_exponent) { - constexpr double one_over_log2_10 = 0.30102999566398114; // 1 / log2(10) + const double one_over_log2_10 = 0.30102999566398114; // 1 / log2(10) return static_cast(std::ceil((min_exponent - e + 63) * one_over_log2_10)); } @@ -2236,7 +2236,9 @@ void handle_dynamic_spec( /** The default argument formatter. */ template class arg_formatter: - public internal::function, public internal::arg_formatter_base { + public internal::function< + typename internal::arg_formatter_base::iterator>, + public internal::arg_formatter_base { private: typedef typename Range::value_type char_type; typedef internal::arg_formatter_base base; diff --git a/include/fmt/locale.h b/include/fmt/locale.h deleted file mode 100644 index a6a4b748..00000000 --- a/include/fmt/locale.h +++ /dev/null @@ -1,25 +0,0 @@ -// Formatting library for C++ - locale support -// -// Copyright (c) 2012 - 2016, Victor Zverovich -// All rights reserved. -// -// For the license information refer to format.h. - -#ifndef FMT_LOCALE_H_ -#define FMT_LOCALE_H_ - -#include "format.h" -#include - -FMT_BEGIN_NAMESPACE -class locale { - private: - std::locale locale_; - - public: - explicit locale(std::locale loc = std::locale()) : locale_(loc) {} - std::locale get() { return locale_; } -}; -FMT_END_NAMESPACE - -#endif // FMT_LOCALE_ diff --git a/include/fmt/ostream.h b/include/fmt/ostream.h index 14619364..5daa272b 100644 --- a/include/fmt/ostream.h +++ b/include/fmt/ostream.h @@ -140,13 +140,13 @@ inline void vprint(std::basic_ostream &os, template inline void print(std::ostream &os, string_view format_str, const Args & ... args) { - vprint(os, format_str, make_format_args(args...)); + vprint(os, format_str, make_format_args(args...)); } template inline void print(std::wostream &os, wstring_view format_str, const Args & ... args) { - vprint(os, format_str, make_format_args(args...)); + vprint(os, format_str, make_format_args(args...)); } FMT_END_NAMESPACE diff --git a/include/fmt/printf.h b/include/fmt/printf.h index 5a396964..e9a81669 100644 --- a/include/fmt/printf.h +++ b/include/fmt/printf.h @@ -215,7 +215,9 @@ class basic_printf_context; */ template class printf_arg_formatter: - public internal::function, public internal::arg_formatter_base { + public internal::function< + typename internal::arg_formatter_base::iterator>, + public internal::arg_formatter_base { private: typedef typename Range::value_type char_type; typedef decltype(internal::declval().begin()) iterator; @@ -627,7 +629,7 @@ template inline int fprintf(std::FILE *f, string_view format_str, const Args & ... args) { auto vargs = make_format_args< typename printf_context::type>(args...); - return vfprintf(f, format_str, vargs); + return vfprintf(f, format_str, vargs); } template diff --git a/include/fmt/ranges.h b/include/fmt/ranges.h index 3e8480a1..c04488de 100644 --- a/include/fmt/ranges.h +++ b/include/fmt/ranges.h @@ -34,18 +34,20 @@ template struct formatting_range : formatting_base { static FMT_CONSTEXPR_DECL const std::size_t range_length_limit = FMT_RANGE_OUTPUT_LENGTH_LIMIT; // output only up to N items from the range. - Char prefix = '{'; - Char delimiter = ','; - Char postfix = '}'; + Char prefix; + Char delimiter; + Char postfix; + formatting_range() : prefix('{'), delimiter(','), postfix('}') {} static FMT_CONSTEXPR_DECL const bool add_delimiter_spaces = true; static FMT_CONSTEXPR_DECL const bool add_prepostfix_space = false; }; template struct formatting_tuple : formatting_base { - Char prefix = '('; - Char delimiter = ','; - Char postfix = ')'; + Char prefix; + Char delimiter; + Char postfix; + formatting_tuple() : prefix('('), delimiter(','), postfix(')') {} static FMT_CONSTEXPR_DECL const bool add_delimiter_spaces = true; static FMT_CONSTEXPR_DECL const bool add_prepostfix_space = false; }; @@ -54,9 +56,8 @@ namespace internal { template void copy(const RangeT &range, OutputIterator out) { - for (const auto &it : range) { - *out++ = it; - } + for (auto it = range.begin(), end = range.end(); it != end; ++it) + *out++ = *it; } template @@ -83,7 +84,7 @@ class is_like_std_string { public: static FMT_CONSTEXPR_DECL const bool value = - !std::is_void(nullptr))>::value; + !std::is_void(FMT_NULL))>::value; }; template @@ -95,8 +96,8 @@ struct is_range_ : std::false_type {}; template struct is_range_().begin()), - decltype(std::declval().end())>, + conditional_helper().begin()), + decltype(internal::declval().end())>, void>::type> : std::true_type {}; template @@ -111,13 +112,13 @@ class is_tuple_like_ { template static auto check(U *p) -> decltype(std::tuple_size::value, - std::declval::type>(), int()); + internal::declval::type>(), int()); template static void check(...); public: static FMT_CONSTEXPR_DECL const bool value = - !std::is_void(nullptr))>::value; + !std::is_void(FMT_NULL))>::value; }; template @@ -125,7 +126,16 @@ struct is_tuple_like { static FMT_CONSTEXPR_DECL const bool value = is_tuple_like_::value && !is_range_::value; }; +} // namespace internal +#if FMT_HAS_FEATURE(__cpp_lib_integer_sequence) +# define FMT_USE_INTEGER_SEQUENCE 1 +#else +# define FMT_USE_INTEGER_SEQUENCE 0 +#endif + +#if FMT_USE_INTEGER_SEQUENCE +namespace internal { template void for_each(std::index_sequence, Tuple &&tup, F &&f) noexcept { using std::get; @@ -183,6 +193,7 @@ struct formatter struct formatter< RangeT, Char, @@ -196,11 +207,12 @@ struct formatter< RangeT, Char, } template - auto format(const RangeT &values, FormatContext &ctx) -> decltype(ctx.out()) { + typename FormatContext::iterator format( + const RangeT &values, FormatContext &ctx) { auto out = ctx.out(); internal::copy(formatting.prefix, out); std::size_t i = 0; - for (const auto &it : values) { + for (auto it = values.begin(), end = values.end(); it != end; ++it) { if (i > 0) { if (formatting.add_prepostfix_space) { *out++ = ' '; @@ -208,9 +220,9 @@ struct formatter< RangeT, Char, internal::copy(formatting.delimiter, out); } if (formatting.add_delimiter_spaces && i > 0) { - format_to(out, " {}", it); + format_to(out, " {}", *it); } else { - format_to(out, "{}", it); + format_to(out, "{}", *it); } if (++i > formatting.range_length_limit) { format_to(out, " ... "); diff --git a/src/posix.cc b/src/posix.cc index d672b0fc..88f52edf 100644 --- a/src/posix.cc +++ b/src/posix.cc @@ -223,9 +223,9 @@ buffered_file file::fdopen(const char *mode) { if (!f) FMT_THROW(system_error(errno, "cannot associate stream with file descriptor")); - buffered_file file(f); + buffered_file bf(f); fd_ = -1; - return file; + return bf; } long getpagesize() { diff --git a/test/format-test.cc b/test/format-test.cc index daa0b676..0044390e 100644 --- a/test/format-test.cc +++ b/test/format-test.cc @@ -1394,7 +1394,8 @@ TEST(FormatTest, FixedEnum) { typedef fmt::back_insert_range buffer_range; class mock_arg_formatter: - public fmt::internal::function, + public fmt::internal::function< + fmt::internal::arg_formatter_base::iterator>, public fmt::internal::arg_formatter_base { private: MOCK_METHOD1(call, void (int value)); diff --git a/test/ranges-test.cc b/test/ranges-test.cc index 76c37a11..c27833f2 100644 --- a/test/ranges-test.cc +++ b/test/ranges-test.cc @@ -30,6 +30,7 @@ TEST(RangesTest, FormatVector2) { EXPECT_EQ("{{1, 2}, {3, 5}, {7, 11}}", ivf); } +#if FMT_USE_INTEGER_SEQUENCE TEST(RangesTest, FormatMap) { std::map simap{{"one", 1}, {"two", 2}}; EXPECT_EQ("{(one, 1), (two, 2)}", fmt::format("{}", simap)); @@ -46,7 +47,7 @@ TEST(RangesTest, FormatTuple) { EXPECT_EQ("(42, 3.14159, this is tuple)", fmt::format("{}", tu1)); } -/// check if 'if constexpr' is supported. +/// Check if 'if constexpr' is supported. #if (__cplusplus > 201402L) || \ (defined(_MSVC_LANG) && _MSVC_LANG > 201402L && _MSC_VER >= 1910) @@ -86,3 +87,4 @@ TEST(RangesTest, FormatStruct) { #endif // (__cplusplus > 201402L) || (defined(_MSVC_LANG) && _MSVC_LANG > // 201402L && _MSC_VER >= 1910) +#endif // FMT_USE_INTEGER_SEQUENCE diff --git a/test/util-test.cc b/test/util-test.cc index 5bba7736..7c7b6079 100644 --- a/test/util-test.cc +++ b/test/util-test.cc @@ -17,7 +17,6 @@ # include #endif -#include "fmt/locale.h" #include "gmock/gmock.h" #include "gtest-extra.h" #include "mock-allocator.h" @@ -397,13 +396,14 @@ TEST(FixedBufferTest, BufferOverflow) { EXPECT_THROW_MSG(buffer.resize(11), std::runtime_error, "buffer overflow"); } +struct uint32_pair { + uint32_t u[2]; +}; + TEST(UtilTest, BitCast) { - struct S { - uint32_t u[2]; - }; - auto s = fmt::internal::bit_cast(uint64_t(42)); + auto s = fmt::internal::bit_cast(uint64_t{42}); EXPECT_EQ(fmt::internal::bit_cast(s), 42u); - s = fmt::internal::bit_cast(uint64_t(~0ull)); + s = fmt::internal::bit_cast(uint64_t(~0ull)); EXPECT_EQ(fmt::internal::bit_cast(s), ~0ull); }