diff --git a/include/fmt/core.h b/include/fmt/core.h index 74b9b08d..ae80ec1c 100644 --- a/include/fmt/core.h +++ b/include/fmt/core.h @@ -371,6 +371,9 @@ template class basic_string_view { the size with ``std::char_traits::length``. \endrst */ +#if __cplusplus >= 201703L // C++17's char_traits::length() is constexpr. + FMT_CONSTEXPR +#endif basic_string_view(const Char* s) : data_(s), size_(std::char_traits::length(s)) {} @@ -381,11 +384,12 @@ template class basic_string_view { : data_(s.data()), size_(s.size()) {} - template < - typename S, - FMT_ENABLE_IF(std::is_same>::value)> - FMT_CONSTEXPR basic_string_view(S s) FMT_NOEXCEPT : data_(s.data()), - size_(s.size()) {} +#if __cplusplus >= 201703L // C++17's std::string_view::size() is constexpr. + FMT_CONSTEXPR +#endif + basic_string_view(internal::std_string_view s) + FMT_NOEXCEPT : data_(s.data()), + size_(s.size()) {} /** Returns a pointer to the string data. */ FMT_CONSTEXPR const Char* data() const { return data_; } diff --git a/include/fmt/format.h b/include/fmt/format.h index bb9bc1e5..f1bd6dc6 100644 --- a/include/fmt/format.h +++ b/include/fmt/format.h @@ -2236,6 +2236,7 @@ enum class arg_id_kind { none, index, name }; // An argument reference. template struct arg_ref { FMT_CONSTEXPR arg_ref() : kind(arg_id_kind::none), val() {} + FMT_CONSTEXPR explicit arg_ref(int index) : kind(arg_id_kind::index), val(index) {} FMT_CONSTEXPR explicit arg_ref(basic_string_view name) @@ -2266,8 +2267,8 @@ struct dynamic_format_specs : basic_format_specs { arg_ref precision_ref; }; -// Format spec handler that saves references to arguments representing dynamic -// width and precision to be resolved at formatting time. +// Format spec handler that saves references to arguments representing +// dynamic width and precision to be resolved at formatting time. template class dynamic_specs_handler : public specs_setter { @@ -2579,8 +2580,9 @@ FMT_CONSTEXPR void parse_format_string(basic_string_view format_str, auto begin = format_str.data(); auto end = begin + format_str.size(); while (begin != end) { - // Doing two passes with memchr (one for '{' and another for '}') is up to - // 2.5x faster than the naive one-pass implementation on big format strings. + // Doing two passes with memchr (one for '{' and another for '}') is up + // to 2.5x faster than the naive one-pass implementation on big format + // strings. const Char* p = begin; if (*begin != '{' && !find(begin + 1, end, '{', p)) return write(begin, end); @@ -3523,9 +3525,22 @@ template struct udl_arg { } }; +// Converts string literals to basic_string_view. template -FMT_CONSTEXPR basic_string_view literal_to_view(const Char (&s)[N]) { - return {s, N - 1}; +FMT_CONSTEXPR basic_string_view compile_string_to_view( + const Char (&s)[N]) { + // Remove trailing null character if needed. Won't be present if this is used + // with raw character array (i.e. not defined as a string). + return {s, N - ((N > 0 && std::char_traits::to_int_type(s[N - 1]) == 0) + ? 1 + : 0)}; +} + +// Converts string_view to basic_string_view. +template +FMT_CONSTEXPR basic_string_view compile_string_to_view( + const std_string_view& s) { + return {s.data(), s.size()}; } } // namespace internal @@ -3583,18 +3598,17 @@ FMT_CONSTEXPR internal::udl_arg operator"" _a(const wchar_t* s, #endif // FMT_USE_USER_DEFINED_LITERALS FMT_END_NAMESPACE -#define FMT_STRING_IMPL(s, ...) \ - [] { \ - /* Use a macro-like name to avoid shadowing warnings. */ \ - struct FMT_COMPILE_STRING : fmt::compile_string { \ - using char_type = fmt::remove_cvref_t; \ - FMT_MAYBE_UNUSED __VA_ARGS__ FMT_CONSTEXPR \ - operator fmt::basic_string_view() const { \ - /* FMT_STRING only accepts string literals. */ \ - return fmt::internal::literal_to_view(s); \ - } \ - }; \ - return FMT_COMPILE_STRING(); \ +#define FMT_STRING_IMPL(s, ...) \ + [] { \ + /* Use a macro-like name to avoid shadowing warnings. */ \ + struct FMT_COMPILE_STRING : fmt::compile_string { \ + using char_type = fmt::remove_cvref_t; \ + FMT_MAYBE_UNUSED __VA_ARGS__ FMT_CONSTEXPR \ + operator fmt::basic_string_view() const { \ + return fmt::internal::compile_string_to_view(s); \ + } \ + }; \ + return FMT_COMPILE_STRING(); \ }() /** diff --git a/test/format-test.cc b/test/format-test.cc index a2223749..65f9c274 100644 --- a/test/format-test.cc +++ b/test/format-test.cc @@ -46,6 +46,7 @@ using fmt::format_error; using fmt::memory_buffer; using fmt::string_view; using fmt::wmemory_buffer; +using fmt::wstring_view; using fmt::internal::basic_writer; using fmt::internal::max_value; @@ -1853,10 +1854,25 @@ TEST(FormatTest, UnpackedArgs) { struct string_like {}; fmt::string_view to_string_view(string_like) { return "foo"; } +#if __cplusplus >= 201703L +namespace { +FMT_CONSTEXPR char withNull[3] = {'{', '}', '\0'}; +FMT_CONSTEXPR char noNull[2] = {'{', '}'}; +} // namespace +#endif + TEST(FormatTest, CompileTimeString) { EXPECT_EQ("42", fmt::format(FMT_STRING("{}"), 42)); EXPECT_EQ(L"42", fmt::format(FMT_STRING(L"{}"), 42)); EXPECT_EQ("foo", fmt::format(FMT_STRING("{}"), string_like())); +#if __cplusplus >= 201703L + EXPECT_EQ("42", fmt::format(FMT_STRING(withNull), 42)); + EXPECT_EQ("42", fmt::format(FMT_STRING(noNull), 42)); +#endif +#if defined(FMT_USE_STRING_VIEW) && __cplusplus >= 201703L + EXPECT_EQ("42", fmt::format(FMT_STRING(std::string_view("{}")), 42)); + EXPECT_EQ(L"42", fmt::format(FMT_STRING(std::wstring_view(L"{}")), 42)); +#endif } TEST(FormatTest, CustomFormatCompileTimeString) {