From 8c99ec07386fb2fc386e339f661880ccad957135 Mon Sep 17 00:00:00 2001 From: stryku Date: Tue, 27 Nov 2018 11:52:00 +0100 Subject: [PATCH] Implemented fmt::prepare() Implementation of fmt::prepare() function and features around it. --- CMakeLists.txt | 2 +- include/fmt/chrono.h | 14 +- include/fmt/color.h | 15 +- include/fmt/core.h | 30 +- include/fmt/format.h | 252 +++--- include/fmt/prepare.h | 814 ++++++++++++++++++ test/CMakeLists.txt | 6 + test/common-format-test.cc | 1621 ++++++++++++++++++++++++++++++++++++ test/format-test.cc | 1063 +---------------------- test/ostream-test.cc | 2 +- test/prepare-test.cc | 643 ++++++++++++++ 11 files changed, 3301 insertions(+), 1161 deletions(-) create mode 100644 include/fmt/prepare.h create mode 100644 test/common-format-test.cc create mode 100644 test/prepare-test.cc diff --git a/CMakeLists.txt b/CMakeLists.txt index ce9dbf95..b6ff7251 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -140,7 +140,7 @@ endfunction() # Define the fmt library, its includes and the needed defines. add_headers(FMT_HEADERS chrono.h color.h core.h format.h format-inl.h locale.h - ostream.h printf.h time.h ranges.h) + ostream.h prepare.h printf.h time.h ranges.h) set(FMT_SOURCES src/format.cc) if (HAVE_OPEN) add_headers(FMT_HEADERS posix.h) diff --git a/include/fmt/chrono.h b/include/fmt/chrono.h index 209cdc25..593530a3 100644 --- a/include/fmt/chrono.h +++ b/include/fmt/chrono.h @@ -372,15 +372,15 @@ template struct formatter, Char> { private: align_spec spec; - internal::arg_ref width_ref; + typedef internal::arg_ref arg_ref_type; + arg_ref_type width_ref; mutable basic_string_view format_str; typedef std::chrono::duration duration; struct spec_handler { formatter &f; basic_parse_context &context; - - typedef internal::arg_ref arg_ref_type; + basic_string_view format_str; template FMT_CONSTEXPR arg_ref_type make_arg_ref(Id arg_id) { @@ -388,6 +388,12 @@ struct formatter, Char> { return arg_ref_type(arg_id); } + FMT_CONSTEXPR arg_ref_type make_arg_ref(basic_string_view arg_id) { + context.check_arg_id(arg_id); + const auto str_val = internal::string_view_metadata(format_str, arg_id); + return arg_ref_type(str_val); + } + FMT_CONSTEXPR arg_ref_type make_arg_ref(internal::auto_id) { return arg_ref_type(context.next_arg_id()); } @@ -410,7 +416,7 @@ struct formatter, Char> { -> decltype(ctx.begin()) { auto begin = ctx.begin(), end = ctx.end(); if (begin == end) return begin; - spec_handler handler{*this, ctx}; + spec_handler handler{*this, ctx, format_str}; begin = internal::parse_align(begin, end, handler); if (begin == end) return begin; begin = internal::parse_width(begin, end, handler); diff --git a/include/fmt/color.h b/include/fmt/color.h index f38455a4..08eafec2 100644 --- a/include/fmt/color.h +++ b/include/fmt/color.h @@ -221,14 +221,13 @@ enum class emphasis : uint8_t { // We use rgb as name because some editors will show it as color direct in the // editor. struct rgb { - FMT_CONSTEXPR_DECL rgb() : r(0), g(0), b(0) {} - FMT_CONSTEXPR_DECL rgb(uint8_t r_, uint8_t g_, uint8_t b_) - : r(r_), g(g_), b(b_) {} - FMT_CONSTEXPR_DECL rgb(uint32_t hex) - : r((hex >> 16) & 0xFF), g((hex >> 8) & 0xFF), b((hex) & 0xFF) {} - FMT_CONSTEXPR_DECL rgb(color hex) - : r((uint32_t(hex) >> 16) & 0xFF), g((uint32_t(hex) >> 8) & 0xFF), - b(uint32_t(hex) & 0xFF) {} + FMT_CONSTEXPR rgb() : r(0), g(0), b(0) {} + FMT_CONSTEXPR rgb(uint8_t r_, uint8_t g_, uint8_t b_) : r(r_), g(g_), b(b_) {} + FMT_CONSTEXPR rgb(uint32_t hex) + : r((hex >> 16) & 0xFF), g((hex >> 8) & 0xFF), b((hex)&0xFF) {} + FMT_CONSTEXPR rgb(color hex) + : r((uint32_t(hex) >> 16) & 0xFF), g((uint32_t(hex) >> 8) & 0xFF), + b(uint32_t(hex) & 0xFF) {} uint8_t r; uint8_t g; uint8_t b; diff --git a/include/fmt/core.h b/include/fmt/core.h index 85503b4a..f4d80b65 100644 --- a/include/fmt/core.h +++ b/include/fmt/core.h @@ -454,9 +454,11 @@ template inline basic_string_view to_string_view(basic_string_view s) { return s; } -template +template inline basic_string_view - to_string_view(const std::basic_string &s) { return s; } +to_string_view(const std::basic_string &s) { + return {s.data(), s.size()}; +} template inline basic_string_view to_string_view(const Char *s) { return s; } @@ -504,6 +506,8 @@ template struct convert_to_int: std::integral_constant< bool, !std::is_arithmetic::value && std::is_convertible::value> {}; +template struct basic_format_specs; + namespace internal { struct dummy_string_view { typedef void char_type; }; @@ -877,16 +881,18 @@ FMT_CONSTEXPR typename internal::result_of::type template class basic_parse_context : private ErrorHandler { private: - basic_string_view format_str_; - int next_arg_id_; + basic_string_view primary_format_str_; + basic_string_view format_str_; + int next_arg_id_; public: 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()) - : ErrorHandler(eh), format_str_(format_str), next_arg_id_(0) {} + explicit FMT_CONSTEXPR basic_parse_context(basic_string_view format_str, + ErrorHandler eh = ErrorHandler()) + : ErrorHandler(eh), primary_format_str_(format_str), + format_str_(format_str), next_arg_id_(0) {} // Returns an iterator to the beginning of the format string range being // parsed. @@ -913,13 +919,17 @@ class basic_parse_context : private ErrorHandler { next_arg_id_ = -1; return true; } - void check_arg_id(basic_string_view) {} + + FMT_CONSTEXPR void check_arg_id(basic_string_view) {} FMT_CONSTEXPR void on_error(const char *message) { ErrorHandler::on_error(message); } FMT_CONSTEXPR ErrorHandler error_handler() const { return *this; } + FMT_CONSTEXPR basic_string_view primary_format() const { + return primary_format_str_; + } }; typedef basic_parse_context format_parse_context; @@ -1038,6 +1048,10 @@ class context_base { void advance_to(iterator it) { out_ = it; } locale_ref locale() { return loc_; } + + basic_string_view primary_format() const { + return parse_context_.primary_format(); + } }; template diff --git a/include/fmt/format.h b/include/fmt/format.h index 1bb24a52..20bc65c1 100644 --- a/include/fmt/format.h +++ b/include/fmt/format.h @@ -81,6 +81,16 @@ # define FMT_SECURE_SCL 0 #endif +// Check whether we can use unrestricted unions (e.g. unions with non-PODs +// members). If no, use struct. +#ifndef FMT_UNRESTRICTED_UNION +#if (FMT_MSC_VER >= 1900 || FMT_GCC_VERSION >= 406 || FMT_CLANG_VERSION >= 303) +#define FMT_UNRESTRICTED_UNION union +#else +#define FMT_UNRESTRICTED_UNION struct +#endif +#endif + #if FMT_SECURE_SCL # include #endif @@ -1104,6 +1114,7 @@ struct core_format_specs { FMT_CONSTEXPR core_format_specs() : precision(-1), flags(0), type(0) {} FMT_CONSTEXPR bool has(unsigned f) const { return (flags & f) != 0; } + FMT_CONSTEXPR bool has_precision() const { return precision != -1; } }; // Format specifiers. @@ -1585,68 +1596,79 @@ class specs_setter { basic_format_specs &specs_; }; +template class numeric_specs_checker { +public: + FMT_CONSTEXPR numeric_specs_checker(ErrorHandler &eh, internal::type arg_type) + : error_handler_(eh), arg_type_(arg_type) {} + FMT_CONSTEXPR void require_numeric_argument() { + if (!is_arithmetic(arg_type_)) { + error_handler_.on_error("format specifier requires numeric argument"); + } + } + FMT_CONSTEXPR void check_sign() { + require_numeric_argument(); + if (is_integral(arg_type_) && arg_type_ != int_type && + arg_type_ != long_long_type && arg_type_ != internal::char_type) { + error_handler_.on_error("format specifier requires signed argument"); + } + } + FMT_CONSTEXPR void check_precision() { + if (is_integral(arg_type_) || arg_type_ == internal::pointer_type) { + error_handler_.on_error("precision not allowed for this argument type"); + } + } + +private: + ErrorHandler &error_handler_; + internal::type arg_type_; +}; + // A format specifier handler that checks if specifiers are consistent with the // argument type. template class specs_checker : public Handler { public: - FMT_CONSTEXPR specs_checker(const Handler& handler, internal::type arg_type) - : Handler(handler), arg_type_(arg_type) {} + FMT_CONSTEXPR specs_checker(const Handler &handler, internal::type arg_type) + : Handler(handler), checker_(*this, arg_type) {} - FMT_CONSTEXPR specs_checker(const specs_checker &other) - : Handler(other), arg_type_(other.arg_type_) {} + FMT_CONSTEXPR specs_checker(const specs_checker &other) + : Handler(other), checker_(*this, other.arg_type_) {} - FMT_CONSTEXPR void on_align(alignment align) { - if (align == ALIGN_NUMERIC) - require_numeric_argument(); - Handler::on_align(align); + FMT_CONSTEXPR void on_align(alignment align) { + if (align == ALIGN_NUMERIC) + checker_.require_numeric_argument(); + Handler::on_align(align); } FMT_CONSTEXPR void on_plus() { - check_sign(); + checker_.check_sign(); Handler::on_plus(); } FMT_CONSTEXPR void on_minus() { - check_sign(); + checker_.check_sign(); Handler::on_minus(); } FMT_CONSTEXPR void on_space() { - check_sign(); + checker_.check_sign(); Handler::on_space(); } FMT_CONSTEXPR void on_hash() { - require_numeric_argument(); + checker_.require_numeric_argument(); Handler::on_hash(); } FMT_CONSTEXPR void on_zero() { - require_numeric_argument(); + checker_.require_numeric_argument(); Handler::on_zero(); } - FMT_CONSTEXPR void end_precision() { - if (is_integral(arg_type_) || arg_type_ == pointer_type) - this->on_error("precision not allowed for this argument type"); - } + FMT_CONSTEXPR void end_precision() { checker_.check_precision(); } - private: - FMT_CONSTEXPR void require_numeric_argument() { - if (!is_arithmetic(arg_type_)) - this->on_error("format specifier requires numeric argument"); - } - - FMT_CONSTEXPR void check_sign() { - require_numeric_argument(); - 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"); - } - } - - internal::type arg_type_; +private: + numeric_specs_checker checker_; }; template