Support named arguments (WIP)
This commit is contained in:
parent
a5e7d08ad0
commit
344de54aa9
@ -15,13 +15,15 @@ literal text, it can be escaped by doubling: ``{{`` and ``}}``.
|
||||
The grammar for a replacement field is as follows:
|
||||
|
||||
.. productionlist:: sf
|
||||
replacement_field: "{" [`arg_index`] [":" `format_spec`] "}"
|
||||
replacement_field: "{" [`arg_field`] [":" `format_spec`] "}"
|
||||
arg_field: `arg_index` | `arg_name`
|
||||
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
|
||||
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.
|
||||
|
||||
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 '}'>
|
||||
align: "<" | ">" | "=" | "^"
|
||||
sign: "+" | "-" | " "
|
||||
width: `integer` | "{" `arg_index` "}"
|
||||
precision: `integer` | "{" `arg_index` "}"
|
||||
width: `integer` | "{" `arg_field` "}"
|
||||
precision: `integer` | "{" `arg_field` "}"
|
||||
type: `int_type` | "c" | "e" | "E" | "f" | "F" | "g" | "G" | "p" | "s"
|
||||
int_type: "b" | "B" | "d" | "o" | "x" | "X"
|
||||
|
||||
|
||||
72
format.cc
72
format.cc
@ -265,6 +265,12 @@ int parse_nonnegative_int(const Char *&s) {
|
||||
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) {
|
||||
if (arg.type > Arg::LAST_NUMERIC_TYPE) {
|
||||
std::string message =
|
||||
@ -402,10 +408,10 @@ inline Arg::StringValue<wchar_t> ignore_incompatible_str(
|
||||
} // namespace
|
||||
|
||||
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;
|
||||
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;
|
||||
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(
|
||||
int err_code, StringRef format_str, ArgList args) {
|
||||
int err_code, StringRef format_str, ArgList args, const ArgMap &map) {
|
||||
error_code_ = err_code;
|
||||
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;
|
||||
base = std::runtime_error(w.str());
|
||||
}
|
||||
@ -693,6 +699,24 @@ inline Arg fmt::BasicFormatter<Char>::parse_arg_index(const Char *&s) {
|
||||
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(
|
||||
unsigned arg_index, const char *&error) {
|
||||
Arg arg = args_[arg_index];
|
||||
@ -960,7 +984,7 @@ void fmt::internal::PrintfFormatter<Char>::format(
|
||||
|
||||
template <typename Char>
|
||||
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;
|
||||
FormatSpec spec;
|
||||
if (*s == ':') {
|
||||
@ -1038,7 +1062,7 @@ const Char *fmt::BasicFormatter<Char>::format(
|
||||
spec.width_ = parse_nonnegative_int(s);
|
||||
} else if (*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++ != '}')
|
||||
FMT_THROW(FormatError("invalid format string"));
|
||||
ULongLong value = 0;
|
||||
@ -1075,7 +1099,7 @@ const Char *fmt::BasicFormatter<Char>::format(
|
||||
spec.precision_ = parse_nonnegative_int(s);
|
||||
} else if (*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++ != '}')
|
||||
FMT_THROW(FormatError("invalid format string"));
|
||||
ULongLong value = 0;
|
||||
@ -1128,7 +1152,7 @@ const Char *fmt::BasicFormatter<Char>::format(
|
||||
|
||||
template <typename Char>
|
||||
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();
|
||||
set_args(args);
|
||||
while (*s) {
|
||||
@ -1142,8 +1166,8 @@ void fmt::BasicFormatter<Char>::format(
|
||||
if (c == '}')
|
||||
FMT_THROW(FormatError("unmatched '}' in format string"));
|
||||
write(writer_, start_, s - 1);
|
||||
Arg arg = parse_arg_index(s);
|
||||
s = format(s, arg);
|
||||
Arg arg = is_name_start(*s) ? parse_arg_name(s, map) : parse_arg_index(s);
|
||||
s = format(s, arg, map);
|
||||
}
|
||||
write(writer_, start_, s);
|
||||
}
|
||||
@ -1160,33 +1184,33 @@ FMT_FUNC void fmt::report_windows_error(
|
||||
}
|
||||
#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;
|
||||
w.write(format_str, args);
|
||||
w.write(format_str, args, map);
|
||||
std::fwrite(w.data(), 1, w.size(), f);
|
||||
}
|
||||
|
||||
FMT_FUNC void fmt::print(StringRef format_str, ArgList args) {
|
||||
print(stdout, format_str, args);
|
||||
FMT_FUNC void fmt::print(StringRef format_str, ArgList args, const ArgMap &map) {
|
||||
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;
|
||||
w.write(format_str, args);
|
||||
w.write(format_str, args, map);
|
||||
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";
|
||||
escape[3] = '0' + static_cast<char>(c);
|
||||
std::fputs(escape, stdout);
|
||||
print(format, args);
|
||||
print(format, args, map);
|
||||
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;
|
||||
printf(w, format, args);
|
||||
printf(w, format, args, map);
|
||||
std::size_t size = w.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 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(
|
||||
BasicStringRef<char> format, const ArgList &args);
|
||||
BasicStringRef<char> format, const ArgList &args, const BasicArgMap<char> &map);
|
||||
|
||||
template void fmt::internal::PrintfFormatter<char>::format(
|
||||
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 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(
|
||||
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(
|
||||
BasicWriter<wchar_t> &writer, BasicStringRef<wchar_t> format,
|
||||
|
||||
184
format.h
184
format.h
@ -227,6 +227,11 @@ class BasicStringRef {
|
||||
std::size_t size_;
|
||||
|
||||
public:
|
||||
/**
|
||||
Constructs an empty string reference object.
|
||||
*/
|
||||
BasicStringRef() : data_(), 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) {
|
||||
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;
|
||||
@ -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
|
||||
// internal::MemoryBuffer.
|
||||
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
|
||||
|
||||
// An argument visitor.
|
||||
@ -1146,6 +1207,48 @@ class ArgList {
|
||||
|
||||
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 {
|
||||
|
||||
class FormatterBase {
|
||||
@ -1208,14 +1311,17 @@ class BasicFormatter : private internal::FormatterBase {
|
||||
// Parses argument index and returns corresponding argument.
|
||||
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:
|
||||
explicit BasicFormatter(BasicWriter<Char> &w) : writer_(w) {}
|
||||
|
||||
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 {
|
||||
@ -1556,7 +1662,11 @@ inline uint64_t make_type(FMT_GEN15(FMT_ARG_TYPE_DEFAULT)) {
|
||||
template <typename... Args> \
|
||||
void func(arg_type arg0, const Args & ... args) { \
|
||||
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.
|
||||
@ -1564,7 +1674,11 @@ inline uint64_t make_type(FMT_GEN15(FMT_ARG_TYPE_DEFAULT)) {
|
||||
template <typename... Args> \
|
||||
ctor(arg0_type arg0, arg1_type arg1, const Args & ... args) { \
|
||||
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
|
||||
@ -1641,7 +1755,7 @@ inline uint64_t make_type(FMT_GEN15(FMT_ARG_TYPE_DEFAULT)) {
|
||||
*/
|
||||
class SystemError : public internal::RuntimeError {
|
||||
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:
|
||||
int error_code_;
|
||||
@ -1677,7 +1791,7 @@ class SystemError : public internal::RuntimeError {
|
||||
\endrst
|
||||
*/
|
||||
SystemError(int error_code, StringRef message) {
|
||||
init(error_code, message, ArgList());
|
||||
init(error_code, message, ArgList(), ArgMap());
|
||||
}
|
||||
FMT_VARIADIC_CTOR(SystemError, init, int, StringRef)
|
||||
|
||||
@ -1852,8 +1966,8 @@ class BasicWriter {
|
||||
See also :ref:`syntax`.
|
||||
\endrst
|
||||
*/
|
||||
void write(BasicStringRef<Char> format, ArgList args) {
|
||||
BasicFormatter<Char>(*this).format(format, args);
|
||||
void write(BasicStringRef<Char> format, ArgList args, const BasicArgMap<Char> &map) {
|
||||
BasicFormatter<Char>(*this).format(format, args, map);
|
||||
}
|
||||
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);
|
||||
arg.type = static_cast<internal::Arg::Type>(
|
||||
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.
|
||||
@ -2413,7 +2527,7 @@ void report_system_error(int error_code, StringRef message) FMT_NOEXCEPT;
|
||||
/** A Windows error. */
|
||||
class WindowsError : public SystemError {
|
||||
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:
|
||||
/**
|
||||
@ -2445,7 +2559,7 @@ class WindowsError : public SystemError {
|
||||
\endrst
|
||||
*/
|
||||
WindowsError(int error_code, StringRef message) {
|
||||
init(error_code, message, ArgList());
|
||||
init(error_code, message, ArgList(), ArgMap());
|
||||
}
|
||||
FMT_VARIADIC_CTOR(WindowsError, init, int, StringRef)
|
||||
};
|
||||
@ -2464,7 +2578,7 @@ enum Color { BLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE };
|
||||
Example:
|
||||
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
|
||||
@ -2475,15 +2589,15 @@ void print_colored(Color c, StringRef format, ArgList args);
|
||||
std::string message = format("The answer is {}", 42);
|
||||
\endrst
|
||||
*/
|
||||
inline std::string format(StringRef format_str, ArgList args) {
|
||||
inline std::string format(StringRef format_str, ArgList args, const ArgMap &map) {
|
||||
MemoryWriter w;
|
||||
w.write(format_str, args);
|
||||
w.write(format_str, args, map);
|
||||
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;
|
||||
w.write(format_str, args);
|
||||
w.write(format_str, args, map);
|
||||
return w.str();
|
||||
}
|
||||
|
||||
@ -2496,7 +2610,7 @@ inline std::wstring format(WStringRef format_str, ArgList args) {
|
||||
print(stderr, "Don't {}!", "panic");
|
||||
\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
|
||||
@ -2507,7 +2621,7 @@ void print(std::FILE *f, StringRef format_str, ArgList args);
|
||||
print("Elapsed time: {0:.2f} seconds", 1.23);
|
||||
\endrst
|
||||
*/
|
||||
void print(StringRef format_str, ArgList args);
|
||||
void print(StringRef format_str, ArgList args, const ArgMap &map);
|
||||
|
||||
/**
|
||||
\rst
|
||||
@ -2518,10 +2632,10 @@ void print(StringRef format_str, ArgList args);
|
||||
print(cerr, "Don't {}!", "panic");
|
||||
\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>
|
||||
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);
|
||||
}
|
||||
|
||||
@ -2534,9 +2648,9 @@ void printf(BasicWriter<Char> &w, BasicStringRef<Char> format, ArgList args) {
|
||||
std::string message = fmt::sprintf("The answer is %d", 42);
|
||||
\endrst
|
||||
*/
|
||||
inline std::string sprintf(StringRef format, ArgList args) {
|
||||
inline std::string sprintf(StringRef format, ArgList args, const ArgMap &map) {
|
||||
MemoryWriter w;
|
||||
printf(w, format, args);
|
||||
printf(w, format, args, map);
|
||||
return w.str();
|
||||
}
|
||||
|
||||
@ -2549,7 +2663,7 @@ inline std::string sprintf(StringRef format, ArgList args) {
|
||||
fmt::fprintf(stderr, "Don't %s!", "panic");
|
||||
\endrst
|
||||
*/
|
||||
int fprintf(std::FILE *f, StringRef format, ArgList args);
|
||||
int fprintf(std::FILE *f, StringRef format, ArgList args, const ArgMap &);
|
||||
|
||||
/**
|
||||
\rst
|
||||
@ -2560,8 +2674,8 @@ int fprintf(std::FILE *f, StringRef format, ArgList args);
|
||||
fmt::printf("Elapsed time: %.2f seconds", 1.23);
|
||||
\endrst
|
||||
*/
|
||||
inline int printf(StringRef format, ArgList args) {
|
||||
return fprintf(stdout, format, args);
|
||||
inline int printf(StringRef format, ArgList args, const ArgMap &map) {
|
||||
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);
|
||||
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
|
||||
@ -2702,8 +2826,12 @@ inline void format_decimal(char *&buffer, T value) {
|
||||
ReturnType func(FMT_FOR_EACH(FMT_ADD_ARG_NAME, __VA_ARGS__), \
|
||||
const Args & ... args) { \
|
||||
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__), \
|
||||
fmt::internal::make_arg_list<Char>(array, args...)); \
|
||||
fmt::internal::make_arg_list<Char>(array, fmt::internal::strip_name(args)...), map); \
|
||||
}
|
||||
#else
|
||||
// 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, ...) \
|
||||
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 {
|
||||
FMT_VARIADIC(std::string, format, StringRef)
|
||||
FMT_VARIADIC_W(std::wstring, format, WStringRef)
|
||||
|
||||
4
posix.h
4
posix.h
@ -197,8 +197,8 @@ public:
|
||||
// of MinGW that define fileno as a macro.
|
||||
int (fileno)() const;
|
||||
|
||||
void print(fmt::StringRef format_str, const ArgList &args) {
|
||||
fmt::print(file_, format_str, args);
|
||||
void print(fmt::StringRef format_str, const ArgList &args, const ArgMap &map) {
|
||||
fmt::print(file_, format_str, args, map);
|
||||
}
|
||||
FMT_VARIADIC(void, print, fmt::StringRef)
|
||||
};
|
||||
|
||||
@ -561,7 +561,7 @@ TEST(FormatterTest, ArgsInDifferentPositions) {
|
||||
|
||||
TEST(FormatterTest, ArgErrors) {
|
||||
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, "argument index out of range");
|
||||
|
||||
@ -606,6 +606,20 @@ TEST(FormatterTest, ManyArgs) {
|
||||
EXPECT_THROW_MSG(TestFormat<MAX_PACKED_ARGS>::format(format_str),
|
||||
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
|
||||
|
||||
TEST(FormatterTest, AutoArgIndex) {
|
||||
@ -919,7 +933,7 @@ TEST(FormatterTest, RuntimeWidth) {
|
||||
FormatError, "invalid format string");
|
||||
EXPECT_THROW_MSG(format("{0:{}", 0),
|
||||
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");
|
||||
EXPECT_THROW_MSG(format("{0:{1}}", 0),
|
||||
FormatError, "argument index out of range");
|
||||
@ -1036,7 +1050,7 @@ TEST(FormatterTest, RuntimePrecision) {
|
||||
FormatError, "invalid format string");
|
||||
EXPECT_THROW_MSG(format("{0:.{}", 0),
|
||||
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");
|
||||
EXPECT_THROW_MSG(format("{0:.{1}", 0, 0),
|
||||
FormatError, "precision not allowed in integer format specifier");
|
||||
@ -1550,10 +1564,10 @@ TEST(StrTest, Convert) {
|
||||
}
|
||||
|
||||
std::string format_message(int id, const char *format,
|
||||
const fmt::ArgList &args) {
|
||||
const fmt::ArgList &args, const fmt::ArgMap &map) {
|
||||
MemoryWriter w;
|
||||
w.write("[{}] ", id);
|
||||
w.write(format, args);
|
||||
w.write(format, args, map);
|
||||
return w.str();
|
||||
}
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user