Replace formatter with context

This commit is contained in:
Victor Zverovich
2016-11-06 16:11:24 -08:00
parent 2bba420337
commit 9998f66f8c
10 changed files with 223 additions and 242 deletions

View File

@ -446,7 +446,7 @@ void printf(BasicWriter<Char> &w, BasicCStringRef<Char> format,
format_args args); format_args args);
FMT_FUNC int vfprintf(std::FILE *f, CStringRef format, FMT_FUNC int vfprintf(std::FILE *f, CStringRef format,
basic_format_args<PrintfFormatter<char>> args) { basic_format_args<printf_context<char>> args) {
MemoryWriter w; MemoryWriter w;
printf(w, format, args); printf(w, format, args);
std::size_t size = w.size(); std::size_t size = w.size();
@ -463,7 +463,7 @@ template void internal::FixedBuffer<char>::grow(std::size_t);
template void internal::ArgMap<char>::init(const format_args &args); template void internal::ArgMap<char>::init(const format_args &args);
template void PrintfFormatter<char>::format(Writer &writer, CStringRef format); template void printf_context<char>::format(Writer &writer);
template int internal::CharTraits<char>::format_float( template int internal::CharTraits<char>::format_float(
char *buffer, std::size_t size, const char *format, char *buffer, std::size_t size, const char *format,
@ -479,8 +479,7 @@ template void internal::FixedBuffer<wchar_t>::grow(std::size_t);
template void internal::ArgMap<wchar_t>::init(const format_args &args); template void internal::ArgMap<wchar_t>::init(const format_args &args);
template void PrintfFormatter<wchar_t>::format(WWriter &writer, template void printf_context<wchar_t>::format(WWriter &writer);
WCStringRef format);
template int internal::CharTraits<wchar_t>::format_float( template int internal::CharTraits<wchar_t>::format_float(
wchar_t *buffer, std::size_t size, const wchar_t *format, wchar_t *buffer, std::size_t size, const wchar_t *format,

View File

@ -377,8 +377,11 @@ class ArgFormatter;
template <typename Impl, typename Char> template <typename Impl, typename Char>
class BasicPrintfArgFormatter; class BasicPrintfArgFormatter;
template <typename Char, typename ArgFormatter = fmt::ArgFormatter<Char> > template <typename Char>
class basic_formatter; class basic_format_context;
typedef basic_format_context<char> format_context;
typedef basic_format_context<wchar_t> wformat_context;
/** /**
\rst \rst
@ -993,7 +996,7 @@ struct Value {
}; };
typedef void (*FormatFunc)( typedef void (*FormatFunc)(
void *writer, const void *arg, void *formatter, void *format_str_ptr); void *writer, const void *arg, void *ctx);
struct CustomValue { struct CustomValue {
const void *value; const void *value;
@ -1230,10 +1233,10 @@ template <typename T>
constexpr Type type() { return gettype<typename std::decay<T>::type>(); } constexpr Type type() { return gettype<typename std::decay<T>::type>(); }
// Makes an Arg object from any type. // Makes an Arg object from any type.
template <typename Formatter> template <typename Context>
class MakeValue : public Arg { class MakeValue : public Arg {
public: public:
typedef typename Formatter::char_type Char; typedef typename Context::char_type Char;
private: private:
// The following two methods are private to disallow formatting of // The following two methods are private to disallow formatting of
@ -1271,12 +1274,10 @@ class MakeValue : public Arg {
// Formats an argument of a custom type, such as a user-defined class. // Formats an argument of a custom type, such as a user-defined class.
template <typename T> template <typename T>
static void format_custom_arg( static void format_custom_arg(
void *writer, const void *arg, void *formatter, void *format_str_ptr) { void *writer, const void *arg, void *context) {
typedef BasicWriter<typename Formatter::char_type> Writer; format_value(*static_cast<BasicWriter<Char>*>(writer),
format_value(*static_cast<Writer*>(writer),
*static_cast<const T*>(arg), *static_cast<const T*>(arg),
*static_cast<Formatter*>(formatter), *static_cast<Context*>(context));
*static_cast<const Char**>(format_str_ptr));
} }
public: public:
@ -1406,7 +1407,7 @@ struct NamedArg : Arg {
template <typename T> template <typename T>
NamedArg(BasicStringRef<Char> argname, const T &value) NamedArg(BasicStringRef<Char> argname, const T &value)
: Arg(MakeArg< basic_formatter<Char> >(value)), name(argname) {} : Arg(MakeArg< basic_format_context<Char> >(value)), name(argname) {}
}; };
class RuntimeError : public std::runtime_error { class RuntimeError : public std::runtime_error {
@ -1430,7 +1431,7 @@ constexpr uint64_t make_type<void>() { return 0; }
enum { MAX_PACKED_ARGS = 16 }; enum { MAX_PACKED_ARGS = 16 };
} // namespace internal } // namespace internal
template <typename Formatter, typename ...Args> template <typename Context, typename ...Args>
class format_arg_store { class format_arg_store {
private: private:
static const size_t NUM_ARGS = sizeof...(Args); static const size_t NUM_ARGS = sizeof...(Args);
@ -1443,26 +1444,25 @@ class format_arg_store {
std::array<value_type, NUM_ARGS + (IS_PACKED ? 0 : 1)> data_; std::array<value_type, NUM_ARGS + (IS_PACKED ? 0 : 1)> data_;
template <typename ...A> template <typename ...A>
friend format_arg_store<Formatter, A...> make_format_args(const A & ... args); friend format_arg_store<Context, A...> make_format_args(const A & ... args);
public: public:
static const uint64_t TYPES = internal::make_type<Args..., void>(); static const uint64_t TYPES = internal::make_type<Args..., void>();
format_arg_store(const Args &... args, Formatter *) format_arg_store(const Args &... args)
: data_{{internal::MakeValue<Formatter>(args)...}} {} : data_{{internal::MakeValue<Context>(args)...}} {}
const value_type *data() const { return data_.data(); } const value_type *data() const { return data_.data(); }
}; };
template <typename Formatter, typename ...Args> template <typename Context, typename ...Args>
inline format_arg_store<Formatter, Args...> inline format_arg_store<Context, Args...>
make_format_args(const Args & ... args) { make_format_args(const Args & ... args) {
Formatter *f = nullptr; return format_arg_store<Context, Args...>(args...);
return format_arg_store<Formatter, Args...>(args..., f);
} }
/** Formatting arguments. */ /** Formatting arguments. */
template <typename Formatter> template <typename Context>
class basic_format_args { class basic_format_args {
private: private:
// To reduce compiled code size per formatting function call, types of first // To reduce compiled code size per formatting function call, types of first
@ -1496,7 +1496,7 @@ class basic_format_args {
basic_format_args() : types_(0) {} basic_format_args() : types_(0) {}
template <typename... Args> template <typename... Args>
basic_format_args(const format_arg_store<Formatter, Args...> &store) basic_format_args(const format_arg_store<Context, Args...> &store)
: types_(store.TYPES) { : types_(store.TYPES) {
set_data(store.data()); set_data(store.data());
} }
@ -1527,8 +1527,8 @@ class basic_format_args {
} }
}; };
typedef basic_format_args<basic_formatter<char>> format_args; typedef basic_format_args<basic_format_context<char>> format_args;
typedef basic_format_args<basic_formatter<wchar_t>> wformat_args; typedef basic_format_args<basic_format_context<wchar_t>> wformat_args;
#define FMT_DISPATCH(call) static_cast<Impl*>(this)->call #define FMT_DISPATCH(call) static_cast<Impl*>(this)->call
@ -2014,6 +2014,8 @@ class ArgFormatterBase : public ArgVisitor<Impl, void> {
} }
public: public:
typedef Char char_type;
ArgFormatterBase(BasicWriter<Char> &w, FormatSpec &s) ArgFormatterBase(BasicWriter<Char> &w, FormatSpec &s)
: writer_(w), spec_(s) {} : writer_(w), spec_(s) {}
@ -2082,21 +2084,35 @@ class ArgFormatterBase : public ArgVisitor<Impl, void> {
} }
}; };
template <typename Formatter> template <typename Char>
class FormatterBase { inline void write(BasicWriter<Char> &w, const Char *start, const Char *end) {
private: if (start != end)
basic_format_args<Formatter> args_; w << BasicStringRef<Char>(start, internal::to_unsigned(end - start));
}
template <typename Char, typename Context>
class format_context_base {
private:
const Char *ptr_;
basic_format_args<Context> args_;
int next_arg_index_; int next_arg_index_;
protected:
format_context_base(const Char *format_str, basic_format_args<Context> args)
: ptr_(format_str), args_(args), next_arg_index_(0) {}
~format_context_base() {}
basic_format_args<Context> args() const { return args_; }
// Returns the argument with specified index. // Returns the argument with specified index.
Arg do_get_arg(unsigned arg_index, const char *&error) { format_arg do_get_arg(unsigned arg_index, const char *&error) {
Arg arg = args_[arg_index]; format_arg arg = args_[arg_index];
switch (arg.type) { switch (arg.type) {
case Arg::NONE: case format_arg::NONE:
error = "argument index out of range"; error = "argument index out of range";
break; break;
case Arg::NAMED_ARG: case format_arg::NAMED_ARG:
arg = *static_cast<const internal::Arg*>(arg.pointer); arg = *static_cast<const format_arg*>(arg.pointer);
break; break;
default: default:
/*nothing*/; /*nothing*/;
@ -2104,24 +2120,19 @@ private:
return arg; return arg;
} }
protected:
FormatterBase(basic_format_args<Formatter> args)
: args_(args), next_arg_index_(0) {}
const basic_format_args<Formatter> &args() const { return args_; }
// Returns the next argument.
Arg next_arg(const char *&error) {
if (next_arg_index_ >= 0)
return do_get_arg(internal::to_unsigned(next_arg_index_++), error);
error = "cannot switch from manual to automatic argument indexing";
return Arg();
}
// Checks if manual indexing is used and returns the argument with // Checks if manual indexing is used and returns the argument with
// specified index. // specified index.
Arg get_arg(unsigned arg_index, const char *&error) { format_arg get_arg(unsigned arg_index, const char *&error) {
return check_no_auto_index(error) ? do_get_arg(arg_index, error) : Arg(); return this->check_no_auto_index(error) ?
this->do_get_arg(arg_index, error) : format_arg();
}
// Returns the next argument.
format_arg next_arg(const char *&error) {
if (next_arg_index_ >= 0)
return this->do_get_arg(internal::to_unsigned(next_arg_index_++), error);
error = "cannot switch from manual to automatic argument indexing";
return format_arg();
} }
bool check_no_auto_index(const char *&error) { bool check_no_auto_index(const char *&error) {
@ -2132,13 +2143,11 @@ private:
next_arg_index_ = -1; next_arg_index_ = -1;
return true; return true;
} }
};
template <typename Char> public:
inline void write(BasicWriter<Char> &w, const Char *start, const Char *end) { // Returns a pointer to the current position in the format string.
if (start != end) const Char *&ptr() { return ptr_; }
w << BasicStringRef<Char>(start, internal::to_unsigned(end - start)); };
}
} // namespace internal } // namespace internal
/** /**
@ -2161,8 +2170,7 @@ inline void write(BasicWriter<Char> &w, const Char *start, const Char *end) {
template <typename Impl, typename Char> template <typename Impl, typename Char>
class BasicArgFormatter : public internal::ArgFormatterBase<Impl, Char> { class BasicArgFormatter : public internal::ArgFormatterBase<Impl, Char> {
private: private:
basic_formatter<Char, Impl> &formatter_; basic_format_context<Char> &ctx_;
const Char *format_;
public: public:
/** /**
@ -2173,15 +2181,13 @@ class BasicArgFormatter : public internal::ArgFormatterBase<Impl, Char> {
to the part of the format string being parsed for custom argument types. to the part of the format string being parsed for custom argument types.
\endrst \endrst
*/ */
BasicArgFormatter(BasicWriter<Char> &writer, BasicArgFormatter(BasicWriter<Char> &writer, basic_format_context<Char> &ctx,
basic_formatter<Char, Impl> &formatter, FormatSpec &spec)
FormatSpec &spec, const Char *fmt) : internal::ArgFormatterBase<Impl, Char>(writer, spec), ctx_(ctx) {}
: internal::ArgFormatterBase<Impl, Char>(writer, spec),
formatter_(formatter), format_(fmt) {}
/** Formats an argument of a custom (user-defined) type. */ /** Formats an argument of a custom (user-defined) type. */
void visit_custom(internal::Arg::CustomValue c) { void visit_custom(internal::Arg::CustomValue c) {
c.format(&this->writer(), c.value, &formatter_, &format_); c.format(&this->writer(), c.value, &ctx_);
} }
}; };
@ -2190,49 +2196,45 @@ template <typename Char>
class ArgFormatter : public BasicArgFormatter<ArgFormatter<Char>, Char> { class ArgFormatter : public BasicArgFormatter<ArgFormatter<Char>, Char> {
public: public:
/** Constructs an argument formatter object. */ /** Constructs an argument formatter object. */
ArgFormatter(BasicWriter<Char> &writer, basic_formatter<Char> &formatter, ArgFormatter(BasicWriter<Char> &writer, basic_format_context<Char> &ctx,
FormatSpec &spec, const Char *fmt) FormatSpec &spec)
: BasicArgFormatter<ArgFormatter<Char>, Char>(writer, formatter, spec, fmt) {} : BasicArgFormatter<ArgFormatter<Char>, Char>(writer, ctx, spec) {}
}; };
/** This template formats data and writes the output to a writer. */ template <typename Char>
template <typename Char, typename ArgFormatter> class basic_format_context :
class basic_formatter : public internal::format_context_base<Char, basic_format_context<Char>> {
private internal::FormatterBase<basic_formatter<Char, ArgFormatter>> {
public:
/** The character type for the output. */
typedef Char char_type;
private: private:
internal::ArgMap<Char> map_; internal::ArgMap<Char> map_;
FMT_DISALLOW_COPY_AND_ASSIGN(basic_formatter); FMT_DISALLOW_COPY_AND_ASSIGN(basic_format_context);
typedef internal::format_context_base<Char, basic_format_context> Base;
typedef internal::FormatterBase<basic_formatter> Base;
using Base::get_arg; using Base::get_arg;
// Checks if manual indexing is used and returns the argument with // Checks if manual indexing is used and returns the argument with
// specified name. // specified name.
internal::Arg get_arg(BasicStringRef<Char> arg_name, const char *&error); format_arg get_arg(BasicStringRef<Char> name, const char *&error);
public: public:
/** The character type for the output. */
typedef Char char_type;
/** /**
\rst \rst
Constructs a ``basic_formatter`` object. References to the arguments are Constructs a ``basic_format_context`` object. References to the arguments are
stored in the formatter object so make sure they have appropriate lifetimes. stored in the object so make sure they have appropriate lifetimes.
\endrst \endrst
*/ */
basic_formatter(basic_format_args<basic_formatter> args) : Base(args) {} basic_format_context(const Char *format_str,
basic_format_args<basic_format_context> args)
: Base(format_str, args) {}
// Parses argument index and returns corresponding argument. // Parses argument id and returns corresponding argument.
internal::Arg parse_arg_index(const Char *&s); format_arg parse_arg_id();
// Parses argument name and returns corresponding argument.
internal::Arg parse_arg_name(const Char *&s);
// Formats a single argument and advances format_str, a format string pointer. using Base::ptr;
const Char *format(BasicWriter<Char> &writer, const Char *&format_str,
const internal::Arg &arg);
}; };
/** /**
@ -2269,7 +2271,7 @@ class SystemError : public internal::RuntimeError {
*/ */
template <typename... Args> template <typename... Args>
SystemError(int error_code, CStringRef message, const Args & ... args) { SystemError(int error_code, CStringRef message, const Args & ... args) {
init(error_code, message, make_format_args<basic_formatter<char>>(args...)); init(error_code, message, make_format_args<format_context>(args...));
} }
~SystemError() throw(); ~SystemError() throw();
@ -2464,10 +2466,7 @@ class BasicWriter {
} }
void vwrite(BasicCStringRef<Char> format, void vwrite(BasicCStringRef<Char> format,
basic_format_args<basic_formatter<Char>> args) { basic_format_args<basic_format_context<Char>> args);
vformat(*this, format, args);
}
/** /**
\rst \rst
Writes formatted data. Writes formatted data.
@ -2495,7 +2494,7 @@ class BasicWriter {
*/ */
template <typename... Args> template <typename... Args>
void write(BasicCStringRef<Char> format, const Args & ... args) { void write(BasicCStringRef<Char> format, const Args & ... args) {
vwrite(format, make_format_args<fmt::basic_formatter<Char>>(args...)); vwrite(format, make_format_args<basic_format_context<Char>>(args...));
} }
BasicWriter &operator<<(int value) { BasicWriter &operator<<(int value) {
@ -3123,7 +3122,7 @@ class WindowsError : public SystemError {
*/ */
template <typename... Args> template <typename... Args>
WindowsError(int error_code, CStringRef message, const Args & ... args) { WindowsError(int error_code, CStringRef message, const Args & ... args) {
init(error_code, message, make_format_args<basic_formatter<char>>(args...)); init(error_code, message, make_format_args<format_context>(args...));
} }
}; };
@ -3147,8 +3146,7 @@ FMT_API void vprint_colored(Color c, CStringRef format, format_args args);
template <typename... Args> template <typename... Args>
inline void print_colored(Color c, CStringRef format_str, inline void print_colored(Color c, CStringRef format_str,
const Args & ... args) { const Args & ... args) {
vprint_colored(c, format_str, vprint_colored(c, format_str, make_format_args<format_context>(args...));
make_format_args<basic_formatter<char>>(args...));
} }
inline std::string vformat(CStringRef format_str, format_args args) { inline std::string vformat(CStringRef format_str, format_args args) {
@ -3168,7 +3166,7 @@ inline std::string vformat(CStringRef format_str, format_args args) {
*/ */
template <typename... Args> template <typename... Args>
inline std::string format(CStringRef format_str, const Args & ... args) { inline std::string format(CStringRef format_str, const Args & ... args) {
return vformat(format_str, make_format_args<basic_formatter<char>>(args...)); return vformat(format_str, make_format_args<format_context>(args...));
} }
inline std::wstring vformat(WCStringRef format_str, wformat_args args) { inline std::wstring vformat(WCStringRef format_str, wformat_args args) {
@ -3179,7 +3177,7 @@ inline std::wstring vformat(WCStringRef format_str, wformat_args args) {
template <typename... Args> template <typename... Args>
inline std::wstring format(WCStringRef format_str, const Args & ... args) { inline std::wstring format(WCStringRef format_str, const Args & ... args) {
auto vargs = make_format_args<basic_formatter<wchar_t>>(args...); auto vargs = make_format_args<wformat_context>(args...);
return vformat(format_str, vargs); return vformat(format_str, vargs);
} }
@ -3196,7 +3194,7 @@ FMT_API void vprint(std::FILE *f, CStringRef format_str, format_args args);
*/ */
template <typename... Args> template <typename... Args>
inline void print(std::FILE *f, CStringRef format_str, const Args & ... args) { inline void print(std::FILE *f, CStringRef format_str, const Args & ... args) {
vprint(f, format_str, make_format_args<basic_formatter<char>>(args...)); vprint(f, format_str, make_format_args<format_context>(args...));
} }
FMT_API void vprint(CStringRef format_str, format_args args); FMT_API void vprint(CStringRef format_str, format_args args);
@ -3212,7 +3210,7 @@ FMT_API void vprint(CStringRef format_str, format_args args);
*/ */
template <typename... Args> template <typename... Args>
inline void print(CStringRef format_str, const Args & ... args) { inline void print(CStringRef format_str, const Args & ... args) {
vprint(format_str, make_format_args<basic_formatter<char>>(args...)); vprint(format_str, make_format_args<format_context>(args...));
} }
/** /**
@ -3405,57 +3403,55 @@ void check_sign(const Char *&s, const Arg &arg) {
} }
} // namespace internal } // namespace internal
template <typename Char, typename AF> template <typename Char>
inline internal::Arg basic_formatter<Char, AF>::get_arg( inline format_arg basic_format_context<Char>::get_arg(
BasicStringRef<Char> arg_name, const char *&error) { BasicStringRef<Char> name, const char *&error) {
if (this->check_no_auto_index(error)) { if (this->check_no_auto_index(error)) {
map_.init(this->args()); map_.init(this->args());
const internal::Arg *arg = map_.find(arg_name); const internal::Arg *arg = map_.find(name);
if (arg) if (arg)
return *arg; return *arg;
error = "argument not found"; error = "argument not found";
} }
return internal::Arg(); return format_arg();
} }
template <typename Char, typename AF> template <typename Char>
inline internal::Arg basic_formatter<Char, AF>::parse_arg_index( inline format_arg basic_format_context<Char>::parse_arg_id() {
const Char *&s) { const Char *&s = this->ptr();
const char *error = 0; if (!internal::is_name_start(*s)) {
internal::Arg arg = *s < '0' || *s > '9' ? const char *error = 0;
this->next_arg(error) : get_arg(internal::parse_nonnegative_int(s), error); format_arg arg = *s < '0' || *s > '9' ?
if (error) { this->next_arg(error) : get_arg(internal::parse_nonnegative_int(s), error);
FMT_THROW(format_error( if (error) {
*s != '}' && *s != ':' ? "invalid format string" : error)); FMT_THROW(format_error(
*s != '}' && *s != ':' ? "invalid format string" : error));
}
return arg;
} }
return arg;
}
template <typename Char, typename AF>
inline internal::Arg basic_formatter<Char, AF>::parse_arg_name(const Char *&s) {
assert(internal::is_name_start(*s));
const Char *start = s; const Char *start = s;
Char c; Char c;
do { do {
c = *++s; c = *++s;
} while (internal::is_name_start(c) || ('0' <= c && c <= '9')); } while (internal::is_name_start(c) || ('0' <= c && c <= '9'));
const char *error = 0; const char *error = 0;
internal::Arg arg = get_arg(BasicStringRef<Char>(start, s - start), error); format_arg arg = get_arg(BasicStringRef<Char>(start, s - start), error);
if (error) if (error)
FMT_THROW(format_error(error)); FMT_THROW(format_error(error));
return arg; return arg;
} }
template <typename Char, typename ArgFormatter> // Formats a single argument.
const Char *basic_formatter<Char, ArgFormatter>::format( template <typename ArgFormatter, typename Char, typename Context>
BasicWriter<Char> &writer, const Char *&format_str, const internal::Arg &arg) { void format_arg(BasicWriter<Char> &writer, const internal::Arg &arg,
Context &ctx) {
using internal::Arg; using internal::Arg;
const Char *s = format_str; const Char *&s = ctx.ptr();
FormatSpec spec; FormatSpec spec;
if (*s == ':') { if (*s == ':') {
if (arg.type == Arg::CUSTOM) { if (arg.type == Arg::CUSTOM) {
arg.custom.format(&writer, arg.custom.value, this, &s); arg.custom.format(&writer, arg.custom.value, &ctx);
return s; return;
} }
++s; ++s;
// Parse fill and alignment. // Parse fill and alignment.
@ -3527,8 +3523,7 @@ const Char *basic_formatter<Char, ArgFormatter>::format(
spec.width_ = internal::parse_nonnegative_int(s); spec.width_ = internal::parse_nonnegative_int(s);
} else if (*s == '{') { } else if (*s == '{') {
++s; ++s;
Arg width_arg = internal::is_name_start(*s) ? Arg width_arg = ctx.parse_arg_id();
parse_arg_name(s) : parse_arg_index(s);
if (*s++ != '}') if (*s++ != '}')
FMT_THROW(format_error("invalid format string")); FMT_THROW(format_error("invalid format string"));
ULongLong value = 0; ULongLong value = 0;
@ -3565,8 +3560,7 @@ const Char *basic_formatter<Char, ArgFormatter>::format(
spec.precision_ = internal::parse_nonnegative_int(s); spec.precision_ = internal::parse_nonnegative_int(s);
} else if (*s == '{') { } else if (*s == '{') {
++s; ++s;
Arg precision_arg = internal::is_name_start(*s) ? Arg precision_arg = ctx.parse_arg_id();
parse_arg_name(s) : parse_arg_index(s);
if (*s++ != '}') if (*s++ != '}')
FMT_THROW(format_error("invalid format string")); FMT_THROW(format_error("invalid format string"));
ULongLong value = 0; ULongLong value = 0;
@ -3608,20 +3602,19 @@ const Char *basic_formatter<Char, ArgFormatter>::format(
spec.type_ = static_cast<char>(*s++); spec.type_ = static_cast<char>(*s++);
} }
if (*s++ != '}') if (*s != '}')
FMT_THROW(format_error("missing '}' in format string")); FMT_THROW(format_error("missing '}' in format string"));
// Format argument. // Format argument.
ArgFormatter(writer, *this, spec, s - 1).visit(arg); ArgFormatter(writer, ctx, spec).visit(arg);
return s;
} }
/** Formats arguments and writes the output to the writer. */ /** Formats arguments and writes the output to the writer. */
template <typename ArgFormatter, typename Char = typename ArgFormatter::Char> template <typename ArgFormatter, typename Char, typename Context>
void vformat(BasicWriter<Char> &writer, BasicCStringRef<Char> format_str, void vformat(BasicWriter<Char> &writer, BasicCStringRef<Char> format_str,
basic_format_args<basic_formatter<Char, ArgFormatter>> args) { basic_format_args<Context> args) {
basic_formatter<Char, ArgFormatter> formatter(args); basic_format_context<Char> ctx(format_str.c_str(), args);
const Char *s = format_str.c_str(); const Char *&s = ctx.ptr();
const Char *start = s; const Char *start = s;
while (*s) { while (*s) {
Char c = *s++; Char c = *s++;
@ -3634,12 +3627,19 @@ void vformat(BasicWriter<Char> &writer, BasicCStringRef<Char> format_str,
if (c == '}') if (c == '}')
FMT_THROW(format_error("unmatched '}' in format string")); FMT_THROW(format_error("unmatched '}' in format string"));
internal::write(writer, start, s - 1); internal::write(writer, start, s - 1);
internal::Arg arg = internal::is_name_start(*s) ? format_arg<ArgFormatter>(writer, ctx.parse_arg_id(), ctx);
formatter.parse_arg_name(s) : formatter.parse_arg_index(s); assert(*s == '}');
start = s = formatter.format(writer, s, arg); start = ++s;
} }
internal::write(writer, start, s); internal::write(writer, start, s);
} }
template <typename Char>
inline void BasicWriter<Char>::vwrite(
BasicCStringRef<Char> format,
basic_format_args<basic_format_context<Char>> args) {
vformat<ArgFormatter<Char>>(*this, format, args);
}
} // namespace fmt } // namespace fmt
#if FMT_USE_USER_DEFINED_LITERALS #if FMT_USE_USER_DEFINED_LITERALS

View File

@ -82,14 +82,13 @@ BasicStringRef<Char> format_value(
} // namespace internal } // namespace internal
// Formats a value. // Formats a value.
template <typename Char, typename ArgFormatter, typename T> template <typename Char, typename T>
void format_value(BasicWriter<Char> &w, const T &value, void format_value(BasicWriter<Char> &w, const T &value,
basic_formatter<Char, ArgFormatter> &f, basic_format_context<Char> &ctx) {
const Char *&format_str) {
internal::MemoryBuffer<Char, internal::INLINE_BUFFER_SIZE> buffer; internal::MemoryBuffer<Char, internal::INLINE_BUFFER_SIZE> buffer;
auto str = internal::format_value(buffer, value); auto str = internal::format_value(buffer, value);
typedef internal::MakeArg< basic_formatter<Char> > MakeArg; typedef internal::MakeArg< basic_format_context<Char> > MakeArg;
format_str = f.format(w, format_str, MakeArg(str)); format_arg< ArgFormatter<Char> >(w, MakeArg(str), ctx);
} }
FMT_API void vprint(std::ostream &os, CStringRef format_str, format_args args); FMT_API void vprint(std::ostream &os, CStringRef format_str, format_args args);
@ -106,7 +105,7 @@ FMT_API void vprint(std::ostream &os, CStringRef format_str, format_args args);
template <typename... Args> template <typename... Args>
inline void print(std::ostream &os, CStringRef format_str, inline void print(std::ostream &os, CStringRef format_str,
const Args & ... args) { const Args & ... args) {
vprint(os, format_str, make_format_args<basic_formatter<char>>(args...)); vprint(os, format_str, make_format_args<format_context>(args...));
} }
} // namespace fmt } // namespace fmt

View File

@ -172,7 +172,7 @@ public:
template <typename... Args> template <typename... Args>
inline void print(CStringRef format_str, const Args & ... args) { inline void print(CStringRef format_str, const Args & ... args) {
vprint(format_str, make_format_args<basic_formatter<char>>(args...)); vprint(format_str, make_format_args<format_context>(args...));
} }
}; };

View File

@ -262,11 +262,10 @@ class BasicPrintfArgFormatter : public internal::ArgFormatterBase<Impl, Char> {
/** Formats an argument of a custom (user-defined) type. */ /** Formats an argument of a custom (user-defined) type. */
void visit_custom(internal::Arg::CustomValue c) { void visit_custom(internal::Arg::CustomValue c) {
typedef basic_formatter<Char> Formatter; const Char format_str[] = {'}', '\0'};
Formatter formatter((basic_format_args<Formatter>())); auto args = basic_format_args<basic_format_context<Char>>();
const Char format_str[] = {'}', 0}; basic_format_context<Char> ctx(format_str, args);
const Char *format = format_str; c.format(&this->writer(), c.value, &ctx);
c.format(&this->writer(), c.value, &formatter, &format);
} }
}; };
@ -283,14 +282,15 @@ class PrintfArgFormatter
/** This template formats data and writes the output to a writer. */ /** This template formats data and writes the output to a writer. */
template <typename Char, template <typename Char,
typename ArgFormatter = PrintfArgFormatter<Char> > typename ArgFormatter = PrintfArgFormatter<Char> >
class PrintfFormatter : class printf_context :
private internal::FormatterBase<PrintfFormatter<Char, ArgFormatter>> { private internal::format_context_base<
Char, printf_context<Char, ArgFormatter>> {
public: public:
/** The character type for the output. */ /** The character type for the output. */
typedef Char char_type; typedef Char char_type;
private: private:
typedef internal::FormatterBase<PrintfFormatter> Base; typedef internal::format_context_base<Char, printf_context> Base;
void parse_flags(FormatSpec &spec, const Char *&s); void parse_flags(FormatSpec &spec, const Char *&s);
@ -306,21 +306,21 @@ class PrintfFormatter :
public: public:
/** /**
\rst \rst
Constructs a ``PrintfFormatter`` object. References to the arguments and Constructs a ``printf_context`` object. References to the arguments and
the writer are stored in the formatter object so make sure they have the writer are stored in the context object so make sure they have
appropriate lifetimes. appropriate lifetimes.
\endrst \endrst
*/ */
explicit PrintfFormatter(basic_format_args<PrintfFormatter> args) explicit printf_context(BasicCStringRef<Char> format_str,
: Base(args) {} basic_format_args<printf_context> args)
: Base(format_str.c_str(), args) {}
/** Formats stored arguments and writes the output to the writer. */ /** Formats stored arguments and writes the output to the writer. */
FMT_API void format(BasicWriter<Char> &writer, FMT_API void format(BasicWriter<Char> &writer);
BasicCStringRef<Char> format_str);
}; };
template <typename Char, typename AF> template <typename Char, typename AF>
void PrintfFormatter<Char, AF>::parse_flags(FormatSpec &spec, const Char *&s) { void printf_context<Char, AF>::parse_flags(FormatSpec &spec, const Char *&s) {
for (;;) { for (;;) {
switch (*s++) { switch (*s++) {
case '-': case '-':
@ -346,8 +346,8 @@ void PrintfFormatter<Char, AF>::parse_flags(FormatSpec &spec, const Char *&s) {
} }
template <typename Char, typename AF> template <typename Char, typename AF>
internal::Arg PrintfFormatter<Char, AF>::get_arg(const Char *s, internal::Arg printf_context<Char, AF>::get_arg(const Char *s,
unsigned arg_index) { unsigned arg_index) {
(void)s; (void)s;
const char *error = 0; const char *error = 0;
internal::Arg arg = arg_index == std::numeric_limits<unsigned>::max() ? internal::Arg arg = arg_index == std::numeric_limits<unsigned>::max() ?
@ -358,7 +358,7 @@ internal::Arg PrintfFormatter<Char, AF>::get_arg(const Char *s,
} }
template <typename Char, typename AF> template <typename Char, typename AF>
unsigned PrintfFormatter<Char, AF>::parse_header( unsigned printf_context<Char, AF>::parse_header(
const Char *&s, FormatSpec &spec) { const Char *&s, FormatSpec &spec) {
unsigned arg_index = std::numeric_limits<unsigned>::max(); unsigned arg_index = std::numeric_limits<unsigned>::max();
Char c = *s; Char c = *s;
@ -392,9 +392,8 @@ unsigned PrintfFormatter<Char, AF>::parse_header(
} }
template <typename Char, typename AF> template <typename Char, typename AF>
void PrintfFormatter<Char, AF>::format(BasicWriter<Char> &writer, void printf_context<Char, AF>::format(BasicWriter<Char> &writer) {
BasicCStringRef<Char> format_str) { const Char *start = this->ptr();
const Char *start = format_str.c_str();
const Char *s = start; const Char *s = start;
while (*s) { while (*s) {
Char c = *s++; Char c = *s++;
@ -495,19 +494,19 @@ void PrintfFormatter<Char, AF>::format(BasicWriter<Char> &writer,
// Formats a value. // Formats a value.
template <typename Char, typename T> template <typename Char, typename T>
void format_value(BasicWriter<Char> &w, const T &value, void format_value(BasicWriter<Char> &w, const T &value,
PrintfFormatter<Char> &f, const Char *&) { printf_context<Char>& ctx) {
internal::MemoryBuffer<Char, internal::INLINE_BUFFER_SIZE> buffer; internal::MemoryBuffer<Char, internal::INLINE_BUFFER_SIZE> buffer;
w << internal::format_value(buffer, value); w << internal::format_value(buffer, value);
} }
template <typename Char> template <typename Char>
void printf(BasicWriter<Char> &w, BasicCStringRef<Char> format, void printf(BasicWriter<Char> &w, BasicCStringRef<Char> format,
basic_format_args<PrintfFormatter<Char>> args) { basic_format_args<printf_context<Char>> args) {
PrintfFormatter<Char>(args).format(w, format); printf_context<Char>(format, args).format(w);
} }
inline std::string vsprintf(CStringRef format, inline std::string vsprintf(CStringRef format,
basic_format_args<PrintfFormatter<char>> args) { basic_format_args<printf_context<char>> args) {
MemoryWriter w; MemoryWriter w;
printf(w, format, args); printf(w, format, args);
return w.str(); return w.str();
@ -524,11 +523,11 @@ inline std::string vsprintf(CStringRef format,
*/ */
template <typename... Args> template <typename... Args>
inline std::string sprintf(CStringRef format_str, const Args & ... args) { inline std::string sprintf(CStringRef format_str, const Args & ... args) {
return vsprintf(format_str, make_format_args<PrintfFormatter<char>>(args...)); return vsprintf(format_str, make_format_args<printf_context<char>>(args...));
} }
inline std::wstring vsprintf(WCStringRef format, inline std::wstring vsprintf(WCStringRef format,
basic_format_args<PrintfFormatter<wchar_t>> args) { basic_format_args<printf_context<wchar_t>> args) {
WMemoryWriter w; WMemoryWriter w;
printf(w, format, args); printf(w, format, args);
return w.str(); return w.str();
@ -536,12 +535,12 @@ inline std::wstring vsprintf(WCStringRef format,
template <typename... Args> template <typename... Args>
inline std::wstring sprintf(WCStringRef format_str, const Args & ... args) { inline std::wstring sprintf(WCStringRef format_str, const Args & ... args) {
auto vargs = make_format_args<PrintfFormatter<wchar_t>>(args...); auto vargs = make_format_args<printf_context<wchar_t>>(args...);
return vsprintf(format_str, vargs); return vsprintf(format_str, vargs);
} }
FMT_API int vfprintf(std::FILE *f, CStringRef format, FMT_API int vfprintf(std::FILE *f, CStringRef format,
basic_format_args<PrintfFormatter<char>> args); basic_format_args<printf_context<char>> args);
/** /**
\rst \rst
@ -554,12 +553,12 @@ FMT_API int vfprintf(std::FILE *f, CStringRef format,
*/ */
template <typename... Args> template <typename... Args>
inline int fprintf(std::FILE *f, CStringRef format_str, const Args & ... args) { inline int fprintf(std::FILE *f, CStringRef format_str, const Args & ... args) {
auto vargs = make_format_args<PrintfFormatter<char>>(args...); auto vargs = make_format_args<printf_context<char>>(args...);
return vfprintf(f, format_str, vargs); return vfprintf(f, format_str, vargs);
} }
inline int vprintf(CStringRef format, inline int vprintf(CStringRef format,
basic_format_args<PrintfFormatter<char>> args) { basic_format_args<printf_context<char>> args) {
return vfprintf(stdout, format, args); return vfprintf(stdout, format, args);
} }
@ -574,11 +573,11 @@ inline int vprintf(CStringRef format,
*/ */
template <typename... Args> template <typename... Args>
inline int printf(CStringRef format_str, const Args & ... args) { inline int printf(CStringRef format_str, const Args & ... args) {
return vprintf(format_str, make_format_args<PrintfFormatter<char>>(args...)); return vprintf(format_str, make_format_args<printf_context<char>>(args...));
} }
inline int vfprintf(std::ostream &os, CStringRef format_str, inline int vfprintf(std::ostream &os, CStringRef format_str,
basic_format_args<PrintfFormatter<char>> args) { basic_format_args<printf_context<char>> args) {
MemoryWriter w; MemoryWriter w;
printf(w, format_str, args); printf(w, format_str, args);
internal::write(os, w); internal::write(os, w);
@ -597,7 +596,7 @@ inline int vfprintf(std::ostream &os, CStringRef format_str,
template <typename... Args> template <typename... Args>
inline int fprintf(std::ostream &os, CStringRef format_str, inline int fprintf(std::ostream &os, CStringRef format_str,
const Args & ... args) { const Args & ... args) {
auto vargs = make_format_args<PrintfFormatter<char>>(args...); auto vargs = make_format_args<printf_context<char>>(args...);
return vfprintf(os, format_str, vargs); return vfprintf(os, format_str, vargs);
} }
} // namespace fmt } // namespace fmt

View File

@ -15,19 +15,17 @@
namespace fmt { namespace fmt {
template <typename ArgFormatter> void format_value(Writer &w, const std::tm &tm, format_context &ctx) {
void format_value(Writer &w, const std::tm &tm, const char *&s = ctx.ptr();
basic_formatter<char, ArgFormatter> &f, if (*s == ':')
const char *&format_str) { ++s;
if (*format_str == ':') const char *end = s;
++format_str;
const char *end = format_str;
while (*end && *end != '}') while (*end && *end != '}')
++end; ++end;
if (*end != '}') if (*end != '}')
FMT_THROW(format_error("missing '}' in format string")); FMT_THROW(format_error("missing '}' in format string"));
internal::MemoryBuffer<char, internal::INLINE_BUFFER_SIZE> format; internal::MemoryBuffer<char, internal::INLINE_BUFFER_SIZE> format;
format.append(format_str, end + 1); format.append(s, end + 1);
format[format.size() - 1] = '\0'; format[format.size() - 1] = '\0';
Buffer<char> &buffer = w.buffer(); Buffer<char> &buffer = w.buffer();
std::size_t start = buffer.size(); std::size_t start = buffer.size();
@ -48,7 +46,7 @@ void format_value(Writer &w, const std::tm &tm,
const std::size_t MIN_GROWTH = 10; const std::size_t MIN_GROWTH = 10;
buffer.reserve(buffer.capacity() + (size > MIN_GROWTH ? size : MIN_GROWTH)); buffer.reserve(buffer.capacity() + (size > MIN_GROWTH ? size : MIN_GROWTH));
} }
format_str = end + 1; s = end;
} }
} }

View File

@ -17,10 +17,9 @@ using fmt::BasicPrintfArgFormatter;
class CustomArgFormatter class CustomArgFormatter
: public fmt::BasicArgFormatter<CustomArgFormatter, char> { : public fmt::BasicArgFormatter<CustomArgFormatter, char> {
public: public:
CustomArgFormatter(fmt::Writer &w, CustomArgFormatter(fmt::Writer &w, fmt::basic_format_context<char> &ctx,
fmt::basic_formatter<char, CustomArgFormatter> &f, fmt::FormatSpec &s)
fmt::FormatSpec &s, const char *fmt) : fmt::BasicArgFormatter<CustomArgFormatter, char>(w, ctx, s) {}
: fmt::BasicArgFormatter<CustomArgFormatter, char>(w, f, s, fmt) {}
void visit_double(double value) { void visit_double(double value) {
if (round(value * pow(10, spec().precision())) == 0) if (round(value * pow(10, spec().precision())) == 0)
@ -46,10 +45,7 @@ class CustomPrintfArgFormatter :
} }
}; };
typedef fmt::basic_formatter<char, CustomArgFormatter> CustomFormatter; std::string custom_vformat(fmt::CStringRef format_str, fmt::format_args args) {
std::string custom_vformat(fmt::CStringRef format_str,
fmt::basic_format_args<CustomFormatter> args) {
fmt::MemoryWriter writer; fmt::MemoryWriter writer;
// Pass custom argument formatter as a template arg to vformat. // Pass custom argument formatter as a template arg to vformat.
fmt::vformat<CustomArgFormatter>(writer, format_str, args); fmt::vformat<CustomArgFormatter>(writer, format_str, args);
@ -58,19 +54,19 @@ std::string custom_vformat(fmt::CStringRef format_str,
template <typename... Args> template <typename... Args>
std::string custom_format(const char *format_str, const Args & ... args) { std::string custom_format(const char *format_str, const Args & ... args) {
auto va = fmt::make_format_args<CustomFormatter>(args...); auto va = fmt::make_format_args<fmt::format_context>(args...);
return custom_vformat(format_str, va); return custom_vformat(format_str, va);
} }
typedef fmt::PrintfFormatter<char, CustomPrintfArgFormatter> typedef fmt::printf_context<char, CustomPrintfArgFormatter>
CustomPrintfFormatter; CustomPrintfFormatter;
std::string custom_vsprintf( std::string custom_vsprintf(
const char* format_str, const char* format_str,
fmt::basic_format_args<CustomPrintfFormatter> args) { fmt::basic_format_args<CustomPrintfFormatter> args) {
fmt::MemoryWriter writer; fmt::MemoryWriter writer;
CustomPrintfFormatter formatter(args); CustomPrintfFormatter formatter(format_str, args);
formatter.format(writer, format_str); formatter.format(writer);
return writer.str(); return writer.str();
} }

View File

@ -1355,8 +1355,7 @@ TEST(FormatterTest, FormatCStringRef) {
EXPECT_EQ("test", format("{0}", CStringRef("test"))); EXPECT_EQ("test", format("{0}", CStringRef("test")));
} }
void format_value(fmt::Writer &w, const Date &d, fmt::basic_formatter<char> &f, void format_value(fmt::Writer &w, const Date &d, fmt::format_context &) {
const char *) {
w << d.year() << '-' << d.month() << '-' << d.day(); w << d.year() << '-' << d.month() << '-' << d.day();
} }
@ -1369,8 +1368,7 @@ TEST(FormatterTest, FormatCustom) {
class Answer {}; class Answer {};
template <typename Char> template <typename Char>
void format_value(BasicWriter<Char> &w, Answer, fmt::basic_formatter<Char> &f, void format_value(BasicWriter<Char> &w, Answer, fmt::format_context &) {
const Char *) {
w << "42"; w << "42";
} }
@ -1561,7 +1559,7 @@ std::string vformat_message(int id, const char *format, fmt::format_args args) {
template <typename... Args> template <typename... Args>
std::string format_message(int id, const char *format, const Args & ... args) { std::string format_message(int id, const char *format, const Args & ... args) {
auto va = fmt::make_format_args<fmt::basic_formatter<char>>(args...); auto va = fmt::make_format_args<fmt::format_context>(args...);
return vformat_message(id, format, va); return vformat_message(id, format, va);
} }
@ -1626,9 +1624,8 @@ class MockArgFormatter :
public: public:
typedef fmt::internal::ArgFormatterBase<MockArgFormatter, char> Base; typedef fmt::internal::ArgFormatterBase<MockArgFormatter, char> Base;
MockArgFormatter(fmt::Writer &w, MockArgFormatter(fmt::Writer &w, fmt::format_context &ctx,
fmt::basic_formatter<char, MockArgFormatter> &f, fmt::FormatSpec &s)
fmt::FormatSpec &s, const char *)
: fmt::internal::ArgFormatterBase<MockArgFormatter, char>(w, s) { : fmt::internal::ArgFormatterBase<MockArgFormatter, char>(w, s) {
EXPECT_CALL(*this, visit_int(42)); EXPECT_CALL(*this, visit_int(42));
} }
@ -1636,17 +1633,14 @@ class MockArgFormatter :
MOCK_METHOD1(visit_int, void (int value)); MOCK_METHOD1(visit_int, void (int value));
}; };
typedef fmt::basic_formatter<char, MockArgFormatter> CustomFormatter; void custom_vformat(fmt::CStringRef format_str, fmt::format_args args) {
void custom_vformat(fmt::CStringRef format_str,
fmt::basic_format_args<CustomFormatter> args) {
fmt::MemoryWriter writer; fmt::MemoryWriter writer;
vformat(writer, format_str, args); fmt::vformat<MockArgFormatter>(writer, format_str, args);
} }
template <typename... Args> template <typename... Args>
void custom_format(const char *format_str, const Args & ... args) { void custom_format(const char *format_str, const Args & ... args) {
auto va = fmt::make_format_args<CustomFormatter>(args...); auto va = fmt::make_format_args<fmt::format_context>(args...);
return custom_vformat(format_str, va); return custom_vformat(format_str, va);
} }

View File

@ -59,19 +59,17 @@ TEST(OStreamTest, Enum) {
} }
struct TestArgFormatter : fmt::BasicArgFormatter<TestArgFormatter, char> { struct TestArgFormatter : fmt::BasicArgFormatter<TestArgFormatter, char> {
TestArgFormatter(fmt::Writer &w, TestArgFormatter(fmt::Writer &w, fmt::format_context &ctx,
fmt::basic_formatter<char, TestArgFormatter> &f, fmt::FormatSpec &s)
fmt::FormatSpec &s, const char *fmt) : fmt::BasicArgFormatter<TestArgFormatter, char>(w, ctx, s) {}
: fmt::BasicArgFormatter<TestArgFormatter, char>(w, f, s, fmt) {}
}; };
TEST(OStreamTest, CustomArg) { TEST(OStreamTest, CustomArg) {
fmt::MemoryWriter writer; fmt::MemoryWriter writer;
typedef fmt::basic_formatter<char, TestArgFormatter> Formatter; fmt::format_context ctx("}", fmt::format_args());
Formatter formatter((fmt::basic_format_args<Formatter>()));
fmt::FormatSpec spec; fmt::FormatSpec spec;
TestArgFormatter af(writer, formatter, spec, "}"); TestArgFormatter af(writer, ctx, spec);
af.visit(fmt::internal::MakeArg<Formatter>(TestEnum())); af.visit(fmt::internal::MakeArg<fmt::format_context>(TestEnum()));
EXPECT_EQ("TestEnum", writer.str()); EXPECT_EQ("TestEnum", writer.str());
} }

View File

@ -65,13 +65,13 @@ struct Test {};
template <typename Char> template <typename Char>
void format_value(fmt::BasicWriter<Char> &w, Test, void format_value(fmt::BasicWriter<Char> &w, Test,
fmt::basic_formatter<Char> &f, const Char *) { fmt::basic_format_context<Char> &) {
w << "test"; w << "test";
} }
template <typename Char, typename T> template <typename Char, typename T>
Arg make_arg(const T &value) { Arg make_arg(const T &value) {
typedef fmt::internal::MakeValue< fmt::basic_formatter<Char> > MakeValue; typedef fmt::internal::MakeValue< fmt::basic_format_context<Char> > MakeValue;
Arg arg = MakeValue(value); Arg arg = MakeValue(value);
arg.type = fmt::internal::type<T>(); arg.type = fmt::internal::type<T>();
return arg; return arg;
@ -567,9 +567,8 @@ TEST(ArgTest, MakeArg) {
EXPECT_EQ(fmt::internal::Arg::CUSTOM, arg.type); EXPECT_EQ(fmt::internal::Arg::CUSTOM, arg.type);
EXPECT_EQ(&t, arg.custom.value); EXPECT_EQ(&t, arg.custom.value);
fmt::MemoryWriter w; fmt::MemoryWriter w;
fmt::basic_formatter<char> formatter((fmt::format_args())); fmt::format_context ctx("}", fmt::format_args());
const char *s = "}"; arg.custom.format(&w, &t, &ctx);
arg.custom.format(&w, &formatter, &t, &s);
EXPECT_EQ("test", w.str()); EXPECT_EQ("test", w.str());
} }
@ -580,21 +579,20 @@ TEST(UtilTest, FormatArgs) {
struct CustomFormatter { struct CustomFormatter {
typedef char char_type; typedef char char_type;
bool called;
}; };
void format_value(fmt::Writer &, const Test &, CustomFormatter &, void format_value(fmt::Writer &, const Test &, CustomFormatter &ctx) {
const char *&s) { ctx.called = true;
s = "custom_format";
} }
TEST(UtilTest, MakeValueWithCustomFormatter) { TEST(UtilTest, MakeValueWithCustomFormatter) {
::Test t; ::Test t;
Arg arg = fmt::internal::MakeValue<CustomFormatter>(t); Arg arg = fmt::internal::MakeValue<CustomFormatter>(t);
CustomFormatter formatter; CustomFormatter ctx = {false};
const char *s = "";
fmt::MemoryWriter w; fmt::MemoryWriter w;
arg.custom.format(&w, &formatter, &t, &s); arg.custom.format(&w, &t, &ctx);
EXPECT_STREQ("custom_format", s); EXPECT_TRUE(ctx.called);
} }
struct Result { struct Result {