diff --git a/include/fmt/core.h b/include/fmt/core.h index 3174b173..a1fddb73 100644 --- a/include/fmt/core.h +++ b/include/fmt/core.h @@ -1,35 +1,16 @@ -/* - Formatting library for C++ - - Copyright (c) 2012 - 2016, Victor Zverovich - All rights reserved. - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions are met: - - 1. Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. - 2. Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR - ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ +// Formatting library for C++ - the core API +// +// Copyright (c) 2012 - present, Victor Zverovich +// All rights reserved. +// +// For the license information refer to format.h. #ifndef FMT_CORE_H_ #define FMT_CORE_H_ #include #include +#include #include #include @@ -92,36 +73,15 @@ # define FMT_ASSERT(condition, message) assert((condition) && message) #endif +#define FMT_DELETED = delete + // A macro to disallow the copy construction and assignment. -#define FMT_DISALLOW_COPY_AND_ASSIGN(TypeName) \ - TypeName(const TypeName&) = delete; \ - TypeName& operator=(const TypeName&) = delete -#define FMT_DELETED_OR_UNDEFINED = delete +#define FMT_DISALLOW_COPY_AND_ASSIGN(Type) \ + Type(const Type &) FMT_DELETED; \ + void operator=(const Type &) FMT_DELETED namespace fmt { -template -class basic_buffer; - -using buffer = basic_buffer; -using wbuffer = basic_buffer; - -template -class basic_arg; - -template -class basic_format_args; - -template -class basic_context; - -using context = basic_context; -using wcontext = basic_context; - -// A formatter for objects of type T. -template -struct formatter; - /** \rst An implementation of ``std::basic_string_view`` for pre-C++17. It provides a @@ -138,10 +98,10 @@ class basic_string_view { using char_type = Char; using iterator = const Char *; - constexpr basic_string_view() noexcept : data_(0), size_(0) {} + constexpr basic_string_view() FMT_NOEXCEPT : data_(0), size_(0) {} /** Constructs a string reference object from a C string and a size. */ - constexpr basic_string_view(const Char *s, size_t size) noexcept + constexpr basic_string_view(const Char *s, size_t size) FMT_NOEXCEPT : data_(s), size_(size) {} /** @@ -158,7 +118,7 @@ class basic_string_view { Constructs a string reference from an ``std::string`` object. \endrst */ - constexpr basic_string_view(const std::basic_string &s) noexcept + constexpr basic_string_view(const std::basic_string &s) FMT_NOEXCEPT : data_(s.c_str()), size_(s.size()) {} /** @@ -216,6 +176,96 @@ class basic_string_view { using string_view = basic_string_view; using wstring_view = basic_string_view; +/** A contiguous memory buffer with an optional growing ability. */ +template +class basic_buffer { + private: + FMT_DISALLOW_COPY_AND_ASSIGN(basic_buffer); + + T *ptr_; + std::size_t size_; + std::size_t capacity_; + + protected: + basic_buffer() FMT_NOEXCEPT : ptr_(0), size_(0), capacity_(0) {} + + /** Sets the buffer data and capacity. */ + void set(T *data, std::size_t capacity) FMT_NOEXCEPT { + ptr_ = data; + capacity_ = capacity; + } + + /** + \rst + Increases the buffer capacity to hold at least *capacity* elements. + \endrst + */ + virtual void grow(std::size_t capacity) = 0; + + public: + using value_type = T; + + virtual ~basic_buffer() {} + + T *begin() FMT_NOEXCEPT { return ptr_; } + T *end() FMT_NOEXCEPT { return ptr_ + size_; } + + /** Returns the size of this buffer. */ + std::size_t size() const FMT_NOEXCEPT { return size_; } + + /** Returns the capacity of this buffer. */ + std::size_t capacity() const FMT_NOEXCEPT { return capacity_; } + + /** Returns a pointer to the buffer data. */ + T *data() FMT_NOEXCEPT { return ptr_; } + + /** Returns a pointer to the buffer data. */ + const T *data() const FMT_NOEXCEPT { return ptr_; } + + /** + Resizes the buffer. If T is a POD type new elements may not be initialized. + */ + void resize(std::size_t new_size) { + reserve(new_size); + size_ = new_size; + } + + /** + \rst + Reserves space to store at least *capacity* elements. + \endrst + */ + void reserve(std::size_t capacity) { + if (capacity > capacity_) + grow(capacity); + } + + void push_back(const T &value) { + reserve(size_ + 1); + ptr_[size_++] = value; + } + + /** Appends data to the end of the buffer. */ + template + void append(const U *begin, const U *end); + + T &operator[](std::size_t index) { return ptr_[index]; } + const T &operator[](std::size_t index) const { return ptr_[index]; } +}; + +using buffer = basic_buffer; +using wbuffer = basic_buffer; + +template +class basic_arg; + +template +class basic_format_args; + +// A formatter for objects of type T. +template +struct formatter; + namespace internal { template @@ -286,14 +336,17 @@ FMT_DISABLE_CONVERSION_TO_INT(float); FMT_DISABLE_CONVERSION_TO_INT(double); FMT_DISABLE_CONVERSION_TO_INT(long double); -template +template +struct named_arg_base; + +template struct named_arg; template struct is_named_arg : std::false_type {}; -template -struct is_named_arg> : std::true_type {}; +template +struct is_named_arg> : std::true_type {}; enum type { NONE, NAMED_ARG, @@ -370,6 +423,9 @@ constexpr uint64_t get_types() { template <> constexpr uint64_t get_types() { return 0; } +template +constexpr basic_arg make_arg(const T &value); + template struct string_value { const Char *value; @@ -378,12 +434,8 @@ struct string_value { template struct custom_value { - using format_func = void (*)( - basic_buffer &buffer, - const void *arg, Context &ctx); - const void *value; - format_func format; + void (*format)(const void *arg, Context &ctx); }; // A formatting argument value. @@ -487,13 +539,17 @@ class value { custom.format = &format_custom_arg; } - // Additional template param `Ctx` is needed here because get_type always - // uses basic_context. - template - value(const named_arg &value) { - static_assert( - get_type &>() == NAMED_ARG, "invalid type"); - pointer = &value; + template + value(const named_arg &na) { + static_assert(get_type &>() == NAMED_ARG, + "invalid type"); + basic_arg arg = make_arg(na.value); + std::memcpy(na.data, &arg, sizeof(arg)); + pointer = &na; + } + + const named_arg_base &as_named_arg() { + return *static_cast*>(pointer); } private: @@ -519,15 +575,14 @@ class value { // Formats an argument of a custom type, such as a user-defined class. template - static void format_custom_arg( - basic_buffer &buffer, const void *arg, Context &ctx) { + static void format_custom_arg(const void *arg, Context &ctx) { // 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; auto &&parse_ctx = ctx.parse_context(); parse_ctx.advance_to(f.parse(parse_ctx)); - f.format(buffer, *static_cast(arg), ctx); + f.format(*static_cast(arg), ctx); } }; @@ -536,9 +591,6 @@ enum { MAX_PACKED_ARGS = 15 }; template class arg_map; - -template -constexpr basic_arg make_arg(const T &value); } // A formatting argument. It is a trivially copyable/constructible type to @@ -574,7 +626,9 @@ class basic_arg { constexpr basic_arg() : type_(internal::NONE) {} - explicit operator bool() const noexcept { return type_ != internal::NONE; } + explicit operator bool() const FMT_NOEXCEPT { + return type_ != internal::NONE; + } internal::type type() const { return type_; } @@ -648,27 +702,16 @@ constexpr basic_arg make_arg(const T &value) { template inline typename std::enable_if>::type - make_arg(const T& value) { + make_arg(const T &value) { return value; } template inline typename std::enable_if>::type - make_arg(const T& value) { + make_arg(const T &value) { return make_arg(value); } -template -struct named_arg : basic_arg { - using char_type = typename Context::char_type; - - basic_string_view name; - - template - named_arg(basic_string_view argname, const T &value) - : basic_arg(make_arg(value)), name(argname) {} -}; - template class arg_map { private: @@ -676,16 +719,17 @@ class arg_map { using char_type = typename Context::char_type; - struct arg { + struct entry { basic_string_view name; - basic_arg value; + basic_arg arg; }; - arg *map_ = nullptr; + entry *map_ = nullptr; unsigned size_ = 0; - void push_back(arg a) { - map_[size_] = a; + void push_back(value val) { + const internal::named_arg_base &named = val.as_named_arg(); + map_[size_] = entry{named.name, named.template deserialize()}; ++size_; } @@ -694,29 +738,29 @@ class arg_map { void init(const basic_format_args &args); ~arg_map() { delete [] map_; } - const basic_arg - *find(const basic_string_view &name) const { + basic_arg find(basic_string_view name) const { // The list is unsorted, so just return the first matching name. for (auto it = map_, end = map_ + size_; it != end; ++it) { if (it->name == name) - return &it->value; + return it->arg; } - return 0; + return basic_arg(); } }; -template -class context_base : public basic_parse_context{ +template +class context_base : public basic_parse_context { private: + Range &range_; basic_format_args args_; protected: + using char_type = typename Range::value_type; using format_arg = basic_arg; - context_base(basic_string_view format_str, + context_base(Range &range, basic_string_view format_str, basic_format_args args) - : basic_parse_context(format_str), args_(args) {} - ~context_base() {} + : basic_parse_context(format_str), range_(range), args_(args) {} basic_format_args args() const { return args_; } @@ -736,29 +780,30 @@ class context_base : public basic_parse_context{ } public: - basic_parse_context &parse_context() { return *this; } + basic_parse_context &parse_context() { return *this; } + Range &range() { return range_; } }; } // namespace internal -template +template class basic_context : - public internal::context_base> { + public internal::context_base> { public: /** The character type for the output. */ - using char_type = Char; + using char_type = typename Range::value_type; template - using formatter_type = formatter; + using formatter_type = formatter; private: - internal::arg_map> map_; + internal::arg_map map_; FMT_DISALLOW_COPY_AND_ASSIGN(basic_context); - using Base = internal::context_base>; + using base = internal::context_base; - using format_arg = typename Base::format_arg; - using Base::get_arg; + using format_arg = typename base::format_arg; + using base::get_arg; public: /** @@ -767,18 +812,21 @@ class basic_context : stored in the object so make sure they have appropriate lifetimes. \endrst */ - basic_context( - basic_string_view format_str, basic_format_args args) - : Base(format_str, args) {} + basic_context(Range &range, basic_string_view format_str, + basic_format_args args) + : base(range, format_str, args) {} format_arg next_arg() { return this->do_get_arg(this->next_arg_id()); } format_arg get_arg(unsigned arg_id) { return this->do_get_arg(arg_id); } // Checks if manual indexing is used and returns the argument with // specified name. - format_arg get_arg(basic_string_view name); + format_arg get_arg(basic_string_view name); }; +using context = basic_context; +using wcontext = basic_context; + template class arg_store { private: @@ -876,7 +924,7 @@ class basic_format_args { format_arg operator[](size_type index) const { format_arg arg = get(index); return arg.type_ == internal::NAMED_ARG ? - *static_cast(arg.value_.pointer) : arg; + arg.value_.as_named_arg().template deserialize() : arg; } unsigned max_size() const { @@ -888,6 +936,31 @@ class basic_format_args { using format_args = basic_format_args; using wformat_args = basic_format_args; +namespace internal { +template +struct named_arg_base { + basic_string_view name; + + // Serialized value. + mutable char data[sizeof(basic_arg)]; + + template + basic_arg deserialize() const { + basic_arg arg; + std::memcpy(&arg, data, sizeof(basic_arg)); + return arg; + } +}; + +template +struct named_arg : named_arg_base { + const T &value; + + named_arg(basic_string_view name, const T &val) + : named_arg_base{name}, value(val) {} +}; +} + /** \rst Returns a named argument for formatting functions. @@ -895,25 +968,24 @@ using wformat_args = basic_format_args; **Example**:: print("Elapsed time: {s:.2f} seconds", arg("s", 1.23)); - \endrst */ template -inline internal::named_arg arg(string_view name, const T &arg) { - return internal::named_arg(name, arg); +inline internal::named_arg arg(string_view name, const T &arg) { + return internal::named_arg(name, arg); } template -inline internal::named_arg arg(wstring_view name, const T &arg) { - return internal::named_arg(name, arg); +inline internal::named_arg arg(wstring_view name, const T &arg) { + return internal::named_arg(name, arg); } // The following two functions are deleted intentionally to disable // nested named arguments as in ``format("{}", arg("a", arg("b", 42)))``. -template -void arg(string_view, internal::named_arg) FMT_DELETED_OR_UNDEFINED; -template -void arg(wstring_view, internal::named_arg) FMT_DELETED_OR_UNDEFINED; +template +void arg(string_view, internal::named_arg) FMT_DELETED; +template +void arg(wstring_view, internal::named_arg) FMT_DELETED; enum Color { BLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE }; diff --git a/include/fmt/format.cc b/include/fmt/format.cc index 1df50e59..32975ca1 100644 --- a/include/fmt/format.cc +++ b/include/fmt/format.cc @@ -1,29 +1,9 @@ -/* - Formatting library for C++ - - Copyright (c) 2012 - 2016, Victor Zverovich - All rights reserved. - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions are met: - - 1. Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. - 2. Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR - ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ +// Formatting library for C++ +// +// Copyright (c) 2012 - 2016, Victor Zverovich +// All rights reserved. +// +// For the license information refer to format.h. #include "fmt/format.h" #include "fmt/locale.h" @@ -348,18 +328,18 @@ FMT_FUNC void windows_error::init( FMT_FUNC void internal::format_windows_error( buffer &out, int error_code, string_view message) FMT_NOEXCEPT { FMT_TRY { - wmemory_buffer buffer; - buffer.resize(INLINE_BUFFER_SIZE); + wmemory_buffer buf; + buf.resize(INLINE_BUFFER_SIZE); for (;;) { - wchar_t *system_message = &buffer[0]; + wchar_t *system_message = &buf[0]; int result = FormatMessageW( FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, 0, error_code, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), - system_message, static_cast(buffer.size()), 0); + system_message, static_cast(buf.size()), 0); if (result != 0) { utf16_to_utf8 utf8_message; if (utf8_message.convert(system_message) == ERROR_SUCCESS) { - basic_writer w(out); + basic_writer w(out); w.write(message); w.write(": "); w.write(utf8_message); @@ -369,10 +349,10 @@ FMT_FUNC void internal::format_windows_error( } if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) break; // Can't get error message, report error code instead. - buffer.resize(buffer.size() * 2); + buf.resize(buf.size() * 2); } } FMT_CATCH(...) {} - fmt::format_error_code(out, error_code, message); // 'fmt::' is for bcc32. + format_error_code(out, error_code, message); } #endif // FMT_USE_WINDOWS_H @@ -467,8 +447,6 @@ template int internal::char_traits::format_float( template wchar_t internal::thousands_sep(locale_provider *lp); -template class basic_context; - template void basic_fixed_buffer::grow(std::size_t); template void internal::arg_map::init(const wformat_args &args); diff --git a/include/fmt/format.h b/include/fmt/format.h index 0f015ad9..8fabd8e0 100644 --- a/include/fmt/format.h +++ b/include/fmt/format.h @@ -113,9 +113,6 @@ #endif #ifndef FMT_USE_USER_DEFINED_LITERALS -// All compilers which support UDLs also support variadic templates. This -// 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. # if (FMT_HAS_FEATURE(cxx_user_literals) || \ FMT_GCC_VERSION >= 407 || FMT_MSC_VER >= 1900) && \ @@ -261,7 +258,7 @@ class numeric_limits : namespace fmt { -template +template class basic_writer; /** A formatting error such as invalid format string. */ @@ -308,84 +305,7 @@ class locale; class locale_provider { public: virtual ~locale_provider() {} - virtual locale locale(); -}; - -/** A contiguous memory buffer with an optional growing ability. */ -template -class basic_buffer { - private: - FMT_DISALLOW_COPY_AND_ASSIGN(basic_buffer); - - T *ptr_; - std::size_t size_; - std::size_t capacity_; - - protected: - basic_buffer() FMT_NOEXCEPT : ptr_(0), size_(0), capacity_(0) {} - - /** Sets the buffer data and capacity. */ - void set(T* data, std::size_t capacity) FMT_NOEXCEPT { - ptr_ = data; - capacity_ = capacity; - } - - /** - \rst - Increases the buffer capacity to hold at least *capacity* elements. - \endrst - */ - virtual void grow(std::size_t capacity) = 0; - - public: - using value_type = T; - - virtual ~basic_buffer() {} - - T *begin() FMT_NOEXCEPT { return ptr_; } - T *end() FMT_NOEXCEPT { return ptr_ + capacity_; } - - /** Returns the size of this buffer. */ - std::size_t size() const FMT_NOEXCEPT { return size_; } - - /** Returns the capacity of this buffer. */ - std::size_t capacity() const FMT_NOEXCEPT { return capacity_; } - - /** Returns a pointer to the buffer data. */ - T *data() FMT_NOEXCEPT { return ptr_; } - - /** Returns a pointer to the buffer data. */ - const T *data() const FMT_NOEXCEPT { return ptr_; } - - /** - Resizes the buffer. If T is a POD type new elements may not be initialized. - */ - void resize(std::size_t new_size) { - reserve(new_size); - size_ = new_size; - } - - /** - \rst - Reserves space to store at least *capacity* elements. - \endrst - */ - void reserve(std::size_t capacity) { - if (capacity > capacity_) - grow(capacity); - } - - void push_back(const T &value) { - reserve(size_ + 1); - ptr_[size_++] = value; - } - - /** Appends data to the end of the buffer. */ - template - void append(const U *begin, const U *end); - - T &operator[](std::size_t index) { return ptr_[index]; } - const T &operator[](std::size_t index) const { return ptr_[index]; } + virtual fmt::locale locale(); }; template @@ -690,6 +610,33 @@ constexpr const Char *pointer_from(null_terminating_iterator it) { return it.ptr_; } +// A range that can grow dynamically. +template +class dynamic_range { + private: + Container &container_; + + public: + using iterator = decltype(container_.begin()); + using value_type = typename Container::value_type; + + struct sentinel { + friend bool operator!=(sentinel, iterator) { return false; } + friend bool operator!=(iterator, sentinel) { return false; } + }; + + explicit dynamic_range(Container &c) : container_(c) {} + + iterator begin() const { return container_.begin(); } + sentinel end() const { return sentinel(); } + + friend iterator grow(dynamic_range r, size_t n) { + auto size = r.container_.size(); + r.container_.resize(size + n); + return r.container_.begin() + size; + } +}; + // Returns true if value is negative, false otherwise. // Same as (value < 0) but doesn't produce warnings if T is an unsigned type. template @@ -1255,11 +1202,8 @@ template void arg_map::init(const basic_format_args &args) { if (map_) return; - map_ = new arg[args.max_size()]; - typedef internal::named_arg NamedArg; - const NamedArg *named_arg = 0; - bool use_values = - args.type(MAX_PACKED_ARGS - 1) == internal::NONE; + map_ = new entry[args.max_size()]; + bool use_values = args.type(MAX_PACKED_ARGS - 1) == internal::NONE; if (use_values) { for (unsigned i = 0;/*nothing*/; ++i) { internal::type arg_type = args.type(i); @@ -1267,8 +1211,7 @@ void arg_map::init(const basic_format_args &args) { case internal::NONE: return; case internal::NAMED_ARG: - named_arg = static_cast(args.values_[i].pointer); - push_back(arg{named_arg->name, *named_arg}); + push_back(args.values_[i]); break; default: break; // Do nothing. @@ -1277,19 +1220,15 @@ void arg_map::init(const basic_format_args &args) { return; } for (unsigned i = 0; i != MAX_PACKED_ARGS; ++i) { - internal::type arg_type = args.type(i); - if (arg_type == internal::NAMED_ARG) { - named_arg = static_cast(args.args_[i].value_.pointer); - push_back(arg{named_arg->name, *named_arg}); - } + if (args.type(i) == internal::NAMED_ARG) + push_back(args.args_[i].value_); } for (unsigned i = MAX_PACKED_ARGS; ; ++i) { switch (args.args_[i].type_) { case internal::NONE: return; case internal::NAMED_ARG: - named_arg = static_cast(args.args_[i].value_.pointer); - push_back(arg{named_arg->name, *named_arg}); + push_back(args.args_[i].value_); break; default: break; // Do nothing. @@ -1434,15 +1373,13 @@ constexpr unsigned parse_nonnegative_int(Iterator &it, ErrorHandler &&eh) { template class custom_formatter { private: - basic_buffer &buffer_; Context &ctx_; public: - custom_formatter(basic_buffer &buffer, Context &ctx) - : buffer_(buffer), ctx_(ctx) {} + explicit custom_formatter(Context &ctx): ctx_(ctx) {} bool operator()(typename basic_arg::handle h) { - h.format(buffer_, ctx_); + h.format(ctx_); return true; } @@ -2031,17 +1968,18 @@ struct format_type : std::integral_constant() != CUSTOM> {}; template struct format_enum : std::integral_constant::value> {}; -template