rearranged type erasure to enable 'strlen' elision optimization

This commit is contained in:
Pavel Novikov 2021-10-19 20:06:36 +03:00
parent 51d13724c3
commit c531965517
No known key found for this signature in database
GPG Key ID: AEC4E958FA860F27
5 changed files with 67 additions and 36 deletions

View File

@ -1110,6 +1110,14 @@ enum class type {
custom_type
};
template <typename Char> struct basic_c_string_view : basic_string_view<Char> {
using basic_string_view<Char>::basic_string_view;
FMT_CONSTEXPR basic_c_string_view() = default;
FMT_CONSTEXPR basic_c_string_view(const Char* val)
: basic_string_view<Char>{
val, val ? std::char_traits<Char>::length(val) : 0} {}
};
// Maps core type T to the corresponding type enum constant.
template <typename T, typename Char>
struct type_constant : std::integral_constant<type, type::custom_type> {};
@ -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<Char>, cstring_type);
FMT_TYPE_CONSTANT(basic_string_view<Char>, string_type);
FMT_TYPE_CONSTANT(const void*, pointer_type);
@ -1199,9 +1208,9 @@ template <typename Context> 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<char_type> val) {
string.data = val.data();
string.size = val.size();
}
FMT_CONSTEXPR FMT_INLINE value(basic_string_view<char_type> val) {
string.data = val.data();
@ -1303,10 +1312,16 @@ template <typename Context> 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<char_type> {
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<char_type> {
return val;
}
FMT_CONSTEXPR FMT_INLINE auto map(detail::basic_c_string_view<char_type> val)
-> detail::basic_c_string_view<char_type> {
return val;
}
template <typename T,
@ -1563,7 +1578,8 @@ FMT_CONSTEXPR FMT_INLINE auto visit_format_arg(
case detail::type::long_double_type:
return vis(arg.value_.long_double_value);
case detail::type::cstring_type:
return vis(arg.value_.string.data);
using c_sv = detail::basic_c_string_view<typename Context::char_type>;
return vis(c_sv(arg.value_.string.data, arg.value_.string.size));
case detail::type::string_type:
using sv = basic_string_view<typename Context::char_type>;
return vis(sv(arg.value_.string.data, arg.value_.string.size));

View File

@ -2558,7 +2558,8 @@ FMT_FUNC void format_system_error(detail::buffer<char>& 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<char>{
std::system_error(ec, message).what()});
return;
}
FMT_CATCH(...) {}

View File

@ -1645,12 +1645,13 @@ FMT_CONSTEXPR auto write(OutputIt out,
return write(out, s, specs);
}
template <typename Char, typename OutputIt>
FMT_CONSTEXPR auto write(OutputIt out, const Char* s,
FMT_CONSTEXPR auto write(OutputIt out,
basic_c_string_view<type_identity_t<Char>> s,
const basic_format_specs<Char>& specs, locale_ref)
-> OutputIt {
return check_cstring_type_spec(specs.type)
? write(out, basic_string_view<Char>(s), specs, {})
: write_ptr<Char>(out, to_uintptr(s), &specs);
? write(out, static_cast<basic_string_view<Char>&>(s), specs, {})
: write_ptr<Char>(out, to_uintptr(s.data()), &specs);
}
template <typename Char, typename OutputIt>
@ -2113,15 +2114,20 @@ FMT_CONSTEXPR auto write(OutputIt out, Char value) -> OutputIt {
return base_iterator(out, it);
}
template <typename Char, typename OutputIt>
FMT_CONSTEXPR_CHAR_TRAITS auto write(OutputIt out,
basic_c_string_view<Char> value)
-> OutputIt {
if (!value.data()) throw_format_error("string pointer is null");
out = write(out, static_cast<basic_string_view<Char>&>(value));
return out;
}
template <typename Char, typename OutputIt>
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<Char>(value));
}
return out;
return write(out, basic_c_string_view<Char>{value});
}
template <typename Char, typename OutputIt, typename T,
@ -2537,9 +2543,13 @@ formatter<T, Char,
specs.width_ref, ctx);
detail::handle_dynamic_spec<detail::precision_checker>(
specs.precision, specs.precision_ref, ctx);
return detail::write<Char>(ctx.out(), val, specs, ctx.locale());
return detail::write<Char>(ctx.out(),
detail::arg_mapper<FormatContext>{}.map(val),
specs, ctx.locale());
}
return detail::write<Char>(ctx.out(), val, specs_, ctx.locale());
return detail::write<Char>(ctx.out(),
detail::arg_mapper<FormatContext>{}.map(val),
specs_, ctx.locale());
}
#define FMT_FORMAT_AS(Type, Base) \
@ -2630,6 +2640,11 @@ template <typename Char = char> class dynamic_formatter {
if (specs_.precision >= 0) checker.end_precision();
return detail::write<Char>(ctx.out(), val, specs_, ctx.locale());
}
template <typename FormatContext>
auto format(const Char* val, FormatContext& ctx) -> decltype(ctx.out()) {
return this->format(detail::basic_c_string_view<Char>{val}, ctx);
}
};
/**

View File

@ -187,8 +187,13 @@ template <typename Context> class char_converter {
// An argument visitor that return a pointer to a C string if argument is a
// string or null otherwise.
template <typename Char> struct get_cstring {
template <typename T> const Char* operator()(T) { return nullptr; }
const Char* operator()(const Char* s) { return s; }
template <typename T> detail::basic_c_string_view<Char> operator()(T) {
return detail::basic_c_string_view<Char>{};
}
detail::basic_c_string_view<Char> operator()(
detail::basic_c_string_view<Char> 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<Char> {
}
/** 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<Char> 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<Char>& buf, basic_string_view<Char> 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<Char>(), arg);
auto str_end = str + specs.precision;
auto nul = std::find(str, str_end, Char());
const auto str = visit_format_arg(detail::get_cstring<Char>(), arg);
arg = detail::make_arg<basic_printf_context<OutputIt, Char>>(
basic_string_view<Char>(
str, detail::to_unsigned(nul != str_end ? nul - str
: specs.precision)));
str.data(),
std::min(str.size(), static_cast<size_t>(specs.precision))));
}
if (specs.alt && visit_format_arg(detail::is_zero_int(), arg))
specs.alt = false;

View File

@ -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<char>{str}, str);
CHECK_ARG(char, fmt::detail::basic_c_string_view<char>{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<wchar_t>{str}, str);
CHECK_ARG(wchar_t, fmt::detail::basic_c_string_view<wchar_t>{str}, cstr);
auto sv = fmt::basic_string_view<wchar_t>(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<wchar_t>(str));
}