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