From 097203968f0a9b17628062f4adca63ea5cb97aa2 Mon Sep 17 00:00:00 2001 From: MerryMage Date: Wed, 22 Apr 2020 20:41:46 +0100 Subject: [PATCH] Squashed 'externals/fmt/' changes from 39834389..135ab5cf 135ab5cf Update version 93d95f17 Fix markup 4f15c72f Fix markup e9b19414 Automatically add package to release c3d1f604 Fix markup c96062bf Update changelog and version number f9c97de4 Add note about errno to the documentation 62df6f27 CMakeLists: Use GNUInstallDirs to set install location 493586cb Fix overflow check 1d751bc6 fix warning in header: signed/unsigned comparison 11415bce Update usage.rst 9982dd01 Fix for warning C5030 in VS2015 42e88c4f Silenced MSVC 2017 constant if expression warning 7a9c1ba1 FMT_VARIADIC_CONST - Support for const variadic methods (#591) 324415c0 Use allocator_traits if available. 5f39721c Fix a warning ca96acbe Add examples 708d9509 fix(Clang CodeGen): remove warnings 9328a074 Fix handling of fixed enums in clang (#580) 2c077dd4 Enable stream exceptions (#581) 933a33a7 Added MSVC checking for support for string_view. bef89db6 Fix a bogus -Wduplicated-branches gcc warning (#573) 2a619d96 Make format work with C++17 std::string_view (#571) e051de37 Use less version 2.6.1 and sudo to fix npm install issues on travis 5de459bf Suppress Clang's warning on zero as a null pointer 16589534 Make ArgMap::init not explicitly instantiated (#563) 3e75d3e0 Fix handling of types convertible to int 89654cd1 to_wstring added 37eb419a Fix noreturn attribute detection (#555) 14d85349 Explicitly cast range length to std::size_t to prevent conversion warnings c2201ce0 Accept wide chars as integers to prevent conversion warning 6efbccb3 Add one more CMake warning fix 032c8380 Fix a segfault in test on glibc 2.26 #551, take 2 6655e804 Fix a segfault in test on glibc 2.26 #551 d16c4d20 Suppress warning about missing noreturn attribute (#549) 9c56a8ce Make format_arg() accept class hierarchies ca0e3830 Update README.rst 81790d72 Update format.h to remove C4574 error on MSVC 14.2 30283443 Fix undefined behavior in UDL macro 4045d7fe Fix warning about missing ' character 89c3bc58 Remove warning C4668 in MSVC for FMT_GCC_VERSION and FMT_HAS_GXX_CXX11 4af9421f Adding OpenSpace to the list of projects 1a398b54 Fixed CMake CMP0048 warning. 589ccc16 Bump version c3817046 Add an error on broken includes 16bdd842 Update scripts b492316d Update version list 91f4ce02 Automatically update version in release script (#431) git-subtree-dir: externals/fmt git-subtree-split: 135ab5cf71ed731fc9fa0653051e7d4884a3652f --- CMakeLists.txt | 8 ++ ChangeLog.rst | 27 +++- README.rst | 4 + doc/api.rst | 32 +++++ doc/build.py | 6 +- doc/index.rst | 6 + doc/syntax.rst | 19 +-- doc/usage.rst | 4 + fmt/CMakeLists.txt | 10 +- fmt/format.cc | 64 ++------- fmt/format.h | 279 +++++++++++++++++++++++++++++++--------- fmt/ostream.h | 7 +- fmt/printf.h | 4 +- fmt/string.h | 22 ++++ support/manage.py | 37 +++++- support/travis-build.py | 2 +- test/format-test.cc | 47 +++++++ test/mock-allocator.h | 8 +- test/ostream-test.cc | 15 +++ test/string-test.cc | 4 + test/util-test.cc | 17 ++- 21 files changed, 481 insertions(+), 141 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index b08f9cbe..d99babdb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,6 +2,14 @@ message(STATUS "CMake version: ${CMAKE_VERSION}") cmake_minimum_required(VERSION 2.8.12) +if (POLICY CMP0048) # Version variables + cmake_policy(SET CMP0048 OLD) +endif () + +if (POLICY CMP0063) # Visibility + cmake_policy(SET CMP0063 OLD) +endif (POLICY CMP0063) + # Determine if fmt is built as a subproject (using add_subdirectory) # or if it is the master project. set(MASTER_PROJECT OFF) diff --git a/ChangeLog.rst b/ChangeLog.rst index 4a16be95..ce0406a9 100644 --- a/ChangeLog.rst +++ b/ChangeLog.rst @@ -1,7 +1,32 @@ +4.1.0 - 2017-12-20 +------------------ + +* Added ``fmt::to_wstring()`` in addition to ``fmt::to_string()`` (`#559 `_). Thanks `@alabuzhev (Alex Alabuzhev) `_. + +* Added support for C++17 ``std::string_view`` (`#571 `_ and `#578 `_). Thanks `@thelostt (Mário Feroldi) `_ and `@mwinterb `_. + +* Enabled stream exceptions to catch errors (`#581 `_). Thanks `@crusader-mike `_. + +* Allowed formatting of class hierarchies with ``fmt::format_arg()`` (`#547 `_). Thanks `@rollbear (Björn Fahller) `_. + +* Removed limitations on character types + (`#563 `_). + Thanks `@Yelnats321 (Elnar Dakeshov) `_. + +* Conditionally enabled use of ``std::allocator_traits`` (`#583 `_). Thanks `@mwinterb `_. + +* Added support for ``const`` variadic member function emulation with ``FMT_VARIADIC_CONST`` (`#591 `_). Thanks `@ludekvodicka (Ludek Vodicka) `_. + +* Various bugfixes: bad overflow check, unsupported implicit type conversion when determining formatting function, test segfaults (`#551 `_), ill-formed macros (`#542 `_) and ambiguous overloads (`#580 `_). Thanks `@xylosper (Byoung-young Lee) `_. + +* Prevented warnings on MSVC (`#605 `_, `#602 `_, and `#545 `_), clang (`#582 `_), GCC (`#573 `_), various conversion warnings (`#609 `_, `#567 `_, `#553 `_ and `#553 `_), and added ``override`` and ``[[noreturn]]`` (`#549 `_ and `#555 `_). Thanks `@alabuzhev (Alex Alabuzhev) `_, `@virgiliofornazin (Virgilio Alexandre Fornazin) `_, `@alexanderbock (Alexander Bock) `_, `@yumetodo `_, `@VaderY (Császár Mátyás) `_, `@jpcima (JP Cimalando) `_, `@thelostt (Mário Feroldi) `_, and `@Manu343726 (Manu Sánchez) `_. + +* Improved CMake: Used GNUInstallDirs to set installation location (`#610 `_) and fixed warnings (`#536 `_ and `#556 `_). Thanks `@mikecrowe (Mike Crowe) `_, `@evgen231 `_ and `@henryiii (Henry Schreiner) `_. + 4.0.0 - 2017-06-27 ------------------ -* Removed old compatibility headers ``cppformat/*.h`` and CMake options (`#527 `_). Thanks `@maddinat0r (Alex Martin) `_. +* Removed old compatibility headers ``cppformat/*.h`` and CMake options (`#527 `_). Thanks `@maddinat0r (Alex Martin) `_. * Added ``string.h`` containing ``fmt::to_string()`` as alternative to ``std::to_string()`` as well as other string writer functionality (`#326 `_ and `#441 `_): diff --git a/README.rst b/README.rst index ea2a6da4..32f50257 100644 --- a/README.rst +++ b/README.rst @@ -142,6 +142,8 @@ Projects using this library * `Envoy `_: C++ L7 proxy and communication bus (Lyft) +* `FiveM `_: a modification framework for GTA V + * `HarpyWar/pvpgn `_: Player vs Player Gaming Network with tweaks @@ -155,6 +157,8 @@ Projects using this library * `MongoDB Smasher `_: A small tool to generate randomized datasets +* `OpenSpace `_: An open-source astrovisualization framework + * `PenUltima Online (POL) `_: An MMO server, compatible with most Ultima Online clients diff --git a/doc/api.rst b/doc/api.rst index 8dbef39e..9647c90b 100644 --- a/doc/api.rst +++ b/doc/api.rst @@ -84,6 +84,36 @@ Note in the example above the ``format_arg`` function ignores the contents of ``format_arg`` in :file:`fmt/time.h` for an advanced example of how to use the ``format_str`` argument to customize the formatted output. +This technique can also be used for formatting class hierarchies:: + + namespace local { + struct Parent { + Parent(int p) : p(p) {} + virtual void write(fmt::Writer &w) const { + w.write("Parent : p={}", p); + } + int p; + }; + + struct Child : Parent { + Child(int c, int p) : Parent(p), c(c) {} + virtual void write(fmt::Writer &w) const { + w.write("Child c={} : ", c); + Parent::write(w); + } + int c; + }; + + void format_arg(fmt::BasicFormatter &f, + const char *&format_str, const Parent &p) { + p.write(f.writer()); + } + } + Local::Child c(1,2); + Local::Parent &p = c; + fmt::print("via ref to base: {}\n", p); + fmt::print("direct to child: {}\n", c); + 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. @@ -232,6 +262,8 @@ Utilities .. doxygenfunction:: fmt::to_string(const T&) +.. doxygenfunction:: fmt::to_wstring(const T&) + .. doxygenclass:: fmt::BasicStringRef :members: diff --git a/doc/build.py b/doc/build.py index 55992b9f..cb8d0c3c 100755 --- a/doc/build.py +++ b/doc/build.py @@ -6,6 +6,8 @@ import errno, os, shutil, sys, tempfile from subprocess import check_call, check_output, CalledProcessError, Popen, PIPE from distutils.version import LooseVersion +versions = ['1.0.0', '1.1.0', '2.0.0', '3.0.2', '4.0.0', '4.1.0'] + def pip_install(package, commit=None, **kwargs): "Install package using pip." min_version = kwargs.get('min_version') @@ -93,11 +95,11 @@ def build_docs(version='dev', **kwargs): if p.returncode != 0: raise CalledProcessError(p.returncode, cmd) html_dir = os.path.join(work_dir, 'html') - versions = ['3.0.0', '2.0.0', '1.1.0'] + main_versions = reversed(versions[-3:]) check_call(['sphinx-build', '-Dbreathe_projects.format=' + os.path.abspath(doxyxml_dir), '-Dversion=' + version, '-Drelease=' + version, - '-Aversion=' + version, '-Aversions=' + ','.join(versions), + '-Aversion=' + version, '-Aversions=' + ','.join(main_versions), '-b', 'html', doc_dir, html_dir]) try: check_call(['lessc', '--clean-css', diff --git a/doc/index.rst b/doc/index.rst index ce9b7bf9..a00828b0 100644 --- a/doc/index.rst +++ b/doc/index.rst @@ -143,6 +143,12 @@ 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``. + .. _portability: Portability diff --git a/doc/syntax.rst b/doc/syntax.rst index 1051467a..e6d2efeb 100644 --- a/doc/syntax.rst +++ b/doc/syntax.rst @@ -335,6 +335,16 @@ Aligning the text and specifying a width:: format("{:*^30}", "centered"); // use '*' as a fill char // Result: "***********centered***********" +Dynamic width:: + + format("{:<{}}", "left aligned", 30); + // Result: "left aligned " + +Dynamic precision:: + + format("{:.{}f}", 3.14, 1); + // Result: "3.1" + Replacing ``%+f``, ``%-f``, and ``% f`` and specifying a sign:: format("{:+f}; {:+f}", 3.14, -3.14); // show it always @@ -350,7 +360,7 @@ Replacing ``%x`` and ``%o`` and converting the value to different bases:: // Result: "int: 42; hex: 2a; oct: 52; bin: 101010" // with 0x or 0 or 0b as prefix: format("int: {0:d}; hex: {0:#x}; oct: {0:#o}; bin: {0:#b}", 42); - // Result: "int: 42; hex: 0x2a; oct: 052; bin: 0b101010" + // Result: "int: 42; hex: 0x2a; oct: 052; bin: 0b101010" .. ifconfig:: False @@ -359,13 +369,6 @@ Replacing ``%x`` and ``%o`` and converting the value to different bases:: format("{:,}", 1234567890); '1,234,567,890' - Expressing a percentage:: - - >>> points = 19 - >>> total = 22 - Format("Correct answers: {:.2%}") << points/total) - 'Correct answers: 86.36%' - Using type-specific formatting:: >>> import datetime diff --git a/doc/usage.rst b/doc/usage.rst index dff312df..4c65f8ab 100644 --- a/doc/usage.rst +++ b/doc/usage.rst @@ -41,6 +41,10 @@ current directory. Now you can build the library by running :command:`make`. Once the library has been built you can invoke :command:`make test` to run the tests. +You can control generation of the make ``test`` target with the ``FMT_TEST`` +CMake option. This can be useful if you include fmt as a subdirectory in +your project but don't want to add fmt's tests to your ``test`` target. + If you use Windows and have Visual Studio installed, a :file:`FORMAT.sln` file and several :file:`.vcproj` files will be created. You can then build them using Visual Studio or msbuild. diff --git a/fmt/CMakeLists.txt b/fmt/CMakeLists.txt index 90eaf575..3259d788 100644 --- a/fmt/CMakeLists.txt +++ b/fmt/CMakeLists.txt @@ -49,8 +49,9 @@ endif () # Install targets. if (FMT_INSTALL) + include(GNUInstallDirs) include(CMakePackageConfigHelpers) - set(FMT_CMAKE_DIR lib/cmake/fmt CACHE STRING + set(FMT_CMAKE_DIR ${CMAKE_INSTALL_LIBDIR}/cmake/fmt CACHE STRING "Installation directory for cmake files, relative to ${CMAKE_INSTALL_PREFIX}.") set(version_config ${PROJECT_BINARY_DIR}/fmt-config-version.cmake) set(project_config ${PROJECT_BINARY_DIR}/fmt-config.cmake) @@ -61,9 +62,12 @@ if (FMT_INSTALL) set(INSTALL_TARGETS ${INSTALL_TARGETS} fmt-header-only) endif () - set(FMT_LIB_DIR lib CACHE STRING + set(FMT_LIB_DIR ${CMAKE_INSTALL_LIBDIR} CACHE STRING "Installation directory for libraries, relative to ${CMAKE_INSTALL_PREFIX}.") + set(FMT_INC_DIR ${CMAKE_INSTALL_INCLUDEDIR}/fmt CACHE STRING + "Installation directory for include files, relative to ${CMAKE_INSTALL_PREFIX}.") + # Generate the version, config and target files into the build directory. write_basic_package_version_file( ${version_config} @@ -86,5 +90,5 @@ if (FMT_INSTALL) # Install the library and headers. install(TARGETS ${INSTALL_TARGETS} EXPORT ${targets_export_name} DESTINATION ${FMT_LIB_DIR}) - install(FILES ${FMT_HEADERS} DESTINATION include/fmt) + install(FILES ${FMT_HEADERS} DESTINATION ${FMT_INC_DIR}) endif () diff --git a/fmt/format.cc b/fmt/format.cc index 09d2ea9f..2d236bc6 100644 --- a/fmt/format.cc +++ b/fmt/format.cc @@ -72,9 +72,11 @@ // Dummy implementations of strerror_r and strerror_s called if corresponding // system functions are not available. +FMT_MAYBE_UNUSED static inline fmt::internal::Null<> strerror_r(int, char *, ...) { return fmt::internal::Null<>(); } +FMT_MAYBE_UNUSED static inline fmt::internal::Null<> strerror_s(char *, std::size_t, ...) { return fmt::internal::Null<>(); } @@ -121,7 +123,7 @@ typedef void (*FormatFunc)(Writer &, int, StringRef); // Buffer should be at least of size 1. int safe_strerror( int error_code, char *&buffer, std::size_t buffer_size) FMT_NOEXCEPT { - FMT_ASSERT(buffer != 0 && buffer_size != 0, "invalid buffer"); + FMT_ASSERT(buffer != FMT_NULL && buffer_size != 0, "invalid buffer"); class StrError { private: @@ -159,6 +161,11 @@ int safe_strerror( ERANGE : result; } +#ifdef __c2__ +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wdeprecated-declarations" +#endif + // Fallback to strerror if strerror_r and strerror_s are not available. int fallback(internal::Null<>) { errno = 0; @@ -166,13 +173,15 @@ int safe_strerror( return errno; } +#ifdef __c2__ +# pragma clang diagnostic pop +#endif + public: StrError(int err_code, char *&buf, std::size_t buf_size) : error_code_(err_code), buffer_(buf), buffer_size_(buf_size) {} int run() { - // Suppress a warning about unused strerror_r. - strerror_r(0, FMT_NULL, ""); return handle(strerror_r(error_code_, buffer_, buffer_size_)); } }; @@ -396,51 +405,6 @@ FMT_FUNC void format_system_error( fmt::format_error_code(out, error_code, message); // 'fmt::' is for bcc32. } -template -void internal::ArgMap::init(const ArgList &args) { - if (!map_.empty()) - return; - typedef internal::NamedArg NamedArg; - const NamedArg *named_arg = FMT_NULL; - bool use_values = - args.type(ArgList::MAX_PACKED_ARGS - 1) == internal::Arg::NONE; - if (use_values) { - for (unsigned i = 0;/*nothing*/; ++i) { - internal::Arg::Type arg_type = args.type(i); - switch (arg_type) { - case internal::Arg::NONE: - return; - case internal::Arg::NAMED_ARG: - named_arg = static_cast(args.values_[i].pointer); - map_.push_back(Pair(named_arg->name, *named_arg)); - break; - default: - /*nothing*/; - } - } - return; - } - for (unsigned i = 0; i != ArgList::MAX_PACKED_ARGS; ++i) { - internal::Arg::Type arg_type = args.type(i); - if (arg_type == internal::Arg::NAMED_ARG) { - named_arg = static_cast(args.args_[i].pointer); - map_.push_back(Pair(named_arg->name, *named_arg)); - } - } - for (unsigned i = ArgList::MAX_PACKED_ARGS;/*nothing*/; ++i) { - switch (args.args_[i].type) { - case internal::Arg::NONE: - return; - case internal::Arg::NAMED_ARG: - named_arg = static_cast(args.args_[i].pointer); - map_.push_back(Pair(named_arg->name, *named_arg)); - break; - default: - /*nothing*/; - } - } -} - template void internal::FixedBuffer::grow(std::size_t) { FMT_THROW(std::runtime_error("buffer overflow")); @@ -502,8 +466,6 @@ template struct internal::BasicData; template void internal::FixedBuffer::grow(std::size_t); -template void internal::ArgMap::init(const ArgList &args); - template FMT_API int internal::CharTraits::format_float( char *buffer, std::size_t size, const char *format, unsigned width, int precision, double value); @@ -516,8 +478,6 @@ template FMT_API int internal::CharTraits::format_float( template void internal::FixedBuffer::grow(std::size_t); -template void internal::ArgMap::init(const ArgList &args); - template FMT_API int internal::CharTraits::format_float( wchar_t *buffer, std::size_t size, const wchar_t *format, unsigned width, int precision, double value); diff --git a/fmt/format.h b/fmt/format.h index 6ee9d2a2..561a9e07 100644 --- a/fmt/format.h +++ b/fmt/format.h @@ -28,6 +28,7 @@ #ifndef FMT_FORMAT_H_ #define FMT_FORMAT_H_ +#define FMT_INCLUDE #include #include #include @@ -39,11 +40,26 @@ #include #include #include // for std::pair +#undef FMT_INCLUDE // The fmt library version in the form major * 10000 + minor * 100 + patch. -#define FMT_VERSION 40000 +#define FMT_VERSION 40100 -#ifdef _SECURE_SCL +#if defined(__has_include) +# define FMT_HAS_INCLUDE(x) __has_include(x) +#else +# define FMT_HAS_INCLUDE(x) 0 +#endif + +#if (FMT_HAS_INCLUDE() && __cplusplus > 201402L) || \ + (defined(_MSVC_LANG) && _MSVC_LANG > 201402L && _MSC_VER >= 1910) +# include +# define FMT_HAS_STRING_VIEW 1 +#else +# define FMT_HAS_STRING_VIEW 0 +#endif + +#if defined _SECURE_SCL && _SECURE_SCL # define FMT_SECURE_SCL _SECURE_SCL #else # define FMT_SECURE_SCL 0 @@ -97,7 +113,9 @@ typedef __int64 intmax_t; # define FMT_HAS_GXX_CXX11 1 # endif #else +# define FMT_GCC_VERSION 0 # define FMT_GCC_EXTENSION +# define FMT_HAS_GXX_CXX11 0 #endif #if defined(__INTEL_COMPILER) @@ -135,6 +153,32 @@ typedef __int64 intmax_t; # define FMT_HAS_CPP_ATTRIBUTE(x) 0 #endif +#if FMT_HAS_CPP_ATTRIBUTE(maybe_unused) +# define FMT_HAS_CXX17_ATTRIBUTE_MAYBE_UNUSED +// VC++ 1910 support /std: option and that will set _MSVC_LANG macro +// Clang with Microsoft CodeGen doesn't define _MSVC_LANG macro +#elif defined(_MSVC_LANG) && _MSVC_LANG > 201402 && _MSC_VER >= 1910 +# define FMT_HAS_CXX17_ATTRIBUTE_MAYBE_UNUSED +#endif + +#ifdef FMT_HAS_CXX17_ATTRIBUTE_MAYBE_UNUSED +# define FMT_MAYBE_UNUSED [[maybe_unused]] +// g++/clang++ also support [[gnu::unused]]. However, we don't use it. +#elif defined(__GNUC__) +# define FMT_MAYBE_UNUSED __attribute__((unused)) +#else +# define FMT_MAYBE_UNUSED +#endif + +// Use the compiler's attribute noreturn +#if defined(__MINGW32__) || defined(__MINGW64__) +# define FMT_NORETURN __attribute__((noreturn)) +#elif FMT_HAS_CPP_ATTRIBUTE(noreturn) && __cplusplus >= 201103L +# define FMT_NORETURN [[noreturn]] +#else +# define FMT_NORETURN +#endif + #ifndef FMT_USE_VARIADIC_TEMPLATES // Variadic templates are available in GCC since version 4.4 // (http://gcc.gnu.org/projects/cxx0x.html) and in Visual C++ @@ -156,6 +200,12 @@ typedef __int64 intmax_t; # endif #endif +#if __cplusplus >= 201103L || FMT_MSC_VER >= 1700 +# define FMT_USE_ALLOCATOR_TRAITS 1 +#else +# define FMT_USE_ALLOCATOR_TRAITS 0 +#endif + // Check if exceptions are disabled. #if defined(__GNUC__) && !defined(__EXCEPTIONS) # define FMT_EXCEPTIONS 0 @@ -262,11 +312,14 @@ typedef __int64 intmax_t; // makes the fmt::literals implementation easier. However, an explicit check // for variadic templates is added here just in case. // For Intel's compiler both it and the system gcc/msc must support UDLs. -# define FMT_USE_USER_DEFINED_LITERALS \ - FMT_USE_VARIADIC_TEMPLATES && FMT_USE_RVALUE_REFERENCES && \ +# if FMT_USE_VARIADIC_TEMPLATES && FMT_USE_RVALUE_REFERENCES && \ (FMT_HAS_FEATURE(cxx_user_literals) || \ (FMT_GCC_VERSION >= 407 && FMT_HAS_GXX_CXX11) || FMT_MSC_VER >= 1900) && \ (!defined(FMT_ICC_VERSION) || FMT_ICC_VERSION >= 1500) +# define FMT_USE_USER_DEFINED_LITERALS 1 +# else +# define FMT_USE_USER_DEFINED_LITERALS 0 +# endif #endif #ifndef FMT_USE_EXTERN_TEMPLATES @@ -305,7 +358,10 @@ typedef __int64 intmax_t; namespace fmt { namespace internal { -# pragma intrinsic(_BitScanReverse) +// avoid Clang with Microsoft CodeGen's -Wunknown-pragmas warning +# ifndef __clang__ +# pragma intrinsic(_BitScanReverse) +# endif inline uint32_t clz(uint32_t x) { unsigned long r = 0; _BitScanReverse(&r, x); @@ -319,7 +375,8 @@ inline uint32_t clz(uint32_t x) { } # define FMT_BUILTIN_CLZ(n) fmt::internal::clz(n) -# ifdef _WIN64 +// avoid Clang with Microsoft CodeGen's -Wunknown-pragmas warning +# if defined(_WIN64) && !defined(__clang__) # pragma intrinsic(_BitScanReverse64) # endif @@ -505,6 +562,26 @@ class BasicStringRef { const std::basic_string, Allocator> &s) : data_(s.c_str()), size_(s.size()) {} +#if FMT_HAS_STRING_VIEW + /** + \rst + Constructs a string reference from a ``std::basic_string_view`` object. + \endrst + */ + BasicStringRef( + const std::basic_string_view> &s) + : data_(s.data()), size_(s.size()) {} + + /** + \rst + Converts a string reference to an ``std::string_view`` object. + \endrst + */ + explicit operator std::basic_string_view() const FMT_NOEXCEPT { + return std::basic_string_view(data_, size_); + } +#endif + /** \rst Converts a string reference to an ``std::string`` object. @@ -609,7 +686,7 @@ class FormatError : public std::runtime_error { explicit FormatError(CStringRef message) : std::runtime_error(message.c_str()) {} FormatError(const FormatError &ferr) : std::runtime_error(ferr) {} - FMT_API ~FormatError() FMT_DTOR_NOEXCEPT; + FMT_API ~FormatError() FMT_DTOR_NOEXCEPT FMT_OVERRIDE; }; namespace internal { @@ -726,7 +803,7 @@ template template void Buffer::append(const U *begin, const U *end) { FMT_ASSERT(end >= begin, "negative value"); - std::size_t new_size = size_ + (end - begin); + std::size_t new_size = size_ + static_cast(end - begin); if (new_size > capacity_) grow(new_size); std::uninitialized_copy(begin, end, @@ -754,7 +831,7 @@ class MemoryBuffer : private Allocator, public Buffer { public: explicit MemoryBuffer(const Allocator &alloc = Allocator()) : Allocator(alloc), Buffer(data_, SIZE) {} - ~MemoryBuffer() { deallocate(); } + ~MemoryBuffer() FMT_OVERRIDE { deallocate(); } #if FMT_USE_RVALUE_REFERENCES private: @@ -798,7 +875,12 @@ void MemoryBuffer::grow(std::size_t size) { std::size_t new_capacity = this->capacity_ + this->capacity_ / 2; if (size > new_capacity) new_capacity = size; +#if FMT_USE_ALLOCATOR_TRAITS + T *new_ptr = + std::allocator_traits::allocate(*this, new_capacity, FMT_NULL); +#else T *new_ptr = this->allocate(new_capacity, FMT_NULL); +#endif // The following code doesn't throw, so the raw pointer above doesn't leak. std::uninitialized_copy(this->ptr_, this->ptr_ + this->size_, make_ptr(new_ptr, new_capacity)); @@ -916,7 +998,7 @@ struct IntTraits { TypeSelector::digits <= 32>::Type MainType; }; -FMT_API void report_unknown_type(char code, const char *type); +FMT_API FMT_NORETURN void report_unknown_type(char code, const char *type); // Static data is placed in this class template to allow header-only // configuration. @@ -1155,17 +1237,17 @@ T &get(); Yes &convert(fmt::ULongLong); No &convert(...); -template +template struct ConvertToIntImpl { enum { value = ENABLE_CONVERSION }; }; -template +template struct ConvertToIntImpl2 { enum { value = false }; }; -template +template struct ConvertToIntImpl2 { enum { // Don't convert numeric types. @@ -1173,7 +1255,7 @@ struct ConvertToIntImpl2 { }; }; -template +template struct ConvertToInt { enum { enable_conversion = sizeof(fmt::internal::convert(get())) == sizeof(Yes) @@ -1190,16 +1272,16 @@ FMT_DISABLE_CONVERSION_TO_INT(float); FMT_DISABLE_CONVERSION_TO_INT(double); FMT_DISABLE_CONVERSION_TO_INT(long double); -template +template struct EnableIf {}; -template +template struct EnableIf { typedef T type; }; -template +template struct Conditional { typedef T type; }; -template +template struct Conditional { typedef F type; }; // For bcc32 which doesn't understand ! in template arguments. @@ -1248,9 +1330,9 @@ inline fmt::StringRef thousands_sep(...) { return ""; } typedef int FMT_CONCAT_(Assert, __LINE__)[(cond) ? 1 : -1] FMT_UNUSED #endif -template -void format_arg(Formatter &, const Char *, const T &) { - FMT_STATIC_ASSERT(FalseType::value, +template +void format_arg(Formatter&, ...) { + FMT_STATIC_ASSERT(FalseType::value, "Cannot format argument. To enable the use of ostream " "operator<< include fmt/ostream.h. Otherwise provide " "an overload of format_arg."); @@ -1283,6 +1365,9 @@ class MakeValue : public Arg { MakeValue(typename WCharHelper::Unsupported); MakeValue(typename WCharHelper::Unsupported); MakeValue(typename WCharHelper::Unsupported); +#if FMT_HAS_STRING_VIEW + MakeValue(typename WCharHelper::Unsupported); +#endif MakeValue(typename WCharHelper::Unsupported); void set_string(StringRef str) { @@ -1352,6 +1437,20 @@ class MakeValue : public Arg { FMT_MAKE_VALUE(unsigned char, uint_value, UINT) FMT_MAKE_VALUE(char, int_value, CHAR) +#if __cplusplus >= 201103L + template < + typename T, + typename = typename std::enable_if< + std::is_enum::value && ConvertToInt::value>::type> + MakeValue(T value) { int_value = value; } + + template < + typename T, + typename = typename std::enable_if< + std::is_enum::value && ConvertToInt::value>::type> + static uint64_t type(T) { return Arg::INT; } +#endif + #if !defined(_MSC_VER) || defined(_NATIVE_WCHAR_T_DEFINED) MakeValue(typename WCharHelper::Supported value) { int_value = value; @@ -1370,6 +1469,9 @@ class MakeValue : public Arg { FMT_MAKE_VALUE(unsigned char *, ustring.value, CSTRING) FMT_MAKE_VALUE(const unsigned char *, ustring.value, CSTRING) FMT_MAKE_STR_VALUE(const std::string &, STRING) +#if FMT_HAS_STRING_VIEW + FMT_MAKE_STR_VALUE(const std::string_view &, STRING) +#endif FMT_MAKE_STR_VALUE(StringRef, STRING) FMT_MAKE_VALUE_(CStringRef, string.value, CSTRING, value.c_str()) @@ -1382,6 +1484,9 @@ class MakeValue : public Arg { FMT_MAKE_WSTR_VALUE(wchar_t *, WSTRING) FMT_MAKE_WSTR_VALUE(const wchar_t *, WSTRING) FMT_MAKE_WSTR_VALUE(const std::wstring &, WSTRING) +#if FMT_HAS_STRING_VIEW + FMT_MAKE_WSTR_VALUE(const std::wstring_view &, WSTRING) +#endif FMT_MAKE_WSTR_VALUE(WStringRef, WSTRING) FMT_MAKE_VALUE(void *, pointer, POINTER) @@ -1447,7 +1552,7 @@ class RuntimeError : public std::runtime_error { protected: RuntimeError() : std::runtime_error("") {} RuntimeError(const RuntimeError &rerr) : std::runtime_error(rerr) {} - FMT_API ~RuntimeError() FMT_DTOR_NOEXCEPT; + FMT_API ~RuntimeError() FMT_DTOR_NOEXCEPT FMT_OVERRIDE; }; template @@ -1921,7 +2026,7 @@ class ArgMap { MapType map_; public: - FMT_API void init(const ArgList &args); + void init(const ArgList &args); const internal::Arg *find(const fmt::BasicStringRef &name) const { // The list is unsorted, so just return the first matching name. @@ -1934,6 +2039,51 @@ class ArgMap { } }; +template +void ArgMap::init(const ArgList &args) { + if (!map_.empty()) + return; + typedef internal::NamedArg NamedArg; + const NamedArg *named_arg = FMT_NULL; + bool use_values = + args.type(ArgList::MAX_PACKED_ARGS - 1) == internal::Arg::NONE; + if (use_values) { + for (unsigned i = 0;/*nothing*/; ++i) { + internal::Arg::Type arg_type = args.type(i); + switch (arg_type) { + case internal::Arg::NONE: + return; + case internal::Arg::NAMED_ARG: + named_arg = static_cast(args.values_[i].pointer); + map_.push_back(Pair(named_arg->name, *named_arg)); + break; + default: + /*nothing*/; + } + } + return; + } + for (unsigned i = 0; i != ArgList::MAX_PACKED_ARGS; ++i) { + internal::Arg::Type arg_type = args.type(i); + if (arg_type == internal::Arg::NAMED_ARG) { + named_arg = static_cast(args.args_[i].pointer); + map_.push_back(Pair(named_arg->name, *named_arg)); + } + } + for (unsigned i = ArgList::MAX_PACKED_ARGS;/*nothing*/; ++i) { + switch (args.args_[i].type) { + case internal::Arg::NONE: + return; + case internal::Arg::NAMED_ARG: + named_arg = static_cast(args.args_[i].pointer); + map_.push_back(Pair(named_arg->name, *named_arg)); + break; + default: + /*nothing*/; + } + } +} + template class ArgFormatterBase : public ArgVisitor { private: @@ -2220,7 +2370,8 @@ struct ArgArray; template struct ArgArray { - typedef Value Type[N > 0 ? N : 1]; + // '+' is used to silence GCC -Wduplicated-branches warning. + typedef Value Type[N > 0 ? N : +1]; template static Value make(const T &value) { @@ -2410,7 +2561,7 @@ class SystemError : public internal::RuntimeError { FMT_DEFAULTED_COPY_CTOR(SystemError) FMT_VARIADIC_CTOR(SystemError, init, int, CStringRef) - FMT_API ~SystemError() FMT_DTOR_NOEXCEPT; + FMT_API ~SystemError() FMT_DTOR_NOEXCEPT FMT_OVERRIDE; int error_code() const { return error_code_; } }; @@ -3483,10 +3634,10 @@ void arg(WStringRef, const internal::NamedArg&) FMT_DELETED_OR_UNDEFINED; #define FMT_GET_ARG_NAME(type, index) arg##index #if FMT_USE_VARIADIC_TEMPLATES -# define FMT_VARIADIC_(Char, ReturnType, func, call, ...) \ +# define FMT_VARIADIC_(Const, Char, ReturnType, func, call, ...) \ template \ ReturnType func(FMT_FOR_EACH(FMT_ADD_ARG_NAME, __VA_ARGS__), \ - const Args & ... args) { \ + const Args & ... args) Const { \ typedef fmt::internal::ArgArray ArgArray; \ typename ArgArray::Type array{ \ ArgArray::template make >(args)...}; \ @@ -3496,35 +3647,35 @@ void arg(WStringRef, const internal::NamedArg&) FMT_DELETED_OR_UNDEFINED; #else // Defines a wrapper for a function taking __VA_ARGS__ arguments // and n additional arguments of arbitrary types. -# define FMT_WRAP(Char, ReturnType, func, call, n, ...) \ +# define FMT_WRAP(Const, Char, ReturnType, func, call, n, ...) \ template \ inline ReturnType func(FMT_FOR_EACH(FMT_ADD_ARG_NAME, __VA_ARGS__), \ - FMT_GEN(n, FMT_MAKE_ARG)) { \ + FMT_GEN(n, FMT_MAKE_ARG)) Const { \ fmt::internal::ArgArray::Type arr; \ FMT_GEN(n, FMT_ASSIGN_##Char); \ call(FMT_FOR_EACH(FMT_GET_ARG_NAME, __VA_ARGS__), fmt::ArgList( \ fmt::internal::make_type(FMT_GEN(n, FMT_MAKE_REF2)), arr)); \ } -# define FMT_VARIADIC_(Char, ReturnType, func, call, ...) \ - inline ReturnType func(FMT_FOR_EACH(FMT_ADD_ARG_NAME, __VA_ARGS__)) { \ +# define FMT_VARIADIC_(Const, Char, ReturnType, func, call, ...) \ + inline ReturnType func(FMT_FOR_EACH(FMT_ADD_ARG_NAME, __VA_ARGS__)) Const { \ call(FMT_FOR_EACH(FMT_GET_ARG_NAME, __VA_ARGS__), fmt::ArgList()); \ } \ - FMT_WRAP(Char, ReturnType, func, call, 1, __VA_ARGS__) \ - FMT_WRAP(Char, ReturnType, func, call, 2, __VA_ARGS__) \ - FMT_WRAP(Char, ReturnType, func, call, 3, __VA_ARGS__) \ - FMT_WRAP(Char, ReturnType, func, call, 4, __VA_ARGS__) \ - FMT_WRAP(Char, ReturnType, func, call, 5, __VA_ARGS__) \ - FMT_WRAP(Char, ReturnType, func, call, 6, __VA_ARGS__) \ - FMT_WRAP(Char, ReturnType, func, call, 7, __VA_ARGS__) \ - FMT_WRAP(Char, ReturnType, func, call, 8, __VA_ARGS__) \ - FMT_WRAP(Char, ReturnType, func, call, 9, __VA_ARGS__) \ - FMT_WRAP(Char, ReturnType, func, call, 10, __VA_ARGS__) \ - FMT_WRAP(Char, ReturnType, func, call, 11, __VA_ARGS__) \ - FMT_WRAP(Char, ReturnType, func, call, 12, __VA_ARGS__) \ - FMT_WRAP(Char, ReturnType, func, call, 13, __VA_ARGS__) \ - FMT_WRAP(Char, ReturnType, func, call, 14, __VA_ARGS__) \ - FMT_WRAP(Char, ReturnType, func, call, 15, __VA_ARGS__) + FMT_WRAP(Const, Char, ReturnType, func, call, 1, __VA_ARGS__) \ + FMT_WRAP(Const, Char, ReturnType, func, call, 2, __VA_ARGS__) \ + FMT_WRAP(Const, Char, ReturnType, func, call, 3, __VA_ARGS__) \ + FMT_WRAP(Const, Char, ReturnType, func, call, 4, __VA_ARGS__) \ + FMT_WRAP(Const, Char, ReturnType, func, call, 5, __VA_ARGS__) \ + FMT_WRAP(Const, Char, ReturnType, func, call, 6, __VA_ARGS__) \ + FMT_WRAP(Const, Char, ReturnType, func, call, 7, __VA_ARGS__) \ + FMT_WRAP(Const, Char, ReturnType, func, call, 8, __VA_ARGS__) \ + FMT_WRAP(Const, Char, ReturnType, func, call, 9, __VA_ARGS__) \ + FMT_WRAP(Const, Char, ReturnType, func, call, 10, __VA_ARGS__) \ + FMT_WRAP(Const, Char, ReturnType, func, call, 11, __VA_ARGS__) \ + FMT_WRAP(Const, Char, ReturnType, func, call, 12, __VA_ARGS__) \ + FMT_WRAP(Const, Char, ReturnType, func, call, 13, __VA_ARGS__) \ + FMT_WRAP(Const, Char, ReturnType, func, call, 14, __VA_ARGS__) \ + FMT_WRAP(Const, Char, ReturnType, func, call, 15, __VA_ARGS__) #endif // FMT_USE_VARIADIC_TEMPLATES /** @@ -3555,10 +3706,16 @@ void arg(WStringRef, const internal::NamedArg&) FMT_DELETED_OR_UNDEFINED; \endrst */ #define FMT_VARIADIC(ReturnType, func, ...) \ - FMT_VARIADIC_(char, ReturnType, func, return func, __VA_ARGS__) + FMT_VARIADIC_(, char, ReturnType, func, return func, __VA_ARGS__) + +#define FMT_VARIADIC_CONST(ReturnType, func, ...) \ + FMT_VARIADIC_(const, char, ReturnType, func, return func, __VA_ARGS__) #define FMT_VARIADIC_W(ReturnType, func, ...) \ - FMT_VARIADIC_(wchar_t, ReturnType, func, return func, __VA_ARGS__) + FMT_VARIADIC_(, wchar_t, ReturnType, func, return func, __VA_ARGS__) + +#define FMT_VARIADIC_CONST_W(ReturnType, func, ...) \ + FMT_VARIADIC_(const, wchar_t, ReturnType, func, return func, __VA_ARGS__) #define FMT_CAPTURE_ARG_(id, index) ::fmt::arg(#id, id) @@ -3601,17 +3758,19 @@ template unsigned parse_nonnegative_int(const Char *&s) { assert('0' <= *s && *s <= '9'); unsigned value = 0; - do { - unsigned new_value = value * 10 + (*s++ - '0'); - // Check if value wrapped around. - if (new_value < value) { - value = (std::numeric_limits::max)(); - break; - } - value = new_value; - } while ('0' <= *s && *s <= '9'); // Convert to unsigned to prevent a warning. unsigned max_int = (std::numeric_limits::max)(); + unsigned big = max_int / 10; + do { + // Check for overflow. + if (value > big) { + value = max_int + 1; + break; + } + value = value * 10 + (*s - '0'); + ++s; + } while ('0' <= *s && *s <= '9'); + // Convert to unsigned to prevent a warning. if (value > max_int) FMT_THROW(FormatError("number is too big")); return value; @@ -3783,7 +3942,8 @@ const Char *BasicFormatter::format( default: FMT_THROW(FormatError("width is not integer")); } - if (value > (std::numeric_limits::max)()) + unsigned max_int = (std::numeric_limits::max)(); + if (value > max_int) FMT_THROW(FormatError("number is too big")); spec.width_ = static_cast(value); } @@ -3821,7 +3981,8 @@ const Char *BasicFormatter::format( default: FMT_THROW(FormatError("precision is not integer")); } - if (value > (std::numeric_limits::max)()) + unsigned max_int = (std::numeric_limits::max)(); + if (value > max_int) FMT_THROW(FormatError("number is too big")); spec.precision_ = static_cast(value); } else { diff --git a/fmt/ostream.h b/fmt/ostream.h index 84a02d17..6848aac2 100644 --- a/fmt/ostream.h +++ b/fmt/ostream.h @@ -52,13 +52,15 @@ Yes &convert(std::ostream &); struct DummyStream : std::ostream { DummyStream(); // Suppress a bogus warning in MSVC. + // Hide all operator<< overloads from std::ostream. - void operator<<(Null<>); + template + typename EnableIf::type operator<<(const T &); }; No &operator<<(std::ostream &, int); -template +template struct ConvertToIntImpl { // Convert to int only if T doesn't have an overloaded operator<<. enum { @@ -78,6 +80,7 @@ void format_arg(BasicFormatter &f, internal::FormatBuf format_buf(buffer); std::basic_ostream output(&format_buf); + output.exceptions(std::ios_base::failbit | std::ios_base::badbit); output << value; BasicStringRef str(&buffer[0], buffer.size()); diff --git a/fmt/printf.h b/fmt/printf.h index 30cbc49b..46205a78 100644 --- a/fmt/printf.h +++ b/fmt/printf.h @@ -110,7 +110,7 @@ class ArgConverter : public ArgVisitor, void> { visit_any_int(value); } - void visit_char(char value) { + void visit_char(int value) { if (type_ != 's') visit_any_int(value); } @@ -125,7 +125,7 @@ class ArgConverter : public ArgVisitor, void> { using internal::Arg; typedef typename internal::Conditional< is_same::value, U, T>::type TargetType; - if (sizeof(TargetType) <= sizeof(int)) { + if (const_check(sizeof(TargetType) <= sizeof(int))) { // Extra casts are used to silence warnings. if (is_signed) { arg_.type = Arg::INT; diff --git a/fmt/string.h b/fmt/string.h index ccf46ee1..05996eb5 100644 --- a/fmt/string.h +++ b/fmt/string.h @@ -7,6 +7,10 @@ For the license information refer to format.h. */ +#ifdef FMT_INCLUDE +# error "Add the fmt's parent directory and not fmt itself to includes." +#endif + #ifndef FMT_STRING_H_ #define FMT_STRING_H_ @@ -121,6 +125,24 @@ std::string to_string(const T &value) { w << value; return w.str(); } + +/** + \rst + Converts *value* to ``std::wstring`` using the default format for type *T*. + + **Example**:: + + #include "fmt/string.h" + + std::wstring answer = fmt::to_wstring(42); + \endrst + */ +template +std::wstring to_wstring(const T &value) { + fmt::WMemoryWriter w; + w << value; + return w.str(); +} } #endif // FMT_STRING_H_ diff --git a/support/manage.py b/support/manage.py index 1da371d4..baafe805 100755 --- a/support/manage.py +++ b/support/manage.py @@ -8,7 +8,7 @@ Usage: """ from __future__ import print_function -import datetime, docopt, fileinput, json, os +import datetime, docopt, errno, fileinput, json, os import re, requests, shutil, sys, tempfile from contextlib import contextmanager from distutils.version import LooseVersion @@ -80,6 +80,7 @@ def create_build_env(): import build env.build_dir = 'build' + env.versions = build.versions # Virtualenv and repos are cached to speed up builds. build.create_build_env(os.path.join(env.build_dir, 'virtualenv')) @@ -113,7 +114,7 @@ def update_site(env): doc_repo = Git(os.path.join(env.build_dir, 'fmtlib.github.io')) doc_repo.update('git@github.com:fmtlib/fmtlib.github.io') - for version in ['1.0.0', '1.1.0', '2.0.0', '3.0.0']: + for version in env.versions: clean_checkout(env.fmt_repo, version) target_doc_dir = os.path.join(env.fmt_repo.dir, 'doc') # Remove the old theme. @@ -165,7 +166,11 @@ def update_site(env): os.symlink(target, link) # Copy docs to the website. version_doc_dir = os.path.join(doc_repo.dir, version) - shutil.rmtree(version_doc_dir) + try: + shutil.rmtree(version_doc_dir) + except OSError as e: + if e.errno != errno.ENOENT: + raise shutil.move(html_dir, version_doc_dir) @@ -204,27 +209,45 @@ def release(args): line = '-' * title_len + '\n' title_len = 0 sys.stdout.write(line) - # TODO: add new version to manage.py + + # Add the version to the build script. + script = os.path.join('doc', 'build.py') + script_path = os.path.join(fmt_repo.dir, script) + for line in fileinput.input(script_path, inplace=True): + m = re.match(r'( *versions = )\[(.+)\]', line) + if m: + line = '{}[{}, \'{}\']\n'.format(m.group(1), m.group(2), version) + sys.stdout.write(line) + fmt_repo.checkout('-B', 'release') - fmt_repo.add(changelog, cmakelists) + fmt_repo.add(changelog, cmakelists, script) fmt_repo.commit('-m', 'Update version') # Build the docs and package. run = Runner(fmt_repo.dir) run('cmake', '.') run('make', 'doc', 'package_source') - update_site(env) # Create a release on GitHub. fmt_repo.push('origin', 'release') + params = {'access_token': os.getenv('FMT_TOKEN')} r = requests.post('https://api.github.com/repos/fmtlib/fmt/releases', - params={'access_token': os.getenv('FMT_TOKEN')}, + params=params, data=json.dumps({'tag_name': version, 'target_commitish': 'release', 'body': changes, 'draft': True})) if r.status_code != 201: raise Exception('Failed to create a release ' + str(r)) + id = r.json()['id'] + uploads_url = 'https://uploads.github.com/repos/fmtlib/fmt/releases' + package = 'fmt-{}.zip'.format(version) + with open('build/fmt/' + package, 'rb') as f: + r = requests.post( + '{}/{}/assets?name={}'.format(uploads_url, id, package), + params=params, files={package: f}) + if r.status_code != 201: + raise Exception('Failed to upload an asset ' + str(r)) if __name__ == '__main__': diff --git a/support/travis-build.py b/support/travis-build.py index 91017792..c4b8daf1 100755 --- a/support/travis-build.py +++ b/support/travis-build.py @@ -30,7 +30,7 @@ def install_dependencies(): '| sudo tee /etc/apt/sources.list.d/nodesource.list', shell=True) check_call(['sudo', 'apt-get', 'update']) check_call(['sudo', 'apt-get', 'install', 'python-virtualenv', 'nodejs']) - check_call(['npm', 'install', '-g', 'less', 'less-plugin-clean-css']) + check_call(['sudo', 'npm', 'install', '-g', 'less@2.6.1', 'less-plugin-clean-css']) deb_file = 'doxygen_1.8.6-2_amd64.deb' urllib.urlretrieve('http://mirrors.kernel.org/ubuntu/pool/main/d/doxygen/' + deb_file, deb_file) diff --git a/test/format-test.cc b/test/format-test.cc index 6388d5a5..f512ef48 100644 --- a/test/format-test.cc +++ b/test/format-test.cc @@ -151,16 +151,31 @@ TEST(StringRefTest, Ctor) { EXPECT_STREQ("defg", StringRef(std::string("defg")).data()); EXPECT_EQ(4u, StringRef(std::string("defg")).size()); + +#if FMT_HAS_STRING_VIEW + EXPECT_STREQ("hijk", StringRef(std::string_view("hijk")).data()); + EXPECT_EQ(4u, StringRef(std::string_view("hijk")).size()); +#endif } TEST(StringRefTest, ConvertToString) { std::string s = StringRef("abc").to_string(); EXPECT_EQ("abc", s); + +#if FMT_HAS_STRING_VIEW + StringRef str_ref("defg"); + std::string_view sv = static_cast(str_ref); + EXPECT_EQ("defg", sv); +#endif } TEST(CStringRefTest, Ctor) { EXPECT_STREQ("abc", CStringRef("abc").c_str()); EXPECT_STREQ("defg", CStringRef(std::string("defg")).c_str()); + +#if FMT_HAS_STRING_VIEW + EXPECT_STREQ("hijk", CStringRef(std::string_view("hijk")).c_str()); +#endif } #if FMT_USE_TYPE_TRAITS @@ -1378,6 +1393,12 @@ TEST(FormatterTest, FormatCStringRef) { EXPECT_EQ("test", format("{0}", CStringRef("test"))); } +#if FMT_HAS_STRING_VIEW +TEST(FormatterTest, FormatStringView) { + EXPECT_EQ("test", format("{0}", std::string_view("test"))); +} +#endif + void format_arg(fmt::BasicFormatter &f, const char *, const Date &d) { f.writer() << d.year() << '-' << d.month() << '-' << d.day(); } @@ -1609,6 +1630,24 @@ TEST(FormatTest, FormatMessageExample) { format_message(42, "{} happened", "something")); } +class test_class +{ +public: + std::string format_message(int id, const char *format,const fmt::ArgList &args) const { + MemoryWriter w; + w.write("[{}] ", id); + w.write(format, args); + return w.str(); + } + FMT_VARIADIC_CONST(std::string, format_message, int, const char *) +}; + +TEST(FormatTest, ConstFormatMessage) { + test_class c; + EXPECT_EQ("[42] something happened", + c.format_message(42, "{} happened", "something")); +} + #if FMT_USE_VARIADIC_TEMPLATES template void print_error(const char *file, int line, const char *format, @@ -1660,6 +1699,14 @@ TEST(FormatTest, Enum) { EXPECT_EQ("0", fmt::format("{}", A)); } +#if __cplusplus >= 201103L +enum TestFixedEnum : short { B }; + +TEST(FormatTest, FixedEnum) { + EXPECT_EQ("0", fmt::format("{}", B)); +} +#endif + class MockArgFormatter : public fmt::internal::ArgFormatterBase { public: diff --git a/test/mock-allocator.h b/test/mock-allocator.h index 34b9c11a..5f0f0bc4 100644 --- a/test/mock-allocator.h +++ b/test/mock-allocator.h @@ -36,7 +36,7 @@ class MockAllocator { MockAllocator() {} MockAllocator(const MockAllocator &) {} typedef T value_type; - MOCK_METHOD2_T(allocate, T *(std::size_t n, const T *h)); + MOCK_METHOD2_T(allocate, T *(std::size_t n, const void *h)); MOCK_METHOD2_T(deallocate, void (T *p, std::size_t n)); }; @@ -78,8 +78,12 @@ class AllocatorRef { Allocator *get() const { return alloc_; } - value_type *allocate(std::size_t n, const value_type *h) { + value_type *allocate(std::size_t n, const void *h) { +#if FMT_USE_ALLOCATOR_TRAITS + return std::allocator_traits::allocate(*alloc_, n, h); +#else return alloc_->allocate(n, h); +#endif } void deallocate(value_type *p, std::size_t n) { alloc_->deallocate(p, n); } }; diff --git a/test/ostream-test.cc b/test/ostream-test.cc index 4081b43f..486981bc 100644 --- a/test/ostream-test.cc +++ b/test/ostream-test.cc @@ -172,3 +172,18 @@ TEST(OStreamTest, WriteToOStreamMaxSize) { } while (size != 0); fmt::internal::write(os, w); } + +struct ConvertibleToInt { + template + operator ValueType() const { + return 0; + } + + friend std::ostream &operator<<(std::ostream &o, ConvertibleToInt) { + return o << "foo"; + } +}; + +TEST(FormatTest, FormatConvertibleToInt) { + EXPECT_EQ("foo", fmt::format("{}", ConvertibleToInt())); +} diff --git a/test/string-test.cc b/test/string-test.cc index 10e537b7..06f55f65 100644 --- a/test/string-test.cc +++ b/test/string-test.cc @@ -78,3 +78,7 @@ TEST(StringWriterTest, WString) { TEST(StringTest, ToString) { EXPECT_EQ("42", fmt::to_string(42)); } + +TEST(StringTest, ToWString) { + EXPECT_EQ(L"42", fmt::to_wstring(42)); +} diff --git a/test/util-test.cc b/test/util-test.cc index a3882558..6617b857 100644 --- a/test/util-test.cc +++ b/test/util-test.cc @@ -837,8 +837,21 @@ TEST(UtilTest, FormatSystemError) { fmt::format_system_error(message, EDOM, "test"); EXPECT_EQ(fmt::format("test: {}", get_system_error(EDOM)), message.str()); message.clear(); - fmt::format_system_error( - message, EDOM, fmt::StringRef(0, std::numeric_limits::max())); + + // Check if std::allocator throws on allocating max size_t / 2 chars. + size_t max_size = std::numeric_limits::max() / 2; + bool throws_on_alloc = false; + try { + std::allocator alloc; + alloc.deallocate(alloc.allocate(max_size), max_size); + } catch (std::bad_alloc) { + throws_on_alloc = true; + } + if (!throws_on_alloc) { + fmt::print("warning: std::allocator allocates {} chars", max_size); + return; + } + fmt::format_system_error(message, EDOM, fmt::StringRef(0, max_size)); EXPECT_EQ(fmt::format("error {}", EDOM), message.str()); }