Support named arguments (WIP)

This commit is contained in:
jamboree 2015-06-05 18:06:29 +08:00
parent a5e7d08ad0
commit 344de54aa9
5 changed files with 234 additions and 62 deletions

View File

@ -15,13 +15,15 @@ literal text, it can be escaped by doubling: ``{{`` and ``}}``.
The grammar for a replacement field is as follows: The grammar for a replacement field is as follows:
.. productionlist:: sf .. productionlist:: sf
replacement_field: "{" [`arg_index`] [":" `format_spec`] "}" replacement_field: "{" [`arg_field`] [":" `format_spec`] "}"
arg_field: `arg_index` | `arg_name`
arg_index: `integer` arg_index: `integer`
arg_name: \^[a-zA-Z_][a-zA-Z0-9_]*$\
In less formal terms, the replacement field can start with an *arg_index* In less formal terms, the replacement field can start with an *arg_field*
that specifies the argument whose value is to be formatted and inserted into that specifies the argument whose value is to be formatted and inserted into
the output instead of the replacement field. the output instead of the replacement field.
The *arg_index* is optionally followed by a *format_spec*, which is preceded The *arg_field* is optionally followed by a *format_spec*, which is preceded
by a colon ``':'``. These specify a non-default format for the replacement value. by a colon ``':'``. These specify a non-default format for the replacement value.
See also the :ref:`formatspec` section. See also the :ref:`formatspec` section.
@ -73,8 +75,8 @@ The general form of a *standard format specifier* is:
fill: <a character other than '{' or '}'> fill: <a character other than '{' or '}'>
align: "<" | ">" | "=" | "^" align: "<" | ">" | "=" | "^"
sign: "+" | "-" | " " sign: "+" | "-" | " "
width: `integer` | "{" `arg_index` "}" width: `integer` | "{" `arg_field` "}"
precision: `integer` | "{" `arg_index` "}" precision: `integer` | "{" `arg_field` "}"
type: `int_type` | "c" | "e" | "E" | "f" | "F" | "g" | "G" | "p" | "s" type: `int_type` | "c" | "e" | "E" | "f" | "F" | "g" | "G" | "p" | "s"
int_type: "b" | "B" | "d" | "o" | "x" | "X" int_type: "b" | "B" | "d" | "o" | "x" | "X"

View File

