From 9a777b9e1c610f86f137c7cb3ac18be6eab06cce 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 | 9 +- include/fmt/format.h | 195 +++++++---- include/fmt/prepare.h | 783 ++++++++++++++++++++++++++++++++++++++++++ test/CMakeLists.txt | 1 + test/format-test.cc | 18 +- test/ostream-test.cc | 2 +- test/prepare-test.cc | 643 ++++++++++++++++++++++++++++++++++ 10 files changed, 1586 insertions(+), 96 deletions(-) create mode 100644 include/fmt/prepare.h 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 dd146fd5..1926701f 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..857d5d13 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..d95e98a3 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; } @@ -913,7 +915,8 @@ 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); diff --git a/include/fmt/format.h b/include/fmt/format.h index 1bb24a52..3485f5f1 100644 --- a/include/fmt/format.h +++ b/include/fmt/format.h @@ -81,6 +81,15 @@ # define FMT_SECURE_SCL 0 #endif +// Check whether we can use unrestricted unions and use struct if not. +#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 +1113,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,70 +1595,83 @@ class specs_setter { basic_format_specs &specs_; }; -// A format specifier handler that checks if specifiers are consistent with the -// argument type. -template -class specs_checker : public Handler { +template +class numeric_specs_checker { public: - FMT_CONSTEXPR specs_checker(const Handler& handler, internal::type arg_type) - : Handler(handler), arg_type_(arg_type) {} + FMT_CONSTEXPR numeric_specs_checker(ErrorHandler &eh, internal::type arg_type) + : error_handler_(eh), arg_type_(arg_type) {} - FMT_CONSTEXPR specs_checker(const specs_checker &other) - : Handler(other), arg_type_(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_plus() { - check_sign(); - Handler::on_plus(); - } - - FMT_CONSTEXPR void on_minus() { - check_sign(); - Handler::on_minus(); - } - - FMT_CONSTEXPR void on_space() { - check_sign(); - Handler::on_space(); - } - - FMT_CONSTEXPR void on_hash() { - require_numeric_argument(); - Handler::on_hash(); - } - - FMT_CONSTEXPR void on_zero() { - 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"); - } - - private: FMT_CONSTEXPR void require_numeric_argument() { if (!is_arithmetic(arg_type_)) - this->on_error("format specifier requires numeric argument"); + 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) { - this->on_error("format specifier requires signed argument"); + 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), checker_(*this, 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) + checker_.require_numeric_argument(); + Handler::on_align(align); + } + + FMT_CONSTEXPR void on_plus() { + checker_.check_sign(); + Handler::on_plus(); + } + + FMT_CONSTEXPR void on_minus() { + checker_.check_sign(); + Handler::on_minus(); + } + + FMT_CONSTEXPR void on_space() { + checker_.check_sign(); + Handler::on_space(); + } + + FMT_CONSTEXPR void on_hash() { + checker_.require_numeric_argument(); + Handler::on_hash(); + } + + FMT_CONSTEXPR void on_zero() { + checker_.require_numeric_argument(); + Handler::on_zero(); + } + + FMT_CONSTEXPR void end_precision() { checker_.check_precision(); } + + private: + numeric_specs_checker checker_; +}; + template