rearranged type erasure to enable 'strlen' elision optimization
This commit is contained in:
parent
51d13724c3
commit
c531965517
@ -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));
|
||||
|
||||
@ -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(...) {}
|
||||
|
||||
@ -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);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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));
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user