@ -265,6 +265,12 @@ int parse_nonnegative_int(const Char *&s) {
return value; return value;
} }
template <typename Char>
inline bool is_name_start(Char c)
{
return 'a' <= c && c <= 'z' || 'A' <= c && c <= 'Z' || '_' == c;
}
inline void require_numeric_argument(const Arg &arg, char spec) { inline void require_numeric_argument(const Arg &arg, char spec) {
if (arg.type > Arg::LAST_NUMERIC_TYPE) { if (arg.type > Arg::LAST_NUMERIC_TYPE) {
std::string message = std::string message =
@ -402,10 +408,10 @@ inline Arg::StringValue<wchar_t> ignore_incompatible_str(
} // namespace } // namespace
FMT_FUNC void fmt::SystemError::init( FMT_FUNC void fmt::SystemError::init(
int err_code, StringRef format_str, ArgList args) { int err_code, StringRef format_str, ArgList args, const ArgMap &map) {
error_code_ = err_code; error_code_ = err_code;
MemoryWriter w; MemoryWriter w;
internal::format_system_error(w, err_code, format(format_str, args)); internal::format_system_error(w, err_code, format(format_str, args, map));
std::runtime_error &base = *this; std::runtime_error &base = *this;
base = std::runtime_error(w.str()); base = std::runtime_error(w.str());
} }
@ -518,10 +524,10 @@ FMT_FUNC int fmt::internal::UTF16ToUTF8::convert(fmt::WStringRef s) {
} }
FMT_FUNC void fmt::WindowsError::init( FMT_FUNC void fmt::WindowsError::init(
int err_code, StringRef format_str, ArgList args) { int err_code, StringRef format_str, ArgList args, const ArgMap &map) {
error_code_ = err_code; error_code_ = err_code;
MemoryWriter w; MemoryWriter w;
internal::format_windows_error(w, err_code, format(format_str, args)); internal::format_windows_error(w, err_code, format(format_str, args, map));
std::runtime_error &base = *this; std::runtime_error &base = *this;
base = std::runtime_error(w.str()); base = std::runtime_error(w.str());
} }
@ -693,6 +699,24 @@ inline Arg fmt::BasicFormatter<Char>::parse_arg_index(const Char *&s) {
return arg; return arg;
} }
template <typename Char>
inline Arg fmt::BasicFormatter<Char>::parse_arg_name(const Char *&s, const fmt::BasicArgMap<Char> &map) {
assert(is_name_start(*s));
const Char *start = s;
Char c;
do {
c = *++s;
} while ('a' <= c && c <= 'z' || 'A' <= c && c <= 'Z' || '0' <= c && c <= '9');
const int* index = map.find(fmt::BasicStringRef<Char>(start, s - start));
if (!index)
FMT_THROW(fmt::FormatError("argument not found"));
const char *error = 0;
Arg arg = get_arg(*index, error);
if (error)
FMT_THROW(fmt::FormatError(error));
return arg;
}
FMT_FUNC Arg fmt::internal::FormatterBase::do_get_arg( FMT_FUNC Arg fmt::internal::FormatterBase::do_get_arg(
unsigned arg_index, const char *&error) { unsigned arg_index, const char *&error) {
Arg arg = args_[arg_index]; Arg arg = args_[arg_index];
@ -960,7 +984,7 @@ void fmt::internal::PrintfFormatter<Char>::format(
template <typename Char> template <typename Char>
const Char *fmt::BasicFormatter<Char>::format( const Char *fmt::BasicFormatter<Char>::format(
const Char *&format_str, const Arg &arg) { const Char *&format_str, const Arg &arg, const BasicArgMap<Char> &map) {
const Char *s = format_str; const Char *s = format_str;
FormatSpec spec; FormatSpec spec;
if (*s == ':') { if (*s == ':') {
@ -1038,7 +1062,7 @@ const Char *fmt::BasicFormatter<Char>::format(
spec.width_ = parse_nonnegative_int(s); spec.width_ = parse_nonnegative_int(s);
} else if (*s == '{') { } else if (*s == '{') {
++s; ++s;
const Arg &width_arg = parse_arg_index(s); const Arg &width_arg = is_name_start(*s) ? parse_arg_name(s, map) : parse_arg_index(s);
if (*s++ != '}') if (*s++ != '}')
FMT_THROW(FormatError("invalid format string")); FMT_THROW(FormatError("invalid format string"));
ULongLong value = 0; ULongLong value = 0;
@ -1075,7 +1099,7 @@ const Char *fmt::BasicFormatter<Char>::format(
spec.precision_ = parse_nonnegative_int(s); spec.precision_ = parse_nonnegative_int(s);
} else if (*s == '{') { } else if (*s == '{') {
++s; ++s;
const Arg &precision_arg = parse_arg_index(s); const Arg &precision_arg = is_name_start(*s) ? parse_arg_name(s, map) : parse_arg_index(s);
if (*s++ != '}') if (*s++ != '}')
FMT_THROW(FormatError("invalid format string")); FMT_THROW(FormatError("invalid format string"));
ULongLong value = 0; ULongLong value = 0;
@ -1128,7 +1152,7 @@ const Char *fmt::BasicFormatter<Char>::format(
template <typename Char> template <typename Char>
void fmt::BasicFormatter<Char>::format( void fmt::BasicFormatter<Char>::format(
BasicStringRef<Char> format_str, const ArgList &args) { BasicStringRef<Char> format_str, const ArgList &args, const BasicArgMap<Char> &map) {
const Char *s = start_ = format_str.c_str(); const Char *s = start_ = format_str.c_str();
set_args(args); set_args(args);
while (*s) { while (*s) {
@ -1142,8 +1166,8 @@ void fmt::BasicFormatter<Char>::format(
if (c == '}') if (c == '}')
FMT_THROW(FormatError("unmatched '}' in format string")); FMT_THROW(FormatError("unmatched '}' in format string"));
write(writer_, start_, s - 1); write(writer_, start_, s - 1);
Arg arg = parse_arg_index(s); Arg arg = is_name_start(*s) ? parse_arg_name(s, map) : parse_arg_index(s);
s = format(s, arg); s = format(s, arg, map);
} }
write(writer_, start_, s); write(writer_, start_, s);
} }
@ -1160,33 +1184,33 @@ FMT_FUNC void fmt::report_windows_error(
} }
#endif #endif
FMT_FUNC void fmt::print(std::FILE *f, StringRef format_str, ArgList args) { FMT_FUNC void fmt::print(std::FILE *f, StringRef format_str, ArgList args, const ArgMap &map) {
MemoryWriter w; MemoryWriter w;
w.write(format_str, args); w.write(format_str, args, map);
std::fwrite(w.data(), 1, w.size(), f); std::fwrite(w.data(), 1, w.size(), f);
} }
FMT_FUNC void fmt::print(StringRef format_str, ArgList args) { FMT_FUNC void fmt::print(StringRef format_str, ArgList args, const ArgMap &map) {
print(stdout, format_str, args); print(stdout, format_str, args, map);
} }
FMT_FUNC void fmt::print(std::ostream &os, StringRef format_str, ArgList args) { FMT_FUNC void fmt::print(std::ostream &os, StringRef format_str, ArgList args, const ArgMap &map) {
MemoryWriter w; MemoryWriter w;
w.write(format_str, args); w.write(format_str, args, map);
os.write(w.data(), w.size()); os.write(w.data(), w.size());
} }
FMT_FUNC void fmt::print_colored(Color c, StringRef format, ArgList args) { FMT_FUNC void fmt::print_colored(Color c, StringRef format, ArgList args, const ArgMap &map) {
char escape[] = "\x1b[30m"; char escape[] = "\x1b[30m";
escape[3] = '0' + static_cast<char>(c); escape[3] = '0' + static_cast<char>(c);
std::fputs(escape, stdout); std::fputs(escape, stdout);
print(format, args); print(format, args, map);
std::fputs(RESET_COLOR, stdout); std::fputs(RESET_COLOR, stdout);
} }
FMT_FUNC int fmt::fprintf(std::FILE *f, StringRef format, ArgList args) { FMT_FUNC int fmt::fprintf(std::FILE *f, StringRef format, ArgList args, const ArgMap &map) {
MemoryWriter w; MemoryWriter w;
printf(w, format, args); printf(w, format, args, map);
std::size_t size = w.size(); std::size_t size = w.size();
return std::fwrite(w.data(), 1, size, f) < size ? -1 : static_cast<int>(size); return std::fwrite(w.data(), 1, size, f) < size ? -1 : static_cast<int>(size);
} }
@ -1200,10 +1224,10 @@ template struct fmt::internal::BasicData<void>;
template void fmt::internal::FixedBuffer<char>::grow(std::size_t); template void fmt::internal::FixedBuffer<char>::grow(std::size_t);
template const char *fmt::BasicFormatter<char>::format( template const char *fmt::BasicFormatter<char>::format(
const char *&format_str, const fmt::internal::Arg &arg); const char *&format_str, const fmt::internal::Arg &arg, const BasicArgMap<char> &map);
template void fmt::BasicFormatter<char>::format( template void fmt::BasicFormatter<char>::format(
BasicStringRef<char> format, const ArgList &args); BasicStringRef<char> format, const ArgList &args, const BasicArgMap<char> &map);
template void fmt::internal::PrintfFormatter<char>::format( template void fmt::internal::PrintfFormatter<char>::format(
BasicWriter<char> &writer, BasicStringRef<char> format, const ArgList &args); BasicWriter<char> &writer, BasicStringRef<char> format, const ArgList &args);
@ -1221,10 +1245,10 @@ template int fmt::internal::CharTraits<char>::format_float(
template void fmt::internal::FixedBuffer<wchar_t>::grow(std::size_t); template void fmt::internal::FixedBuffer<wchar_t>::grow(std::size_t);
template const wchar_t *fmt::BasicFormatter<wchar_t>::format( template const wchar_t *fmt::BasicFormatter<wchar_t>::format(
const wchar_t *&format_str, const fmt::internal::Arg &arg); const wchar_t *&format_str, const fmt::internal::Arg &arg, const BasicArgMap<wchar_t> &map);
template void fmt::BasicFormatter<wchar_t>::format( template void fmt::BasicFormatter<wchar_t>::format(
BasicStringRef<wchar_t> format, const ArgList &args); BasicStringRef<wchar_t> format, const ArgList &args, const BasicArgMap<wchar_t> &map);
template void fmt::internal::PrintfFormatter<wchar_t>::format( template void fmt::internal::PrintfFormatter<wchar_t>::format(
BasicWriter<wchar_t> &writer, BasicStringRef<wchar_t> format, BasicWriter<wchar_t> &writer, BasicStringRef<wchar_t> format,

184
format.h
View File

@ -227,6 +227,11 @@ class BasicStringRef {
std::size_t size_; std::size_t size_;
public: public:
/**
Constructs an empty string reference object.
*/
BasicStringRef() : data_(), size_() {}
/** /**
Constructs a string reference object from a C string and a size. Constructs a string reference object from a C string and a size.
*/ */
@ -274,6 +279,9 @@ class BasicStringRef {
friend bool operator!=(BasicStringRef lhs, BasicStringRef rhs) { friend bool operator!=(BasicStringRef lhs, BasicStringRef rhs) {
return lhs.data_ != rhs.data_; return lhs.data_ != rhs.data_;
} }
friend bool operator<(BasicStringRef lhs, BasicStringRef rhs) {
return std::lexicographical_compare(lhs.data_, lhs.data_ + lhs.size_, rhs.data_, rhs.data_ + rhs.size_);
}
}; };
typedef BasicStringRef<char> StringRef; typedef BasicStringRef<char> StringRef;
@ -759,6 +767,15 @@ struct Value {
}; };
}; };
template <typename Char, typename T>
struct NamedArg
{
BasicStringRef<Char> name;
T const& arg;
NamedArg(BasicStringRef<Char> name, T const& arg) : name(name), arg(arg) {}
};
// A formatting argument. It is a POD type to allow storage in // A formatting argument. It is a POD type to allow storage in
// internal::MemoryBuffer. // internal::MemoryBuffer.
struct Arg : Value { struct Arg : Value {
@ -962,6 +979,50 @@ class MakeValue : public Arg {
} }
}; };
template <typename T>
inline const T &strip_name(const T &arg)
{
return arg;
}
template <typename Char, typename T>
inline const T &strip_name(const NamedArg<Char, T> &namedArg)
{
return namedArg.arg;
}
template <typename... T>
struct NamedArgsCounter
{
static const int value = 0;
};
template <typename T, typename... U>
struct NamedArgsCounter<T, U...> : NamedArgsCounter<U...>
{};
template <typename Char, typename T, typename... U>
struct NamedArgsCounter<NamedArg<Char, T>, U...>
{
static const int value = 1 + NamedArgsCounter<U...>::value;
};
template <int N, typename NameIndexPair>
inline void add_named_args(NameIndexPair*) {}
template <int N, typename NameIndexPair, typename T, typename... U>
inline void add_named_args(NameIndexPair* map, T const&, U const&... rest)
{
add_named_args<N + 1>(map, rest...);
}
template <int N, typename NameIndexPair, typename Char, typename T, typename... U>
inline void add_named_args(NameIndexPair* map, const NamedArg<Char, T> &namedArg, U const&... rest)
{
*map = NameIndexPair(namedArg.name, N);
add_named_args<N + 1>(map + 1, rest...);
}
#define FMT_DISPATCH(call) static_cast<Impl*>(this)->call #define FMT_DISPATCH(call) static_cast<Impl*>(this)->call
// An argument visitor. // An argument visitor.
@ -1146,6 +1207,48 @@ class ArgList {
struct FormatSpec; struct FormatSpec;
template <typename Char>
struct BasicArgMap
{
typedef std::pair<BasicStringRef<Char>, int> value_type;
BasicArgMap() : map_(), size_() {}
BasicArgMap(value_type* map, int size)
: map_(map), size_(size)
{
std::sort(map, map + size, [](const value_type &lhs, const value_type &rhs)
{
return lhs.first < rhs.first;
});
}
const int* find(BasicStringRef<Char> name) const
{
value_type* first = map_;
value_type* last = map_ + size_;
while (first != last)
{
value_type* it(first + (last - first >> 1));
if (name < it->first)
last = it;
else if (it->first < name)
first = ++it;
else
return &it->second;
}
return nullptr;
}
private:
value_type* map_;
int size_;
};
typedef BasicArgMap<char> ArgMap;
typedef BasicArgMap<wchar_t> WArgMap;
namespace internal { namespace internal {
class FormatterBase { class FormatterBase {
@ -1208,14 +1311,17 @@ class BasicFormatter : private internal::FormatterBase {
// Parses argument index and returns corresponding argument. // Parses argument index and returns corresponding argument.
internal::Arg parse_arg_index(const Char *&s); internal::Arg parse_arg_index(const Char *&s);
// Parses argument name and returns corresponding argument.
internal::Arg parse_arg_name(const Char *&s, const BasicArgMap<Char> &map);
public: public:
explicit BasicFormatter(BasicWriter<Char> &w) : writer_(w) {} explicit BasicFormatter(BasicWriter<Char> &w) : writer_(w) {}
BasicWriter<Char> &writer() { return writer_; } BasicWriter<Char> &writer() { return writer_; }
void format(BasicStringRef<Char> format_str, const ArgList &args); void format(BasicStringRef<Char> format_str, const ArgList &args, const BasicArgMap<Char> &map);
const Char *format(const Char *&format_str, const internal::Arg &arg); const Char *format(const Char *&format_str, const internal::Arg &arg, const BasicArgMap<Char> &map);
}; };
enum Alignment { enum Alignment {
@ -1556,7 +1662,11 @@ inline uint64_t make_type(FMT_GEN15(FMT_ARG_TYPE_DEFAULT)) {
template <typename... Args> \ template <typename... Args> \
void func(arg_type arg0, const Args & ... args) { \ void func(arg_type arg0, const Args & ... args) { \
typename fmt::internal::ArgArray<sizeof...(Args)>::Type array; \ typename fmt::internal::ArgArray<sizeof...(Args)>::Type array; \
func(arg0, fmt::internal::make_arg_list<Char>(array, args...)); \ const int count = fmt::internal::NamedArgsCounter<Args...>::value; \
fmt::BasicArgMap<Char>::value_type mapArray[count + 1]; \
fmt::internal::add_named_args<0>(mapArray, args...); \
fmt::BasicArgMap<Char> map(mapArray, count); \
func(arg0, fmt::internal::make_arg_list<Char>(array, fmt::internal::strip_name(args)...), map); \
} }
// Defines a variadic constructor. // Defines a variadic constructor.
@ -1564,7 +1674,11 @@ inline uint64_t make_type(FMT_GEN15(FMT_ARG_TYPE_DEFAULT)) {
template <typename... Args> \ template <typename... Args> \
ctor(arg0_type arg0, arg1_type arg1, const Args & ... args) { \ ctor(arg0_type arg0, arg1_type arg1, const Args & ... args) { \
typename fmt::internal::ArgArray<sizeof...(Args)>::Type array; \ typename fmt::internal::ArgArray<sizeof...(Args)>::Type array; \
func(arg0, arg1, fmt::internal::make_arg_list<Char>(array, args...)); \ const int count = fmt::internal::NamedArgsCounter<Args...>::value; \
fmt::BasicArgMap<Char>::value_type mapArray[count + 1]; \
fmt::internal::add_named_args<0>(mapArray, args...); \
fmt::BasicArgMap<Char> map(mapArray, count); \
func(arg0, arg1, fmt::internal::make_arg_list<Char>(array, fmt::internal::strip_name(args)...), map); \
} }
#else #else
@ -1641,7 +1755,7 @@ inline uint64_t make_type(FMT_GEN15(FMT_ARG_TYPE_DEFAULT)) {
*/ */
class SystemError : public internal::RuntimeError { class SystemError : public internal::RuntimeError {
private: private:
void init(int err_code, StringRef format_str, ArgList args); void init(int err_code, StringRef format_str, ArgList args, const ArgMap &map);
protected: protected:
int error_code_; int error_code_;
@ -1677,7 +1791,7 @@ class SystemError : public internal::RuntimeError {
\endrst \endrst
*/ */
SystemError(int error_code, StringRef message) { SystemError(int error_code, StringRef message) {
init(error_code, message, ArgList()); init(error_code, message, ArgList(), ArgMap());
} }
FMT_VARIADIC_CTOR(SystemError, init, int, StringRef) FMT_VARIADIC_CTOR(SystemError, init, int, StringRef)
@ -1852,8 +1966,8 @@ class BasicWriter {
See also :ref:`syntax`. See also :ref:`syntax`.
\endrst \endrst
*/ */
void write(BasicStringRef<Char> format, ArgList args) { void write(BasicStringRef<Char> format, ArgList args, const BasicArgMap<Char> &map) {
BasicFormatter<Char>(*this).format(format, args); BasicFormatter<Char>(*this).format(format, args, map);
} }
FMT_VARIADIC_VOID(write, BasicStringRef<Char>) FMT_VARIADIC_VOID(write, BasicStringRef<Char>)
@ -2401,7 +2515,7 @@ void format(BasicFormatter<Char> &f, const Char *&format_str, const T &value) {
internal::Arg arg = internal::MakeValue<Char>(str); internal::Arg arg = internal::MakeValue<Char>(str);
arg.type = static_cast<internal::Arg::Type>( arg.type = static_cast<internal::Arg::Type>(
internal::MakeValue<Char>::type(str)); internal::MakeValue<Char>::type(str));
format_str = f.format(format_str, arg); format_str = f.format(format_str, arg, BasicArgMap<Char>());
} }
// Reports a system error without throwing an exception. // Reports a system error without throwing an exception.
@ -2413,7 +2527,7 @@ void report_system_error(int error_code, StringRef message) FMT_NOEXCEPT;
/** A Windows error. */ /** A Windows error. */
class WindowsError : public SystemError { class WindowsError : public SystemError {
private: private:
void init(int error_code, StringRef format_str, ArgList args); void init(int error_code, StringRef format_str, ArgList args, const ArgMap &map);
public: public:
/** /**
@ -2445,7 +2559,7 @@ class WindowsError : public SystemError {
\endrst \endrst
*/ */
WindowsError(int error_code, StringRef message) { WindowsError(int error_code, StringRef message) {
init(error_code, message, ArgList()); init(error_code, message, ArgList(), ArgMap());
} }
FMT_VARIADIC_CTOR(WindowsError, init, int, StringRef) FMT_VARIADIC_CTOR(WindowsError, init, int, StringRef)
}; };
@ -2464,7 +2578,7 @@ enum Color { BLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE };
Example: Example:
PrintColored(fmt::RED, "Elapsed time: {0:.2f} seconds") << 1.23; PrintColored(fmt::RED, "Elapsed time: {0:.2f} seconds") << 1.23;
*/ */
void print_colored(Color c, StringRef format, ArgList args); void print_colored(Color c, StringRef format, ArgList args, const ArgMap &map);
/** /**
\rst \rst
@ -2475,15 +2589,15 @@ void print_colored(Color c, StringRef format, ArgList args);
std::string message = format("The answer is {}", 42); std::string message = format("The answer is {}", 42);
\endrst \endrst
*/ */
inline std::string format(StringRef format_str, ArgList args) { inline std::string format(StringRef format_str, ArgList args, const ArgMap &map) {
MemoryWriter w; MemoryWriter w;
w.write(format_str, args); w.write(format_str, args, map);
return w.str(); return w.str();
} }
inline std::wstring format(WStringRef format_str, ArgList args) { inline std::wstring format(WStringRef format_str, ArgList args, const WArgMap &map) {
WMemoryWriter w; WMemoryWriter w;
w.write(format_str, args); w.write(format_str, args, map);
return w.str(); return w.str();
} }
@ -2496,7 +2610,7 @@ inline std::wstring format(WStringRef format_str, ArgList args) {
print(stderr, "Don't {}!", "panic"); print(stderr, "Don't {}!", "panic");
\endrst \endrst
*/ */
void print(std::FILE *f, StringRef format_str, ArgList args); void print(std::FILE *f, StringRef format_str, ArgList args, const ArgMap &map);
/** /**
\rst \rst
@ -2507,7 +2621,7 @@ void print(std::FILE *f, StringRef format_str, ArgList args);
print("Elapsed time: {0:.2f} seconds", 1.23); print("Elapsed time: {0:.2f} seconds", 1.23);
\endrst \endrst
*/ */
void print(StringRef format_str, ArgList args); void print(StringRef format_str, ArgList args, const ArgMap &map);
/** /**
\rst \rst
@ -2518,10 +2632,10 @@ void print(StringRef format_str, ArgList args);
print(cerr, "Don't {}!", "panic"); print(cerr, "Don't {}!", "panic");
\endrst \endrst
*/ */
void print(std::ostream &os, StringRef format_str, ArgList args); void print(std::ostream &os, StringRef format_str, ArgList args, const ArgMap &map);
template <typename Char> template <typename Char>
void printf(BasicWriter<Char> &w, BasicStringRef<Char> format, ArgList args) { void printf(BasicWriter<Char> &w, BasicStringRef<Char> format, ArgList args, const ArgMap &) {
internal::PrintfFormatter<Char>().format(w, format, args); internal::PrintfFormatter<Char>().format(w, format, args);
} }
@ -2534,9 +2648,9 @@ void printf(BasicWriter<Char> &w, BasicStringRef<Char> format, ArgList args) {
std::string message = fmt::sprintf("The answer is %d", 42); std::string message = fmt::sprintf("The answer is %d", 42);
\endrst \endrst
*/ */
inline std::string sprintf(StringRef format, ArgList args) { inline std::string sprintf(StringRef format, ArgList args, const ArgMap &map) {
MemoryWriter w; MemoryWriter w;
printf(w, format, args); printf(w, format, args, map);
return w.str(); return w.str();
} }
@ -2549,7 +2663,7 @@ inline std::string sprintf(StringRef format, ArgList args) {
fmt::fprintf(stderr, "Don't %s!", "panic"); fmt::fprintf(stderr, "Don't %s!", "panic");
\endrst \endrst
*/ */
int fprintf(std::FILE *f, StringRef format, ArgList args); int fprintf(std::FILE *f, StringRef format, ArgList args, const ArgMap &);
/** /**
\rst \rst
@ -2560,8 +2674,8 @@ int fprintf(std::FILE *f, StringRef format, ArgList args);
fmt::printf("Elapsed time: %.2f seconds", 1.23); fmt::printf("Elapsed time: %.2f seconds", 1.23);
\endrst \endrst
*/ */
inline int printf(StringRef format, ArgList args) { inline int printf(StringRef format, ArgList args, const ArgMap &map) {
return fprintf(stdout, format, args); return fprintf(stdout, format, args, map);
} }
/** /**
@ -2667,6 +2781,16 @@ inline void format_decimal(char *&buffer, T value) {
internal::format_decimal(buffer, abs_value, num_digits); internal::format_decimal(buffer, abs_value, num_digits);
buffer += num_digits; buffer += num_digits;
} }
template <typename T>
inline internal::NamedArg<char, T> arg(StringRef name, T const& arg) {
return internal::NamedArg<char, T>(name, arg);
}
template <typename T>
inline internal::NamedArg<wchar_t, T> arg(WStringRef name, T const& arg) {
return internal::NamedArg<wchar_t, T>(name, arg);
}
} }
#if FMT_GCC_VERSION #if FMT_GCC_VERSION
@ -2702,8 +2826,12 @@ inline void format_decimal(char *&buffer, T value) {
ReturnType func(FMT_FOR_EACH(FMT_ADD_ARG_NAME, __VA_ARGS__), \ ReturnType func(FMT_FOR_EACH(FMT_ADD_ARG_NAME, __VA_ARGS__), \
const Args & ... args) { \ const Args & ... args) { \
typename fmt::internal::ArgArray<sizeof...(Args)>::Type array; \ typename fmt::internal::ArgArray<sizeof...(Args)>::Type array; \
const int count = fmt::internal::NamedArgsCounter<Args...>::value; \
fmt::BasicArgMap<Char>::value_type mapArray[count + 1]; \
fmt::internal::add_named_args<0>(mapArray, args...); \
fmt::BasicArgMap<Char> map(mapArray, count); \
call(FMT_FOR_EACH(FMT_GET_ARG_NAME, __VA_ARGS__), \ call(FMT_FOR_EACH(FMT_GET_ARG_NAME, __VA_ARGS__), \
fmt::internal::make_arg_list<Char>(array, args...)); \ fmt::internal::make_arg_list<Char>(array, fmt::internal::strip_name(args)...), map); \
} }
#else #else
// Defines a wrapper for a function taking __VA_ARGS__ arguments // Defines a wrapper for a function taking __VA_ARGS__ arguments
@ -2771,6 +2899,10 @@ inline void format_decimal(char *&buffer, T value) {
#define FMT_VARIADIC_W(ReturnType, func, ...) \ #define FMT_VARIADIC_W(ReturnType, func, ...) \
FMT_VARIADIC_(wchar_t, ReturnType, func, return func, __VA_ARGS__) FMT_VARIADIC_(wchar_t, ReturnType, func, return func, __VA_ARGS__)
#define FMT_CAPTURE_ARG_(id, index) ::fmt::arg(#id, id)
#define FMT_CAPTURE(...) FMT_FOR_EACH(FMT_CAPTURE_ARG_, __VA_ARGS__)
namespace fmt { namespace fmt {
FMT_VARIADIC(std::string, format, StringRef) FMT_VARIADIC(std::string, format, StringRef)
FMT_VARIADIC_W(std::wstring, format, WStringRef) FMT_VARIADIC_W(std::wstring, format, WStringRef)

View File

@ -197,8 +197,8 @@ public:
// of MinGW that define fileno as a macro. // of MinGW that define fileno as a macro.
int (fileno)() const; int (fileno)() const;
void print(fmt::StringRef format_str, const ArgList &args) { void print(fmt::StringRef format_str, const ArgList &args, const ArgMap &map) {
fmt::print(file_, format_str, args); fmt::print(file_, format_str, args, map);
} }
FMT_VARIADIC(void, print, fmt::StringRef) FMT_VARIADIC(void, print, fmt::StringRef)
}; };

View File

@ -561,7 +561,7 @@ TEST(FormatterTest, ArgsInDifferentPositions) {
TEST(FormatterTest, ArgErrors) { TEST(FormatterTest, ArgErrors) {
EXPECT_THROW_MSG(format("{"), FormatError, "invalid format string"); EXPECT_THROW_MSG(format("{"), FormatError, "invalid format string");
EXPECT_THROW_MSG(format("{x}"), FormatError, "invalid format string"); EXPECT_THROW_MSG(format("{?}"), FormatError, "invalid format string");
EXPECT_THROW_MSG(format("{0"), FormatError, "invalid format string"); EXPECT_THROW_MSG(format("{0"), FormatError, "invalid format string");
EXPECT_THROW_MSG(format("{0}"), FormatError, "argument index out of range"); EXPECT_THROW_MSG(format("{0}"), FormatError, "argument index out of range");
@ -606,6 +606,20 @@ TEST(FormatterTest, ManyArgs) {
EXPECT_THROW_MSG(TestFormat<MAX_PACKED_ARGS>::format(format_str), EXPECT_THROW_MSG(TestFormat<MAX_PACKED_ARGS>::format(format_str),
FormatError, "argument index out of range"); FormatError, "argument index out of range");
} }
TEST(FormatterTest, NamedArg) {
char a = 'A', b = 'B', c = 'C';
EXPECT_EQ("BBAACC", format("{1}{b}{0}{a}{2}{c}", FMT_CAPTURE(a, b, c)));
EXPECT_EQ(" A", format("{a:>2}", FMT_CAPTURE(a)));
EXPECT_THROW_MSG(format("{a+}", FMT_CAPTURE(a)), FormatError, "missing '}' in format string");
EXPECT_THROW_MSG(format("{d}", FMT_CAPTURE(a, b, c)), FormatError, "argument not found");
EXPECT_THROW_MSG(format("{a}{}", FMT_CAPTURE(a)),
FormatError, "cannot switch from manual to automatic argument indexing");
EXPECT_THROW_MSG(format("{}{a}", FMT_CAPTURE(a)),
FormatError, "cannot switch from automatic to manual argument indexing");
EXPECT_EQ(" -42", format("{0:{width}}", -42, fmt::arg("width", 4)));
EXPECT_EQ("st", format("{0:.{precision}}", "str", fmt::arg("precision", 2)));
}
#endif #endif
TEST(FormatterTest, AutoArgIndex) { TEST(FormatterTest, AutoArgIndex) {
@ -919,7 +933,7 @@ TEST(FormatterTest, RuntimeWidth) {
FormatError, "invalid format string"); FormatError, "invalid format string");
EXPECT_THROW_MSG(format("{0:{}", 0), EXPECT_THROW_MSG(format("{0:{}", 0),
FormatError, "cannot switch from manual to automatic argument indexing"); FormatError, "cannot switch from manual to automatic argument indexing");
EXPECT_THROW_MSG(format("{0:{x}}", 0), EXPECT_THROW_MSG(format("{0:{?}}", 0),
FormatError, "invalid format string"); FormatError, "invalid format string");
EXPECT_THROW_MSG(format("{0:{1}}", 0), EXPECT_THROW_MSG(format("{0:{1}}", 0),
FormatError, "argument index out of range"); FormatError, "argument index out of range");
@ -1036,7 +1050,7 @@ TEST(FormatterTest, RuntimePrecision) {
FormatError, "invalid format string"); FormatError, "invalid format string");
EXPECT_THROW_MSG(format("{0:.{}", 0), EXPECT_THROW_MSG(format("{0:.{}", 0),
FormatError, "cannot switch from manual to automatic argument indexing"); FormatError, "cannot switch from manual to automatic argument indexing");
EXPECT_THROW_MSG(format("{0:.{x}}", 0), EXPECT_THROW_MSG(format("{0:.{?}}", 0),
FormatError, "invalid format string"); FormatError, "invalid format string");
EXPECT_THROW_MSG(format("{0:.{1}", 0, 0), EXPECT_THROW_MSG(format("{0:.{1}", 0, 0),
FormatError, "precision not allowed in integer format specifier"); FormatError, "precision not allowed in integer format specifier");
@ -1550,10 +1564,10 @@ TEST(StrTest, Convert) {
} }
std::string format_message(int id, const char *format, std::string format_message(int id, const char *format,
const fmt::ArgList &args) { const fmt::ArgList &args, const fmt::ArgMap &map) {
MemoryWriter w; MemoryWriter w;
w.write("[{}] ", id); w.write("[{}] ", id);
w.write(format, args); w.write(format, args, map);
return w.str(); return w.str();
} }