diff --git a/README.rst b/README.rst index 0b802058..757d0b99 100644 --- a/README.rst +++ b/README.rst @@ -42,8 +42,8 @@ Features performance of IOStreams. See `Speed tests`_ and `Fast integer to string conversion in C++ `_. -* Small code size both in terms of source code (the core library consists of a - single header file and a single source file) and compiled code. +* Small code size both in terms of source code (the core library consists of two + header files and a single source file) and compiled code. See `Compile time and code bloat`_. * Reliability: the library has an extensive set of `unit tests `_. @@ -84,24 +84,24 @@ Format strings can be checked at compile time: .. code:: c++ // test.cc - using namespace fmt::literals; - std::string s = "{2}"_format(42); + #include + std::string s = fmt::format(fmt("{2}"), 42); .. code:: $ g++ -Iinclude test.cc -std=c++14 ... - test.cc:5:31: note: in instantiation of function template specialization - 'fmt::internal::udl_formatter::operator()' requested - here - std::string s = "{2}"_format(42); - ^ - include/fmt/format.h:3838:7: note: non-constexpr function 'on_error' cannot be - used in a constant expression - on_error("argument index out of range"); - ^ + test.cc:2:22: note: in instantiation of function template specialization 'fmt::format' requested here + std::string s = fmt::format(fmt("{2}"), 42); + ^ + include/fmt/core.h:749:19: note: non-constexpr function 'on_error' cannot be used in a constant expression + ErrorHandler::on_error(message); + ^ + include/fmt/format.h:2081:16: note: in call to '&checker.context_->on_error(&"argument index out of range"[0])' + context_.on_error("argument index out of range"); + ^ -fmt can be used as a safe portable replacement for ``itoa``: +{fmt} can be used as a safe portable replacement for ``itoa``: .. code:: c++ @@ -159,6 +159,8 @@ Projects using this library * `AMPL/MP `_: An open-source library for mathematical programming + +* `AvioBook `_: A comprehensive aircraft operations suite. * `CUAUV `_: Cornell University's autonomous underwater vehicle diff --git a/doc/api.rst b/doc/api.rst index 4c18268c..e888f94f 100644 --- a/doc/api.rst +++ b/doc/api.rst @@ -4,52 +4,249 @@ API Reference ************* -All functions and classes provided by the fmt library reside -in namespace ``fmt`` and macros have prefix ``FMT_``. For brevity the -namespace is usually omitted in examples. +The {fmt} library API consists of the following parts: -Format API -========== +* :ref:`fmt/core.h `: the core API providing argument handling + facilities and a lightweight subset of formatting functions +* :ref:`fmt/format.h `: the full format API providing compile-time + format string checks, output iterator and user-defined type support +* :ref:`fmt/time.h `: date and time formatting +* :ref:`fmt/ostream.h `: ``std::ostream`` support +* :ref:`fmt/printf.h `: ``printf`` formatting -The following functions defined in ``fmt/format.h`` use :ref:`format string -syntax ` similar to the one used by Python's `str.format -`_ function. +All functions and types provided by the library reside in namespace ``fmt`` and +macros have prefix ``FMT_`` or ``fmt``. + +.. _core-api: + +Core API +======== + +``fmt/core.h`` defines the core API which provides argument handling facilities +and a lightweight subset of formatting functions. + +The following functions use :ref:`format string syntax ` +imilar to that of Python's `str.format +`_. They take *format_str* and *args* as arguments. *format_str* is a format string that contains literal text and replacement fields surrounded by braces ``{}``. The fields are replaced with formatted arguments in the resulting string. -*args* is an argument list representing arbitrary arguments. - -The `performance of the format API -`_ is close -to that of glibc's ``printf`` and better than the performance of IOStreams. -For even better speed use the `write API`_. +*args* is an argument list representing objects to be formatted. .. _format: -.. doxygenfunction:: format(CStringRef, ArgList) - -.. doxygenfunction:: operator""_format(const char *, std::size_t) +.. doxygenfunction:: format(string_view, const Args&...) +.. doxygenfunction:: vformat(string_view, format_args) .. _print: -.. doxygenfunction:: print(CStringRef, ArgList) +.. doxygenfunction:: print(string_view, const Args&...) +.. doxygenfunction:: vprint(string_view, format_args) -.. doxygenfunction:: print(std::FILE *, CStringRef, ArgList) +.. doxygenfunction:: print(std::FILE *, string_view, const Args&...) +.. doxygenfunction:: vprint(std::FILE *, string_view, format_args) -.. doxygenclass:: fmt::BasicFormatter +.. _format-api: + +Named arguments +--------------- + +.. doxygenfunction:: fmt::arg(string_view, const T&) + +Argument lists +-------------- + +.. doxygenclass:: fmt::basic_format_args :members: +.. doxygenstruct:: fmt::format_args + +.. doxygenfunction:: fmt::make_args(const Args&...) + +Compatibility +------------- + +.. doxygenclass:: fmt::basic_string_view + :members: + +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 +--------------------------------- + +.. doxygendefine:: fmt + +Formatting user-defined types +----------------------------- + +To make a user-defined type formattable, specialize the ``formatter`` struct +template and implement ``parse`` and ``format`` methods:: + + #include + + struct point { double x, y; }; + + namespace fmt { + template <> + struct formatter { + template + constexpr auto parse(ParseContext &ctx) { return ctx.begin(); } + + template + auto format(const point &p, FormatContext &ctx) { + return format_to(ctx.begin(), "({:.1f}, {:.1f})", p.x, p.y); + } + }; + } + +Then you can pass objects of type ``point`` to any formatting function:: + + point p = {1, 2}; + std::string s = fmt::format("{}", p); + // s == "(1.0, 2.0)" + +In the example above the ``formatter::parse`` function ignores the +contents of the format string referred to by ``ctx.begin()`` so the object will +always be formatted in the same way. See ``formatter::parse`` in +:file:`fmt/time.h` for an advanced example of how to parse the format string and +customize the formatted output. + +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 +----------------------- + +.. doxygenfunction:: fmt::format_to(OutputIt, string_view, const Args&...) + +Literal-based API +----------------- + +The following user-defined literals are defined in ``fmt/format.h``. + +.. doxygenfunction:: operator""_format(const char *, std::size_t) + +.. doxygenfunction:: operator""_a(const char *, std::size_t) + +Utilities +--------- + +.. doxygenfunction:: fmt::to_string(const T&) + +.. doxygenclass:: fmt::basic_memory_buffer + :protected-members: + :members: + +System errors +------------- + +.. doxygenclass:: fmt::system_error + :members: + +.. doxygenfunction:: fmt::format_system_error + +.. doxygenclass:: fmt::windows_error + :members: + +.. _formatstrings: + +Custom allocators +----------------- + +The {fmt} library supports custom dynamic memory allocators. +A custom allocator class can be specified as a template argument to +:class:`fmt::basic_memory_buffer`:: + + using custom_memory_buffer = + fmt::basic_memory_buffer; + +It is also possible to write a formatting function that uses a custom +allocator:: + + using custom_string = + std::basic_string, custom_allocator>; + + custom_string vformat(custom_allocator alloc, fmt::string_view format_str, + fmt::format_args args) { + custom_memory_buffer buf(alloc); + fmt::vformat_to(buf, format_str, args); + return custom_string(buf.data(), buf.size(), alloc); + } + + template + inline custom_string format(custom_allocator alloc, + fmt::string_view format_str, + const Args & ... args) { + return vformat(alloc, format_str, fmt::make_args(args...)); + } +Custom formatting of built-in types +----------------------------------- + +It is possible to change the way arguments are formatted by providing a +custom argument formatter class:: + + using arg_formatter = + fmt::arg_formatter>; + + // A custom argument formatter that formats negative integers as unsigned + // with the ``x`` format specifier. + class custom_arg_formatter : public arg_formatter { + public: + custom_arg_formatter(fmt::context &ctx, fmt::format_specs &spec) + : arg_formatter(ctx, spec) {} + + using arg_formatter::operator(); + + void operator()(int value) { + if (spec().type() == 'x') + (*this)(static_cast(value)); // convert to unsigned and format + else + arg_formatter::operator()(value); + } + }; + + std::string custom_vformat(fmt::string_view format_str, fmt::format_args args) { + fmt::memory_buffer buffer; + // Pass custom argument formatter as a template arg to vformat_to. + fmt::vformat_to(buffer, format_str, args); + return fmt::to_string(buffer); + } + + template + inline std::string custom_format( + fmt::string_view format_str, const Args &... args) { + return custom_vformat(format_str, fmt::make_args(args...)); + } + + std::string s = custom_format("{:x}", -42); // s == "ffffffd6" + +.. doxygenclass:: fmt::ArgVisitor + :members: + +.. doxygenclass:: fmt::arg_formatter_base + :members: + +.. doxygenclass:: fmt::arg_formatter + :members: + +.. _time-api: + Date and time formatting ------------------------- +======================== The library supports `strftime `_-like date and time formatting:: - #include "fmt/time.h" + #include std::time_t t = std::time(nullptr); // Prints "The date is 2016-04-29." (with the current date) @@ -58,111 +255,35 @@ formatting:: The format string syntax is described in the documentation of `strftime `_. -Formatting user-defined types ------------------------------ - -To make a user-defined type formattable, specialize the ``formatter`` struct -template and implement ``parse`` and ``format`` methods:: - - struct MyStruct { double x, y; }; - - namespace fmt { - template <> - struct formatter { - template - auto parse(ParseContext &ctx) { return ctx.begin(); } - - template - auto format(const MyStruct &s, FormatContext &ctx) { - fmt::format_to(ctx.begin(), "[MyStruct: x={:.1f}, y={:.2f}]", s.x, s.y); - } - }; - } - -Then you can pass objects of type ``MyStruct`` to any formatting function:: - - MyStruct m = {1, 2}; - std::string s = fmt::format("m={}", m); - // s == "m=[MyStruct: x=1.0, y=2.00]" - -In the example above the ``formatter::parse`` function ignores the -contents of the format string referred to by ``ctx.begin()`` so the object will -always be formatted as specified. See ``formatter::parse`` in -:file:`fmt/time.h` for an advanced example of how to parse the format string and -customize the formatted output. - -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. +.. _ostream-api: ``std::ostream`` support ------------------------- +======================== -The header ``fmt/ostream.h`` provides ``std::ostream`` support including -formatting of user-defined types that have overloaded ``operator<<``:: +``fmt/ostream.h`` provides ``std::ostream`` support including formatting of +user-defined types that have overloaded ``operator<<``:: - #include "fmt/ostream.h" + #include - class Date { + class date { int year_, month_, day_; public: - Date(int year, int month, int day): year_(year), month_(month), day_(day) {} + date(int year, int month, int day): year_(year), month_(month), day_(day) {} - friend std::ostream &operator<<(std::ostream &os, const Date &d) { + friend std::ostream &operator<<(std::ostream &os, const date &d) { return os << d.year_ << '-' << d.month_ << '-' << d.day_; } }; - std::string s = fmt::format("The date is {}", Date(2012, 12, 9)); + std::string s = fmt::format("The date is {}", date(2012, 12, 9)); // s == "The date is 2012-12-9" -.. doxygenfunction:: print(std::ostream&, CStringRef, ArgList) +.. doxygenfunction:: print(std::ostream&, string_view, const Args&...) -Argument formatters -------------------- +.. _printf-api: -It is possible to change the way arguments are formatted by providing a -custom argument formatter class:: - - // A custom argument formatter that formats negative integers as unsigned - // with the ``x`` format specifier. - class CustomArgFormatter : - public fmt::BasicArgFormatter { - public: - CustomArgFormatter(fmt::BasicFormatter &f, - fmt::FormatSpec &s, const char *fmt) - : fmt::BasicArgFormatter(f, s, fmt) {} - - void visit_int(int value) { - if (spec().type() == 'x') - visit_uint(value); // convert to unsigned and format - else - fmt::BasicArgFormatter::visit_int(value); - } - }; - - std::string custom_format(const char *format_str, fmt::ArgList args) { - fmt::MemoryWriter writer; - // Pass custom argument formatter as a template arg to BasicFormatter. - fmt::BasicFormatter formatter(args, writer); - formatter.format(format_str); - return writer.str(); - } - FMT_VARIADIC(std::string, custom_format, const char *) - - std::string s = custom_format("{:x}", -42); // s == "ffffffd6" - -.. doxygenclass:: fmt::ArgVisitor - :members: - -.. doxygenclass:: fmt::BasicArgFormatter - :members: - -.. doxygenclass:: fmt::ArgFormatter - :members: - -Printf formatting ------------------ +``printf`` formatting +===================== The header ``fmt/printf.h`` provides ``printf``-like formatting functionality. The following functions use `printf format string syntax @@ -171,22 +292,13 @@ the POSIX extension for positional arguments. Unlike their standard counterparts, the ``fmt`` functions are type-safe and throw an exception if an argument type doesn't match its format specification. -.. doxygenfunction:: printf(CStringRef, ArgList) +.. doxygenfunction:: printf(string_view, const Args&...) -.. doxygenfunction:: fprintf(std::FILE *, CStringRef, ArgList) +.. doxygenfunction:: fprintf(std::FILE *, string_view, const Args&...) -.. doxygenfunction:: fprintf(std::ostream&, CStringRef, ArgList) +.. doxygenfunction:: fprintf(std::ostream&, string_view, const Args&...) -.. doxygenfunction:: sprintf(CStringRef, ArgList) - -.. doxygenclass:: fmt::PrintfFormatter - :members: - -.. doxygenclass:: fmt::BasicPrintfArgFormatter - :members: - -.. doxygenclass:: fmt::PrintfArgFormatter - :members: +.. doxygenfunction:: sprintf(string_view, const Args&...) Write API ========= @@ -194,14 +306,14 @@ Write API The write API provides classes for writing formatted data into character streams. It is usually faster than the `format API`_ but, as IOStreams, may result in larger compiled code size. The main writer class is -`~fmt::BasicMemoryWriter` which stores its output in a memory buffer and +`~fmt::basic_memory_writer` which stores its output in a memory buffer and provides direct access to it. It is possible to create custom writers that store output elsewhere by subclassing `~fmt::BasicWriter`. .. doxygenclass:: fmt::BasicWriter :members: -.. doxygenclass:: fmt::BasicMemoryWriter +.. doxygenclass:: fmt::basic_memory_writer :members: .. doxygenclass:: fmt::BasicArrayWriter @@ -220,64 +332,3 @@ store output elsewhere by subclassing `~fmt::BasicWriter`. .. doxygenfunction:: pad(int, unsigned, Char) -Utilities -========= - -.. doxygenfunction:: fmt::arg(StringRef, const T&) - -.. doxygenfunction:: operator""_a(const char *, std::size_t) - -.. doxygendefine:: FMT_CAPTURE - -.. doxygendefine:: FMT_VARIADIC - -.. doxygenclass:: fmt::ArgList - :members: - -.. doxygenfunction:: fmt::to_string(const T&) - -.. doxygenclass:: fmt::BasicStringRef - :members: - -.. doxygenclass:: fmt::BasicCStringRef - :members: - -.. doxygenclass:: fmt::Buffer - :protected-members: - :members: - -System errors -============= - -.. doxygenclass:: fmt::SystemError - :members: - -.. doxygenfunction:: fmt::format_system_error - -.. doxygenclass:: fmt::WindowsError - :members: - -.. _formatstrings: - -Custom allocators -================= - -The fmt library supports custom dynamic memory allocators. -A custom allocator class can be specified as a template argument to -:class:`fmt::BasicMemoryWriter`:: - - typedef fmt::BasicMemoryWriter CustomMemoryWriter; - -It is also possible to write a formatting function that uses a custom -allocator:: - - typedef std::basic_string, CustomAllocator> - CustomString; - - CustomString format(CustomAllocator alloc, fmt::CStringRef format_str, - fmt::ArgList args) { - CustomMemoryWriter writer(alloc); - writer.write(format_str, args); - return CustomString(writer.data(), writer.size(), alloc); - } - FMT_VARIADIC(CustomString, format, CustomAllocator, fmt::CStringRef) diff --git a/doc/build.py b/doc/build.py index cb8d0c3c..2506e45a 100755 --- a/doc/build.py +++ b/doc/build.py @@ -62,8 +62,8 @@ def create_build_env(dirname='virtualenv'): def build_docs(version='dev', **kwargs): doc_dir = kwargs.get('doc_dir', os.path.dirname(os.path.realpath(__file__))) work_dir = kwargs.get('work_dir', '.') - include_dir = kwargs.get('include_dir', - os.path.join(os.path.dirname(doc_dir), 'fmt')) + include_dir = kwargs.get( + 'include_dir', os.path.join(os.path.dirname(doc_dir), 'include', 'fmt')) # Build docs. cmd = ['doxygen', '-'] p = Popen(cmd, stdin=PIPE) @@ -74,8 +74,8 @@ def build_docs(version='dev', **kwargs): GENERATE_MAN = NO GENERATE_RTF = NO CASE_SENSE_NAMES = NO - INPUT = {0}/container.h {0}/format.h {0}/ostream.h \ - {0}/printf.h {0}/string.h + INPUT = {0}/core.h {0}/format.h {0}/ostream.h \ + {0}/printf.h {0}/time.h QUIET = YES JAVADOC_AUTOBRIEF = YES AUTOLINK_SUPPORT = NO diff --git a/doc/conf.py b/doc/conf.py index 1e9010e7..0756192b 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -47,7 +47,7 @@ source_suffix = '.rst' # General information about the project. project = u'fmt' -copyright = u'2012-2015, Victor Zverovich' +copyright = u'2012-present, Victor Zverovich' # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the diff --git a/doc/index.rst b/doc/index.rst index a00828b0..ea70c46e 100644 --- a/doc/index.rst +++ b/doc/index.rst @@ -25,7 +25,7 @@ The replacement-based Format API provides a safe alternative to ``printf``, ``sprintf`` and friends with comparable or `better performance `_. The `format string syntax `_ is similar to the one used by -`str.format `_ +`str.format `_ in Python: .. code:: c++ diff --git a/include/fmt/core.h b/include/fmt/core.h index 0944973e..8acc2f60 100644 --- a/include/fmt/core.h +++ b/include/fmt/core.h @@ -24,7 +24,7 @@ # define FMT_HAS_FEATURE(x) 0 #endif -#if defined(__has_include) +#ifdef __has_include # define FMT_HAS_INCLUDE(x) __has_include(x) #else # define FMT_HAS_INCLUDE(x) 0 @@ -36,7 +36,7 @@ # define FMT_GCC_VERSION 0 #endif -#if __cplusplus >= 201103L || defined __GXX_EXPERIMENTAL_CXX0X__ +#if __cplusplus >= 201103L || defined(__GXX_EXPERIMENTAL_CXX0X__) # define FMT_HAS_GXX_CXX11 FMT_GCC_VERSION #else # define FMT_HAS_GXX_CXX11 0 @@ -72,17 +72,26 @@ # endif #endif +#if FMT_HAS_FEATURE(cxx_explicit_conversions) +# define FMT_EXPLICIT explicit +#else +# define FMT_EXPLICIT +#endif + #ifndef FMT_NULL # if FMT_HAS_FEATURE(cxx_nullptr) || \ (FMT_GCC_VERSION >= 408 && FMT_HAS_GXX_CXX11) || \ FMT_MSC_VER >= 1600 # define FMT_NULL nullptr +# define FMT_USE_NULLPTR 1 # else # define FMT_NULL NULL # endif #endif -#define FMT_USE_STRONG_ENUMS FMT_HAS_FEATURE(cxx_strong_enums) +#ifndef FMT_USE_NULLPTR +# define FMT_USE_NULLPTR 0 +#endif // Check if exceptions are disabled. #if defined(__GNUC__) && !defined(__EXCEPTIONS) @@ -148,19 +157,36 @@ #if (FMT_HAS_INCLUDE() && __cplusplus > 201402L) || \ (defined(_MSVC_LANG) && _MSVC_LANG > 201402L && _MSC_VER >= 1910) # include -namespace fmt { using std::basic_string_view; } -// std::experimental::basic_string_view::remove_prefix isn't constexpr in gcc 6. +# define FMT_USE_STD_STRING_VIEW +// std::experimental::basic_string_view::remove_prefix isn't constexpr until +// gcc 7.3. #elif (FMT_HAS_INCLUDE() && \ - (FMT_GCC_VERSION == 0 || FMT_GCC_VERSION >= 700) && \ + (FMT_GCC_VERSION == 0 || FMT_GCC_VERSION >= 730) && \ __cplusplus >= 201402L) # include -namespace fmt { using std::experimental::basic_string_view; } -#else +# define FMT_USE_EXPERIMENTAL_STRING_VIEW +#endif + +// std::result_of is defined in in gcc 4.4. +#if FMT_GCC_VERSION && FMT_GCC_VERSION <= 404 +# include +#endif + namespace fmt { + +// An implementation of declval for pre-C++11 compilers such as gcc 4. +namespace internal { +template +typename std::add_rvalue_reference::type declval() FMT_NOEXCEPT; +} + /** \rst An implementation of ``std::basic_string_view`` for pre-C++17. It provides a - subset of the API. + subset of the API. ``fmt::basic_string_view`` is used for format strings even + if ``std::string_view`` is available to prevent issues when a library is + compiled with a different ``-std`` option than the client code (which is not + recommended). \endrst */ template @@ -170,10 +196,22 @@ class basic_string_view { size_t size_; public: - using char_type = Char; - using iterator = const Char *; + typedef Char char_type; + typedef const Char *iterator; - FMT_CONSTEXPR basic_string_view() FMT_NOEXCEPT : data_(0), size_(0) {} + // Standard basic_string_view type. +#if defined(FMT_USE_STD_STRING_VIEW) + typedef std::basic_string_view type; +#elif defined(FMT_USE_EXPERIMENTAL_STRING_VIEW) + typedef std::experimental::basic_string_view type; +#else + struct type { + const char *data() const { return FMT_NULL; } + size_t size() const { return 0; }; + }; +#endif + + FMT_CONSTEXPR basic_string_view() FMT_NOEXCEPT : data_(FMT_NULL), size_(0) {} /** Constructs a string reference object from a C string and a size. */ FMT_CONSTEXPR basic_string_view(const Char *s, size_t size) FMT_NOEXCEPT @@ -198,6 +236,9 @@ class basic_string_view { const std::basic_string &s) FMT_NOEXCEPT : data_(s.c_str()), size_(s.size()) {} + FMT_CONSTEXPR basic_string_view(type s) FMT_NOEXCEPT + : data_(s.data()), size_(s.size()) {} + /** Returns a pointer to the string data. */ const Char *data() const { return data_; } @@ -240,12 +281,9 @@ class basic_string_view { return lhs.compare(rhs) >= 0; } }; -} // namespace fmt -#endif -namespace fmt { -using string_view = basic_string_view; -using wstring_view = basic_string_view; +typedef basic_string_view string_view; +typedef basic_string_view wstring_view; template class basic_arg; @@ -287,7 +325,8 @@ class basic_buffer { virtual void grow(std::size_t capacity) = 0; public: - using value_type = T; + typedef T value_type; + typedef const T &const_reference; virtual ~basic_buffer() {} @@ -337,8 +376,8 @@ class basic_buffer { const T &operator[](std::size_t index) const { return ptr_[index]; } }; -using buffer = basic_buffer; -using wbuffer = basic_buffer; +typedef basic_buffer buffer; +typedef basic_buffer wbuffer; // A container-backed buffer. template @@ -347,7 +386,7 @@ class container_buffer : public basic_buffer { Container &container_; protected: - virtual void grow(std::size_t capacity) { + void grow(std::size_t capacity) FMT_OVERRIDE { container_.resize(capacity); this->set(&container_[0], capacity); } @@ -358,11 +397,6 @@ class container_buffer : public basic_buffer { container_(c) {} }; -// A helper function to suppress bogus "conditional expression is constant" -// warnings. -template -inline T const_check(T value) { return value; } - struct error_handler { FMT_CONSTEXPR error_handler() {} FMT_CONSTEXPR error_handler(const error_handler &) {} @@ -395,40 +429,32 @@ template struct is_named_arg> : std::true_type {}; enum type { - NONE, NAMED_ARG, + none_type, name_arg_type, // Integer types should go first, - INT, UINT, LONG_LONG, ULONG_LONG, BOOL, CHAR, LAST_INTEGER_TYPE = CHAR, + int_type, uint_type, long_long_type, ulong_long_type, bool_type, char_type, + last_integer_type = char_type, // followed by floating-point types. - DOUBLE, LONG_DOUBLE, LAST_NUMERIC_TYPE = LONG_DOUBLE, - CSTRING, STRING, POINTER, CUSTOM + double_type, long_double_type, last_numeric_type = long_double_type, + cstring_type, string_type, pointer_type, custom_type }; FMT_CONSTEXPR bool is_integral(type t) { - FMT_ASSERT(t != internal::NAMED_ARG, "invalid argument type"); - return t > internal::NONE && t <= internal::LAST_INTEGER_TYPE; + FMT_ASSERT(t != internal::name_arg_type, "invalid argument type"); + return t > internal::none_type && t <= internal::last_integer_type; } FMT_CONSTEXPR bool is_arithmetic(type t) { - FMT_ASSERT(t != internal::NAMED_ARG, "invalid argument type"); - return t > internal::NONE && t <= internal::LAST_NUMERIC_TYPE; + FMT_ASSERT(t != internal::name_arg_type, "invalid argument type"); + return t > internal::none_type && t <= internal::last_numeric_type; } -template +template struct convert_to_int { enum { - value = !std::is_arithmetic::value && std::is_convertible::value + value = !std::is_arithmetic::value && std::is_convertible::value }; }; -#define FMT_DISABLE_CONVERSION_TO_INT(Type) \ - template <> \ - struct convert_to_int { enum { value = 0 }; } - -// Silence warnings about convering float to int. -FMT_DISABLE_CONVERSION_TO_INT(float); -FMT_DISABLE_CONVERSION_TO_INT(double); -FMT_DISABLE_CONVERSION_TO_INT(long double); - template struct string_value { const Char *value; @@ -445,7 +471,7 @@ struct custom_value { template class value { public: - using char_type = typename Context::char_type; + typedef typename Context::char_type char_type; union { int int_value; @@ -501,7 +527,7 @@ class value { // Get the formatter type through the context to allow different contexts // have different extension points, e.g. `formatter` for `format` and // `printf_formatter` for `printf`. - typename Context::template formatter_type f; + typename Context::template formatter_type::type f; auto &&parse_ctx = ctx.parse_context(); parse_ctx.advance_to(f.parse(parse_ctx)); ctx.advance_to(f.format(*static_cast(arg), ctx)); @@ -520,85 +546,97 @@ template FMT_CONSTEXPR basic_arg make_arg(const T &value); #define FMT_MAKE_VALUE(TAG, ArgType, ValueType) \ - template \ + template \ FMT_CONSTEXPR typed_value make_value(ArgType val) { \ return static_cast(val); \ } -FMT_MAKE_VALUE(BOOL, bool, int) -FMT_MAKE_VALUE(INT, short, int) -FMT_MAKE_VALUE(UINT, unsigned short, unsigned) -FMT_MAKE_VALUE(INT, int, int) -FMT_MAKE_VALUE(UINT, unsigned, unsigned) +FMT_MAKE_VALUE(bool_type, bool, int) +FMT_MAKE_VALUE(int_type, short, int) +FMT_MAKE_VALUE(uint_type, unsigned short, unsigned) +FMT_MAKE_VALUE(int_type, int, int) +FMT_MAKE_VALUE(uint_type, unsigned, unsigned) // 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. -using long_type = - std::conditional::type; -FMT_MAKE_VALUE((sizeof(long) == sizeof(int) ? INT : LONG_LONG), long, long_type) -using ulong_type = - std::conditional::type; -FMT_MAKE_VALUE((sizeof(unsigned long) == sizeof(unsigned) ? UINT : ULONG_LONG), +typedef std::conditional::type + long_type; +FMT_MAKE_VALUE( + (sizeof(long) == sizeof(int) ? int_type : long_long_type), long, long_type) +typedef std::conditional::type ulong_type; +FMT_MAKE_VALUE( + (sizeof(unsigned long) == sizeof(unsigned) ? uint_type : ulong_long_type), unsigned long, ulong_type) -FMT_MAKE_VALUE(LONG_LONG, long long, long long) -FMT_MAKE_VALUE(ULONG_LONG, unsigned long long, unsigned long long) -FMT_MAKE_VALUE(INT, signed char, int) -FMT_MAKE_VALUE(UINT, unsigned char, unsigned) -FMT_MAKE_VALUE(CHAR, char, int) +FMT_MAKE_VALUE(long_long_type, long long, long long) +FMT_MAKE_VALUE(ulong_long_type, unsigned long long, unsigned long long) +FMT_MAKE_VALUE(int_type, signed char, int) +FMT_MAKE_VALUE(uint_type, unsigned char, unsigned) +FMT_MAKE_VALUE(char_type, char, int) #if !defined(_MSC_VER) || defined(_NATIVE_WCHAR_T_DEFINED) template -inline typed_value make_value(wchar_t val) { +inline typed_value make_value(wchar_t val) { require_wchar(); return static_cast(val); } #endif -FMT_MAKE_VALUE(DOUBLE, float, double) -FMT_MAKE_VALUE(DOUBLE, double, double) -FMT_MAKE_VALUE(LONG_DOUBLE, long double, long double) +FMT_MAKE_VALUE(double_type, float, double) +FMT_MAKE_VALUE(double_type, double, double) +FMT_MAKE_VALUE(long_double_type, long double, long double) // Formatting of wide strings into a narrow buffer and multibyte strings // into a wide buffer is disallowed (https://github.com/fmtlib/fmt/pull/606). -FMT_MAKE_VALUE(CSTRING, char_type*, const char_type*) -FMT_MAKE_VALUE(CSTRING, const char_type*, const char_type*) +FMT_MAKE_VALUE(cstring_type, typename C::char_type*, + const typename C::char_type*) +FMT_MAKE_VALUE(cstring_type, const typename C::char_type*, + const typename C::char_type*) -FMT_MAKE_VALUE(CSTRING, signed char*, const signed char*) -FMT_MAKE_VALUE(CSTRING, const signed char*, const signed char*) -FMT_MAKE_VALUE(CSTRING, unsigned char*, const unsigned char*) -FMT_MAKE_VALUE(CSTRING, const unsigned char*, const unsigned char*) -FMT_MAKE_VALUE(STRING, basic_string_view, - basic_string_view) -FMT_MAKE_VALUE(STRING, const std::basic_string&, - basic_string_view) -FMT_MAKE_VALUE(POINTER, void*, const void*) -FMT_MAKE_VALUE(POINTER, const void*, const void*) -FMT_MAKE_VALUE(POINTER, std::nullptr_t, const void*) +FMT_MAKE_VALUE(cstring_type, signed char*, const signed char*) +FMT_MAKE_VALUE(cstring_type, const signed char*, const signed char*) +FMT_MAKE_VALUE(cstring_type, unsigned char*, const unsigned char*) +FMT_MAKE_VALUE(cstring_type, const unsigned char*, const unsigned char*) +FMT_MAKE_VALUE(string_type, basic_string_view, + basic_string_view) +FMT_MAKE_VALUE(string_type, + typename basic_string_view::type, + basic_string_view) +FMT_MAKE_VALUE(string_type, const std::basic_string&, + basic_string_view) +FMT_MAKE_VALUE(pointer_type, void*, const void*) +FMT_MAKE_VALUE(pointer_type, const void*, const void*) + +#if FMT_USE_NULLPTR +FMT_MAKE_VALUE(pointer_type, std::nullptr_t, const void*) +#endif // Formatting of arbitrary pointers is disallowed. If you want to output a // pointer cast it to "void *" or "const void *". In particular, this forbids // formatting of "[const] volatile char *" which is printed as bool by // iostreams. template -void make_value(const T *p) { +void make_value(const T *) { static_assert(!sizeof(T), "formatting of non-void pointers is disallowed"); } template inline typename std::enable_if< - convert_to_int::value && std::is_enum::value, - typed_value>::type + std::is_enum::value && convert_to_int::value, + typed_value>::type make_value(const T &val) { return static_cast(val); } -template +template inline typename std::enable_if< - !convert_to_int::value, typed_value>::type + !convert_to_int::value && + !std::is_convertible>::value && + !std::is_convertible>::value, + typed_value>::type make_value(const T &val) { return val; } template -typed_value +typed_value make_value(const named_arg &val) { basic_arg arg = make_arg(val.value); std::memcpy(val.data, &arg, sizeof(arg)); @@ -606,10 +644,20 @@ typed_value } // Maximum number of arguments with packed types. -enum { MAX_PACKED_ARGS = 15 }; +enum { max_packed_args = 15 }; template class arg_map; + +template +struct result_of; + +template +struct result_of { + // A workaround for gcc 4.4 that doesn't allow F to be a reference. + typedef typename std::result_of< + typename std::remove_reference::type(Args...)>::type type; +}; } // A formatting argument. It is a trivially copyable/constructible type to @@ -624,13 +672,13 @@ class basic_arg { friend FMT_CONSTEXPR basic_arg internal::make_arg(const T &value); template - friend FMT_CONSTEXPR typename std::result_of::type + friend FMT_CONSTEXPR typename internal::result_of::type visit(Visitor &&vis, basic_arg arg); friend class basic_format_args; friend class internal::arg_map; - using char_type = typename Context::char_type; + typedef typename Context::char_type char_type; public: class handle { @@ -643,17 +691,17 @@ class basic_arg { internal::custom_value custom_; }; - FMT_CONSTEXPR basic_arg() : type_(internal::NONE) {} + FMT_CONSTEXPR basic_arg() : type_(internal::none_type) {} - explicit operator bool() const FMT_NOEXCEPT { - return type_ != internal::NONE; + FMT_EXPLICIT operator bool() const FMT_NOEXCEPT { + return type_ != internal::none_type; } internal::type type() const { return type_; } bool is_integral() const { return internal::is_integral(type_); } bool is_arithmetic() const { return internal::is_arithmetic(type_); } - bool is_pointer() const { return type_ == internal::POINTER; } + bool is_pointer() const { return type_ == internal::pointer_type; } }; // Parsing context consisting of a format string range being parsed and an @@ -665,8 +713,8 @@ class basic_parse_context : private ErrorHandler { int next_arg_id_; public: - using char_type = Char; - using iterator = typename basic_string_view::iterator; + typedef Char char_type; + typedef typename basic_string_view::iterator iterator; explicit FMT_CONSTEXPR basic_parse_context( basic_string_view format_str, ErrorHandler eh = ErrorHandler()) @@ -706,8 +754,8 @@ class basic_parse_context : private ErrorHandler { FMT_CONSTEXPR ErrorHandler error_handler() const { return *this; } }; -using parse_context = basic_parse_context; -using wparse_context = basic_parse_context; +typedef basic_parse_context parse_context; +typedef basic_parse_context wparse_context; namespace internal { // A map from argument names to their values for named arguments. @@ -716,15 +764,15 @@ class arg_map { private: FMT_DISALLOW_COPY_AND_ASSIGN(arg_map); - using char_type = typename Context::char_type; + typedef typename Context::char_type char_type; struct entry { basic_string_view name; basic_arg arg; }; - entry *map_ = nullptr; - unsigned size_ = 0; + entry *map_; + unsigned size_; void push_back(value val) { const internal::named_arg_base &named = val.as_named_arg(); @@ -733,7 +781,7 @@ class arg_map { } public: - arg_map() {} + arg_map() : map_(FMT_NULL), size_(0) {} void init(const basic_format_args &args); ~arg_map() { delete [] map_; } @@ -750,7 +798,7 @@ class arg_map { template class context_base { public: - using iterator = OutputIt; + typedef OutputIt iterator; private: basic_parse_context parse_context_; @@ -758,8 +806,8 @@ class context_base { basic_format_args args_; protected: - using char_type = Char; - using format_arg = basic_arg; + typedef Char char_type; + typedef basic_arg format_arg; context_base(OutputIt out, basic_string_view format_str, basic_format_args args) @@ -794,7 +842,7 @@ class context_base { void on_error(const char *message) { parse_context_.on_error(message); } // Returns an iterator to the beginning of the output range. - auto begin() { return out_; } + iterator begin() { return out_; } // Advances the begin iterator to ``it``. void advance_to(iterator it) { out_ = it; } @@ -803,49 +851,34 @@ class context_base { // Extracts a reference to the container from back_insert_iterator. template inline Container &get_container(std::back_insert_iterator it) { - using iterator = std::back_insert_iterator; - struct accessor: iterator { - accessor(iterator it) : iterator(it) {} - using iterator::container; + typedef std::back_insert_iterator bi_iterator; + struct accessor: bi_iterator { + accessor(bi_iterator it) : bi_iterator(it) {} + using bi_iterator::container; }; return *accessor(it).container; } } // namespace internal -template -class output_range { - private: - OutputIt it_; - - // Unused yet. - using sentinel = void; - sentinel end() const; - - public: - using value_type = T; - - explicit output_range(OutputIt it): it_(it) {} - OutputIt begin() const { return it_; } -}; - // Formatting context. template class basic_context : public internal::context_base, Char> { public: /** The character type for the output. */ - using char_type = Char; + typedef Char char_type; + // using formatter_type = formatter; template - using formatter_type = formatter; + struct formatter_type { typedef formatter type; }; private: internal::arg_map map_; FMT_DISALLOW_COPY_AND_ASSIGN(basic_context); - using base = internal::context_base; - using format_arg = typename base::format_arg; + typedef internal::context_base base; + typedef typename base::format_arg format_arg; using base::get_arg; public: @@ -872,19 +905,18 @@ class basic_context : }; template -using buffer_context_t = basic_context< - std::back_insert_iterator>, Char>; -using context = buffer_context_t; -using wcontext = buffer_context_t; +struct buffer_context { + typedef basic_context< + std::back_insert_iterator>, Char> type; +}; +typedef buffer_context::type context; +typedef buffer_context::type wcontext; namespace internal { template -class get_type { - private: - static const T& val(); - - public: - using value_type = decltype(make_value(val())); +struct get_type { + typedef decltype(make_value( + declval::type&>())) value_type; static const type value = value_type::type_tag; }; @@ -923,10 +955,10 @@ class arg_store { static const size_t NUM_ARGS = sizeof...(Args); // Packed is a macro on MinGW so use IS_PACKED instead. - static const bool IS_PACKED = NUM_ARGS < internal::MAX_PACKED_ARGS; + static const bool IS_PACKED = NUM_ARGS < internal::max_packed_args; - using value_type = typename std::conditional< - IS_PACKED, internal::value, basic_arg>::type; + typedef typename std::conditional< + IS_PACKED, internal::value, basic_arg>::type value_type; // If the arguments are not packed, add one more element to mark the end. value_type data_[NUM_ARGS + (IS_PACKED && NUM_ARGS != 0 ? 0 : 1)]; @@ -934,8 +966,15 @@ class arg_store { public: static const uint64_t TYPES; +#if FMT_GCC_VERSION && FMT_GCC_VERSION <= 405 && !defined(__clang__) + // Workaround an array initialization bug in gcc 4.5 and earlier. + arg_store(const Args &... args) { + data_ = {internal::make_arg(args)...}; + } +#else arg_store(const Args &... args) : data_{internal::make_arg(args)...} {} +#endif basic_format_args operator*() const { return *this; } @@ -961,15 +1000,15 @@ inline arg_store make_args(const Args & ... args) { template class basic_format_args { public: - using size_type = unsigned; - using format_arg = basic_arg ; + typedef unsigned size_type; + typedef basic_arg format_arg; private: // To reduce compiled code size per formatting function call, types of first - // MAX_PACKED_ARGS arguments are passed in the types_ field. + // max_packed_args arguments are passed in the types_ field. uint64_t types_; union { - // If the number of arguments is less than MAX_PACKED_ARGS, the argument + // If the number of arguments is less than max_packed_args, the argument // values are stored in values_, otherwise they are stored in args_. // This is done to reduce compiled code size as storing larger objects // may require more code (at least on x86-64) even if the same amount of @@ -997,10 +1036,10 @@ class basic_format_args { return index < num_args ? args_[index] : format_arg(); } format_arg arg; - if (index > internal::MAX_PACKED_ARGS) + if (index > internal::max_packed_args) return arg; arg.type_ = type(index); - if (arg.type_ == internal::NONE) + if (arg.type_ == internal::none_type) return arg; internal::value &val = arg.value_; val = values_[index]; @@ -1019,19 +1058,25 @@ class basic_format_args { /** Returns the argument at specified index. */ format_arg operator[](size_type index) const { format_arg arg = get(index); - return arg.type_ == internal::NAMED_ARG ? + return arg.type_ == internal::name_arg_type ? arg.value_.as_named_arg().template deserialize() : arg; } unsigned max_size() const { int64_t signed_types = static_cast(types_); return signed_types < 0 ? - -signed_types : static_cast(internal::MAX_PACKED_ARGS); + -signed_types : static_cast(internal::max_packed_args); } }; -using format_args = basic_format_args; -using wformat_args = basic_format_args; +/** An alias to ``basic_format_args``. */ +// It is a separate type rather than a typedef to make symbols readable. +struct format_args: basic_format_args { + template + format_args(Args && ... arg) + : basic_format_args(std::forward(arg)...) {}; +}; +typedef basic_format_args wformat_args; namespace internal { template @@ -1062,11 +1107,11 @@ struct named_arg : named_arg_base { /** \rst - Returns a named argument for formatting functions. + Returns a named argument to be used in a formatting function. **Example**:: - print("Elapsed time: {s:.2f} seconds", arg("s", 1.23)); + fmt::print("Elapsed time: {s:.2f} seconds", fmt::arg("s", 1.23)); \endrst */ template @@ -1081,29 +1126,29 @@ inline internal::named_arg arg(wstring_view name, const T &arg) { // This function template is deleted intentionally to disable nested named // arguments as in ``format("{}", arg("a", arg("b", 42)))``. -template -void arg(S, internal::named_arg) FMT_DELETED; +template +void arg(S, internal::named_arg) FMT_DELETED; -enum Color { BLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE }; +enum color { black, red, green, yellow, blue, magenta, cyan, white }; -FMT_API void vprint_colored(Color c, string_view format, format_args args); +FMT_API void vprint_colored(color c, string_view format, format_args args); /** Formats a string and prints it to stdout using ANSI escape sequences to specify color (experimental). Example: - print_colored(fmt::RED, "Elapsed time: {0:.2f} seconds", 1.23); + fmt::print_colored(fmt::RED, "Elapsed time: {0:.2f} seconds", 1.23); */ template -inline void print_colored(Color c, string_view format_str, +inline void print_colored(color c, string_view format_str, const Args & ... args) { vprint_colored(c, format_str, make_args(args...)); } -void vformat_to(internal::buffer &buf, string_view format_str, - format_args args); -void vformat_to(internal::wbuffer &buf, wstring_view format_str, - wformat_args args); +context::iterator vformat_to(internal::buffer &buf, string_view format_str, + format_args args); +wcontext::iterator vformat_to(internal::wbuffer &buf, wstring_view format_str, + wformat_args args); template struct is_contiguous : std::false_type {}; @@ -1116,11 +1161,14 @@ struct is_contiguous> : std::true_type {}; /** Formats a string and writes the output to ``out``. */ template -typename std::enable_if::value>::type +typename std::enable_if< + is_contiguous::value, std::back_insert_iterator>::type vformat_to(std::back_insert_iterator out, string_view format_str, format_args args) { - internal::container_buffer buf(internal::get_container(out)); + auto& container = internal::get_container(out); + internal::container_buffer buf(container); vformat_to(buf, format_str, args); + return std::back_inserter(container); } std::string vformat(string_view format_str, format_args args); @@ -1132,16 +1180,22 @@ std::wstring vformat(wstring_view format_str, wformat_args args); **Example**:: - std::string message = format("The answer is {}", 42); + #include + std::string message = fmt::format("The answer is {}", 42); \endrst */ template inline std::string format(string_view format_str, const Args & ... args) { - return vformat(format_str, make_args(args...)); + // This should be just + // return vformat(format_str, make_args(args...)); + // but gcc has trouble optimizing the latter, so break it down. + arg_store as(args...); + return vformat(format_str, as); } template inline std::wstring format(wstring_view format_str, const Args & ... args) { - return vformat(format_str, make_args(args...)); + arg_store as(args...); + return vformat(format_str, as); } FMT_API void vprint(std::FILE *f, string_view format_str, format_args args); @@ -1152,12 +1206,13 @@ FMT_API void vprint(std::FILE *f, string_view format_str, format_args args); **Example**:: - print(stderr, "Don't {}!", "panic"); + fmt::print(stderr, "Don't {}!", "panic"); \endrst */ template inline void print(std::FILE *f, string_view format_str, const Args & ... args) { - vprint(f, format_str, make_args(args...)); + arg_store as(args...); + vprint(f, format_str, as); } FMT_API void vprint(string_view format_str, format_args args); @@ -1168,12 +1223,13 @@ FMT_API void vprint(string_view format_str, format_args args); **Example**:: - print("Elapsed time: {0:.2f} seconds", 1.23); + fmt::print("Elapsed time: {0:.2f} seconds", 1.23); \endrst */ template inline void print(string_view format_str, const Args & ... args) { - vprint(format_str, make_args(args...)); + arg_store as(args...); + vprint(format_str, as); } } // namespace fmt diff --git a/include/fmt/format.cc b/include/fmt/format.cc index edecd87d..429657fb 100644 --- a/include/fmt/format.cc +++ b/include/fmt/format.cc @@ -166,7 +166,7 @@ int safe_strerror( void format_error_code(internal::buffer &out, int error_code, string_view message) FMT_NOEXCEPT { // Report error code making sure that the output fits into - // INLINE_BUFFER_SIZE to avoid dynamic memory allocation and potential + // inline_buffer_size to avoid dynamic memory allocation and potential // bad_alloc. out.resize(0); static const char SEP[] = ": "; @@ -181,13 +181,13 @@ void format_error_code(internal::buffer &out, int error_code, } error_code_size += internal::count_digits(abs_value); writer w(out); - if (message.size() <= internal::INLINE_BUFFER_SIZE - error_code_size) { + if (message.size() <= inline_buffer_size - error_code_size) { w.write(message); w.write(SEP); } w.write(ERROR_STR); w.write(error_code); - assert(out.size() <= internal::INLINE_BUFFER_SIZE); + assert(out.size() <= inline_buffer_size); } void report_error(FormatFunc func, int error_code, @@ -332,7 +332,7 @@ FMT_FUNC void internal::format_windows_error( internal::buffer &out, int error_code, string_view message) FMT_NOEXCEPT { FMT_TRY { wmemory_buffer buf; - buf.resize(INLINE_BUFFER_SIZE); + buf.resize(inline_buffer_size); for (;;) { wchar_t *system_message = &buf[0]; int result = FormatMessageW( @@ -364,7 +364,7 @@ FMT_FUNC void format_system_error( internal::buffer &out, int error_code, string_view message) FMT_NOEXCEPT { FMT_TRY { memory_buffer buf; - buf.resize(internal::INLINE_BUFFER_SIZE); + buf.resize(inline_buffer_size); for (;;) { char *system_message = &buf[0]; int result = safe_strerror(error_code, system_message, buf.size()); @@ -414,7 +414,7 @@ FMT_FUNC void vprint(string_view format_str, format_args args) { vprint(stdout, format_str, args); } -FMT_FUNC void vprint_colored(Color c, string_view format, format_args args) { +FMT_FUNC void vprint_colored(color c, string_view format, format_args args) { char escape[] = "\x1b[30m"; escape[3] = static_cast('0' + c); std::fputs(escape, stdout); @@ -434,7 +434,8 @@ template char internal::thousands_sep(locale_provider *lp); template void basic_fixed_buffer::grow(std::size_t); -template void internal::arg_map::init(const format_args &args); +template void internal::arg_map::init( + const basic_format_args &args); template FMT_API int internal::char_traits::format_float( char *buffer, std::size_t size, const char *format, diff --git a/include/fmt/format.h b/include/fmt/format.h index 2e94fab0..dc2336cd 100644 --- a/include/fmt/format.h +++ b/include/fmt/format.h @@ -140,6 +140,10 @@ # endif #endif +#if FMT_HAS_GXX_CXX11 || FMT_MSC_VER >= 1600 +# define FMT_USE_TRAILING_RETURN 1 +#endif + // __builtin_clz is broken in clang with Microsoft CodeGen: // https://github.com/fmtlib/fmt/issues/519 #ifndef _MSC_VER @@ -152,6 +156,13 @@ # endif #endif +// A workaround for gcc 4.4 that doesn't support union members with ctors. +#if FMT_GCC_VERSION && FMT_GCC_VERSION <= 404 +# define FMT_UNION struct +#else +# define FMT_UNION union +#endif + // Some compilers masquerade as both MSVC and GCC-likes or otherwise support // __builtin_clz and __builtin_clzll, so only define FMT_BUILTIN_CLZ using the // MSVC intrinsics if the clz and clzll builtins are not available. @@ -208,6 +219,26 @@ inline uint32_t clzll(uint64_t x) { namespace fmt { namespace internal { + +// An implementation of begin and end for pre-C++11 compilers such as gcc 4. +template +FMT_CONSTEXPR auto begin(const C &c) -> decltype(c.begin()) { + return c.begin(); +} +template +FMT_CONSTEXPR T *begin(T (&array)[N]) FMT_NOEXCEPT { return array; } +template +FMT_CONSTEXPR auto end(const C &c) -> decltype(c.end()) { return c.end(); } +template +FMT_CONSTEXPR T *end(T (&array)[N]) FMT_NOEXCEPT { return array + N; } + +// For std::result_of in gcc 4.4. +template +struct function { + template + struct result { typedef Result type; }; +}; + struct dummy_int { int data[2]; operator int() const { return 0; } @@ -228,9 +259,14 @@ typename Allocator::value_type *allocate(Allocator& alloc, std::size_t n) { #if __cplusplus >= 201103L || FMT_MSC_VER >= 1700 return std::allocator_traits::allocate(alloc, n); #else - return this->allocate(n); + return alloc.allocate(n); #endif } + +// A helper function to suppress bogus "conditional expression is constant" +// warnings. +template +inline T const_check(T value) { return value; } } // namespace internal } // namespace fmt @@ -304,10 +340,6 @@ FMT_CONSTEXPR typename std::make_unsigned::type to_unsigned(Int value) { return static_cast::type>(value); } -// The number of characters to store in the basic_memory_buffer object itself -// to avoid dynamic memory allocation. -enum { INLINE_BUFFER_SIZE = 500 }; - #if FMT_SECURE_SCL // Use checked iterator to avoid warnings on MSVC. template @@ -340,10 +372,14 @@ class locale_provider { virtual fmt::locale locale(); }; +// The number of characters to store in the basic_memory_buffer object itself +// to avoid dynamic memory allocation. +enum { inline_buffer_size = 500 }; + /** \rst A dynamically growing memory buffer for trivially copyable/constructible types - with the first SIZE elements stored in the object itself. + with the first ``SIZE`` elements stored in the object itself. You can use one of the following typedefs for common character types: @@ -357,7 +393,7 @@ class locale_provider { **Example**:: - memory_buffer out; + fmt::memory_buffer out; format_to(out, "The answer is {}.", 42); This will write the following output to the ``out`` object: @@ -369,7 +405,7 @@ class locale_provider { The output can be converted to an ``std::string`` with ``to_string(out)``. \endrst */ -template > class basic_memory_buffer: private Allocator, public internal::basic_buffer { private: @@ -382,7 +418,7 @@ class basic_memory_buffer: private Allocator, public internal::basic_buffer { } protected: - void grow(std::size_t size); + void grow(std::size_t size) FMT_OVERRIDE; public: explicit basic_memory_buffer(const Allocator &alloc = Allocator()) @@ -494,7 +530,7 @@ class basic_fixed_buffer : public internal::basic_buffer { } protected: - FMT_API void grow(std::size_t size); + FMT_API void grow(std::size_t size) FMT_OVERRIDE; }; namespace internal { @@ -567,7 +603,7 @@ template class null_terminating_iterator; template -FMT_CONSTEXPR const Char *pointer_from(null_terminating_iterator it); +FMT_CONSTEXPR_DECL const Char *pointer_from(null_terminating_iterator it); // An iterator that produces a null terminator on *end. This simplifies parsing // and allows comparing the performance of processing a null-terminated string @@ -575,11 +611,11 @@ FMT_CONSTEXPR const Char *pointer_from(null_terminating_iterator it); template class null_terminating_iterator { public: - using difference_type = std::ptrdiff_t; - using value_type = Char; - using pointer = const Char*; - using reference = const Char&; - using iterator_category = std::random_access_iterator_tag; + typedef std::ptrdiff_t difference_type; + typedef Char value_type; + typedef const Char* pointer; + typedef const Char& reference; + typedef std::random_access_iterator_tag iterator_category; null_terminating_iterator() : ptr_(0), end_(0) {} @@ -642,8 +678,11 @@ class null_terminating_iterator { return ptr_ >= other.ptr_; } - friend FMT_CONSTEXPR_DECL const Char *pointer_from( - null_terminating_iterator it); + // This should be a friend specialization pointer_from but the latter + // doesn't compile by gcc 5.1 due to a compiler bug. + template + friend FMT_CONSTEXPR_DECL const CharT *pointer_from( + null_terminating_iterator it); private: const Char *ptr_; @@ -667,11 +706,11 @@ class counting_iterator { mutable T blackhole_; public: - using iterator_category = std::output_iterator_tag; - using value_type = T; - using difference_type = std::ptrdiff_t; - using pointer = T*; - using reference = T&; + typedef std::output_iterator_tag iterator_category; + typedef T value_type; + typedef std::ptrdiff_t difference_type; + typedef T* pointer; + typedef T& reference; explicit counting_iterator(std::size_t &count): count_(count) {} counting_iterator(const counting_iterator &other): count_(other.count_) {} @@ -900,7 +939,7 @@ inline Char *format_decimal(Char *buffer, UInt value, unsigned num_digits, template inline Iterator format_decimal( - Iterator out, UInt value, unsigned num_digits, ThousandsSep thousands_sep) { + Iterator out, UInt value, unsigned num_digits, ThousandsSep) { // Buffer should be large enough to hold all digits (digits10 + 1) and null. char buffer[std::numeric_limits::digits10 + 2]; format_decimal(buffer, value, num_digits, no_thousands_sep()); @@ -996,42 +1035,42 @@ struct monostate {}; \endrst */ template -FMT_CONSTEXPR typename std::result_of::type +FMT_CONSTEXPR typename internal::result_of::type visit(Visitor &&vis, basic_arg arg) { - using char_type = typename Context::char_type; + typedef typename Context::char_type char_type; switch (arg.type_) { - case internal::NONE: + case internal::none_type: return vis(monostate()); - case internal::NAMED_ARG: + case internal::name_arg_type: FMT_ASSERT(false, "invalid argument type"); break; - case internal::INT: + case internal::int_type: return vis(arg.value_.int_value); - case internal::UINT: + case internal::uint_type: return vis(arg.value_.uint_value); - case internal::LONG_LONG: + case internal::long_long_type: return vis(arg.value_.long_long_value); - case internal::ULONG_LONG: + case internal::ulong_long_type: return vis(arg.value_.ulong_long_value); - case internal::BOOL: + case internal::bool_type: return vis(arg.value_.int_value != 0); - case internal::CHAR: + case internal::char_type: return vis(static_cast(arg.value_.int_value)); - case internal::DOUBLE: + case internal::double_type: return vis(arg.value_.double_value); - case internal::LONG_DOUBLE: + case internal::long_double_type: return vis(arg.value_.long_double_value); - case internal::CSTRING: + case internal::cstring_type: return vis(arg.value_.string.value); - case internal::STRING: + case internal::string_type: return vis(basic_string_view( arg.value_.string.value, arg.value_.string.size)); - case internal::POINTER: + case internal::pointer_type: return vis(arg.value_.pointer); - case internal::CUSTOM: + case internal::custom_type: return vis(typename basic_arg::handle(arg.value_.custom)); } - return typename std::result_of::type(); + return typename internal::result_of::type(); } enum alignment { @@ -1058,7 +1097,7 @@ class format_spec { }; // template -// using fill_spec = format_spec; +// typedef format_spec fill_spec; template class fill_spec : public format_spec { public: @@ -1287,14 +1326,14 @@ void arg_map::init(const basic_format_args &args) { if (map_) return; map_ = new entry[args.max_size()]; - bool use_values = args.type(MAX_PACKED_ARGS - 1) == internal::NONE; + bool use_values = args.type(max_packed_args - 1) == internal::none_type; if (use_values) { for (unsigned i = 0;/*nothing*/; ++i) { internal::type arg_type = args.type(i); switch (arg_type) { - case internal::NONE: + case internal::none_type: return; - case internal::NAMED_ARG: + case internal::name_arg_type: push_back(args.values_[i]); break; default: @@ -1303,15 +1342,15 @@ void arg_map::init(const basic_format_args &args) { } return; } - for (unsigned i = 0; i != MAX_PACKED_ARGS; ++i) { - if (args.type(i) == internal::NAMED_ARG) + for (unsigned i = 0; i != max_packed_args; ++i) { + if (args.type(i) == internal::name_arg_type) push_back(args.args_[i].value_); } - for (unsigned i = MAX_PACKED_ARGS; ; ++i) { + for (unsigned i = max_packed_args; ; ++i) { switch (args.args_[i].type_) { - case internal::NONE: + case internal::none_type: return; - case internal::NAMED_ARG: + case internal::name_arg_type: push_back(args.args_[i].value_); break; default: @@ -1323,11 +1362,11 @@ void arg_map::init(const basic_format_args &args) { template class arg_formatter_base { public: - using char_type = typename Range::value_type; - using format_specs = basic_format_specs; + typedef typename Range::value_type char_type; + typedef basic_format_specs format_specs; private: - using writer_type = basic_writer; + typedef basic_writer writer_type; writer_type writer_; format_specs &specs_; @@ -1360,7 +1399,7 @@ class arg_formatter_base { } void write(const char_type *value) { - auto length = value != 0 ? std::char_traits::length(value) : 0; + auto length = value != FMT_NULL ? std::char_traits::length(value) : 0; writer_.write_str(basic_string_view(value, length), specs_); } @@ -1385,33 +1424,35 @@ class arg_formatter_base { write(value); } + struct char_spec_handler : internal::error_handler { + arg_formatter_base &formatter; + char_type value; + + char_spec_handler(arg_formatter_base& f, char_type val) + : formatter(f), value(val) {} + + void on_int() { formatter.writer_.write_int(value, formatter.specs_); } + void on_char() { formatter.write_char(value); } + }; + void operator()(char_type value) { - struct spec_handler : internal::error_handler { - arg_formatter_base &formatter; - char_type value; - - spec_handler(arg_formatter_base& f, char_type val) - : formatter(f), value(val) {} - - void on_int() { formatter.writer_.write_int(value, formatter.specs_); } - void on_char() { formatter.write_char(value); } - }; - internal::handle_char_specs(specs_, spec_handler(*this, value)); + internal::handle_char_specs(specs_, char_spec_handler(*this, value)); } + struct cstring_spec_handler : internal::error_handler { + arg_formatter_base &formatter; + const char_type *value; + + cstring_spec_handler(arg_formatter_base &f, const char_type *val) + : formatter(f), value(val) {} + + void on_string() { formatter.write(value); } + void on_pointer() { formatter.write_pointer(value); } + }; + void operator()(const char_type *value) { - struct spec_handler : internal::error_handler { - arg_formatter_base &formatter; - const char_type *value; - - spec_handler(arg_formatter_base &f, const char_type *val) - : formatter(f), value(val) {} - - void on_string() { formatter.write(value); } - void on_pointer() { formatter.write_pointer(value); } - }; internal::handle_cstring_type_spec( - specs_.type_, spec_handler(*this, value)); + specs_.type_, cstring_spec_handler(*this, value)); } void operator()(basic_string_view value) { @@ -1427,6 +1468,10 @@ class arg_formatter_base { struct format_string {}; +template +struct is_format_string: + std::integral_constant::value> {}; + template FMT_CONSTEXPR bool is_name_start(Char c) { return ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || '_' == c; @@ -1460,20 +1505,20 @@ FMT_CONSTEXPR unsigned parse_nonnegative_int(Iterator &it, ErrorHandler &&eh) { } template -class custom_formatter { +class custom_formatter: public function { private: Context &ctx_; public: explicit custom_formatter(Context &ctx): ctx_(ctx) {} - bool operator()(typename basic_arg::handle h) { + bool operator()(typename basic_arg::handle h) const { h.format(ctx_); return true; } template - bool operator()(T) { return false; } + bool operator()(T) const { return false; } }; template @@ -1485,12 +1530,13 @@ struct is_integer { }; template -class width_checker { +class width_checker: public function { public: explicit FMT_CONSTEXPR width_checker(ErrorHandler &eh) : handler_(eh) {} template - FMT_CONSTEXPR typename std::enable_if< + FMT_CONSTEXPR + typename std::enable_if< is_integer::value, unsigned long long>::type operator()(T value) { if (is_negative(value)) handler_.on_error("negative width"); @@ -1509,7 +1555,7 @@ class width_checker { }; template -class precision_checker { +class precision_checker: public function { public: explicit FMT_CONSTEXPR precision_checker(ErrorHandler &eh) : handler_(eh) {} @@ -1608,7 +1654,7 @@ class specs_checker : public Handler { } FMT_CONSTEXPR void end_precision() { - if (is_integral(arg_type_) || arg_type_ == POINTER) + if (is_integral(arg_type_) || arg_type_ == pointer_type) this->on_error("precision not allowed for this argument type"); } @@ -1620,8 +1666,8 @@ class specs_checker : public Handler { FMT_CONSTEXPR void check_sign() { require_numeric_argument(); - if (is_integral(arg_type_) && arg_type_ != INT && arg_type_ != LONG_LONG && - arg_type_ != CHAR) { + if (is_integral(arg_type_) && arg_type_ != int_type && + arg_type_ != long_long_type && arg_type_ != internal::char_type) { this->on_error("format specifier requires signed argument"); } } @@ -1696,7 +1742,7 @@ struct arg_ref { } Kind kind; - union { + FMT_UNION { unsigned index; basic_string_view name; }; @@ -1717,7 +1763,7 @@ template class dynamic_specs_handler : public specs_setter { public: - using char_type = typename ParseContext::char_type; + typedef typename ParseContext::char_type char_type; FMT_CONSTEXPR dynamic_specs_handler( dynamic_format_specs &specs, ParseContext &ctx) @@ -1742,7 +1788,7 @@ class dynamic_specs_handler : } private: - using arg_ref_type = arg_ref; + typedef arg_ref arg_ref_type; template FMT_CONSTEXPR arg_ref_type make_arg_ref(Id arg_id) { @@ -1760,7 +1806,7 @@ class dynamic_specs_handler : template FMT_CONSTEXPR Iterator parse_arg_id(Iterator it, IDHandler &&handler) { - using char_type = typename std::iterator_traits::value_type; + typedef typename std::iterator_traits::value_type char_type; char_type c = *it; if (c == '}' || c == ':') { handler(); @@ -1830,7 +1876,7 @@ struct precision_adapter { // format specifiers. template FMT_CONSTEXPR Iterator parse_format_specs(Iterator it, SpecHandler &&handler) { - using char_type = typename std::iterator_traits::value_type; + typedef typename std::iterator_traits::value_type char_type; // Parse fill and alignment. if (char_type c = *it) { alignment align = ALIGN_DEFAULT; @@ -1949,7 +1995,7 @@ struct id_adapter { template FMT_CONSTEXPR void parse_format_string(Iterator it, Handler &&handler) { - using char_type = typename std::iterator_traits::value_type; + typedef typename std::iterator_traits::value_type char_type; auto start = it; while (*it) { char_type ch = *it++; @@ -1988,7 +2034,8 @@ FMT_CONSTEXPR void parse_format_string(Iterator it, Handler &&handler) { template FMT_CONSTEXPR const typename ParseContext::char_type * parse_format_specs(ParseContext &ctx) { - formatter f; + // GCC 7.2 requires initializer. + formatter f{}; return f.parse(ctx); } @@ -1997,7 +2044,8 @@ class format_string_checker { public: explicit FMT_CONSTEXPR format_string_checker( basic_string_view format_str, ErrorHandler eh) - : context_(format_str, eh) {} + : arg_id_(-1), context_(format_str, eh), + parse_funcs_{&parse_format_specs...} {} FMT_CONSTEXPR void on_text(const Char *, const Char *) {} @@ -2025,7 +2073,7 @@ class format_string_checker { } private: - using parse_context_type = basic_parse_context; + typedef basic_parse_context parse_context_type; enum { NUM_ARGS = sizeof...(Args) }; FMT_CONSTEXPR void check_arg_id() { @@ -2034,13 +2082,11 @@ class format_string_checker { } // Format specifier parsing function. - using parse_func = const Char *(*)(parse_context_type &); + typedef const Char *(*parse_func)(parse_context_type &); - int arg_id_ = -1; + int arg_id_; parse_context_type context_; - parse_func parse_funcs_[NUM_ARGS > 0 ? NUM_ARGS : 1] = { - &parse_format_specs... - }; + parse_func parse_funcs_[NUM_ARGS > 0 ? NUM_ARGS : 1]; }; template @@ -2051,12 +2097,20 @@ FMT_CONSTEXPR bool check_format_string( return true; } +template +void check_format_string(String format_str) { + FMT_CONSTEXPR_DECL bool invalid_format = + internal::check_format_string( + string_view(format_str.data(), format_str.size())); + (void)invalid_format; +} + // Specifies whether to format T using the standard formatter. // It is not possible to use get_type in formatter specialization directly // because of a bug in MSVC. template struct format_type : - std::integral_constant::value != CUSTOM> {}; + std::integral_constant::value != custom_type> {}; // Specifies whether to format enums. template @@ -2065,7 +2119,7 @@ struct format_enum : std::integral_constant::value> {}; template