From 1c855a47623656b58846de011762c7eb6303e857 Mon Sep 17 00:00:00 2001 From: Victor Zverovich Date: Sun, 5 Nov 2017 09:28:50 -0800 Subject: [PATCH] Integrate constexpr format specs parsing --- include/fmt/format.h | 84 ++++++++++++++++++++++++++++++-------------- 1 file changed, 57 insertions(+), 27 deletions(-) diff --git a/include/fmt/format.h b/include/fmt/format.h index 4b9fbff0..da7e0e45 100644 --- a/include/fmt/format.h +++ b/include/fmt/format.h @@ -364,6 +364,7 @@ class basic_string_view { public: using char_type = Char; + using iterator = const Char *; constexpr basic_string_view() noexcept : data_(0), size_(0) {} @@ -403,8 +404,8 @@ class basic_string_view { /** Returns the string size. */ constexpr std::size_t size() const { return size_; } - const Char *begin() const { return data_; } - const Char *end() const { return data_ + size_; } + constexpr iterator begin() const { return data_; } + constexpr iterator end() const { return data_ + size_; } void remove_prefix(size_t n) { data_ += n; @@ -790,11 +791,11 @@ class null_terminating_iterator { null_terminating_iterator() : ptr_(0), end_(0) {} - null_terminating_iterator(const Char *ptr, const Char *end) + constexpr null_terminating_iterator(const Char *ptr, const Char *end) : ptr_(ptr), end_(end) {} template - explicit null_terminating_iterator(const Range &r) + constexpr explicit null_terminating_iterator(const Range &r) : ptr_(r.begin()), end_(r.end()) {} null_terminating_iterator &operator=(const Char *ptr) { @@ -803,16 +804,16 @@ class null_terminating_iterator { return *this; } - Char operator*() const { + constexpr Char operator*() const { return ptr_ != end_ ? *ptr_ : 0; } - null_terminating_iterator operator++() { + constexpr null_terminating_iterator operator++() { ++ptr_; return *this; } - null_terminating_iterator operator++(int) { + constexpr null_terminating_iterator operator++(int) { null_terminating_iterator result(*this); ++ptr_; return result; @@ -823,7 +824,7 @@ class null_terminating_iterator { return *this; } - null_terminating_iterator operator+(difference_type n) { + constexpr null_terminating_iterator operator+(difference_type n) { return null_terminating_iterator(ptr_ + n, end_); } @@ -1919,15 +1920,15 @@ class parse_context { using char_type = Char; using iterator = const Char*; - explicit parse_context(basic_string_view format_str) + explicit constexpr parse_context(basic_string_view format_str) : format_str_(format_str), next_arg_index_(0) {} // Returns an iterator to the beginning of the format string range being // parsed. - iterator begin() const { return format_str_.begin(); } + constexpr iterator begin() const { return format_str_.begin(); } // Returns an iterator past the end of the format string range being parsed. - iterator end() const { return format_str_.end(); } + constexpr iterator end() const { return format_str_.end(); } // Advances the begin iterator to ``it``. void advance_to(iterator it) { @@ -3429,7 +3430,7 @@ struct precision_adapter { // characters, possibly emulated via null_terminating_iterator, representing // format specifiers. template -constexpr Iterator parse_format_specs(Iterator it, SpecHandler &handler) { +constexpr Iterator parse_format_specs(Iterator it, SpecHandler &&handler) { using char_type = typename std::iterator_traits::value_type; // Parse fill and alignment. if (char_type c = *it) { @@ -3616,7 +3617,7 @@ struct formatter< // Parses format specifiers stopping either at the end of the range or at the // terminating '}'. template - auto parse(ParseContext &ctx) -> decltype(ctx.begin()) { + constexpr typename ParseContext::iterator parse(ParseContext &ctx) { auto it = internal::null_terminating_iterator(ctx); using handler_type = internal::dynamic_specs_handler; internal::specs_checker @@ -3797,40 +3798,69 @@ inline const void *ptr(const T *p) { return p; } namespace fmt { namespace internal { +template +constexpr const Char *parse_format_specs(parse_context &ctx) { + formatter f; + return f.parse(ctx); +} + # if FMT_UDL_TEMPLATE -template +template struct udl_format_handler { - bool error = false; + public: + explicit constexpr udl_format_handler(const Char *end) : end_(end) {} constexpr void on_text(const Char *, const Char *) {} - constexpr void on_arg_id() {} + constexpr void on_arg_id() { ++arg_index_; } template constexpr void on_arg_id(T) {} constexpr void on_replacement_field(const Char *) {} - constexpr const Char *on_format_specs(const Char *s) { return s; } + constexpr const Char *on_format_specs(const Char *s) { + if (arg_index_ < 0 || arg_index_ >= sizeof...(Args)) { + on_error("argument index out of range"); + return s; + } + parse_context ctx(basic_string_view(s, end_ - s)); + return parse_funcs_[arg_index_](ctx); + } - constexpr void on_error(const char *) { error = true; } + constexpr void on_error(const char *) { error_ = true; } + + constexpr bool is_valid() const { return !error_; } + + private: + // Format specifier parsing function. + using parse_func = const Char *(*)(parse_context &); + + const Char *end_; + int arg_index_ = -1; + bool error_ = false; + parse_func parse_funcs_[sizeof...(Args)] = { + &parse_format_specs... + }; }; template -struct udl_formatter { - template - static constexpr bool check_format(const Char *s) { - udl_format_handler handler; - internal::parse_format_string(s, handler); - return !handler.error; - } - +class udl_formatter { + public: template std::basic_string operator()(const Args &... args) const { constexpr Char s[] = {CHARS..., '\0'}; - static_assert(check_format(s), "invalid format string"); + static_assert(check_format(s), "error parsing format string"); return format(s, args...); } + + private: + template + static constexpr bool check_format(const Char *s) { + udl_format_handler handler(s + sizeof...(CHARS)); + internal::parse_format_string(s, handler); + return handler.is_valid(); + } }; # else template