added thread safety

This commit is contained in:
wind85 2017-07-03 03:56:27 +02:00
parent 589ccc1675
commit 49e6c114ef

View File

@ -12,6 +12,7 @@
#include <algorithm> // std::fill_n #include <algorithm> // std::fill_n
#include <limits> // std::numeric_limits #include <limits> // std::numeric_limits
#include <mutex>
#include "ostream.h" #include "ostream.h"
@ -22,71 +23,78 @@ namespace internal {
// signed and unsigned integers. // signed and unsigned integers.
template <bool IsSigned> template <bool IsSigned>
struct IntChecker { struct IntChecker {
template <typename T> template <typename T>
static bool fits_in_int(T value) { static bool fits_in_int(T value) {
unsigned max = std::numeric_limits<int>::max(); unsigned max = std::numeric_limits<int>::max();
return value <= max; return value <= max;
} }
static bool fits_in_int(bool) { return true; } static bool fits_in_int(bool) { return true; }
}; };
template <> template <>
struct IntChecker<true> { struct IntChecker<true> {
template <typename T> template <typename T>
static bool fits_in_int(T value) { static bool fits_in_int(T value) {
return value >= std::numeric_limits<int>::min() && return value >= std::numeric_limits<int>::min() &&
value <= std::numeric_limits<int>::max(); value <= std::numeric_limits<int>::max();
} }
static bool fits_in_int(int) { return true; } static bool fits_in_int(int) { return true; }
}; };
class PrecisionHandler : public ArgVisitor<PrecisionHandler, int> { class PrecisionHandler : public ArgVisitor<PrecisionHandler, int> {
public: public:
void report_unhandled_arg() { void report_unhandled_arg() {
FMT_THROW(FormatError("precision is not integer")); FMT_THROW(FormatError("precision is not integer"));
} }
template <typename T> template <typename T>
int visit_any_int(T value) { int visit_any_int(T value) {
if (!IntChecker<std::numeric_limits<T>::is_signed>::fits_in_int(value)) if (!IntChecker<std::numeric_limits<T>::is_signed>::fits_in_int(
FMT_THROW(FormatError("number is too big")); value))
return static_cast<int>(value); FMT_THROW(FormatError("number is too big"));
} return static_cast<int>(value);
}
}; };
// IsZeroInt::visit(arg) returns true iff arg is a zero integer. // IsZeroInt::visit(arg) returns true iff arg is a zero integer.
class IsZeroInt : public ArgVisitor<IsZeroInt, bool> { class IsZeroInt : public ArgVisitor<IsZeroInt, bool> {
public: public:
template <typename T> template <typename T>
bool visit_any_int(T value) { return value == 0; } bool visit_any_int(T value) {
return value == 0;
}
}; };
// returns the default type for format specific "%s" // returns the default type for format specific "%s"
class DefaultType : public ArgVisitor<DefaultType, char> { class DefaultType : public ArgVisitor<DefaultType, char> {
public: public:
char visit_char(int) { return 'c'; } char visit_char(int) { return 'c'; }
char visit_bool(bool) { return 's'; } char visit_bool(bool) { return 's'; }
char visit_pointer(const void *) { return 'p'; } char visit_pointer(const void *) { return 'p'; }
template <typename T> template <typename T>
char visit_any_int(T) { return 'd'; } char visit_any_int(T) {
return 'd';
}
template <typename T> template <typename T>
char visit_any_double(T) { return 'g'; } char visit_any_double(T) {
return 'g';
}
char visit_unhandled_arg() { return 's'; } char visit_unhandled_arg() { return 's'; }
}; };
template <typename T, typename U> template <typename T, typename U>
struct is_same { struct is_same {
enum { value = 0 }; enum { value = 0 };
}; };
template <typename T> template <typename T>
struct is_same<T, T> { struct is_same<T, T> {
enum { value = 1 }; enum { value = 1 };
}; };
// An argument visitor that converts an integer argument to T for printf, // An argument visitor that converts an integer argument to T for printf,
@ -95,107 +103,113 @@ struct is_same<T, T> {
// 'd' and 'i' - signed, other - unsigned) // 'd' and 'i' - signed, other - unsigned)
template <typename T = void> template <typename T = void>
class ArgConverter : public ArgVisitor<ArgConverter<T>, void> { class ArgConverter : public ArgVisitor<ArgConverter<T>, void> {
private: private:
internal::Arg &arg_; internal::Arg &arg_;
wchar_t type_; wchar_t type_;
FMT_DISALLOW_COPY_AND_ASSIGN(ArgConverter); FMT_DISALLOW_COPY_AND_ASSIGN(ArgConverter);
public: public:
ArgConverter(internal::Arg &arg, wchar_t type) ArgConverter(internal::Arg &arg, wchar_t type)
: arg_(arg), type_(type) {} : arg_(arg), type_(type) {}
void visit_bool(bool value) { void visit_bool(bool value) {
if (type_ != 's') if (type_ != 's') visit_any_int(value);
visit_any_int(value); }
}
void visit_char(char value) { void visit_char(char value) {
if (type_ != 's') if (type_ != 's') visit_any_int(value);
visit_any_int(value); }
}
template <typename U> template <typename U>
void visit_any_int(U value) { void visit_any_int(U value) {
bool is_signed = type_ == 'd' || type_ == 'i'; bool is_signed = type_ == 'd' || type_ == 'i';
if (type_ == 's') { if (type_ == 's') {
is_signed = std::numeric_limits<U>::is_signed; is_signed = std::numeric_limits<U>::is_signed;
} }
using internal::Arg; using internal::Arg;
typedef typename internal::Conditional< typedef typename internal::Conditional<is_same<T, void>::value,
is_same<T, void>::value, U, T>::type TargetType; U, T>::type TargetType;
if (sizeof(TargetType) <= sizeof(int)) { if (sizeof(TargetType) <= sizeof(int)) {
// Extra casts are used to silence warnings. // Extra casts are used to silence warnings.
if (is_signed) { if (is_signed) {
arg_.type = Arg::INT; arg_.type = Arg::INT;
arg_.int_value = static_cast<int>(static_cast<TargetType>(value)); arg_.int_value = static_cast<int>(
} else { static_cast<TargetType>(value));
arg_.type = Arg::UINT; } else {
typedef typename internal::MakeUnsigned<TargetType>::Type Unsigned; arg_.type = Arg::UINT;
arg_.uint_value = static_cast<unsigned>(static_cast<Unsigned>(value)); typedef typename internal::MakeUnsigned<
} TargetType>::Type Unsigned;
} else { arg_.uint_value = static_cast<unsigned>(
if (is_signed) { static_cast<Unsigned>(value));
arg_.type = Arg::LONG_LONG; }
// glibc's printf doesn't sign extend arguments of smaller types: } else {
// std::printf("%lld", -42); // prints "4294967254" if (is_signed) {
// but we don't have to do the same because it's a UB. arg_.type = Arg::LONG_LONG;
arg_.long_long_value = static_cast<LongLong>(value); // glibc's printf doesn't sign extend arguments
} else { // of smaller types:
arg_.type = Arg::ULONG_LONG; // std::printf("%lld", -42); // prints
arg_.ulong_long_value = // "4294967254"
static_cast<typename internal::MakeUnsigned<U>::Type>(value); // but we don't have to do the same because it's
} // a UB.
} arg_.long_long_value =
} static_cast<LongLong>(value);
} else {
arg_.type = Arg::ULONG_LONG;
arg_.ulong_long_value = static_cast<
typename internal::MakeUnsigned<U>::Type>(
value);
}
}
}
}; };
// Converts an integer argument to char for printf. // Converts an integer argument to char for printf.
class CharConverter : public ArgVisitor<CharConverter, void> { class CharConverter : public ArgVisitor<CharConverter, void> {
private: private:
internal::Arg &arg_; internal::Arg &arg_;
FMT_DISALLOW_COPY_AND_ASSIGN(CharConverter); FMT_DISALLOW_COPY_AND_ASSIGN(CharConverter);
public: public:
explicit CharConverter(internal::Arg &arg) : arg_(arg) {} explicit CharConverter(internal::Arg &arg) : arg_(arg) {}
template <typename T> template <typename T>
void visit_any_int(T value) { void visit_any_int(T value) {
arg_.type = internal::Arg::CHAR; arg_.type = internal::Arg::CHAR;
arg_.int_value = static_cast<char>(value); arg_.int_value = static_cast<char>(value);
} }
}; };
// Checks if an argument is a valid printf width specifier and sets // Checks if an argument is a valid printf width specifier and sets
// left alignment if it is negative. // left alignment if it is negative.
class WidthHandler : public ArgVisitor<WidthHandler, unsigned> { class WidthHandler : public ArgVisitor<WidthHandler, unsigned> {
private: private:
FormatSpec &spec_; FormatSpec &spec_;
FMT_DISALLOW_COPY_AND_ASSIGN(WidthHandler); FMT_DISALLOW_COPY_AND_ASSIGN(WidthHandler);
public: public:
explicit WidthHandler(FormatSpec &spec) : spec_(spec) {} explicit WidthHandler(FormatSpec &spec) : spec_(spec) {}
void report_unhandled_arg() { void report_unhandled_arg() {
FMT_THROW(FormatError("width is not integer")); FMT_THROW(FormatError("width is not integer"));
} }
template <typename T> template <typename T>
unsigned visit_any_int(T value) { unsigned visit_any_int(T value) {
typedef typename internal::IntTraits<T>::MainType UnsignedType; typedef typename internal::IntTraits<T>::MainType UnsignedType;
UnsignedType width = static_cast<UnsignedType>(value); UnsignedType width = static_cast<UnsignedType>(value);
if (internal::is_negative(value)) { if (internal::is_negative(value)) {
spec_.align_ = ALIGN_LEFT; spec_.align_ = ALIGN_LEFT;
width = 0 - width; width = 0 - width;
} }
unsigned int_max = std::numeric_limits<int>::max(); unsigned int_max = std::numeric_limits<int>::max();
if (width > int_max) if (width > int_max)
FMT_THROW(FormatError("number is too big")); FMT_THROW(FormatError("number is too big"));
return static_cast<unsigned>(width); return static_cast<unsigned>(width);
} }
}; };
} // namespace internal } // namespace internal
@ -217,315 +231,329 @@ class WidthHandler : public ArgVisitor<WidthHandler, unsigned> {
\endrst \endrst
*/ */
template <typename Impl, typename Char, typename Spec> template <typename Impl, typename Char, typename Spec>
class BasicPrintfArgFormatter : class BasicPrintfArgFormatter
public internal::ArgFormatterBase<Impl, Char, Spec> { : public internal::ArgFormatterBase<Impl, Char, Spec> {
private: private:
void write_null_pointer() { void write_null_pointer() {
this->spec().type_ = 0; this->spec().type_ = 0;
this->write("(nil)"); this->write("(nil)");
} }
typedef internal::ArgFormatterBase<Impl, Char, Spec> Base; typedef internal::ArgFormatterBase<Impl, Char, Spec> Base;
public: public:
/** /**
\rst \rst
Constructs an argument formatter object. Constructs an argument formatter object.
*writer* is a reference to the output writer and *spec* contains format *writer* is a reference to the output writer and *spec* contains
specifier information for standard argument types. format
\endrst specifier information for standard argument types.
*/ \endrst
BasicPrintfArgFormatter(BasicWriter<Char> &w, Spec &s) */
: internal::ArgFormatterBase<Impl, Char, Spec>(w, s) {} BasicPrintfArgFormatter(BasicWriter<Char> &w, Spec &s)
: internal::ArgFormatterBase<Impl, Char, Spec>(w, s) {}
/** Formats an argument of type ``bool``. */ /** Formats an argument of type ``bool``. */
void visit_bool(bool value) { void visit_bool(bool value) {
Spec &fmt_spec = this->spec(); Spec &fmt_spec = this->spec();
if (fmt_spec.type_ != 's') if (fmt_spec.type_ != 's') return this->visit_any_int(value);
return this->visit_any_int(value); fmt_spec.type_ = 0;
fmt_spec.type_ = 0; this->write(value);
this->write(value); }
}
/** Formats a character. */ /** Formats a character. */
void visit_char(int value) { void visit_char(int value) {
const Spec &fmt_spec = this->spec(); const Spec &fmt_spec = this->spec();
BasicWriter<Char> &w = this->writer(); BasicWriter<Char> &w = this->writer();
if (fmt_spec.type_ && fmt_spec.type_ != 'c') if (fmt_spec.type_ && fmt_spec.type_ != 'c')
w.write_int(value, fmt_spec); w.write_int(value, fmt_spec);
typedef typename BasicWriter<Char>::CharPtr CharPtr; typedef typename BasicWriter<Char>::CharPtr CharPtr;
CharPtr out = CharPtr(); CharPtr out = CharPtr();
if (fmt_spec.width_ > 1) { if (fmt_spec.width_ > 1) {
Char fill = ' '; Char fill = ' ';
out = w.grow_buffer(fmt_spec.width_); out = w.grow_buffer(fmt_spec.width_);
if (fmt_spec.align_ != ALIGN_LEFT) { if (fmt_spec.align_ != ALIGN_LEFT) {
std::fill_n(out, fmt_spec.width_ - 1, fill); std::fill_n(out, fmt_spec.width_ - 1, fill);
out += fmt_spec.width_ - 1; out += fmt_spec.width_ - 1;
} else { } else {
std::fill_n(out + 1, fmt_spec.width_ - 1, fill); std::fill_n(out + 1, fmt_spec.width_ - 1, fill);
} }
} else { } else {
out = w.grow_buffer(1); out = w.grow_buffer(1);
} }
*out = static_cast<Char>(value); *out = static_cast<Char>(value);
} }
/** Formats a null-terminated C string. */ /** Formats a null-terminated C string. */
void visit_cstring(const char *value) { void visit_cstring(const char *value) {
if (value) if (value)
Base::visit_cstring(value); Base::visit_cstring(value);
else if (this->spec().type_ == 'p') else if (this->spec().type_ == 'p')
write_null_pointer(); write_null_pointer();
else else
this->write("(null)"); this->write("(null)");
} }
/** Formats a pointer. */ /** Formats a pointer. */
void visit_pointer(const void *value) { void visit_pointer(const void *value) {
if (value) if (value) return Base::visit_pointer(value);
return Base::visit_pointer(value); this->spec().type_ = 0;
this->spec().type_ = 0; write_null_pointer();
write_null_pointer(); }
}
/** Formats an argument of a custom (user-defined) type. */ /** Formats an argument of a custom (user-defined) type. */
void visit_custom(internal::Arg::CustomValue c) { void visit_custom(internal::Arg::CustomValue c) {
BasicFormatter<Char> formatter(ArgList(), this->writer()); BasicFormatter<Char> formatter(ArgList(), this->writer());
const Char format_str[] = {'}', 0}; const Char format_str[] = {'}', 0};
const Char *format = format_str; const Char *format = format_str;
c.format(&formatter, c.value, &format); c.format(&formatter, c.value, &format);
} }
}; };
/** The default printf argument formatter. */ /** The default printf argument formatter. */
template <typename Char> template <typename Char>
class PrintfArgFormatter : class PrintfArgFormatter
public BasicPrintfArgFormatter<PrintfArgFormatter<Char>, Char, FormatSpec> { : public BasicPrintfArgFormatter<PrintfArgFormatter<Char>, Char,
public: FormatSpec> {
/** Constructs an argument formatter object. */ public:
PrintfArgFormatter(BasicWriter<Char> &w, FormatSpec &s) /** Constructs an argument formatter object. */
: BasicPrintfArgFormatter<PrintfArgFormatter<Char>, Char, FormatSpec>(w, s) {} PrintfArgFormatter(BasicWriter<Char> &w, FormatSpec &s)
: BasicPrintfArgFormatter<PrintfArgFormatter<Char>, Char,
FormatSpec>(w, s) {}
}; };
/** 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, typename ArgFormatter = PrintfArgFormatter<Char> > template <typename Char, typename ArgFormatter = PrintfArgFormatter<Char> >
class PrintfFormatter : private internal::FormatterBase { class PrintfFormatter : private internal::FormatterBase {
private: private:
BasicWriter<Char> &writer_; BasicWriter<Char> &writer_;
void parse_flags(FormatSpec &spec, const Char *&s); void parse_flags(FormatSpec &spec, const Char *&s);
// Returns the argument with specified index or, if arg_index is equal // Returns the argument with specified index or, if arg_index is equal
// to the maximum unsigned value, the next argument. // to the maximum unsigned value, the next argument.
internal::Arg get_arg( internal::Arg get_arg(
const Char *s, const Char *s,
unsigned arg_index = (std::numeric_limits<unsigned>::max)()); unsigned arg_index = (std::numeric_limits<unsigned>::max)());
// Parses argument index, flags and width and returns the argument index. // Parses argument index, flags and width and returns the argument
unsigned parse_header(const Char *&s, FormatSpec &spec); // index.
unsigned parse_header(const Char *&s, FormatSpec &spec);
public: public:
/** /**
\rst \rst
Constructs a ``PrintfFormatter`` object. References to the arguments and Constructs a ``PrintfFormatter`` object. References to the arguments
the writer are stored in the formatter object so make sure they have and
appropriate lifetimes. the writer are stored in the formatter object so make sure they have
\endrst appropriate lifetimes.
*/ \endrst
explicit PrintfFormatter(const ArgList &al, BasicWriter<Char> &w) */
: FormatterBase(al), writer_(w) {} explicit PrintfFormatter(const ArgList &al, BasicWriter<Char> &w)
: FormatterBase(al), writer_(w) {}
/** Formats stored arguments and writes the output to the writer. */ /** Formats stored arguments and writes the output to the writer. */
void format(BasicCStringRef<Char> format_str); void format(BasicCStringRef<Char> format_str);
}; };
template <typename Char, typename AF> template <typename Char, typename AF>
void PrintfFormatter<Char, AF>::parse_flags(FormatSpec &spec, const Char *&s) { void PrintfFormatter<Char, AF>::parse_flags(FormatSpec &spec, const Char *&s) {
for (;;) { for (;;) {
switch (*s++) { switch (*s++) {
case '-': case '-':
spec.align_ = ALIGN_LEFT; spec.align_ = ALIGN_LEFT;
break; break;
case '+': case '+':
spec.flags_ |= SIGN_FLAG | PLUS_FLAG; spec.flags_ |= SIGN_FLAG | PLUS_FLAG;
break; break;
case '0': case '0':
spec.fill_ = '0'; spec.fill_ = '0';
break; break;
case ' ': case ' ':
spec.flags_ |= SIGN_FLAG; spec.flags_ |= SIGN_FLAG;
break; break;
case '#': case '#':
spec.flags_ |= HASH_FLAG; spec.flags_ |= HASH_FLAG;
break; break;
default: default:
--s; --s;
return; return;
} }
} }
} }
template <typename Char, typename AF> template <typename Char, typename AF>
internal::Arg PrintfFormatter<Char, AF>::get_arg(const Char *s, internal::Arg PrintfFormatter<Char, AF>::get_arg(const Char *s,
unsigned arg_index) { unsigned arg_index) {
(void)s; (void)s;
const char *error = FMT_NULL; const char *error = FMT_NULL;
internal::Arg arg = arg_index == std::numeric_limits<unsigned>::max() ? internal::Arg arg = arg_index == std::numeric_limits<unsigned>::max()
next_arg(error) : FormatterBase::get_arg(arg_index - 1, error); ? next_arg(error)
if (error) : FormatterBase::get_arg(arg_index - 1, error);
FMT_THROW(FormatError(!*s ? "invalid format string" : error)); if (error)
return arg; FMT_THROW(FormatError(!*s ? "invalid format string" : error));
return arg;
} }
template <typename Char, typename AF> template <typename Char, typename AF>
unsigned PrintfFormatter<Char, AF>::parse_header( unsigned PrintfFormatter<Char, AF>::parse_header(const Char *&s,
const Char *&s, FormatSpec &spec) { FormatSpec &spec) {
unsigned arg_index = std::numeric_limits<unsigned>::max(); unsigned arg_index = std::numeric_limits<unsigned>::max();
Char c = *s; Char c = *s;
if (c >= '0' && c <= '9') { if (c >= '0' && c <= '9') {
// Parse an argument index (if followed by '$') or a width possibly // Parse an argument index (if followed by '$') or a width
// preceded with '0' flag(s). // possibly
unsigned value = internal::parse_nonnegative_int(s); // preceded with '0' flag(s).
if (*s == '$') { // value is an argument index unsigned value = internal::parse_nonnegative_int(s);
++s; if (*s == '$') { // value is an argument index
arg_index = value; ++s;
} else { arg_index = value;
if (c == '0') } else {
spec.fill_ = '0'; if (c == '0') spec.fill_ = '0';
if (value != 0) { if (value != 0) {
// Nonzero value means that we parsed width and don't need to // Nonzero value means that we parsed width and
// parse it or flags again, so return now. // don't need to
spec.width_ = value; // parse it or flags again, so return now.
return arg_index; spec.width_ = value;
} return arg_index;
} }
} }
parse_flags(spec, s); }
// Parse width. parse_flags(spec, s);
if (*s >= '0' && *s <= '9') { // Parse width.
spec.width_ = internal::parse_nonnegative_int(s); if (*s >= '0' && *s <= '9') {
} else if (*s == '*') { spec.width_ = internal::parse_nonnegative_int(s);
++s; } else if (*s == '*') {
spec.width_ = internal::WidthHandler(spec).visit(get_arg(s)); ++s;
} spec.width_ = internal::WidthHandler(spec).visit(get_arg(s));
return arg_index; }
return arg_index;
} }
template <typename Char, typename AF> template <typename Char, typename AF>
void PrintfFormatter<Char, AF>::format(BasicCStringRef<Char> format_str) { void PrintfFormatter<Char, AF>::format(BasicCStringRef<Char> format_str) {
const Char *start = format_str.c_str(); const Char *start = format_str.c_str();
const Char *s = start; const Char *s = start;
while (*s) { while (*s) {
Char c = *s++; Char c = *s++;
if (c != '%') continue; if (c != '%') continue;
if (*s == c) { if (*s == c) {
write(writer_, start, s); write(writer_, start, s);
start = ++s; start = ++s;
continue; continue;
} }
write(writer_, start, s - 1); write(writer_, start, s - 1);
FormatSpec spec; FormatSpec spec;
spec.align_ = ALIGN_RIGHT; spec.align_ = ALIGN_RIGHT;
// Parse argument index, flags and width. // Parse argument index, flags and width.
unsigned arg_index = parse_header(s, spec); unsigned arg_index = parse_header(s, spec);
// Parse precision. // Parse precision.
if (*s == '.') { if (*s == '.') {
++s; ++s;
if ('0' <= *s && *s <= '9') { if ('0' <= *s && *s <= '9') {
spec.precision_ = static_cast<int>(internal::parse_nonnegative_int(s)); spec.precision_ = static_cast<int>(
} else if (*s == '*') { internal::parse_nonnegative_int(s));
++s; } else if (*s == '*') {
spec.precision_ = internal::PrecisionHandler().visit(get_arg(s)); ++s;
} else { spec.precision_ =
spec.precision_ = 0; internal::PrecisionHandler().visit(
} get_arg(s));
} } else {
spec.precision_ = 0;
}
}
using internal::Arg; using internal::Arg;
Arg arg = get_arg(s, arg_index); Arg arg = get_arg(s, arg_index);
if (spec.flag(HASH_FLAG) && internal::IsZeroInt().visit(arg)) if (spec.flag(HASH_FLAG) && internal::IsZeroInt().visit(arg))
spec.flags_ &= ~internal::to_unsigned<int>(HASH_FLAG); spec.flags_ &= ~internal::to_unsigned<int>(HASH_FLAG);
if (spec.fill_ == '0') { if (spec.fill_ == '0') {
if (arg.type <= Arg::LAST_NUMERIC_TYPE) if (arg.type <= Arg::LAST_NUMERIC_TYPE)
spec.align_ = ALIGN_NUMERIC; spec.align_ = ALIGN_NUMERIC;
else else
spec.fill_ = ' '; // Ignore '0' flag for non-numeric types. spec.fill_ = ' '; // Ignore '0' flag for
} // non-numeric types.
}
// Parse length and convert the argument to the required type. // Parse length and convert the argument to the required type.
using internal::ArgConverter; using internal::ArgConverter;
switch (*s++) { switch (*s++) {
case 'h': case 'h':
if (*s == 'h') if (*s == 'h')
ArgConverter<signed char>(arg, *++s).visit(arg); ArgConverter<signed char>(arg, *++s)
else .visit(arg);
ArgConverter<short>(arg, *s).visit(arg); else
break; ArgConverter<short>(arg, *s).visit(arg);
case 'l': break;
if (*s == 'l') case 'l':
ArgConverter<fmt::LongLong>(arg, *++s).visit(arg); if (*s == 'l')
else ArgConverter<fmt::LongLong>(arg, *++s)
ArgConverter<long>(arg, *s).visit(arg); .visit(arg);
break; else
case 'j': ArgConverter<long>(arg, *s).visit(arg);
ArgConverter<intmax_t>(arg, *s).visit(arg); break;
break; case 'j':
case 'z': ArgConverter<intmax_t>(arg, *s).visit(arg);
ArgConverter<std::size_t>(arg, *s).visit(arg); break;
break; case 'z':
case 't': ArgConverter<std::size_t>(arg, *s).visit(arg);
ArgConverter<std::ptrdiff_t>(arg, *s).visit(arg); break;
break; case 't':
case 'L': ArgConverter<std::ptrdiff_t>(arg, *s).visit(
// printf produces garbage when 'L' is omitted for long double, no arg);
// need to do the same. break;
break; case 'L':
default: // printf produces garbage when 'L' is omitted
--s; // for long double, no
ArgConverter<void>(arg, *s).visit(arg); // need to do the same.
} break;
default:
--s;
ArgConverter<void>(arg, *s).visit(arg);
}
// Parse type. // Parse type.
if (!*s) if (!*s) FMT_THROW(FormatError("invalid format string"));
FMT_THROW(FormatError("invalid format string")); spec.type_ = static_cast<char>(*s++);
spec.type_ = static_cast<char>(*s++);
if (spec.type_ == 's') { if (spec.type_ == 's') {
// set the format type to the default if 's' is specified // set the format type to the default if 's' is
spec.type_ = internal::DefaultType().visit(arg); // specified
} spec.type_ = internal::DefaultType().visit(arg);
}
if (arg.type <= Arg::LAST_INTEGER_TYPE) { if (arg.type <= Arg::LAST_INTEGER_TYPE) {
// Normalize type. // Normalize type.
switch (spec.type_) { switch (spec.type_) {
case 'i': case 'u': case 'i':
spec.type_ = 'd'; case 'u':
break; spec.type_ = 'd';
case 'c': break;
// TODO: handle wchar_t case 'c':
internal::CharConverter(arg).visit(arg); // TODO: handle wchar_t
break; internal::CharConverter(arg).visit(arg);
} break;
} }
}
start = s; start = s;
// Format argument. // Format argument.
AF(writer_, spec).visit(arg); AF(writer_, spec).visit(arg);
} }
write(writer_, start, s); write(writer_, start, s);
} }
inline void printf(Writer &w, CStringRef format, ArgList args) { inline void printf(Writer &w, CStringRef format, ArgList args) {
PrintfFormatter<char>(args, w).format(format); PrintfFormatter<char>(args, w).format(format);
} }
FMT_VARIADIC(void, printf, Writer &, CStringRef) FMT_VARIADIC(void, printf, Writer &, CStringRef)
inline void printf(WWriter &w, WCStringRef format, ArgList args) { inline void printf(WWriter &w, WCStringRef format, ArgList args) {
PrintfFormatter<wchar_t>(args, w).format(format); PrintfFormatter<wchar_t>(args, w).format(format);
} }
FMT_VARIADIC(void, printf, WWriter &, WCStringRef) FMT_VARIADIC(void, printf, WWriter &, WCStringRef)
@ -539,16 +567,16 @@ FMT_VARIADIC(void, printf, WWriter &, WCStringRef)
\endrst \endrst
*/ */
inline std::string sprintf(CStringRef format, ArgList args) { inline std::string sprintf(CStringRef format, ArgList args) {
MemoryWriter w; MemoryWriter w;
printf(w, format, args); printf(w, format, args);
return w.str(); return w.str();
} }
FMT_VARIADIC(std::string, sprintf, CStringRef) FMT_VARIADIC(std::string, sprintf, CStringRef)
inline std::wstring sprintf(WCStringRef format, ArgList args) { inline std::wstring sprintf(WCStringRef format, ArgList args) {
WMemoryWriter w; WMemoryWriter w;
printf(w, format, args); printf(w, format, args);
return w.str(); return w.str();
} }
FMT_VARIADIC_W(std::wstring, sprintf, WCStringRef) FMT_VARIADIC_W(std::wstring, sprintf, WCStringRef)
@ -574,7 +602,7 @@ FMT_VARIADIC(int, fprintf, std::FILE *, CStringRef)
\endrst \endrst
*/ */
inline int printf(CStringRef format, ArgList args) { inline int printf(CStringRef format, ArgList args) {
return fprintf(stdout, format, args); return fprintf(stdout, format, args);
} }
FMT_VARIADIC(int, printf, CStringRef) FMT_VARIADIC(int, printf, CStringRef)
@ -588,16 +616,18 @@ FMT_VARIADIC(int, printf, CStringRef)
\endrst \endrst
*/ */
inline int fprintf(std::ostream &os, CStringRef format_str, ArgList args) { inline int fprintf(std::ostream &os, CStringRef format_str, ArgList args) {
MemoryWriter w; std::mutex mtx;
printf(w, format_str, args); std::unique_lock<std::mutex> lck(mtx);
internal::write(os, w); MemoryWriter w;
return static_cast<int>(w.size()); printf(w, format_str, args);
internal::write(os, w);
return static_cast<int>(w.size());
} }
FMT_VARIADIC(int, fprintf, std::ostream &, CStringRef) FMT_VARIADIC(int, fprintf, std::ostream &, CStringRef)
} // namespace fmt } // namespace fmt
#ifdef FMT_HEADER_ONLY #ifdef FMT_HEADER_ONLY
# include "printf.cc" #include "printf.cc"
#endif #endif
#endif // FMT_PRINTF_H_ #endif // FMT_PRINTF_H_