diff --git a/include/fmt/format.h b/include/fmt/format.h index 7b9a6e5b..751a1594 100644 --- a/include/fmt/format.h +++ b/include/fmt/format.h @@ -850,13 +850,13 @@ constexpr const Char *pointer_from(null_terminating_iterator it) { // Returns true if value is negative, false otherwise. // Same as (value < 0) but doesn't produce warnings if T is an unsigned type. template -inline typename std::enable_if::is_signed, bool>::type - is_negative(T value) { +constexpr typename std::enable_if< + std::numeric_limits::is_signed, bool>::type is_negative(T value) { return value < 0; } template -inline typename std::enable_if::is_signed, bool>::type - is_negative(T) { +constexpr typename std::enable_if< + !std::numeric_limits::is_signed, bool>::type is_negative(T) { return false; } @@ -1205,11 +1205,11 @@ class value { custom_value custom; }; - value() {} + constexpr value() {} value(bool val) { set(int_value, val); } value(short val) { set(int_value, val); } value(unsigned short val) { set(uint_value, val); } - value(int val) { set(int_value, val); } + constexpr value(int val) : int_value(val) {} value(unsigned val) { set(uint_value, val); } value(long val) { @@ -1299,7 +1299,7 @@ class value { private: template - void set(T &field, const U &value) { + constexpr void set(T &field, const U &value) { static_assert(get_type() == TYPE, "invalid type"); field = value; } @@ -1338,7 +1338,7 @@ template class arg_map; template -basic_arg make_arg(const T &value); +constexpr basic_arg make_arg(const T &value); } // namespace internal struct monostate {}; @@ -1355,17 +1355,17 @@ class basic_arg { internal::type type_; template - friend basic_arg internal::make_arg(const T &value); + friend constexpr basic_arg internal::make_arg(const T &value); template - friend typename std::result_of::type + friend constexpr typename std::result_of::type visit(Visitor &&vis, basic_arg arg); friend class basic_args; friend class internal::arg_map; public: - basic_arg() : type_(internal::NONE) {} + constexpr basic_arg() : type_(internal::NONE) {} explicit operator bool() const noexcept { return type_ != internal::NONE; } @@ -1384,7 +1384,7 @@ class basic_arg { \endrst */ template -typename std::result_of::type +constexpr typename std::result_of::type visit(Visitor &&vis, basic_arg arg) { typedef typename Context::char_type Char; switch (arg.type_) { @@ -1427,7 +1427,7 @@ typename std::result_of::type namespace internal { template -basic_arg make_arg(const T &value) { +constexpr basic_arg make_arg(const T &value) { basic_arg arg; arg.type_ = get_type(); arg.value_ = value; @@ -3074,16 +3074,16 @@ struct is_integer { struct width_checker { template - typename std::enable_if::value, unsigned long long>::type - operator()(T value) { + constexpr typename std::enable_if< + is_integer::value, unsigned long long>::type operator()(T value) { if (is_negative(value)) FMT_THROW(format_error("negative width")); return value; } template - typename std::enable_if::value, unsigned long long>::type - operator()(T) { + constexpr typename std::enable_if< + !is_integer::value, unsigned long long>::type operator()(T) { FMT_THROW(format_error("width is not integer")); return 0; } @@ -3091,16 +3091,16 @@ struct width_checker { struct precision_checker { template - typename std::enable_if::value, unsigned long long>::type - operator()(T value) { + constexpr typename std::enable_if< + is_integer::value, unsigned long long>::type operator()(T value) { if (is_negative(value)) FMT_THROW(format_error("negative precision")); return value; } template - typename std::enable_if::value, unsigned long long>::type - operator()(T) { + constexpr typename std::enable_if< + !is_integer::value, unsigned long long>::type operator()(T) { FMT_THROW(format_error("precision is not integer")); return 0; } @@ -3217,7 +3217,7 @@ class specs_checker : public Handler { }; template -inline void set_dynamic_spec(T &value, basic_arg arg) { +constexpr void set_dynamic_spec(T &value, basic_arg arg) { unsigned long long big_value = visit(Handler(), arg); if (big_value > (std::numeric_limits::max)()) FMT_THROW(format_error("number is too big")); @@ -3236,24 +3236,24 @@ class specs_handler: public specs_setter { : specs_setter(specs), context_(ctx) {} template - void on_dynamic_width(Id arg_id) { + constexpr void on_dynamic_width(Id arg_id) { set_dynamic_spec( this->specs_.width_, get_arg(arg_id)); } template - void on_dynamic_precision(Id arg_id) { + constexpr void on_dynamic_precision(Id arg_id) { set_dynamic_spec( this->specs_.precision_, get_arg(arg_id)); } private: - basic_arg get_arg(auto_id) { + constexpr basic_arg get_arg(auto_id) { return context_.next_arg(); } template - basic_arg get_arg(Id arg_id) { + constexpr basic_arg get_arg(Id arg_id) { context_.check_arg_id(arg_id); return context_.get_arg(arg_id); } @@ -3511,11 +3511,8 @@ const Char *do_format_arg(basic_buffer &buffer, basic_format_specs specs; if (*it == ':') { ctx.advance_to(pointer_from(++it)); - if (visit(custom_formatter(buffer, ctx), arg)) { - // TODO: if constexpr, then use formatter::parse, else dispatch - // dynamically + if (visit(custom_formatter(buffer, ctx), arg)) return ctx.begin(); - } specs_checker> handler(specs_handler(specs, ctx), arg.type()); it = parse_format_specs(it, handler); diff --git a/test/format-test.cc b/test/format-test.cc index 1cf6cd0d..48f6c439 100644 --- a/test/format-test.cc +++ b/test/format-test.cc @@ -1682,17 +1682,17 @@ TEST(FormatTest, ConstexprParseFormatSpecs) { struct test_context { using char_type = char; - fmt::basic_arg next_arg() { - return fmt::basic_arg(); + constexpr fmt::basic_arg next_arg() { + return fmt::internal::make_arg(11); } template - fmt::basic_arg get_arg(Id) { - return fmt::basic_arg(); + constexpr fmt::basic_arg get_arg(Id) { + return fmt::internal::make_arg(22); } template - void check_arg_id(Id) {} + constexpr void check_arg_id(Id) {} }; constexpr fmt::format_specs parse_specs(const char *s) { @@ -1712,6 +1712,10 @@ TEST(FormatTest, ConstexprSpecsHandler) { static_assert(parse_specs("#").flag(fmt::HASH_FLAG), ""); static_assert(parse_specs("0").align() == fmt::ALIGN_NUMERIC, ""); static_assert(parse_specs("42").width() == 42, ""); + static_assert(parse_specs("{}").width() == 11, ""); + static_assert(parse_specs("{0}").width() == 22, ""); static_assert(parse_specs(".42").precision() == 42, ""); + static_assert(parse_specs(".{}").precision() == 11, ""); + static_assert(parse_specs(".{0}").precision() == 22, ""); static_assert(parse_specs("d").type() == 'd', ""); }