Commit 246bdafc authored by Victor Zverovich's avatar Victor Zverovich

Add FMT_STRING macro for compile-time strings

parent e8055433
......@@ -2027,1636 +2027,1699 @@ class context_base : public parse_context<Char>{
public:
parse_context<Char> &get_parse_context() { return *this; }
};
} // namespace internal
/** The default argument formatter. */
struct format_string {};
template <typename Char>
class arg_formatter : public internal::arg_formatter_base<Char> {
private:
basic_context<Char> &ctx_;
constexpr bool is_name_start(Char c) {
return ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || '_' == c;
}
typedef internal::arg_formatter_base<Char> Base;
// Parses the input as an unsigned integer. This function assumes that the
// first character is a digit and presence of a non-digit character at the end.
// it: an iterator pointing to the beginning of the input range.
template <typename Iterator, typename ErrorHandler>
constexpr unsigned parse_nonnegative_int(Iterator &it, ErrorHandler& handler) {
assert('0' <= *it && *it <= '9');
unsigned value = 0;
do {
unsigned new_value = value * 10 + (*it - '0');
// Workaround for MSVC "setup_exception stack overflow" error:
auto next = it;
++next;
it = next;
// Check if value wrapped around.
if (new_value < value) {
value = (std::numeric_limits<unsigned>::max)();
break;
}
value = new_value;
} while ('0' <= *it && *it <= '9');
// Convert to unsigned to prevent a warning.
unsigned max_int = (std::numeric_limits<int>::max)();
if (value > max_int)
handler.on_error("number is too big");
return value;
}
template <typename Char, typename Context>
class custom_formatter {
private:
basic_buffer<Char> &buffer_;
Context &ctx_;
public:
typedef typename Base::format_specs format_specs;
custom_formatter(basic_buffer<Char> &buffer, Context &ctx)
: buffer_(buffer), ctx_(ctx) {}
/**
\rst
Constructs an argument formatter object.
*buffer* is a reference to the buffer to be used for output,
*ctx* is a reference to the formatting context, *spec* contains
format specifier information for standard argument types.
\endrst
*/
arg_formatter(basic_buffer<Char> &buffer, basic_context<Char> &ctx,
format_specs &spec)
: internal::arg_formatter_base<Char>(buffer, spec), ctx_(ctx) {}
bool operator()(internal::custom_value<Char> custom) {
custom.format(buffer_, custom.value, &ctx_);
return true;
}
using internal::arg_formatter_base<Char>::operator();
template <typename T>
bool operator()(T) { return false; }
};
/** Formats an argument of a custom (user-defined) type. */
void operator()(internal::custom_value<Char> c) {
c.format(this->writer().buffer(), c.value, &ctx_);
}
template <typename T>
struct is_integer {
enum {
value = std::is_integral<T>::value && !std::is_same<T, bool>::value &&
!std::is_same<T, char>::value && !std::is_same<T, wchar_t>::value
};
};
template <typename Char>
class basic_context :
public internal::context_base<Char, basic_context<Char>> {
template <typename ErrorHandler>
class width_checker {
public:
/** The character type for the output. */
using char_type = Char;
explicit constexpr width_checker(ErrorHandler &eh) : handler_(eh) {}
template <typename T>
using formatter_type = formatter<T, Char>;
private:
internal::arg_map<basic_context<Char>> map_;
FMT_DISALLOW_COPY_AND_ASSIGN(basic_context);
constexpr typename std::enable_if<
is_integer<T>::value, unsigned long long>::type operator()(T value) {
if (is_negative(value))
handler_.on_error("negative width");
return value;
}
typedef internal::context_base<Char, basic_context<Char>> Base;
template <typename T>
constexpr typename std::enable_if<
!is_integer<T>::value, unsigned long long>::type operator()(T) {
handler_.on_error("width is not integer");
return 0;
}
typedef typename Base::format_arg format_arg;
using Base::get_arg;
private:
ErrorHandler &handler_;
};
template <typename ErrorHandler>
class precision_checker {
public:
/**
\rst
Constructs a ``basic_context`` object. References to the arguments are
stored in the object so make sure they have appropriate lifetimes.
\endrst
*/
basic_context(
basic_string_view<Char> format_str, basic_args<basic_context> args)
: Base(format_str, args) {}
explicit constexpr precision_checker(ErrorHandler &eh) : handler_(eh) {}
format_arg next_arg() {
const char *error = 0;
format_arg arg = this->do_get_arg(this->next_arg_index(error), error);
if (error)
FMT_THROW(format_error(error));
return arg;
template <typename T>
constexpr typename std::enable_if<
is_integer<T>::value, unsigned long long>::type operator()(T value) {
if (is_negative(value))
handler_.on_error("negative precision");
return value;
}
format_arg get_arg(unsigned arg_index) {
const char *error = 0;
format_arg arg = this->do_get_arg(arg_index, error);
if (error)
FMT_THROW(format_error(error));
return arg;
template <typename T>
constexpr typename std::enable_if<
!is_integer<T>::value, unsigned long long>::type operator()(T) {
handler_.on_error("precision is not integer");
return 0;
}
// Checks if manual indexing is used and returns the argument with
// specified name.
format_arg get_arg(basic_string_view<Char> name);
};
/**
An error returned by an operating system or a language runtime,
for example a file opening error.
*/
class system_error : public std::runtime_error {
private:
void init(int err_code, string_view format_str, args args);
protected:
int error_code_;
system_error() : std::runtime_error("") {}
ErrorHandler &handler_;
};
// A format specifier handler that sets fields in basic_format_specs.
template <typename Char>
class specs_setter : public error_handler {
public:
/**
\rst
Constructs a :class:`fmt::system_error` object with a description
formatted with `fmt::format_system_error`. *message* and additional
arguments passed into the constructor are formatted similarly to
`fmt::format`.
explicit constexpr specs_setter(basic_format_specs<Char> &specs):
specs_(specs) {}
**Example**::
constexpr void on_align(alignment align) { specs_.align_ = align; }
constexpr void on_fill(Char fill) { specs_.fill_ = fill; }
constexpr void on_plus() { specs_.flags_ |= SIGN_FLAG | PLUS_FLAG; }
constexpr void on_minus() { specs_.flags_ |= MINUS_FLAG; }
constexpr void on_space() { specs_.flags_ |= SIGN_FLAG; }
constexpr void on_hash() { specs_.flags_ |= HASH_FLAG; }
// This throws a system_error with the description
// cannot open file 'madeup': No such file or directory
// or similar (system message may vary).
const char *filename = "madeup";
std::FILE *file = std::fopen(filename, "r");
if (!file)
throw fmt::system_error(errno, "cannot open file '{}'", filename);
\endrst
*/
template <typename... Args>
system_error(int error_code, string_view message, const Args & ... args)
: std::runtime_error("") {
init(error_code, message, make_args(args...));
constexpr void on_zero() {
specs_.align_ = ALIGN_NUMERIC;
specs_.fill_ = '0';
}
~system_error() throw();
constexpr void on_width(unsigned width) { specs_.width_ = width; }
constexpr void on_precision(unsigned precision) {
specs_.precision_ = precision;
}
constexpr void end_precision() {}
int error_code() const { return error_code_; }
constexpr void on_type(Char type) { specs_.type_ = type; }
protected:
basic_format_specs<Char> &specs_;
};
/**
\rst
Formats an error returned by an operating system or a language runtime,
for example a file opening error, and writes it to *out* in the following
form:
// A format specifier handler that checks if specifiers are consistent with the
// argument type.
template <typename Handler>
class specs_checker : public Handler {
public:
constexpr specs_checker(const Handler& handler, internal::type arg_type)
: Handler(handler), arg_type_(arg_type) {}
.. parsed-literal::
*<message>*: *<system-message>*
constexpr void on_align(alignment align) {
if (align == ALIGN_NUMERIC)
require_numeric_argument('=');
Handler::on_align(align);
}
where *<message>* is the passed message and *<system-message>* is
the system message corresponding to the error code.
*error_code* is a system error code as given by ``errno``.
If *error_code* is not a valid error code such as -1, the system message
may look like "Unknown error -1" and is platform-dependent.
\endrst
*/
FMT_API void format_system_error(fmt::buffer &out, int error_code,
fmt::string_view message) FMT_NOEXCEPT;
constexpr void on_plus() {
check_sign('+');
Handler::on_plus();
}
/**
\rst
This template provides operations for formatting and writing data into a
character buffer. The output buffer is specified by a subclass such as
:class:`fmt::BasicMemoryWriter`.
constexpr void on_minus() {
check_sign('-');
Handler::on_minus();
}
You can use one of the following typedefs for common character types:
constexpr void on_space() {
check_sign(' ');
Handler::on_space();
}
+---------+-----------------------+
| Type | Definition |
+=========+=======================+
| writer | basic_writer<char> |
+---------+-----------------------+
| wwriter | basic_writer<wchar_t> |
+---------+-----------------------+
\endrst
*/
template <typename Char>
class basic_writer {
public:
typedef basic_format_specs<Char> format_specs;
private:
// Output buffer.
basic_buffer<Char> &buffer_;
FMT_DISALLOW_COPY_AND_ASSIGN(basic_writer);
#if FMT_SECURE_SCL
typedef stdext::checked_array_iterator<Char*> pointer_type;
// Returns pointer value.
static Char *get(pointer_type p) { return p.base(); }
#else
typedef Char *pointer_type;
static Char *get(Char *p) { return p; }
#endif
constexpr void on_hash() {
require_numeric_argument('#');
Handler::on_hash();
}
// Fills the padding around the content and returns the pointer to the
// content area.
static pointer_type fill_padding(pointer_type buffer,
unsigned total_size, std::size_t content_size, wchar_t fill);
constexpr void on_zero() {
require_numeric_argument('0');
Handler::on_zero();
}
// Grows the buffer by n characters and returns a pointer to the newly
// allocated area.
pointer_type grow_buffer(std::size_t n) {
std::size_t size = buffer_.size();
buffer_.resize(size + n);
return internal::make_ptr(&buffer_[size], n);
constexpr void end_precision() {
if (is_integral(arg_type_) || arg_type_ == POINTER) {
report_error("precision not allowed in {} format specifier",
arg_type_ == POINTER ? "pointer" : "integer");
}
}
// Writes an unsigned decimal integer.
template <typename UInt>
Char *write_unsigned_decimal(UInt value, unsigned prefix_size = 0) {
unsigned num_digits = internal::count_digits(value);
Char *ptr = get(grow_buffer(prefix_size + num_digits));
internal::format_decimal(ptr + prefix_size, value, num_digits);
return ptr;
private:
template <typename... Args>
void report_error(string_view format_str, const Args &... args) {
this->on_error(format(format_str, args...).c_str());
}
// Writes a decimal integer.
template <typename Int>
void write_decimal(Int value) {
typedef typename internal::int_traits<Int>::main_type main_type;
main_type abs_value = static_cast<main_type>(value);
if (internal::is_negative(value)) {
abs_value = 0 - abs_value;
*write_unsigned_decimal(abs_value, 1) = '-';
} else {
write_unsigned_decimal(abs_value, 0);
template <typename Char>
constexpr void require_numeric_argument(Char spec) {
if (!is_numeric(arg_type_)) {
report_error("format specifier '{}' requires numeric argument",
static_cast<char>(spec));
}
}
// Prepare a buffer for integer formatting.
pointer_type prepare_int_buffer(unsigned num_digits,
const empty_spec &, const char *prefix, unsigned prefix_size) {
unsigned size = prefix_size + num_digits;
pointer_type p = grow_buffer(size);
std::uninitialized_copy(prefix, prefix + prefix_size, p);
return p + size - 1;
template <typename Char>
constexpr void check_sign(Char sign) {
require_numeric_argument(sign);
if (is_integral(arg_type_) && arg_type_ != INT && arg_type_ != LONG_LONG &&
arg_type_ != CHAR) {
report_error("format specifier '{}' requires signed argument",
static_cast<char>(sign));
}
}
template <typename Spec>
pointer_type prepare_int_buffer(unsigned num_digits,
const Spec &spec, const char *prefix, unsigned prefix_size);
internal::type arg_type_;
};
// Writes a formatted integer.
template <typename T, typename Spec>
void write_int(T value, const Spec& spec);
template <template <typename> class Handler, typename T, typename Context>
constexpr void set_dynamic_spec(T &value, basic_arg<Context> arg) {
error_handler eh;
unsigned long long big_value = visit(Handler<error_handler>(eh), arg);
if (big_value > (std::numeric_limits<int>::max)())
eh.on_error("number is too big");
value = static_cast<int>(big_value);
}
// Formats a floating-point number (double or long double).
template <typename T>
void write_double(T value, const format_specs &spec);
struct auto_id {};
// Writes a formatted string.
template <typename StrChar>
pointer_type write_str(
const StrChar *s, std::size_t size, const align_spec &spec);
// The standard format specifier handler with checking.
template <typename Context>
class specs_handler: public specs_setter<typename Context::char_type> {
public:
typedef typename Context::char_type char_type;
template <typename StrChar>
void write_str(basic_string_view<StrChar> str, const format_specs &spec);
constexpr specs_handler(basic_format_specs<char_type> &specs, Context &ctx)
: specs_setter<char_type>(specs), context_(ctx) {}
// Appends floating-point length specifier to the format string.
// The second argument is only used for overload resolution.
void append_float_length(Char *&format_ptr, long double) {
*format_ptr++ = 'L';
template <typename Id>
constexpr void on_dynamic_width(Id arg_id) {
set_dynamic_spec<width_checker>(this->specs_.width_, get_arg(arg_id));
}
template<typename T>
void append_float_length(Char *&, T) {}
template <typename Id>
constexpr void on_dynamic_precision(Id arg_id) {
set_dynamic_spec<precision_checker>(
this->specs_.precision_, get_arg(arg_id));
}
template <typename Char_>
friend class internal::arg_formatter_base;
private:
constexpr basic_arg<Context> get_arg(auto_id) {
return context_.next_arg();
}
public:
/**
Constructs a ``basic_writer`` object.
*/
explicit basic_writer(basic_buffer<Char> &b) : buffer_(b) {}
template <typename Id>
constexpr basic_arg<Context> get_arg(Id arg_id) {
context_.check_arg_id(arg_id);
return context_.get_arg(arg_id);
}
/**
\rst
Destroys the ``basic_writer`` object.
\endrst
*/
virtual ~basic_writer() {}
Context &context_;
};
/**
Returns the total number of characters written.
*/
std::size_t size() const { return buffer_.size(); }
// An argument reference.
template <typename Char>
struct arg_ref {
enum Kind { NONE, INDEX, NAME };
/**
Returns a pointer to the output buffer content. No terminating null
character is appended.
*/
const Char *data() const FMT_NOEXCEPT { return &buffer_[0]; }
constexpr arg_ref() : kind(NONE), index(0) {}
constexpr explicit arg_ref(unsigned index) : kind(INDEX), index(index) {}
explicit arg_ref(basic_string_view<Char> name) : kind(NAME), name(name) {}
/**
Returns a pointer to the output buffer content with terminating null
character appended.
*/
const Char *c_str() const {
std::size_t size = buffer_.size();
buffer_.reserve(size + 1);
buffer_[size] = '\0';
return &buffer_[0];
constexpr arg_ref &operator=(unsigned index) {
kind = INDEX;
this->index = index;
return *this;
}
/**
\rst
Returns the content of the output buffer as an `std::string`.
\endrst
*/
std::basic_string<Char> str() const {
return std::basic_string<Char>(&buffer_[0], buffer_.size());
}
Kind kind;
union {
unsigned index;
basic_string_view<Char> name;
};
};
void write(int value) {
write_decimal(value);
}
void write(long value) {
write_decimal(value);
}
void write(long long value) {
write_decimal(value);
}
// Format specifiers with width and precision resolved at formatting rather
// than parsing time to allow re-using the same parsed specifiers with
// differents sets of arguments (precompilation of format strings).
template <typename Char>
struct dynamic_format_specs : basic_format_specs<Char> {
arg_ref<Char> width_ref;
arg_ref<Char> precision_ref;
};
/**
\rst
Formats *value* and writes it to the buffer.
\endrst
*/
template <typename T, typename... FormatSpecs>
typename std::enable_if<std::is_integral<T>::value, void>::type
write(T value, FormatSpecs... specs) {
write_int(value, format_specs(specs...));
}
// Format spec handler that saves references to arguments representing dynamic
// width and precision to be resolved at formatting time.
template <typename ParseContext>
class dynamic_specs_handler :
public specs_setter<typename ParseContext::char_type> {
public:
using char_type = typename ParseContext::char_type;
void write(double value) {
write_double(value, format_specs());
constexpr dynamic_specs_handler(
dynamic_format_specs<char_type> &specs, ParseContext &ctx)
: specs_setter<char_type>(specs), specs_(specs), context_(ctx) {}
template <typename Id>
constexpr void on_dynamic_width(Id arg_id) {
specs_.width_ref = make_arg_ref(arg_id);
}
/**
\rst
Formats *value* using the general format for floating-point numbers
(``'g'``) and writes it to the buffer.
\endrst
*/
void write(long double value) {
write_double(value, format_specs());
template <typename Id>
constexpr void on_dynamic_precision(Id arg_id) {
specs_.precision_ref = make_arg_ref(arg_id);
}
/**
Writes a character to the buffer.
*/
void write(char value) {
buffer_.push_back(value);
private:
using arg_ref_type = arg_ref<char_type>;
template <typename Id>
constexpr arg_ref_type make_arg_ref(Id arg_id) {
context_.check_arg_id(arg_id);
return arg_ref_type(arg_id);
}
void write(wchar_t value) {
internal::require_wchar<Char>();
buffer_.push_back(value);
constexpr arg_ref_type make_arg_ref(auto_id) {
const char *error = 0;
auto index = context_.next_arg_index(error);
if (error)
FMT_THROW(format_error(error));
return arg_ref_type(index);
}
/**
\rst
Writes *value* to the buffer.
\endrst
*/
void write(string_view value) {
const char *str = value.data();
buffer_.append(str, str + value.size());
dynamic_format_specs<char_type> &specs_;
ParseContext &context_;
};
template <typename Iterator, typename IDHandler>
constexpr Iterator parse_arg_id(Iterator it, IDHandler &&handler) {
using char_type = typename std::iterator_traits<Iterator>::value_type;
char_type c = *it;
if (c == '}' || c == ':') {
handler();
return it;
}
if (c >= '0' && c <= '9') {
unsigned index = parse_nonnegative_int(it, handler);
if (*it != '}' && *it != ':') {
handler.on_error("invalid format string");
return it;
}
handler(index);
return it;
}
if (!is_name_start(c)) {
handler.on_error("invalid format string");
return it;
}
auto start = it;
do {
c = *++it;
} while (is_name_start(c) || ('0' <= c && c <= '9'));
handler(basic_string_view<char_type>(pointer_from(start), it - start));
return it;
}
void write(basic_string_view<wchar_t> value) {
internal::require_wchar<Char>();
const wchar_t *str = value.data();
buffer_.append(str, str + value.size());
// Adapts SpecHandler to IDHandler API for dynamic width.
template <typename SpecHandler, typename Char>
struct width_adapter {
explicit constexpr width_adapter(SpecHandler &h) : handler(h) {}
constexpr void operator()() { handler.on_dynamic_width(auto_id()); }
constexpr void operator()(unsigned id) { handler.on_dynamic_width(id); }
constexpr void operator()(basic_string_view<Char> id) {
handler.on_dynamic_width(id);
}
template <typename... FormatSpecs>
void write(basic_string_view<Char> str, FormatSpecs... specs) {
write_str(str, format_specs(specs...));
constexpr void on_error(const char *message) { handler.on_error(message); }
SpecHandler &handler;
};
// Adapts SpecHandler to IDHandler API for dynamic precision.
template <typename SpecHandler, typename Char>
struct precision_adapter {
explicit constexpr precision_adapter(SpecHandler &h) : handler(h) {}
constexpr void operator()() { handler.on_dynamic_precision(auto_id()); }
constexpr void operator()(unsigned id) { handler.on_dynamic_precision(id); }
constexpr void operator()(basic_string_view<Char> id) {
handler.on_dynamic_precision(id);
}
void clear() FMT_NOEXCEPT { buffer_.resize(0); }
constexpr void on_error(const char *message) { handler.on_error(message); }
basic_buffer<Char> &buffer() FMT_NOEXCEPT { return buffer_; }
SpecHandler &handler;
};
template <typename Char>
template <typename StrChar>
typename basic_writer<Char>::pointer_type basic_writer<Char>::write_str(
const StrChar *s, std::size_t size, const align_spec &spec) {
pointer_type out = pointer_type();
if (spec.width() > size) {
out = grow_buffer(spec.width());
Char fill = internal::char_traits<Char>::cast(spec.fill());
if (spec.align() == ALIGN_RIGHT) {
std::uninitialized_fill_n(out, spec.width() - size, fill);
out += spec.width() - size;
} else if (spec.align() == ALIGN_CENTER) {
out = fill_padding(out, spec.width(), size, fill);
// Parses standard format specifiers and sends notifications about parsed
// 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 SpecHandler>
constexpr Iterator parse_format_specs(Iterator it, SpecHandler &&handler) {
using char_type = typename std::iterator_traits<Iterator>::value_type;
// Parse fill and alignment.
if (char_type c = *it) {
alignment align = ALIGN_DEFAULT;
int i = 1;
do {
auto p = it + i;
switch (*p) {
case '<':
align = ALIGN_LEFT;
break;
case '>':
align = ALIGN_RIGHT;
break;
case '=':
align = ALIGN_NUMERIC;
break;
case '^':
align = ALIGN_CENTER;
break;
}
if (align != ALIGN_DEFAULT) {
handler.on_align(align);
if (p != it) {
if (c == '}') break;
if (c == '{') {
handler.on_error("invalid fill character '{'");
return it;
}
it += 2;
handler.on_fill(c);
} else ++it;
break;
}
} while (--i >= 0);
}
// Parse sign.
switch (*it) {
case '+':
handler.on_plus();
++it;
break;
case '-':
handler.on_minus();
++it;
break;
case ' ':
handler.on_space();
++it;
break;
}
if (*it == '#') {
handler.on_hash();
++it;
}
// Parse zero flag.
if (*it == '0') {
handler.on_zero();
++it;
}
// Parse width.
if ('0' <= *it && *it <= '9') {
handler.on_width(parse_nonnegative_int(it, handler));
} else if (*it == '{') {
it = parse_arg_id(it + 1, width_adapter<SpecHandler, char_type>(handler));
if (*it++ != '}') {
handler.on_error("invalid format string");
return it;
}
}
// Parse precision.
if (*it == '.') {
++it;
if ('0' <= *it && *it <= '9') {
handler.on_precision(parse_nonnegative_int(it, handler));
} else if (*it == '{') {
it = parse_arg_id(
it + 1, precision_adapter<SpecHandler, char_type>(handler));
if (*it++ != '}') {
handler.on_error("invalid format string");
return it;
}
} else {
std::uninitialized_fill_n(out + size, spec.width() - size, fill);
handler.on_error("missing precision specifier");
return it;
}
} else {
out = grow_buffer(size);
handler.end_precision();
}
std::uninitialized_copy(s, s + size, out);
return out;
}
template <typename Char>
template <typename StrChar>
void basic_writer<Char>::write_str(
basic_string_view<StrChar> s, const format_specs &spec) {
// Check if StrChar is convertible to Char.
internal::char_traits<Char>::convert(StrChar());
if (spec.type_ && spec.type_ != 's')
internal::report_unknown_type(spec.type_, "string");
const StrChar *str_value = s.data();
std::size_t str_size = s.size();
if (str_size == 0 && !str_value)
FMT_THROW(format_error("string pointer is null"));
std::size_t precision = static_cast<std::size_t>(spec.precision_);
if (spec.precision_ >= 0 && precision < str_size)
str_size = precision;
write_str(str_value, str_size, spec);
// Parse type.
if (*it != '}' && *it)
handler.on_type(*it++);
return it;
}
template <typename Char>
typename basic_writer<Char>::pointer_type basic_writer<Char>::fill_padding(
pointer_type buffer, unsigned total_size,
std::size_t content_size, wchar_t fill) {
std::size_t padding = total_size - content_size;
std::size_t left_padding = padding / 2;
Char fill_char = internal::char_traits<Char>::cast(fill);
std::uninitialized_fill_n(buffer, left_padding, fill_char);
buffer += left_padding;
pointer_type content = buffer;
std::uninitialized_fill_n(buffer + content_size,
padding - left_padding, fill_char);
return content;
}
template <typename Handler, typename Char>
struct id_adapter {
constexpr explicit id_adapter(Handler &h): handler(h) {}
template <typename Char>
template <typename Spec>
typename basic_writer<Char>::pointer_type
basic_writer<Char>::prepare_int_buffer(
unsigned num_digits, const Spec &spec,
const char *prefix, unsigned prefix_size) {
unsigned width = spec.width();
alignment align = spec.align();
Char fill = internal::char_traits<Char>::cast(spec.fill());
if (spec.precision() > static_cast<int>(num_digits)) {
// Octal prefix '0' is counted as a digit, so ignore it if precision
// is specified.
if (prefix_size > 0 && prefix[prefix_size - 1] == '0')
--prefix_size;
unsigned number_size =
prefix_size + internal::to_unsigned(spec.precision());
align_spec subspec(number_size, '0', ALIGN_NUMERIC);
if (number_size >= width)
return prepare_int_buffer(num_digits, subspec, prefix, prefix_size);
buffer_.reserve(width);
unsigned fill_size = width - number_size;
if (align != ALIGN_LEFT) {
pointer_type p = grow_buffer(fill_size);
std::uninitialized_fill(p, p + fill_size, fill);
}
pointer_type result = prepare_int_buffer(
num_digits, subspec, prefix, prefix_size);
if (align == ALIGN_LEFT) {
pointer_type p = grow_buffer(fill_size);
std::uninitialized_fill(p, p + fill_size, fill);
}
return result;
constexpr void operator()() { handler.on_arg_id(); }
constexpr void operator()(unsigned id) { handler.on_arg_id(id); }
constexpr void operator()(basic_string_view<Char> id) {
handler.on_arg_id(id);
}
unsigned size = prefix_size + num_digits;
if (width <= size) {
pointer_type p = grow_buffer(size);
std::uninitialized_copy(prefix, prefix + prefix_size, p);
return p + size - 1;
constexpr void on_error(const char *message) {
handler.on_error(message);
}
pointer_type p = grow_buffer(width);
pointer_type end = p + width;
if (align == ALIGN_LEFT) {
std::uninitialized_copy(prefix, prefix + prefix_size, p);
p += size;
std::uninitialized_fill(p, end, fill);
} else if (align == ALIGN_CENTER) {
p = fill_padding(p, width, size, fill);
std::uninitialized_copy(prefix, prefix + prefix_size, p);
p += size;
} else {
if (align == ALIGN_NUMERIC) {
if (prefix_size != 0) {
p = std::uninitialized_copy(prefix, prefix + prefix_size, p);
size -= prefix_size;
}
Handler &handler;
};
template <typename Iterator, typename Handler>
constexpr void parse_format_string(Iterator it, Handler &&handler) {
using char_type = typename std::iterator_traits<Iterator>::value_type;
auto start = it;
while (*it) {
char_type ch = *it++;
if (ch != '{' && ch != '}') continue;
if (*it == ch) {
handler.on_text(start, it);
start = ++it;
continue;
}
if (ch == '}') {
handler.on_error("unmatched '}' in format string");
return;
}
handler.on_text(start, it - 1);
it = parse_arg_id(it, id_adapter<Handler, char_type>(handler));
if (*it == '}') {
handler.on_replacement_field(it);
} else if (*it == ':') {
++it;
it = handler.on_format_specs(it);
if (*it != '}')
handler.on_error("unknown format specifier");
} else {
std::uninitialized_copy(prefix, prefix + prefix_size, end - size);
handler.on_error("missing '}' in format string");
}
std::uninitialized_fill(p, end - size, fill);
p = end;
start = ++it;
}
return p - 1;
handler.on_text(start, it);
}
template <typename Char>
template <typename T, typename Spec>
void basic_writer<Char>::write_int(T value, const Spec& spec) {
struct spec_handler {
basic_writer<Char> &writer;
const Spec& spec;
unsigned prefix_size = 0;
typedef typename internal::int_traits<T>::main_type UnsignedType;
UnsignedType abs_value;
char prefix[4] = "";
template <typename Char, typename T>
constexpr const Char *parse_format_specs(parse_context<Char> &ctx) {
formatter<T, Char> f;
return f.parse(ctx);
}
spec_handler(basic_writer<Char> &w, T value, const Spec& s)
: writer(w), abs_value(static_cast<UnsignedType>(value)), spec(s) {
if (internal::is_negative(value)) {
prefix[0] = '-';
++prefix_size;
abs_value = 0 - abs_value;
} else if (spec.flag(SIGN_FLAG)) {
prefix[0] = spec.flag(PLUS_FLAG) ? '+' : ' ';
++prefix_size;
}
}
template <typename Char, typename... Args>
struct format_string_checker {
public:
explicit constexpr format_string_checker(const Char *end) : end_(end) {}
void on_dec() {
unsigned num_digits = internal::count_digits(abs_value);
pointer_type p =
writer.prepare_int_buffer(num_digits, spec, prefix, prefix_size) + 1;
internal::format_decimal(get(p), abs_value, 0);
}
constexpr void on_text(const Char *, const Char *) {}
void on_hex() {
UnsignedType n = abs_value;
if (spec.flag(HASH_FLAG)) {
prefix[prefix_size++] = '0';
prefix[prefix_size++] = spec.type();
}
unsigned num_digits = 0;
do {
++num_digits;
} while ((n >>= 4) != 0);
Char *p =
get(writer.prepare_int_buffer(num_digits, spec, prefix, prefix_size));
n = abs_value;
const char *digits = spec.type() == 'x' ?
"0123456789abcdef" : "0123456789ABCDEF";
do {
*p-- = digits[n & 0xf];
} while ((n >>= 4) != 0);
}
constexpr void on_arg_id() {
++arg_index_;
check_arg_index();
}
constexpr void on_arg_id(unsigned index) {
arg_index_ = index;
check_arg_index();
}
constexpr void on_arg_id(basic_string_view<Char>) {}
void on_bin() {
UnsignedType n = abs_value;
if (spec.flag(HASH_FLAG)) {
prefix[prefix_size++] = '0';
prefix[prefix_size++] = spec.type();
}
unsigned num_digits = 0;
do {
++num_digits;
} while ((n >>= 1) != 0);
Char *p =
get(writer.prepare_int_buffer(num_digits, spec, prefix, prefix_size));
n = abs_value;
do {
*p-- = static_cast<Char>('0' + (n & 1));
} while ((n >>= 1) != 0);
}
constexpr void on_replacement_field(const Char *) {}
void on_oct() {
UnsignedType n = abs_value;
if (spec.flag(HASH_FLAG))
prefix[prefix_size++] = '0';
unsigned num_digits = 0;
do {
++num_digits;
} while ((n >>= 3) != 0);
Char *p =
get(writer.prepare_int_buffer(num_digits, spec, prefix, prefix_size));
n = abs_value;
do {
*p-- = static_cast<Char>('0' + (n & 7));
} while ((n >>= 3) != 0);
}
constexpr const Char *on_format_specs(const Char *s) {
parse_context<Char> ctx(basic_string_view<Char>(s, end_ - s));
return parse_funcs_[arg_index_](ctx);
}
void on_num() {
unsigned num_digits = internal::count_digits(abs_value);
std::locale loc = writer.buffer_.locale();
Char thousands_sep =
std::use_facet<std::numpunct<Char>>(loc).thousands_sep();
fmt::basic_string_view<Char> sep(&thousands_sep, 1);
unsigned size = static_cast<unsigned>(
num_digits + sep.size() * ((num_digits - 1) / 3));
pointer_type p =
writer.prepare_int_buffer(size, spec, prefix, prefix_size) + 1;
internal::format_decimal(get(p), abs_value, 0,
internal::add_thousands_sep<Char>(sep));
}
// This function is intentionally not constexpr to give a compile-time error.
void on_error(const char *);
void on_error() {
internal::report_unknown_type(
spec.type(), spec.flag(CHAR_FLAG) ? "char" : "integer");
}
private:
constexpr void check_arg_index() {
if (arg_index_ < 0 || arg_index_ >= sizeof...(Args))
on_error("argument index out of range");
}
// Format specifier parsing function.
using parse_func = const Char *(*)(parse_context<Char> &);
const Char *end_;
int arg_index_ = -1;
parse_func parse_funcs_[sizeof...(Args)] = {
&parse_format_specs<Char, Args>...
};
internal::handle_integral_type_spec(
spec.type(), spec_handler(*this, value, spec));
};
template <typename Char, typename... Args>
constexpr bool check_format_string(basic_string_view<Char> s) {
format_string_checker<Char, Args...> checker(s.end());
internal::parse_format_string(s.begin(), checker);
return true;
}
template <typename Char>
// Specifies whether to format T using the standard formatter.
// It is not possible to use get_type in formatter specialization directly
// because of a bug in MSVC.
template <typename T>
void basic_writer<Char>::write_double(T value, const format_specs &spec) {
// Check type.
char type = spec.type();
bool upper = false;
switch (type) {
case 0:
type = 'g';
struct format_type : std::integral_constant<bool, get_type<T>() != CUSTOM> {};
// Specifies whether to format enums.
template <typename T, typename Enable = void>
struct format_enum : std::integral_constant<bool, std::is_enum<T>::value> {};
template <template <typename> class Handler, typename Spec, typename Char>
void handle_dynamic_spec(
Spec &value, arg_ref<Char> ref, basic_context<Char> &ctx) {
switch (ref.kind) {
case arg_ref<Char>::NONE:
break;
case 'e': case 'f': case 'g': case 'a':
case arg_ref<Char>::INDEX:
internal::set_dynamic_spec<Handler>(value, ctx.get_arg(ref.index));
break;
case 'F':
#if FMT_MSC_VER
// MSVC's printf doesn't support 'F'.
type = 'f';
#endif
// Fall through.
case 'E': case 'G': case 'A':
upper = true;
break;
default:
internal::report_unknown_type(type, "double");
case arg_ref<Char>::NAME:
internal::set_dynamic_spec<Handler>(value, ctx.get_arg(ref.name));
break;
}
}
} // namespace internal
char sign = 0;
// Use isnegative instead of value < 0 because the latter is always
// false for NaN.
if (internal::fputil::isnegative(static_cast<double>(value))) {
sign = '-';
value = -value;
} else if (spec.flag(SIGN_FLAG)) {
sign = spec.flag(PLUS_FLAG) ? '+' : ' ';
}
if (internal::fputil::isnotanumber(value)) {
// Format NaN ourselves because sprintf's output is not consistent
// across platforms.
std::size_t nan_size = 4;
const char *nan = upper ? " NAN" : " nan";
if (!sign) {
--nan_size;
++nan;
}
pointer_type out = write_str(nan, nan_size, spec);
if (sign)
*out = sign;
return;
}
/** The default argument formatter. */
template <typename Char>
class arg_formatter : public internal::arg_formatter_base<Char> {
private:
basic_context<Char> &ctx_;
if (internal::fputil::isinfinity(value)) {
// Format infinity ourselves because sprintf's output is not consistent
// across platforms.
std::size_t inf_size = 4;
const char *inf = upper ? " INF" : " inf";
if (!sign) {
--inf_size;
++inf;
}
pointer_type out = write_str(inf, inf_size, spec);
if (sign)
*out = sign;
return;
}
typedef internal::arg_formatter_base<Char> Base;
std::size_t offset = buffer_.size();
unsigned width = spec.width();
if (sign) {
buffer_.reserve(buffer_.size() + (width > 1u ? width : 1u));
if (width > 0)
--width;
++offset;
}
public:
typedef typename Base::format_specs format_specs;
// Build format string.
enum { MAX_FORMAT_SIZE = 10}; // longest format: %#-*.*Lg
Char format[MAX_FORMAT_SIZE];
Char *format_ptr = format;
*format_ptr++ = '%';
unsigned width_for_sprintf = width;
if (spec.flag(HASH_FLAG))
*format_ptr++ = '#';
if (spec.align() == ALIGN_CENTER) {
width_for_sprintf = 0;
} else {
if (spec.align() == ALIGN_LEFT)
*format_ptr++ = '-';
if (width != 0)
*format_ptr++ = '*';
}
if (spec.precision() >= 0) {
*format_ptr++ = '.';
*format_ptr++ = '*';
}
/**
\rst
Constructs an argument formatter object.
*buffer* is a reference to the buffer to be used for output,
*ctx* is a reference to the formatting context, *spec* contains
format specifier information for standard argument types.
\endrst
*/
arg_formatter(basic_buffer<Char> &buffer, basic_context<Char> &ctx,
format_specs &spec)
: internal::arg_formatter_base<Char>(buffer, spec), ctx_(ctx) {}
append_float_length(format_ptr, value);
*format_ptr++ = type;
*format_ptr = '\0';
using internal::arg_formatter_base<Char>::operator();
// Format using snprintf.
Char fill = internal::char_traits<Char>::cast(spec.fill());
unsigned n = 0;
Char *start = 0;
for (;;) {
std::size_t buffer_size = buffer_.capacity() - offset;
#if FMT_MSC_VER
// MSVC's vsnprintf_s doesn't work with zero size, so reserve
// space for at least one extra character to make the size non-zero.
// Note that the buffer's capacity will increase by more than 1.
if (buffer_size == 0) {
buffer_.reserve(offset + 1);
buffer_size = buffer_.capacity() - offset;
}
#endif
start = &buffer_[offset];
int result = internal::char_traits<Char>::format_float(
start, buffer_size, format, width_for_sprintf, spec.precision(), value);
if (result >= 0) {
n = internal::to_unsigned(result);
if (offset + n < buffer_.capacity())
break; // The buffer is large enough - continue with formatting.
buffer_.reserve(offset + n + 1);
} else {
// If result is negative we ask to increase the capacity by at least 1,
// but as std::vector, the buffer grows exponentially.
buffer_.reserve(buffer_.capacity() + 1);
}
}
if (sign) {
if ((spec.align() != ALIGN_RIGHT && spec.align() != ALIGN_DEFAULT) ||
*start != ' ') {
*(start - 1) = sign;
sign = 0;
} else {
*(start - 1) = fill;
}
++n;
}
if (spec.align() == ALIGN_CENTER && spec.width() > n) {
width = spec.width();
pointer_type p = grow_buffer(width);
std::memmove(get(p) + (width - n) / 2, get(p), n * sizeof(Char));
fill_padding(p, spec.width(), n, fill);
return;
}
if (spec.fill() != ' ' || sign) {
while (*start == ' ')
*start++ = fill;
if (sign)
*(start - 1) = sign;
/** Formats an argument of a custom (user-defined) type. */
void operator()(internal::custom_value<Char> c) {
c.format(this->writer().buffer(), c.value, &ctx_);
}
grow_buffer(n);
}
};
// Reports a system error without throwing an exception.
// Can be used to report errors from destructors.
FMT_API void report_system_error(int error_code,
string_view message) FMT_NOEXCEPT;
template <typename Char>
class basic_context :
public internal::context_base<Char, basic_context<Char>> {
public:
/** The character type for the output. */
using char_type = Char;
#if FMT_USE_WINDOWS_H
template <typename T>
using formatter_type = formatter<T, Char>;
/** A Windows error. */
class windows_error : public system_error {
private:
FMT_API void init(int error_code, string_view format_str, args args);
internal::arg_map<basic_context<Char>> map_;
FMT_DISALLOW_COPY_AND_ASSIGN(basic_context);
typedef internal::context_base<Char, basic_context<Char>> Base;
typedef typename Base::format_arg format_arg;
using Base::get_arg;
public:
/**
\rst
Constructs a :class:`fmt::windows_error` object with the description
of the form
Constructs a ``basic_context`` object. References to the arguments are
stored in the object so make sure they have appropriate lifetimes.
\endrst
*/
basic_context(
basic_string_view<Char> format_str, basic_args<basic_context> args)
: Base(format_str, args) {}
.. parsed-literal::
*<message>*: *<system-message>*
format_arg next_arg() {
const char *error = 0;
format_arg arg = this->do_get_arg(this->next_arg_index(error), error);
if (error)
FMT_THROW(format_error(error));
return arg;
}
where *<message>* is the formatted message and *<system-message>* is the
system message corresponding to the error code.
*error_code* is a Windows error code as given by ``GetLastError``.
If *error_code* is not a valid error code such as -1, the system message
will look like "error -1".
format_arg get_arg(unsigned arg_index) {
const char *error = 0;
format_arg arg = this->do_get_arg(arg_index, error);
if (error)
FMT_THROW(format_error(error));
return arg;
}
// Checks if manual indexing is used and returns the argument with
// specified name.
format_arg get_arg(basic_string_view<Char> name);
};
/**
An error returned by an operating system or a language runtime,
for example a file opening error.
*/
class system_error : public std::runtime_error {
private:
void init(int err_code, string_view format_str, args args);
protected:
int error_code_;
system_error() : std::runtime_error("") {}
public:
/**
\rst
Constructs a :class:`fmt::system_error` object with a description
formatted with `fmt::format_system_error`. *message* and additional
arguments passed into the constructor are formatted similarly to
`fmt::format`.
**Example**::
// This throws a windows_error with the description
// cannot open file 'madeup': The system cannot find the file specified.
// This throws a system_error with the description
// cannot open file 'madeup': No such file or directory
// or similar (system message may vary).
const char *filename = "madeup";
LPOFSTRUCT of = LPOFSTRUCT();
HFILE file = OpenFile(filename, &of, OF_READ);
if (file == HFILE_ERROR) {
throw fmt::windows_error(GetLastError(),
"cannot open file '{}'", filename);
}
std::FILE *file = std::fopen(filename, "r");
if (!file)
throw fmt::system_error(errno, "cannot open file '{}'", filename);
\endrst
*/
template <typename... Args>
windows_error(int error_code, string_view message, const Args & ... args) {
system_error(int error_code, string_view message, const Args & ... args)
: std::runtime_error("") {
init(error_code, message, make_args(args...));
}
~system_error() throw();
int error_code() const { return error_code_; }
};
// Reports a Windows error without throwing an exception.
// Can be used to report errors from destructors.
FMT_API void report_windows_error(int error_code,
string_view message) FMT_NOEXCEPT;
#endif
enum Color { BLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE };
FMT_API void vprint_colored(Color c, string_view format, args args);
/**
Formats a string and prints it to stdout using ANSI escape sequences
to specify color (experimental).
Example:
print_colored(fmt::RED, "Elapsed time: {0:.2f} seconds", 1.23);
*/
template <typename... Args>
inline void print_colored(Color c, string_view format_str,
const Args & ... args) {
vprint_colored(c, format_str, make_args(args...));
}
template <typename ArgFormatter, typename Char, typename Context>
void vformat_to(basic_buffer<Char> &buffer, basic_string_view<Char> format_str,
basic_args<Context> args);
inline void vformat_to(buffer &buf, string_view format_str, args args) {
vformat_to<arg_formatter<char>>(buf, format_str, args);
}
inline void vformat_to(wbuffer &buf, wstring_view format_str, wargs args) {
vformat_to<arg_formatter<wchar_t>>(buf, format_str, args);
}
template <typename... Args>
inline void format_to(buffer &buf, string_view format_str,
const Args & ... args) {
vformat_to(buf, format_str, make_args(args...));
}
template <typename... Args>
inline void format_to(wbuffer &buf, wstring_view format_str,
const Args & ... args) {
vformat_to(buf, format_str, make_args<wcontext>(args...));
}
inline std::string vformat(string_view format_str, args args) {
memory_buffer buffer;
vformat_to(buffer, format_str, args);
return to_string(buffer);
}
/**
\rst
Formats arguments and returns the result as a string.
Formats an error returned by an operating system or a language runtime,
for example a file opening error, and writes it to *out* in the following
form:
**Example**::
.. parsed-literal::
*<message>*: *<system-message>*
std::string message = format("The answer is {}", 42);
where *<message>* is the passed message and *<system-message>* is
the system message corresponding to the error code.
*error_code* is a system error code as given by ``errno``.
If *error_code* is not a valid error code such as -1, the system message
may look like "Unknown error -1" and is platform-dependent.
\endrst
*/
template <typename... Args>
inline std::string format(string_view format_str, const Args & ... args) {
return vformat(format_str, make_args(args...));
}
inline std::wstring vformat(wstring_view format_str, wargs args) {
wmemory_buffer buffer;
vformat_to(buffer, format_str, args);
return to_string(buffer);
}
template <typename... Args>
inline std::wstring format(wstring_view format_str, const Args & ... args) {
return vformat(format_str, make_args<wcontext>(args...));
}
FMT_API void vprint(std::FILE *f, string_view format_str, args args);
*/
FMT_API void format_system_error(fmt::buffer &out, int error_code,
fmt::string_view message) FMT_NOEXCEPT;
/**
\rst
Prints formatted data to the file *f*.
This template provides operations for formatting and writing data into a
character buffer. The output buffer is specified by a subclass such as
:class:`fmt::BasicMemoryWriter`.
**Example**::
You can use one of the following typedefs for common character types:
+---------+-----------------------+
| Type | Definition |
+=========+=======================+
| writer | basic_writer<char> |
+---------+-----------------------+
| wwriter | basic_writer<wchar_t> |
+---------+-----------------------+
print(stderr, "Don't {}!", "panic");
\endrst
*/
template <typename... Args>
inline void print(std::FILE *f, string_view format_str,
const Args & ... args) {
vprint(f, format_str, make_args(args...));
}
template <typename Char>
class basic_writer {
public:
typedef basic_format_specs<Char> format_specs;
FMT_API void vprint(string_view format_str, args args);
private:
// Output buffer.
basic_buffer<Char> &buffer_;
/**
\rst
Prints formatted data to ``stdout``.
FMT_DISALLOW_COPY_AND_ASSIGN(basic_writer);
**Example**::
#if FMT_SECURE_SCL
typedef stdext::checked_array_iterator<Char*> pointer_type;
// Returns pointer value.
static Char *get(pointer_type p) { return p.base(); }
#else
typedef Char *pointer_type;
static Char *get(Char *p) { return p; }
#endif
print("Elapsed time: {0:.2f} seconds", 1.23);
\endrst
*/
template <typename... Args>
inline void print(string_view format_str, const Args & ... args) {
vprint(format_str, make_args(args...));
}
// Fills the padding around the content and returns the pointer to the
// content area.
static pointer_type fill_padding(pointer_type buffer,
unsigned total_size, std::size_t content_size, wchar_t fill);
/**
Fast integer formatter.
*/
class FormatInt {
private:
// Buffer should be large enough to hold all digits (digits10 + 1),
// a sign and a null character.
enum {BUFFER_SIZE = std::numeric_limits<unsigned long long>::digits10 + 3};
mutable char buffer_[BUFFER_SIZE];
char *str_;
// Grows the buffer by n characters and returns a pointer to the newly
// allocated area.
pointer_type grow_buffer(std::size_t n) {
std::size_t size = buffer_.size();
buffer_.resize(size + n);
return internal::make_ptr(&buffer_[size], n);
}
// Formats value in reverse and returns the number of digits.
char *format_decimal(unsigned long long value) {
char *buffer_end = buffer_ + BUFFER_SIZE - 1;
while (value >= 100) {
// Integer division is slow so do it for a group of two digits instead
// of for every digit. The idea comes from the talk by Alexandrescu
// "Three Optimization Tips for C++". See speed-test for a comparison.
unsigned index = static_cast<unsigned>((value % 100) * 2);
value /= 100;
*--buffer_end = internal::data::DIGITS[index + 1];
*--buffer_end = internal::data::DIGITS[index];
}
if (value < 10) {
*--buffer_end = static_cast<char>('0' + value);
return buffer_end;
}
unsigned index = static_cast<unsigned>(value * 2);
*--buffer_end = internal::data::DIGITS[index + 1];
*--buffer_end = internal::data::DIGITS[index];
return buffer_end;
// Writes an unsigned decimal integer.
template <typename UInt>
Char *write_unsigned_decimal(UInt value, unsigned prefix_size = 0) {
unsigned num_digits = internal::count_digits(value);
Char *ptr = get(grow_buffer(prefix_size + num_digits));
internal::format_decimal(ptr + prefix_size, value, num_digits);
return ptr;
}
void FormatSigned(long long value) {
unsigned long long abs_value = static_cast<unsigned long long>(value);
bool negative = value < 0;
if (negative)
// Writes a decimal integer.
template <typename Int>
void write_decimal(Int value) {
typedef typename internal::int_traits<Int>::main_type main_type;
main_type abs_value = static_cast<main_type>(value);
if (internal::is_negative(value)) {
abs_value = 0 - abs_value;
str_ = format_decimal(abs_value);
if (negative)
*--str_ = '-';
*write_unsigned_decimal(abs_value, 1) = '-';
} else {
write_unsigned_decimal(abs_value, 0);
}
}
public:
explicit FormatInt(int value) { FormatSigned(value); }
explicit FormatInt(long value) { FormatSigned(value); }
explicit FormatInt(long long value) { FormatSigned(value); }
explicit FormatInt(unsigned value) : str_(format_decimal(value)) {}
explicit FormatInt(unsigned long value) : str_(format_decimal(value)) {}
explicit FormatInt(unsigned long long value) : str_(format_decimal(value)) {}
// Prepare a buffer for integer formatting.
pointer_type prepare_int_buffer(unsigned num_digits,
const empty_spec &, const char *prefix, unsigned prefix_size) {
unsigned size = prefix_size + num_digits;
pointer_type p = grow_buffer(size);
std::uninitialized_copy(prefix, prefix + prefix_size, p);
return p + size - 1;
}
/** Returns the number of characters written to the output buffer. */
std::size_t size() const {
return internal::to_unsigned(buffer_ - str_ + BUFFER_SIZE - 1);
template <typename Spec>
pointer_type prepare_int_buffer(unsigned num_digits,
const Spec &spec, const char *prefix, unsigned prefix_size);
// Writes a formatted integer.
template <typename T, typename Spec>
void write_int(T value, const Spec& spec);
// Formats a floating-point number (double or long double).
template <typename T>
void write_double(T value, const format_specs &spec);
// Writes a formatted string.
template <typename StrChar>
pointer_type write_str(
const StrChar *s, std::size_t size, const align_spec &spec);
template <typename StrChar>
void write_str(basic_string_view<StrChar> str, const format_specs &spec);
// Appends floating-point length specifier to the format string.
// The second argument is only used for overload resolution.
void append_float_length(Char *&format_ptr, long double) {
*format_ptr++ = 'L';
}
template<typename T>
void append_float_length(Char *&, T) {}
template <typename Char_>
friend class internal::arg_formatter_base;
public:
/**
Returns a pointer to the output buffer content. No terminating null
character is appended.
Constructs a ``basic_writer`` object.
*/
const char *data() const { return str_; }
explicit basic_writer(basic_buffer<Char> &b) : buffer_(b) {}
/**
Returns a pointer to the output buffer content with terminating null
character appended.
*/
const char *c_str() const {
buffer_[BUFFER_SIZE - 1] = '\0';
return str_;
\rst
Destroys the ``basic_writer`` object.
\endrst
*/
virtual ~basic_writer() {}
/**
Returns the total number of characters written.
*/
std::size_t size() const { return buffer_.size(); }
/**
Returns a pointer to the output buffer content. No terminating null
character is appended.
*/
const Char *data() const FMT_NOEXCEPT { return &buffer_[0]; }
/**
Returns a pointer to the output buffer content with terminating null
character appended.
*/
const Char *c_str() const {
std::size_t size = buffer_.size();
buffer_.reserve(size + 1);
buffer_[size] = '\0';
return &buffer_[0];
}
/**
\rst
Returns the content of the output buffer as an ``std::string``.
Returns the content of the output buffer as an `std::string`.
\endrst
*/
std::string str() const { return std::string(str_, size()); }
};
std::basic_string<Char> str() const {
return std::basic_string<Char>(&buffer_[0], buffer_.size());
}
// Formats a decimal integer value writing into buffer and returns
// a pointer to the end of the formatted string. This function doesn't
// write a terminating null character.
template <typename T>
inline void format_decimal(char *&buffer, T value) {
typedef typename internal::int_traits<T>::main_type main_type;
main_type abs_value = static_cast<main_type>(value);
if (internal::is_negative(value)) {
*buffer++ = '-';
abs_value = 0 - abs_value;
void write(int value) {
write_decimal(value);
}
if (abs_value < 100) {
if (abs_value < 10) {
*buffer++ = static_cast<char>('0' + abs_value);
return;
}
unsigned index = static_cast<unsigned>(abs_value * 2);
*buffer++ = internal::data::DIGITS[index];
*buffer++ = internal::data::DIGITS[index + 1];
return;
void write(long value) {
write_decimal(value);
}
void write(long long value) {
write_decimal(value);
}
unsigned num_digits = internal::count_digits(abs_value);
internal::format_decimal(buffer, abs_value, num_digits);
buffer += num_digits;
}
/**
\rst
Returns a named argument for formatting functions.
**Example**::
print("Elapsed time: {s:.2f} seconds", arg("s", 1.23));
\endrst
*/
template <typename T>
inline internal::named_arg<context> arg(string_view name, const T &arg) {
return internal::named_arg<context>(name, arg);
}
/**
\rst
Formats *value* and writes it to the buffer.
\endrst
*/
template <typename T, typename... FormatSpecs>
typename std::enable_if<std::is_integral<T>::value, void>::type
write(T value, FormatSpecs... specs) {
write_int(value, format_specs(specs...));
}
template <typename T>
inline internal::named_arg<wcontext> arg(wstring_view name, const T &arg) {
return internal::named_arg<wcontext>(name, arg);
}
void write(double value) {
write_double(value, format_specs());
}
// The following two functions are deleted intentionally to disable
// nested named arguments as in ``format("{}", arg("a", arg("b", 42)))``.
template <typename Context>
void arg(string_view, const internal::named_arg<Context>&)
FMT_DELETED_OR_UNDEFINED;
template <typename Context>
void arg(wstring_view, const internal::named_arg<Context>&)
FMT_DELETED_OR_UNDEFINED;
}
/**
\rst
Formats *value* using the general format for floating-point numbers
(``'g'``) and writes it to the buffer.
\endrst
*/
void write(long double value) {
write_double(value, format_specs());
}
namespace fmt {
namespace internal {
template <typename Char>
constexpr bool is_name_start(Char c) {
return ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || '_' == c;
}
/**
Writes a character to the buffer.
*/
void write(char value) {
buffer_.push_back(value);
}
// Parses the input as an unsigned integer. This function assumes that the
// first character is a digit and presence of a non-digit character at the end.
// it: an iterator pointing to the beginning of the input range.
template <typename Iterator, typename ErrorHandler>
constexpr unsigned parse_nonnegative_int(Iterator &it, ErrorHandler& handler) {
assert('0' <= *it && *it <= '9');
unsigned value = 0;
do {
unsigned new_value = value * 10 + (*it - '0');
// Workaround for MSVC "setup_exception stack overflow" error:
auto next = it;
++next;
it = next;
// Check if value wrapped around.
if (new_value < value) {
value = (std::numeric_limits<unsigned>::max)();
break;
}
value = new_value;
} while ('0' <= *it && *it <= '9');
// Convert to unsigned to prevent a warning.
unsigned max_int = (std::numeric_limits<int>::max)();
if (value > max_int)
handler.on_error("number is too big");
return value;
}
void write(wchar_t value) {
internal::require_wchar<Char>();
buffer_.push_back(value);
}
template <typename Char, typename Context>
class custom_formatter {
private:
basic_buffer<Char> &buffer_;
Context &ctx_;
/**
\rst
Writes *value* to the buffer.
\endrst
*/
void write(string_view value) {
const char *str = value.data();
buffer_.append(str, str + value.size());
}
public:
custom_formatter(basic_buffer<Char> &buffer, Context &ctx)
: buffer_(buffer), ctx_(ctx) {}
void write(basic_string_view<wchar_t> value) {
internal::require_wchar<Char>();
const wchar_t *str = value.data();
buffer_.append(str, str + value.size());
}
bool operator()(internal::custom_value<Char> custom) {
custom.format(buffer_, custom.value, &ctx_);
return true;
template <typename... FormatSpecs>
void write(basic_string_view<Char> str, FormatSpecs... specs) {
write_str(str, format_specs(specs...));
}
template <typename T>
bool operator()(T) { return false; }
};
void clear() FMT_NOEXCEPT { buffer_.resize(0); }
template <typename T>
struct is_integer {
enum {
value = std::is_integral<T>::value && !std::is_same<T, bool>::value &&
!std::is_same<T, char>::value && !std::is_same<T, wchar_t>::value
};
basic_buffer<Char> &buffer() FMT_NOEXCEPT { return buffer_; }
};
template <typename ErrorHandler>
class width_checker {
public:
explicit constexpr width_checker(ErrorHandler &eh) : handler_(eh) {}
template <typename T>
constexpr typename std::enable_if<
is_integer<T>::value, unsigned long long>::type operator()(T value) {
if (is_negative(value))
handler_.on_error("negative width");
return value;
}
template <typename T>
constexpr typename std::enable_if<
!is_integer<T>::value, unsigned long long>::type operator()(T) {
handler_.on_error("width is not integer");
return 0;
template <typename Char>
template <typename StrChar>
typename basic_writer<Char>::pointer_type basic_writer<Char>::write_str(
const StrChar *s, std::size_t size, const align_spec &spec) {
pointer_type out = pointer_type();
if (spec.width() > size) {
out = grow_buffer(spec.width());
Char fill = internal::char_traits<Char>::cast(spec.fill());
if (spec.align() == ALIGN_RIGHT) {
std::uninitialized_fill_n(out, spec.width() - size, fill);
out += spec.width() - size;
} else if (spec.align() == ALIGN_CENTER) {
out = fill_padding(out, spec.width(), size, fill);
} else {
std::uninitialized_fill_n(out + size, spec.width() - size, fill);
}
} else {
out = grow_buffer(size);
}
std::uninitialized_copy(s, s + size, out);
return out;
}
private:
ErrorHandler &handler_;
};
template <typename Char>
template <typename StrChar>
void basic_writer<Char>::write_str(
basic_string_view<StrChar> s, const format_specs &spec) {
// Check if StrChar is convertible to Char.
internal::char_traits<Char>::convert(StrChar());
if (spec.type_ && spec.type_ != 's')
internal::report_unknown_type(spec.type_, "string");
const StrChar *str_value = s.data();
std::size_t str_size = s.size();
if (str_size == 0 && !str_value)
FMT_THROW(format_error("string pointer is null"));
std::size_t precision = static_cast<std::size_t>(spec.precision_);
if (spec.precision_ >= 0 && precision < str_size)
str_size = precision;
write_str(str_value, str_size, spec);
}
template <typename ErrorHandler>
class precision_checker {
public:
explicit constexpr precision_checker(ErrorHandler &eh) : handler_(eh) {}
template <typename Char>
typename basic_writer<Char>::pointer_type basic_writer<Char>::fill_padding(
pointer_type buffer, unsigned total_size,
std::size_t content_size, wchar_t fill) {
std::size_t padding = total_size - content_size;
std::size_t left_padding = padding / 2;
Char fill_char = internal::char_traits<Char>::cast(fill);
std::uninitialized_fill_n(buffer, left_padding, fill_char);
buffer += left_padding;
pointer_type content = buffer;
std::uninitialized_fill_n(buffer + content_size,
padding - left_padding, fill_char);
return content;
}
template <typename T>
constexpr typename std::enable_if<
is_integer<T>::value, unsigned long long>::type operator()(T value) {
if (is_negative(value))
handler_.on_error("negative precision");
return value;
template <typename Char>
template <typename Spec>
typename basic_writer<Char>::pointer_type
basic_writer<Char>::prepare_int_buffer(
unsigned num_digits, const Spec &spec,
const char *prefix, unsigned prefix_size) {
unsigned width = spec.width();
alignment align = spec.align();
Char fill = internal::char_traits<Char>::cast(spec.fill());
if (spec.precision() > static_cast<int>(num_digits)) {
// Octal prefix '0' is counted as a digit, so ignore it if precision
// is specified.
if (prefix_size > 0 && prefix[prefix_size - 1] == '0')
--prefix_size;
unsigned number_size =
prefix_size + internal::to_unsigned(spec.precision());
align_spec subspec(number_size, '0', ALIGN_NUMERIC);
if (number_size >= width)
return prepare_int_buffer(num_digits, subspec, prefix, prefix_size);
buffer_.reserve(width);
unsigned fill_size = width - number_size;
if (align != ALIGN_LEFT) {
pointer_type p = grow_buffer(fill_size);
std::uninitialized_fill(p, p + fill_size, fill);
}
pointer_type result = prepare_int_buffer(
num_digits, subspec, prefix, prefix_size);
if (align == ALIGN_LEFT) {
pointer_type p = grow_buffer(fill_size);
std::uninitialized_fill(p, p + fill_size, fill);
}
return result;
}
template <typename T>
constexpr typename std::enable_if<
!is_integer<T>::value, unsigned long long>::type operator()(T) {
handler_.on_error("precision is not integer");
return 0;
unsigned size = prefix_size + num_digits;
if (width <= size) {
pointer_type p = grow_buffer(size);
std::uninitialized_copy(prefix, prefix + prefix_size, p);
return p + size - 1;
}
pointer_type p = grow_buffer(width);
pointer_type end = p + width;
if (align == ALIGN_LEFT) {
std::uninitialized_copy(prefix, prefix + prefix_size, p);
p += size;
std::uninitialized_fill(p, end, fill);
} else if (align == ALIGN_CENTER) {
p = fill_padding(p, width, size, fill);
std::uninitialized_copy(prefix, prefix + prefix_size, p);
p += size;
} else {
if (align == ALIGN_NUMERIC) {
if (prefix_size != 0) {
p = std::uninitialized_copy(prefix, prefix + prefix_size, p);
size -= prefix_size;
}
} else {
std::uninitialized_copy(prefix, prefix + prefix_size, end - size);
}
std::uninitialized_fill(p, end - size, fill);
p = end;
}
return p - 1;
}
private:
ErrorHandler &handler_;
};
// A format specifier handler that sets fields in basic_format_specs.
template <typename Char>
class specs_setter : public error_handler {
public:
explicit constexpr specs_setter(basic_format_specs<Char> &specs):
specs_(specs) {}
template <typename T, typename Spec>
void basic_writer<Char>::write_int(T value, const Spec& spec) {
struct spec_handler {
basic_writer<Char> &writer;
const Spec& spec;
unsigned prefix_size = 0;
typedef typename internal::int_traits<T>::main_type UnsignedType;
UnsignedType abs_value;
char prefix[4] = "";
constexpr void on_align(alignment align) { specs_.align_ = align; }
constexpr void on_fill(Char fill) { specs_.fill_ = fill; }
constexpr void on_plus() { specs_.flags_ |= SIGN_FLAG | PLUS_FLAG; }
constexpr void on_minus() { specs_.flags_ |= MINUS_FLAG; }
constexpr void on_space() { specs_.flags_ |= SIGN_FLAG; }
constexpr void on_hash() { specs_.flags_ |= HASH_FLAG; }
spec_handler(basic_writer<Char> &w, T value, const Spec& s)
: writer(w), abs_value(static_cast<UnsignedType>(value)), spec(s) {
if (internal::is_negative(value)) {
prefix[0] = '-';
++prefix_size;
abs_value = 0 - abs_value;
} else if (spec.flag(SIGN_FLAG)) {
prefix[0] = spec.flag(PLUS_FLAG) ? '+' : ' ';
++prefix_size;
}
}
constexpr void on_zero() {
specs_.align_ = ALIGN_NUMERIC;
specs_.fill_ = '0';
}
void on_dec() {
unsigned num_digits = internal::count_digits(abs_value);
pointer_type p =
writer.prepare_int_buffer(num_digits, spec, prefix, prefix_size) + 1;
internal::format_decimal(get(p), abs_value, 0);
}
constexpr void on_width(unsigned width) { specs_.width_ = width; }
constexpr void on_precision(unsigned precision) {
specs_.precision_ = precision;
}
constexpr void end_precision() {}
void on_hex() {
UnsignedType n = abs_value;
if (spec.flag(HASH_FLAG)) {
prefix[prefix_size++] = '0';
prefix[prefix_size++] = spec.type();
}
unsigned num_digits = 0;
do {
++num_digits;
} while ((n >>= 4) != 0);
Char *p =
get(writer.prepare_int_buffer(num_digits, spec, prefix, prefix_size));
n = abs_value;
const char *digits = spec.type() == 'x' ?
"0123456789abcdef" : "0123456789ABCDEF";
do {
*p-- = digits[n & 0xf];
} while ((n >>= 4) != 0);
}
constexpr void on_type(Char type) { specs_.type_ = type; }
void on_bin() {
UnsignedType n = abs_value;
if (spec.flag(HASH_FLAG)) {
prefix[prefix_size++] = '0';
prefix[prefix_size++] = spec.type();
}
unsigned num_digits = 0;
do {
++num_digits;
} while ((n >>= 1) != 0);
Char *p =
get(writer.prepare_int_buffer(num_digits, spec, prefix, prefix_size));
n = abs_value;
do {
*p-- = static_cast<Char>('0' + (n & 1));
} while ((n >>= 1) != 0);
}
protected:
basic_format_specs<Char> &specs_;
};
void on_oct() {
UnsignedType n = abs_value;
if (spec.flag(HASH_FLAG))
prefix[prefix_size++] = '0';
unsigned num_digits = 0;
do {
++num_digits;
} while ((n >>= 3) != 0);
Char *p =
get(writer.prepare_int_buffer(num_digits, spec, prefix, prefix_size));
n = abs_value;
do {
*p-- = static_cast<Char>('0' + (n & 7));
} while ((n >>= 3) != 0);
}
// A format specifier handler that checks if specifiers are consistent with the
// argument type.
template <typename Handler>
class specs_checker : public Handler {
public:
constexpr specs_checker(const Handler& handler, internal::type arg_type)
: Handler(handler), arg_type_(arg_type) {}
void on_num() {
unsigned num_digits = internal::count_digits(abs_value);
std::locale loc = writer.buffer_.locale();
Char thousands_sep =
std::use_facet<std::numpunct<Char>>(loc).thousands_sep();
fmt::basic_string_view<Char> sep(&thousands_sep, 1);
unsigned size = static_cast<unsigned>(
num_digits + sep.size() * ((num_digits - 1) / 3));
pointer_type p =
writer.prepare_int_buffer(size, spec, prefix, prefix_size) + 1;
internal::format_decimal(get(p), abs_value, 0,
internal::add_thousands_sep<Char>(sep));
}
constexpr void on_align(alignment align) {
if (align == ALIGN_NUMERIC)
require_numeric_argument('=');
Handler::on_align(align);
void on_error() {
internal::report_unknown_type(
spec.type(), spec.flag(CHAR_FLAG) ? "char" : "integer");
}
};
internal::handle_integral_type_spec(
spec.type(), spec_handler(*this, value, spec));
}
template <typename Char>
template <typename T>
void basic_writer<Char>::write_double(T value, const format_specs &spec) {
// Check type.
char type = spec.type();
bool upper = false;
switch (type) {
case 0:
type = 'g';
break;
case 'e': case 'f': case 'g': case 'a':
break;
case 'F':
#if FMT_MSC_VER
// MSVC's printf doesn't support 'F'.
type = 'f';
#endif
// Fall through.
case 'E': case 'G': case 'A':
upper = true;
break;
default:
internal::report_unknown_type(type, "double");
break;
}
constexpr void on_plus() {
check_sign('+');
Handler::on_plus();
char sign = 0;
// Use isnegative instead of value < 0 because the latter is always
// false for NaN.
if (internal::fputil::isnegative(static_cast<double>(value))) {
sign = '-';
value = -value;
} else if (spec.flag(SIGN_FLAG)) {
sign = spec.flag(PLUS_FLAG) ? '+' : ' ';
}
constexpr void on_minus() {
check_sign('-');
Handler::on_minus();
if (internal::fputil::isnotanumber(value)) {
// Format NaN ourselves because sprintf's output is not consistent
// across platforms.
std::size_t nan_size = 4;
const char *nan = upper ? " NAN" : " nan";
if (!sign) {
--nan_size;
++nan;
}
pointer_type out = write_str(nan, nan_size, spec);
if (sign)
*out = sign;
return;
}
constexpr void on_space() {
check_sign(' ');
Handler::on_space();
if (internal::fputil::isinfinity(value)) {
// Format infinity ourselves because sprintf's output is not consistent
// across platforms.
std::size_t inf_size = 4;
const char *inf = upper ? " INF" : " inf";
if (!sign) {
--inf_size;
++inf;
}
pointer_type out = write_str(inf, inf_size, spec);
if (sign)
*out = sign;
return;
}
constexpr void on_hash() {
require_numeric_argument('#');
Handler::on_hash();
std::size_t offset = buffer_.size();
unsigned width = spec.width();
if (sign) {
buffer_.reserve(buffer_.size() + (width > 1u ? width : 1u));
if (width > 0)
--width;
++offset;
}
constexpr void on_zero() {
require_numeric_argument('0');
Handler::on_zero();
// Build format string.
enum { MAX_FORMAT_SIZE = 10}; // longest format: %#-*.*Lg
Char format[MAX_FORMAT_SIZE];
Char *format_ptr = format;
*format_ptr++ = '%';
unsigned width_for_sprintf = width;
if (spec.flag(HASH_FLAG))
*format_ptr++ = '#';
if (spec.align() == ALIGN_CENTER) {
width_for_sprintf = 0;
} else {
if (spec.align() == ALIGN_LEFT)
*format_ptr++ = '-';
if (width != 0)
*format_ptr++ = '*';
}
constexpr void end_precision() {
if (is_integral(arg_type_) || arg_type_ == POINTER) {
report_error("precision not allowed in {} format specifier",
arg_type_ == POINTER ? "pointer" : "integer");
}
if (spec.precision() >= 0) {
*format_ptr++ = '.';
*format_ptr++ = '*';
}
private:
template <typename... Args>
void report_error(string_view format_str, const Args &... args) {
this->on_error(format(format_str, args...).c_str());
}
append_float_length(format_ptr, value);
*format_ptr++ = type;
*format_ptr = '\0';
template <typename Char>
constexpr void require_numeric_argument(Char spec) {
if (!is_numeric(arg_type_)) {
report_error("format specifier '{}' requires numeric argument",
static_cast<char>(spec));
// Format using snprintf.
Char fill = internal::char_traits<Char>::cast(spec.fill());
unsigned n = 0;
Char *start = 0;
for (;;) {
std::size_t buffer_size = buffer_.capacity() - offset;
#if FMT_MSC_VER
// MSVC's vsnprintf_s doesn't work with zero size, so reserve
// space for at least one extra character to make the size non-zero.
// Note that the buffer's capacity will increase by more than 1.
if (buffer_size == 0) {
buffer_.reserve(offset + 1);
buffer_size = buffer_.capacity() - offset;
}
#endif
start = &buffer_[offset];
int result = internal::char_traits<Char>::format_float(
start, buffer_size, format, width_for_sprintf, spec.precision(), value);
if (result >= 0) {
n = internal::to_unsigned(result);
if (offset + n < buffer_.capacity())
break; // The buffer is large enough - continue with formatting.
buffer_.reserve(offset + n + 1);
} else {
// If result is negative we ask to increase the capacity by at least 1,
// but as std::vector, the buffer grows exponentially.
buffer_.reserve(buffer_.capacity() + 1);
}
}
template <typename Char>
constexpr void check_sign(Char sign) {
require_numeric_argument(sign);
if (is_integral(arg_type_) && arg_type_ != INT && arg_type_ != LONG_LONG &&
arg_type_ != CHAR) {
report_error("format specifier '{}' requires signed argument",
static_cast<char>(sign));
if (sign) {
if ((spec.align() != ALIGN_RIGHT && spec.align() != ALIGN_DEFAULT) ||
*start != ' ') {
*(start - 1) = sign;
sign = 0;
} else {
*(start - 1) = fill;
}
++n;
}
if (spec.align() == ALIGN_CENTER && spec.width() > n) {
width = spec.width();
pointer_type p = grow_buffer(width);
std::memmove(get(p) + (width - n) / 2, get(p), n * sizeof(Char));
fill_padding(p, spec.width(), n, fill);
return;
}
if (spec.fill() != ' ' || sign) {
while (*start == ' ')
*start++ = fill;
if (sign)
*(start - 1) = sign;
}
grow_buffer(n);
}
internal::type arg_type_;
};
// Reports a system error without throwing an exception.
// Can be used to report errors from destructors.
FMT_API void report_system_error(int error_code,
string_view message) FMT_NOEXCEPT;
template <template <typename> class Handler, typename T, typename Context>
constexpr void set_dynamic_spec(T &value, basic_arg<Context> arg) {
error_handler eh;
unsigned long long big_value = visit(Handler<error_handler>(eh), arg);
if (big_value > (std::numeric_limits<int>::max)())
eh.on_error("number is too big");
value = static_cast<int>(big_value);
}
#if FMT_USE_WINDOWS_H
struct auto_id {};
/** A Windows error. */
class windows_error : public system_error {
private:
FMT_API void init(int error_code, string_view format_str, args args);
// The standard format specifier handler with checking.
template <typename Context>
class specs_handler: public specs_setter<typename Context::char_type> {
public:
typedef typename Context::char_type char_type;
constexpr specs_handler(basic_format_specs<char_type> &specs, Context &ctx)
: specs_setter<char_type>(specs), context_(ctx) {}
/**
\rst
Constructs a :class:`fmt::windows_error` object with the description
of the form
template <typename Id>
constexpr void on_dynamic_width(Id arg_id) {
set_dynamic_spec<width_checker>(this->specs_.width_, get_arg(arg_id));
}
.. parsed-literal::
*<message>*: *<system-message>*
template <typename Id>
constexpr void on_dynamic_precision(Id arg_id) {
set_dynamic_spec<precision_checker>(
this->specs_.precision_, get_arg(arg_id));
}
where *<message>* is the formatted message and *<system-message>* is the
system message corresponding to the error code.
*error_code* is a Windows error code as given by ``GetLastError``.
If *error_code* is not a valid error code such as -1, the system message
will look like "error -1".
private:
constexpr basic_arg<Context> get_arg(auto_id) {
return context_.next_arg();
}
**Example**::
template <typename Id>
constexpr basic_arg<Context> get_arg(Id arg_id) {
context_.check_arg_id(arg_id);
return context_.get_arg(arg_id);
// This throws a windows_error with the description
// cannot open file 'madeup': The system cannot find the file specified.
// or similar (system message may vary).
const char *filename = "madeup";
LPOFSTRUCT of = LPOFSTRUCT();
HFILE file = OpenFile(filename, &of, OF_READ);
if (file == HFILE_ERROR) {
throw fmt::windows_error(GetLastError(),
"cannot open file '{}'", filename);
}
\endrst
*/
template <typename... Args>
windows_error(int error_code, string_view message, const Args & ... args) {
init(error_code, message, make_args(args...));
}
Context &context_;
};
// An argument reference.
template <typename Char>
struct arg_ref {
enum Kind { NONE, INDEX, NAME };
// Reports a Windows error without throwing an exception.
// Can be used to report errors from destructors.
FMT_API void report_windows_error(int error_code,
string_view message) FMT_NOEXCEPT;
constexpr arg_ref() : kind(NONE), index(0) {}
constexpr explicit arg_ref(unsigned index) : kind(INDEX), index(index) {}
explicit arg_ref(basic_string_view<Char> name) : kind(NAME), name(name) {}
#endif
constexpr arg_ref &operator=(unsigned index) {
kind = INDEX;
this->index = index;
return *this;
}
enum Color { BLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE };
Kind kind;
union {
unsigned index;
basic_string_view<Char> name;
};
};
FMT_API void vprint_colored(Color c, string_view format, args args);
// Format specifiers with width and precision resolved at formatting rather
// than parsing time to allow re-using the same parsed specifiers with
// differents sets of arguments (precompilation of format strings).
template <typename Char>
struct dynamic_format_specs : basic_format_specs<Char> {
arg_ref<Char> width_ref;
arg_ref<Char> precision_ref;
};
/**
Formats a string and prints it to stdout using ANSI escape sequences
to specify color (experimental).
Example:
print_colored(fmt::RED, "Elapsed time: {0:.2f} seconds", 1.23);
*/
template <typename... Args>
inline void print_colored(Color c, string_view format_str,
const Args & ... args) {
vprint_colored(c, format_str, make_args(args...));
}
// Format spec handler that saves references to arguments representing dynamic
// width and precision to be resolved at formatting time.
template <typename ParseContext>
class dynamic_specs_handler :
public specs_setter<typename ParseContext::char_type> {
public:
using char_type = typename ParseContext::char_type;
template <typename ArgFormatter, typename Char, typename Context>
void vformat_to(basic_buffer<Char> &buffer, basic_string_view<Char> format_str,
basic_args<Context> args);
constexpr dynamic_specs_handler(
dynamic_format_specs<char_type> &specs, ParseContext &ctx)
: specs_setter<char_type>(specs), specs_(specs), context_(ctx) {}
inline void vformat_to(buffer &buf, string_view format_str, args args) {
vformat_to<arg_formatter<char>>(buf, format_str, args);
}
template <typename Id>
constexpr void on_dynamic_width(Id arg_id) {
specs_.width_ref = make_arg_ref(arg_id);
}
inline void vformat_to(wbuffer &buf, wstring_view format_str, wargs args) {
vformat_to<arg_formatter<wchar_t>>(buf, format_str, args);
}
template <typename Id>
constexpr void on_dynamic_precision(Id arg_id) {
specs_.precision_ref = make_arg_ref(arg_id);
}
template <typename... Args>
inline void format_to(buffer &buf, string_view format_str,
const Args & ... args) {
vformat_to(buf, format_str, make_args(args...));
}
private:
using arg_ref_type = arg_ref<char_type>;
template <typename... Args>
inline void format_to(wbuffer &buf, wstring_view format_str,
const Args & ... args) {
vformat_to(buf, format_str, make_args<wcontext>(args...));
}
template <typename Id>
constexpr arg_ref_type make_arg_ref(Id arg_id) {
context_.check_arg_id(arg_id);
return arg_ref_type(arg_id);
}
inline std::string vformat(string_view format_str, args args) {
memory_buffer buffer;
vformat_to(buffer, format_str, args);
return to_string(buffer);
}
constexpr arg_ref_type make_arg_ref(auto_id) {
const char *error = 0;
auto index = context_.next_arg_index(error);
if (error)
FMT_THROW(format_error(error));
return arg_ref_type(index);
}
/**
\rst
Formats arguments and returns the result as a string.
dynamic_format_specs<char_type> &specs_;
ParseContext &context_;
};
**Example**::
template <typename Iterator, typename IDHandler>
constexpr Iterator parse_arg_id(Iterator it, IDHandler &&handler) {
using char_type = typename std::iterator_traits<Iterator>::value_type;
char_type c = *it;
if (c == '}' || c == ':') {
handler();
return it;
}
if (c >= '0' && c <= '9') {
unsigned index = parse_nonnegative_int(it, handler);
if (*it != '}' && *it != ':') {
handler.on_error("invalid format string");
return it;
}
handler(index);
return it;
}
if (!is_name_start(c)) {
handler.on_error("invalid format string");
return it;
}
auto start = it;
do {
c = *++it;
} while (is_name_start(c) || ('0' <= c && c <= '9'));
handler(basic_string_view<char_type>(pointer_from(start), it - start));
return it;
std::string message = format("The answer is {}", 42);
\endrst
*/
template <typename... Args>
inline std::string format(string_view format_str, const Args & ... args) {
return vformat(format_str, make_args(args...));
}
// Adapts SpecHandler to IDHandler API for dynamic width.
template <typename SpecHandler, typename Char>
struct width_adapter {
explicit constexpr width_adapter(SpecHandler &h) : handler(h) {}
template <typename String, typename... Args>
inline typename std::enable_if<
std::is_base_of<internal::format_string, String>::value, std::string>::type
format(String format_str, const Args & ... args) {
constexpr bool invalid_format = internal::check_format_string<char, Args...>(
string_view(format_str.value(), format_str.size()));
return vformat(format_str.value(), make_args(args...));
}
constexpr void operator()() { handler.on_dynamic_width(auto_id()); }
constexpr void operator()(unsigned id) { handler.on_dynamic_width(id); }
constexpr void operator()(basic_string_view<Char> id) {
handler.on_dynamic_width(id);
}
inline std::wstring vformat(wstring_view format_str, wargs args) {
wmemory_buffer buffer;
vformat_to(buffer, format_str, args);
return to_string(buffer);
}
constexpr void on_error(const char *message) { handler.on_error(message); }
template <typename... Args>
inline std::wstring format(wstring_view format_str, const Args & ... args) {
return vformat(format_str, make_args<wcontext>(args...));
}
SpecHandler &handler;
};
FMT_API void vprint(std::FILE *f, string_view format_str, args args);
// Adapts SpecHandler to IDHandler API for dynamic precision.
template <typename SpecHandler, typename Char>
struct precision_adapter {
explicit constexpr precision_adapter(SpecHandler &h) : handler(h) {}
/**
\rst
Prints formatted data to the file *f*.
constexpr void operator()() { handler.on_dynamic_precision(auto_id()); }
constexpr void operator()(unsigned id) { handler.on_dynamic_precision(id); }
constexpr void operator()(basic_string_view<Char> id) {
handler.on_dynamic_precision(id);
}
**Example**::
constexpr void on_error(const char *message) { handler.on_error(message); }
print(stderr, "Don't {}!", "panic");
\endrst
*/
template <typename... Args>
inline void print(std::FILE *f, string_view format_str,
const Args & ... args) {
vprint(f, format_str, make_args(args...));
}
SpecHandler &handler;
};
FMT_API void vprint(string_view format_str, args args);
// Parses standard format specifiers and sends notifications about parsed
// 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 SpecHandler>
constexpr Iterator parse_format_specs(Iterator it, SpecHandler &&handler) {
using char_type = typename std::iterator_traits<Iterator>::value_type;
// Parse fill and alignment.
if (char_type c = *it) {
alignment align = ALIGN_DEFAULT;
int i = 1;
do {
auto p = it + i;
switch (*p) {
case '<':
align = ALIGN_LEFT;
break;
case '>':
align = ALIGN_RIGHT;
break;
case '=':
align = ALIGN_NUMERIC;
break;
case '^':
align = ALIGN_CENTER;
break;
}
if (align != ALIGN_DEFAULT) {
handler.on_align(align);
if (p != it) {
if (c == '}') break;
if (c == '{') {
handler.on_error("invalid fill character '{'");
return it;
}
it += 2;
handler.on_fill(c);
} else ++it;
break;
}
} while (--i >= 0);
}
/**
\rst
Prints formatted data to ``stdout``.
// Parse sign.
switch (*it) {
case '+':
handler.on_plus();
++it;
break;
case '-':
handler.on_minus();
++it;
break;
case ' ':
handler.on_space();
++it;
break;
}
**Example**::
if (*it == '#') {
handler.on_hash();
++it;
}
print("Elapsed time: {0:.2f} seconds", 1.23);
\endrst
*/
template <typename... Args>
inline void print(string_view format_str, const Args & ... args) {
vprint(format_str, make_args(args...));
}
// Parse zero flag.
if (*it == '0') {
handler.on_zero();
++it;
}
/**
Fast integer formatter.
*/
class FormatInt {
private:
// Buffer should be large enough to hold all digits (digits10 + 1),
// a sign and a null character.
enum {BUFFER_SIZE = std::numeric_limits<unsigned long long>::digits10 + 3};
mutable char buffer_[BUFFER_SIZE];
char *str_;
// Parse width.
if ('0' <= *it && *it <= '9') {
handler.on_width(parse_nonnegative_int(it, handler));
} else if (*it == '{') {
it = parse_arg_id(it + 1, width_adapter<SpecHandler, char_type>(handler));
if (*it++ != '}') {
handler.on_error("invalid format string");
return it;
// Formats value in reverse and returns the number of digits.
char *format_decimal(unsigned long long value) {
char *buffer_end = buffer_ + BUFFER_SIZE - 1;
while (value >= 100) {
// Integer division is slow so do it for a group of two digits instead
// of for every digit. The idea comes from the talk by Alexandrescu
// "Three Optimization Tips for C++". See speed-test for a comparison.
unsigned index = static_cast<unsigned>((value % 100) * 2);
value /= 100;
*--buffer_end = internal::data::DIGITS[index + 1];
*--buffer_end = internal::data::DIGITS[index];
}
}
// Parse precision.
if (*it == '.') {
++it;
if ('0' <= *it && *it <= '9') {
handler.on_precision(parse_nonnegative_int(it, handler));
} else if (*it == '{') {
it = parse_arg_id(
it + 1, precision_adapter<SpecHandler, char_type>(handler));
if (*it++ != '}') {
handler.on_error("invalid format string");
return it;
}
} else {
handler.on_error("missing precision specifier");
return it;
if (value < 10) {
*--buffer_end = static_cast<char>('0' + value);
return buffer_end;
}
handler.end_precision();
unsigned index = static_cast<unsigned>(value * 2);
*--buffer_end = internal::data::DIGITS[index + 1];
*--buffer_end = internal::data::DIGITS[index];
return buffer_end;
}
// Parse type.
if (*it != '}' && *it)
handler.on_type(*it++);
return it;
}
void FormatSigned(long long value) {
unsigned long long abs_value = static_cast<unsigned long long>(value);
bool negative = value < 0;
if (negative)
abs_value = 0 - abs_value;
str_ = format_decimal(abs_value);
if (negative)
*--str_ = '-';
}
template <typename Handler, typename Char>
struct id_adapter {
constexpr explicit id_adapter(Handler &h): handler(h) {}
public:
explicit FormatInt(int value) { FormatSigned(value); }
explicit FormatInt(long value) { FormatSigned(value); }
explicit FormatInt(long long value) { FormatSigned(value); }
explicit FormatInt(unsigned value) : str_(format_decimal(value)) {}
explicit FormatInt(unsigned long value) : str_(format_decimal(value)) {}
explicit FormatInt(unsigned long long value) : str_(format_decimal(value)) {}
constexpr void operator()() { handler.on_arg_id(); }
constexpr void operator()(unsigned id) { handler.on_arg_id(id); }
constexpr void operator()(basic_string_view<Char> id) {
handler.on_arg_id(id);
/** Returns the number of characters written to the output buffer. */
std::size_t size() const {
return internal::to_unsigned(buffer_ - str_ + BUFFER_SIZE - 1);
}
constexpr void on_error(const char *message) {
handler.on_error(message);
/**
Returns a pointer to the output buffer content. No terminating null
character is appended.
*/
const char *data() const { return str_; }
/**
Returns a pointer to the output buffer content with terminating null
character appended.
*/
const char *c_str() const {
buffer_[BUFFER_SIZE - 1] = '\0';
return str_;
}
Handler &handler;
/**
\rst
Returns the content of the output buffer as an ``std::string``.
\endrst
*/
std::string str() const { return std::string(str_, size()); }
};
template <typename Iterator, typename Handler>
constexpr void parse_format_string(Iterator it, Handler &&handler) {
using char_type = typename std::iterator_traits<Iterator>::value_type;
auto start = it;
while (*it) {
char_type ch = *it++;
if (ch != '{' && ch != '}') continue;
if (*it == ch) {
handler.on_text(start, it);
start = ++it;
continue;
}
if (ch == '}') {
handler.on_error("unmatched '}' in format string");
// Formats a decimal integer value writing into buffer and returns
// a pointer to the end of the formatted string. This function doesn't
// write a terminating null character.
template <typename T>
inline void format_decimal(char *&buffer, T value) {
typedef typename internal::int_traits<T>::main_type main_type;
main_type abs_value = static_cast<main_type>(value);
if (internal::is_negative(value)) {
*buffer++ = '-';
abs_value = 0 - abs_value;
}
if (abs_value < 100) {
if (abs_value < 10) {
*buffer++ = static_cast<char>('0' + abs_value);
return;
}
handler.on_text(start, it - 1);
it = parse_arg_id(it, id_adapter<Handler, char_type>(handler));
if (*it == '}') {
handler.on_replacement_field(it);
} else if (*it == ':') {
++it;
it = handler.on_format_specs(it);
if (*it != '}')
handler.on_error("unknown format specifier");
} else {
handler.on_error("missing '}' in format string");
}
start = ++it;
unsigned index = static_cast<unsigned>(abs_value * 2);
*buffer++ = internal::data::DIGITS[index];
*buffer++ = internal::data::DIGITS[index + 1];
return;
}
handler.on_text(start, it);
unsigned num_digits = internal::count_digits(abs_value);
internal::format_decimal(buffer, abs_value, num_digits);
buffer += num_digits;
}
// Specifies whether to format T using the standard formatter.
// It is not possible to use get_type in formatter specialization directly
// because of a bug in MSVC.
template <typename T>
struct format_type : std::integral_constant<bool, get_type<T>() != CUSTOM> {};
/**
\rst
Returns a named argument for formatting functions.
// Specifies whether to format enums.
template <typename T, typename Enable = void>
struct format_enum : std::integral_constant<bool, std::is_enum<T>::value> {};
**Example**::
template <template <typename> class Handler, typename Spec, typename Char>
void handle_dynamic_spec(
Spec &value, arg_ref<Char> ref, basic_context<Char> &ctx) {
switch (ref.kind) {
case arg_ref<Char>::NONE:
break;
case arg_ref<Char>::INDEX:
internal::set_dynamic_spec<Handler>(value, ctx.get_arg(ref.index));
break;
case arg_ref<Char>::NAME:
internal::set_dynamic_spec<Handler>(value, ctx.get_arg(ref.name));
break;
}
print("Elapsed time: {s:.2f} seconds", arg("s", 1.23));
\endrst
*/
template <typename T>
inline internal::named_arg<context> arg(string_view name, const T &arg) {
return internal::named_arg<context>(name, arg);
}
} // namespace internal
template <typename T>
inline internal::named_arg<wcontext> arg(wstring_view name, const T &arg) {
return internal::named_arg<wcontext>(name, arg);
}
// The following two functions are deleted intentionally to disable
// nested named arguments as in ``format("{}", arg("a", arg("b", 42)))``.
template <typename Context>
void arg(string_view, const internal::named_arg<Context>&)
FMT_DELETED_OR_UNDEFINED;
template <typename Context>
void arg(wstring_view, const internal::named_arg<Context>&)
FMT_DELETED_OR_UNDEFINED;
// Formatter of objects of type T.
template <typename T, typename Char>
......@@ -3849,73 +3912,17 @@ inline const void *ptr(const T *p) { return p; }
namespace fmt {
namespace internal {
template <typename Char, typename T>
constexpr const Char *parse_format_specs(parse_context<Char> &ctx) {
formatter<T, Char> f;
return f.parse(ctx);
}
# if FMT_UDL_TEMPLATE
template <typename Char, typename... Args>
struct udl_format_handler {
public:
explicit constexpr udl_format_handler(const Char *end) : end_(end) {}
constexpr void on_text(const Char *, const Char *) {}
constexpr void on_arg_id() {
++arg_index_;
check_arg_index();
}
constexpr void on_arg_id(unsigned index) {
arg_index_ = index;
check_arg_index();
}
constexpr void on_arg_id(basic_string_view<Char>) {}
constexpr void on_replacement_field(const Char *) {}
constexpr const Char *on_format_specs(const Char *s) {
parse_context<Char> ctx(basic_string_view<Char>(s, end_ - s));
return parse_funcs_[arg_index_](ctx);
}
// This function is intentionally not constexpr to give a compile-time error.
void on_error(const char *);
private:
constexpr void check_arg_index() {
if (arg_index_ < 0 || arg_index_ >= sizeof...(Args))
on_error("argument index out of range");
}
// Format specifier parsing function.
using parse_func = const Char *(*)(parse_context<Char> &);
const Char *end_;
int arg_index_ = -1;
parse_func parse_funcs_[sizeof...(Args)] = {
&parse_format_specs<Char, Args>...
};
};
template <typename Char, Char... CHARS>
class udl_formatter {
public:
template <typename... Args>
std::basic_string<Char> operator()(const Args &... args) const {
constexpr Char s[] = {CHARS..., '\0'};
constexpr bool invalid_format = check_format<Args...>(s);
constexpr bool invalid_format = check_format_string<Char, Args...>(
basic_string_view<Char>(s, sizeof...(CHARS)));
return format(s, args...);
}
private:
template <typename... Args>
static constexpr bool check_format(const Char *s) {
udl_format_handler<Char, Args...> handler(s + sizeof...(CHARS));
internal::parse_format_string(s, handler);
return true;
}
};
# else
template <typename Char>
......@@ -3984,6 +3991,13 @@ operator"" _a(const wchar_t *s, std::size_t) { return {s}; }
} // namespace fmt
#endif // FMT_USE_USER_DEFINED_LITERALS
#define FMT_STRING(s) [] { \
struct S : fmt::internal::format_string { \
static constexpr auto value() { return s; } \
static constexpr size_t size() { return sizeof(s); } \
}; \
return S{}; \
}()
#ifdef FMT_HEADER_ONLY
# define FMT_FUNC inline
......
......@@ -1815,4 +1815,5 @@ TEST(FormatTest, ConstexprParseFormatString) {
TEST(FormatTest, UdlTemplate) {
EXPECT_EQ("foo", "foo"_format());
EXPECT_EQ(" 42", "{0:10}"_format(42));
EXPECT_EQ("42", fmt::format(FMT_STRING("{}"), 42));
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment