diff --git a/include/fmt/core.h b/include/fmt/core.h index e576b6c6..cfb27ddf 100644 --- a/include/fmt/core.h +++ b/include/fmt/core.h @@ -42,12 +42,18 @@ # define FMT_MSC_VER 0 #endif -#ifndef FMT_CONSTEXPR -# if FMT_HAS_FEATURE(cxx_constexpr) -# define FMT_CONSTEXPR constexpr -# else -# define FMT_CONSTEXPR -# endif +// Check if relaxed c++14 constexpr is supported. +#ifndef FMT_USE_CONSTEXPR +# define FMT_USE_CONSTEXPR \ + (FMT_HAS_FEATURE(cxx_relaxed_constexpr) || FMT_GCC_VERSION >= 500 || \ + FMT_MSC_VER >= 1910) +#endif +#if FMT_USE_CONSTEXPR +# define FMT_CONSTEXPR constexpr +# define FMT_CONSTEXPR_VAR constexpr +#else +# define FMT_CONSTEXPR inline +# define FMT_CONSTEXPR_VAR #endif #ifndef FMT_OVERRIDE @@ -855,23 +861,23 @@ using wcontext = buffer_context_t; namespace internal { template -FMT_CONSTEXPR type get_type() { +struct get_type { using value_type = decltype(make_value(std::declval())); - return value_type::type_tag; -} + static const type value = value_type::type_tag; +}; template FMT_CONSTEXPR uint64_t get_types() { return 0; } template FMT_CONSTEXPR uint64_t get_types() { - return get_type() | (get_types() << 4); + return get_type::value | (get_types() << 4); } template FMT_CONSTEXPR basic_arg make_arg(const T &value) { basic_arg arg; - arg.type_ = get_type(); + arg.type_ = get_type::value; arg.value_ = make_value(value); return arg; } diff --git a/include/fmt/format.h b/include/fmt/format.h index 3fc54d51..ff7a90d6 100644 --- a/include/fmt/format.h +++ b/include/fmt/format.h @@ -2044,7 +2044,7 @@ FMT_CONSTEXPR bool check_format_string( // because of a bug in MSVC. template struct format_type : - std::integral_constant() != CUSTOM> {}; + std::integral_constant::value != CUSTOM> {}; // Specifies whether to format enums. template @@ -2335,7 +2335,7 @@ class basic_writer { void on_num() { unsigned num_digits = internal::count_digits(abs_value); char_type sep = internal::thousands_sep(writer.locale_.get()); - static FMT_CONSTEXPR unsigned SEP_SIZE = 1; + enum { SEP_SIZE = 1 }; unsigned size = num_digits + SEP_SIZE * ((num_digits - 1) / 3); writer.write_int(size, get_prefix(), spec, [this, size, sep](auto &&it) { basic_string_view s(&sep, SEP_SIZE); @@ -2828,7 +2828,7 @@ struct formatter< FMT_CONSTEXPR typename ParseContext::iterator parse(ParseContext &ctx) { auto it = internal::null_terminating_iterator(ctx); using handler_type = internal::dynamic_specs_handler; - auto type = internal::get_type, T>(); + auto type = internal::get_type, T>::value; internal::specs_checker handler(handler_type(specs_, ctx), type); it = parse_format_specs(it, handler); @@ -2933,7 +2933,7 @@ struct dynamic_formatter { void on_hash() {} }; internal::specs_checker - checker(null_handler(), internal::get_type()); + checker(null_handler(), internal::get_type::value); checker.on_align(specs_.align()); if (specs_.flags_ == 0) { // Do nothing. @@ -3068,9 +3068,9 @@ class format_spec_factory { } }; -FMT_CONSTEXPR fill_spec_factory fill; -FMT_CONSTEXPR format_spec_factory width; -FMT_CONSTEXPR format_spec_factory type; +static const fill_spec_factory fill; +static const format_spec_factory width; +static const format_spec_factory type; template struct arg_join { @@ -3216,7 +3216,7 @@ template inline typename std::enable_if< std::is_base_of::value, std::string>::type format(String format_str, const Args & ... args) { - FMT_CONSTEXPR bool invalid_format = + FMT_CONSTEXPR_VAR bool invalid_format = internal::check_format_string( string_view(format_str.value(), format_str.size())); (void)invalid_format; @@ -3243,8 +3243,8 @@ class udl_formatter { public: template std::basic_string operator()(const Args &... args) const { - FMT_CONSTEXPR Char s[] = {CHARS..., '\0'}; - FMT_CONSTEXPR bool invalid_format = + FMT_CONSTEXPR_VAR Char s[] = {CHARS..., '\0'}; + FMT_CONSTEXPR_VAR bool invalid_format = check_format_string( basic_string_view(s, sizeof...(CHARS))); (void)invalid_format; diff --git a/test/format-test.cc b/test/format-test.cc index bd4fa016..6de47bb3 100644 --- a/test/format-test.cc +++ b/test/format-test.cc @@ -1598,6 +1598,30 @@ TEST(FormatTest, DynamicFormatter) { format_error, "precision not allowed for this argument type"); } +TEST(FormatTest, UdlTemplate) { + EXPECT_EQ("foo", "foo"_format()); + EXPECT_EQ(" 42", "{0:10}"_format(42)); + EXPECT_EQ("42", fmt::format(FMT_STRING("{}"), 42)); +} + +TEST(FormatTest, ToString) { + EXPECT_EQ("42", fmt::to_string(42)); +} + +TEST(FormatTest, OutputIterators) { + std::list out; + fmt::format_to(std::back_inserter(out), "{}", 42); + EXPECT_EQ("42", std::string(out.begin(), out.end())); + std::stringstream s; + fmt::format_to(std::ostream_iterator(s), "{}", 42); + EXPECT_EQ("42", s.str()); +} + +TEST(FormatTest, OutputSize) { + EXPECT_EQ(2, fmt::count("{}", 42)); +} + +#if FMT_USE_CONSTEXPR struct test_arg_id_handler { enum result { NONE, EMPTY, INDEX, NAME, ERROR }; result res = NONE; @@ -1834,12 +1858,6 @@ TEST(FormatTest, ConstexprParseFormatString) { static_assert(parse_string("{:}"), ""); } -TEST(FormatTest, UdlTemplate) { - EXPECT_EQ("foo", "foo"_format()); - EXPECT_EQ(" 42", "{0:10}"_format(42)); - EXPECT_EQ("42", fmt::format(FMT_STRING("{}"), 42)); -} - struct test_error_handler { const char *&error; @@ -1936,20 +1954,4 @@ TEST(FormatTest, FormatStringErrors) { "cannot switch from automatic to manual argument indexing", int, int); } - -TEST(FormatTest, ToString) { - EXPECT_EQ("42", fmt::to_string(42)); -} - -TEST(FormatTest, OutputIterators) { - std::list out; - fmt::format_to(std::back_inserter(out), "{}", 42); - EXPECT_EQ("42", std::string(out.begin(), out.end())); - std::stringstream s; - fmt::format_to(std::ostream_iterator(s), "{}", 42); - EXPECT_EQ("42", s.str()); -} - -TEST(FormatTest, OutputSize) { - EXPECT_EQ(2, fmt::count("{}", 42)); -} +#endif // FMT_USE_CONSTEXPR