mirror of
https://github.com/fmtlib/fmt.git
synced 2025-07-30 10:47:35 +02:00
Separate parsing and formatting
This commit is contained in:
476
fmt/format.h
476
fmt/format.h
@ -451,6 +451,11 @@ class basic_string_view {
|
|||||||
/** Returns the string size. */
|
/** Returns the string size. */
|
||||||
std::size_t size() const { return size_; }
|
std::size_t size() const { return size_; }
|
||||||
|
|
||||||
|
void remove_prefix(size_t n) {
|
||||||
|
data_ += n;
|
||||||
|
size_ -= n;
|
||||||
|
}
|
||||||
|
|
||||||
// Lexicographically compare this string reference to other.
|
// Lexicographically compare this string reference to other.
|
||||||
int compare(basic_string_view other) const {
|
int compare(basic_string_view other) const {
|
||||||
std::size_t size = size_ < other.size_ ? size_ : other.size_;
|
std::size_t size = size_ < other.size_ ? size_ : other.size_;
|
||||||
@ -483,6 +488,9 @@ class basic_string_view {
|
|||||||
typedef basic_string_view<char> string_view;
|
typedef basic_string_view<char> string_view;
|
||||||
typedef basic_string_view<wchar_t> wstring_view;
|
typedef basic_string_view<wchar_t> wstring_view;
|
||||||
|
|
||||||
|
template <typename Char>
|
||||||
|
inline const Char *begin(basic_string_view<Char> s) { return s.data(); }
|
||||||
|
|
||||||
/** A formatting error such as invalid format string. */
|
/** A formatting error such as invalid format string. */
|
||||||
class format_error : public std::runtime_error {
|
class format_error : public std::runtime_error {
|
||||||
public:
|
public:
|
||||||
@ -496,8 +504,8 @@ class format_error : public std::runtime_error {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// A formatter for objects of type T.
|
// A formatter for objects of type T.
|
||||||
template <typename Char, typename T>
|
template <typename T, typename Char = char, typename Enable = void>
|
||||||
class formatter;
|
struct formatter;
|
||||||
|
|
||||||
namespace internal {
|
namespace internal {
|
||||||
|
|
||||||
@ -847,6 +855,109 @@ struct conditional { typedef T type; };
|
|||||||
template<class T, class F>
|
template<class T, class F>
|
||||||
struct conditional<false, T, F> { typedef F type; };
|
struct conditional<false, T, F> { typedef F type; };
|
||||||
|
|
||||||
|
template <typename Char>
|
||||||
|
class null_terminating_iterator;
|
||||||
|
|
||||||
|
template <typename Char>
|
||||||
|
const Char *pointer_from(null_terminating_iterator<Char> it);
|
||||||
|
|
||||||
|
// An iterator that produces a null terminator on *end. This simplifies parsing
|
||||||
|
// and allows comparing the performance of processing a null-terminated string
|
||||||
|
// vs string_view.
|
||||||
|
template <typename Char>
|
||||||
|
class null_terminating_iterator {
|
||||||
|
public:
|
||||||
|
typedef Char value_type;
|
||||||
|
typedef std::ptrdiff_t difference_type;
|
||||||
|
|
||||||
|
null_terminating_iterator() : ptr_(0), end_(0) {}
|
||||||
|
|
||||||
|
null_terminating_iterator(const Char *ptr, const Char *end)
|
||||||
|
: ptr_(ptr), end_(end) {}
|
||||||
|
|
||||||
|
explicit null_terminating_iterator(basic_string_view<Char> s)
|
||||||
|
: ptr_(s.data()), end_(s.data() + s.size()) {}
|
||||||
|
|
||||||
|
null_terminating_iterator &operator=(const Char *ptr) {
|
||||||
|
assert(ptr <= end_);
|
||||||
|
ptr_ = ptr;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
Char operator*() const {
|
||||||
|
return ptr_ != end_ ? *ptr_ : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
null_terminating_iterator operator++() {
|
||||||
|
++ptr_;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
null_terminating_iterator operator++(int) {
|
||||||
|
null_terminating_iterator result(*this);
|
||||||
|
++ptr_;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
null_terminating_iterator operator--() {
|
||||||
|
--ptr_;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
null_terminating_iterator operator+(difference_type n) {
|
||||||
|
return null_terminating_iterator(ptr_ + n, end_);
|
||||||
|
}
|
||||||
|
|
||||||
|
null_terminating_iterator operator+=(difference_type n) {
|
||||||
|
ptr_ += n;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
difference_type operator-(null_terminating_iterator other) const {
|
||||||
|
return ptr_ - other.ptr_;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator!=(null_terminating_iterator other) const {
|
||||||
|
return ptr_ != other.ptr_;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator>=(null_terminating_iterator other) const {
|
||||||
|
return ptr_ >= other.ptr_;
|
||||||
|
}
|
||||||
|
|
||||||
|
friend const Char *pointer_from<Char>(null_terminating_iterator it);
|
||||||
|
|
||||||
|
private:
|
||||||
|
const Char *ptr_;
|
||||||
|
const Char *end_;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <
|
||||||
|
typename T,
|
||||||
|
typename Char,
|
||||||
|
typename std::enable_if<
|
||||||
|
std::is_same<T, null_terminating_iterator<Char>>::value, int>::type = 0>
|
||||||
|
null_terminating_iterator<Char> to_iterator(basic_string_view<Char> v) {
|
||||||
|
const Char *s = v.data();
|
||||||
|
return null_terminating_iterator<Char>(s, s + v.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
template <
|
||||||
|
typename T,
|
||||||
|
typename Char,
|
||||||
|
typename std::enable_if<std::is_same<T, const Char*>::value, int>::type = 0>
|
||||||
|
const Char *to_iterator(basic_string_view<Char> v) {
|
||||||
|
return v.data();
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
const T *pointer_from(const T *p) { return p; }
|
||||||
|
|
||||||
|
template <typename Char>
|
||||||
|
const Char *pointer_from(null_terminating_iterator<Char> it) {
|
||||||
|
return it.ptr_;
|
||||||
|
}
|
||||||
|
|
||||||
// Returns true if value is negative, false otherwise.
|
// Returns true if value is negative, false otherwise.
|
||||||
// Same as (value < 0) but doesn't produce warnings if T is an unsigned type.
|
// Same as (value < 0) but doesn't produce warnings if T is an unsigned type.
|
||||||
template <typename T>
|
template <typename T>
|
||||||
@ -1122,7 +1233,8 @@ struct string_value {
|
|||||||
template <typename Char>
|
template <typename Char>
|
||||||
struct custom_value {
|
struct custom_value {
|
||||||
typedef void (*FormatFunc)(
|
typedef void (*FormatFunc)(
|
||||||
basic_buffer<Char> &buffer, const void *arg, void *ctx);
|
basic_buffer<Char> &buffer, const void *arg,
|
||||||
|
basic_string_view<Char>& format, void *ctx);
|
||||||
|
|
||||||
const void *value;
|
const void *value;
|
||||||
FormatFunc format;
|
FormatFunc format;
|
||||||
@ -1243,9 +1355,16 @@ class value {
|
|||||||
// 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(
|
||||||
basic_buffer<Char> &buffer, const void *arg, void *context) {
|
basic_buffer<Char> &buffer, const void *arg,
|
||||||
format_value(buffer, *static_cast<const T*>(arg),
|
basic_string_view<Char> &format, void *context) {
|
||||||
*static_cast<Context*>(context));
|
Context &ctx = *static_cast<Context*>(context);
|
||||||
|
// Get the formatter type through the context to allow different contexts
|
||||||
|
// have different extension points, e.g. `formatter<T>` for `format` and
|
||||||
|
// `printf_formatter<T>` for `printf`.
|
||||||
|
typename Context::template formatter_type<T> f;
|
||||||
|
auto it = f.parse(format);
|
||||||
|
format.remove_prefix(it - begin(format));
|
||||||
|
f.format(buffer, *static_cast<const T*>(arg), ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
@ -1475,14 +1594,6 @@ basic_arg<Context> make_arg(const T &value) {
|
|||||||
typedef int FMT_CONCAT_(Assert, __LINE__)[(cond) ? 1 : -1] FMT_UNUSED
|
typedef int FMT_CONCAT_(Assert, __LINE__)[(cond) ? 1 : -1] FMT_UNUSED
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
template <typename Formatter, typename T, typename Char>
|
|
||||||
void format_value(basic_buffer<Char> &, const T &, Formatter &, const Char *) {
|
|
||||||
FMT_STATIC_ASSERT(sizeof(T) < 0,
|
|
||||||
"Cannot format argument. To enable the use of ostream "
|
|
||||||
"operator<< include fmt/ostream.h. Otherwise provide "
|
|
||||||
"an overload of format_value.");
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename Context>
|
template <typename Context>
|
||||||
struct named_arg : basic_arg<Context> {
|
struct named_arg : basic_arg<Context> {
|
||||||
typedef typename Context::char_type Char;
|
typedef typename Context::char_type Char;
|
||||||
@ -1947,115 +2058,17 @@ class arg_formatter_base {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename Char>
|
|
||||||
class null_terminating_iterator;
|
|
||||||
|
|
||||||
template <typename Char>
|
|
||||||
const Char *pointer_from(null_terminating_iterator<Char> it);
|
|
||||||
|
|
||||||
// An iterator that produces a null terminator on *end. This simplifies parsing
|
|
||||||
// and allows comparing the performance of processing a null-terminated string
|
|
||||||
// vs string_view.
|
|
||||||
template <typename Char>
|
|
||||||
class null_terminating_iterator {
|
|
||||||
public:
|
|
||||||
typedef Char value_type;
|
|
||||||
typedef std::ptrdiff_t difference_type;
|
|
||||||
|
|
||||||
null_terminating_iterator() : ptr_(0), end_(0) {}
|
|
||||||
|
|
||||||
null_terminating_iterator(const Char *ptr, const Char *end)
|
|
||||||
: ptr_(ptr), end_(end) {}
|
|
||||||
|
|
||||||
Char operator*() const {
|
|
||||||
return ptr_ != end_ ? *ptr_ : 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
null_terminating_iterator operator++() {
|
|
||||||
++ptr_;
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
null_terminating_iterator operator++(int) {
|
|
||||||
null_terminating_iterator result(*this);
|
|
||||||
++ptr_;
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
null_terminating_iterator operator--() {
|
|
||||||
--ptr_;
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
null_terminating_iterator operator+(difference_type n) {
|
|
||||||
return null_terminating_iterator(ptr_ + n, end_);
|
|
||||||
}
|
|
||||||
|
|
||||||
null_terminating_iterator operator+=(difference_type n) {
|
|
||||||
ptr_ += n;
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
difference_type operator-(null_terminating_iterator other) const {
|
|
||||||
return ptr_ - other.ptr_;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool operator!=(null_terminating_iterator other) const {
|
|
||||||
return ptr_ != other.ptr_;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool operator>=(null_terminating_iterator other) const {
|
|
||||||
return ptr_ >= other.ptr_;
|
|
||||||
}
|
|
||||||
|
|
||||||
friend const Char *pointer_from<Char>(null_terminating_iterator it);
|
|
||||||
|
|
||||||
private:
|
|
||||||
const Char *ptr_;
|
|
||||||
const Char *end_;
|
|
||||||
};
|
|
||||||
|
|
||||||
template <
|
|
||||||
typename T,
|
|
||||||
typename Char,
|
|
||||||
typename std::enable_if<
|
|
||||||
std::is_same<T, null_terminating_iterator<Char>>::value, int>::type = 0>
|
|
||||||
null_terminating_iterator<Char> to_iterator(basic_string_view<Char> v) {
|
|
||||||
const Char *s = v.data();
|
|
||||||
return null_terminating_iterator<Char>(s, s + v.size());
|
|
||||||
}
|
|
||||||
|
|
||||||
template <
|
|
||||||
typename T,
|
|
||||||
typename Char,
|
|
||||||
typename std::enable_if<std::is_same<T, const Char*>::value, int>::type = 0>
|
|
||||||
const Char *to_iterator(const basic_string_view<Char> v) {
|
|
||||||
return v.data();
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
const T *pointer_from(const T *p) { return p; }
|
|
||||||
|
|
||||||
template <typename Char>
|
|
||||||
const Char *pointer_from(null_terminating_iterator<Char> it) {
|
|
||||||
return it.ptr_;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename Char, typename Context>
|
template <typename Char, typename Context>
|
||||||
class context_base {
|
class context_base {
|
||||||
public:
|
|
||||||
typedef null_terminating_iterator<Char> iterator;
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
iterator pos_;
|
|
||||||
basic_args<Context> args_;
|
basic_args<Context> args_;
|
||||||
int next_arg_index_;
|
int next_arg_index_;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
typedef basic_arg<Context> format_arg;
|
typedef basic_arg<Context> format_arg;
|
||||||
|
|
||||||
context_base(basic_string_view<Char> format_str, basic_args<Context> args)
|
explicit context_base(basic_args<Context> args)
|
||||||
: pos_(to_iterator<iterator>(format_str)), args_(args), next_arg_index_(0) {}
|
: args_(args), next_arg_index_(0) {}
|
||||||
~context_base() {}
|
~context_base() {}
|
||||||
|
|
||||||
basic_args<Context> args() const { return args_; }
|
basic_args<Context> args() const { return args_; }
|
||||||
@ -2091,10 +2104,6 @@ class context_base {
|
|||||||
next_arg_index_ = -1;
|
next_arg_index_ = -1;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public:
|
|
||||||
// Returns an iterator to the current position in the format string.
|
|
||||||
iterator &pos() { return pos_; }
|
|
||||||
};
|
};
|
||||||
} // namespace internal
|
} // namespace internal
|
||||||
|
|
||||||
@ -2125,7 +2134,8 @@ class arg_formatter : public internal::arg_formatter_base<Char> {
|
|||||||
|
|
||||||
/** Formats an argument of a custom (user-defined) type. */
|
/** Formats an argument of a custom (user-defined) type. */
|
||||||
void operator()(internal::custom_value<Char> c) {
|
void operator()(internal::custom_value<Char> c) {
|
||||||
c.format(this->writer().buffer(), c.value, &ctx_);
|
basic_string_view<Char> format_str;
|
||||||
|
c.format(this->writer().buffer(), c.value, format_str, &ctx_);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -2134,7 +2144,10 @@ class basic_context :
|
|||||||
public internal::context_base<Char, basic_context<Char>> {
|
public internal::context_base<Char, basic_context<Char>> {
|
||||||
public:
|
public:
|
||||||
/** The character type for the output. */
|
/** The character type for the output. */
|
||||||
typedef Char char_type;
|
using char_type = Char;
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
using formatter_type = formatter<T, Char>;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
internal::arg_map<basic_context<Char>> map_;
|
internal::arg_map<basic_context<Char>> map_;
|
||||||
@ -2153,9 +2166,7 @@ class basic_context :
|
|||||||
stored in the object so make sure they have appropriate lifetimes.
|
stored in the object so make sure they have appropriate lifetimes.
|
||||||
\endrst
|
\endrst
|
||||||
*/
|
*/
|
||||||
basic_context(
|
basic_context(basic_args<basic_context> args): Base(args) {}
|
||||||
basic_string_view<Char> format_str, basic_args<basic_context> args)
|
|
||||||
: Base(format_str, args) {}
|
|
||||||
|
|
||||||
format_arg next_arg() {
|
format_arg next_arg() {
|
||||||
const char *error = 0;
|
const char *error = 0;
|
||||||
@ -2177,8 +2188,6 @@ class basic_context :
|
|||||||
// 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.
|
||||||
format_arg get_arg(basic_string_view<Char> name);
|
format_arg get_arg(basic_string_view<Char> name);
|
||||||
|
|
||||||
using Base::pos;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -3210,14 +3219,16 @@ template <typename Char, typename Context>
|
|||||||
class custom_formatter {
|
class custom_formatter {
|
||||||
private:
|
private:
|
||||||
basic_buffer<Char> &buffer_;
|
basic_buffer<Char> &buffer_;
|
||||||
|
basic_string_view<Char> &format_;
|
||||||
Context &ctx_;
|
Context &ctx_;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
custom_formatter(basic_buffer<Char> &buffer, Context &ctx)
|
custom_formatter(basic_buffer<Char> &buffer, basic_string_view<Char> &format,
|
||||||
: buffer_(buffer), ctx_(ctx) {}
|
Context &ctx)
|
||||||
|
: buffer_(buffer), format_(format), ctx_(ctx) {}
|
||||||
|
|
||||||
bool operator()(internal::custom_value<Char> custom) {
|
bool operator()(internal::custom_value<Char> custom) {
|
||||||
custom.format(buffer_, custom.value, &ctx_);
|
custom.format(buffer_, custom.value, format_, &ctx_);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3267,16 +3278,14 @@ struct precision_handler {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename Context>
|
template <typename Char>
|
||||||
class specs_handler {
|
class specs_handler_base {
|
||||||
public:
|
public:
|
||||||
typedef typename Context::char_type char_type;
|
explicit specs_handler_base(basic_format_specs<Char> &specs)
|
||||||
|
: specs_(specs) {}
|
||||||
specs_handler(basic_format_specs<char_type> &specs, Context &ctx)
|
|
||||||
: specs_(specs), context_(ctx) {}
|
|
||||||
|
|
||||||
void on_align(alignment align) { specs_.align_ = align; }
|
void on_align(alignment align) { specs_.align_ = align; }
|
||||||
void on_fill(char_type fill) { specs_.fill_ = fill; }
|
void on_fill(Char fill) { specs_.fill_ = fill; }
|
||||||
void on_plus() { specs_.flags_ |= SIGN_FLAG | PLUS_FLAG; }
|
void on_plus() { specs_.flags_ |= SIGN_FLAG | PLUS_FLAG; }
|
||||||
void on_minus() { specs_.flags_ |= MINUS_FLAG; }
|
void on_minus() { specs_.flags_ |= MINUS_FLAG; }
|
||||||
void on_space() { specs_.flags_ |= SIGN_FLAG; }
|
void on_space() { specs_.flags_ |= SIGN_FLAG; }
|
||||||
@ -3288,46 +3297,107 @@ class specs_handler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void on_width(unsigned width) { specs_.width_ = width; }
|
void on_width(unsigned width) { specs_.width_ = width; }
|
||||||
|
void on_precision(unsigned precision) { specs_.precision_ = precision; }
|
||||||
|
void on_type(char type) { specs_.type_ = type; }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
~specs_handler_base() {}
|
||||||
|
|
||||||
|
basic_format_specs<Char> &specs_;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename Handler, typename T, typename Context>
|
||||||
|
inline void set_dynamic_spec(T &value, basic_arg<Context> arg) {
|
||||||
|
ulong_long big_value = visit(Handler(), arg);
|
||||||
|
if (big_value > (std::numeric_limits<int>::max)())
|
||||||
|
FMT_THROW(format_error("number is too big"));
|
||||||
|
value = static_cast<int>(big_value);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Context>
|
||||||
|
class specs_handler : public specs_handler_base<typename Context::char_type> {
|
||||||
|
public:
|
||||||
|
typedef typename Context::char_type char_type;
|
||||||
|
|
||||||
|
specs_handler(basic_format_specs<char_type> &specs, Context &ctx)
|
||||||
|
: specs_handler_base<char_type>(specs), context_(ctx) {}
|
||||||
|
|
||||||
template <typename Id>
|
template <typename Id>
|
||||||
void on_dynamic_width(Id arg_id) {
|
void on_dynamic_width(Id arg_id) {
|
||||||
auto width_arg = get_arg(arg_id);
|
set_dynamic_spec<internal::width_handler>(
|
||||||
ulong_long width = visit(internal::width_handler(), width_arg);
|
this->specs_.width_, get_arg(arg_id));
|
||||||
if (width > (std::numeric_limits<int>::max)())
|
|
||||||
FMT_THROW(format_error("number is too big"));
|
|
||||||
specs_.width_ = static_cast<int>(width);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void on_precision(unsigned precision) { specs_.precision_ = precision; }
|
|
||||||
|
|
||||||
template <typename Id>
|
template <typename Id>
|
||||||
void on_dynamic_precision(Id arg_id) {
|
void on_dynamic_precision(Id arg_id) {
|
||||||
auto precision_arg = get_arg(arg_id);
|
set_dynamic_spec<internal::precision_handler>(
|
||||||
ulong_long precision = visit(internal::precision_handler(), precision_arg);
|
this->specs_.precision_, get_arg(arg_id));
|
||||||
if (precision > (std::numeric_limits<int>::max)())
|
|
||||||
FMT_THROW(format_error("number is too big"));
|
|
||||||
specs_.precision_ = static_cast<int>(precision);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void on_type(char type) { specs_.type_ = type; }
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
basic_arg<Context> get_arg(monostate) {
|
basic_arg<Context> get_arg(monostate) {
|
||||||
return context_.next_arg();
|
return context_.next_arg();
|
||||||
}
|
}
|
||||||
|
|
||||||
basic_arg<Context> get_arg(unsigned index) {
|
template <typename Id>
|
||||||
return context_.get_arg(index);
|
basic_arg<Context> get_arg(Id arg_id) {
|
||||||
|
return context_.get_arg(arg_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
basic_arg<Context> get_arg(basic_string_view<char_type> name) {
|
|
||||||
return context_.get_arg(name);
|
|
||||||
}
|
|
||||||
|
|
||||||
basic_format_specs<char_type> &specs_;
|
|
||||||
Context &context_;
|
Context &context_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// An argument reference.
|
||||||
|
template <typename Char>
|
||||||
|
struct arg_ref {
|
||||||
|
enum Kind { NONE, INDEX, NAME };
|
||||||
|
|
||||||
|
arg_ref() : kind(NONE) {}
|
||||||
|
explicit arg_ref(unsigned index) : kind(INDEX), index(index) {}
|
||||||
|
explicit arg_ref(basic_string_view<Char> name) : kind(NAME), name(name) {}
|
||||||
|
|
||||||
|
Kind kind;
|
||||||
|
union {
|
||||||
|
unsigned index;
|
||||||
|
basic_string_view<Char> name;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename Char>
|
||||||
|
struct dynamic_format_specs : basic_format_specs<Char> {
|
||||||
|
arg_ref<Char> width_ref;
|
||||||
|
arg_ref<Char> precision_ref;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename Char>
|
||||||
|
class dynamic_specs_handler : public specs_handler_base<Char> {
|
||||||
|
public:
|
||||||
|
explicit dynamic_specs_handler(dynamic_format_specs<Char> &specs)
|
||||||
|
: specs_handler_base<Char>(specs), specs_(specs) {}
|
||||||
|
|
||||||
|
template <typename Id>
|
||||||
|
void on_dynamic_width(Id arg_id) {
|
||||||
|
set(specs_.width_ref, arg_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Id>
|
||||||
|
void on_dynamic_precision(Id arg_id) {
|
||||||
|
set(specs_.precision_ref, arg_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
template <typename Id>
|
||||||
|
void set(arg_ref<Char> &ref, Id arg_id) {
|
||||||
|
ref = arg_ref<Char>(arg_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
void set(arg_ref<Char> &ref, monostate) {
|
||||||
|
ref.kind = arg_ref<Char>::NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
dynamic_format_specs<Char> &specs_;
|
||||||
|
};
|
||||||
|
|
||||||
template <typename Iterator, typename Handler>
|
template <typename Iterator, typename Handler>
|
||||||
Iterator parse_arg_id(Iterator it, Handler handler) {
|
Iterator parse_arg_id(Iterator it, Handler handler) {
|
||||||
typedef typename Iterator::value_type char_type;
|
typedef typename Iterator::value_type char_type;
|
||||||
@ -3338,15 +3408,13 @@ Iterator parse_arg_id(Iterator it, Handler handler) {
|
|||||||
}
|
}
|
||||||
if (c >= '0' && c <= '9') {
|
if (c >= '0' && c <= '9') {
|
||||||
unsigned index = parse_nonnegative_int(it);
|
unsigned index = parse_nonnegative_int(it);
|
||||||
if (*it != '}' && *it != ':') {
|
if (*it != '}' && *it != ':')
|
||||||
FMT_THROW(format_error("invalid format string"));
|
FMT_THROW(format_error("invalid format string"));
|
||||||
}
|
|
||||||
handler(index);
|
handler(index);
|
||||||
return it;
|
return it;
|
||||||
}
|
}
|
||||||
if (!is_name_start(c)) {
|
if (!is_name_start(c))
|
||||||
FMT_THROW(format_error("invalid format string"));
|
FMT_THROW(format_error("invalid format string"));
|
||||||
}
|
|
||||||
auto start = it;
|
auto start = it;
|
||||||
do {
|
do {
|
||||||
c = *++it;
|
c = *++it;
|
||||||
@ -3357,6 +3425,9 @@ Iterator parse_arg_id(Iterator it, Handler handler) {
|
|||||||
|
|
||||||
// Parses standard format specifiers and sends notifications about parsed
|
// Parses standard format specifiers and sends notifications about parsed
|
||||||
// components to handler.
|
// components to handler.
|
||||||
|
// it: an iterator pointing to the beginning of a null-terminated range of
|
||||||
|
// characters, possibly emulated via null_terminating_iterator, representing
|
||||||
|
// format specifiers.
|
||||||
template <typename Iterator, typename Handler>
|
template <typename Iterator, typename Handler>
|
||||||
Iterator parse_format_specs(Iterator it, Type arg_type, Handler &handler) {
|
Iterator parse_format_specs(Iterator it, Type arg_type, Handler &handler) {
|
||||||
typedef typename Iterator::value_type char_type;
|
typedef typename Iterator::value_type char_type;
|
||||||
@ -3482,13 +3553,16 @@ Iterator parse_format_specs(Iterator it, Type arg_type, Handler &handler) {
|
|||||||
|
|
||||||
// Formats a single argument.
|
// Formats a single argument.
|
||||||
template <typename ArgFormatter, typename Char, typename Context>
|
template <typename ArgFormatter, typename Char, typename Context>
|
||||||
void do_format_arg(basic_buffer<Char> &buffer, const basic_arg<Context> &arg,
|
const Char *do_format_arg(basic_buffer<Char> &buffer,
|
||||||
Context &ctx) {
|
const basic_arg<Context> &arg,
|
||||||
auto &it = ctx.pos();
|
basic_string_view<Char> format,
|
||||||
|
Context &ctx) {
|
||||||
|
auto it = null_terminating_iterator<Char>(format);
|
||||||
basic_format_specs<Char> specs;
|
basic_format_specs<Char> specs;
|
||||||
if (*it == ':') {
|
if (*it == ':') {
|
||||||
if (visit(custom_formatter<Char, Context>(buffer, ctx), arg))
|
format.remove_prefix(1);
|
||||||
return;
|
if (visit(custom_formatter<Char, Context>(buffer, format, ctx), arg))
|
||||||
|
return begin(format);
|
||||||
specs_handler<Context> handler(specs, ctx);
|
specs_handler<Context> handler(specs, ctx);
|
||||||
it = parse_format_specs(it + 1, arg.type(), handler);
|
it = parse_format_specs(it + 1, arg.type(), handler);
|
||||||
}
|
}
|
||||||
@ -3498,28 +3572,69 @@ void do_format_arg(basic_buffer<Char> &buffer, const basic_arg<Context> &arg,
|
|||||||
|
|
||||||
// Format argument.
|
// Format argument.
|
||||||
visit(ArgFormatter(buffer, ctx, specs), arg);
|
visit(ArgFormatter(buffer, ctx, specs), arg);
|
||||||
|
return pointer_from(it);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Specifies whether to format enums.
|
||||||
|
template <typename T, typename Enable = void>
|
||||||
|
struct format_enum : std::integral_constant<bool, std::is_enum<T>::value> {};
|
||||||
} // namespace internal
|
} // namespace internal
|
||||||
|
|
||||||
template <typename T, typename Char = char>
|
// Formatter of objects of type T.
|
||||||
class formatter {
|
template <typename T, typename Char>
|
||||||
public:
|
struct formatter<T, Char,
|
||||||
explicit formatter(basic_context<Char> &ctx) {
|
typename std::enable_if<internal::gettype<T>() != internal::CUSTOM>::type> {
|
||||||
auto &it = ctx.pos();
|
|
||||||
if (*it == ':') {
|
// Parses format specifiers stopping either at the end of the range or at the
|
||||||
++it;
|
// terminating '}'.
|
||||||
internal::specs_handler<basic_context<Char>> handler(specs_, ctx);
|
template <typename Range>
|
||||||
it = parse_format_specs(it, internal::gettype<T>(), handler);
|
auto parse(Range format) -> decltype(begin(format)) {
|
||||||
}
|
auto it = internal::null_terminating_iterator<Char>(format);
|
||||||
|
internal::dynamic_specs_handler<Char> handler(specs_);
|
||||||
|
it = parse_format_specs(it, internal::gettype<T>(), handler);
|
||||||
|
return pointer_from(it);
|
||||||
}
|
}
|
||||||
|
|
||||||
void format(basic_buffer<Char> &buf, const T &val, basic_context<Char> &ctx) {
|
void format(basic_buffer<Char> &buf, const T &val, basic_context<Char> &ctx) {
|
||||||
|
handle_dynamic_spec<internal::width_handler>(
|
||||||
|
specs_.width_, specs_.width_ref, ctx);
|
||||||
|
handle_dynamic_spec<internal::precision_handler>(
|
||||||
|
specs_.precision_, specs_.precision_ref, ctx);
|
||||||
visit(arg_formatter<Char>(buf, ctx, specs_),
|
visit(arg_formatter<Char>(buf, ctx, specs_),
|
||||||
internal::make_arg<basic_context<Char>>(val));
|
internal::make_arg<basic_context<Char>>(val));
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
basic_format_specs<Char> specs_;
|
using arg_ref = internal::arg_ref<Char>;
|
||||||
|
|
||||||
|
template <typename Handler, typename Spec>
|
||||||
|
static void handle_dynamic_spec(
|
||||||
|
Spec &value, arg_ref ref, basic_context<Char> &ctx) {
|
||||||
|
switch (ref.kind) {
|
||||||
|
case arg_ref::NONE:
|
||||||
|
// Do nothing.
|
||||||
|
break;
|
||||||
|
case arg_ref::INDEX:
|
||||||
|
internal::set_dynamic_spec<Handler>(value, ctx.get_arg(ref.index));
|
||||||
|
break;
|
||||||
|
case arg_ref::NAME:
|
||||||
|
internal::set_dynamic_spec<Handler>(value, ctx.get_arg(ref.name));
|
||||||
|
break;
|
||||||
|
// TODO: handle automatic numbering
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal::dynamic_format_specs<Char> specs_;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T, typename Char>
|
||||||
|
struct formatter<T, Char,
|
||||||
|
typename std::enable_if<internal::format_enum<T>::value>::type>
|
||||||
|
: public formatter<int, Char> {
|
||||||
|
template <typename Range>
|
||||||
|
auto parse(Range format) -> decltype(begin(format)) {
|
||||||
|
return begin(format);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename Char>
|
template <typename Char>
|
||||||
@ -3541,9 +3656,9 @@ inline typename basic_context<Char>::format_arg
|
|||||||
template <typename ArgFormatter, typename Char, typename Context>
|
template <typename ArgFormatter, typename Char, typename Context>
|
||||||
void vformat_to(basic_buffer<Char> &buffer, basic_string_view<Char> format_str,
|
void vformat_to(basic_buffer<Char> &buffer, basic_string_view<Char> format_str,
|
||||||
basic_args<Context> args) {
|
basic_args<Context> args) {
|
||||||
basic_context<Char> ctx(format_str, args);
|
basic_context<Char> ctx(args);
|
||||||
auto &it = ctx.pos();
|
auto start = internal::null_terminating_iterator<Char>(format_str);
|
||||||
auto start = it;
|
auto it = start;
|
||||||
using internal::pointer_from;
|
using internal::pointer_from;
|
||||||
while (*it) {
|
while (*it) {
|
||||||
Char c = *it++;
|
Char c = *it++;
|
||||||
@ -3573,7 +3688,8 @@ void vformat_to(basic_buffer<Char> &buffer, basic_string_view<Char> format_str,
|
|||||||
} handler(ctx, arg);
|
} handler(ctx, arg);
|
||||||
|
|
||||||
it = parse_arg_id(it, handler);
|
it = parse_arg_id(it, handler);
|
||||||
internal::do_format_arg<ArgFormatter>(buffer, arg, ctx);
|
format_str.remove_prefix(pointer_from(it) - format_str.data());
|
||||||
|
it = internal::do_format_arg<ArgFormatter>(buffer, arg, format_str, ctx);
|
||||||
if (*it != '}')
|
if (*it != '}')
|
||||||
FMT_THROW(format_error(fmt::format("unknown format specifier")));
|
FMT_THROW(format_error(fmt::format("unknown format specifier")));
|
||||||
start = ++it;
|
start = ++it;
|
||||||
|
@ -77,18 +77,28 @@ void format_value(basic_buffer<Char> &buffer, const T &value) {
|
|||||||
output << value;
|
output << value;
|
||||||
buffer.resize(format_buf.size());
|
buffer.resize(format_buf.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Disable builtin formatting of enums and use operator<< instead.
|
||||||
|
template <typename T>
|
||||||
|
struct format_enum<T,
|
||||||
|
typename std::enable_if<std::is_enum<T>::value>::type> : std::false_type {};
|
||||||
} // namespace internal
|
} // namespace internal
|
||||||
|
|
||||||
// Formats a value.
|
// Formats an object of type T that has an overloaded ostream operator<<.
|
||||||
template <typename Char, typename T>
|
template <typename T, typename Char>
|
||||||
void format_value(basic_buffer<Char> &buf, const T &value,
|
struct formatter<T, Char,
|
||||||
basic_context<Char> &ctx) {
|
typename std::enable_if<
|
||||||
basic_memory_buffer<Char> buffer;
|
internal::gettype<T>() == internal::CUSTOM>::type>
|
||||||
internal::format_value(buffer, value);
|
: formatter<basic_string_view<Char>, Char> {
|
||||||
basic_string_view<Char> str(buffer.data(), buffer.size());
|
|
||||||
internal::do_format_arg< arg_formatter<Char> >(
|
void format(basic_buffer<Char> &buf, const T &value,
|
||||||
buf, internal::make_arg< basic_context<Char> >(str), ctx);
|
basic_context<Char> &ctx) {
|
||||||
}
|
basic_memory_buffer<Char> buffer;
|
||||||
|
internal::format_value(buffer, value);
|
||||||
|
basic_string_view<Char> str(buffer.data(), buffer.size());
|
||||||
|
formatter<basic_string_view<Char>, Char>::format(buf, str, ctx);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
FMT_API void vprint(std::ostream &os, string_view format_str, args args);
|
FMT_API void vprint(std::ostream &os, string_view format_str, args args);
|
||||||
|
|
||||||
|
@ -14,7 +14,7 @@ FMT_FUNC int vfprintf(std::FILE *f, string_view format, printf_args args) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#ifndef FMT_HEADER_ONLY
|
#ifndef FMT_HEADER_ONLY
|
||||||
template void printf_context<char>::format(buffer &);
|
template void printf_context<char>::format(string_view, buffer &);
|
||||||
template void printf_context<wchar_t>::format(wbuffer &);
|
template void printf_context<wchar_t>::format(wstring_view, wbuffer &);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
53
fmt/printf.h
53
fmt/printf.h
@ -286,28 +286,46 @@ class printf_arg_formatter : public internal::arg_formatter_base<Char> {
|
|||||||
|
|
||||||
/** Formats an argument of a custom (user-defined) type. */
|
/** Formats an argument of a custom (user-defined) type. */
|
||||||
void operator()(internal::custom_value<Char> c) {
|
void operator()(internal::custom_value<Char> c) {
|
||||||
const Char format_str[] = {'}', '\0'};
|
const Char format_str_data[] = {'}', '\0'};
|
||||||
|
basic_string_view<Char> format_str = format_str_data;
|
||||||
auto args = basic_args<basic_context<Char>>();
|
auto args = basic_args<basic_context<Char>>();
|
||||||
basic_context<Char> ctx(format_str, args);
|
basic_context<Char> ctx(args);
|
||||||
c.format(this->writer().buffer(), c.value, &ctx);
|
c.format(this->writer().buffer(), c.value, format_str, &ctx);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename Char,
|
||||||
|
typename ArgFormatter = printf_arg_formatter<Char> >
|
||||||
|
class printf_context;
|
||||||
|
|
||||||
|
template <typename T, typename Char = char>
|
||||||
|
struct printf_formatter {
|
||||||
|
const Char *parse(basic_string_view<Char> s) {
|
||||||
|
return s.data();
|
||||||
|
}
|
||||||
|
|
||||||
|
void format(basic_buffer<Char> &buf, const T &value, printf_context<Char> &) {
|
||||||
|
internal::format_value(buf, value);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/** 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>
|
||||||
typename ArgFormatter = printf_arg_formatter<Char> >
|
|
||||||
class printf_context :
|
class printf_context :
|
||||||
private internal::context_base<
|
private internal::context_base<
|
||||||
Char, printf_context<Char, ArgFormatter>> {
|
Char, printf_context<Char, ArgFormatter>> {
|
||||||
public:
|
public:
|
||||||
/** The character type for the output. */
|
/** The character type for the output. */
|
||||||
typedef Char char_type;
|
using char_type = Char;
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
using formatter_type = printf_formatter<T>;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
typedef internal::context_base<Char, printf_context> Base;
|
typedef internal::context_base<Char, printf_context> Base;
|
||||||
typedef typename Base::format_arg format_arg;
|
typedef typename Base::format_arg format_arg;
|
||||||
typedef basic_format_specs<Char> format_specs;
|
typedef basic_format_specs<Char> format_specs;
|
||||||
typedef typename Base::iterator iterator;
|
typedef internal::null_terminating_iterator<Char> iterator;
|
||||||
|
|
||||||
void parse_flags(format_specs &spec, iterator &it);
|
void parse_flags(format_specs &spec, iterator &it);
|
||||||
|
|
||||||
@ -328,12 +346,11 @@ class printf_context :
|
|||||||
appropriate lifetimes.
|
appropriate lifetimes.
|
||||||
\endrst
|
\endrst
|
||||||
*/
|
*/
|
||||||
explicit printf_context(basic_string_view<Char> format_str,
|
explicit printf_context(basic_args<printf_context> args): Base(args) {}
|
||||||
basic_args<printf_context> args)
|
|
||||||
: Base(format_str, args) {}
|
|
||||||
|
|
||||||
/** Formats stored arguments and writes the output to the buffer. */
|
/** Formats stored arguments and writes the output to the buffer. */
|
||||||
FMT_API void format(basic_buffer<Char> &buffer);
|
FMT_API void format(
|
||||||
|
basic_string_view<Char> format_str, basic_buffer<Char> &buffer);
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename Char, typename AF>
|
template <typename Char, typename AF>
|
||||||
@ -415,8 +432,9 @@ unsigned printf_context<Char, AF>::parse_header(
|
|||||||
}
|
}
|
||||||
|
|
||||||
template <typename Char, typename AF>
|
template <typename Char, typename AF>
|
||||||
void printf_context<Char, AF>::format(basic_buffer<Char> &buffer) {
|
void printf_context<Char, AF>::format(
|
||||||
auto start = this->pos();
|
basic_string_view<Char> format_str, basic_buffer<Char> &buffer) {
|
||||||
|
auto start = iterator(format_str);
|
||||||
auto it = start;
|
auto it = start;
|
||||||
using internal::pointer_from;
|
using internal::pointer_from;
|
||||||
while (*it) {
|
while (*it) {
|
||||||
@ -515,17 +533,10 @@ void printf_context<Char, AF>::format(basic_buffer<Char> &buffer) {
|
|||||||
buffer.append(pointer_from(start), pointer_from(it));
|
buffer.append(pointer_from(start), pointer_from(it));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Formats a value.
|
|
||||||
template <typename Char, typename T>
|
|
||||||
void format_value(basic_buffer<Char> &buf, const T &value,
|
|
||||||
printf_context<Char>& ctx) {
|
|
||||||
internal::format_value(buf, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename Char>
|
template <typename Char>
|
||||||
void printf(basic_buffer<Char> &buf, basic_string_view<Char> format,
|
void printf(basic_buffer<Char> &buf, basic_string_view<Char> format,
|
||||||
basic_args<printf_context<Char>> args) {
|
basic_args<printf_context<Char>> args) {
|
||||||
printf_context<Char>(format, args).format(buf);
|
printf_context<Char>(args).format(format, buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
typedef basic_args<printf_context<char>> printf_args;
|
typedef basic_args<printf_context<char>> printf_args;
|
||||||
|
72
fmt/time.h
72
fmt/time.h
@ -15,40 +15,46 @@
|
|||||||
|
|
||||||
namespace fmt {
|
namespace fmt {
|
||||||
|
|
||||||
void format_value(buffer &buf, const std::tm &tm, context &ctx) {
|
template <>
|
||||||
auto &it = ctx.pos();
|
struct formatter<std::tm> {
|
||||||
if (*it == ':')
|
template <typename Range>
|
||||||
++it;
|
auto parse(Range format) -> decltype(begin(format)) {
|
||||||
auto end = it;
|
auto it = internal::null_terminating_iterator<char>(format);
|
||||||
while (*end && *end != '}')
|
if (*it == ':')
|
||||||
++end;
|
++it;
|
||||||
if (*end != '}')
|
auto end = it;
|
||||||
FMT_THROW(format_error("missing '}' in format string"));
|
while (*end && *end != '}')
|
||||||
memory_buffer format;
|
++end;
|
||||||
format.reserve(end - it + 1);
|
tm_format.reserve(end - it + 1);
|
||||||
using internal::pointer_from;
|
using internal::pointer_from;
|
||||||
format.append(pointer_from(it), pointer_from(end));
|
tm_format.append(pointer_from(it), pointer_from(end));
|
||||||
format.push_back('\0');
|
tm_format.push_back('\0');
|
||||||
std::size_t start = buf.size();
|
return pointer_from(end);
|
||||||
for (;;) {
|
|
||||||
std::size_t size = buf.capacity() - start;
|
|
||||||
std::size_t count = std::strftime(&buf[start], size, &format[0], &tm);
|
|
||||||
if (count != 0) {
|
|
||||||
buf.resize(start + count);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (size >= format.size() * 256) {
|
|
||||||
// If the buffer is 256 times larger than the format string, assume
|
|
||||||
// that `strftime` gives an empty result. There doesn't seem to be a
|
|
||||||
// better way to distinguish the two cases:
|
|
||||||
// https://github.com/fmtlib/fmt/issues/367
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
const std::size_t MIN_GROWTH = 10;
|
|
||||||
buf.reserve(buf.capacity() + (size > MIN_GROWTH ? size : MIN_GROWTH));
|
|
||||||
}
|
}
|
||||||
it = end;
|
|
||||||
}
|
void format(buffer &buf, const std::tm &tm, context &ctx) {
|
||||||
|
std::size_t start = buf.size();
|
||||||
|
for (;;) {
|
||||||
|
std::size_t size = buf.capacity() - start;
|
||||||
|
std::size_t count = std::strftime(&buf[start], size, &tm_format[0], &tm);
|
||||||
|
if (count != 0) {
|
||||||
|
buf.resize(start + count);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (size >= tm_format.size() * 256) {
|
||||||
|
// If the buffer is 256 times larger than the format string, assume
|
||||||
|
// that `strftime` gives an empty result. There doesn't seem to be a
|
||||||
|
// better way to distinguish the two cases:
|
||||||
|
// https://github.com/fmtlib/fmt/issues/367
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
const std::size_t MIN_GROWTH = 10;
|
||||||
|
buf.reserve(buf.capacity() + (size > MIN_GROWTH ? size : MIN_GROWTH));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
memory_buffer tm_format;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // FMT_TIME_H_
|
#endif // FMT_TIME_H_
|
||||||
|
@ -65,8 +65,8 @@ std::string custom_vsprintf(
|
|||||||
const char* format_str,
|
const char* format_str,
|
||||||
fmt::basic_args<CustomPrintfFormatter> args) {
|
fmt::basic_args<CustomPrintfFormatter> args) {
|
||||||
fmt::memory_buffer buffer;
|
fmt::memory_buffer buffer;
|
||||||
CustomPrintfFormatter formatter(format_str, args);
|
CustomPrintfFormatter formatter(args);
|
||||||
formatter.format(buffer);
|
formatter.format(format_str, buffer);
|
||||||
return std::string(buffer.data(), buffer.size());
|
return std::string(buffer.data(), buffer.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1227,8 +1227,18 @@ TEST(FormatterTest, FormatStringView) {
|
|||||||
EXPECT_EQ("test", format("{0}", string_view("test")));
|
EXPECT_EQ("test", format("{0}", string_view("test")));
|
||||||
}
|
}
|
||||||
|
|
||||||
void format_value(fmt::buffer &buf, const Date &d, fmt::context &) {
|
namespace fmt {
|
||||||
fmt::format_to(buf, "{}-{}-{}", d.year(), d.month(), d.day());
|
template <>
|
||||||
|
struct formatter<Date> {
|
||||||
|
template <typename Range>
|
||||||
|
auto parse(Range format) -> decltype(begin(format)) {
|
||||||
|
return begin(format);
|
||||||
|
}
|
||||||
|
|
||||||
|
void format(buffer &buf, const Date &d, context &) {
|
||||||
|
format_to(buf, "{}-{}-{}", d.year(), d.month(), d.day());
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(FormatterTest, FormatCustom) {
|
TEST(FormatterTest, FormatCustom) {
|
||||||
@ -1239,9 +1249,13 @@ TEST(FormatterTest, FormatCustom) {
|
|||||||
|
|
||||||
class Answer {};
|
class Answer {};
|
||||||
|
|
||||||
void format_value(fmt::buffer &buf, Answer, fmt::context &ctx) {
|
namespace fmt {
|
||||||
fmt::formatter<int> f(ctx);
|
template <>
|
||||||
f.format(buf, 42, ctx);
|
struct formatter<Answer> : formatter<int> {
|
||||||
|
void format(fmt::buffer &buf, Answer, fmt::context &ctx) {
|
||||||
|
formatter<int>::format(buf, 42, ctx);
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(FormatterTest, CustomFormat) {
|
TEST(FormatterTest, CustomFormat) {
|
||||||
|
@ -65,7 +65,7 @@ struct TestArgFormatter : fmt::arg_formatter<char> {
|
|||||||
|
|
||||||
TEST(OStreamTest, CustomArg) {
|
TEST(OStreamTest, CustomArg) {
|
||||||
fmt::memory_buffer buffer;
|
fmt::memory_buffer buffer;
|
||||||
fmt::context ctx("}", fmt::args());
|
fmt::context ctx((fmt::args()));
|
||||||
fmt::format_specs spec;
|
fmt::format_specs spec;
|
||||||
TestArgFormatter af(buffer, ctx, spec);
|
TestArgFormatter af(buffer, ctx, spec);
|
||||||
visit(af, fmt::internal::make_arg<fmt::context>(TestEnum()));
|
visit(af, fmt::internal::make_arg<fmt::context>(TestEnum()));
|
||||||
|
@ -66,19 +66,27 @@ namespace {
|
|||||||
|
|
||||||
struct Test {};
|
struct Test {};
|
||||||
|
|
||||||
template <typename Char>
|
|
||||||
void format_value(fmt::basic_buffer<Char> &b, Test,
|
|
||||||
fmt::basic_context<Char> &) {
|
|
||||||
const Char *test = "test";
|
|
||||||
b.append(test, test + std::strlen(test));
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename Context, typename T>
|
template <typename Context, typename T>
|
||||||
basic_arg<Context> make_arg(const T &value) {
|
basic_arg<Context> make_arg(const T &value) {
|
||||||
return fmt::internal::make_arg<Context>(value);
|
return fmt::internal::make_arg<Context>(value);
|
||||||
}
|
}
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
|
namespace fmt {
|
||||||
|
template <typename Char>
|
||||||
|
struct formatter<Test, Char> {
|
||||||
|
template <typename Range>
|
||||||
|
auto parse(Range format) -> decltype(begin(format)) {
|
||||||
|
return begin(format);
|
||||||
|
}
|
||||||
|
|
||||||
|
void format(basic_buffer<Char> &b, Test, basic_context<Char> &) {
|
||||||
|
const Char *test = "test";
|
||||||
|
b.append(test, test + std::strlen(test));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
void CheckForwarding(
|
void CheckForwarding(
|
||||||
MockAllocator<int> &alloc, AllocatorRef< MockAllocator<int> > &ref) {
|
MockAllocator<int> &alloc, AllocatorRef< MockAllocator<int> > &ref) {
|
||||||
int mem;
|
int mem;
|
||||||
@ -424,20 +432,33 @@ TEST(UtilTest, FormatArgs) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
struct CustomContext {
|
struct CustomContext {
|
||||||
typedef char char_type;
|
using char_type = char;
|
||||||
bool called;
|
|
||||||
};
|
|
||||||
|
|
||||||
void format_value(fmt::buffer &, const Test &, CustomContext &ctx) {
|
template <typename T>
|
||||||
ctx.called = true;
|
struct formatter_type {
|
||||||
}
|
template <typename Range>
|
||||||
|
auto parse(Range range) -> decltype(begin(range)) {
|
||||||
|
return begin(range);
|
||||||
|
}
|
||||||
|
|
||||||
|
void format(fmt::buffer &, const T &, CustomContext& ctx) {
|
||||||
|
ctx.called = true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
bool called;
|
||||||
|
|
||||||
|
fmt::string_view format() { return ""; }
|
||||||
|
void advance_to(const char *) {}
|
||||||
|
};
|
||||||
|
|
||||||
TEST(UtilTest, MakeValueWithCustomFormatter) {
|
TEST(UtilTest, MakeValueWithCustomFormatter) {
|
||||||
::Test t;
|
::Test t;
|
||||||
fmt::internal::value<CustomContext> arg(t);
|
fmt::internal::value<CustomContext> arg(t);
|
||||||
CustomContext ctx = {false};
|
CustomContext ctx = {false};
|
||||||
fmt::memory_buffer buffer;
|
fmt::memory_buffer buffer;
|
||||||
arg.custom.format(buffer, &t, &ctx);
|
fmt::string_view format_str;
|
||||||
|
arg.custom.format(buffer, &t, format_str, &ctx);
|
||||||
EXPECT_TRUE(ctx.called);
|
EXPECT_TRUE(ctx.called);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -581,8 +602,9 @@ TEST(UtilTest, CustomArg) {
|
|||||||
testing::Invoke([&](fmt::internal::custom_value<char> custom) {
|
testing::Invoke([&](fmt::internal::custom_value<char> custom) {
|
||||||
EXPECT_EQ(&test, custom.value);
|
EXPECT_EQ(&test, custom.value);
|
||||||
fmt::memory_buffer buffer;
|
fmt::memory_buffer buffer;
|
||||||
fmt::context ctx("}", fmt::args());
|
fmt::context ctx((fmt::args()));
|
||||||
custom.format(buffer, &test, &ctx);
|
fmt::string_view format_str;
|
||||||
|
custom.format(buffer, &test, format_str, &ctx);
|
||||||
EXPECT_EQ("test", std::string(buffer.data(), buffer.size()));
|
EXPECT_EQ("test", std::string(buffer.data(), buffer.size()));
|
||||||
return Visitor::Result();
|
return Visitor::Result();
|
||||||
}));
|
}));
|
||||||
|
Reference in New Issue
Block a user