mirror of
https://github.com/fmtlib/fmt.git
synced 2025-02-11 18:40:48 +00:00
Refactor the format API
This commit is contained in:
parent
813ac49543
commit
8d70c0edab
@ -2439,26 +2439,17 @@ FMT_CONSTEXPR void check_int_type_spec(char spec, ErrorHandler&& eh) {
|
||||
}
|
||||
}
|
||||
|
||||
template <typename ErrorHandler>
|
||||
class char_specs_checker : public ErrorHandler {
|
||||
private:
|
||||
char type_;
|
||||
|
||||
public:
|
||||
FMT_CONSTEXPR char_specs_checker(char type, ErrorHandler eh)
|
||||
: ErrorHandler(eh), type_(type) {}
|
||||
|
||||
FMT_CONSTEXPR void on_int() { check_int_type_spec(type_, *this); }
|
||||
FMT_CONSTEXPR void on_char() {}
|
||||
};
|
||||
|
||||
template <typename Char, typename Handler>
|
||||
FMT_CONSTEXPR void handle_char_specs(const basic_format_specs<Char>& specs,
|
||||
Handler&& handler) {
|
||||
if (specs.type && specs.type != 'c') return handler.on_int();
|
||||
// Checks char specs and returns true if the type spec is char (and not int).
|
||||
template <typename Char, typename ErrorHandler>
|
||||
FMT_CONSTEXPR bool check_char_specs(const basic_format_specs<Char>& specs,
|
||||
ErrorHandler&& eh) {
|
||||
if (specs.type && specs.type != 'c') {
|
||||
check_int_type_spec(specs.type, eh);
|
||||
return false;
|
||||
}
|
||||
if (specs.align == align::numeric || specs.sign != sign::none || specs.alt)
|
||||
handler.on_error("invalid format specifier for char");
|
||||
handler.on_char();
|
||||
eh.on_error("invalid format specifier for char");
|
||||
return true;
|
||||
}
|
||||
|
||||
// A floating-point presentation format.
|
||||
@ -2789,8 +2780,7 @@ struct formatter<T, Char,
|
||||
detail::check_int_type_spec(specs_.type, eh);
|
||||
break;
|
||||
case detail::type::char_type:
|
||||
detail::handle_char_specs(
|
||||
specs_, detail::char_specs_checker<decltype(eh)>(specs_.type, eh));
|
||||
detail::check_char_specs(specs_, eh);
|
||||
break;
|
||||
case detail::type::float_type:
|
||||
if (detail::const_check(FMT_USE_FLOAT))
|
||||
@ -2919,10 +2909,13 @@ FMT_INLINE auto vformat(
|
||||
}
|
||||
|
||||
#if FMT_COMPILE_TIME_CHECKS
|
||||
template <typename... Args> struct format_string {
|
||||
string_view str;
|
||||
template <typename Char, typename... Args> class basic_format_string {
|
||||
private:
|
||||
basic_string_view<Char> str;
|
||||
|
||||
template <size_t N> consteval format_string(const char (&s)[N]) : str(s) {
|
||||
public:
|
||||
template <size_t N>
|
||||
consteval basic_format_string(const char (&s)[N]) : str(s) {
|
||||
if constexpr (detail::count_named_args<Args...>() == 0) {
|
||||
using checker = detail::format_string_checker<char, detail::error_handler,
|
||||
remove_cvref_t<Args>...>;
|
||||
@ -2932,13 +2925,17 @@ template <typename... Args> struct format_string {
|
||||
|
||||
template <typename T,
|
||||
FMT_ENABLE_IF(std::is_constructible_v<string_view, const T&>)>
|
||||
format_string(const T& s) : str(s) {}
|
||||
basic_format_string(const T& s) : str(s) {}
|
||||
|
||||
operator basic_string_view<Char>() const { return str; }
|
||||
};
|
||||
|
||||
template <typename... Args>
|
||||
FMT_INLINE std::string format(
|
||||
format_string<std::type_identity_t<Args>...> format_str, Args&&... args) {
|
||||
return detail::vformat(format_str.str, make_format_args(args...));
|
||||
using format_string = basic_format_string<char, std::type_identity_t<Args>...>;
|
||||
|
||||
template <typename... T>
|
||||
FMT_INLINE auto format(format_string<T...> str, T&&... args) -> std::string {
|
||||
return detail::vformat(str, make_format_args(args...));
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@ -346,6 +346,8 @@ template <typename T> using checked_ptr = T*;
|
||||
template <typename T> inline T* make_checked(T* p, size_t) { return p; }
|
||||
#endif
|
||||
|
||||
// Attempts to reserve space for n extra characters in the output range.
|
||||
// Returns a pointer to the reserved range or a reference to it.
|
||||
template <typename Container, FMT_ENABLE_IF(is_contiguous<Container>::value)>
|
||||
#if FMT_CLANG_VERSION >= 307 && !FMT_ICC_VERSION
|
||||
__attribute__((no_sanitize("undefined")))
|
||||
@ -1304,7 +1306,7 @@ template <typename Char, typename OutputIt>
|
||||
FMT_CONSTEXPR OutputIt write(OutputIt out, Char value,
|
||||
const basic_format_specs<Char>& specs,
|
||||
locale_ref loc = {}) {
|
||||
return !specs.type || specs.type == 'c'
|
||||
return check_char_specs(specs, error_handler())
|
||||
? write_char(out, value, specs)
|
||||
: write(out, static_cast<int>(value), specs, loc);
|
||||
}
|
||||
@ -1807,7 +1809,8 @@ inline OutputIt write(OutputIt out, T value) {
|
||||
}
|
||||
|
||||
template <typename Char, typename OutputIt>
|
||||
OutputIt write(OutputIt out, monostate) {
|
||||
OutputIt write(OutputIt out, monostate, basic_format_specs<Char> = {},
|
||||
locale_ref = {}) {
|
||||
FMT_ASSERT(false, "");
|
||||
return out;
|
||||
}
|
||||
@ -1927,152 +1930,32 @@ template <typename OutputIt, typename Char> struct default_arg_formatter {
|
||||
basic_format_args<context> args;
|
||||
locale_ref loc;
|
||||
|
||||
template <typename T> OutputIt operator()(T value) {
|
||||
template <typename T> auto operator()(T value) -> OutputIt {
|
||||
return write<Char>(out, value);
|
||||
}
|
||||
|
||||
OutputIt operator()(typename basic_format_arg<context>::handle handle) {
|
||||
auto operator()(typename basic_format_arg<context>::handle h) -> OutputIt {
|
||||
basic_format_parse_context<Char> parse_ctx({});
|
||||
basic_format_context<OutputIt, Char> format_ctx(out, args, loc);
|
||||
handle.format(parse_ctx, format_ctx);
|
||||
h.format(parse_ctx, format_ctx);
|
||||
return format_ctx.out();
|
||||
}
|
||||
};
|
||||
|
||||
template <typename OutputIt, typename Char,
|
||||
typename ErrorHandler = error_handler>
|
||||
class arg_formatter_base {
|
||||
public:
|
||||
using iterator = OutputIt;
|
||||
using char_type = Char;
|
||||
using format_specs = basic_format_specs<Char>;
|
||||
template <typename OutputIt, typename Char> struct arg_formatter {
|
||||
using context = basic_format_context<OutputIt, Char>;
|
||||
|
||||
private:
|
||||
iterator out_;
|
||||
const format_specs& specs_;
|
||||
locale_ref locale_;
|
||||
OutputIt out;
|
||||
const basic_format_specs<Char>& specs;
|
||||
locale_ref locale;
|
||||
|
||||
// Attempts to reserve space for n extra characters in the output range.
|
||||
// Returns a pointer to the reserved range or a reference to out_.
|
||||
auto reserve(size_t n) -> decltype(detail::reserve(out_, n)) {
|
||||
return detail::reserve(out_, n);
|
||||
template <typename T>
|
||||
FMT_CONSTEXPR FMT_INLINE auto operator()(T value) -> OutputIt {
|
||||
return detail::write(out, value, specs, locale);
|
||||
}
|
||||
|
||||
void write(char value) {
|
||||
auto&& it = reserve(1);
|
||||
*it++ = value;
|
||||
}
|
||||
|
||||
template <typename Ch, FMT_ENABLE_IF(std::is_same<Ch, Char>::value)>
|
||||
void write(Ch value) {
|
||||
out_ = detail::write<Char>(out_, value);
|
||||
}
|
||||
|
||||
void write(string_view value) {
|
||||
auto&& it = reserve(value.size());
|
||||
it = copy_str<Char>(value.begin(), value.end(), it);
|
||||
}
|
||||
void write(wstring_view value) {
|
||||
static_assert(std::is_same<Char, wchar_t>::value, "");
|
||||
auto&& it = reserve(value.size());
|
||||
it = copy_str<Char>(value.begin(), value.end(), it);
|
||||
}
|
||||
|
||||
template <typename Ch>
|
||||
void write(const Ch* s, size_t size, const format_specs& specs) {
|
||||
auto width =
|
||||
specs.width != 0 ? compute_width(basic_string_view<Ch>(s, size)) : 0;
|
||||
out_ = write_padded(out_, specs, size, width,
|
||||
[=](reserve_iterator<OutputIt> it) {
|
||||
return copy_str<Char>(s, s + size, it);
|
||||
});
|
||||
}
|
||||
|
||||
template <typename Ch>
|
||||
FMT_CONSTEXPR void write(basic_string_view<Ch> s,
|
||||
const format_specs& specs = {}) {
|
||||
out_ = detail::write(out_, s, specs);
|
||||
}
|
||||
|
||||
struct char_spec_handler : ErrorHandler {
|
||||
arg_formatter_base& formatter;
|
||||
Char value;
|
||||
|
||||
constexpr char_spec_handler(arg_formatter_base& f, Char val)
|
||||
: formatter(f), value(val) {}
|
||||
|
||||
FMT_CONSTEXPR void on_int() {
|
||||
// char is only formatted as int if there are specs.
|
||||
formatter.out_ = detail::write(formatter.out_, static_cast<int>(value),
|
||||
formatter.specs_, formatter.locale_);
|
||||
}
|
||||
FMT_CONSTEXPR void on_char() {
|
||||
formatter.out_ =
|
||||
detail::write<Char>(formatter.out_, value, formatter.specs_);
|
||||
}
|
||||
};
|
||||
|
||||
protected:
|
||||
iterator out() { return out_; }
|
||||
const format_specs& specs() { return specs_; }
|
||||
|
||||
FMT_CONSTEXPR void write(bool value) {
|
||||
write(string_view(value ? "true" : "false"), specs_);
|
||||
}
|
||||
|
||||
void write(const Char* value) {
|
||||
if (value)
|
||||
write(basic_string_view<char_type>(value), specs_);
|
||||
else
|
||||
FMT_THROW(format_error("string pointer is null"));
|
||||
}
|
||||
|
||||
public:
|
||||
constexpr arg_formatter_base(OutputIt out, const format_specs& s,
|
||||
locale_ref loc)
|
||||
: out_(out), specs_(s), locale_(loc) {}
|
||||
|
||||
iterator operator()(monostate) {
|
||||
FMT_ASSERT(false, "invalid argument type");
|
||||
return out_;
|
||||
}
|
||||
|
||||
template <typename T, FMT_ENABLE_IF(is_integral<T>::value)>
|
||||
FMT_CONSTEXPR FMT_INLINE iterator operator()(T value) {
|
||||
return out_ = detail::write(out_, value, specs_, locale_);
|
||||
}
|
||||
|
||||
FMT_CONSTEXPR iterator operator()(Char value) {
|
||||
handle_char_specs(specs_,
|
||||
char_spec_handler(*this, static_cast<Char>(value)));
|
||||
return out_;
|
||||
}
|
||||
|
||||
FMT_CONSTEXPR iterator operator()(bool value) {
|
||||
if (specs_.type && specs_.type != 's') return (*this)(value ? 1 : 0);
|
||||
write(value != 0);
|
||||
return out_;
|
||||
}
|
||||
|
||||
template <typename T, FMT_ENABLE_IF(std::is_floating_point<T>::value)>
|
||||
iterator operator()(T value) {
|
||||
if (const_check(is_supported_floating_point(value)))
|
||||
out_ = detail::write(out_, value, specs_, locale_);
|
||||
else
|
||||
FMT_ASSERT(false, "unsupported float argument type");
|
||||
return out_;
|
||||
}
|
||||
|
||||
iterator operator()(const Char* value) {
|
||||
return out_ = detail::write(out_, value, specs_, locale_);
|
||||
}
|
||||
|
||||
FMT_CONSTEXPR iterator operator()(basic_string_view<Char> value) {
|
||||
return out_ = detail::write(out_, value, specs_, locale_);
|
||||
}
|
||||
|
||||
iterator operator()(const void* value) {
|
||||
return out_ = detail::write(out_, value, specs_, locale_);
|
||||
auto operator()(typename basic_format_arg<context>::handle) -> OutputIt {
|
||||
// User-defined types are handled separately because they require access
|
||||
// to the parse context.
|
||||
return out;
|
||||
}
|
||||
};
|
||||
|
||||
@ -2247,25 +2130,9 @@ struct format_handler : detail::error_handler {
|
||||
arg.type());
|
||||
begin = parse_format_specs(begin, end, handler);
|
||||
if (begin == end || *begin != '}') on_error("missing '}' in format string");
|
||||
|
||||
using arg_formatter_base = detail::arg_formatter_base<OutputIt, Char>;
|
||||
struct arg_formatter : arg_formatter_base {
|
||||
Context& ctx_;
|
||||
|
||||
FMT_CONSTEXPR explicit arg_formatter(
|
||||
Context& ctx, const typename arg_formatter_base::format_specs& specs)
|
||||
: arg_formatter_base(ctx.out(), specs, ctx.locale()), ctx_(ctx) {}
|
||||
|
||||
using arg_formatter_base::operator();
|
||||
|
||||
auto operator()(typename basic_format_arg<Context>::handle) ->
|
||||
typename arg_formatter_base::iterator {
|
||||
// User-defined types are handled separately because they require access
|
||||
// to the parse context.
|
||||
return ctx_.out();
|
||||
}
|
||||
};
|
||||
context.advance_to(visit_format_arg(arg_formatter(context, specs), arg));
|
||||
auto f = detail::arg_formatter<OutputIt, Char>{context.out(), specs,
|
||||
context.locale()};
|
||||
context.advance_to(visit_format_arg(f, arg));
|
||||
return begin;
|
||||
}
|
||||
};
|
||||
@ -2631,8 +2498,8 @@ struct formatter<arg_join<It, Sentinel, Char>, Char> {
|
||||
};
|
||||
|
||||
/**
|
||||
Returns an object that formats the iterator range `[begin, end)` with elements
|
||||
separated by `sep`.
|
||||
Returns an object that formats the iterator range `[begin, end)` with
|
||||
elements separated by `sep`.
|
||||
*/
|
||||
template <typename It, typename Sentinel>
|
||||
arg_join<It, Sentinel, char> join(It begin, Sentinel end, string_view sep) {
|
||||
@ -2692,8 +2559,8 @@ inline std::string to_string(const T& value) {
|
||||
|
||||
template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
|
||||
inline std::string to_string(T value) {
|
||||
// The buffer should be large enough to store the number including the sign or
|
||||
// "false" for bool.
|
||||
// The buffer should be large enough to store the number including the sign
|
||||
// or "false" for bool.
|
||||
constexpr int max_size = detail::digits10<T>() + 2;
|
||||
char buffer[max_size > 5 ? static_cast<unsigned>(max_size) : 5];
|
||||
char* begin = buffer;
|
||||
|
@ -191,33 +191,32 @@ template <typename Char> class printf_width_handler {
|
||||
|
||||
// The ``printf`` argument formatter.
|
||||
template <typename OutputIt, typename Char>
|
||||
class printf_arg_formatter : public detail::arg_formatter_base<OutputIt, Char> {
|
||||
class printf_arg_formatter : public arg_formatter<OutputIt, Char> {
|
||||
private:
|
||||
using base = detail::arg_formatter_base<OutputIt, Char>;
|
||||
using base = arg_formatter<OutputIt, Char>;
|
||||
using context_type = basic_printf_context<OutputIt, Char>;
|
||||
using format_specs = typename base::format_specs;
|
||||
using format_specs = basic_format_specs<Char>;
|
||||
|
||||
context_type& context_;
|
||||
|
||||
OutputIt write_null_pointer(bool is_string = false) {
|
||||
auto s = this->specs();
|
||||
auto s = this->specs;
|
||||
s.type = 0;
|
||||
return detail::write(this->out(),
|
||||
string_view(is_string ? "(null)" : "(nil)"), s);
|
||||
return write(this->out, string_view(is_string ? "(null)" : "(nil)"), s);
|
||||
}
|
||||
|
||||
public:
|
||||
printf_arg_formatter(OutputIt iter, format_specs& specs, context_type& ctx)
|
||||
: base(iter, specs, detail::locale_ref()), context_(ctx) {}
|
||||
printf_arg_formatter(OutputIt iter, format_specs& s, context_type& ctx)
|
||||
: base{iter, s, locale_ref()}, context_(ctx) {}
|
||||
|
||||
OutputIt operator()(monostate value) { return base::operator()(value); }
|
||||
|
||||
template <typename T, FMT_ENABLE_IF(fmt::detail::is_integral<T>::value)>
|
||||
template <typename T, FMT_ENABLE_IF(detail::is_integral<T>::value)>
|
||||
OutputIt operator()(T value) {
|
||||
// MSVC2013 fails to compile separate overloads for bool and Char so use
|
||||
// std::is_same instead.
|
||||
if (std::is_same<T, Char>::value) {
|
||||
format_specs fmt_specs = this->specs();
|
||||
format_specs fmt_specs = this->specs;
|
||||
if (fmt_specs.type && fmt_specs.type != 'c')
|
||||
return (*this)(static_cast<int>(value));
|
||||
fmt_specs.sign = sign::none;
|
||||
@ -227,8 +226,7 @@ class printf_arg_formatter : public detail::arg_formatter_base<OutputIt, Char> {
|
||||
// ignored for non-numeric types
|
||||
if (fmt_specs.align == align::none || fmt_specs.align == align::numeric)
|
||||
fmt_specs.align = align::right;
|
||||
return detail::write<Char>(this->out(), static_cast<Char>(value),
|
||||
fmt_specs);
|
||||
return write<Char>(this->out, static_cast<Char>(value), fmt_specs);
|
||||
}
|
||||
return base::operator()(value);
|
||||
}
|
||||
@ -241,13 +239,13 @@ class printf_arg_formatter : public detail::arg_formatter_base<OutputIt, 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 != 'p');
|
||||
return write_null_pointer(this->specs.type != 'p');
|
||||
}
|
||||
|
||||
/** Formats a null-terminated wide C string. */
|
||||
OutputIt operator()(const wchar_t* value) {
|
||||
if (value) return base::operator()(value);
|
||||
return write_null_pointer(this->specs().type != 'p');
|
||||
return write_null_pointer(this->specs.type != 'p');
|
||||
}
|
||||
|
||||
OutputIt operator()(basic_string_view<Char> value) {
|
||||
@ -262,7 +260,7 @@ class printf_arg_formatter : public detail::arg_formatter_base<OutputIt, Char> {
|
||||
/** Formats an argument of a custom (user-defined) type. */
|
||||
OutputIt operator()(typename basic_format_arg<context_type>::handle handle) {
|
||||
handle.format(context_.parse_context(), context_);
|
||||
return this->out();
|
||||
return this->out;
|
||||
}
|
||||
};
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user