From 94edb1a71c578c1ee81f28c063d96e344d31a16d Mon Sep 17 00:00:00 2001 From: Victor Zverovich Date: Wed, 6 Dec 2017 07:42:42 -0800 Subject: [PATCH] Add a lightweight header for the core API --- CMakeLists.txt | 4 +- include/fmt/core.h | 967 ++++++++++++++++++++++++++++++++++ include/fmt/format.cc | 4 + include/fmt/format.h | 1048 +++---------------------------------- include/fmt/write.h | 61 --- test/assert-test.cc | 2 +- test/format-test.cc | 3 +- test/gtest-extra.h | 2 +- test/header-only-test.cc | 2 +- test/header-only-test2.cc | 2 +- test/printf-test.cc | 2 +- test/util-test.cc | 2 +- 12 files changed, 1057 insertions(+), 1042 deletions(-) create mode 100644 include/fmt/core.h delete mode 100644 include/fmt/write.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 1ee1e50c..ea883b80 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -82,8 +82,8 @@ endfunction() # Define the fmt library, its includes and the needed defines. # format.cc is added to FMT_HEADERS for the header-only configuration. -add_headers(FMT_HEADERS format.h format.cc locale.h ostream.h ostream.cc - printf.h printf.cc string.h time.h write.h) +add_headers(FMT_HEADERS core.h format.h format.cc locale.h ostream.h ostream.cc + printf.h printf.cc string.h time.h) if (HAVE_OPEN) add_headers(FMT_HEADERS posix.h) add_headers(FMT_SOURCES posix.cc) diff --git a/include/fmt/core.h b/include/fmt/core.h new file mode 100644 index 00000000..50159808 --- /dev/null +++ b/include/fmt/core.h @@ -0,0 +1,967 @@ +/* + 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. + */ + +#ifndef FMT_CORE_H_ +#define FMT_CORE_H_ + +#include +#include +#include +#include +#include +#include +#include + +#ifdef __has_feature +# define FMT_HAS_FEATURE(x) __has_feature(x) +#else +# define FMT_HAS_FEATURE(x) 0 +#endif + +#ifdef __has_builtin +# define FMT_HAS_BUILTIN(x) __has_builtin(x) +#else +# define FMT_HAS_BUILTIN(x) 0 +#endif + +#ifdef __GNUC__ +# define FMT_GCC_VERSION (__GNUC__ * 100 + __GNUC_MINOR__) +#endif + +#ifdef _MSC_VER +# define FMT_MSC_VER _MSC_VER +#else +# define FMT_MSC_VER 0 +#endif + +// Check if exceptions are disabled. +#if defined(__GNUC__) && !defined(__EXCEPTIONS) +# define FMT_EXCEPTIONS 0 +#endif +#if FMT_MSC_VER && !_HAS_EXCEPTIONS +# define FMT_EXCEPTIONS 0 +#endif +#ifndef FMT_EXCEPTIONS +# define FMT_EXCEPTIONS 1 +#endif + +// Define FMT_USE_NOEXCEPT to make fmt use noexcept (C++11 feature). +#ifndef FMT_USE_NOEXCEPT +# define FMT_USE_NOEXCEPT 0 +#endif + +#ifndef FMT_NOEXCEPT +# if FMT_EXCEPTIONS +# if FMT_USE_NOEXCEPT || FMT_HAS_FEATURE(cxx_noexcept) || \ + FMT_GCC_VERSION >= 408 || FMT_MSC_VER >= 1900 +# define FMT_NOEXCEPT noexcept +# else +# define FMT_NOEXCEPT throw() +# endif +# else +# define FMT_NOEXCEPT +# endif +#endif + +#if !defined(FMT_HEADER_ONLY) && defined(_WIN32) +# ifdef FMT_EXPORT +# define FMT_API __declspec(dllexport) +# elif defined(FMT_SHARED) +# define FMT_API __declspec(dllimport) +# endif +#endif +#ifndef FMT_API +# define FMT_API +#endif + +#ifndef FMT_ASSERT +# define FMT_ASSERT(condition, message) assert((condition) && message) +#endif + +// 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 + +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 + subset of the API. + \endrst + */ +template +class basic_string_view { + private: + const Char *data_; + size_t size_; + + public: + using char_type = Char; + using iterator = const Char *; + + constexpr basic_string_view() 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 + : data_(s), size_(size) {} + + /** + \rst + Constructs a string reference object from a C string computing + the size with ``std::char_traits::length``. + \endrst + */ + basic_string_view(const Char *s) + : data_(s), size_(std::char_traits::length(s)) {} + + /** + \rst + Constructs a string reference from an ``std::string`` object. + \endrst + */ + constexpr basic_string_view(const std::basic_string &s) noexcept + : data_(s.c_str()), size_(s.size()) {} + + /** + \rst + Converts a string reference to an ``std::string`` object. + \endrst + */ + std::basic_string to_string() const { + return std::basic_string(data_, size_); + } + + /** Returns a pointer to the string data. */ + const Char *data() const { return data_; } + + /** Returns the string size. */ + constexpr size_t size() const { return size_; } + + constexpr iterator begin() const { return data_; } + constexpr iterator end() const { return data_ + size_; } + + constexpr void remove_prefix(size_t n) { + data_ += n; + size_ -= n; + } + + // Lexicographically compare this string reference to other. + int compare(basic_string_view other) const { + size_t size = size_ < other.size_ ? size_ : other.size_; + int result = std::char_traits::compare(data_, other.data_, size); + if (result == 0) + result = size_ == other.size_ ? 0 : (size_ < other.size_ ? -1 : 1); + return result; + } + + friend bool operator==(basic_string_view lhs, basic_string_view rhs) { + return lhs.compare(rhs) == 0; + } + friend bool operator!=(basic_string_view lhs, basic_string_view rhs) { + return lhs.compare(rhs) != 0; + } + friend bool operator<(basic_string_view lhs, basic_string_view rhs) { + return lhs.compare(rhs) < 0; + } + friend bool operator<=(basic_string_view lhs, basic_string_view rhs) { + return lhs.compare(rhs) <= 0; + } + friend bool operator>(basic_string_view lhs, basic_string_view rhs) { + return lhs.compare(rhs) > 0; + } + friend bool operator>=(basic_string_view lhs, basic_string_view rhs) { + return lhs.compare(rhs) >= 0; + } +}; + +typedef basic_string_view string_view; +typedef basic_string_view wstring_view; + +namespace internal { + +template +inline const T *as_const(T *p) { return p; } + +// A helper function to suppress bogus "conditional expression is constant" +// warnings. +template +inline T const_check(T value) { return value; } + +struct error_handler { + constexpr error_handler() {} + constexpr error_handler(const error_handler &) {} + + // This function is intentionally not constexpr to give a compile-time error. + void on_error(const char *message); +}; + +// Formatting of wide characters and strings into a narrow output is disallowed: +// fmt::format("{}", L"test"); // error +// To fix this, use a wide format string: +// fmt::format(L"{}", L"test"); +template +inline void require_wchar() { + static_assert( + std::is_same::value, + "formatting of wide characters into a narrow output is disallowed"); +} + +typedef char yes[1]; +typedef char no[2]; + +yes &convert(unsigned long long); +no &convert(...); + +template +struct convert_to_int_impl { + enum { value = ENABLE_CONVERSION }; +}; + +template +struct convert_to_int_impl2 { + enum { value = false }; +}; + +template +struct convert_to_int_impl2 { + enum { + // Don't convert numeric types. + value = convert_to_int_impl< + T, !std::numeric_limits::is_specialized>::value + }; +}; + +template +struct convert_to_int { + enum { + enable_conversion = sizeof(convert(std::declval())) == sizeof(yes) + }; + enum { value = convert_to_int_impl2::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 named_arg; + +template +struct is_named_arg : std::false_type {}; + +template +struct is_named_arg> : std::true_type {}; + +enum type { + NONE, NAMED_ARG, + // Integer types should go first, + INT, UINT, LONG_LONG, ULONG_LONG, BOOL, CHAR, LAST_INTEGER_TYPE = CHAR, + // followed by floating-point types. + DOUBLE, LONG_DOUBLE, LAST_NUMERIC_TYPE = LONG_DOUBLE, + CSTRING, STRING, POINTER, CUSTOM +}; + +constexpr bool is_integral(type t) { + FMT_ASSERT(t != internal::NAMED_ARG, "invalid argument type"); + return t > internal::NONE && t <= internal::LAST_INTEGER_TYPE; +} + +constexpr bool is_numeric(type t) { + FMT_ASSERT(t != internal::NAMED_ARG, "invalid argument type"); + return t > internal::NONE && t <= internal::LAST_NUMERIC_TYPE; +} + +template +constexpr type get_type() { + return std::is_reference::value || std::is_array::value ? + get_type::type>() : + (is_named_arg::value ? + NAMED_ARG : (convert_to_int::value ? INT : CUSTOM)); +} + +template <> constexpr type get_type() { return BOOL; } +template <> constexpr type get_type() { return INT; } +template <> constexpr type get_type() { return UINT; } +template <> constexpr type get_type() { return INT; } +template <> constexpr type get_type() { return UINT; } +template <> constexpr type get_type() { + return sizeof(long) == sizeof(int) ? INT : LONG_LONG; +} +template <> constexpr type get_type() { + return sizeof(unsigned long) == sizeof(unsigned) ? UINT : ULONG_LONG; +} +template <> constexpr type get_type() { return LONG_LONG; } +template <> constexpr type get_type() { return ULONG_LONG; } +template <> constexpr type get_type() { return DOUBLE; } +template <> constexpr type get_type() { return DOUBLE; } +template <> constexpr type get_type() { return LONG_DOUBLE; } +template <> constexpr type get_type() { return INT; } +template <> constexpr type get_type() { return UINT; } +template <> constexpr type get_type() { return CHAR; } + +#if !defined(_MSC_VER) || defined(_NATIVE_WCHAR_T_DEFINED) +template <> constexpr type get_type() { return CHAR; } +#endif + +template <> constexpr type get_type() { return CSTRING; } +template <> constexpr type get_type() { return CSTRING; } +template <> constexpr type get_type() { return CSTRING; } +template <> constexpr type get_type() { return CSTRING; } +template <> constexpr type get_type() { return CSTRING; } +template <> constexpr type get_type() { return CSTRING; } +template <> constexpr type get_type() { return STRING; } +template <> constexpr type get_type() { return STRING; } +template <> constexpr type get_type() { return CSTRING; } +template <> constexpr type get_type() { return CSTRING; } +template <> constexpr type get_type() { return STRING; } +template <> constexpr type get_type() { return STRING; } +template <> constexpr type get_type() { return POINTER; } +template <> constexpr type get_type() { return POINTER; } +template <> constexpr type get_type() { return POINTER; } + +template +constexpr uint64_t get_types() { + return get_type() | (get_types() << 4); +} + +template <> +constexpr uint64_t get_types() { return 0; } + +template +struct string_value { + const Char *value; + std::size_t size; +}; + +template +struct custom_value { + typedef void (*format_func)( + basic_buffer &buffer, + const void *arg, Context &ctx); + + const void *value; + format_func format; +}; + +// A formatting argument value. +template +class value { + public: + using char_type = typename Context::char_type; + + union { + int int_value; + unsigned uint_value; + long long long_long_value; + unsigned long long ulong_long_value; + double double_value; + long double long_double_value; + const void *pointer; + string_value string; + string_value sstring; + string_value ustring; + custom_value custom; + }; + + constexpr value() : int_value(0) {} + value(bool val) { set(int_value, val); } + value(short val) { set(int_value, val); } + value(unsigned short val) { set(uint_value, val); } + constexpr value(int val) : int_value(val) {} + value(unsigned val) { set(uint_value, val); } + + value(long val) { + // 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. + if (const_check(sizeof(val) == sizeof(int))) + int_value = static_cast(val); + else + long_long_value = val; + } + + value(unsigned long val) { + if (const_check(sizeof(val) == sizeof(unsigned))) + uint_value = static_cast(val); + else + ulong_long_value = val; + } + + value(long long val) { set(long_long_value, val); } + value(unsigned long long val) { set(ulong_long_value, val); } + value(float val) { set(double_value, val); } + value(double val) { set(double_value, val); } + value(long double val) { set(long_double_value, val); } + value(signed char val) { set(int_value, val); } + value(unsigned char val) { set(uint_value, val); } + value(char val) { set(int_value, val); } + +#if !defined(_MSC_VER) || defined(_NATIVE_WCHAR_T_DEFINED) + value(wchar_t value) { + require_wchar(); + set(int_value, value); + } +#endif + + // Formatting of wide strings into a narrow buffer and multibyte strings + // into a wide buffer is disallowed (https://github.com/fmtlib/fmt/pull/606). + value(char_type *s) { set(string.value, s); } + value(const char_type *s) { set(string.value, s); } + value(signed char *s) { set_cstring(sstring.value, s); } + value(const signed char *s) { set_cstring(sstring.value, s); } + value(unsigned char *s) { set_cstring(ustring.value, s); } + value(const unsigned char *s) { set_cstring(ustring.value, s); } + value(basic_string_view s) { set_string(s); } + value(const std::basic_string &s) { set_string(s); } + + // 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 + value(const T *p) { + static_assert(std::is_same::value, + "formatting of non-void pointers is disallowed"); + set(pointer, p); + } + + template + value(T *p) : value(as_const(p)) {} + + value(std::nullptr_t) { pointer = nullptr; } + + template + value(const T &value, + typename std::enable_if::value, int>::type = 0) { + static_assert(get_type() == INT, "invalid type"); + int_value = value; + } + + template + value(const T &value, + typename std::enable_if::value, int>::type = 0) { + static_assert(get_type() == CUSTOM, "invalid type"); + custom.value = &value; + custom.format = &format_custom_arg; + } + + // Additional template param `Char` is needed here because get_type always + // uses char. + template + value(const named_arg &value) { + static_assert( + get_type &>() == NAMED_ARG, "invalid type"); + pointer = &value; + } + + private: + template + constexpr void set(T &field, const U &value) { + static_assert(get_type() == TYPE, "invalid type"); + field = value; + } + + template + void set_string(const T &value) { + static_assert(get_type() == STRING, "invalid type"); + string.value = value.data(); + string.size = value.size(); + } + + template + constexpr void set_cstring(T &field, const U *str) { + static_assert(std::is_same::value, + "incompatible string types"); + set(field, str); + } + + // 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) { + // 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); + } +}; + +// Maximum number of arguments with packed types. +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 +// allow storage in basic_memory_buffer. +template +class basic_arg { + private: + internal::value value_; + internal::type type_; + + template + friend constexpr basic_arg internal::make_arg(const T &value); + + template + friend constexpr typename std::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; + + public: + class handle { + public: + explicit handle(internal::custom_value custom) + : custom_(custom) {} + + void format(basic_buffer &buf, Context &ctx) { + custom_.format(buf, custom_.value, ctx); + } + + private: + internal::custom_value custom_; + }; + + constexpr basic_arg() : type_(internal::NONE) {} + + explicit operator bool() const noexcept { return type_ != internal::NONE; } + + internal::type type() const { return type_; } + + bool is_integral() const { return internal::is_integral(type_); } + bool is_numeric() const { return internal::is_numeric(type_); } + bool is_pointer() const { return type_ == internal::POINTER; } +}; + +// Parsing context consisting of a format string range being parsed and an +// argument counter for automatic indexing. +template +class basic_parse_context : private ErrorHandler { + private: + basic_string_view format_str_; + int next_arg_id_; + + protected: + constexpr bool check_no_auto_index() { + if (next_arg_id_ > 0) { + on_error("cannot switch from automatic to manual argument indexing"); + return false; + } + next_arg_id_ = -1; + return true; + } + + public: + using char_type = Char; + using iterator = typename basic_string_view::iterator; + + explicit constexpr basic_parse_context( + basic_string_view format_str, ErrorHandler eh = ErrorHandler()) + : ErrorHandler(eh), format_str_(format_str), next_arg_id_(0) {} + + // Returns an iterator to the beginning of the format string range being + // parsed. + constexpr iterator begin() const FMT_NOEXCEPT { return format_str_.begin(); } + + // Returns an iterator past the end of the format string range being parsed. + constexpr iterator end() const FMT_NOEXCEPT { return format_str_.end(); } + + // Advances the begin iterator to ``it``. + constexpr void advance_to(iterator it) { + format_str_.remove_prefix(it - begin()); + } + + // Returns the next argument index. + constexpr unsigned next_arg_id(); + + constexpr void check_arg_id(unsigned) { check_no_auto_index(); } + void check_arg_id(basic_string_view) {} + + constexpr void on_error(const char *message) { + ErrorHandler::on_error(message); + } + + constexpr ErrorHandler error_handler() const { return *this; } +}; + +using parse_context = basic_parse_context; +using wparse_context = basic_parse_context; + +namespace internal { +template +constexpr basic_arg make_arg(const T &value) { + basic_arg arg; + arg.type_ = get_type(); + arg.value_ = value; + return arg; +} + +template +inline typename std::enable_if>::type + make_arg(const T& value) { + return value; +} + +template +inline typename std::enable_if>::type + make_arg(const T& value) { + return make_arg(value); +} + +template +class arg_map { + private: + typedef typename Context::char_type Char; + typedef std::vector< + std::pair, basic_arg > > MapType; + typedef typename MapType::value_type Pair; + + MapType map_; + + public: + void init(const basic_format_args &args); + + const basic_arg + *find(const fmt::basic_string_view &name) const { + // The list is unsorted, so just return the first matching name. + for (typename MapType::const_iterator it = map_.begin(), end = map_.end(); + it != end; ++it) { + if (it->first == name) + return &it->second; + } + return 0; + } +}; + +template +class context_base : public basic_parse_context{ + private: + basic_format_args args_; + + protected: + typedef basic_arg format_arg; + + context_base(basic_string_view format_str, + basic_format_args args) + : basic_parse_context(format_str), args_(args) {} + ~context_base() {} + + basic_format_args args() const { return args_; } + + // Returns the argument with specified index. + format_arg do_get_arg(unsigned arg_id) { + format_arg arg = args_[arg_id]; + if (!arg) + this->on_error("argument index out of range"); + return arg; + } + + // Checks if manual indexing is used and returns the argument with + // specified index. + format_arg get_arg(unsigned arg_id) { + return this->check_no_auto_index() ? + this->do_get_arg(arg_id) : format_arg(); + } + + public: + basic_parse_context &parse_context() { return *this; } +}; +} // namespace internal + +template +class basic_context : + public internal::context_base> { + public: + /** The character type for the output. */ + using char_type = Char; + + template + using formatter_type = formatter; + + private: + internal::arg_map> map_; + + FMT_DISALLOW_COPY_AND_ASSIGN(basic_context); + + typedef internal::context_base> Base; + + typedef typename Base::format_arg format_arg; + using Base::get_arg; + + public: + /** + \rst + Constructs a ``basic_context`` object. References to the arguments are + 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) {} + + 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); +}; + +template +class arg_store { + private: + 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; + + typedef typename Context::char_type char_type; + + typedef typename std::conditional, basic_arg>::type value_type; + + // If the arguments are not packed, add one more element to mark the end. + typedef std::array Array; + Array data_; + + public: + static const uint64_t TYPES = IS_PACKED ? + internal::get_types() : -static_cast(NUM_ARGS); + + arg_store(const Args &... args) + : data_(Array{{internal::make_arg(args)...}}) {} + + const value_type *data() const { return data_.data(); } +}; + +template +inline arg_store make_args(const Args & ... args) { + return arg_store(args...); +} + +template +inline arg_store make_args(const Args & ... args) { + return arg_store(args...); +} + +/** Formatting arguments. */ +template +class basic_format_args { + public: + 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. + uint64_t types_; + union { + // 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 + // data is actually copied to stack. It saves ~10% on the bloat test. + const internal::value *values_; + const format_arg *args_; + }; + + typename internal::type type(unsigned index) const { + unsigned shift = index * 4; + uint64_t mask = 0xf; + return static_cast( + (types_ & (mask << shift)) >> shift); + } + + friend class internal::arg_map; + + void set_data(const internal::value *values) { values_ = values; } + void set_data(const format_arg *args) { args_ = args; } + + format_arg get(size_type index) const { + int64_t signed_types = static_cast(types_); + if (signed_types < 0) { + uint64_t num_args = -signed_types; + return index < num_args ? args_[index] : format_arg(); + } + format_arg arg; + if (index > internal::MAX_PACKED_ARGS) + return arg; + arg.type_ = type(index); + if (arg.type_ == internal::NONE) + return arg; + internal::value &val = arg.value_; + val = values_[index]; + return arg; + } + + public: + basic_format_args() : types_(0) {} + + template + basic_format_args(const arg_store &store) + : types_(store.TYPES) { + set_data(store.data()); + } + + /** Returns the argument at specified index. */ + format_arg operator[](size_type index) const { + format_arg arg = get(index); + return arg.type_ == internal::NAMED_ARG ? + *static_cast(arg.value_.pointer) : arg; + } +}; + +typedef basic_format_args format_args; +typedef basic_format_args wformat_args; + +enum Color { BLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE }; + +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); + */ +template +inline void print_colored(Color c, string_view format_str, + const Args & ... args) { + vprint_colored(c, format_str, make_args(args...)); +} + +void vformat_to(buffer &buf, string_view format_str, format_args args); +void vformat_to(wbuffer &buf, wstring_view format_str, wformat_args args); + +template +inline void format_to(buffer &buf, string_view format_str, + const Args & ... args) { + vformat_to(buf, format_str, make_args(args...)); +} + +template +inline void format_to(wbuffer &buf, wstring_view format_str, + const Args & ... args) { + vformat_to(buf, format_str, make_args(args...)); +} + +std::string vformat(string_view format_str, format_args args); +std::wstring vformat(wstring_view format_str, wformat_args args); + +/** + \rst + Formats arguments and returns the result as a string. + + **Example**:: + + std::string message = 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...)); +} +template +inline std::wstring format(wstring_view format_str, const Args & ... args) { + return vformat(format_str, make_args(args...)); +} + +FMT_API void vprint(std::FILE *f, string_view format_str, format_args args); + +/** + \rst + Prints formatted data to the file *f*. + + **Example**:: + + 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...)); +} + +FMT_API void vprint(string_view format_str, format_args args); + +/** + \rst + Prints formatted data to ``stdout``. + + **Example**:: + + 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...)); +} +} // namespace fmt + +#endif // FMT_CORE_H_ diff --git a/include/fmt/format.cc b/include/fmt/format.cc index 272c0eae..ad05af2c 100644 --- a/include/fmt/format.cc +++ b/include/fmt/format.cc @@ -405,6 +405,10 @@ void basic_fixed_buffer::grow(std::size_t) { FMT_THROW(std::runtime_error("buffer overflow")); } +void internal::error_handler::on_error(const char *message) { + FMT_THROW(format_error(message)); +} + FMT_FUNC void report_system_error( int error_code, fmt::string_view message) FMT_NOEXCEPT { // 'fmt::' is for bcc32. diff --git a/include/fmt/format.h b/include/fmt/format.h index ae591704..41ffffb0 100644 --- a/include/fmt/format.h +++ b/include/fmt/format.h @@ -28,20 +28,16 @@ #ifndef FMT_FORMAT_H_ #define FMT_FORMAT_H_ -#include #include #include -#include #include #include #include #include -#include -#include -#include -#include #include +#include "fmt/core.h" + #ifdef _SECURE_SCL # define FMT_SECURE_SCL _SECURE_SCL #else @@ -52,19 +48,7 @@ # include #endif -#if !defined(FMT_HEADER_ONLY) && defined(_WIN32) -# ifdef FMT_EXPORT -# define FMT_API __declspec(dllexport) -# elif defined(FMT_SHARED) -# define FMT_API __declspec(dllimport) -# endif -#endif -#ifndef FMT_API -# define FMT_API -#endif - #ifdef __GNUC__ -# define FMT_GCC_VERSION (__GNUC__ * 100 + __GNUC_MINOR__) # if FMT_GCC_VERSION >= 406 # pragma GCC diagnostic push @@ -99,30 +83,20 @@ # define FMT_GNUC_LIBSTD_VERSION (__GNUC_LIBSTD__ * 100 + __GNUC_LIBSTD_MINOR__) #endif -#ifdef _MSC_VER -# define FMT_MSC_VER _MSC_VER -#else -# define FMT_MSC_VER 0 -#endif - -#ifdef __has_feature -# define FMT_HAS_FEATURE(x) __has_feature(x) -#else -# define FMT_HAS_FEATURE(x) 0 -#endif - -#ifdef __has_builtin -# define FMT_HAS_BUILTIN(x) __has_builtin(x) -#else -# define FMT_HAS_BUILTIN(x) 0 -#endif - #ifdef __has_cpp_attribute # define FMT_HAS_CPP_ATTRIBUTE(x) __has_cpp_attribute(x) #else # define FMT_HAS_CPP_ATTRIBUTE(x) 0 #endif +#ifndef FMT_THROW +# if FMT_EXCEPTIONS +# define FMT_THROW(x) throw x +# else +# define FMT_THROW(x) assert(false) +# endif +#endif + // Use the compiler's attribute noreturn. #if defined(__MINGW32__) || defined(__MINGW64__) # define FMT_NORETURN __attribute__((noreturn)) @@ -132,41 +106,6 @@ # define FMT_NORETURN #endif -// Check if exceptions are disabled. -#if defined(__GNUC__) && !defined(__EXCEPTIONS) -# define FMT_EXCEPTIONS 0 -#endif -#if FMT_MSC_VER && !_HAS_EXCEPTIONS -# define FMT_EXCEPTIONS 0 -#endif -#ifndef FMT_EXCEPTIONS -# define FMT_EXCEPTIONS 1 -#endif - -// Define FMT_USE_NOEXCEPT to make fmt use noexcept (C++11 feature). -#ifndef FMT_USE_NOEXCEPT -# define FMT_USE_NOEXCEPT 0 -#endif - -#ifndef FMT_NOEXCEPT -# if FMT_EXCEPTIONS -# if FMT_USE_NOEXCEPT || FMT_HAS_FEATURE(cxx_noexcept) || \ - FMT_GCC_VERSION >= 408 || FMT_MSC_VER >= 1900 -# define FMT_NOEXCEPT noexcept -# else -# define FMT_NOEXCEPT throw() -# endif -# else -# define FMT_NOEXCEPT -# endif -#endif - -// 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 - #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 @@ -188,10 +127,6 @@ # define FMT_UDL_TEMPLATE 0 #endif -#ifndef FMT_ASSERT -# define FMT_ASSERT(condition, message) assert((condition) && message) -#endif - #if FMT_GCC_VERSION >= 400 || FMT_HAS_BUILTIN(__builtin_clz) # define FMT_BUILTIN_CLZ(n) __builtin_clz(n) #endif @@ -267,11 +202,6 @@ inline dummy_int isinf(...) { return dummy_int(); } inline dummy_int _finite(...) { return dummy_int(); } inline dummy_int isnan(...) { return dummy_int(); } inline dummy_int _isnan(...) { return dummy_int(); } - -// A helper function to suppress bogus "conditional expression is constant" -// warnings. -template -inline T const_check(T value) { return value; } } } // namespace fmt @@ -328,113 +258,6 @@ namespace fmt { template class basic_writer; -template -class basic_arg; - -template -class basic_context; - -typedef basic_context context; -typedef basic_context wcontext; - -// 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 - subset of the API. - \endrst - */ -template -class basic_string_view { - private: - const Char *data_; - std::size_t size_; - - public: - using char_type = Char; - using iterator = const Char *; - - constexpr basic_string_view() noexcept : data_(0), size_(0) {} - - /** Constructs a string reference object from a C string and a size. */ - constexpr basic_string_view(const Char *s, std::size_t size) noexcept - : data_(s), size_(size) {} - - /** - \rst - Constructs a string reference object from a C string computing - the size with ``std::char_traits::length``. - \endrst - */ - basic_string_view(const Char *s) - : data_(s), size_(std::char_traits::length(s)) {} - - /** - \rst - Constructs a string reference from an ``std::string`` object. - \endrst - */ - constexpr basic_string_view(const std::basic_string &s) noexcept - : data_(s.c_str()), size_(s.size()) {} - - /** - \rst - Converts a string reference to an ``std::string`` object. - \endrst - */ - std::basic_string to_string() const { - return std::basic_string(data_, size_); - } - - /** Returns a pointer to the string data. */ - const Char *data() const { return data_; } - - /** Returns the string size. */ - constexpr std::size_t size() const { return size_; } - - constexpr iterator begin() const { return data_; } - constexpr iterator end() const { return data_ + size_; } - - constexpr void remove_prefix(size_t n) { - data_ += n; - size_ -= n; - } - - // Lexicographically compare this string reference to other. - int compare(basic_string_view other) const { - std::size_t size = size_ < other.size_ ? size_ : other.size_; - int result = std::char_traits::compare(data_, other.data_, size); - if (result == 0) - result = size_ == other.size_ ? 0 : (size_ < other.size_ ? -1 : 1); - return result; - } - - friend bool operator==(basic_string_view lhs, basic_string_view rhs) { - return lhs.compare(rhs) == 0; - } - friend bool operator!=(basic_string_view lhs, basic_string_view rhs) { - return lhs.compare(rhs) != 0; - } - friend bool operator<(basic_string_view lhs, basic_string_view rhs) { - return lhs.compare(rhs) < 0; - } - friend bool operator<=(basic_string_view lhs, basic_string_view rhs) { - return lhs.compare(rhs) <= 0; - } - friend bool operator>(basic_string_view lhs, basic_string_view rhs) { - return lhs.compare(rhs) > 0; - } - friend bool operator>=(basic_string_view lhs, basic_string_view rhs) { - return lhs.compare(rhs) >= 0; - } -}; - -typedef basic_string_view string_view; -typedef basic_string_view wstring_view; - /** A formatting error such as invalid format string. */ class format_error : public std::runtime_error { public: @@ -470,24 +293,6 @@ inline stdext::checked_array_iterator make_ptr(T *ptr, std::size_t size) { template inline T *make_ptr(T *ptr, std::size_t) { return ptr; } #endif - -#ifndef FMT_THROW -# if FMT_EXCEPTIONS -# define FMT_THROW(x) throw x -# else -# define FMT_THROW(x) assert(false) -# endif -#endif - -struct error_handler { - constexpr error_handler() {} - constexpr error_handler(const error_handler &) {} - - // This function is intentionally not constexpr to give a compile-time error. - void on_error(const char *message) { - FMT_THROW(format_error(message)); - } -}; } // namespace internal // A wrapper around std::locale used to reduce compile times since @@ -572,9 +377,6 @@ class basic_buffer { virtual fmt::locale locale() const; }; -typedef basic_buffer buffer; -typedef basic_buffer wbuffer; - template template void basic_buffer::append(const U *begin, const U *end) { @@ -1067,357 +869,10 @@ FMT_API void format_windows_error(fmt::buffer &out, int error_code, template struct null {}; - -typedef char yes[1]; -typedef char no[2]; - -yes &convert(unsigned long long); -no &convert(...); - -template -struct convert_to_int_impl { - enum { value = ENABLE_CONVERSION }; -}; - -template -struct convert_to_int_impl2 { - enum { value = false }; -}; - -template -struct convert_to_int_impl2 { - enum { - // Don't convert numeric types. - value = convert_to_int_impl< - T, !std::numeric_limits::is_specialized>::value - }; -}; - -template -struct convert_to_int { - enum { - enable_conversion = sizeof(convert(std::declval())) == sizeof(yes) - }; - enum { value = convert_to_int_impl2::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); - -enum type { - NONE, NAMED_ARG, - // Integer types should go first, - INT, UINT, LONG_LONG, ULONG_LONG, BOOL, CHAR, LAST_INTEGER_TYPE = CHAR, - // followed by floating-point types. - DOUBLE, LONG_DOUBLE, LAST_NUMERIC_TYPE = LONG_DOUBLE, - CSTRING, STRING, POINTER, CUSTOM -}; - -constexpr bool is_integral(type t) { - FMT_ASSERT(t != internal::NAMED_ARG, "invalid argument type"); - return t > internal::NONE && t <= internal::LAST_INTEGER_TYPE; -} - -constexpr bool is_numeric(type t) { - FMT_ASSERT(t != internal::NAMED_ARG, "invalid argument type"); - return t > internal::NONE && t <= internal::LAST_NUMERIC_TYPE; -} - -template -struct string_value { - const Char *value; - std::size_t size; -}; - -template -struct custom_value { - typedef void (*format_func)( - basic_buffer &buffer, - const void *arg, Context &ctx); - - const void *value; - format_func format; -}; - -template -struct named_arg; - -template -struct is_named_arg : std::false_type {}; - -template -struct is_named_arg> : std::true_type {}; - -template -constexpr type get_type() { - return std::is_reference::value || std::is_array::value ? - get_type::type>() : - (is_named_arg::value ? - NAMED_ARG : (convert_to_int::value ? INT : CUSTOM)); -} - -template <> constexpr type get_type() { return BOOL; } -template <> constexpr type get_type() { return INT; } -template <> constexpr type get_type() { return UINT; } -template <> constexpr type get_type() { return INT; } -template <> constexpr type get_type() { return UINT; } -template <> constexpr type get_type() { - return sizeof(long) == sizeof(int) ? INT : LONG_LONG; -} -template <> constexpr type get_type() { - return sizeof(unsigned long) == sizeof(unsigned) ? UINT : ULONG_LONG; -} -template <> constexpr type get_type() { return LONG_LONG; } -template <> constexpr type get_type() { return ULONG_LONG; } -template <> constexpr type get_type() { return DOUBLE; } -template <> constexpr type get_type() { return DOUBLE; } -template <> constexpr type get_type() { return LONG_DOUBLE; } -template <> constexpr type get_type() { return INT; } -template <> constexpr type get_type() { return UINT; } -template <> constexpr type get_type() { return CHAR; } - -#if !defined(_MSC_VER) || defined(_NATIVE_WCHAR_T_DEFINED) -template <> constexpr type get_type() { return CHAR; } -#endif - -template <> constexpr type get_type() { return CSTRING; } -template <> constexpr type get_type() { return CSTRING; } -template <> constexpr type get_type() { return CSTRING; } -template <> constexpr type get_type() { return CSTRING; } -template <> constexpr type get_type() { return CSTRING; } -template <> constexpr type get_type() { return CSTRING; } -template <> constexpr type get_type() { return STRING; } -template <> constexpr type get_type() { return STRING; } -template <> constexpr type get_type() { return CSTRING; } -template <> constexpr type get_type() { return CSTRING; } -template <> constexpr type get_type() { return STRING; } -template <> constexpr type get_type() { return STRING; } -template <> constexpr type get_type() { return POINTER; } -template <> constexpr type get_type() { return POINTER; } -template <> constexpr type get_type() { return POINTER; } - -// Formatting of wide characters and strings into a narrow output is disallowed: -// fmt::format("{}", L"test"); // error -// To fix this, use a wide format string: -// fmt::format(L"{}", L"test"); -template -inline void require_wchar() { - static_assert( - std::is_same::value, - "formatting of wide characters into a narrow output is disallowed"); -} - -template -inline const T *as_const(T *p) { return p; } - -// A formatting argument value. -template -class value { - public: - using char_type = typename Context::char_type; - - union { - int int_value; - unsigned uint_value; - long long long_long_value; - unsigned long long ulong_long_value; - double double_value; - long double long_double_value; - const void *pointer; - string_value string; - string_value sstring; - string_value ustring; - custom_value custom; - }; - - constexpr value() : int_value(0) {} - value(bool val) { set(int_value, val); } - value(short val) { set(int_value, val); } - value(unsigned short val) { set(uint_value, val); } - constexpr value(int val) : int_value(val) {} - value(unsigned val) { set(uint_value, val); } - - value(long val) { - // 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. - if (const_check(sizeof(val) == sizeof(int))) - int_value = static_cast(val); - else - long_long_value = val; - } - - value(unsigned long val) { - if (const_check(sizeof(val) == sizeof(unsigned))) - uint_value = static_cast(val); - else - ulong_long_value = val; - } - - value(long long val) { set(long_long_value, val); } - value(unsigned long long val) { set(ulong_long_value, val); } - value(float val) { set(double_value, val); } - value(double val) { set(double_value, val); } - value(long double val) { set(long_double_value, val); } - value(signed char val) { set(int_value, val); } - value(unsigned char val) { set(uint_value, val); } - value(char val) { set(int_value, val); } - -#if !defined(_MSC_VER) || defined(_NATIVE_WCHAR_T_DEFINED) - value(wchar_t value) { - require_wchar(); - set(int_value, value); - } -#endif - - // Formatting of wide strings into a narrow buffer and multibyte strings - // into a wide buffer is disallowed (https://github.com/fmtlib/fmt/pull/606). - value(char_type *s) { set(string.value, s); } - value(const char_type *s) { set(string.value, s); } - value(signed char *s) { set_cstring(sstring.value, s); } - value(const signed char *s) { set_cstring(sstring.value, s); } - value(unsigned char *s) { set_cstring(ustring.value, s); } - value(const unsigned char *s) { set_cstring(ustring.value, s); } - value(basic_string_view s) { set_string(s); } - value(const std::basic_string &s) { set_string(s); } - - // 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 - value(const T *p) { - static_assert(std::is_same::value, - "formatting of non-void pointers is disallowed"); - set(pointer, p); - } - - template - value(T *p) : value(as_const(p)) {} - - value(std::nullptr_t) { pointer = nullptr; } - - template - value(const T &value, - typename std::enable_if::value, int>::type = 0) { - static_assert(get_type() == INT, "invalid type"); - int_value = value; - } - - template - value(const T &value, - typename std::enable_if::value, int>::type = 0) { - static_assert(get_type() == CUSTOM, "invalid type"); - custom.value = &value; - custom.format = &format_custom_arg; - } - - // Additional template param `Char` is needed here because get_type always - // uses char. - template - value(const named_arg &value) { - static_assert( - get_type &>() == NAMED_ARG, "invalid type"); - pointer = &value; - } - - private: - template - constexpr void set(T &field, const U &value) { - static_assert(get_type() == TYPE, "invalid type"); - field = value; - } - - template - void set_string(const T &value) { - static_assert(get_type() == STRING, "invalid type"); - string.value = value.data(); - string.size = value.size(); - } - - template - constexpr void set_cstring(T &field, const U *str) { - static_assert(std::is_same::value, - "incompatible string types"); - set(field, str); - } - - // 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) { - // 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); - } -}; - -template -class arg_map; - -template -constexpr basic_arg make_arg(const T &value); } // namespace internal struct monostate {}; -template -class basic_format_args; - -// A formatting argument. It is a trivially copyable/constructible type to -// allow storage in basic_memory_buffer. -template -class basic_arg { - private: - internal::value value_; - internal::type type_; - - template - friend constexpr basic_arg internal::make_arg(const T &value); - - template - friend constexpr typename std::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; - - public: - class handle { - public: - explicit handle(internal::custom_value custom) - : custom_(custom) {} - - void format(basic_buffer &buf, Context &ctx) { - custom_.format(buf, custom_.value, ctx); - } - - private: - internal::custom_value custom_; - }; - - constexpr basic_arg() : type_(internal::NONE) {} - - explicit operator bool() const noexcept { return type_ != internal::NONE; } - - internal::type type() const { return type_; } - - bool is_integral() const { return internal::is_integral(type_); } - bool is_numeric() const { return internal::is_numeric(type_); } - bool is_pointer() const { return type_ == internal::POINTER; } -}; - /** \rst Visits an argument dispatching to the appropriate visit method based on @@ -1466,20 +921,6 @@ constexpr typename std::result_of::type namespace internal { -template -constexpr basic_arg make_arg(const T &value) { - basic_arg arg; - arg.type_ = get_type(); - arg.value_ = value; - return arg; -} - -#if FMT_GCC_VERSION >= 407 -# define FMT_UNUSED __attribute__((unused)) -#else -# define FMT_UNUSED -#endif - template struct named_arg : basic_arg { typedef typename Context::char_type Char; @@ -1490,138 +931,8 @@ struct named_arg : basic_arg { named_arg(basic_string_view argname, const T &value) : basic_arg(make_arg(value)), name(argname) {} }; - -template -constexpr uint64_t get_types() { - return get_type() | (get_types() << 4); -} - -template <> -constexpr uint64_t get_types() { return 0; } - -// Maximum number of arguments with packed types. -enum { MAX_PACKED_ARGS = 15 }; - -template -inline typename std::enable_if>::type - make_arg(const T& value) { - return value; -} - -template -inline typename std::enable_if>::type - make_arg(const T& value) { - return make_arg(value); -} } // namespace internal -template -class arg_store { - private: - 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; - - typedef typename Context::char_type char_type; - - typedef typename std::conditional, basic_arg>::type value_type; - - // If the arguments are not packed, add one more element to mark the end. - typedef std::array Array; - Array data_; - - public: - static const uint64_t TYPES = IS_PACKED ? - internal::get_types() : -static_cast(NUM_ARGS); - - arg_store(const Args &... args) - : data_(Array{{internal::make_arg(args)...}}) {} - - const value_type *data() const { return data_.data(); } -}; - -template -inline arg_store make_args(const Args & ... args) { - return arg_store(args...); -} - -template -inline arg_store make_args(const Args & ... args) { - return arg_store(args...); -} - -/** Formatting arguments. */ -template -class basic_format_args { - public: - 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. - uint64_t types_; - union { - // 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 - // data is actually copied to stack. It saves ~10% on the bloat test. - const internal::value *values_; - const format_arg *args_; - }; - - typename internal::type type(unsigned index) const { - unsigned shift = index * 4; - uint64_t mask = 0xf; - return static_cast( - (types_ & (mask << shift)) >> shift); - } - - friend class internal::arg_map; - - void set_data(const internal::value *values) { values_ = values; } - void set_data(const format_arg *args) { args_ = args; } - - format_arg get(size_type index) const { - int64_t signed_types = static_cast(types_); - if (signed_types < 0) { - uint64_t num_args = -signed_types; - return index < num_args ? args_[index] : format_arg(); - } - format_arg arg; - if (index > internal::MAX_PACKED_ARGS) - return arg; - arg.type_ = type(index); - if (arg.type_ == internal::NONE) - return arg; - internal::value &val = arg.value_; - val = values_[index]; - return arg; - } - - public: - basic_format_args() : types_(0) {} - - template - basic_format_args(const arg_store &store) - : types_(store.TYPES) { - set_data(store.data()); - } - - /** Returns the argument at specified index. */ - format_arg operator[](size_type index) const { - format_arg arg = get(index); - return arg.type_ == internal::NAMED_ARG ? - *static_cast(arg.value_.pointer) : arg; - } -}; - -typedef basic_format_args format_args; -typedef basic_format_args wformat_args; - enum alignment { ALIGN_DEFAULT, ALIGN_LEFT, ALIGN_RIGHT, ALIGN_CENTER, ALIGN_NUMERIC }; @@ -1725,64 +1036,13 @@ class basic_format_specs : public align_spec { typedef basic_format_specs format_specs; -// Parsing context consisting of a format string range being parsed and an -// argument counter for automatic indexing. -template -class basic_parse_context : private ErrorHandler { - private: - basic_string_view format_str_; - int next_arg_id_; - - protected: - constexpr bool check_no_auto_index() { - if (next_arg_id_ > 0) { - on_error("cannot switch from automatic to manual argument indexing"); - return false; - } - next_arg_id_ = -1; - return true; - } - - public: - using char_type = Char; - using iterator = typename basic_string_view::iterator; - - explicit constexpr basic_parse_context( - basic_string_view format_str, ErrorHandler eh = ErrorHandler()) - : ErrorHandler(eh), format_str_(format_str), next_arg_id_(0) {} - - // Returns an iterator to the beginning of the format string range being - // parsed. - constexpr iterator begin() const FMT_NOEXCEPT { return format_str_.begin(); } - - // Returns an iterator past the end of the format string range being parsed. - constexpr iterator end() const FMT_NOEXCEPT { return format_str_.end(); } - - // Advances the begin iterator to ``it``. - constexpr void advance_to(iterator it) { - format_str_.remove_prefix(it - begin()); - } - - // Returns the next argument index. - constexpr unsigned next_arg_id() { - if (next_arg_id_ >= 0) - return internal::to_unsigned(next_arg_id_++); - on_error("cannot switch from manual to automatic argument indexing"); - return 0; - } - - constexpr void check_arg_id(unsigned) { check_no_auto_index(); } - void check_arg_id(basic_string_view) {} - - constexpr void on_error(const char *message) { - ErrorHandler::on_error(message); - } - - constexpr ErrorHandler error_handler() const { return *this; } -}; - -using parse_context = basic_parse_context; -using wparse_context = basic_parse_context; +template +constexpr unsigned basic_parse_context::next_arg_id() { + if (next_arg_id_ >= 0) + return internal::to_unsigned(next_arg_id_++); + on_error("cannot switch from manual to automatic argument indexing"); + return 0; +} namespace internal { @@ -1919,31 +1179,6 @@ class cstring_type_checker : public ErrorHandler { constexpr void on_pointer() {} }; -template -class arg_map { - private: - typedef typename Context::char_type Char; - typedef std::vector< - std::pair, basic_arg > > MapType; - typedef typename MapType::value_type Pair; - - MapType map_; - - public: - void init(const basic_format_args &args); - - const basic_arg - *find(const fmt::basic_string_view &name) const { - // The list is unsorted, so just return the first matching name. - for (typename MapType::const_iterator it = map_.begin(), end = map_.end(); - it != end; ++it) { - if (it->first == name) - return &it->second; - } - return 0; - } -}; - template void arg_map::init(const basic_format_args &args) { if (!map_.empty()) @@ -2105,40 +1340,6 @@ class arg_formatter_base { } }; -template -class context_base : public basic_parse_context{ - private: - basic_format_args args_; - - protected: - typedef basic_arg format_arg; - - context_base(basic_string_view format_str, - basic_format_args args) - : basic_parse_context(format_str), args_(args) {} - ~context_base() {} - - basic_format_args args() const { return args_; } - - // Returns the argument with specified index. - format_arg do_get_arg(unsigned arg_id) { - format_arg arg = args_[arg_id]; - if (!arg) - this->on_error("argument index out of range"); - return arg; - } - - // Checks if manual indexing is used and returns the argument with - // specified index. - format_arg get_arg(unsigned arg_id) { - return this->check_no_auto_index() ? - this->do_get_arg(arg_id) : format_arg(); - } - - public: - basic_parse_context &parse_context() { return *this; } -}; - struct format_string {}; template @@ -2822,45 +2023,6 @@ class arg_formatter : public internal::arg_formatter_base { } }; -template -class basic_context : - public internal::context_base> { - public: - /** The character type for the output. */ - using char_type = Char; - - template - using formatter_type = formatter; - - private: - internal::arg_map> map_; - - FMT_DISALLOW_COPY_AND_ASSIGN(basic_context); - - typedef internal::context_base> Base; - - typedef typename Base::format_arg format_arg; - using Base::get_arg; - - public: - /** - \rst - Constructs a ``basic_context`` object. References to the arguments are - 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) {} - - 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); -}; - /** An error returned by an operating system or a language runtime, for example a file opening error. @@ -3602,122 +2764,6 @@ FMT_API void report_windows_error(int error_code, #endif -enum Color { BLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE }; - -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); - */ -template -inline void print_colored(Color c, string_view format_str, - const Args & ... args) { - vprint_colored(c, format_str, make_args(args...)); -} - -template -void vformat_to(basic_buffer &buffer, basic_string_view format_str, - basic_format_args args); - -inline void vformat_to(buffer &buf, string_view format_str, format_args args) { - vformat_to>(buf, format_str, args); -} - -inline void vformat_to(wbuffer &buf, wstring_view format_str, - wformat_args args) { - vformat_to>(buf, format_str, args); -} - -template -inline void format_to(buffer &buf, string_view format_str, - const Args & ... args) { - vformat_to(buf, format_str, make_args(args...)); -} - -template -inline void format_to(wbuffer &buf, wstring_view format_str, - const Args & ... args) { - vformat_to(buf, format_str, make_args(args...)); -} - -inline std::string vformat(string_view format_str, format_args args) { - memory_buffer buffer; - vformat_to(buffer, format_str, args); - return to_string(buffer); -} - -/** - \rst - Formats arguments and returns the result as a string. - - **Example**:: - - std::string message = 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...)); -} - -template -inline typename std::enable_if< - std::is_base_of::value, std::string>::type - format(String format_str, const Args & ... args) { - constexpr bool invalid_format = - internal::check_format_string( - string_view(format_str.value(), format_str.size())); - (void)invalid_format; - return vformat(format_str.value(), make_args(args...)); -} - -inline std::wstring vformat(wstring_view format_str, wformat_args args) { - wmemory_buffer buffer; - vformat_to(buffer, format_str, args); - return to_string(buffer); -} - -template -inline std::wstring format(wstring_view format_str, const Args & ... args) { - return vformat(format_str, make_args(args...)); -} - -FMT_API void vprint(std::FILE *f, string_view format_str, format_args args); - -/** - \rst - Prints formatted data to the file *f*. - - **Example**:: - - 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...)); -} - -FMT_API void vprint(string_view format_str, format_args args); - -/** - \rst - Prints formatted data to ``stdout``. - - **Example**:: - - 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...)); -} - /** Fast integer formatter. */ @@ -4070,6 +3116,66 @@ void vformat_to(basic_buffer &buffer, basic_string_view format_str, // auto s = format("{}", ptr(p)); template inline const void *ptr(const T *p) { return p; } + +class fill_spec_factory { + public: + constexpr fill_spec_factory() {} + + template + fill_spec operator=(Char value) const { + return fill_spec(value); + } +}; + +template +class format_spec_factory { + public: + constexpr format_spec_factory() {} + + FormatSpec operator=(typename FormatSpec::value_type value) const { + return FormatSpec(value); + } +}; + +constexpr fill_spec_factory fill; +constexpr format_spec_factory width; +constexpr format_spec_factory type; + +template +void vformat_to(basic_buffer &buffer, basic_string_view format_str, + basic_format_args args); + +inline void vformat_to(buffer &buf, string_view format_str, format_args args) { + vformat_to>(buf, format_str, args); +} + +inline void vformat_to(wbuffer &buf, wstring_view format_str, + wformat_args args) { + vformat_to>(buf, format_str, args); +} + +inline std::string vformat(string_view format_str, format_args args) { + memory_buffer buffer; + vformat_to(buffer, format_str, args); + return to_string(buffer); +} + +inline std::wstring vformat(wstring_view format_str, wformat_args args) { + wmemory_buffer buffer; + vformat_to(buffer, format_str, args); + return to_string(buffer); +} + +template +inline typename std::enable_if< + std::is_base_of::value, std::string>::type + format(String format_str, const Args & ... args) { + constexpr bool invalid_format = + internal::check_format_string( + string_view(format_str.value(), format_str.size())); + (void)invalid_format; + return vformat(format_str.value(), make_args(args...)); +} } // namespace fmt #if FMT_USE_USER_DEFINED_LITERALS diff --git a/include/fmt/write.h b/include/fmt/write.h deleted file mode 100644 index c6aec5a7..00000000 --- a/include/fmt/write.h +++ /dev/null @@ -1,61 +0,0 @@ -/* - 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. - */ - -#ifndef FMT_WRITE_H_ -#define FMT_WRITE_H_ - -#include "fmt/format.h" - -namespace fmt { - -class fill_spec_factory { - public: - constexpr fill_spec_factory() {} - - template - fill_spec operator=(Char value) const { - return fill_spec(value); - } -}; - -template -class format_spec_factory { - public: - constexpr format_spec_factory() {} - - FormatSpec operator=(typename FormatSpec::value_type value) const { - return FormatSpec(value); - } -}; - -constexpr fill_spec_factory fill; -constexpr format_spec_factory width; -constexpr format_spec_factory type; - -} // namespace fmt - -#endif // FMT_WRITE_H_ diff --git a/test/assert-test.cc b/test/assert-test.cc index eef342ee..897740c0 100644 --- a/test/assert-test.cc +++ b/test/assert-test.cc @@ -25,7 +25,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#include "fmt/format.h" +#include "fmt/core.h" #include "gtest/gtest.h" #if GTEST_HAS_DEATH_TEST diff --git a/test/format-test.cc b/test/format-test.cc index 13258d44..5712b46f 100644 --- a/test/format-test.cc +++ b/test/format-test.cc @@ -42,8 +42,7 @@ // Test that the library compiles if None is defined to 0 as done by xlib.h. #define None 0 -#include "fmt/format.h" -#include "fmt/write.h" +#include "fmt/core.h" #include "util.h" #include "mock-allocator.h" diff --git a/test/gtest-extra.h b/test/gtest-extra.h index be6708c0..cb739575 100644 --- a/test/gtest-extra.h +++ b/test/gtest-extra.h @@ -31,7 +31,7 @@ #include #include -#include "fmt/format.h" +#include "fmt/core.h" #ifndef FMT_USE_FILE_DESCRIPTORS # define FMT_USE_FILE_DESCRIPTORS 0 diff --git a/test/header-only-test.cc b/test/header-only-test.cc index df242033..674dab99 100644 --- a/test/header-only-test.cc +++ b/test/header-only-test.cc @@ -1,3 +1,3 @@ // Header-only configuration test -#include "fmt/format.h" +#include "fmt/core.h" diff --git a/test/header-only-test2.cc b/test/header-only-test2.cc index 305c6415..ea90b604 100644 --- a/test/header-only-test2.cc +++ b/test/header-only-test2.cc @@ -1,3 +1,3 @@ // Additional translation unit for the header-only configuration test -#include "fmt/format.h" +#include "fmt/core.h" diff --git a/test/printf-test.cc b/test/printf-test.cc index 65020c03..288b0b28 100644 --- a/test/printf-test.cc +++ b/test/printf-test.cc @@ -29,8 +29,8 @@ #include #include +#include "fmt/core.h" #include "fmt/printf.h" -#include "fmt/format.h" #include "gtest-extra.h" #include "util.h" diff --git a/test/util-test.cc b/test/util-test.cc index eb41271e..9b36446e 100644 --- a/test/util-test.cc +++ b/test/util-test.cc @@ -48,7 +48,7 @@ # include #endif -#include "fmt/format.h" +#include "fmt/core.h" #undef min #undef max