From c5319655176c07e82424867ba65d03e55d5f958f Mon Sep 17 00:00:00 2001 From: Pavel Novikov Date: Tue, 19 Oct 2021 20:06:36 +0300 Subject: [PATCH] rearranged type erasure to enable 'strlen' elision optimization --- include/fmt/core.h | 28 ++++++++++++++++++++++------ include/fmt/format-inl.h | 3 ++- include/fmt/format.h | 37 ++++++++++++++++++++++++++----------- include/fmt/printf.h | 27 ++++++++++++--------------- test/core-test.cc | 8 +++++--- 5 files changed, 67 insertions(+), 36 deletions(-) diff --git a/include/fmt/core.h b/include/fmt/core.h index 5dc4e352..c5f378b2 100644 --- a/include/fmt/core.h +++ b/include/fmt/core.h @@ -1110,6 +1110,14 @@ enum class type { custom_type }; +template struct basic_c_string_view : basic_string_view { + using basic_string_view::basic_string_view; + FMT_CONSTEXPR basic_c_string_view() = default; + FMT_CONSTEXPR basic_c_string_view(const Char* val) + : basic_string_view{ + val, val ? std::char_traits::length(val) : 0} {} +}; + // Maps core type T to the corresponding type enum constant. template struct type_constant : std::integral_constant {}; @@ -1131,6 +1139,7 @@ FMT_TYPE_CONSTANT(float, float_type); FMT_TYPE_CONSTANT(double, double_type); FMT_TYPE_CONSTANT(long double, long_double_type); FMT_TYPE_CONSTANT(const Char*, cstring_type); +FMT_TYPE_CONSTANT(basic_c_string_view, cstring_type); FMT_TYPE_CONSTANT(basic_string_view, string_type); FMT_TYPE_CONSTANT(const void*, pointer_type); @@ -1199,9 +1208,9 @@ template class value { FMT_INLINE value(long double val) : long_double_value(val) {} constexpr FMT_INLINE value(bool val) : bool_value(val) {} constexpr FMT_INLINE value(char_type val) : char_value(val) {} - FMT_CONSTEXPR FMT_INLINE value(const char_type* val) { - string.data = val; - if (is_constant_evaluated()) string.size = {}; + FMT_CONSTEXPR FMT_INLINE value(basic_c_string_view val) { + string.data = val.data(); + string.size = val.size(); } FMT_CONSTEXPR FMT_INLINE value(basic_string_view val) { string.data = val.data(); @@ -1303,10 +1312,16 @@ template struct arg_mapper { return val; } - FMT_CONSTEXPR FMT_INLINE auto map(char_type* val) -> const char_type* { + FMT_CONSTEXPR FMT_INLINE auto map(char_type* val) + -> detail::basic_c_string_view { return val; } - FMT_CONSTEXPR FMT_INLINE auto map(const char_type* val) -> const char_type* { + FMT_CONSTEXPR FMT_INLINE auto map(const char_type* val) + -> detail::basic_c_string_view { + return val; + } + FMT_CONSTEXPR FMT_INLINE auto map(detail::basic_c_string_view val) + -> detail::basic_c_string_view { return val; } template ; + return vis(c_sv(arg.value_.string.data, arg.value_.string.size)); case detail::type::string_type: using sv = basic_string_view; return vis(sv(arg.value_.string.data, arg.value_.string.size)); diff --git a/include/fmt/format-inl.h b/include/fmt/format-inl.h index 38a0e9cc..90aa55cb 100644 --- a/include/fmt/format-inl.h +++ b/include/fmt/format-inl.h @@ -2558,7 +2558,8 @@ FMT_FUNC void format_system_error(detail::buffer& out, int error_code, const char* message) FMT_NOEXCEPT { FMT_TRY { auto ec = std::error_code(error_code, std::generic_category()); - write(std::back_inserter(out), std::system_error(ec, message).what()); + write(std::back_inserter(out), detail::basic_c_string_view{ + std::system_error(ec, message).what()}); return; } FMT_CATCH(...) {} diff --git a/include/fmt/format.h b/include/fmt/format.h index 8bea74a4..a2ba90cc 100644 --- a/include/fmt/format.h +++ b/include/fmt/format.h @@ -1645,12 +1645,13 @@ FMT_CONSTEXPR auto write(OutputIt out, return write(out, s, specs); } template -FMT_CONSTEXPR auto write(OutputIt out, const Char* s, +FMT_CONSTEXPR auto write(OutputIt out, + basic_c_string_view> s, const basic_format_specs& specs, locale_ref) -> OutputIt { return check_cstring_type_spec(specs.type) - ? write(out, basic_string_view(s), specs, {}) - : write_ptr(out, to_uintptr(s), &specs); + ? write(out, static_cast&>(s), specs, {}) + : write_ptr(out, to_uintptr(s.data()), &specs); } template @@ -2113,15 +2114,20 @@ FMT_CONSTEXPR auto write(OutputIt out, Char value) -> OutputIt { return base_iterator(out, it); } +template +FMT_CONSTEXPR_CHAR_TRAITS auto write(OutputIt out, + basic_c_string_view value) + -> OutputIt { + if (!value.data()) throw_format_error("string pointer is null"); + + out = write(out, static_cast&>(value)); + return out; +} + template FMT_CONSTEXPR_CHAR_TRAITS auto write(OutputIt out, const Char* value) -> OutputIt { - if (!value) { - throw_format_error("string pointer is null"); - } else { - out = write(out, basic_string_view(value)); - } - return out; + return write(out, basic_c_string_view{value}); } template ( specs.precision, specs.precision_ref, ctx); - return detail::write(ctx.out(), val, specs, ctx.locale()); + return detail::write(ctx.out(), + detail::arg_mapper{}.map(val), + specs, ctx.locale()); } - return detail::write(ctx.out(), val, specs_, ctx.locale()); + return detail::write(ctx.out(), + detail::arg_mapper{}.map(val), + specs_, ctx.locale()); } #define FMT_FORMAT_AS(Type, Base) \ @@ -2630,6 +2640,11 @@ template class dynamic_formatter { if (specs_.precision >= 0) checker.end_precision(); return detail::write(ctx.out(), val, specs_, ctx.locale()); } + + template + auto format(const Char* val, FormatContext& ctx) -> decltype(ctx.out()) { + return this->format(detail::basic_c_string_view{val}, ctx); + } }; /** diff --git a/include/fmt/printf.h b/include/fmt/printf.h index 19d550f6..bc018bba 100644 --- a/include/fmt/printf.h +++ b/include/fmt/printf.h @@ -187,8 +187,13 @@ template class char_converter { // An argument visitor that return a pointer to a C string if argument is a // string or null otherwise. template struct get_cstring { - template const Char* operator()(T) { return nullptr; } - const Char* operator()(const Char* s) { return s; } + template detail::basic_c_string_view operator()(T) { + return detail::basic_c_string_view{}; + } + detail::basic_c_string_view operator()( + detail::basic_c_string_view s) { + return s; + } }; // Checks if an argument is a valid printf width specifier and sets @@ -271,14 +276,8 @@ class printf_arg_formatter : public arg_formatter { } /** Formats a null-terminated C string. */ - OutputIt operator()(const char* value) { - if (value) return base::operator()(value); - return write_null_pointer(this->specs.type != presentation_type::pointer); - } - - /** Formats a null-terminated wide C string. */ - OutputIt operator()(const wchar_t* value) { - if (value) return base::operator()(value); + OutputIt operator()(basic_c_string_view value) { + if (value.data()) return base::operator()(value); return write_null_pointer(this->specs.type != presentation_type::pointer); } @@ -431,13 +430,11 @@ void vprintf(buffer& buf, basic_string_view format, specs.fill[0] = ' '; // Ignore '0' flag for non-numeric types or if '-' present. if (specs.precision >= 0 && arg.type() == detail::type::cstring_type) { - auto str = visit_format_arg(detail::get_cstring(), arg); - auto str_end = str + specs.precision; - auto nul = std::find(str, str_end, Char()); + const auto str = visit_format_arg(detail::get_cstring(), arg); arg = detail::make_arg>( basic_string_view( - str, detail::to_unsigned(nul != str_end ? nul - str - : specs.precision))); + str.data(), + std::min(str.size(), static_cast(specs.precision)))); } if (specs.alt && visit_format_arg(detail::is_zero_int(), arg)) specs.alt = false; diff --git a/test/core-test.cc b/test/core-test.cc index 5cf9dbd8..965dae25 100644 --- a/test/core-test.cc +++ b/test/core-test.cc @@ -414,9 +414,11 @@ TEST(arg_test, string_arg) { char str_data[] = "test"; char* str = str_data; const char* cstr = str; - CHECK_ARG(char, cstr, str); + CHECK_ARG(char, fmt::detail::basic_c_string_view{str}, str); + CHECK_ARG(char, fmt::detail::basic_c_string_view{str}, cstr); auto sv = fmt::string_view(str); + CHECK_ARG(char, sv, sv); CHECK_ARG(char, sv, std::string(str)); } @@ -424,10 +426,10 @@ TEST(arg_test, wstring_arg) { wchar_t str_data[] = L"test"; wchar_t* str = str_data; const wchar_t* cstr = str; + CHECK_ARG(wchar_t, fmt::detail::basic_c_string_view{str}, str); + CHECK_ARG(wchar_t, fmt::detail::basic_c_string_view{str}, cstr); auto sv = fmt::basic_string_view(str); - CHECK_ARG(wchar_t, cstr, str); - CHECK_ARG(wchar_t, cstr, cstr); CHECK_ARG(wchar_t, sv, std::wstring(str)); CHECK_ARG(wchar_t, sv, fmt::basic_string_view(str)); }