Format the code using clang-format

This commit is contained in:
Victor Zverovich 2019-01-12 18:27:38 -08:00
parent 9a777b9e1c
commit 58b6f8db48
39 changed files with 4734 additions and 5108 deletions

8
.clang-format Normal file
View File

@ -0,0 +1,8 @@
# Run manually to reformat a file:
# clang-format -i --style=file <file>
Language: Cpp
BasedOnStyle: Google
IndentPPDirectives: AfterHash
IndentCaseLabels: false
AlwaysBreakTemplateDeclarations: false
DerivePointerAlignment: false

View File

@ -28,8 +28,9 @@ enum class numeric_system {
// Parses a put_time-like format string and invokes handler actions. // Parses a put_time-like format string and invokes handler actions.
template <typename Char, typename Handler> template <typename Char, typename Handler>
FMT_CONSTEXPR const Char *parse_chrono_format( FMT_CONSTEXPR const Char* parse_chrono_format(const Char* begin,
const Char *begin, const Char *end, Handler &&handler) { const Char* end,
Handler&& handler) {
auto ptr = begin; auto ptr = begin;
while (ptr != end) { while (ptr != end) {
auto c = *ptr; auto c = *ptr;
@ -38,11 +39,9 @@ FMT_CONSTEXPR const Char *parse_chrono_format(
++ptr; ++ptr;
continue; continue;
} }
if (begin != ptr) if (begin != ptr) handler.on_text(begin, ptr);
handler.on_text(begin, ptr); ++ptr; // consume '%'
++ptr; // consume '%' if (ptr == end) throw format_error("invalid format");
if (ptr == end)
throw format_error("invalid format");
c = *ptr++; c = *ptr++;
switch (c) { switch (c) {
case '%': case '%':
@ -127,8 +126,7 @@ FMT_CONSTEXPR const Char *parse_chrono_format(
break; break;
// Alternative representation: // Alternative representation:
case 'E': { case 'E': {
if (ptr == end) if (ptr == end) throw format_error("invalid format");
throw format_error("invalid format");
c = *ptr++; c = *ptr++;
switch (c) { switch (c) {
case 'c': case 'c':
@ -146,8 +144,7 @@ FMT_CONSTEXPR const Char *parse_chrono_format(
break; break;
} }
case 'O': case 'O':
if (ptr == end) if (ptr == end) throw format_error("invalid format");
throw format_error("invalid format");
c = *ptr++; c = *ptr++;
switch (c) { switch (c) {
case 'w': case 'w':
@ -177,16 +174,14 @@ FMT_CONSTEXPR const Char *parse_chrono_format(
} }
begin = ptr; begin = ptr;
} }
if (begin != ptr) if (begin != ptr) handler.on_text(begin, ptr);
handler.on_text(begin, ptr);
return ptr; return ptr;
} }
struct chrono_format_checker { struct chrono_format_checker {
void report_no_date() { throw format_error("no date"); } void report_no_date() { throw format_error("no date"); }
template <typename Char> template <typename Char> void on_text(const Char*, const Char*) {}
void on_text(const Char *, const Char *) {}
void on_abbr_weekday() { report_no_date(); } void on_abbr_weekday() { report_no_date(); }
void on_full_weekday() { report_no_date(); } void on_full_weekday() { report_no_date(); }
void on_dec0_weekday(numeric_system) { report_no_date(); } void on_dec0_weekday(numeric_system) { report_no_date(); }
@ -210,24 +205,23 @@ struct chrono_format_checker {
void on_tz_name() { report_no_date(); } void on_tz_name() { report_no_date(); }
}; };
template <typename Int> template <typename Int> inline int to_int(Int value) {
inline int to_int(Int value) {
FMT_ASSERT(value >= (std::numeric_limits<int>::min)() && FMT_ASSERT(value >= (std::numeric_limits<int>::min)() &&
value <= (std::numeric_limits<int>::max)(), "invalid value"); value <= (std::numeric_limits<int>::max)(),
"invalid value");
return static_cast<int>(value); return static_cast<int>(value);
} }
template <typename FormatContext, typename OutputIt> template <typename FormatContext, typename OutputIt> struct chrono_formatter {
struct chrono_formatter { FormatContext& context;
FormatContext &context;
OutputIt out; OutputIt out;
std::chrono::seconds s; std::chrono::seconds s;
std::chrono::milliseconds ms; std::chrono::milliseconds ms;
typedef typename FormatContext::char_type char_type; typedef typename FormatContext::char_type char_type;
explicit chrono_formatter(FormatContext &ctx, OutputIt o) explicit chrono_formatter(FormatContext& ctx, OutputIt o)
: context(ctx), out(o) {} : context(ctx), out(o) {}
int hour() const { return to_int((s.count() / 3600) % 24); } int hour() const { return to_int((s.count() / 3600) % 24); }
@ -251,14 +245,13 @@ struct chrono_formatter {
typedef typename int_traits<int>::main_type main_type; typedef typename int_traits<int>::main_type main_type;
main_type n = to_unsigned(value); main_type n = to_unsigned(value);
int num_digits = internal::count_digits(n); int num_digits = internal::count_digits(n);
if (width > num_digits) if (width > num_digits) out = std::fill_n(out, width - num_digits, '0');
out = std::fill_n(out, width - num_digits, '0');
out = format_decimal<char_type>(out, n, num_digits); out = format_decimal<char_type>(out, n, num_digits);
} }
void format_localized(const tm &time, const char *format) { void format_localized(const tm& time, const char* format) {
auto locale = context.locale().template get<std::locale>(); auto locale = context.locale().template get<std::locale>();
auto &facet = std::use_facet<std::time_put<char_type>>(locale); auto& facet = std::use_facet<std::time_put<char_type>>(locale);
std::basic_ostringstream<char_type> os; std::basic_ostringstream<char_type> os;
os.imbue(locale); os.imbue(locale);
facet.put(os, os, ' ', &time, format, format + std::strlen(format)); facet.put(os, os, ' ', &time, format, format + std::strlen(format));
@ -266,7 +259,7 @@ struct chrono_formatter {
std::copy(str.begin(), str.end(), out); std::copy(str.begin(), str.end(), out);
} }
void on_text(const char_type *begin, const char_type *end) { void on_text(const char_type* begin, const char_type* end) {
std::copy(begin, end, out); std::copy(begin, end, out);
} }
@ -286,24 +279,21 @@ struct chrono_formatter {
void on_tz_name() {} void on_tz_name() {}
void on_24_hour(numeric_system ns) { void on_24_hour(numeric_system ns) {
if (ns == numeric_system::standard) if (ns == numeric_system::standard) return write(hour(), 2);
return write(hour(), 2);
auto time = tm(); auto time = tm();
time.tm_hour = hour(); time.tm_hour = hour();
format_localized(time, "%OH"); format_localized(time, "%OH");
} }
void on_12_hour(numeric_system ns) { void on_12_hour(numeric_system ns) {
if (ns == numeric_system::standard) if (ns == numeric_system::standard) return write(hour12(), 2);
return write(hour12(), 2);
auto time = tm(); auto time = tm();
time.tm_hour = hour(); time.tm_hour = hour();
format_localized(time, "%OI"); format_localized(time, "%OI");
} }
void on_minute(numeric_system ns) { void on_minute(numeric_system ns) {
if (ns == numeric_system::standard) if (ns == numeric_system::standard) return write(minute(), 2);
return write(minute(), 2);
auto time = tm(); auto time = tm();
time.tm_min = minute(); time.tm_min = minute();
format_localized(time, "%OM"); format_localized(time, "%OM");
@ -341,30 +331,30 @@ struct chrono_formatter {
}; };
} // namespace internal } // namespace internal
template <typename Period> FMT_CONSTEXPR const char *get_units() { template <typename Period> FMT_CONSTEXPR const char* get_units() {
return FMT_NULL; return FMT_NULL;
} }
template <> FMT_CONSTEXPR const char *get_units<std::atto>() { return "as"; } template <> FMT_CONSTEXPR const char* get_units<std::atto>() { return "as"; }
template <> FMT_CONSTEXPR const char *get_units<std::femto>() { return "fs"; } template <> FMT_CONSTEXPR const char* get_units<std::femto>() { return "fs"; }
template <> FMT_CONSTEXPR const char *get_units<std::pico>() { return "ps"; } template <> FMT_CONSTEXPR const char* get_units<std::pico>() { return "ps"; }
template <> FMT_CONSTEXPR const char *get_units<std::nano>() { return "ns"; } template <> FMT_CONSTEXPR const char* get_units<std::nano>() { return "ns"; }
template <> FMT_CONSTEXPR const char *get_units<std::micro>() { return "µs"; } template <> FMT_CONSTEXPR const char* get_units<std::micro>() { return "µs"; }
template <> FMT_CONSTEXPR const char *get_units<std::milli>() { return "ms"; } template <> FMT_CONSTEXPR const char* get_units<std::milli>() { return "ms"; }
template <> FMT_CONSTEXPR const char *get_units<std::centi>() { return "cs"; } template <> FMT_CONSTEXPR const char* get_units<std::centi>() { return "cs"; }
template <> FMT_CONSTEXPR const char *get_units<std::deci>() { return "ds"; } template <> FMT_CONSTEXPR const char* get_units<std::deci>() { return "ds"; }
template <> FMT_CONSTEXPR const char *get_units<std::ratio<1>>() { return "s"; } template <> FMT_CONSTEXPR const char* get_units<std::ratio<1>>() { return "s"; }
template <> FMT_CONSTEXPR const char *get_units<std::deca>() { return "das"; } template <> FMT_CONSTEXPR const char* get_units<std::deca>() { return "das"; }
template <> FMT_CONSTEXPR const char *get_units<std::hecto>() { return "hs"; } template <> FMT_CONSTEXPR const char* get_units<std::hecto>() { return "hs"; }
template <> FMT_CONSTEXPR const char *get_units<std::kilo>() { return "ks"; } template <> FMT_CONSTEXPR const char* get_units<std::kilo>() { return "ks"; }
template <> FMT_CONSTEXPR const char *get_units<std::mega>() { return "Ms"; } template <> FMT_CONSTEXPR const char* get_units<std::mega>() { return "Ms"; }
template <> FMT_CONSTEXPR const char *get_units<std::giga>() { return "Gs"; } template <> FMT_CONSTEXPR const char* get_units<std::giga>() { return "Gs"; }
template <> FMT_CONSTEXPR const char *get_units<std::tera>() { return "Ts"; } template <> FMT_CONSTEXPR const char* get_units<std::tera>() { return "Ts"; }
template <> FMT_CONSTEXPR const char *get_units<std::peta>() { return "Ps"; } template <> FMT_CONSTEXPR const char* get_units<std::peta>() { return "Ps"; }
template <> FMT_CONSTEXPR const char *get_units<std::exa>() { return "Es"; } template <> FMT_CONSTEXPR const char* get_units<std::exa>() { return "Es"; }
template <> FMT_CONSTEXPR const char *get_units<std::ratio<60>>() { template <> FMT_CONSTEXPR const char* get_units<std::ratio<60>>() {
return "m"; return "m";
} }
template <> FMT_CONSTEXPR const char *get_units<std::ratio<3600>>() { template <> FMT_CONSTEXPR const char* get_units<std::ratio<3600>>() {
return "h"; return "h";
} }
@ -378,12 +368,11 @@ struct formatter<std::chrono::duration<Rep, Period>, Char> {
typedef std::chrono::duration<Rep, Period> duration; typedef std::chrono::duration<Rep, Period> duration;
struct spec_handler { struct spec_handler {
formatter &f; formatter& f;
basic_parse_context<Char> &context; basic_parse_context<Char>& context;
basic_string_view<Char> format_str; basic_string_view<Char> format_str;
template <typename Id> template <typename Id> FMT_CONSTEXPR arg_ref_type make_arg_ref(Id arg_id) {
FMT_CONSTEXPR arg_ref_type make_arg_ref(Id arg_id) {
context.check_arg_id(arg_id); context.check_arg_id(arg_id);
return arg_ref_type(arg_id); return arg_ref_type(arg_id);
} }
@ -398,13 +387,12 @@ struct formatter<std::chrono::duration<Rep, Period>, Char> {
return arg_ref_type(context.next_arg_id()); return arg_ref_type(context.next_arg_id());
} }
void on_error(const char *msg) { throw format_error(msg); } void on_error(const char* msg) { throw format_error(msg); }
void on_fill(Char fill) { f.spec.fill_ = fill; } void on_fill(Char fill) { f.spec.fill_ = fill; }
void on_align(alignment align) { f.spec.align_ = align; } void on_align(alignment align) { f.spec.align_ = align; }
void on_width(unsigned width) { f.spec.width_ = width; } void on_width(unsigned width) { f.spec.width_ = width; }
template <typename Id> template <typename Id> void on_dynamic_width(Id arg_id) {
void on_dynamic_width(Id arg_id) {
f.width_ref = make_arg_ref(arg_id); f.width_ref = make_arg_ref(arg_id);
} }
}; };
@ -412,7 +400,7 @@ struct formatter<std::chrono::duration<Rep, Period>, Char> {
public: public:
formatter() : spec() {} formatter() : spec() {}
FMT_CONSTEXPR auto parse(basic_parse_context<Char> &ctx) FMT_CONSTEXPR auto parse(basic_parse_context<Char>& ctx)
-> decltype(ctx.begin()) { -> decltype(ctx.begin()) {
auto begin = ctx.begin(), end = ctx.end(); auto begin = ctx.begin(), end = ctx.end();
if (begin == end) return begin; if (begin == end) return begin;
@ -421,14 +409,13 @@ struct formatter<std::chrono::duration<Rep, Period>, Char> {
if (begin == end) return begin; if (begin == end) return begin;
begin = internal::parse_width(begin, end, handler); begin = internal::parse_width(begin, end, handler);
end = parse_chrono_format(begin, end, internal::chrono_format_checker()); end = parse_chrono_format(begin, end, internal::chrono_format_checker());
format_str = basic_string_view<Char>( format_str =
&*begin, internal::to_unsigned(end - begin)); basic_string_view<Char>(&*begin, internal::to_unsigned(end - begin));
return end; return end;
} }
template <typename FormatContext> template <typename FormatContext>
auto format(const duration &d, FormatContext &ctx) auto format(const duration& d, FormatContext& ctx) -> decltype(ctx.out()) {
-> decltype(ctx.out()) {
auto begin = format_str.begin(), end = format_str.end(); auto begin = format_str.begin(), end = format_str.end();
// As a possible future optimization, we could avoid extra copying if width // As a possible future optimization, we could avoid extra copying if width
// is not specified. // is not specified.
@ -436,7 +423,7 @@ struct formatter<std::chrono::duration<Rep, Period>, Char> {
typedef output_range<decltype(ctx.out()), Char> range; typedef output_range<decltype(ctx.out()), Char> range;
basic_writer<range> w(range(ctx.out())); basic_writer<range> w(range(ctx.out()));
if (begin == end || *begin == '}') { if (begin == end || *begin == '}') {
if (const char *unit = get_units<Period>()) if (const char* unit = get_units<Period>())
format_to(buf, "{}{}", d.count(), unit); format_to(buf, "{}{}", d.count(), unit);
else if (Period::den == 1) else if (Period::den == 1)
format_to(buf, "{}[{}]s", d.count(), Period::num); format_to(buf, "{}[{}]s", d.count(), Period::num);
@ -449,8 +436,8 @@ struct formatter<std::chrono::duration<Rep, Period>, Char> {
f.ms = std::chrono::duration_cast<std::chrono::milliseconds>(d - f.s); f.ms = std::chrono::duration_cast<std::chrono::milliseconds>(d - f.s);
parse_chrono_format(begin, end, f); parse_chrono_format(begin, end, f);
} }
internal::handle_dynamic_spec<internal::width_checker>( internal::handle_dynamic_spec<internal::width_checker>(spec.width_,
spec.width_, width_ref, ctx); width_ref, ctx);
w.write(buf.data(), buf.size(), spec); w.write(buf.data(), buf.size(), spec);
return w.out(); return w.out();
} }

View File

@ -20,12 +20,12 @@ FMT_API void vprint_colored(color c, string_view format, format_args args);
FMT_API void vprint_colored(color c, wstring_view format, wformat_args args); FMT_API void vprint_colored(color c, wstring_view format, wformat_args args);
template <typename... Args> template <typename... Args>
inline void print_colored(color c, string_view format_str, inline void print_colored(color c, string_view format_str,
const Args & ... args) { const Args&... args) {
vprint_colored(c, format_str, make_format_args(args...)); vprint_colored(c, format_str, make_format_args(args...));
} }
template <typename... Args> template <typename... Args>
inline void print_colored(color c, wstring_view format_str, inline void print_colored(color c, wstring_view format_str,
const Args & ... args) { const Args&... args) {
vprint_colored(c, format_str, make_format_args<wformat_context>(args...)); vprint_colored(c, format_str, make_format_args<wformat_context>(args...));
} }
@ -48,148 +48,148 @@ inline void vprint_colored(color c, wstring_view format, wformat_args args) {
#else #else
enum class color : uint32_t { enum class color : uint32_t {
alice_blue = 0xF0F8FF, // rgb(240,248,255) alice_blue = 0xF0F8FF, // rgb(240,248,255)
antique_white = 0xFAEBD7, // rgb(250,235,215) antique_white = 0xFAEBD7, // rgb(250,235,215)
aqua = 0x00FFFF, // rgb(0,255,255) aqua = 0x00FFFF, // rgb(0,255,255)
aquamarine = 0x7FFFD4, // rgb(127,255,212) aquamarine = 0x7FFFD4, // rgb(127,255,212)
azure = 0xF0FFFF, // rgb(240,255,255) azure = 0xF0FFFF, // rgb(240,255,255)
beige = 0xF5F5DC, // rgb(245,245,220) beige = 0xF5F5DC, // rgb(245,245,220)
bisque = 0xFFE4C4, // rgb(255,228,196) bisque = 0xFFE4C4, // rgb(255,228,196)
black = 0x000000, // rgb(0,0,0) black = 0x000000, // rgb(0,0,0)
blanched_almond = 0xFFEBCD, // rgb(255,235,205) blanched_almond = 0xFFEBCD, // rgb(255,235,205)
blue = 0x0000FF, // rgb(0,0,255) blue = 0x0000FF, // rgb(0,0,255)
blue_violet = 0x8A2BE2, // rgb(138,43,226) blue_violet = 0x8A2BE2, // rgb(138,43,226)
brown = 0xA52A2A, // rgb(165,42,42) brown = 0xA52A2A, // rgb(165,42,42)
burly_wood = 0xDEB887, // rgb(222,184,135) burly_wood = 0xDEB887, // rgb(222,184,135)
cadet_blue = 0x5F9EA0, // rgb(95,158,160) cadet_blue = 0x5F9EA0, // rgb(95,158,160)
chartreuse = 0x7FFF00, // rgb(127,255,0) chartreuse = 0x7FFF00, // rgb(127,255,0)
chocolate = 0xD2691E, // rgb(210,105,30) chocolate = 0xD2691E, // rgb(210,105,30)
coral = 0xFF7F50, // rgb(255,127,80) coral = 0xFF7F50, // rgb(255,127,80)
cornflower_blue = 0x6495ED, // rgb(100,149,237) cornflower_blue = 0x6495ED, // rgb(100,149,237)
cornsilk = 0xFFF8DC, // rgb(255,248,220) cornsilk = 0xFFF8DC, // rgb(255,248,220)
crimson = 0xDC143C, // rgb(220,20,60) crimson = 0xDC143C, // rgb(220,20,60)
cyan = 0x00FFFF, // rgb(0,255,255) cyan = 0x00FFFF, // rgb(0,255,255)
dark_blue = 0x00008B, // rgb(0,0,139) dark_blue = 0x00008B, // rgb(0,0,139)
dark_cyan = 0x008B8B, // rgb(0,139,139) dark_cyan = 0x008B8B, // rgb(0,139,139)
dark_golden_rod = 0xB8860B, // rgb(184,134,11) dark_golden_rod = 0xB8860B, // rgb(184,134,11)
dark_gray = 0xA9A9A9, // rgb(169,169,169) dark_gray = 0xA9A9A9, // rgb(169,169,169)
dark_green = 0x006400, // rgb(0,100,0) dark_green = 0x006400, // rgb(0,100,0)
dark_khaki = 0xBDB76B, // rgb(189,183,107) dark_khaki = 0xBDB76B, // rgb(189,183,107)
dark_magenta = 0x8B008B, // rgb(139,0,139) dark_magenta = 0x8B008B, // rgb(139,0,139)
dark_olive_green = 0x556B2F, // rgb(85,107,47) dark_olive_green = 0x556B2F, // rgb(85,107,47)
dark_orange = 0xFF8C00, // rgb(255,140,0) dark_orange = 0xFF8C00, // rgb(255,140,0)
dark_orchid = 0x9932CC, // rgb(153,50,204) dark_orchid = 0x9932CC, // rgb(153,50,204)
dark_red = 0x8B0000, // rgb(139,0,0) dark_red = 0x8B0000, // rgb(139,0,0)
dark_salmon = 0xE9967A, // rgb(233,150,122) dark_salmon = 0xE9967A, // rgb(233,150,122)
dark_sea_green = 0x8FBC8F, // rgb(143,188,143) dark_sea_green = 0x8FBC8F, // rgb(143,188,143)
dark_slate_blue = 0x483D8B, // rgb(72,61,139) dark_slate_blue = 0x483D8B, // rgb(72,61,139)
dark_slate_gray = 0x2F4F4F, // rgb(47,79,79) dark_slate_gray = 0x2F4F4F, // rgb(47,79,79)
dark_turquoise = 0x00CED1, // rgb(0,206,209) dark_turquoise = 0x00CED1, // rgb(0,206,209)
dark_violet = 0x9400D3, // rgb(148,0,211) dark_violet = 0x9400D3, // rgb(148,0,211)
deep_pink = 0xFF1493, // rgb(255,20,147) deep_pink = 0xFF1493, // rgb(255,20,147)
deep_sky_blue = 0x00BFFF, // rgb(0,191,255) deep_sky_blue = 0x00BFFF, // rgb(0,191,255)
dim_gray = 0x696969, // rgb(105,105,105) dim_gray = 0x696969, // rgb(105,105,105)
dodger_blue = 0x1E90FF, // rgb(30,144,255) dodger_blue = 0x1E90FF, // rgb(30,144,255)
fire_brick = 0xB22222, // rgb(178,34,34) fire_brick = 0xB22222, // rgb(178,34,34)
floral_white = 0xFFFAF0, // rgb(255,250,240) floral_white = 0xFFFAF0, // rgb(255,250,240)
forest_green = 0x228B22, // rgb(34,139,34) forest_green = 0x228B22, // rgb(34,139,34)
fuchsia = 0xFF00FF, // rgb(255,0,255) fuchsia = 0xFF00FF, // rgb(255,0,255)
gainsboro = 0xDCDCDC, // rgb(220,220,220) gainsboro = 0xDCDCDC, // rgb(220,220,220)
ghost_white = 0xF8F8FF, // rgb(248,248,255) ghost_white = 0xF8F8FF, // rgb(248,248,255)
gold = 0xFFD700, // rgb(255,215,0) gold = 0xFFD700, // rgb(255,215,0)
golden_rod = 0xDAA520, // rgb(218,165,32) golden_rod = 0xDAA520, // rgb(218,165,32)
gray = 0x808080, // rgb(128,128,128) gray = 0x808080, // rgb(128,128,128)
green = 0x008000, // rgb(0,128,0) green = 0x008000, // rgb(0,128,0)
green_yellow = 0xADFF2F, // rgb(173,255,47) green_yellow = 0xADFF2F, // rgb(173,255,47)
honey_dew = 0xF0FFF0, // rgb(240,255,240) honey_dew = 0xF0FFF0, // rgb(240,255,240)
hot_pink = 0xFF69B4, // rgb(255,105,180) hot_pink = 0xFF69B4, // rgb(255,105,180)
indian_red = 0xCD5C5C, // rgb(205,92,92) indian_red = 0xCD5C5C, // rgb(205,92,92)
indigo = 0x4B0082, // rgb(75,0,130) indigo = 0x4B0082, // rgb(75,0,130)
ivory = 0xFFFFF0, // rgb(255,255,240) ivory = 0xFFFFF0, // rgb(255,255,240)
khaki = 0xF0E68C, // rgb(240,230,140) khaki = 0xF0E68C, // rgb(240,230,140)
lavender = 0xE6E6FA, // rgb(230,230,250) lavender = 0xE6E6FA, // rgb(230,230,250)
lavender_blush = 0xFFF0F5, // rgb(255,240,245) lavender_blush = 0xFFF0F5, // rgb(255,240,245)
lawn_green = 0x7CFC00, // rgb(124,252,0) lawn_green = 0x7CFC00, // rgb(124,252,0)
lemon_chiffon = 0xFFFACD, // rgb(255,250,205) lemon_chiffon = 0xFFFACD, // rgb(255,250,205)
light_blue = 0xADD8E6, // rgb(173,216,230) light_blue = 0xADD8E6, // rgb(173,216,230)
light_coral = 0xF08080, // rgb(240,128,128) light_coral = 0xF08080, // rgb(240,128,128)
light_cyan = 0xE0FFFF, // rgb(224,255,255) light_cyan = 0xE0FFFF, // rgb(224,255,255)
light_golden_rod_yellow = 0xFAFAD2, // rgb(250,250,210) light_golden_rod_yellow = 0xFAFAD2, // rgb(250,250,210)
light_gray = 0xD3D3D3, // rgb(211,211,211) light_gray = 0xD3D3D3, // rgb(211,211,211)
light_green = 0x90EE90, // rgb(144,238,144) light_green = 0x90EE90, // rgb(144,238,144)
light_pink = 0xFFB6C1, // rgb(255,182,193) light_pink = 0xFFB6C1, // rgb(255,182,193)
light_salmon = 0xFFA07A, // rgb(255,160,122) light_salmon = 0xFFA07A, // rgb(255,160,122)
light_sea_green = 0x20B2AA, // rgb(32,178,170) light_sea_green = 0x20B2AA, // rgb(32,178,170)
light_sky_blue = 0x87CEFA, // rgb(135,206,250) light_sky_blue = 0x87CEFA, // rgb(135,206,250)
light_slate_gray = 0x778899, // rgb(119,136,153) light_slate_gray = 0x778899, // rgb(119,136,153)
light_steel_blue = 0xB0C4DE, // rgb(176,196,222) light_steel_blue = 0xB0C4DE, // rgb(176,196,222)
light_yellow = 0xFFFFE0, // rgb(255,255,224) light_yellow = 0xFFFFE0, // rgb(255,255,224)
lime = 0x00FF00, // rgb(0,255,0) lime = 0x00FF00, // rgb(0,255,0)
lime_green = 0x32CD32, // rgb(50,205,50) lime_green = 0x32CD32, // rgb(50,205,50)
linen = 0xFAF0E6, // rgb(250,240,230) linen = 0xFAF0E6, // rgb(250,240,230)
magenta = 0xFF00FF, // rgb(255,0,255) magenta = 0xFF00FF, // rgb(255,0,255)
maroon = 0x800000, // rgb(128,0,0) maroon = 0x800000, // rgb(128,0,0)
medium_aquamarine = 0x66CDAA, // rgb(102,205,170) medium_aquamarine = 0x66CDAA, // rgb(102,205,170)
medium_blue = 0x0000CD, // rgb(0,0,205) medium_blue = 0x0000CD, // rgb(0,0,205)
medium_orchid = 0xBA55D3, // rgb(186,85,211) medium_orchid = 0xBA55D3, // rgb(186,85,211)
medium_purple = 0x9370DB, // rgb(147,112,219) medium_purple = 0x9370DB, // rgb(147,112,219)
medium_sea_green = 0x3CB371, // rgb(60,179,113) medium_sea_green = 0x3CB371, // rgb(60,179,113)
medium_slate_blue = 0x7B68EE, // rgb(123,104,238) medium_slate_blue = 0x7B68EE, // rgb(123,104,238)
medium_spring_green = 0x00FA9A, // rgb(0,250,154) medium_spring_green = 0x00FA9A, // rgb(0,250,154)
medium_turquoise = 0x48D1CC, // rgb(72,209,204) medium_turquoise = 0x48D1CC, // rgb(72,209,204)
medium_violet_red = 0xC71585, // rgb(199,21,133) medium_violet_red = 0xC71585, // rgb(199,21,133)
midnight_blue = 0x191970, // rgb(25,25,112) midnight_blue = 0x191970, // rgb(25,25,112)
mint_cream = 0xF5FFFA, // rgb(245,255,250) mint_cream = 0xF5FFFA, // rgb(245,255,250)
misty_rose = 0xFFE4E1, // rgb(255,228,225) misty_rose = 0xFFE4E1, // rgb(255,228,225)
moccasin = 0xFFE4B5, // rgb(255,228,181) moccasin = 0xFFE4B5, // rgb(255,228,181)
navajo_white = 0xFFDEAD, // rgb(255,222,173) navajo_white = 0xFFDEAD, // rgb(255,222,173)
navy = 0x000080, // rgb(0,0,128) navy = 0x000080, // rgb(0,0,128)
old_lace = 0xFDF5E6, // rgb(253,245,230) old_lace = 0xFDF5E6, // rgb(253,245,230)
olive = 0x808000, // rgb(128,128,0) olive = 0x808000, // rgb(128,128,0)
olive_drab = 0x6B8E23, // rgb(107,142,35) olive_drab = 0x6B8E23, // rgb(107,142,35)
orange = 0xFFA500, // rgb(255,165,0) orange = 0xFFA500, // rgb(255,165,0)
orange_red = 0xFF4500, // rgb(255,69,0) orange_red = 0xFF4500, // rgb(255,69,0)
orchid = 0xDA70D6, // rgb(218,112,214) orchid = 0xDA70D6, // rgb(218,112,214)
pale_golden_rod = 0xEEE8AA, // rgb(238,232,170) pale_golden_rod = 0xEEE8AA, // rgb(238,232,170)
pale_green = 0x98FB98, // rgb(152,251,152) pale_green = 0x98FB98, // rgb(152,251,152)
pale_turquoise = 0xAFEEEE, // rgb(175,238,238) pale_turquoise = 0xAFEEEE, // rgb(175,238,238)
pale_violet_red = 0xDB7093, // rgb(219,112,147) pale_violet_red = 0xDB7093, // rgb(219,112,147)
papaya_whip = 0xFFEFD5, // rgb(255,239,213) papaya_whip = 0xFFEFD5, // rgb(255,239,213)
peach_puff = 0xFFDAB9, // rgb(255,218,185) peach_puff = 0xFFDAB9, // rgb(255,218,185)
peru = 0xCD853F, // rgb(205,133,63) peru = 0xCD853F, // rgb(205,133,63)
pink = 0xFFC0CB, // rgb(255,192,203) pink = 0xFFC0CB, // rgb(255,192,203)
plum = 0xDDA0DD, // rgb(221,160,221) plum = 0xDDA0DD, // rgb(221,160,221)
powder_blue = 0xB0E0E6, // rgb(176,224,230) powder_blue = 0xB0E0E6, // rgb(176,224,230)
purple = 0x800080, // rgb(128,0,128) purple = 0x800080, // rgb(128,0,128)
rebecca_purple = 0x663399, // rgb(102,51,153) rebecca_purple = 0x663399, // rgb(102,51,153)
red = 0xFF0000, // rgb(255,0,0) red = 0xFF0000, // rgb(255,0,0)
rosy_brown = 0xBC8F8F, // rgb(188,143,143) rosy_brown = 0xBC8F8F, // rgb(188,143,143)
royal_blue = 0x4169E1, // rgb(65,105,225) royal_blue = 0x4169E1, // rgb(65,105,225)
saddle_brown = 0x8B4513, // rgb(139,69,19) saddle_brown = 0x8B4513, // rgb(139,69,19)
salmon = 0xFA8072, // rgb(250,128,114) salmon = 0xFA8072, // rgb(250,128,114)
sandy_brown = 0xF4A460, // rgb(244,164,96) sandy_brown = 0xF4A460, // rgb(244,164,96)
sea_green = 0x2E8B57, // rgb(46,139,87) sea_green = 0x2E8B57, // rgb(46,139,87)
sea_shell = 0xFFF5EE, // rgb(255,245,238) sea_shell = 0xFFF5EE, // rgb(255,245,238)
sienna = 0xA0522D, // rgb(160,82,45) sienna = 0xA0522D, // rgb(160,82,45)
silver = 0xC0C0C0, // rgb(192,192,192) silver = 0xC0C0C0, // rgb(192,192,192)
sky_blue = 0x87CEEB, // rgb(135,206,235) sky_blue = 0x87CEEB, // rgb(135,206,235)
slate_blue = 0x6A5ACD, // rgb(106,90,205) slate_blue = 0x6A5ACD, // rgb(106,90,205)
slate_gray = 0x708090, // rgb(112,128,144) slate_gray = 0x708090, // rgb(112,128,144)
snow = 0xFFFAFA, // rgb(255,250,250) snow = 0xFFFAFA, // rgb(255,250,250)
spring_green = 0x00FF7F, // rgb(0,255,127) spring_green = 0x00FF7F, // rgb(0,255,127)
steel_blue = 0x4682B4, // rgb(70,130,180) steel_blue = 0x4682B4, // rgb(70,130,180)
tan = 0xD2B48C, // rgb(210,180,140) tan = 0xD2B48C, // rgb(210,180,140)
teal = 0x008080, // rgb(0,128,128) teal = 0x008080, // rgb(0,128,128)
thistle = 0xD8BFD8, // rgb(216,191,216) thistle = 0xD8BFD8, // rgb(216,191,216)
tomato = 0xFF6347, // rgb(255,99,71) tomato = 0xFF6347, // rgb(255,99,71)
turquoise = 0x40E0D0, // rgb(64,224,208) turquoise = 0x40E0D0, // rgb(64,224,208)
violet = 0xEE82EE, // rgb(238,130,238) violet = 0xEE82EE, // rgb(238,130,238)
wheat = 0xF5DEB3, // rgb(245,222,179) wheat = 0xF5DEB3, // rgb(245,222,179)
white = 0xFFFFFF, // rgb(255,255,255) white = 0xFFFFFF, // rgb(255,255,255)
white_smoke = 0xF5F5F5, // rgb(245,245,245) white_smoke = 0xF5F5F5, // rgb(245,245,245)
yellow = 0xFFFF00, // rgb(255,255,0) yellow = 0xFFFF00, // rgb(255,255,0)
yellow_green = 0x9ACD32 // rgb(154,205,50) yellow_green = 0x9ACD32 // rgb(154,205,50)
}; // enum class color }; // enum class color
enum class terminal_color : uint8_t { enum class terminal_color : uint8_t {
black = 30, black = 30,
@ -226,7 +226,8 @@ struct rgb {
FMT_CONSTEXPR rgb(uint32_t hex) FMT_CONSTEXPR rgb(uint32_t hex)
: r((hex >> 16) & 0xFF), g((hex >> 8) & 0xFF), b(hex & 0xFF) {} : r((hex >> 16) & 0xFF), g((hex >> 8) & 0xFF), b(hex & 0xFF) {}
FMT_CONSTEXPR rgb(color hex) FMT_CONSTEXPR rgb(color hex)
: r((uint32_t(hex) >> 16) & 0xFF), g((uint32_t(hex) >> 8) & 0xFF), : r((uint32_t(hex) >> 16) & 0xFF),
g((uint32_t(hex) >> 8) & 0xFF),
b(uint32_t(hex) & 0xFF) {} b(uint32_t(hex) & 0xFF) {}
uint8_t r; uint8_t r;
uint8_t g; uint8_t g;
@ -237,19 +238,17 @@ namespace internal {
// color is a struct of either a rgb color or a terminal color. // color is a struct of either a rgb color or a terminal color.
struct color_type { struct color_type {
FMT_CONSTEXPR color_type() FMT_NOEXCEPT FMT_CONSTEXPR color_type() FMT_NOEXCEPT : is_rgb(), value{} {}
: is_rgb(), value{} {} FMT_CONSTEXPR color_type(color rgb_color) FMT_NOEXCEPT : is_rgb(true),
FMT_CONSTEXPR color_type(color rgb_color) FMT_NOEXCEPT value{} {
: is_rgb(true), value{} {
value.rgb_color = static_cast<uint32_t>(rgb_color); value.rgb_color = static_cast<uint32_t>(rgb_color);
} }
FMT_CONSTEXPR color_type(rgb rgb_color) FMT_NOEXCEPT FMT_CONSTEXPR color_type(rgb rgb_color) FMT_NOEXCEPT : is_rgb(true), value{} {
: is_rgb(true), value{} { value.rgb_color = (static_cast<uint32_t>(rgb_color.r) << 16) |
value.rgb_color = (static_cast<uint32_t>(rgb_color.r) << 16) (static_cast<uint32_t>(rgb_color.g) << 8) | rgb_color.b;
| (static_cast<uint32_t>(rgb_color.g) << 8) | rgb_color.b;
} }
FMT_CONSTEXPR color_type(terminal_color term_color) FMT_NOEXCEPT FMT_CONSTEXPR color_type(terminal_color term_color) FMT_NOEXCEPT : is_rgb(),
: is_rgb(), value{} { value{} {
value.term_color = static_cast<uint8_t>(term_color); value.term_color = static_cast<uint8_t>(term_color);
} }
bool is_rgb; bool is_rgb;
@ -258,15 +257,17 @@ struct color_type {
uint32_t rgb_color; uint32_t rgb_color;
} value; } value;
}; };
} // namespace internal } // namespace internal
// Experimental text formatting support. // Experimental text formatting support.
class text_style { class text_style {
public: public:
FMT_CONSTEXPR text_style(emphasis em = emphasis()) FMT_NOEXCEPT FMT_CONSTEXPR text_style(emphasis em = emphasis()) FMT_NOEXCEPT
: set_foreground_color(), set_background_color(), ems(em) {} : set_foreground_color(),
set_background_color(),
ems(em) {}
FMT_CONSTEXPR text_style &operator|=(const text_style &rhs) { FMT_CONSTEXPR text_style& operator|=(const text_style& rhs) {
if (!set_foreground_color) { if (!set_foreground_color) {
set_foreground_color = rhs.set_foreground_color; set_foreground_color = rhs.set_foreground_color;
foreground_color = rhs.foreground_color; foreground_color = rhs.foreground_color;
@ -290,12 +291,12 @@ class text_style {
return *this; return *this;
} }
friend FMT_CONSTEXPR friend FMT_CONSTEXPR text_style operator|(text_style lhs,
text_style operator|(text_style lhs, const text_style &rhs) { const text_style& rhs) {
return lhs |= rhs; return lhs |= rhs;
} }
FMT_CONSTEXPR text_style &operator&=(const text_style &rhs) { FMT_CONSTEXPR text_style& operator&=(const text_style& rhs) {
if (!set_foreground_color) { if (!set_foreground_color) {
set_foreground_color = rhs.set_foreground_color; set_foreground_color = rhs.set_foreground_color;
foreground_color = rhs.foreground_color; foreground_color = rhs.foreground_color;
@ -319,8 +320,8 @@ class text_style {
return *this; return *this;
} }
friend FMT_CONSTEXPR friend FMT_CONSTEXPR text_style operator&(text_style lhs,
text_style operator&(text_style lhs, const text_style &rhs) { const text_style& rhs) {
return lhs &= rhs; return lhs &= rhs;
} }
@ -346,20 +347,20 @@ class text_style {
return ems; return ems;
} }
private: private:
FMT_CONSTEXPR text_style(bool is_foreground, FMT_CONSTEXPR text_style(bool is_foreground,
internal::color_type text_color) FMT_NOEXCEPT internal::color_type text_color) FMT_NOEXCEPT
: set_foreground_color(), : set_foreground_color(),
set_background_color(), set_background_color(),
ems() { ems() {
if (is_foreground) { if (is_foreground) {
foreground_color = text_color; foreground_color = text_color;
set_foreground_color = true; set_foreground_color = true;
} else { } else {
background_color = text_color; background_color = text_color;
set_background_color = true; set_background_color = true;
} }
} }
friend FMT_CONSTEXPR_DECL text_style fg(internal::color_type foreground) friend FMT_CONSTEXPR_DECL text_style fg(internal::color_type foreground)
FMT_NOEXCEPT; FMT_NOEXCEPT;
@ -387,10 +388,9 @@ FMT_CONSTEXPR text_style operator|(emphasis lhs, emphasis rhs) FMT_NOEXCEPT {
namespace internal { namespace internal {
template <typename Char> template <typename Char> struct ansi_color_escape {
struct ansi_color_escape {
FMT_CONSTEXPR ansi_color_escape(internal::color_type text_color, FMT_CONSTEXPR ansi_color_escape(internal::color_type text_color,
const char * esc) FMT_NOEXCEPT { const char* esc) FMT_NOEXCEPT {
// If we have a terminal color, we need to output another escape code // If we have a terminal color, we need to output another escape code
// sequence. // sequence.
if (!text_color.is_rgb) { if (!text_color.is_rgb) {
@ -398,8 +398,7 @@ struct ansi_color_escape {
uint32_t value = text_color.value.term_color; uint32_t value = text_color.value.term_color;
// Background ASCII codes are the same as the foreground ones but with // Background ASCII codes are the same as the foreground ones but with
// 10 more. // 10 more.
if (is_background) if (is_background) value += 10u;
value += 10u;
std::size_t index = 0; std::size_t index = 0;
buffer[index++] = static_cast<Char>('\x1b'); buffer[index++] = static_cast<Char>('\x1b');
@ -421,7 +420,7 @@ struct ansi_color_escape {
buffer[i] = static_cast<Char>(esc[i]); buffer[i] = static_cast<Char>(esc[i]);
} }
rgb color(text_color.value.rgb_color); rgb color(text_color.value.rgb_color);
to_esc(color.r, buffer + 7, ';'); to_esc(color.r, buffer + 7, ';');
to_esc(color.g, buffer + 11, ';'); to_esc(color.g, buffer + 11, ';');
to_esc(color.b, buffer + 15, 'm'); to_esc(color.b, buffer + 15, 'm');
buffer[19] = static_cast<Char>(0); buffer[19] = static_cast<Char>(0);
@ -429,19 +428,15 @@ struct ansi_color_escape {
FMT_CONSTEXPR ansi_color_escape(emphasis em) FMT_NOEXCEPT { FMT_CONSTEXPR ansi_color_escape(emphasis em) FMT_NOEXCEPT {
uint8_t em_codes[4] = {}; uint8_t em_codes[4] = {};
uint8_t em_bits = static_cast<uint8_t>(em); uint8_t em_bits = static_cast<uint8_t>(em);
if (em_bits & static_cast<uint8_t>(emphasis::bold)) if (em_bits & static_cast<uint8_t>(emphasis::bold)) em_codes[0] = 1;
em_codes[0] = 1; if (em_bits & static_cast<uint8_t>(emphasis::italic)) em_codes[1] = 3;
if (em_bits & static_cast<uint8_t>(emphasis::italic)) if (em_bits & static_cast<uint8_t>(emphasis::underline)) em_codes[2] = 4;
em_codes[1] = 3;
if (em_bits & static_cast<uint8_t>(emphasis::underline))
em_codes[2] = 4;
if (em_bits & static_cast<uint8_t>(emphasis::strikethrough)) if (em_bits & static_cast<uint8_t>(emphasis::strikethrough))
em_codes[3] = 9; em_codes[3] = 9;
std::size_t index = 0; std::size_t index = 0;
for (int i = 0; i < 4; ++i) { for (int i = 0; i < 4; ++i) {
if (!em_codes[i]) if (!em_codes[i]) continue;
continue;
buffer[index++] = static_cast<Char>('\x1b'); buffer[index++] = static_cast<Char>('\x1b');
buffer[index++] = static_cast<Char>('['); buffer[index++] = static_cast<Char>('[');
buffer[index++] = static_cast<Char>('0' + em_codes[i]); buffer[index++] = static_cast<Char>('0' + em_codes[i]);
@ -449,19 +444,17 @@ struct ansi_color_escape {
} }
buffer[index++] = static_cast<Char>(0); buffer[index++] = static_cast<Char>(0);
} }
FMT_CONSTEXPR operator const Char *() const FMT_NOEXCEPT { return buffer; } FMT_CONSTEXPR operator const Char*() const FMT_NOEXCEPT { return buffer; }
FMT_CONSTEXPR const Char * begin() const FMT_NOEXCEPT { FMT_CONSTEXPR const Char* begin() const FMT_NOEXCEPT { return buffer; }
return buffer; FMT_CONSTEXPR const Char* end() const FMT_NOEXCEPT {
}
FMT_CONSTEXPR const Char * end() const FMT_NOEXCEPT {
return buffer + std::strlen(buffer); return buffer + std::strlen(buffer);
} }
private: private:
Char buffer[7u + 3u * 4u + 1u]; Char buffer[7u + 3u * 4u + 1u];
static FMT_CONSTEXPR void to_esc(uint8_t c, Char *out, static FMT_CONSTEXPR void to_esc(uint8_t c, Char* out,
char delimiter) FMT_NOEXCEPT { char delimiter) FMT_NOEXCEPT {
out[0] = static_cast<Char>('0' + c / 100); out[0] = static_cast<Char>('0' + c / 100);
out[1] = static_cast<Char>('0' + c / 10 % 10); out[1] = static_cast<Char>('0' + c / 10 % 10);
@ -471,40 +464,37 @@ private:
}; };
template <typename Char> template <typename Char>
FMT_CONSTEXPR ansi_color_escape<Char> FMT_CONSTEXPR ansi_color_escape<Char> make_foreground_color(
make_foreground_color(internal::color_type foreground) FMT_NOEXCEPT { internal::color_type foreground) FMT_NOEXCEPT {
return ansi_color_escape<Char>(foreground, internal::data::FOREGROUND_COLOR); return ansi_color_escape<Char>(foreground, internal::data::FOREGROUND_COLOR);
} }
template <typename Char> template <typename Char>
FMT_CONSTEXPR ansi_color_escape<Char> FMT_CONSTEXPR ansi_color_escape<Char> make_background_color(
make_background_color(internal::color_type background) FMT_NOEXCEPT { internal::color_type background) FMT_NOEXCEPT {
return ansi_color_escape<Char>(background, internal::data::BACKGROUND_COLOR); return ansi_color_escape<Char>(background, internal::data::BACKGROUND_COLOR);
} }
template <typename Char> template <typename Char>
FMT_CONSTEXPR ansi_color_escape<Char> FMT_CONSTEXPR ansi_color_escape<Char> make_emphasis(emphasis em) FMT_NOEXCEPT {
make_emphasis(emphasis em) FMT_NOEXCEPT {
return ansi_color_escape<Char>(em); return ansi_color_escape<Char>(em);
} }
template <typename Char> template <typename Char>
inline void fputs(const Char *chars, FILE *stream) FMT_NOEXCEPT { inline void fputs(const Char* chars, FILE* stream) FMT_NOEXCEPT {
std::fputs(chars, stream); std::fputs(chars, stream);
} }
template <> template <>
inline void fputs<wchar_t>(const wchar_t *chars, FILE *stream) FMT_NOEXCEPT { inline void fputs<wchar_t>(const wchar_t* chars, FILE* stream) FMT_NOEXCEPT {
std::fputws(chars, stream); std::fputws(chars, stream);
} }
template <typename Char> template <typename Char> inline void reset_color(FILE* stream) FMT_NOEXCEPT {
inline void reset_color(FILE *stream) FMT_NOEXCEPT {
fputs(internal::data::RESET_COLOR, stream); fputs(internal::data::RESET_COLOR, stream);
} }
template <> template <> inline void reset_color<wchar_t>(FILE* stream) FMT_NOEXCEPT {
inline void reset_color<wchar_t>(FILE *stream) FMT_NOEXCEPT {
fputs(internal::data::WRESET_COLOR, stream); fputs(internal::data::WRESET_COLOR, stream);
} }
@ -519,27 +509,24 @@ inline void reset_color(basic_memory_buffer<Char>& buffer) FMT_NOEXCEPT {
// which is needed because or else // which is needed because or else
// fmt::print(stderr, fmt::emphasis::bold, ""); // fmt::print(stderr, fmt::emphasis::bold, "");
// would take stderr (a std::FILE *) as the format string. // would take stderr (a std::FILE *) as the format string.
template <> template <> struct is_string<std::FILE*> : std::false_type {};
struct is_string<std::FILE *> : std::false_type {}; template <> struct is_string<const std::FILE*> : std::false_type {};
template <>
struct is_string<const std::FILE *> : std::false_type {};
template <typename Char> template <typename Char>
std::basic_string<Char> vformat( std::basic_string<Char> vformat(
const text_style &ts, basic_string_view<Char> format_str, const text_style& ts, basic_string_view<Char> format_str,
basic_format_args<typename buffer_context<Char>::type> args) { basic_format_args<typename buffer_context<Char>::type> args) {
basic_memory_buffer<Char> buffer; basic_memory_buffer<Char> buffer;
bool has_style = false; bool has_style = false;
if (ts.has_emphasis()) { if (ts.has_emphasis()) {
has_style = true; has_style = true;
ansi_color_escape<Char> escape = ansi_color_escape<Char> escape = make_emphasis<Char>(ts.get_emphasis());
make_emphasis<Char>(ts.get_emphasis());
buffer.append(escape.begin(), escape.end()); buffer.append(escape.begin(), escape.end());
} }
if (ts.has_foreground()) { if (ts.has_foreground()) {
has_style = true; has_style = true;
ansi_color_escape<Char> escape = ansi_color_escape<Char> escape =
make_foreground_color<Char>(ts.get_foreground()); make_foreground_color<Char>(ts.get_foreground());
buffer.append(escape.begin(), escape.end()); buffer.append(escape.begin(), escape.end());
} }
if (ts.has_background()) { if (ts.has_background()) {
@ -554,22 +541,20 @@ std::basic_string<Char> vformat(
} }
return fmt::to_string(buffer); return fmt::to_string(buffer);
} }
} // namespace internal } // namespace internal
template < template <typename S, typename Char = typename internal::char_t<S>::type>
typename S, typename Char = typename internal::char_t<S>::type> void vprint(std::FILE* f, const text_style& ts, const S& format,
void vprint(std::FILE *f, const text_style &ts, const S &format,
basic_format_args<typename buffer_context<Char>::type> args) { basic_format_args<typename buffer_context<Char>::type> args) {
bool has_style = false; bool has_style = false;
if (ts.has_emphasis()) { if (ts.has_emphasis()) {
has_style = true; has_style = true;
internal::fputs<Char>( internal::fputs<Char>(internal::make_emphasis<Char>(ts.get_emphasis()), f);
internal::make_emphasis<Char>(ts.get_emphasis()), f);
} }
if (ts.has_foreground()) { if (ts.has_foreground()) {
has_style = true; has_style = true;
internal::fputs<Char>( internal::fputs<Char>(
internal::make_foreground_color<Char>(ts.get_foreground()), f); internal::make_foreground_color<Char>(ts.get_foreground()), f);
} }
if (ts.has_background()) { if (ts.has_background()) {
has_style = true; has_style = true;
@ -591,8 +576,8 @@ void vprint(std::FILE *f, const text_style &ts, const S &format,
*/ */
template <typename String, typename... Args> template <typename String, typename... Args>
typename std::enable_if<internal::is_string<String>::value>::type print( typename std::enable_if<internal::is_string<String>::value>::type print(
std::FILE *f, const text_style &ts, const String &format_str, std::FILE* f, const text_style& ts, const String& format_str,
const Args &... args) { const Args&... args) {
internal::check_format_string<Args...>(format_str); internal::check_format_string<Args...>(format_str);
typedef typename internal::char_t<String>::type char_t; typedef typename internal::char_t<String>::type char_t;
typedef typename buffer_context<char_t>::type context_t; typedef typename buffer_context<char_t>::type context_t;
@ -609,16 +594,13 @@ typename std::enable_if<internal::is_string<String>::value>::type print(
*/ */
template <typename String, typename... Args> template <typename String, typename... Args>
typename std::enable_if<internal::is_string<String>::value>::type print( typename std::enable_if<internal::is_string<String>::value>::type print(
const text_style &ts, const String &format_str, const text_style& ts, const String& format_str, const Args&... args) {
const Args &... args) {
return print(stdout, ts, format_str, args...); return print(stdout, ts, format_str, args...);
} }
template <typename S, typename Char = FMT_CHAR(S)> template <typename S, typename Char = FMT_CHAR(S)>
inline std::basic_string<Char> vformat( inline std::basic_string<Char> vformat(
const text_style &ts, const text_style& ts, const S& format_str,
const S &format_str,
basic_format_args<typename buffer_context<Char>::type> args) { basic_format_args<typename buffer_context<Char>::type> args) {
return internal::vformat(ts, to_string_view(format_str), args); return internal::vformat(ts, to_string_view(format_str), args);
} }
@ -636,11 +618,12 @@ inline std::basic_string<Char> vformat(
\endrst \endrst
*/ */
template <typename S, typename... Args> template <typename S, typename... Args>
inline std::basic_string<FMT_CHAR(S)> format( inline std::basic_string<FMT_CHAR(S)> format(const text_style& ts,
const text_style &ts, const S &format_str, const Args &... args) { const S& format_str,
const Args&... args) {
return internal::vformat( return internal::vformat(
ts, to_string_view(format_str), ts, to_string_view(format_str),
*internal::checked_args<S, Args...>(format_str, args...)); *internal::checked_args<S, Args...>(format_str, args...));
} }
#endif #endif

File diff suppressed because it is too large Load Diff

View File

@ -20,45 +20,45 @@
#include <cstddef> // for std::ptrdiff_t #include <cstddef> // for std::ptrdiff_t
#include <cstring> // for std::memmove #include <cstring> // for std::memmove
#if !defined(FMT_STATIC_THOUSANDS_SEPARATOR) #if !defined(FMT_STATIC_THOUSANDS_SEPARATOR)
# include <locale> # include <locale>
#endif #endif
#if FMT_USE_WINDOWS_H #if FMT_USE_WINDOWS_H
# if !defined(FMT_HEADER_ONLY) && !defined(WIN32_LEAN_AND_MEAN) # if !defined(FMT_HEADER_ONLY) && !defined(WIN32_LEAN_AND_MEAN)
# define WIN32_LEAN_AND_MEAN # define WIN32_LEAN_AND_MEAN
# endif # endif
# if defined(NOMINMAX) || defined(FMT_WIN_MINMAX) # if defined(NOMINMAX) || defined(FMT_WIN_MINMAX)
# include <windows.h> # include <windows.h>
# else # else
# define NOMINMAX # define NOMINMAX
# include <windows.h> # include <windows.h>
# undef NOMINMAX # undef NOMINMAX
# endif # endif
#endif #endif
#if FMT_EXCEPTIONS #if FMT_EXCEPTIONS
# define FMT_TRY try # define FMT_TRY try
# define FMT_CATCH(x) catch (x) # define FMT_CATCH(x) catch (x)
#else #else
# define FMT_TRY if (true) # define FMT_TRY if (true)
# define FMT_CATCH(x) if (false) # define FMT_CATCH(x) if (false)
#endif #endif
#ifdef _MSC_VER #ifdef _MSC_VER
# pragma warning(push) # pragma warning(push)
# pragma warning(disable: 4127) // conditional expression is constant # pragma warning(disable : 4127) // conditional expression is constant
# pragma warning(disable: 4702) // unreachable code # pragma warning(disable : 4702) // unreachable code
// Disable deprecation warning for strerror. The latter is not called but // Disable deprecation warning for strerror. The latter is not called but
// MSVC fails to detect it. // MSVC fails to detect it.
# pragma warning(disable: 4996) # pragma warning(disable : 4996)
#endif #endif
// Dummy implementations of strerror_r and strerror_s called if corresponding // Dummy implementations of strerror_r and strerror_s called if corresponding
// system functions are not available. // system functions are not available.
inline fmt::internal::null<> strerror_r(int, char *, ...) { inline fmt::internal::null<> strerror_r(int, char*, ...) {
return fmt::internal::null<>(); return fmt::internal::null<>();
} }
inline fmt::internal::null<> strerror_s(char *, std::size_t, ...) { inline fmt::internal::null<> strerror_s(char*, std::size_t, ...) {
return fmt::internal::null<>(); return fmt::internal::null<>();
} }
@ -67,25 +67,25 @@ FMT_BEGIN_NAMESPACE
namespace { namespace {
#ifndef _MSC_VER #ifndef _MSC_VER
# define FMT_SNPRINTF snprintf # define FMT_SNPRINTF snprintf
#else // _MSC_VER #else // _MSC_VER
inline int fmt_snprintf(char *buffer, size_t size, const char *format, ...) { inline int fmt_snprintf(char* buffer, size_t size, const char* format, ...) {
va_list args; va_list args;
va_start(args, format); va_start(args, format);
int result = vsnprintf_s(buffer, size, _TRUNCATE, format, args); int result = vsnprintf_s(buffer, size, _TRUNCATE, format, args);
va_end(args); va_end(args);
return result; return result;
} }
# define FMT_SNPRINTF fmt_snprintf # define FMT_SNPRINTF fmt_snprintf
#endif // _MSC_VER #endif // _MSC_VER
#if defined(_WIN32) && defined(__MINGW32__) && !defined(__NO_ISOCEXT) #if defined(_WIN32) && defined(__MINGW32__) && !defined(__NO_ISOCEXT)
# define FMT_SWPRINTF snwprintf # define FMT_SWPRINTF snwprintf
#else #else
# define FMT_SWPRINTF swprintf # define FMT_SWPRINTF swprintf
#endif // defined(_WIN32) && defined(__MINGW32__) && !defined(__NO_ISOCEXT) #endif // defined(_WIN32) && defined(__MINGW32__) && !defined(__NO_ISOCEXT)
typedef void (*FormatFunc)(internal::buffer &, int, string_view); typedef void (*FormatFunc)(internal::buffer&, int, string_view);
// Portable thread-safe version of strerror. // Portable thread-safe version of strerror.
// Sets buffer to point to a string describing the error code. // Sets buffer to point to a string describing the error code.
@ -96,18 +96,18 @@ typedef void (*FormatFunc)(internal::buffer &, int, string_view);
// ERANGE - buffer is not large enough to store the error message // ERANGE - buffer is not large enough to store the error message
// other - failure // other - failure
// Buffer should be at least of size 1. // Buffer should be at least of size 1.
int safe_strerror( int safe_strerror(int error_code, char*& buffer,
int error_code, char *&buffer, std::size_t buffer_size) FMT_NOEXCEPT { std::size_t buffer_size) FMT_NOEXCEPT {
FMT_ASSERT(buffer != FMT_NULL && buffer_size != 0, "invalid buffer"); FMT_ASSERT(buffer != FMT_NULL && buffer_size != 0, "invalid buffer");
class dispatcher { class dispatcher {
private: private:
int error_code_; int error_code_;
char *&buffer_; char*& buffer_;
std::size_t buffer_size_; std::size_t buffer_size_;
// A noop assignment operator to avoid bogus warnings. // A noop assignment operator to avoid bogus warnings.
void operator=(const dispatcher &) {} void operator=(const dispatcher&) {}
// Handle the result of XSI-compliant version of strerror_r. // Handle the result of XSI-compliant version of strerror_r.
int handle(int result) { int handle(int result) {
@ -116,7 +116,7 @@ int safe_strerror(
} }
// Handle the result of GNU-specific version of strerror_r. // Handle the result of GNU-specific version of strerror_r.
int handle(char *message) { int handle(char* message) {
// If the buffer is full then the message is probably truncated. // If the buffer is full then the message is probably truncated.
if (message == buffer_ && strlen(buffer_) == buffer_size_ - 1) if (message == buffer_ && strlen(buffer_) == buffer_size_ - 1)
return ERANGE; return ERANGE;
@ -132,8 +132,8 @@ int safe_strerror(
// Fallback to strerror_s when strerror_r is not available. // Fallback to strerror_s when strerror_r is not available.
int fallback(int result) { int fallback(int result) {
// If the buffer is full then the message is probably truncated. // If the buffer is full then the message is probably truncated.
return result == 0 && strlen(buffer_) == buffer_size_ - 1 ? return result == 0 && strlen(buffer_) == buffer_size_ - 1 ? ERANGE
ERANGE : result; : result;
} }
#if !FMT_MSC_VER #if !FMT_MSC_VER
@ -146,17 +146,15 @@ int safe_strerror(
#endif #endif
public: public:
dispatcher(int err_code, char *&buf, std::size_t buf_size) dispatcher(int err_code, char*& buf, std::size_t buf_size)
: error_code_(err_code), buffer_(buf), buffer_size_(buf_size) {} : error_code_(err_code), buffer_(buf), buffer_size_(buf_size) {}
int run() { int run() { return handle(strerror_r(error_code_, buffer_, buffer_size_)); }
return handle(strerror_r(error_code_, buffer_, buffer_size_));
}
}; };
return dispatcher(error_code, buffer, buffer_size).run(); return dispatcher(error_code, buffer, buffer_size).run();
} }
void format_error_code(internal::buffer &out, int error_code, void format_error_code(internal::buffer& out, int error_code,
string_view message) FMT_NOEXCEPT { string_view message) FMT_NOEXCEPT {
// Report error code making sure that the output fits into // Report error code making sure that the output fits into
// inline_buffer_size to avoid dynamic memory allocation and potential // inline_buffer_size to avoid dynamic memory allocation and potential
@ -195,11 +193,10 @@ void report_error(FormatFunc func, int error_code,
} // namespace } // namespace
FMT_FUNC size_t internal::count_code_points(basic_string_view<char8_t> s) { FMT_FUNC size_t internal::count_code_points(basic_string_view<char8_t> s) {
const char8_t *data = s.data(); const char8_t* data = s.data();
size_t num_code_points = 0; size_t num_code_points = 0;
for (size_t i = 0, size = s.size(); i != size; ++i) { for (size_t i = 0, size = s.size(); i != size; ++i) {
if ((data[i] & 0xc0) != 0x80) if ((data[i] & 0xc0) != 0x80) ++num_code_points;
++num_code_points;
} }
return num_code_points; return num_code_points;
} }
@ -208,22 +205,20 @@ FMT_FUNC size_t internal::count_code_points(basic_string_view<char8_t> s) {
namespace internal { namespace internal {
template <typename Locale> template <typename Locale>
locale_ref::locale_ref(const Locale &loc) : locale_(&loc) { locale_ref::locale_ref(const Locale& loc) : locale_(&loc) {
static_assert(std::is_same<Locale, std::locale>::value, ""); static_assert(std::is_same<Locale, std::locale>::value, "");
} }
template <typename Locale> template <typename Locale> Locale locale_ref::get() const {
Locale locale_ref::get() const {
static_assert(std::is_same<Locale, std::locale>::value, ""); static_assert(std::is_same<Locale, std::locale>::value, "");
return locale_ ? *static_cast<const std::locale*>(locale_) : std::locale(); return locale_ ? *static_cast<const std::locale*>(locale_) : std::locale();
} }
template <typename Char> template <typename Char> FMT_FUNC Char thousands_sep_impl(locale_ref loc) {
FMT_FUNC Char thousands_sep_impl(locale_ref loc) { return std::use_facet<std::numpunct<Char> >(loc.get<std::locale>())
return std::use_facet<std::numpunct<Char> >( .thousands_sep();
loc.get<std::locale>()).thousands_sep();
}
} }
} // namespace internal
#else #else
template <typename Char> template <typename Char>
FMT_FUNC Char internal::thousands_sep_impl(locale_ref) { FMT_FUNC Char internal::thousands_sep_impl(locale_ref) {
@ -231,31 +226,30 @@ FMT_FUNC Char internal::thousands_sep_impl(locale_ref) {
} }
#endif #endif
FMT_FUNC void system_error::init( FMT_FUNC void system_error::init(int err_code, string_view format_str,
int err_code, string_view format_str, format_args args) { format_args args) {
error_code_ = err_code; error_code_ = err_code;
memory_buffer buffer; memory_buffer buffer;
format_system_error(buffer, err_code, vformat(format_str, args)); format_system_error(buffer, err_code, vformat(format_str, args));
std::runtime_error &base = *this; std::runtime_error& base = *this;
base = std::runtime_error(to_string(buffer)); base = std::runtime_error(to_string(buffer));
} }
namespace internal { namespace internal {
template <typename T> template <typename T>
int char_traits<char>::format_float( int char_traits<char>::format_float(char* buf, std::size_t size,
char *buf, std::size_t size, const char *format, int precision, T value) { const char* format, int precision,
return precision < 0 ? T value) {
FMT_SNPRINTF(buf, size, format, value) : return precision < 0 ? FMT_SNPRINTF(buf, size, format, value)
FMT_SNPRINTF(buf, size, format, precision, value); : FMT_SNPRINTF(buf, size, format, precision, value);
} }
template <typename T> template <typename T>
int char_traits<wchar_t>::format_float( int char_traits<wchar_t>::format_float(wchar_t* buf, std::size_t size,
wchar_t *buf, std::size_t size, const wchar_t *format, int precision, const wchar_t* format, int precision,
T value) { T value) {
return precision < 0 ? return precision < 0 ? FMT_SWPRINTF(buf, size, format, value)
FMT_SWPRINTF(buf, size, format, value) : : FMT_SWPRINTF(buf, size, format, precision, value);
FMT_SWPRINTF(buf, size, format, precision, value);
} }
template <typename T> template <typename T>
@ -266,86 +260,75 @@ const char basic_data<T>::DIGITS[] =
"6061626364656667686970717273747576777879" "6061626364656667686970717273747576777879"
"8081828384858687888990919293949596979899"; "8081828384858687888990919293949596979899";
#define FMT_POWERS_OF_10(factor) \ #define FMT_POWERS_OF_10(factor) \
factor * 10, \ factor * 10, factor * 100, factor * 1000, factor * 10000, factor * 100000, \
factor * 100, \ factor * 1000000, factor * 10000000, factor * 100000000, \
factor * 1000, \ factor * 1000000000
factor * 10000, \
factor * 100000, \
factor * 1000000, \
factor * 10000000, \
factor * 100000000, \
factor * 1000000000
template <typename T> template <typename T>
const uint32_t basic_data<T>::POWERS_OF_10_32[] = { const uint32_t basic_data<T>::POWERS_OF_10_32[] = {1, FMT_POWERS_OF_10(1)};
1, FMT_POWERS_OF_10(1)
};
template <typename T> template <typename T>
const uint32_t basic_data<T>::ZERO_OR_POWERS_OF_10_32[] = { const uint32_t basic_data<T>::ZERO_OR_POWERS_OF_10_32[] = {0,
0, FMT_POWERS_OF_10(1) FMT_POWERS_OF_10(1)};
};
template <typename T> template <typename T>
const uint64_t basic_data<T>::ZERO_OR_POWERS_OF_10_64[] = { const uint64_t basic_data<T>::ZERO_OR_POWERS_OF_10_64[] = {
0, 0, FMT_POWERS_OF_10(1), FMT_POWERS_OF_10(1000000000ull),
FMT_POWERS_OF_10(1), 10000000000000000000ull};
FMT_POWERS_OF_10(1000000000ull),
10000000000000000000ull
};
// Normalized 64-bit significands of pow(10, k), for k = -348, -340, ..., 340. // Normalized 64-bit significands of pow(10, k), for k = -348, -340, ..., 340.
// These are generated by support/compute-powers.py. // These are generated by support/compute-powers.py.
template <typename T> template <typename T>
const uint64_t basic_data<T>::POW10_SIGNIFICANDS[] = { const uint64_t basic_data<T>::POW10_SIGNIFICANDS[] = {
0xfa8fd5a0081c0288, 0xbaaee17fa23ebf76, 0x8b16fb203055ac76, 0xfa8fd5a0081c0288, 0xbaaee17fa23ebf76, 0x8b16fb203055ac76,
0xcf42894a5dce35ea, 0x9a6bb0aa55653b2d, 0xe61acf033d1a45df, 0xcf42894a5dce35ea, 0x9a6bb0aa55653b2d, 0xe61acf033d1a45df,
0xab70fe17c79ac6ca, 0xff77b1fcbebcdc4f, 0xbe5691ef416bd60c, 0xab70fe17c79ac6ca, 0xff77b1fcbebcdc4f, 0xbe5691ef416bd60c,
0x8dd01fad907ffc3c, 0xd3515c2831559a83, 0x9d71ac8fada6c9b5, 0x8dd01fad907ffc3c, 0xd3515c2831559a83, 0x9d71ac8fada6c9b5,
0xea9c227723ee8bcb, 0xaecc49914078536d, 0x823c12795db6ce57, 0xea9c227723ee8bcb, 0xaecc49914078536d, 0x823c12795db6ce57,
0xc21094364dfb5637, 0x9096ea6f3848984f, 0xd77485cb25823ac7, 0xc21094364dfb5637, 0x9096ea6f3848984f, 0xd77485cb25823ac7,
0xa086cfcd97bf97f4, 0xef340a98172aace5, 0xb23867fb2a35b28e, 0xa086cfcd97bf97f4, 0xef340a98172aace5, 0xb23867fb2a35b28e,
0x84c8d4dfd2c63f3b, 0xc5dd44271ad3cdba, 0x936b9fcebb25c996, 0x84c8d4dfd2c63f3b, 0xc5dd44271ad3cdba, 0x936b9fcebb25c996,
0xdbac6c247d62a584, 0xa3ab66580d5fdaf6, 0xf3e2f893dec3f126, 0xdbac6c247d62a584, 0xa3ab66580d5fdaf6, 0xf3e2f893dec3f126,
0xb5b5ada8aaff80b8, 0x87625f056c7c4a8b, 0xc9bcff6034c13053, 0xb5b5ada8aaff80b8, 0x87625f056c7c4a8b, 0xc9bcff6034c13053,
0x964e858c91ba2655, 0xdff9772470297ebd, 0xa6dfbd9fb8e5b88f, 0x964e858c91ba2655, 0xdff9772470297ebd, 0xa6dfbd9fb8e5b88f,
0xf8a95fcf88747d94, 0xb94470938fa89bcf, 0x8a08f0f8bf0f156b, 0xf8a95fcf88747d94, 0xb94470938fa89bcf, 0x8a08f0f8bf0f156b,
0xcdb02555653131b6, 0x993fe2c6d07b7fac, 0xe45c10c42a2b3b06, 0xcdb02555653131b6, 0x993fe2c6d07b7fac, 0xe45c10c42a2b3b06,
0xaa242499697392d3, 0xfd87b5f28300ca0e, 0xbce5086492111aeb, 0xaa242499697392d3, 0xfd87b5f28300ca0e, 0xbce5086492111aeb,
0x8cbccc096f5088cc, 0xd1b71758e219652c, 0x9c40000000000000, 0x8cbccc096f5088cc, 0xd1b71758e219652c, 0x9c40000000000000,
0xe8d4a51000000000, 0xad78ebc5ac620000, 0x813f3978f8940984, 0xe8d4a51000000000, 0xad78ebc5ac620000, 0x813f3978f8940984,
0xc097ce7bc90715b3, 0x8f7e32ce7bea5c70, 0xd5d238a4abe98068, 0xc097ce7bc90715b3, 0x8f7e32ce7bea5c70, 0xd5d238a4abe98068,
0x9f4f2726179a2245, 0xed63a231d4c4fb27, 0xb0de65388cc8ada8, 0x9f4f2726179a2245, 0xed63a231d4c4fb27, 0xb0de65388cc8ada8,
0x83c7088e1aab65db, 0xc45d1df942711d9a, 0x924d692ca61be758, 0x83c7088e1aab65db, 0xc45d1df942711d9a, 0x924d692ca61be758,
0xda01ee641a708dea, 0xa26da3999aef774a, 0xf209787bb47d6b85, 0xda01ee641a708dea, 0xa26da3999aef774a, 0xf209787bb47d6b85,
0xb454e4a179dd1877, 0x865b86925b9bc5c2, 0xc83553c5c8965d3d, 0xb454e4a179dd1877, 0x865b86925b9bc5c2, 0xc83553c5c8965d3d,
0x952ab45cfa97a0b3, 0xde469fbd99a05fe3, 0xa59bc234db398c25, 0x952ab45cfa97a0b3, 0xde469fbd99a05fe3, 0xa59bc234db398c25,
0xf6c69a72a3989f5c, 0xb7dcbf5354e9bece, 0x88fcf317f22241e2, 0xf6c69a72a3989f5c, 0xb7dcbf5354e9bece, 0x88fcf317f22241e2,
0xcc20ce9bd35c78a5, 0x98165af37b2153df, 0xe2a0b5dc971f303a, 0xcc20ce9bd35c78a5, 0x98165af37b2153df, 0xe2a0b5dc971f303a,
0xa8d9d1535ce3b396, 0xfb9b7cd9a4a7443c, 0xbb764c4ca7a44410, 0xa8d9d1535ce3b396, 0xfb9b7cd9a4a7443c, 0xbb764c4ca7a44410,
0x8bab8eefb6409c1a, 0xd01fef10a657842c, 0x9b10a4e5e9913129, 0x8bab8eefb6409c1a, 0xd01fef10a657842c, 0x9b10a4e5e9913129,
0xe7109bfba19c0c9d, 0xac2820d9623bf429, 0x80444b5e7aa7cf85, 0xe7109bfba19c0c9d, 0xac2820d9623bf429, 0x80444b5e7aa7cf85,
0xbf21e44003acdd2d, 0x8e679c2f5e44ff8f, 0xd433179d9c8cb841, 0xbf21e44003acdd2d, 0x8e679c2f5e44ff8f, 0xd433179d9c8cb841,
0x9e19db92b4e31ba9, 0xeb96bf6ebadf77d9, 0xaf87023b9bf0ee6b, 0x9e19db92b4e31ba9, 0xeb96bf6ebadf77d9, 0xaf87023b9bf0ee6b,
}; };
// Binary exponents of pow(10, k), for k = -348, -340, ..., 340, corresponding // Binary exponents of pow(10, k), for k = -348, -340, ..., 340, corresponding
// to significands above. // to significands above.
template <typename T> template <typename T>
const int16_t basic_data<T>::POW10_EXPONENTS[] = { const int16_t basic_data<T>::POW10_EXPONENTS[] = {
-1220, -1193, -1166, -1140, -1113, -1087, -1060, -1034, -1007, -980, -954, -1220, -1193, -1166, -1140, -1113, -1087, -1060, -1034, -1007, -980, -954,
-927, -901, -874, -847, -821, -794, -768, -741, -715, -688, -661, -927, -901, -874, -847, -821, -794, -768, -741, -715, -688, -661,
-635, -608, -582, -555, -529, -502, -475, -449, -422, -396, -369, -635, -608, -582, -555, -529, -502, -475, -449, -422, -396, -369,
-343, -316, -289, -263, -236, -210, -183, -157, -130, -103, -77, -343, -316, -289, -263, -236, -210, -183, -157, -130, -103, -77,
-50, -24, 3, 30, 56, 83, 109, 136, 162, 189, 216, -50, -24, 3, 30, 56, 83, 109, 136, 162, 189, 216,
242, 269, 295, 322, 348, 375, 402, 428, 455, 481, 508, 242, 269, 295, 322, 348, 375, 402, 428, 455, 481, 508,
534, 561, 588, 614, 641, 667, 694, 720, 747, 774, 800, 534, 561, 588, 614, 641, 667, 694, 720, 747, 774, 800,
827, 853, 880, 907, 933, 960, 986, 1013, 1039, 1066 827, 853, 880, 907, 933, 960, 986, 1013, 1039, 1066};
};
template <typename T> const char basic_data<T>::FOREGROUND_COLOR[] = "\x1b[38;2;"; template <typename T>
template <typename T> const char basic_data<T>::BACKGROUND_COLOR[] = "\x1b[48;2;"; const char basic_data<T>::FOREGROUND_COLOR[] = "\x1b[38;2;";
template <typename T>
const char basic_data<T>::BACKGROUND_COLOR[] = "\x1b[48;2;";
template <typename T> const char basic_data<T>::RESET_COLOR[] = "\x1b[0m"; template <typename T> const char basic_data<T>::RESET_COLOR[] = "\x1b[0m";
template <typename T> const wchar_t basic_data<T>::WRESET_COLOR[] = L"\x1b[0m"; template <typename T> const wchar_t basic_data<T>::WRESET_COLOR[] = L"\x1b[0m";
@ -356,33 +339,32 @@ class fp {
// All sizes are in bits. // All sizes are in bits.
static FMT_CONSTEXPR_DECL const int char_size = static FMT_CONSTEXPR_DECL const int char_size =
std::numeric_limits<unsigned char>::digits; std::numeric_limits<unsigned char>::digits;
// Subtract 1 to account for an implicit most significant bit in the // Subtract 1 to account for an implicit most significant bit in the
// normalized form. // normalized form.
static FMT_CONSTEXPR_DECL const int double_significand_size = static FMT_CONSTEXPR_DECL const int double_significand_size =
std::numeric_limits<double>::digits - 1; std::numeric_limits<double>::digits - 1;
static FMT_CONSTEXPR_DECL const uint64_t implicit_bit = static FMT_CONSTEXPR_DECL const uint64_t implicit_bit =
1ull << double_significand_size; 1ull << double_significand_size;
public: public:
significand_type f; significand_type f;
int e; int e;
static FMT_CONSTEXPR_DECL const int significand_size = static FMT_CONSTEXPR_DECL const int significand_size =
sizeof(significand_type) * char_size; sizeof(significand_type) * char_size;
fp(): f(0), e(0) {} fp() : f(0), e(0) {}
fp(uint64_t f_val, int e_val): f(f_val), e(e_val) {} fp(uint64_t f_val, int e_val) : f(f_val), e(e_val) {}
// Constructs fp from an IEEE754 double. It is a template to prevent compile // Constructs fp from an IEEE754 double. It is a template to prevent compile
// errors on platforms where double is not IEEE754. // errors on platforms where double is not IEEE754.
template <typename Double> template <typename Double> explicit fp(Double d) {
explicit fp(Double d) {
// Assume double is in the format [sign][exponent][significand]. // Assume double is in the format [sign][exponent][significand].
typedef std::numeric_limits<Double> limits; typedef std::numeric_limits<Double> limits;
const int double_size = static_cast<int>(sizeof(Double) * char_size); const int double_size = static_cast<int>(sizeof(Double) * char_size);
const int exponent_size = const int exponent_size =
double_size - double_significand_size - 1; // -1 for sign double_size - double_significand_size - 1; // -1 for sign
const uint64_t significand_mask = implicit_bit - 1; const uint64_t significand_mask = implicit_bit - 1;
const uint64_t exponent_mask = (~0ull >> 1) & ~significand_mask; const uint64_t exponent_mask = (~0ull >> 1) & ~significand_mask;
const int exponent_bias = (1 << exponent_size) - limits::max_exponent - 1; const int exponent_bias = (1 << exponent_size) - limits::max_exponent - 1;
@ -397,8 +379,7 @@ class fp {
} }
// Normalizes the value converted from double and multiplied by (1 << SHIFT). // Normalizes the value converted from double and multiplied by (1 << SHIFT).
template <int SHIFT = 0> template <int SHIFT = 0> void normalize() {
void normalize() {
// Handle subnormals. // Handle subnormals.
auto shifted_implicit_bit = implicit_bit << SHIFT; auto shifted_implicit_bit = implicit_bit << SHIFT;
while ((f & shifted_implicit_bit) == 0) { while ((f & shifted_implicit_bit) == 0) {
@ -415,9 +396,9 @@ class fp {
// a boundary is a value half way between the number and its predecessor // a boundary is a value half way between the number and its predecessor
// (lower) or successor (upper). The upper boundary is normalized and lower // (lower) or successor (upper). The upper boundary is normalized and lower
// has the same exponent but may be not normalized. // has the same exponent but may be not normalized.
void compute_boundaries(fp &lower, fp &upper) const { void compute_boundaries(fp& lower, fp& upper) const {
lower = f == implicit_bit ? lower =
fp((f << 2) - 1, e - 2) : fp((f << 1) - 1, e - 1); f == implicit_bit ? fp((f << 2) - 1, e - 2) : fp((f << 1) - 1, e - 1);
upper = fp((f << 1) + 1, e - 1); upper = fp((f << 1) + 1, e - 1);
upper.normalize<1>(); // 1 is to account for the exponent shift above. upper.normalize<1>(); // 1 is to account for the exponent shift above.
lower.f <<= lower.e - upper.e; lower.f <<= lower.e - upper.e;
@ -432,12 +413,13 @@ inline fp operator-(fp x, fp y) {
} }
// Computes an fp number r with r.f = x.f * y.f / pow(2, 64) rounded to nearest // Computes an fp number r with r.f = x.f * y.f / pow(2, 64) rounded to nearest
// with half-up tie breaking, r.e = x.e + y.e + 64. Result may not be normalized. // with half-up tie breaking, r.e = x.e + y.e + 64. Result may not be
// normalized.
FMT_API fp operator*(fp x, fp y); FMT_API fp operator*(fp x, fp y);
// Returns cached power (of 10) c_k = c_k.f * pow(2, c_k.e) such that its // Returns cached power (of 10) c_k = c_k.f * pow(2, c_k.e) such that its
// (binary) exponent satisfies min_exponent <= c_k.e <= min_exponent + 3. // (binary) exponent satisfies min_exponent <= c_k.e <= min_exponent + 3.
FMT_API fp get_cached_power(int min_exponent, int &pow10_exponent); FMT_API fp get_cached_power(int min_exponent, int& pow10_exponent);
FMT_FUNC fp operator*(fp x, fp y) { FMT_FUNC fp operator*(fp x, fp y) {
// Multiply 32-bit parts of significands. // Multiply 32-bit parts of significands.
@ -450,10 +432,10 @@ FMT_FUNC fp operator*(fp x, fp y) {
return fp(ac + (ad >> 32) + (bc >> 32) + (mid >> 32), x.e + y.e + 64); return fp(ac + (ad >> 32) + (bc >> 32) + (mid >> 32), x.e + y.e + 64);
} }
FMT_FUNC fp get_cached_power(int min_exponent, int &pow10_exponent) { FMT_FUNC fp get_cached_power(int min_exponent, int& pow10_exponent) {
const double one_over_log2_10 = 0.30102999566398114; // 1 / log2(10) const double one_over_log2_10 = 0.30102999566398114; // 1 / log2(10)
int index = static_cast<int>(std::ceil( int index = static_cast<int>(
(min_exponent + fp::significand_size - 1) * one_over_log2_10)); std::ceil((min_exponent + fp::significand_size - 1) * one_over_log2_10));
// Decimal exponent of the first (smallest) cached power of 10. // Decimal exponent of the first (smallest) cached power of 10.
const int first_dec_exp = -348; const int first_dec_exp = -348;
// Difference between 2 consecutive decimal exponents in cached powers of 10. // Difference between 2 consecutive decimal exponents in cached powers of 10.
@ -463,55 +445,84 @@ FMT_FUNC fp get_cached_power(int min_exponent, int &pow10_exponent) {
return fp(data::POW10_SIGNIFICANDS[index], data::POW10_EXPONENTS[index]); return fp(data::POW10_SIGNIFICANDS[index], data::POW10_EXPONENTS[index]);
} }
FMT_FUNC bool grisu2_round( FMT_FUNC bool grisu2_round(char* buf, int& size, int max_digits, uint64_t delta,
char *buf, int &size, int max_digits, uint64_t delta, uint64_t remainder, uint64_t exp, uint64_t diff,
uint64_t remainder, uint64_t exp, uint64_t diff, int &exp10) { int& exp10) {
while (remainder < diff && delta - remainder >= exp && while (
(remainder + exp < diff || diff - remainder > remainder + exp - diff)) { remainder < diff && delta - remainder >= exp &&
(remainder + exp < diff || diff - remainder > remainder + exp - diff)) {
--buf[size - 1]; --buf[size - 1];
remainder += exp; remainder += exp;
} }
if (size > max_digits) { if (size > max_digits) {
--size; --size;
++exp10; ++exp10;
if (buf[size] >= '5') if (buf[size] >= '5') return false;
return false;
} }
return true; return true;
} }
// Generates output using Grisu2 digit-gen algorithm. // Generates output using Grisu2 digit-gen algorithm.
FMT_FUNC bool grisu2_gen_digits( FMT_FUNC bool grisu2_gen_digits(char* buf, int& size, uint32_t hi, uint64_t lo,
char *buf, int &size, uint32_t hi, uint64_t lo, int &exp, int& exp, uint64_t delta, const fp& one,
uint64_t delta, const fp &one, const fp &diff, int max_digits) { const fp& diff, int max_digits) {
// Generate digits for the most significant part (hi). // Generate digits for the most significant part (hi).
while (exp > 0) { while (exp > 0) {
uint32_t digit = 0; uint32_t digit = 0;
// This optimization by miloyip reduces the number of integer divisions by // This optimization by miloyip reduces the number of integer divisions by
// one per iteration. // one per iteration.
switch (exp) { switch (exp) {
case 10: digit = hi / 1000000000; hi %= 1000000000; break; case 10:
case 9: digit = hi / 100000000; hi %= 100000000; break; digit = hi / 1000000000;
case 8: digit = hi / 10000000; hi %= 10000000; break; hi %= 1000000000;
case 7: digit = hi / 1000000; hi %= 1000000; break; break;
case 6: digit = hi / 100000; hi %= 100000; break; case 9:
case 5: digit = hi / 10000; hi %= 10000; break; digit = hi / 100000000;
case 4: digit = hi / 1000; hi %= 1000; break; hi %= 100000000;
case 3: digit = hi / 100; hi %= 100; break; break;
case 2: digit = hi / 10; hi %= 10; break; case 8:
case 1: digit = hi; hi = 0; break; digit = hi / 10000000;
hi %= 10000000;
break;
case 7:
digit = hi / 1000000;
hi %= 1000000;
break;
case 6:
digit = hi / 100000;
hi %= 100000;
break;
case 5:
digit = hi / 10000;
hi %= 10000;
break;
case 4:
digit = hi / 1000;
hi %= 1000;
break;
case 3:
digit = hi / 100;
hi %= 100;
break;
case 2:
digit = hi / 10;
hi %= 10;
break;
case 1:
digit = hi;
hi = 0;
break;
default: default:
FMT_ASSERT(false, "invalid number of digits"); FMT_ASSERT(false, "invalid number of digits");
} }
if (digit != 0 || size != 0) if (digit != 0 || size != 0) buf[size++] = static_cast<char>('0' + digit);
buf[size++] = static_cast<char>('0' + digit);
--exp; --exp;
uint64_t remainder = (static_cast<uint64_t>(hi) << -one.e) + lo; uint64_t remainder = (static_cast<uint64_t>(hi) << -one.e) + lo;
if (remainder <= delta || size > max_digits) { if (remainder <= delta || size > max_digits) {
return grisu2_round( return grisu2_round(
buf, size, max_digits, delta, remainder, buf, size, max_digits, delta, remainder,
static_cast<uint64_t>(data::POWERS_OF_10_32[exp]) << -one.e, static_cast<uint64_t>(data::POWERS_OF_10_32[exp]) << -one.e, diff.f,
diff.f, exp); exp);
} }
} }
// Generate digits for the least significant part (lo). // Generate digits for the least significant part (lo).
@ -519,8 +530,7 @@ FMT_FUNC bool grisu2_gen_digits(
lo *= 10; lo *= 10;
delta *= 10; delta *= 10;
char digit = static_cast<char>(lo >> -one.e); char digit = static_cast<char>(lo >> -one.e);
if (digit != 0 || size != 0) if (digit != 0 || size != 0) buf[size++] = static_cast<char>('0' + digit);
buf[size++] = static_cast<char>('0' + digit);
lo &= one.f - 1; lo &= one.f - 1;
--exp; --exp;
if (lo < delta || size > max_digits) { if (lo < delta || size > max_digits) {
@ -531,11 +541,11 @@ FMT_FUNC bool grisu2_gen_digits(
} }
#if FMT_CLANG_VERSION #if FMT_CLANG_VERSION
# define FMT_FALLTHROUGH [[clang::fallthrough]]; # define FMT_FALLTHROUGH [[clang::fallthrough]];
#elif FMT_GCC_VERSION >= 700 #elif FMT_GCC_VERSION >= 700
# define FMT_FALLTHROUGH [[gnu::fallthrough]]; # define FMT_FALLTHROUGH [[gnu::fallthrough]];
#else #else
# define FMT_FALLTHROUGH # define FMT_FALLTHROUGH
#endif #endif
struct gen_digits_params { struct gen_digits_params {
@ -546,19 +556,18 @@ struct gen_digits_params {
}; };
struct prettify_handler { struct prettify_handler {
char *data; char* data;
ptrdiff_t size; ptrdiff_t size;
buffer &buf; buffer& buf;
explicit prettify_handler(buffer &b, ptrdiff_t n) explicit prettify_handler(buffer& b, ptrdiff_t n)
: data(b.data()), size(n), buf(b) {} : data(b.data()), size(n), buf(b) {}
~prettify_handler() { ~prettify_handler() {
assert(buf.size() >= to_unsigned(size)); assert(buf.size() >= to_unsigned(size));
buf.resize(to_unsigned(size)); buf.resize(to_unsigned(size));
} }
template <typename F> template <typename F> void insert(ptrdiff_t pos, ptrdiff_t n, F f) {
void insert(ptrdiff_t pos, ptrdiff_t n, F f) {
std::memmove(data + pos + n, data + pos, to_unsigned(size - pos)); std::memmove(data + pos + n, data + pos, to_unsigned(size - pos));
f(data + pos); f(data + pos);
size += n; size += n;
@ -583,8 +592,7 @@ struct prettify_handler {
}; };
// Writes the exponent exp in the form "[+-]d{2,3}" to buffer. // Writes the exponent exp in the form "[+-]d{2,3}" to buffer.
template <typename Handler> template <typename Handler> FMT_FUNC void write_exponent(int exp, Handler&& h) {
FMT_FUNC void write_exponent(int exp, Handler &&h) {
FMT_ASSERT(-1000 < exp && exp < 1000, "exponent out of range"); FMT_ASSERT(-1000 < exp && exp < 1000, "exponent out of range");
if (exp < 0) { if (exp < 0) {
h.append('-'); h.append('-');
@ -595,11 +603,11 @@ FMT_FUNC void write_exponent(int exp, Handler &&h) {
if (exp >= 100) { if (exp >= 100) {
h.append(static_cast<char>('0' + exp / 100)); h.append(static_cast<char>('0' + exp / 100));
exp %= 100; exp %= 100;
const char *d = data::DIGITS + exp * 2; const char* d = data::DIGITS + exp * 2;
h.append(d[0]); h.append(d[0]);
h.append(d[1]); h.append(d[1]);
} else { } else {
const char *d = data::DIGITS + exp * 2; const char* d = data::DIGITS + exp * 2;
h.append(d[0]); h.append(d[0]);
h.append(d[1]); h.append(d[1]);
} }
@ -607,7 +615,7 @@ FMT_FUNC void write_exponent(int exp, Handler &&h) {
struct fill { struct fill {
size_t n; size_t n;
void operator()(char *buf) const { void operator()(char* buf) const {
buf[0] = '0'; buf[0] = '0';
buf[1] = '.'; buf[1] = '.';
std::uninitialized_fill_n(buf + 2, n, '0'); std::uninitialized_fill_n(buf + 2, n, '0');
@ -616,14 +624,13 @@ struct fill {
// The number is given as v = f * pow(10, exp), where f has size digits. // The number is given as v = f * pow(10, exp), where f has size digits.
template <typename Handler> template <typename Handler>
FMT_FUNC void grisu2_prettify(const gen_digits_params &params, FMT_FUNC void grisu2_prettify(const gen_digits_params& params, int size,
int size, int exp, Handler &&handler) { int exp, Handler&& handler) {
if (!params.fixed) { if (!params.fixed) {
// Insert a decimal point after the first digit and add an exponent. // Insert a decimal point after the first digit and add an exponent.
handler.insert(1, '.'); handler.insert(1, '.');
exp += size - 1; exp += size - 1;
if (size < params.num_digits) if (size < params.num_digits) handler.append(params.num_digits - size, '0');
handler.append(params.num_digits - size, '0');
handler.append(params.upper ? 'E' : 'e'); handler.append(params.upper ? 'E' : 'e');
write_exponent(exp, handler); write_exponent(exp, handler);
return; return;
@ -659,8 +666,7 @@ FMT_FUNC void grisu2_prettify(const gen_digits_params &params,
struct char_counter { struct char_counter {
ptrdiff_t size; ptrdiff_t size;
template <typename F> template <typename F> void insert(ptrdiff_t, ptrdiff_t n, F) { size += n; }
void insert(ptrdiff_t, ptrdiff_t n, F) { size += n; }
void insert(ptrdiff_t, char) { ++size; } void insert(ptrdiff_t, char) { ++size; }
void append(ptrdiff_t n, char) { size += n; } void append(ptrdiff_t n, char) { size += n; }
void append(char) { ++size; } void append(char) { ++size; }
@ -670,15 +676,16 @@ struct char_counter {
// Converts format specifiers into parameters for digit generation and computes // Converts format specifiers into parameters for digit generation and computes
// output buffer size for a number in the range [pow(10, exp - 1), pow(10, exp) // output buffer size for a number in the range [pow(10, exp - 1), pow(10, exp)
// or 0 if exp == 1. // or 0 if exp == 1.
FMT_FUNC gen_digits_params process_specs(const core_format_specs &specs, FMT_FUNC gen_digits_params process_specs(const core_format_specs& specs,
int exp, buffer &buf) { int exp, buffer& buf) {
auto params = gen_digits_params(); auto params = gen_digits_params();
int num_digits = specs.precision >= 0 ? specs.precision : 6; int num_digits = specs.precision >= 0 ? specs.precision : 6;
switch (specs.type) { switch (specs.type) {
case 'G': case 'G':
params.upper = true; params.upper = true;
FMT_FALLTHROUGH FMT_FALLTHROUGH
case '\0': case 'g': case '\0':
case 'g':
params.trailing_zeros = (specs.flags & HASH_FLAG) != 0; params.trailing_zeros = (specs.flags & HASH_FLAG) != 0;
if (-4 <= exp && exp < num_digits + 1) { if (-4 <= exp && exp < num_digits + 1) {
params.fixed = true; params.fixed = true;
@ -693,8 +700,7 @@ FMT_FUNC gen_digits_params process_specs(const core_format_specs &specs,
params.fixed = true; params.fixed = true;
params.trailing_zeros = true; params.trailing_zeros = true;
int adjusted_min_digits = num_digits + exp; int adjusted_min_digits = num_digits + exp;
if (adjusted_min_digits > 0) if (adjusted_min_digits > 0) num_digits = adjusted_min_digits;
num_digits = adjusted_min_digits;
break; break;
} }
case 'E': case 'E':
@ -713,7 +719,7 @@ FMT_FUNC gen_digits_params process_specs(const core_format_specs &specs,
template <typename Double> template <typename Double>
FMT_FUNC typename std::enable_if<sizeof(Double) == sizeof(uint64_t), bool>::type FMT_FUNC typename std::enable_if<sizeof(Double) == sizeof(uint64_t), bool>::type
grisu2_format(Double value, buffer &buf, core_format_specs specs) { grisu2_format(Double value, buffer& buf, core_format_specs specs) {
FMT_ASSERT(value >= 0, "value is negative"); FMT_ASSERT(value >= 0, "value is negative");
if (value == 0) { if (value == 0) {
gen_digits_params params = process_specs(specs, 1, buf); gen_digits_params params = process_specs(specs, 1, buf);
@ -728,13 +734,13 @@ FMT_FUNC typename std::enable_if<sizeof(Double) == sizeof(uint64_t), bool>::type
fp_value.compute_boundaries(lower, upper); fp_value.compute_boundaries(lower, upper);
// Find a cached power of 10 close to 1 / upper and use it to scale upper. // Find a cached power of 10 close to 1 / upper and use it to scale upper.
const int min_exp = -60; // alpha in Grisu. const int min_exp = -60; // alpha in Grisu.
int cached_exp = 0; // K in Grisu. int cached_exp = 0; // K in Grisu.
auto cached_pow = get_cached_power( // \tilde{c}_{-k} in Grisu. auto cached_pow = get_cached_power( // \tilde{c}_{-k} in Grisu.
min_exp - (upper.e + fp::significand_size), cached_exp); min_exp - (upper.e + fp::significand_size), cached_exp);
cached_exp = -cached_exp; cached_exp = -cached_exp;
upper = upper * cached_pow; // \tilde{M}^+ in Grisu. upper = upper * cached_pow; // \tilde{M}^+ in Grisu.
--upper.f; // \tilde{M}^+ - 1 ulp -> M^+_{\downarrow}. --upper.f; // \tilde{M}^+ - 1 ulp -> M^+_{\downarrow}.
fp one(1ull << -upper.e, upper.e); fp one(1ull << -upper.e, upper.e);
// hi (p1 in Grisu) contains the most significant digits of scaled_upper. // hi (p1 in Grisu) contains the most significant digits of scaled_upper.
// hi = floor(upper / one). // hi = floor(upper / one).
@ -744,9 +750,9 @@ FMT_FUNC typename std::enable_if<sizeof(Double) == sizeof(uint64_t), bool>::type
fp_value.normalize(); fp_value.normalize();
fp scaled_value = fp_value * cached_pow; fp scaled_value = fp_value * cached_pow;
lower = lower * cached_pow; // \tilde{M}^- in Grisu. lower = lower * cached_pow; // \tilde{M}^- in Grisu.
++lower.f; // \tilde{M}^- + 1 ulp -> M^-_{\uparrow}. ++lower.f; // \tilde{M}^- + 1 ulp -> M^-_{\uparrow}.
uint64_t delta = upper.f - lower.f; uint64_t delta = upper.f - lower.f;
fp diff = upper - scaled_value; // wp_w in Grisu. fp diff = upper - scaled_value; // wp_w in Grisu.
// lo (p2 in Grisu) contains the least significants digits of scaled_upper. // lo (p2 in Grisu) contains the least significants digits of scaled_upper.
// lo = supper % one. // lo = supper % one.
uint64_t lo = upper.f & (one.f - 1); uint64_t lo = upper.f & (one.f - 1);
@ -761,29 +767,27 @@ FMT_FUNC typename std::enable_if<sizeof(Double) == sizeof(uint64_t), bool>::type
} }
template <typename Double> template <typename Double>
void sprintf_format(Double value, internal::buffer &buf, void sprintf_format(Double value, internal::buffer& buf,
core_format_specs spec) { core_format_specs spec) {
// Buffer capacity must be non-zero, otherwise MSVC's vsnprintf_s will fail. // Buffer capacity must be non-zero, otherwise MSVC's vsnprintf_s will fail.
FMT_ASSERT(buf.capacity() != 0, "empty buffer"); FMT_ASSERT(buf.capacity() != 0, "empty buffer");
// Build format string. // Build format string.
enum { MAX_FORMAT_SIZE = 10}; // longest format: %#-*.*Lg enum { MAX_FORMAT_SIZE = 10 }; // longest format: %#-*.*Lg
char format[MAX_FORMAT_SIZE]; char format[MAX_FORMAT_SIZE];
char *format_ptr = format; char* format_ptr = format;
*format_ptr++ = '%'; *format_ptr++ = '%';
if (spec.has(HASH_FLAG)) if (spec.has(HASH_FLAG)) *format_ptr++ = '#';
*format_ptr++ = '#';
if (spec.precision >= 0) { if (spec.precision >= 0) {
*format_ptr++ = '.'; *format_ptr++ = '.';
*format_ptr++ = '*'; *format_ptr++ = '*';
} }
if (std::is_same<Double, long double>::value) if (std::is_same<Double, long double>::value) *format_ptr++ = 'L';
*format_ptr++ = 'L';
*format_ptr++ = spec.type; *format_ptr++ = spec.type;
*format_ptr = '\0'; *format_ptr = '\0';
// Format using snprintf. // Format using snprintf.
char *start = FMT_NULL; char* start = FMT_NULL;
for (;;) { for (;;) {
std::size_t buffer_size = buf.capacity(); std::size_t buffer_size = buf.capacity();
start = &buf[0]; start = &buf[0];
@ -819,28 +823,25 @@ FMT_FUNC internal::utf8_to_utf16::utf8_to_utf16(string_view s) {
return; return;
} }
int length = MultiByteToWideChar( int length = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, s.data(),
CP_UTF8, MB_ERR_INVALID_CHARS, s.data(), s_size, FMT_NULL, 0); s_size, FMT_NULL, 0);
if (length == 0) if (length == 0) FMT_THROW(windows_error(GetLastError(), ERROR_MSG));
FMT_THROW(windows_error(GetLastError(), ERROR_MSG));
buffer_.resize(length + 1); buffer_.resize(length + 1);
length = MultiByteToWideChar( length = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, s.data(), s_size,
CP_UTF8, MB_ERR_INVALID_CHARS, s.data(), s_size, &buffer_[0], length); &buffer_[0], length);
if (length == 0) if (length == 0) FMT_THROW(windows_error(GetLastError(), ERROR_MSG));
FMT_THROW(windows_error(GetLastError(), ERROR_MSG));
buffer_[length] = 0; buffer_[length] = 0;
} }
FMT_FUNC internal::utf16_to_utf8::utf16_to_utf8(wstring_view s) { FMT_FUNC internal::utf16_to_utf8::utf16_to_utf8(wstring_view s) {
if (int error_code = convert(s)) { if (int error_code = convert(s)) {
FMT_THROW(windows_error(error_code, FMT_THROW(windows_error(error_code,
"cannot convert string from UTF-16 to UTF-8")); "cannot convert string from UTF-16 to UTF-8"));
} }
} }
FMT_FUNC int internal::utf16_to_utf8::convert(wstring_view s) { FMT_FUNC int internal::utf16_to_utf8::convert(wstring_view s) {
if (s.size() > INT_MAX) if (s.size() > INT_MAX) return ERROR_INVALID_PARAMETER;
return ERROR_INVALID_PARAMETER;
int s_size = static_cast<int>(s.size()); int s_size = static_cast<int>(s.size());
if (s_size == 0) { if (s_size == 0) {
// WideCharToMultiByte does not support zero length, handle separately. // WideCharToMultiByte does not support zero length, handle separately.
@ -849,39 +850,38 @@ FMT_FUNC int internal::utf16_to_utf8::convert(wstring_view s) {
return 0; return 0;
} }
int length = WideCharToMultiByte( int length = WideCharToMultiByte(CP_UTF8, 0, s.data(), s_size, FMT_NULL, 0,
CP_UTF8, 0, s.data(), s_size, FMT_NULL, 0, FMT_NULL, FMT_NULL); FMT_NULL, FMT_NULL);
if (length == 0) if (length == 0) return GetLastError();
return GetLastError();
buffer_.resize(length + 1); buffer_.resize(length + 1);
length = WideCharToMultiByte( length = WideCharToMultiByte(CP_UTF8, 0, s.data(), s_size, &buffer_[0],
CP_UTF8, 0, s.data(), s_size, &buffer_[0], length, FMT_NULL, FMT_NULL); length, FMT_NULL, FMT_NULL);
if (length == 0) if (length == 0) return GetLastError();
return GetLastError();
buffer_[length] = 0; buffer_[length] = 0;
return 0; return 0;
} }
FMT_FUNC void windows_error::init( FMT_FUNC void windows_error::init(int err_code, string_view format_str,
int err_code, string_view format_str, format_args args) { format_args args) {
error_code_ = err_code; error_code_ = err_code;
memory_buffer buffer; memory_buffer buffer;
internal::format_windows_error(buffer, err_code, vformat(format_str, args)); internal::format_windows_error(buffer, err_code, vformat(format_str, args));
std::runtime_error &base = *this; std::runtime_error& base = *this;
base = std::runtime_error(to_string(buffer)); base = std::runtime_error(to_string(buffer));
} }
FMT_FUNC void internal::format_windows_error( FMT_FUNC void internal::format_windows_error(internal::buffer& out,
internal::buffer &out, int error_code, string_view message) FMT_NOEXCEPT { int error_code,
string_view message) FMT_NOEXCEPT {
FMT_TRY { FMT_TRY {
wmemory_buffer buf; wmemory_buffer buf;
buf.resize(inline_buffer_size); buf.resize(inline_buffer_size);
for (;;) { for (;;) {
wchar_t *system_message = &buf[0]; wchar_t* system_message = &buf[0];
int result = FormatMessageW( int result = FormatMessageW(
FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, FMT_NULL,
FMT_NULL, error_code, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), error_code, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), system_message,
system_message, static_cast<uint32_t>(buf.size()), FMT_NULL); static_cast<uint32_t>(buf.size()), FMT_NULL);
if (result != 0) { if (result != 0) {
utf16_to_utf8 utf8_message; utf16_to_utf8 utf8_message;
if (utf8_message.convert(system_message) == ERROR_SUCCESS) { if (utf8_message.convert(system_message) == ERROR_SUCCESS) {
@ -897,19 +897,20 @@ FMT_FUNC void internal::format_windows_error(
break; // Can't get error message, report error code instead. break; // Can't get error message, report error code instead.
buf.resize(buf.size() * 2); buf.resize(buf.size() * 2);
} }
} FMT_CATCH(...) {} }
FMT_CATCH(...) {}
format_error_code(out, error_code, message); format_error_code(out, error_code, message);
} }
#endif // FMT_USE_WINDOWS_H #endif // FMT_USE_WINDOWS_H
FMT_FUNC void format_system_error( FMT_FUNC void format_system_error(internal::buffer& out, int error_code,
internal::buffer &out, int error_code, string_view message) FMT_NOEXCEPT { string_view message) FMT_NOEXCEPT {
FMT_TRY { FMT_TRY {
memory_buffer buf; memory_buffer buf;
buf.resize(inline_buffer_size); buf.resize(inline_buffer_size);
for (;;) { for (;;) {
char *system_message = &buf[0]; char* system_message = &buf[0];
int result = safe_strerror(error_code, system_message, buf.size()); int result = safe_strerror(error_code, system_message, buf.size());
if (result == 0) { if (result == 0) {
writer w(out); writer w(out);
@ -922,34 +923,35 @@ FMT_FUNC void format_system_error(
break; // Can't get error message, report error code instead. break; // Can't get error message, report error code instead.
buf.resize(buf.size() * 2); buf.resize(buf.size() * 2);
} }
} FMT_CATCH(...) {} }
FMT_CATCH(...) {}
format_error_code(out, error_code, message); format_error_code(out, error_code, message);
} }
FMT_FUNC void internal::error_handler::on_error(const char *message) { FMT_FUNC void internal::error_handler::on_error(const char* message) {
FMT_THROW(format_error(message)); FMT_THROW(format_error(message));
} }
FMT_FUNC void report_system_error( FMT_FUNC void report_system_error(int error_code,
int error_code, fmt::string_view message) FMT_NOEXCEPT { fmt::string_view message) FMT_NOEXCEPT {
report_error(format_system_error, error_code, message); report_error(format_system_error, error_code, message);
} }
#if FMT_USE_WINDOWS_H #if FMT_USE_WINDOWS_H
FMT_FUNC void report_windows_error( FMT_FUNC void report_windows_error(int error_code,
int error_code, fmt::string_view message) FMT_NOEXCEPT { fmt::string_view message) FMT_NOEXCEPT {
report_error(internal::format_windows_error, error_code, message); report_error(internal::format_windows_error, error_code, message);
} }
#endif #endif
FMT_FUNC void vprint(std::FILE *f, string_view format_str, format_args args) { FMT_FUNC void vprint(std::FILE* f, string_view format_str, format_args args) {
memory_buffer buffer; memory_buffer buffer;
internal::vformat_to(buffer, format_str, internal::vformat_to(buffer, format_str,
basic_format_args<buffer_context<char>::type>(args)); basic_format_args<buffer_context<char>::type>(args));
std::fwrite(buffer.data(), 1, buffer.size(), f); std::fwrite(buffer.data(), 1, buffer.size(), f);
} }
FMT_FUNC void vprint(std::FILE *f, wstring_view format_str, wformat_args args) { FMT_FUNC void vprint(std::FILE* f, wstring_view format_str, wformat_args args) {
wmemory_buffer buffer; wmemory_buffer buffer;
internal::vformat_to(buffer, format_str, args); internal::vformat_to(buffer, format_str, args);
std::fwrite(buffer.data(), sizeof(wchar_t), buffer.size(), f); std::fwrite(buffer.data(), sizeof(wchar_t), buffer.size(), f);
@ -966,7 +968,7 @@ FMT_FUNC void vprint(wstring_view format_str, wformat_args args) {
FMT_END_NAMESPACE FMT_END_NAMESPACE
#ifdef _MSC_VER #ifdef _MSC_VER
# pragma warning(pop) # pragma warning(pop)
#endif #endif
#endif // FMT_FORMAT_INL_H_ #endif // FMT_FORMAT_INL_H_

File diff suppressed because it is too large Load Diff

View File

@ -8,63 +8,65 @@
#ifndef FMT_LOCALE_H_ #ifndef FMT_LOCALE_H_
#define FMT_LOCALE_H_ #define FMT_LOCALE_H_
#include "format.h"
#include <locale> #include <locale>
#include "format.h"
FMT_BEGIN_NAMESPACE FMT_BEGIN_NAMESPACE
namespace internal { namespace internal {
template <typename Char> template <typename Char>
typename buffer_context<Char>::type::iterator vformat_to( typename buffer_context<Char>::type::iterator vformat_to(
const std::locale &loc, basic_buffer<Char> &buf, const std::locale& loc, basic_buffer<Char>& buf,
basic_string_view<Char> format_str, basic_string_view<Char> format_str,
basic_format_args<typename buffer_context<Char>::type> args) { basic_format_args<typename buffer_context<Char>::type> args) {
typedef back_insert_range<basic_buffer<Char> > range; typedef back_insert_range<basic_buffer<Char>> range;
return vformat_to<arg_formatter<range>>( return vformat_to<arg_formatter<range>>(buf, to_string_view(format_str), args,
buf, to_string_view(format_str), args, internal::locale_ref(loc)); internal::locale_ref(loc));
} }
template <typename Char> template <typename Char>
std::basic_string<Char> vformat( std::basic_string<Char> vformat(
const std::locale &loc, basic_string_view<Char> format_str, const std::locale& loc, basic_string_view<Char> format_str,
basic_format_args<typename buffer_context<Char>::type> args) { basic_format_args<typename buffer_context<Char>::type> args) {
basic_memory_buffer<Char> buffer; basic_memory_buffer<Char> buffer;
internal::vformat_to(loc, buffer, format_str, args); internal::vformat_to(loc, buffer, format_str, args);
return fmt::to_string(buffer); return fmt::to_string(buffer);
} }
} } // namespace internal
template <typename S, typename Char = FMT_CHAR(S)> template <typename S, typename Char = FMT_CHAR(S)>
inline std::basic_string<Char> vformat( inline std::basic_string<Char> vformat(
const std::locale &loc, const S &format_str, const std::locale& loc, const S& format_str,
basic_format_args<typename buffer_context<Char>::type> args) { basic_format_args<typename buffer_context<Char>::type> args) {
return internal::vformat(loc, to_string_view(format_str), args); return internal::vformat(loc, to_string_view(format_str), args);
} }
template <typename S, typename... Args> template <typename S, typename... Args>
inline std::basic_string<FMT_CHAR(S)> format( inline std::basic_string<FMT_CHAR(S)> format(const std::locale& loc,
const std::locale &loc, const S &format_str, const Args &... args) { const S& format_str,
const Args&... args) {
return internal::vformat( return internal::vformat(
loc, to_string_view(format_str), loc, to_string_view(format_str),
*internal::checked_args<S, Args...>(format_str, args...)); *internal::checked_args<S, Args...>(format_str, args...));
} }
template <typename String, typename OutputIt, typename... Args> template <typename String, typename OutputIt, typename... Args>
inline typename std::enable_if<internal::is_output_iterator<OutputIt>::value, inline typename std::enable_if<internal::is_output_iterator<OutputIt>::value,
OutputIt>::type OutputIt>::type
vformat_to(OutputIt out, const std::locale &loc, const String &format_str, vformat_to(OutputIt out, const std::locale& loc, const String& format_str,
typename format_args_t<OutputIt, FMT_CHAR(String)>::type args) { typename format_args_t<OutputIt, FMT_CHAR(String)>::type args) {
typedef output_range<OutputIt, FMT_CHAR(String)> range; typedef output_range<OutputIt, FMT_CHAR(String)> range;
return vformat_to<arg_formatter<range>>( return vformat_to<arg_formatter<range>>(
range(out), to_string_view(format_str), args, internal::locale_ref(loc)); range(out), to_string_view(format_str), args, internal::locale_ref(loc));
} }
template <typename OutputIt, typename S, typename... Args> template <typename OutputIt, typename S, typename... Args>
inline typename std::enable_if< inline
internal::is_string<S>::value && typename std::enable_if<internal::is_string<S>::value &&
internal::is_output_iterator<OutputIt>::value, OutputIt>::type internal::is_output_iterator<OutputIt>::value,
format_to(OutputIt out, const std::locale &loc, const S &format_str, OutputIt>::type
const Args &... args) { format_to(OutputIt out, const std::locale& loc, const S& format_str,
const Args&... args) {
internal::check_format_string<Args...>(format_str); internal::check_format_string<Args...>(format_str);
typedef typename format_context_t<OutputIt, FMT_CHAR(S)>::type context; typedef typename format_context_t<OutputIt, FMT_CHAR(S)>::type context;
format_arg_store<context, Args...> as{args...}; format_arg_store<context, Args...> as{args...};

View File

@ -8,22 +8,21 @@
#ifndef FMT_OSTREAM_H_ #ifndef FMT_OSTREAM_H_
#define FMT_OSTREAM_H_ #define FMT_OSTREAM_H_
#include "format.h"
#include <ostream> #include <ostream>
#include "format.h"
FMT_BEGIN_NAMESPACE FMT_BEGIN_NAMESPACE
namespace internal { namespace internal {
template <class Char> template <class Char> class formatbuf : public std::basic_streambuf<Char> {
class formatbuf : public std::basic_streambuf<Char> {
private: private:
typedef typename std::basic_streambuf<Char>::int_type int_type; typedef typename std::basic_streambuf<Char>::int_type int_type;
typedef typename std::basic_streambuf<Char>::traits_type traits_type; typedef typename std::basic_streambuf<Char>::traits_type traits_type;
basic_buffer<Char> &buffer_; basic_buffer<Char>& buffer_;
public: public:
formatbuf(basic_buffer<Char> &buffer) : buffer_(buffer) {} formatbuf(basic_buffer<Char>& buffer) : buffer_(buffer) {}
protected: protected:
// The put-area is actually always empty. This makes the implementation // The put-area is actually always empty. This makes the implementation
@ -39,31 +38,30 @@ class formatbuf : public std::basic_streambuf<Char> {
return ch; return ch;
} }
std::streamsize xsputn(const Char *s, std::streamsize count) FMT_OVERRIDE { std::streamsize xsputn(const Char* s, std::streamsize count) FMT_OVERRIDE {
buffer_.append(s, s + count); buffer_.append(s, s + count);
return count; return count;
} }
}; };
template <typename Char> template <typename Char> struct test_stream : std::basic_ostream<Char> {
struct test_stream : std::basic_ostream<Char> {
private: private:
struct null; struct null;
// Hide all operator<< from std::basic_ostream<Char>. // Hide all operator<< from std::basic_ostream<Char>.
void operator<<(null); void operator<<(null);
}; };
// Checks if T has a user-defined operator<< (e.g. not a member of std::ostream). // Checks if T has a user-defined operator<< (e.g. not a member of
template <typename T, typename Char> // std::ostream).
class is_streamable { template <typename T, typename Char> class is_streamable {
private: private:
template <typename U> template <typename U>
static decltype( static decltype(internal::declval<test_stream<Char>&>()
internal::declval<test_stream<Char>&>() << internal::declval<U>(),
<< internal::declval<U>(), std::true_type()) test(int); std::true_type())
test(int);
template <typename> template <typename> static std::false_type test(...);
static std::false_type test(...);
typedef decltype(test<T>(0)) result; typedef decltype(test<T>(0)) result;
@ -73,8 +71,8 @@ class is_streamable {
// Write the content of buf to os. // Write the content of buf to os.
template <typename Char> template <typename Char>
void write(std::basic_ostream<Char> &os, basic_buffer<Char> &buf) { void write(std::basic_ostream<Char>& os, basic_buffer<Char>& buf) {
const Char *data = buf.data(); const Char* data = buf.data();
typedef std::make_unsigned<std::streamsize>::type UnsignedStreamSize; typedef std::make_unsigned<std::streamsize>::type UnsignedStreamSize;
UnsignedStreamSize size = buf.size(); UnsignedStreamSize size = buf.size();
UnsignedStreamSize max_size = UnsignedStreamSize max_size =
@ -88,7 +86,7 @@ void write(std::basic_ostream<Char> &os, basic_buffer<Char> &buf) {
} }
template <typename Char, typename T> template <typename Char, typename T>
void format_value(basic_buffer<Char> &buffer, const T &value) { void format_value(basic_buffer<Char>& buffer, const T& value) {
internal::formatbuf<Char> format_buf(buffer); internal::formatbuf<Char> format_buf(buffer);
std::basic_ostream<Char> output(&format_buf); std::basic_ostream<Char> output(&format_buf);
output.exceptions(std::ios_base::failbit | std::ios_base::badbit); output.exceptions(std::ios_base::failbit | std::ios_base::badbit);
@ -99,24 +97,21 @@ void format_value(basic_buffer<Char> &buffer, const T &value) {
// Disable conversion to int if T has an overloaded operator<< which is a free // Disable conversion to int if T has an overloaded operator<< which is a free
// function (not a member of std::ostream). // function (not a member of std::ostream).
template <typename T, typename Char> template <typename T, typename Char> struct convert_to_int<T, Char, void> {
struct convert_to_int<T, Char, void> { static const bool value = convert_to_int<T, Char, int>::value &&
static const bool value = !internal::is_streamable<T, Char>::value;
convert_to_int<T, Char, int>::value &&
!internal::is_streamable<T, Char>::value;
}; };
// Formats an object of type T that has an overloaded ostream operator<<. // Formats an object of type T that has an overloaded ostream operator<<.
template <typename T, typename Char> template <typename T, typename Char>
struct formatter<T, Char, struct formatter<T, Char,
typename std::enable_if< typename std::enable_if<
internal::is_streamable<T, Char>::value && internal::is_streamable<T, Char>::value &&
!internal::format_type< !internal::format_type<typename buffer_context<Char>::type,
typename buffer_context<Char>::type, T>::value>::type> T>::value>::type>
: formatter<basic_string_view<Char>, Char> { : formatter<basic_string_view<Char>, Char> {
template <typename Context> template <typename Context>
auto format(const T &value, Context &ctx) -> decltype(ctx.out()) { auto format(const T& value, Context& ctx) -> decltype(ctx.out()) {
basic_memory_buffer<Char> buffer; basic_memory_buffer<Char> buffer;
internal::format_value(buffer, value); internal::format_value(buffer, value);
basic_string_view<Char> str(buffer.data(), buffer.size()); basic_string_view<Char> str(buffer.data(), buffer.size());
@ -125,9 +120,9 @@ struct formatter<T, Char,
}; };
template <typename Char> template <typename Char>
inline void vprint(std::basic_ostream<Char> &os, inline void vprint(
basic_string_view<Char> format_str, std::basic_ostream<Char>& os, basic_string_view<Char> format_str,
basic_format_args<typename buffer_context<Char>::type> args) { basic_format_args<typename buffer_context<Char>::type> args) {
basic_memory_buffer<Char> buffer; basic_memory_buffer<Char> buffer;
internal::vformat_to(buffer, format_str, args); internal::vformat_to(buffer, format_str, args);
internal::write(os, buffer); internal::write(os, buffer);
@ -142,9 +137,9 @@ inline void vprint(std::basic_ostream<Char> &os,
\endrst \endrst
*/ */
template <typename S, typename... Args> template <typename S, typename... Args>
inline typename std::enable_if<internal::is_string<S>::value>::type inline typename std::enable_if<internal::is_string<S>::value>::type print(
print(std::basic_ostream<FMT_CHAR(S)> &os, const S &format_str, std::basic_ostream<FMT_CHAR(S)>& os, const S& format_str,
const Args & ... args) { const Args&... args) {
internal::checked_args<S, Args...> ca(format_str, args...); internal::checked_args<S, Args...> ca(format_str, args...);
vprint(os, to_string_view(format_str), *ca); vprint(os, to_string_view(format_str), *ca);
} }

View File

@ -10,7 +10,7 @@
#if defined(__MINGW32__) || defined(__CYGWIN__) #if defined(__MINGW32__) || defined(__CYGWIN__)
// Workaround MinGW bug https://sourceforge.net/p/mingw/bugs/2024/. // Workaround MinGW bug https://sourceforge.net/p/mingw/bugs/2024/.
# undef __STRICT_ANSI__ # undef __STRICT_ANSI__
#endif #endif
#include <errno.h> #include <errno.h>
@ -22,42 +22,42 @@
#include <cstddef> #include <cstddef>
#if defined __APPLE__ || defined(__FreeBSD__) #if defined __APPLE__ || defined(__FreeBSD__)
# include <xlocale.h> // for LC_NUMERIC_MASK on OS X # include <xlocale.h> // for LC_NUMERIC_MASK on OS X
#endif #endif
#include "format.h" #include "format.h"
#ifndef FMT_POSIX #ifndef FMT_POSIX
# if defined(_WIN32) && !defined(__MINGW32__) # if defined(_WIN32) && !defined(__MINGW32__)
// Fix warnings about deprecated symbols. // Fix warnings about deprecated symbols.
# define FMT_POSIX(call) _##call # define FMT_POSIX(call) _##call
# else # else
# define FMT_POSIX(call) call # define FMT_POSIX(call) call
# endif # endif
#endif #endif
// Calls to system functions are wrapped in FMT_SYSTEM for testability. // Calls to system functions are wrapped in FMT_SYSTEM for testability.
#ifdef FMT_SYSTEM #ifdef FMT_SYSTEM
# define FMT_POSIX_CALL(call) FMT_SYSTEM(call) # define FMT_POSIX_CALL(call) FMT_SYSTEM(call)
#else #else
# define FMT_SYSTEM(call) call # define FMT_SYSTEM(call) call
# ifdef _WIN32 # ifdef _WIN32
// Fix warnings about deprecated symbols. // Fix warnings about deprecated symbols.
# define FMT_POSIX_CALL(call) ::_##call # define FMT_POSIX_CALL(call) ::_##call
# else # else
# define FMT_POSIX_CALL(call) ::call # define FMT_POSIX_CALL(call) ::call
# endif # endif
#endif #endif
// Retries the expression while it evaluates to error_result and errno // Retries the expression while it evaluates to error_result and errno
// equals to EINTR. // equals to EINTR.
#ifndef _WIN32 #ifndef _WIN32
# define FMT_RETRY_VAL(result, expression, error_result) \ # define FMT_RETRY_VAL(result, expression, error_result) \
do { \ do { \
result = (expression); \ result = (expression); \
} while (result == error_result && errno == EINTR) } while (result == error_result && errno == EINTR)
#else #else
# define FMT_RETRY_VAL(result, expression, error_result) result = (expression) # define FMT_RETRY_VAL(result, expression, error_result) result = (expression)
#endif #endif
#define FMT_RETRY(result, expression) FMT_RETRY_VAL(result, expression, -1) #define FMT_RETRY(result, expression) FMT_RETRY_VAL(result, expression, -1)
@ -89,24 +89,23 @@ FMT_BEGIN_NAMESPACE
format(std::string("{}"), 42); format(std::string("{}"), 42);
\endrst \endrst
*/ */
template <typename Char> template <typename Char> class basic_cstring_view {
class basic_cstring_view {
private: private:
const Char *data_; const Char* data_;
public: public:
/** Constructs a string reference object from a C string. */ /** Constructs a string reference object from a C string. */
basic_cstring_view(const Char *s) : data_(s) {} basic_cstring_view(const Char* s) : data_(s) {}
/** /**
\rst \rst
Constructs a string reference from an ``std::string`` object. Constructs a string reference from an ``std::string`` object.
\endrst \endrst
*/ */
basic_cstring_view(const std::basic_string<Char> &s) : data_(s.c_str()) {} basic_cstring_view(const std::basic_string<Char>& s) : data_(s.c_str()) {}
/** Returns the pointer to a C string. */ /** Returns the pointer to a C string. */
const Char *c_str() const { return data_; } const Char* c_str() const { return data_; }
}; };
typedef basic_cstring_view<char> cstring_view; typedef basic_cstring_view<char> cstring_view;
@ -126,11 +125,11 @@ class error_code {
// A buffered file. // A buffered file.
class buffered_file { class buffered_file {
private: private:
FILE *file_; FILE* file_;
friend class file; friend class file;
explicit buffered_file(FILE *f) : file_(f) {} explicit buffered_file(FILE* f) : file_(f) {}
public: public:
// Constructs a buffered_file object which doesn't represent any file. // Constructs a buffered_file object which doesn't represent any file.
@ -140,16 +139,15 @@ class buffered_file {
FMT_API ~buffered_file() FMT_NOEXCEPT; FMT_API ~buffered_file() FMT_NOEXCEPT;
private: private:
buffered_file(const buffered_file &) = delete; buffered_file(const buffered_file&) = delete;
void operator=(const buffered_file &) = delete; void operator=(const buffered_file&) = delete;
public: public:
buffered_file(buffered_file &&other) FMT_NOEXCEPT : file_(other.file_) { buffered_file(buffered_file&& other) FMT_NOEXCEPT : file_(other.file_) {
other.file_ = FMT_NULL; other.file_ = FMT_NULL;
} }
buffered_file& operator=(buffered_file &&other) { buffered_file& operator=(buffered_file&& other) {
close(); close();
file_ = other.file_; file_ = other.file_;
other.file_ = FMT_NULL; other.file_ = FMT_NULL;
@ -163,18 +161,18 @@ class buffered_file {
FMT_API void close(); FMT_API void close();
// Returns the pointer to a FILE object representing this file. // Returns the pointer to a FILE object representing this file.
FILE *get() const FMT_NOEXCEPT { return file_; } FILE* get() const FMT_NOEXCEPT { return file_; }
// We place parentheses around fileno to workaround a bug in some versions // We place parentheses around fileno to workaround a bug in some versions
// of MinGW that define fileno as a macro. // of MinGW that define fileno as a macro.
FMT_API int (fileno)() const; FMT_API int(fileno)() const;
void vprint(string_view format_str, format_args args) { void vprint(string_view format_str, format_args args) {
fmt::vprint(file_, format_str, args); fmt::vprint(file_, format_str, args);
} }
template <typename... Args> template <typename... Args>
inline void print(string_view format_str, const Args & ... args) { inline void print(string_view format_str, const Args&... args) {
vprint(format_str, make_format_args(args...)); vprint(format_str, make_format_args(args...));
} }
}; };
@ -195,9 +193,9 @@ class file {
public: public:
// Possible values for the oflag argument to the constructor. // Possible values for the oflag argument to the constructor.
enum { enum {
RDONLY = FMT_POSIX(O_RDONLY), // Open for reading only. RDONLY = FMT_POSIX(O_RDONLY), // Open for reading only.
WRONLY = FMT_POSIX(O_WRONLY), // Open for writing only. WRONLY = FMT_POSIX(O_WRONLY), // Open for writing only.
RDWR = FMT_POSIX(O_RDWR) // Open for reading and writing. RDWR = FMT_POSIX(O_RDWR) // Open for reading and writing.
}; };
// Constructs a file object which doesn't represent any file. // Constructs a file object which doesn't represent any file.
@ -207,15 +205,13 @@ class file {
FMT_API file(cstring_view path, int oflag); FMT_API file(cstring_view path, int oflag);
private: private:
file(const file &) = delete; file(const file&) = delete;
void operator=(const file &) = delete; void operator=(const file&) = delete;
public: public:
file(file &&other) FMT_NOEXCEPT : fd_(other.fd_) { file(file&& other) FMT_NOEXCEPT : fd_(other.fd_) { other.fd_ = -1; }
other.fd_ = -1;
}
file& operator=(file &&other) { file& operator=(file&& other) {
close(); close();
fd_ = other.fd_; fd_ = other.fd_;
other.fd_ = -1; other.fd_ = -1;
@ -236,10 +232,10 @@ class file {
FMT_API long long size() const; FMT_API long long size() const;
// Attempts to read count bytes from the file into the specified buffer. // Attempts to read count bytes from the file into the specified buffer.
FMT_API std::size_t read(void *buffer, std::size_t count); FMT_API std::size_t read(void* buffer, std::size_t count);
// Attempts to write count bytes from the specified buffer to the file. // Attempts to write count bytes from the specified buffer to the file.
FMT_API std::size_t write(const void *buffer, std::size_t count); FMT_API std::size_t write(const void* buffer, std::size_t count);
// Duplicates a file descriptor with the dup function and returns // Duplicates a file descriptor with the dup function and returns
// the duplicate as a file object. // the duplicate as a file object.
@ -251,59 +247,56 @@ class file {
// Makes fd be the copy of this file descriptor, closing fd first if // Makes fd be the copy of this file descriptor, closing fd first if
// necessary. // necessary.
FMT_API void dup2(int fd, error_code &ec) FMT_NOEXCEPT; FMT_API void dup2(int fd, error_code& ec) FMT_NOEXCEPT;
// Creates a pipe setting up read_end and write_end file objects for reading // Creates a pipe setting up read_end and write_end file objects for reading
// and writing respectively. // and writing respectively.
FMT_API static void pipe(file &read_end, file &write_end); FMT_API static void pipe(file& read_end, file& write_end);
// Creates a buffered_file object associated with this file and detaches // Creates a buffered_file object associated with this file and detaches
// this file object from the file. // this file object from the file.
FMT_API buffered_file fdopen(const char *mode); FMT_API buffered_file fdopen(const char* mode);
}; };
// Returns the memory page size. // Returns the memory page size.
long getpagesize(); long getpagesize();
#if (defined(LC_NUMERIC_MASK) || defined(_MSC_VER)) && \ #if (defined(LC_NUMERIC_MASK) || defined(_MSC_VER)) && \
!defined(__ANDROID__) && !defined(__CYGWIN__) && !defined(__OpenBSD__) && \ !defined(__ANDROID__) && !defined(__CYGWIN__) && !defined(__OpenBSD__) && \
!defined(__NEWLIB_H__) !defined(__NEWLIB_H__)
# define FMT_LOCALE # define FMT_LOCALE
#endif #endif
#ifdef FMT_LOCALE #ifdef FMT_LOCALE
// A "C" numeric locale. // A "C" numeric locale.
class Locale { class Locale {
private: private:
# ifdef _MSC_VER # ifdef _MSC_VER
typedef _locale_t locale_t; typedef _locale_t locale_t;
enum { LC_NUMERIC_MASK = LC_NUMERIC }; enum { LC_NUMERIC_MASK = LC_NUMERIC };
static locale_t newlocale(int category_mask, const char *locale, locale_t) { static locale_t newlocale(int category_mask, const char* locale, locale_t) {
return _create_locale(category_mask, locale); return _create_locale(category_mask, locale);
} }
static void freelocale(locale_t locale) { static void freelocale(locale_t locale) { _free_locale(locale); }
_free_locale(locale);
}
static double strtod_l(const char *nptr, char **endptr, _locale_t locale) { static double strtod_l(const char* nptr, char** endptr, _locale_t locale) {
return _strtod_l(nptr, endptr, locale); return _strtod_l(nptr, endptr, locale);
} }
# endif # endif
locale_t locale_; locale_t locale_;
Locale(const Locale &) = delete; Locale(const Locale&) = delete;
void operator=(const Locale &) = delete; void operator=(const Locale&) = delete;
public: public:
typedef locale_t Type; typedef locale_t Type;
Locale() : locale_(newlocale(LC_NUMERIC_MASK, "C", FMT_NULL)) { Locale() : locale_(newlocale(LC_NUMERIC_MASK, "C", FMT_NULL)) {
if (!locale_) if (!locale_) FMT_THROW(system_error(errno, "cannot create locale"));
FMT_THROW(system_error(errno, "cannot create locale"));
} }
~Locale() { freelocale(locale_); } ~Locale() { freelocale(locale_); }
@ -311,8 +304,8 @@ class Locale {
// Converts string to floating-point number and advances str past the end // Converts string to floating-point number and advances str past the end
// of the parsed input. // of the parsed input.
double strtod(const char *&str) const { double strtod(const char*& str) const {
char *end = FMT_NULL; char* end = FMT_NULL;
double result = strtod_l(str, &end, locale_); double result = strtod_l(str, &end, locale_);
str = end; str = end;
return result; return result;

View File

@ -9,8 +9,8 @@
#define FMT_PREPARE_H_ #define FMT_PREPARE_H_
#ifndef FMT_HAS_CONSTRUCTIBLE_TRAITS #ifndef FMT_HAS_CONSTRUCTIBLE_TRAITS
# define FMT_HAS_CONSTRUCTIBLE_TRAITS \ # define FMT_HAS_CONSTRUCTIBLE_TRAITS \
(FMT_GCC_VERSION >= 407 || FMT_CLANG_VERSION || FMT_MSC_VER) (FMT_GCC_VERSION >= 407 || FMT_CLANG_VERSION || FMT_MSC_VER)
#endif #endif
#include "format.h" #include "format.h"
@ -73,7 +73,8 @@ template <typename Char> struct format_part {
: which(which_value::argument_id), end_of_argument_id(0u), val(id) {} : which(which_value::argument_id), end_of_argument_id(0u), val(id) {}
FMT_CONSTEXPR format_part(named_argument_id arg_id) FMT_CONSTEXPR format_part(named_argument_id arg_id)
: which(which_value::named_argument_id), end_of_argument_id(0u), : which(which_value::named_argument_id),
end_of_argument_id(0u),
val(arg_id) {} val(arg_id) {}
FMT_CONSTEXPR format_part(specification spec) FMT_CONSTEXPR format_part(specification spec)
@ -99,23 +100,24 @@ template <typename Char> struct format_part {
internal::string_view_metadata named_arg_id; internal::string_view_metadata named_arg_id;
internal::string_view_metadata text; internal::string_view_metadata text;
specification spec; specification spec;
} val; }
val;
}; };
namespace internal { namespace internal {
template <typename Char, typename PartsContainer> template <typename Char, typename PartsContainer>
class format_preparation_handler : public internal::error_handler { class format_preparation_handler : public internal::error_handler {
private: private:
typedef format_part<Char> part; typedef format_part<Char> part;
public: public:
typedef internal::null_terminating_iterator<Char> iterator; typedef internal::null_terminating_iterator<Char> iterator;
FMT_CONSTEXPR format_preparation_handler(basic_string_view<Char> format, FMT_CONSTEXPR format_preparation_handler(basic_string_view<Char> format,
PartsContainer &parts) PartsContainer& parts)
: parts_(parts), format_(format), parse_context_(format) {} : parts_(parts), format_(format), parse_context_(format) {}
FMT_CONSTEXPR void on_text(const Char *begin, const Char *end) { FMT_CONSTEXPR void on_text(const Char* begin, const Char* end) {
if (begin == end) { if (begin == end) {
return; return;
} }
@ -139,20 +141,19 @@ public:
parts_.add(part(arg_id)); parts_.add(part(arg_id));
} }
FMT_CONSTEXPR void on_replacement_field(const Char *ptr) { FMT_CONSTEXPR void on_replacement_field(const Char* ptr) {
auto last_part = parts_.last(); auto last_part = parts_.last();
last_part.end_of_argument_id = ptr - format_.begin(); last_part.end_of_argument_id = ptr - format_.begin();
parts_.substitute_last(last_part); parts_.substitute_last(last_part);
} }
FMT_CONSTEXPR const Char *on_format_specs(const Char *begin, FMT_CONSTEXPR const Char* on_format_specs(const Char* begin,
const Char *end) { const Char* end) {
const auto specs_offset = to_unsigned(begin - format_.begin()); const auto specs_offset = to_unsigned(begin - format_.begin());
typedef basic_parse_context<Char> parse_context; typedef basic_parse_context<Char> parse_context;
internal::dynamic_format_specs<Char> parsed_specs; internal::dynamic_format_specs<Char> parsed_specs;
dynamic_specs_handler<parse_context> handler( dynamic_specs_handler<parse_context> handler(parsed_specs, parse_context_);
parsed_specs, parse_context_);
begin = parse_format_specs(begin, end, handler); begin = parse_format_specs(begin, end, handler);
if (*begin != '}') { if (*begin != '}') {
@ -176,7 +177,7 @@ public:
} }
private: private:
PartsContainer &parts_; PartsContainer& parts_;
basic_string_view<Char> format_; basic_string_view<Char> format_;
basic_parse_context<Char> parse_context_; basic_parse_context<Char> parse_context_;
}; };
@ -193,7 +194,7 @@ class prepared_format {
prepared_format() = delete; prepared_format() = delete;
std::size_t formatted_size(const Args &... args) const { std::size_t formatted_size(const Args&... args) const {
const auto it = this->format_to(counting_iterator<char_type>(), args...); const auto it = this->format_to(counting_iterator<char_type>(), args...);
return it.count(); return it.count();
} }
@ -201,10 +202,10 @@ class prepared_format {
template <typename OutputIt> template <typename OutputIt>
inline typename std::enable_if<internal::is_output_iterator<OutputIt>::value, inline typename std::enable_if<internal::is_output_iterator<OutputIt>::value,
format_to_n_result<OutputIt>>::type format_to_n_result<OutputIt>>::type
format_to_n(OutputIt out, unsigned n, const Args &... args) const { format_to_n(OutputIt out, unsigned n, const Args&... args) const {
format_arg_store<typename format_to_n_context<OutputIt, char_type>::type, format_arg_store<typename format_to_n_context<OutputIt, char_type>::type,
Args...> Args...>
as(args...); as(args...);
typedef truncating_iterator<OutputIt> trunc_it; typedef truncating_iterator<OutputIt> trunc_it;
typedef output_range<trunc_it, char_type> range; typedef output_range<trunc_it, char_type> range;
@ -214,7 +215,7 @@ class prepared_format {
return {it.base(), it.count()}; return {it.base(), it.count()};
} }
std::basic_string<char_type> format(const Args &... args) const { std::basic_string<char_type> format(const Args&... args) const {
basic_memory_buffer<char_type> buffer; basic_memory_buffer<char_type> buffer;
typedef back_insert_range<internal::basic_buffer<char_type>> range; typedef back_insert_range<internal::basic_buffer<char_type>> range;
this->vformat_to(range(buffer), *checked_args(format_, args...)); this->vformat_to(range(buffer), *checked_args(format_, args...));
@ -225,7 +226,7 @@ class prepared_format {
inline typename std::enable_if<is_contiguous<Container>::value, inline typename std::enable_if<is_contiguous<Container>::value,
std::back_insert_iterator<Container>>::type std::back_insert_iterator<Container>>::type
format_to(std::back_insert_iterator<Container> out, format_to(std::back_insert_iterator<Container> out,
const Args &... args) const { const Args&... args) const {
internal::container_buffer<Container> buffer(internal::get_container(out)); internal::container_buffer<Container> buffer(internal::get_container(out));
typedef back_insert_range<internal::basic_buffer<char_type>> range; typedef back_insert_range<internal::basic_buffer<char_type>> range;
this->vformat_to(range(buffer), *checked_args(format_, args...)); this->vformat_to(range(buffer), *checked_args(format_, args...));
@ -233,7 +234,7 @@ class prepared_format {
} }
template <typename OutputIt> template <typename OutputIt>
inline OutputIt format_to(OutputIt out, const Args &... args) const { inline OutputIt format_to(OutputIt out, const Args&... args) const {
typedef typename format_context_t<OutputIt, char_type>::type context; typedef typename format_context_t<OutputIt, char_type>::type context;
typedef output_range<OutputIt, char_type> range; typedef output_range<OutputIt, char_type> range;
format_arg_store<context, Args...> as(args...); format_arg_store<context, Args...> as(args...);
@ -241,9 +242,8 @@ class prepared_format {
} }
template <std::size_t SIZE = inline_buffer_size> template <std::size_t SIZE = inline_buffer_size>
inline typename buffer_context<char_type>::type::iterator inline typename buffer_context<char_type>::type::iterator format_to(
format_to(basic_memory_buffer<char_type, SIZE> &buf, basic_memory_buffer<char_type, SIZE>& buf, const Args&... args) const {
const Args &... args) const {
typedef back_insert_range<internal::basic_buffer<char_type>> range; typedef back_insert_range<internal::basic_buffer<char_type>> range;
return this->vformat_to(range(buf), *checked_args(format_, args...)); return this->vformat_to(range(buf), *checked_args(format_, args...));
} }
@ -255,16 +255,16 @@ class prepared_format {
const auto format_view = internal::to_string_view(format_); const auto format_view = internal::to_string_view(format_);
Context ctx(out.begin(), format_view, args); Context ctx(out.begin(), format_view, args);
const auto &parts = parts_provider_.parts(); const auto& parts = parts_provider_.parts();
for (auto part_it = parts.begin(); part_it != parts.end(); ++part_it) { for (auto part_it = parts.begin(); part_it != parts.end(); ++part_it) {
const auto &part = *part_it; const auto& part = *part_it;
const auto &value = part.val; const auto& value = part.val;
switch (part.which) { switch (part.which) {
case format_part_t::which_value::text: { case format_part_t::which_value::text: {
const auto text = value.text.to_view(format_view); const auto text = value.text.to_view(format_view);
auto out = ctx.out(); auto out = ctx.out();
auto &&it = internal::reserve(out, text.size()); auto&& it = internal::reserve(out, text.size());
it = std::copy_n(text.begin(), text.size(), it); it = std::copy_n(text.begin(), text.size(), it);
ctx.advance_to(out); ctx.advance_to(out);
} break; } break;
@ -280,7 +280,7 @@ class prepared_format {
format_arg<Range>(ctx, named_arg_id); format_arg<Range>(ctx, named_arg_id);
} break; } break;
case format_part_t::which_value::specification: { case format_part_t::which_value::specification: {
const auto &arg_id_value = value.spec.arg_id.val; const auto& arg_id_value = value.spec.arg_id.val;
const auto arg = const auto arg =
value.spec.arg_id.which == value.spec.arg_id.which ==
format_part_t::argument_id::which_arg_id::index format_part_t::argument_id::which_arg_id::index
@ -306,15 +306,15 @@ class prepared_format {
} }
template <typename Context> template <typename Context>
void advance_parse_context_to_specification(Context &ctx, void advance_parse_context_to_specification(Context& ctx,
const format_part_t &part) const { const format_part_t& part) const {
const auto view = to_string_view(format_); const auto view = to_string_view(format_);
const auto specification_begin = view.data() + part.end_of_argument_id; const auto specification_begin = view.data() + part.end_of_argument_id;
ctx.parse_context().advance_to(specification_begin); ctx.parse_context().advance_to(specification_begin);
} }
template <typename Range, typename Context, typename Id> template <typename Range, typename Context, typename Id>
void format_arg(Context &ctx, Id arg_id) const { void format_arg(Context& ctx, Id arg_id) const {
ctx.parse_context().check_arg_id(arg_id); ctx.parse_context().check_arg_id(arg_id);
const auto stopped_at = const auto stopped_at =
visit_format_arg(arg_formatter<Range>(ctx), ctx.get_arg(arg_id)); visit_format_arg(arg_formatter<Range>(ctx), ctx.get_arg(arg_id));
@ -322,8 +322,8 @@ class prepared_format {
} }
template <typename Char> template <typename Char>
void check_prepared_specs( void check_prepared_specs(const basic_format_specs<Char>& specs,
const basic_format_specs<Char> &specs, internal::type arg_type) const { internal::type arg_type) const {
internal::error_handler h; internal::error_handler h;
numeric_specs_checker<internal::error_handler> checker(h, arg_type); numeric_specs_checker<internal::error_handler> checker(h, arg_type);
if (specs.align_ == ALIGN_NUMERIC) { if (specs.align_ == ALIGN_NUMERIC) {
@ -343,23 +343,23 @@ class prepared_format {
} }
} }
private: private:
Format format_; Format format_;
PreparedPartsProvider parts_provider_; PreparedPartsProvider parts_provider_;
}; };
template <typename Format> class compiletime_prepared_parts_type_provider { template <typename Format> class compiletime_prepared_parts_type_provider {
private: private:
typedef FMT_CHAR(Format) char_type; typedef FMT_CHAR(Format) char_type;
class count_handler { class count_handler {
private: private:
typedef internal::null_terminating_iterator<char_type> iterator; typedef internal::null_terminating_iterator<char_type> iterator;
public: public:
FMT_CONSTEXPR count_handler() : counter_(0u) {} FMT_CONSTEXPR count_handler() : counter_(0u) {}
FMT_CONSTEXPR void on_text(const char_type *begin, const char_type *end) { FMT_CONSTEXPR void on_text(const char_type* begin, const char_type* end) {
if (begin != end) { if (begin != end) {
++counter_; ++counter_;
} }
@ -369,20 +369,20 @@ private:
FMT_CONSTEXPR void on_arg_id(unsigned) { ++counter_; } FMT_CONSTEXPR void on_arg_id(unsigned) { ++counter_; }
FMT_CONSTEXPR void on_arg_id(basic_string_view<char_type>) { ++counter_; } FMT_CONSTEXPR void on_arg_id(basic_string_view<char_type>) { ++counter_; }
FMT_CONSTEXPR void on_replacement_field(const char_type *) {} FMT_CONSTEXPR void on_replacement_field(const char_type*) {}
FMT_CONSTEXPR const char_type *on_format_specs(const char_type *begin, FMT_CONSTEXPR const char_type* on_format_specs(const char_type* begin,
const char_type *end) { const char_type* end) {
return find_matching_brace(begin, end); return find_matching_brace(begin, end);
} }
FMT_CONSTEXPR void on_error(const char *) {} FMT_CONSTEXPR void on_error(const char*) {}
FMT_CONSTEXPR unsigned result() const { return counter_; } FMT_CONSTEXPR unsigned result() const { return counter_; }
private: private:
FMT_CONSTEXPR const char_type *find_matching_brace(const char_type *begin, FMT_CONSTEXPR const char_type* find_matching_brace(const char_type* begin,
const char_type *end) { const char_type* end) {
FMT_CONSTEXPR_DECL const basic_string_view<char_type> text = Format{}; FMT_CONSTEXPR_DECL const basic_string_view<char_type> text = Format{};
unsigned braces_counter{0u}; unsigned braces_counter{0u};
for (; begin != end; ++begin) { for (; begin != end; ++begin) {
@ -399,7 +399,7 @@ private:
return begin; return begin;
} }
private: private:
unsigned counter_; unsigned counter_;
}; };
@ -425,13 +425,13 @@ private:
FMT_CONSTEXPR format_parts_array() : arr{} {} FMT_CONSTEXPR format_parts_array() : arr{} {}
FMT_CONSTEXPR value_type &operator[](unsigned ind) { return arr[ind]; } FMT_CONSTEXPR value_type& operator[](unsigned ind) { return arr[ind]; }
FMT_CONSTEXPR const value_type *begin() const { return arr; } FMT_CONSTEXPR const value_type* begin() const { return arr; }
FMT_CONSTEXPR const value_type *end() const { return begin() + N; } FMT_CONSTEXPR const value_type* end() const { return begin() + N; }
private: private:
value_type arr[N]; value_type arr[N];
}; };
@ -450,7 +450,7 @@ template <typename Parts> class compiletime_prepared_parts_collector {
typedef typename Parts::value_type format_part; typedef typename Parts::value_type format_part;
public: public:
FMT_CONSTEXPR explicit compiletime_prepared_parts_collector(Parts &parts) FMT_CONSTEXPR explicit compiletime_prepared_parts_collector(Parts& parts)
: parts_{parts}, counter_{0u} {} : parts_{parts}, counter_{0u} {}
FMT_CONSTEXPR void add(format_part part) { parts_[counter_++] = part; } FMT_CONSTEXPR void add(format_part part) { parts_[counter_++] = part; }
@ -462,7 +462,7 @@ template <typename Parts> class compiletime_prepared_parts_collector {
FMT_CONSTEXPR format_part last() { return parts_[counter_ - 1]; } FMT_CONSTEXPR format_part last() { return parts_[counter_ - 1]; }
private: private:
Parts &parts_; Parts& parts_;
unsigned counter_; unsigned counter_;
}; };
@ -493,7 +493,7 @@ template <typename PartsContainer> class runtime_parts_provider {
runtime_parts_provider(basic_string_view<Char> format) runtime_parts_provider(basic_string_view<Char> format)
: parts_(prepare_parts<PartsContainer>(format)) {} : parts_(prepare_parts<PartsContainer>(format)) {}
const PartsContainer &parts() const { return parts_; } const PartsContainer& parts() const { return parts_; }
private: private:
PartsContainer parts_; PartsContainer parts_;
@ -505,7 +505,7 @@ struct compiletime_parts_provider {
template <typename Char> template <typename Char>
FMT_CONSTEXPR compiletime_parts_provider(basic_string_view<Char>) {} FMT_CONSTEXPR compiletime_parts_provider(basic_string_view<Char>) {}
const PartsContainer &parts() const { const PartsContainer& parts() const {
static FMT_CONSTEXPR_DECL const PartsContainer prepared_parts = static FMT_CONSTEXPR_DECL const PartsContainer prepared_parts =
prepare_compiletime_parts<PartsContainer>( prepare_compiletime_parts<PartsContainer>(
internal::to_string_view(Format{})); internal::to_string_view(Format{}));
@ -608,7 +608,7 @@ std::basic_string<Char> to_runtime_format(basic_string_view<Char> format) {
} }
template <typename Char> template <typename Char>
std::basic_string<Char> to_runtime_format(const Char *format) { std::basic_string<Char> to_runtime_format(const Char* format) {
return std::basic_string<Char>(format); return std::basic_string<Char>(format);
} }
@ -685,17 +685,17 @@ auto do_prepare(runtime_format_tag, Format format) {
} }
template <typename Format, typename... Args> template <typename Format, typename... Args>
FMT_CONSTEXPR auto do_prepare(compiletime_format_tag, const Format &format) { FMT_CONSTEXPR auto do_prepare(compiletime_format_tag, const Format& format) {
return typename basic_prepared_format<Format, void, Args...>::type(format); return typename basic_prepared_format<Format, void, Args...>::type(format);
} }
#else #else
template <typename Format, typename... Args> template <typename Format, typename... Args>
auto do_prepare(const Format &format) auto do_prepare(const Format& format)
-> decltype(preparator<Format, Args...>::prepare(format)) { -> decltype(preparator<Format, Args...>::prepare(format)) {
return preparator<Format, Args...>::prepare(format); return preparator<Format, Args...>::prepare(format);
} }
#endif #endif
} // namespace internal } // namespace internal
template <typename Char, typename Container = std::vector<format_part<Char>>> template <typename Char, typename Container = std::vector<format_part<Char>>>
struct parts_container { struct parts_container {
@ -757,7 +757,7 @@ auto prepare(Format format) ->
#endif #endif
template <typename... Args, typename Char> template <typename... Args, typename Char>
auto prepare(const Char *format) -> auto prepare(const Char* format) ->
typename internal::preparator<std::basic_string<Char>, typename internal::preparator<std::basic_string<Char>,
Args...>::prepared_format_type { Args...>::prepared_format_type {
return prepare<Args...>(internal::to_runtime_format(format)); return prepare<Args...>(internal::to_runtime_format(format));

View File

@ -18,95 +18,91 @@ namespace internal {
// Checks if a value fits in int - used to avoid warnings about comparing // Checks if a value fits in int - used to avoid warnings about comparing
// signed and unsigned integers. // signed and unsigned integers.
template <bool IsSigned> template <bool IsSigned> struct int_checker {
struct int_checker { template <typename T> static bool fits_in_int(T value) {
template <typename T>
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 int_checker<true> {
struct int_checker<true> { template <typename T> static bool fits_in_int(T value) {
template <typename T>
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 printf_precision_handler: public function<int> { class printf_precision_handler : public function<int> {
public: public:
template <typename T> template <typename T>
typename std::enable_if<std::is_integral<T>::value, int>::type typename std::enable_if<std::is_integral<T>::value, int>::type operator()(
operator()(T value) { T value) {
if (!int_checker<std::numeric_limits<T>::is_signed>::fits_in_int(value)) if (!int_checker<std::numeric_limits<T>::is_signed>::fits_in_int(value))
FMT_THROW(format_error("number is too big")); FMT_THROW(format_error("number is too big"));
return static_cast<int>(value); return static_cast<int>(value);
} }
template <typename T> template <typename T>
typename std::enable_if<!std::is_integral<T>::value, int>::type operator()(T) { typename std::enable_if<!std::is_integral<T>::value, int>::type operator()(
T) {
FMT_THROW(format_error("precision is not integer")); FMT_THROW(format_error("precision is not integer"));
return 0; return 0;
} }
}; };
// An argument visitor that returns true iff arg is a zero integer. // An argument visitor that returns true iff arg is a zero integer.
class is_zero_int: public function<bool> { class is_zero_int : public function<bool> {
public: public:
template <typename T> template <typename T>
typename std::enable_if<std::is_integral<T>::value, bool>::type typename std::enable_if<std::is_integral<T>::value, bool>::type operator()(
operator()(T value) { return value == 0; } T value) {
return value == 0;
}
template <typename T> template <typename T>
typename std::enable_if<!std::is_integral<T>::value, bool>::type typename std::enable_if<!std::is_integral<T>::value, bool>::type operator()(
operator()(T) { return false; } T) {
return false;
}
}; };
template <typename T> template <typename T> struct make_unsigned_or_bool : std::make_unsigned<T> {};
struct make_unsigned_or_bool : std::make_unsigned<T> {};
template <> template <> struct make_unsigned_or_bool<bool> { typedef bool type; };
struct make_unsigned_or_bool<bool> {
typedef bool type;
};
template <typename T, typename Context> template <typename T, typename Context>
class arg_converter: public function<void> { class arg_converter : public function<void> {
private: private:
typedef typename Context::char_type Char; typedef typename Context::char_type Char;
basic_format_arg<Context> &arg_; basic_format_arg<Context>& arg_;
typename Context::char_type type_; typename Context::char_type type_;
public: public:
arg_converter(basic_format_arg<Context> &arg, Char type) arg_converter(basic_format_arg<Context>& arg, Char type)
: arg_(arg), type_(type) {} : arg_(arg), type_(type) {}
void operator()(bool value) { void operator()(bool value) {
if (type_ != 's') if (type_ != 's') operator()<bool>(value);
operator()<bool>(value);
} }
template <typename U> template <typename U>
typename std::enable_if<std::is_integral<U>::value>::type typename std::enable_if<std::is_integral<U>::value>::type operator()(
operator()(U value) { U value) {
bool is_signed = type_ == 'd' || type_ == 'i'; bool is_signed = type_ == 'd' || type_ == 'i';
typedef typename std::conditional< typedef typename std::conditional<std::is_same<T, void>::value, U, T>::type
std::is_same<T, void>::value, U, T>::type TargetType; TargetType;
if (const_check(sizeof(TargetType) <= sizeof(int))) { if (const_check(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_ = internal::make_arg<Context>( arg_ = internal::make_arg<Context>(
static_cast<int>(static_cast<TargetType>(value))); static_cast<int>(static_cast<TargetType>(value)));
} else { } else {
typedef typename make_unsigned_or_bool<TargetType>::type Unsigned; typedef typename make_unsigned_or_bool<TargetType>::type Unsigned;
arg_ = internal::make_arg<Context>( arg_ = internal::make_arg<Context>(
static_cast<unsigned>(static_cast<Unsigned>(value))); static_cast<unsigned>(static_cast<Unsigned>(value)));
} }
} else { } else {
if (is_signed) { if (is_signed) {
@ -116,7 +112,7 @@ class arg_converter: public function<void> {
arg_ = internal::make_arg<Context>(static_cast<long long>(value)); arg_ = internal::make_arg<Context>(static_cast<long long>(value));
} else { } else {
arg_ = internal::make_arg<Context>( arg_ = internal::make_arg<Context>(
static_cast<typename make_unsigned_or_bool<U>::type>(value)); static_cast<typename make_unsigned_or_bool<U>::type>(value));
} }
} }
} }
@ -132,22 +128,21 @@ class arg_converter: public function<void> {
// type depending on the type specifier: 'd' and 'i' - signed, other - // type depending on the type specifier: 'd' and 'i' - signed, other -
// unsigned). // unsigned).
template <typename T, typename Context, typename Char> template <typename T, typename Context, typename Char>
void convert_arg(basic_format_arg<Context> &arg, Char type) { void convert_arg(basic_format_arg<Context>& arg, Char type) {
visit_format_arg(arg_converter<T, Context>(arg, type), arg); visit_format_arg(arg_converter<T, Context>(arg, type), arg);
} }
// Converts an integer argument to char for printf. // Converts an integer argument to char for printf.
template <typename Context> template <typename Context> class char_converter : public function<void> {
class char_converter: public function<void> {
private: private:
basic_format_arg<Context> &arg_; basic_format_arg<Context>& arg_;
public: public:
explicit char_converter(basic_format_arg<Context> &arg) : arg_(arg) {} explicit char_converter(basic_format_arg<Context>& arg) : arg_(arg) {}
template <typename T> template <typename T>
typename std::enable_if<std::is_integral<T>::value>::type typename std::enable_if<std::is_integral<T>::value>::type operator()(
operator()(T value) { T value) {
typedef typename Context::char_type Char; typedef typename Context::char_type Char;
arg_ = internal::make_arg<Context>(static_cast<Char>(value)); arg_ = internal::make_arg<Context>(static_cast<Char>(value));
} }
@ -161,18 +156,18 @@ class char_converter: public function<void> {
// 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.
template <typename Char> template <typename Char>
class printf_width_handler: public function<unsigned> { class printf_width_handler : public function<unsigned> {
private: private:
typedef basic_format_specs<Char> format_specs; typedef basic_format_specs<Char> format_specs;
format_specs &spec_; format_specs& spec_;
public: public:
explicit printf_width_handler(format_specs &spec) : spec_(spec) {} explicit printf_width_handler(format_specs& spec) : spec_(spec) {}
template <typename T> template <typename T>
typename std::enable_if<std::is_integral<T>::value, unsigned>::type typename std::enable_if<std::is_integral<T>::value, unsigned>::type
operator()(T value) { operator()(T value) {
typedef typename internal::int_traits<T>::main_type UnsignedType; typedef typename internal::int_traits<T>::main_type UnsignedType;
UnsignedType width = static_cast<UnsignedType>(value); UnsignedType width = static_cast<UnsignedType>(value);
if (internal::is_negative(value)) { if (internal::is_negative(value)) {
@ -180,21 +175,20 @@ class printf_width_handler: public function<unsigned> {
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(format_error("number is too big"));
FMT_THROW(format_error("number is too big"));
return static_cast<unsigned>(width); return static_cast<unsigned>(width);
} }
template <typename T> template <typename T>
typename std::enable_if<!std::is_integral<T>::value, unsigned>::type typename std::enable_if<!std::is_integral<T>::value, unsigned>::type
operator()(T) { operator()(T) {
FMT_THROW(format_error("width is not integer")); FMT_THROW(format_error("width is not integer"));
return 0; return 0;
} }
}; };
template <typename Char, typename Context> template <typename Char, typename Context>
void printf(basic_buffer<Char> &buf, basic_string_view<Char> format, void printf(basic_buffer<Char>& buf, basic_string_view<Char> format,
basic_format_args<Context> args) { basic_format_args<Context> args) {
Context(std::back_inserter(buf), format, args).format(); Context(std::back_inserter(buf), format, args).format();
} }
@ -202,13 +196,11 @@ void printf(basic_buffer<Char> &buf, basic_string_view<Char> format,
using internal::printf; // For printing into memory_buffer. using internal::printf; // For printing into memory_buffer.
template <typename Range> template <typename Range> class printf_arg_formatter;
class printf_arg_formatter;
template < template <typename OutputIt, typename Char,
typename OutputIt, typename Char, typename ArgFormatter = printf_arg_formatter<
typename ArgFormatter = back_insert_range<internal::basic_buffer<Char>>>>
printf_arg_formatter<back_insert_range<internal::basic_buffer<Char>>>>
class basic_printf_context; class basic_printf_context;
/** /**
@ -217,17 +209,17 @@ class basic_printf_context;
\endrst \endrst
*/ */
template <typename Range> template <typename Range>
class printf_arg_formatter: class printf_arg_formatter
public internal::function< : public internal::function<
typename internal::arg_formatter_base<Range>::iterator>, typename internal::arg_formatter_base<Range>::iterator>,
public internal::arg_formatter_base<Range> { public internal::arg_formatter_base<Range> {
private: private:
typedef typename Range::value_type char_type; typedef typename Range::value_type char_type;
typedef decltype(internal::declval<Range>().begin()) iterator; typedef decltype(internal::declval<Range>().begin()) iterator;
typedef internal::arg_formatter_base<Range> base; typedef internal::arg_formatter_base<Range> base;
typedef basic_printf_context<iterator, char_type> context_type; typedef basic_printf_context<iterator, char_type> context_type;
context_type &context_; context_type& context_;
void write_null_pointer(char) { void write_null_pointer(char) {
this->spec()->type = 0; this->spec()->type = 0;
@ -249,25 +241,24 @@ class printf_arg_formatter:
specifier information for standard argument types. specifier information for standard argument types.
\endrst \endrst
*/ */
printf_arg_formatter(internal::basic_buffer<char_type> &buffer, printf_arg_formatter(internal::basic_buffer<char_type>& buffer,
format_specs &spec, context_type &ctx) format_specs& spec, context_type& ctx)
: base(back_insert_range<internal::basic_buffer<char_type>>(buffer), &spec, : base(back_insert_range<internal::basic_buffer<char_type>>(buffer),
ctx.locale()), &spec, ctx.locale()),
context_(ctx) {} context_(ctx) {}
template <typename T> template <typename T>
typename std::enable_if<std::is_integral<T>::value, iterator>::type typename std::enable_if<std::is_integral<T>::value, iterator>::type
operator()(T value) { operator()(T value) {
// MSVC2013 fails to compile separate overloads for bool and char_type so // MSVC2013 fails to compile separate overloads for bool and char_type so
// use std::is_same instead. // use std::is_same instead.
if (std::is_same<T, bool>::value) { if (std::is_same<T, bool>::value) {
format_specs &fmt_spec = *this->spec(); format_specs& fmt_spec = *this->spec();
if (fmt_spec.type != 's') if (fmt_spec.type != 's') return base::operator()(value ? 1 : 0);
return base::operator()(value ? 1 : 0);
fmt_spec.type = 0; fmt_spec.type = 0;
this->write(value != 0); this->write(value != 0);
} else if (std::is_same<T, char_type>::value) { } else if (std::is_same<T, char_type>::value) {
format_specs &fmt_spec = *this->spec(); format_specs& fmt_spec = *this->spec();
if (fmt_spec.type && fmt_spec.type != 'c') if (fmt_spec.type && fmt_spec.type != 'c')
return (*this)(static_cast<int>(value)); return (*this)(static_cast<int>(value));
fmt_spec.flags = 0; fmt_spec.flags = 0;
@ -281,12 +272,12 @@ class printf_arg_formatter:
template <typename T> template <typename T>
typename std::enable_if<std::is_floating_point<T>::value, iterator>::type typename std::enable_if<std::is_floating_point<T>::value, iterator>::type
operator()(T value) { operator()(T value) {
return base::operator()(value); return base::operator()(value);
} }
/** Formats a null-terminated C string. */ /** Formats a null-terminated C string. */
iterator operator()(const char *value) { iterator operator()(const char* value) {
if (value) if (value)
base::operator()(value); base::operator()(value);
else if (this->spec()->type == 'p') else if (this->spec()->type == 'p')
@ -297,7 +288,7 @@ class printf_arg_formatter:
} }
/** Formats a null-terminated wide C string. */ /** Formats a null-terminated wide C string. */
iterator operator()(const wchar_t *value) { iterator operator()(const wchar_t* value) {
if (value) if (value)
base::operator()(value); base::operator()(value);
else if (this->spec()->type == 'p') else if (this->spec()->type == 'p')
@ -311,14 +302,11 @@ class printf_arg_formatter:
return base::operator()(value); return base::operator()(value);
} }
iterator operator()(monostate value) { iterator operator()(monostate value) { return base::operator()(value); }
return base::operator()(value);
}
/** Formats a pointer. */ /** Formats a pointer. */
iterator operator()(const void *value) { iterator operator()(const void* value) {
if (value) if (value) return base::operator()(value);
return base::operator()(value);
this->spec()->type = 0; this->spec()->type = 0;
write_null_pointer(char_type()); write_null_pointer(char_type());
return this->out(); return this->out();
@ -331,13 +319,14 @@ class printf_arg_formatter:
} }
}; };
template <typename T> template <typename T> struct printf_formatter {
struct printf_formatter {
template <typename ParseContext> template <typename ParseContext>
auto parse(ParseContext &ctx) -> decltype(ctx.begin()) { return ctx.begin(); } auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
return ctx.begin();
}
template <typename FormatContext> template <typename FormatContext>
auto format(const T &value, FormatContext &ctx) -> decltype(ctx.out()) { auto format(const T& value, FormatContext& ctx) -> decltype(ctx.out()) {
internal::format_value(internal::get_container(ctx.out()), value); internal::format_value(internal::get_container(ctx.out()), value);
return ctx.out(); return ctx.out();
} }
@ -346,30 +335,31 @@ struct printf_formatter {
/** This template formats data and writes the output to a writer. */ /** This template formats data and writes the output to a writer. */
template <typename OutputIt, typename Char, typename ArgFormatter> template <typename OutputIt, typename Char, typename ArgFormatter>
class basic_printf_context : class basic_printf_context :
// Inherit publicly as a workaround for the icc bug // Inherit publicly as a workaround for the icc bug
// https://software.intel.com/en-us/forums/intel-c-compiler/topic/783476. // https://software.intel.com/en-us/forums/intel-c-compiler/topic/783476.
public internal::context_base< public internal::context_base<
OutputIt, basic_printf_context<OutputIt, Char, ArgFormatter>, Char> { OutputIt, basic_printf_context<OutputIt, Char, ArgFormatter>, Char> {
public: public:
/** The character type for the output. */ /** The character type for the output. */
typedef Char char_type; typedef Char char_type;
template <typename T> template <typename T> struct formatter_type {
struct formatter_type { typedef printf_formatter<T> type; }; typedef printf_formatter<T> type;
};
private: private:
typedef internal::context_base<OutputIt, basic_printf_context, Char> base; typedef internal::context_base<OutputIt, basic_printf_context, Char> base;
typedef typename base::format_arg format_arg; typedef typename base::format_arg format_arg;
typedef basic_format_specs<char_type> format_specs; typedef basic_format_specs<char_type> format_specs;
static void parse_flags(format_specs &spec, const Char *&it, const Char *end); static void parse_flags(format_specs& spec, const Char*& it, const Char* end);
// 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.
format_arg get_arg(unsigned arg_index = std::numeric_limits<unsigned>::max()); format_arg get_arg(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 index.
unsigned parse_header(const Char *&it, const Char *end, format_specs &spec); unsigned parse_header(const Char*& it, const Char* end, format_specs& spec);
public: public:
/** /**
@ -381,45 +371,46 @@ class basic_printf_context :
*/ */
basic_printf_context(OutputIt out, basic_string_view<char_type> format_str, basic_printf_context(OutputIt out, basic_string_view<char_type> format_str,
basic_format_args<basic_printf_context> args) basic_format_args<basic_printf_context> args)
: base(out, format_str, args) {} : base(out, format_str, args) {}
using base::parse_context;
using base::out;
using base::advance_to; using base::advance_to;
using base::out;
using base::parse_context;
/** Formats stored arguments and writes the output to the range. */ /** Formats stored arguments and writes the output to the range. */
void format(); void format();
}; };
template <typename OutputIt, typename Char, typename AF> template <typename OutputIt, typename Char, typename AF>
void basic_printf_context<OutputIt, Char, AF>::parse_flags( void basic_printf_context<OutputIt, Char, AF>::parse_flags(format_specs& spec,
format_specs &spec, const Char *&it, const Char *end) { const Char*& it,
const Char* end) {
for (; it != end; ++it) { for (; it != end; ++it) {
switch (*it) { switch (*it) {
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:
return; return;
} }
} }
} }
template <typename OutputIt, typename Char, typename AF> template <typename OutputIt, typename Char, typename AF>
typename basic_printf_context<OutputIt, Char, AF>::format_arg typename basic_printf_context<OutputIt, Char, AF>::format_arg
basic_printf_context<OutputIt, Char, AF>::get_arg(unsigned arg_index) { basic_printf_context<OutputIt, Char, AF>::get_arg(unsigned arg_index) {
if (arg_index == std::numeric_limits<unsigned>::max()) if (arg_index == std::numeric_limits<unsigned>::max())
return this->do_get_arg(this->parse_context().next_arg_id()); return this->do_get_arg(this->parse_context().next_arg_id());
return base::get_arg(arg_index - 1); return base::get_arg(arg_index - 1);
@ -427,7 +418,7 @@ typename basic_printf_context<OutputIt, Char, AF>::format_arg
template <typename OutputIt, typename Char, typename AF> template <typename OutputIt, typename Char, typename AF>
unsigned basic_printf_context<OutputIt, Char, AF>::parse_header( unsigned basic_printf_context<OutputIt, Char, AF>::parse_header(
const Char *&it, const Char *end, format_specs &spec) { const Char*& it, const Char* end, format_specs& spec) {
unsigned arg_index = std::numeric_limits<unsigned>::max(); unsigned arg_index = std::numeric_limits<unsigned>::max();
char_type c = *it; char_type c = *it;
if (c >= '0' && c <= '9') { if (c >= '0' && c <= '9') {
@ -439,8 +430,7 @@ unsigned basic_printf_context<OutputIt, Char, AF>::parse_header(
++it; ++it;
arg_index = value; arg_index = value;
} else { } else {
if (c == '0') if (c == '0') spec.fill_ = '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 don't need to
// parse it or flags again, so return now. // parse it or flags again, so return now.
@ -458,7 +448,7 @@ unsigned basic_printf_context<OutputIt, Char, AF>::parse_header(
} else if (*it == '*') { } else if (*it == '*') {
++it; ++it;
spec.width_ = visit_format_arg( spec.width_ = visit_format_arg(
internal::printf_width_handler<char_type>(spec), get_arg()); internal::printf_width_handler<char_type>(spec), get_arg());
} }
} }
return arg_index; return arg_index;
@ -466,10 +456,10 @@ unsigned basic_printf_context<OutputIt, Char, AF>::parse_header(
template <typename OutputIt, typename Char, typename AF> template <typename OutputIt, typename Char, typename AF>
void basic_printf_context<OutputIt, Char, AF>::format() { void basic_printf_context<OutputIt, Char, AF>::format() {
auto &buffer = internal::get_container(this->out()); auto& buffer = internal::get_container(this->out());
const auto range = this->parse_context(); const auto range = this->parse_context();
const Char * end = range.end(); const Char* end = range.end();
const Char * start = range.begin(); const Char* start = range.begin();
auto it = start; auto it = start;
while (it != end) { while (it != end) {
char_type c = *it++; char_type c = *it++;
@ -505,7 +495,8 @@ void basic_printf_context<OutputIt, Char, AF>::format() {
format_arg arg = get_arg(arg_index); format_arg arg = get_arg(arg_index);
if (spec.has(HASH_FLAG) && visit_format_arg(internal::is_zero_int(), arg)) if (spec.has(HASH_FLAG) && visit_format_arg(internal::is_zero_int(), arg))
spec.flags = static_cast<uint_least8_t>(spec.flags & (~internal::to_unsigned<int>(HASH_FLAG))); spec.flags = static_cast<uint_least8_t>(
spec.flags & (~internal::to_unsigned<int>(HASH_FLAG)));
if (spec.fill_ == '0') { if (spec.fill_ == '0') {
if (arg.is_arithmetic()) if (arg.is_arithmetic())
spec.align_ = ALIGN_NUMERIC; spec.align_ = ALIGN_NUMERIC;
@ -555,19 +546,19 @@ void basic_printf_context<OutputIt, Char, AF>::format() {
} }
// Parse type. // Parse type.
if (it == end) if (it == end) FMT_THROW(format_error("invalid format string"));
FMT_THROW(format_error("invalid format string"));
spec.type = static_cast<char>(*it++); spec.type = static_cast<char>(*it++);
if (arg.is_integral()) { if (arg.is_integral()) {
// Normalize type. // Normalize type.
switch (spec.type) { switch (spec.type) {
case 'i': case 'u': case 'i':
case 'u':
spec.type = 'd'; spec.type = 'd';
break; break;
case 'c': case 'c':
// TODO: handle wchar_t better? // TODO: handle wchar_t better?
visit_format_arg( visit_format_arg(internal::char_converter<basic_printf_context>(arg),
internal::char_converter<basic_printf_context>(arg), arg); arg);
break; break;
} }
} }
@ -580,10 +571,10 @@ void basic_printf_context<OutputIt, Char, AF>::format() {
buffer.append(start, it); buffer.append(start, it);
} }
template <typename Buffer> template <typename Buffer> struct basic_printf_context_t {
struct basic_printf_context_t { typedef basic_printf_context<std::back_insert_iterator<Buffer>,
typedef basic_printf_context< typename Buffer::value_type>
std::back_insert_iterator<Buffer>, typename Buffer::value_type> type; type;
}; };
typedef basic_printf_context_t<internal::buffer>::type printf_context; typedef basic_printf_context_t<internal::buffer>::type printf_context;
@ -595,28 +586,33 @@ typedef basic_format_args<wprintf_context> wprintf_args;
/** /**
\rst \rst
Constructs an `~fmt::format_arg_store` object that contains references to Constructs an `~fmt::format_arg_store` object that contains references to
arguments and can be implicitly converted to `~fmt::printf_args`. arguments and can be implicitly converted to `~fmt::printf_args`.
\endrst \endrst
*/ */
template<typename... Args> template <typename... Args>
inline format_arg_store<printf_context, Args...> inline format_arg_store<printf_context, Args...> make_printf_args(
make_printf_args(const Args &... args) { return {args...}; } const Args&... args) {
return {args...};
}
/** /**
\rst \rst
Constructs an `~fmt::format_arg_store` object that contains references to Constructs an `~fmt::format_arg_store` object that contains references to
arguments and can be implicitly converted to `~fmt::wprintf_args`. arguments and can be implicitly converted to `~fmt::wprintf_args`.
\endrst \endrst
*/ */
template<typename... Args> template <typename... Args>
inline format_arg_store<wprintf_context, Args...> inline format_arg_store<wprintf_context, Args...> make_wprintf_args(
make_wprintf_args(const Args &... args) { return {args...}; } const Args&... args) {
return {args...};
}
template <typename S, typename Char = FMT_CHAR(S)> template <typename S, typename Char = FMT_CHAR(S)>
inline std::basic_string<Char> inline std::basic_string<Char> vsprintf(
vsprintf(const S &format, const S& format,
basic_format_args<typename basic_printf_context_t< basic_format_args<
internal::basic_buffer<Char>>::type> args) { typename basic_printf_context_t<internal::basic_buffer<Char>>::type>
args) {
basic_memory_buffer<Char> buffer; basic_memory_buffer<Char> buffer;
printf(buffer, to_string_view(format), args); printf(buffer, to_string_view(format), args);
return to_string(buffer); return to_string(buffer);
@ -632,26 +628,28 @@ vsprintf(const S &format,
\endrst \endrst
*/ */
template <typename S, typename... Args> template <typename S, typename... Args>
inline FMT_ENABLE_IF_T( inline FMT_ENABLE_IF_T(internal::is_string<S>::value,
internal::is_string<S>::value, std::basic_string<FMT_CHAR(S)>) std::basic_string<FMT_CHAR(S)>)
sprintf(const S &format, const Args & ... args) { sprintf(const S& format, const Args&... args) {
internal::check_format_string<Args...>(format); internal::check_format_string<Args...>(format);
typedef internal::basic_buffer<FMT_CHAR(S)> buffer; typedef internal::basic_buffer<FMT_CHAR(S)> buffer;
typedef typename basic_printf_context_t<buffer>::type context; typedef typename basic_printf_context_t<buffer>::type context;
format_arg_store<context, Args...> as{ args... }; format_arg_store<context, Args...> as{args...};
return vsprintf(to_string_view(format), return vsprintf(to_string_view(format), basic_format_args<context>(as));
basic_format_args<context>(as));
} }
template <typename S, typename Char = FMT_CHAR(S)> template <typename S, typename Char = FMT_CHAR(S)>
inline int vfprintf(std::FILE *f, const S &format, inline int vfprintf(
basic_format_args<typename basic_printf_context_t< std::FILE* f, const S& format,
internal::basic_buffer<Char>>::type> args) { basic_format_args<
typename basic_printf_context_t<internal::basic_buffer<Char>>::type>
args) {
basic_memory_buffer<Char> buffer; basic_memory_buffer<Char> buffer;
printf(buffer, to_string_view(format), args); printf(buffer, to_string_view(format), args);
std::size_t size = buffer.size(); std::size_t size = buffer.size();
return std::fwrite( return std::fwrite(buffer.data(), sizeof(Char), size, f) < size
buffer.data(), sizeof(Char), size, f) < size ? -1 : static_cast<int>(size); ? -1
: static_cast<int>(size);
} }
/** /**
@ -665,19 +663,20 @@ inline int vfprintf(std::FILE *f, const S &format,
*/ */
template <typename S, typename... Args> template <typename S, typename... Args>
inline FMT_ENABLE_IF_T(internal::is_string<S>::value, int) inline FMT_ENABLE_IF_T(internal::is_string<S>::value, int)
fprintf(std::FILE *f, const S &format, const Args & ... args) { fprintf(std::FILE* f, const S& format, const Args&... args) {
internal::check_format_string<Args...>(format); internal::check_format_string<Args...>(format);
typedef internal::basic_buffer<FMT_CHAR(S)> buffer; typedef internal::basic_buffer<FMT_CHAR(S)> buffer;
typedef typename basic_printf_context_t<buffer>::type context; typedef typename basic_printf_context_t<buffer>::type context;
format_arg_store<context, Args...> as{ args... }; format_arg_store<context, Args...> as{args...};
return vfprintf(f, to_string_view(format), return vfprintf(f, to_string_view(format), basic_format_args<context>(as));
basic_format_args<context>(as));
} }
template <typename S, typename Char = FMT_CHAR(S)> template <typename S, typename Char = FMT_CHAR(S)>
inline int vprintf(const S &format, inline int vprintf(
basic_format_args<typename basic_printf_context_t< const S& format,
internal::basic_buffer<Char>>::type> args) { basic_format_args<
typename basic_printf_context_t<internal::basic_buffer<Char>>::type>
args) {
return vfprintf(stdout, to_string_view(format), args); return vfprintf(stdout, to_string_view(format), args);
} }
@ -692,20 +691,20 @@ inline int vprintf(const S &format,
*/ */
template <typename S, typename... Args> template <typename S, typename... Args>
inline FMT_ENABLE_IF_T(internal::is_string<S>::value, int) inline FMT_ENABLE_IF_T(internal::is_string<S>::value, int)
printf(const S &format_str, const Args & ... args) { printf(const S& format_str, const Args&... args) {
internal::check_format_string<Args...>(format_str); internal::check_format_string<Args...>(format_str);
typedef internal::basic_buffer<FMT_CHAR(S)> buffer; typedef internal::basic_buffer<FMT_CHAR(S)> buffer;
typedef typename basic_printf_context_t<buffer>::type context; typedef typename basic_printf_context_t<buffer>::type context;
format_arg_store<context, Args...> as{ args... }; format_arg_store<context, Args...> as{args...};
return vprintf(to_string_view(format_str), return vprintf(to_string_view(format_str), basic_format_args<context>(as));
basic_format_args<context>(as));
} }
template <typename S, typename Char = FMT_CHAR(S)> template <typename S, typename Char = FMT_CHAR(S)>
inline int vfprintf(std::basic_ostream<Char> &os, inline int vfprintf(
const S &format, std::basic_ostream<Char>& os, const S& format,
basic_format_args<typename basic_printf_context_t< basic_format_args<
internal::basic_buffer<Char>>::type> args) { typename basic_printf_context_t<internal::basic_buffer<Char>>::type>
args) {
basic_memory_buffer<Char> buffer; basic_memory_buffer<Char> buffer;
printf(buffer, to_string_view(format), args); printf(buffer, to_string_view(format), args);
internal::write(os, buffer); internal::write(os, buffer);
@ -723,12 +722,12 @@ inline int vfprintf(std::basic_ostream<Char> &os,
*/ */
template <typename S, typename... Args> template <typename S, typename... Args>
inline FMT_ENABLE_IF_T(internal::is_string<S>::value, int) inline FMT_ENABLE_IF_T(internal::is_string<S>::value, int)
fprintf(std::basic_ostream<FMT_CHAR(S)> &os, fprintf(std::basic_ostream<FMT_CHAR(S)>& os, const S& format_str,
const S &format_str, const Args & ... args) { const Args&... args) {
internal::check_format_string<Args...>(format_str); internal::check_format_string<Args...>(format_str);
typedef internal::basic_buffer<FMT_CHAR(S)> buffer; typedef internal::basic_buffer<FMT_CHAR(S)> buffer;
typedef typename basic_printf_context_t<buffer>::type context; typedef typename basic_printf_context_t<buffer>::type context;
format_arg_store<context, Args...> as{ args... }; format_arg_store<context, Args...> as{args...};
return vfprintf(os, to_string_view(format_str), return vfprintf(os, to_string_view(format_str),
basic_format_args<context>(as)); basic_format_args<context>(as));
} }

View File

@ -12,20 +12,19 @@
#ifndef FMT_RANGES_H_ #ifndef FMT_RANGES_H_
#define FMT_RANGES_H_ #define FMT_RANGES_H_
#include "format.h"
#include <type_traits> #include <type_traits>
#include "format.h"
// output only up to N items from the range. // output only up to N items from the range.
#ifndef FMT_RANGE_OUTPUT_LENGTH_LIMIT #ifndef FMT_RANGE_OUTPUT_LENGTH_LIMIT
# define FMT_RANGE_OUTPUT_LENGTH_LIMIT 256 # define FMT_RANGE_OUTPUT_LENGTH_LIMIT 256
#endif #endif
FMT_BEGIN_NAMESPACE FMT_BEGIN_NAMESPACE
template <typename Char> template <typename Char> struct formatting_base {
struct formatting_base {
template <typename ParseContext> template <typename ParseContext>
FMT_CONSTEXPR auto parse(ParseContext &ctx) -> decltype(ctx.begin()) { FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
return ctx.begin(); return ctx.begin();
} }
}; };
@ -33,7 +32,8 @@ struct formatting_base {
template <typename Char, typename Enable = void> template <typename Char, typename Enable = void>
struct formatting_range : formatting_base<Char> { struct formatting_range : formatting_base<Char> {
static FMT_CONSTEXPR_DECL const std::size_t range_length_limit = static FMT_CONSTEXPR_DECL const std::size_t range_length_limit =
FMT_RANGE_OUTPUT_LENGTH_LIMIT; // output only up to N items from the range. FMT_RANGE_OUTPUT_LENGTH_LIMIT; // output only up to N items from the
// range.
Char prefix; Char prefix;
Char delimiter; Char delimiter;
Char postfix; Char postfix;
@ -55,87 +55,78 @@ struct formatting_tuple : formatting_base<Char> {
namespace internal { namespace internal {
template <typename RangeT, typename OutputIterator> template <typename RangeT, typename OutputIterator>
void copy(const RangeT &range, OutputIterator out) { void copy(const RangeT& range, OutputIterator out) {
for (auto it = range.begin(), end = range.end(); it != end; ++it) for (auto it = range.begin(), end = range.end(); it != end; ++it)
*out++ = *it; *out++ = *it;
} }
template <typename OutputIterator> template <typename OutputIterator>
void copy(const char *str, OutputIterator out) { void copy(const char* str, OutputIterator out) {
const char *p_curr = str; const char* p_curr = str;
while (*p_curr) { while (*p_curr) {
*out++ = *p_curr++; *out++ = *p_curr++;
} }
} }
template <typename OutputIterator> template <typename OutputIterator> void copy(char ch, OutputIterator out) {
void copy(char ch, OutputIterator out) {
*out++ = ch; *out++ = ch;
} }
/// Return true value if T has std::string interface, like std::string_view. /// Return true value if T has std::string interface, like std::string_view.
template <typename T> template <typename T> class is_like_std_string {
class is_like_std_string {
template <typename U> template <typename U>
static auto check(U *p) -> static auto check(U* p)
decltype(p->find('a'), p->length(), p->data(), int()); -> decltype(p->find('a'), p->length(), p->data(), int());
template <typename> template <typename> static void check(...);
static void check(...);
public: public:
static FMT_CONSTEXPR_DECL const bool value = static FMT_CONSTEXPR_DECL const bool value =
!std::is_void<decltype(check<T>(FMT_NULL))>::value; !std::is_void<decltype(check<T>(FMT_NULL))>::value;
}; };
template <typename Char> template <typename Char>
struct is_like_std_string<fmt::basic_string_view<Char>> : std::true_type {}; struct is_like_std_string<fmt::basic_string_view<Char>> : std::true_type {};
template <typename... Ts> template <typename... Ts> struct conditional_helper {};
struct conditional_helper {};
template <typename T, typename _ = void> template <typename T, typename _ = void> struct is_range_ : std::false_type {};
struct is_range_ : std::false_type {};
#if !FMT_MSC_VER || FMT_MSC_VER > 1800 #if !FMT_MSC_VER || FMT_MSC_VER > 1800
template <typename T> template <typename T>
struct is_range_<T, typename std::conditional< struct is_range_<
false, T, typename std::conditional<
conditional_helper<decltype(internal::declval<T>().begin()), false,
decltype(internal::declval<T>().end())>, conditional_helper<decltype(internal::declval<T>().begin()),
void>::type> : std::true_type {}; decltype(internal::declval<T>().end())>,
void>::type> : std::true_type {};
#endif #endif
/// tuple_size and tuple_element check. /// tuple_size and tuple_element check.
template <typename T> template <typename T> class is_tuple_like_ {
class is_tuple_like_ {
template <typename U> template <typename U>
static auto check(U *p) -> static auto check(U* p)
decltype(std::tuple_size<U>::value, -> decltype(std::tuple_size<U>::value,
internal::declval<typename std::tuple_element<0, U>::type>(), int()); internal::declval<typename std::tuple_element<0, U>::type>(),
template <typename> int());
static void check(...); template <typename> static void check(...);
public: public:
static FMT_CONSTEXPR_DECL const bool value = static FMT_CONSTEXPR_DECL const bool value =
!std::is_void<decltype(check<T>(FMT_NULL))>::value; !std::is_void<decltype(check<T>(FMT_NULL))>::value;
}; };
// Check for integer_sequence // Check for integer_sequence
#if defined(__cpp_lib_integer_sequence) || FMT_MSC_VER >= 1900 #if defined(__cpp_lib_integer_sequence) || FMT_MSC_VER >= 1900
template <typename T, T... N> template <typename T, T... N>
using integer_sequence = std::integer_sequence<T, N...>; using integer_sequence = std::integer_sequence<T, N...>;
template <std::size_t... N> template <std::size_t... N> using index_sequence = std::index_sequence<N...>;
using index_sequence = std::index_sequence<N...>;
template <std::size_t N> template <std::size_t N>
using make_index_sequence = std::make_index_sequence<N>; using make_index_sequence = std::make_index_sequence<N>;
#else #else
template <typename T, T... N> template <typename T, T... N> struct integer_sequence {
struct integer_sequence {
typedef T value_type; typedef T value_type;
static FMT_CONSTEXPR std::size_t size() { static FMT_CONSTEXPR std::size_t size() { return sizeof...(N); }
return sizeof...(N);
}
}; };
template <std::size_t... N> template <std::size_t... N>
@ -151,7 +142,7 @@ using make_index_sequence = make_integer_sequence<std::size_t, N>;
#endif #endif
template <class Tuple, class F, size_t... Is> template <class Tuple, class F, size_t... Is>
void for_each(index_sequence<Is...>, Tuple &&tup, F &&f) FMT_NOEXCEPT { void for_each(index_sequence<Is...>, Tuple&& tup, F&& f) FMT_NOEXCEPT {
using std::get; using std::get;
// using free function get<I>(T) now. // using free function get<I>(T) now.
const int _[] = {0, ((void)f(get<Is>(tup)), 0)...}; const int _[] = {0, ((void)f(get<Is>(tup)), 0)...};
@ -159,26 +150,31 @@ void for_each(index_sequence<Is...>, Tuple &&tup, F &&f) FMT_NOEXCEPT {
} }
template <class T> template <class T>
FMT_CONSTEXPR make_index_sequence<std::tuple_size<T>::value> FMT_CONSTEXPR make_index_sequence<std::tuple_size<T>::value> get_indexes(
get_indexes(T const &) { return {}; } T const&) {
return {};
}
template <class Tuple, class F> template <class Tuple, class F> void for_each(Tuple&& tup, F&& f) {
void for_each(Tuple &&tup, F &&f) {
const auto indexes = get_indexes(tup); const auto indexes = get_indexes(tup);
for_each(indexes, std::forward<Tuple>(tup), std::forward<F>(f)); for_each(indexes, std::forward<Tuple>(tup), std::forward<F>(f));
} }
template<typename Arg> template <typename Arg>
FMT_CONSTEXPR const char* format_str_quoted(bool add_space, const Arg&, FMT_CONSTEXPR const char* format_str_quoted(
typename std::enable_if< bool add_space, const Arg&,
!is_like_std_string<typename std::decay<Arg>::type>::value>::type* = nullptr) { typename std::enable_if<
!is_like_std_string<typename std::decay<Arg>::type>::value>::type* =
nullptr) {
return add_space ? " {}" : "{}"; return add_space ? " {}" : "{}";
} }
template<typename Arg> template <typename Arg>
FMT_CONSTEXPR const char* format_str_quoted(bool add_space, const Arg&, FMT_CONSTEXPR const char* format_str_quoted(
typename std::enable_if< bool add_space, const Arg&,
is_like_std_string<typename std::decay<Arg>::type>::value>::type* = nullptr) { typename std::enable_if<
is_like_std_string<typename std::decay<Arg>::type>::value>::type* =
nullptr) {
return add_space ? " \"{}\"" : "\"{}\""; return add_space ? " \"{}\"" : "\"{}\"";
} }
@ -186,33 +182,31 @@ FMT_CONSTEXPR const char* format_str_quoted(bool add_space, const char*) {
return add_space ? " \"{}\"" : "\"{}\""; return add_space ? " \"{}\"" : "\"{}\"";
} }
FMT_CONSTEXPR const wchar_t* format_str_quoted(bool add_space, const wchar_t*) { FMT_CONSTEXPR const wchar_t* format_str_quoted(bool add_space, const wchar_t*) {
return add_space ? L" \"{}\"" : L"\"{}\""; return add_space ? L" \"{}\"" : L"\"{}\"";
} }
FMT_CONSTEXPR const char* format_str_quoted(bool add_space, const char) { FMT_CONSTEXPR const char* format_str_quoted(bool add_space, const char) {
return add_space ? " '{}'" : "'{}'"; return add_space ? " '{}'" : "'{}'";
} }
FMT_CONSTEXPR const wchar_t* format_str_quoted(bool add_space, const wchar_t) { FMT_CONSTEXPR const wchar_t* format_str_quoted(bool add_space, const wchar_t) {
return add_space ? L" '{}'" : L"'{}'"; return add_space ? L" '{}'" : L"'{}'";
} }
} // namespace internal } // namespace internal
template <typename T> template <typename T> struct is_tuple_like {
struct is_tuple_like {
static FMT_CONSTEXPR_DECL const bool value = static FMT_CONSTEXPR_DECL const bool value =
internal::is_tuple_like_<T>::value && !internal::is_range_<T>::value; internal::is_tuple_like_<T>::value && !internal::is_range_<T>::value;
}; };
template <typename TupleT, typename Char> template <typename TupleT, typename Char>
struct formatter<TupleT, Char, struct formatter<
TupleT, Char,
typename std::enable_if<fmt::is_tuple_like<TupleT>::value>::type> { typename std::enable_if<fmt::is_tuple_like<TupleT>::value>::type> {
private: private:
// C++11 generic lambda for format() // C++11 generic lambda for format()
template <typename FormatContext> template <typename FormatContext> struct format_each {
struct format_each { template <typename T> void operator()(const T& v) {
template <typename T>
void operator()(const T& v) {
if (i > 0) { if (i > 0) {
if (formatting.add_prepostfix_space) { if (formatting.add_prepostfix_space) {
*out++ = ' '; *out++ = ' ';
@ -228,19 +222,20 @@ private:
formatting_tuple<Char>& formatting; formatting_tuple<Char>& formatting;
std::size_t& i; std::size_t& i;
typename std::add_lvalue_reference<decltype(std::declval<FormatContext>().out())>::type out; typename std::add_lvalue_reference<decltype(
std::declval<FormatContext>().out())>::type out;
}; };
public: public:
formatting_tuple<Char> formatting; formatting_tuple<Char> formatting;
template <typename ParseContext> template <typename ParseContext>
FMT_CONSTEXPR auto parse(ParseContext &ctx) -> decltype(ctx.begin()) { FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
return formatting.parse(ctx); return formatting.parse(ctx);
} }
template <typename FormatContext = format_context> template <typename FormatContext = format_context>
auto format(const TupleT &values, FormatContext &ctx) -> decltype(ctx.out()) { auto format(const TupleT& values, FormatContext& ctx) -> decltype(ctx.out()) {
auto out = ctx.out(); auto out = ctx.out();
std::size_t i = 0; std::size_t i = 0;
internal::copy(formatting.prefix, out); internal::copy(formatting.prefix, out);
@ -255,26 +250,24 @@ public:
} }
}; };
template <typename T> template <typename T> struct is_range {
struct is_range {
static FMT_CONSTEXPR_DECL const bool value = static FMT_CONSTEXPR_DECL const bool value =
internal::is_range_<T>::value && !internal::is_like_std_string<T>::value; internal::is_range_<T>::value && !internal::is_like_std_string<T>::value;
}; };
template <typename RangeT, typename Char> template <typename RangeT, typename Char>
struct formatter<RangeT, Char, struct formatter<RangeT, Char,
typename std::enable_if<fmt::is_range<RangeT>::value>::type> { typename std::enable_if<fmt::is_range<RangeT>::value>::type> {
formatting_range<Char> formatting; formatting_range<Char> formatting;
template <typename ParseContext> template <typename ParseContext>
FMT_CONSTEXPR auto parse(ParseContext &ctx) -> decltype(ctx.begin()) { FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
return formatting.parse(ctx); return formatting.parse(ctx);
} }
template <typename FormatContext> template <typename FormatContext>
typename FormatContext::iterator format( typename FormatContext::iterator format(const RangeT& values,
const RangeT &values, FormatContext &ctx) { FormatContext& ctx) {
auto out = ctx.out(); auto out = ctx.out();
internal::copy(formatting.prefix, out); internal::copy(formatting.prefix, out);
std::size_t i = 0; std::size_t i = 0;
@ -304,5 +297,4 @@ struct formatter<RangeT, Char,
FMT_END_NAMESPACE FMT_END_NAMESPACE
#endif // FMT_RANGES_H_ #endif // FMT_RANGES_H_

View File

@ -8,9 +8,9 @@
#ifndef FMT_TIME_H_ #ifndef FMT_TIME_H_
#define FMT_TIME_H_ #define FMT_TIME_H_
#include "format.h"
#include <ctime> #include <ctime>
#include <locale> #include <locale>
#include "format.h"
FMT_BEGIN_NAMESPACE FMT_BEGIN_NAMESPACE
@ -18,7 +18,7 @@ FMT_BEGIN_NAMESPACE
// Usage: f FMT_NOMACRO() // Usage: f FMT_NOMACRO()
#define FMT_NOMACRO #define FMT_NOMACRO
namespace internal{ namespace internal {
inline null<> localtime_r FMT_NOMACRO(...) { return null<>(); } inline null<> localtime_r FMT_NOMACRO(...) { return null<>(); }
inline null<> localtime_s(...) { return null<>(); } inline null<> localtime_s(...) { return null<>(); }
inline null<> gmtime_r(...) { return null<>(); } inline null<> gmtime_r(...) { return null<>(); }
@ -31,14 +31,14 @@ inline std::tm localtime(std::time_t time) {
std::time_t time_; std::time_t time_;
std::tm tm_; std::tm tm_;
dispatcher(std::time_t t): time_(t) {} dispatcher(std::time_t t) : time_(t) {}
bool run() { bool run() {
using namespace fmt::internal; using namespace fmt::internal;
return handle(localtime_r(&time_, &tm_)); return handle(localtime_r(&time_, &tm_));
} }
bool handle(std::tm *tm) { return tm != FMT_NULL; } bool handle(std::tm* tm) { return tm != FMT_NULL; }
bool handle(internal::null<>) { bool handle(internal::null<>) {
using namespace fmt::internal; using namespace fmt::internal;
@ -50,7 +50,7 @@ inline std::tm localtime(std::time_t time) {
#if !FMT_MSC_VER #if !FMT_MSC_VER
bool fallback(internal::null<>) { bool fallback(internal::null<>) {
using namespace fmt::internal; using namespace fmt::internal;
std::tm *tm = std::localtime(&time_); std::tm* tm = std::localtime(&time_);
if (tm) tm_ = *tm; if (tm) tm_ = *tm;
return tm != FMT_NULL; return tm != FMT_NULL;
} }
@ -58,8 +58,7 @@ inline std::tm localtime(std::time_t time) {
}; };
dispatcher lt(time); dispatcher lt(time);
// Too big time values may be unsupported. // Too big time values may be unsupported.
if (!lt.run()) if (!lt.run()) FMT_THROW(format_error("time_t value out of range"));
FMT_THROW(format_error("time_t value out of range"));
return lt.tm_; return lt.tm_;
} }
@ -69,14 +68,14 @@ inline std::tm gmtime(std::time_t time) {
std::time_t time_; std::time_t time_;
std::tm tm_; std::tm tm_;
dispatcher(std::time_t t): time_(t) {} dispatcher(std::time_t t) : time_(t) {}
bool run() { bool run() {
using namespace fmt::internal; using namespace fmt::internal;
return handle(gmtime_r(&time_, &tm_)); return handle(gmtime_r(&time_, &tm_));
} }
bool handle(std::tm *tm) { return tm != FMT_NULL; } bool handle(std::tm* tm) { return tm != FMT_NULL; }
bool handle(internal::null<>) { bool handle(internal::null<>) {
using namespace fmt::internal; using namespace fmt::internal;
@ -87,7 +86,7 @@ inline std::tm gmtime(std::time_t time) {
#if !FMT_MSC_VER #if !FMT_MSC_VER
bool fallback(internal::null<>) { bool fallback(internal::null<>) {
std::tm *tm = std::gmtime(&time_); std::tm* tm = std::gmtime(&time_);
if (tm) tm_ = *tm; if (tm) tm_ = *tm;
return tm != FMT_NULL; return tm != FMT_NULL;
} }
@ -95,33 +94,29 @@ inline std::tm gmtime(std::time_t time) {
}; };
dispatcher gt(time); dispatcher gt(time);
// Too big time values may be unsupported. // Too big time values may be unsupported.
if (!gt.run()) if (!gt.run()) FMT_THROW(format_error("time_t value out of range"));
FMT_THROW(format_error("time_t value out of range"));
return gt.tm_; return gt.tm_;
} }
namespace internal { namespace internal {
inline std::size_t strftime(char *str, std::size_t count, const char *format, inline std::size_t strftime(char* str, std::size_t count, const char* format,
const std::tm *time) { const std::tm* time) {
return std::strftime(str, count, format, time); return std::strftime(str, count, format, time);
} }
inline std::size_t strftime(wchar_t *str, std::size_t count, inline std::size_t strftime(wchar_t* str, std::size_t count,
const wchar_t *format, const std::tm *time) { const wchar_t* format, const std::tm* time) {
return std::wcsftime(str, count, format, time); return std::wcsftime(str, count, format, time);
} }
} } // namespace internal
template <typename Char> template <typename Char> struct formatter<std::tm, Char> {
struct formatter<std::tm, Char> {
template <typename ParseContext> template <typename ParseContext>
auto parse(ParseContext &ctx) -> decltype(ctx.begin()) { auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
auto it = ctx.begin(); auto it = ctx.begin();
if (it != ctx.end() && *it == ':') if (it != ctx.end() && *it == ':') ++it;
++it;
auto end = it; auto end = it;
while (end != ctx.end() && *end != '}') while (end != ctx.end() && *end != '}') ++end;
++end;
tm_format.reserve(internal::to_unsigned(end - it + 1)); tm_format.reserve(internal::to_unsigned(end - it + 1));
tm_format.append(it, end); tm_format.append(it, end);
tm_format.push_back('\0'); tm_format.push_back('\0');
@ -129,13 +124,13 @@ struct formatter<std::tm, Char> {
} }
template <typename FormatContext> template <typename FormatContext>
auto format(const std::tm &tm, FormatContext &ctx) -> decltype(ctx.out()) { auto format(const std::tm& tm, FormatContext& ctx) -> decltype(ctx.out()) {
basic_memory_buffer<Char> buf; basic_memory_buffer<Char> buf;
std::size_t start = buf.size(); std::size_t start = buf.size();
for (;;) { for (;;) {
std::size_t size = buf.capacity() - start; std::size_t size = buf.capacity() - start;
std::size_t count = std::size_t count =
internal::strftime(&buf[start], size, &tm_format[0], &tm); internal::strftime(&buf[start], size, &tm_format[0], &tm);
if (count != 0) { if (count != 0) {
buf.resize(start + count); buf.resize(start + count);
break; break;

View File

@ -9,50 +9,55 @@
FMT_BEGIN_NAMESPACE FMT_BEGIN_NAMESPACE
template struct internal::basic_data<void>; template struct internal::basic_data<void>;
template FMT_API internal::locale_ref::locale_ref(const std::locale &loc); template FMT_API internal::locale_ref::locale_ref(const std::locale& loc);
template FMT_API std::locale internal::locale_ref::get<std::locale>() const; template FMT_API std::locale internal::locale_ref::get<std::locale>() const;
// Explicit instantiations for char. // Explicit instantiations for char.
template FMT_API char internal::thousands_sep_impl(locale_ref); template FMT_API char internal::thousands_sep_impl(locale_ref);
template FMT_API void internal::basic_buffer<char>::append(const char *, const char *); template FMT_API void internal::basic_buffer<char>::append(const char*,
const char*);
template FMT_API void internal::arg_map<format_context>::init( template FMT_API void internal::arg_map<format_context>::init(
const basic_format_args<format_context> &args); const basic_format_args<format_context>& args);
template FMT_API int internal::char_traits<char>::format_float( template FMT_API int internal::char_traits<char>::format_float(char*,
char *, std::size_t, const char *, int, double); std::size_t,
const char*, int,
double);
template FMT_API int internal::char_traits<char>::format_float( template FMT_API int internal::char_traits<char>::format_float(char*,
char *, std::size_t, const char *, int, long double); std::size_t,
const char*, int,
long double);
template FMT_API std::string internal::vformat<char>( template FMT_API std::string internal::vformat<char>(
string_view, basic_format_args<format_context>); string_view, basic_format_args<format_context>);
template FMT_API format_context::iterator internal::vformat_to( template FMT_API format_context::iterator internal::vformat_to(
internal::buffer &, string_view, basic_format_args<format_context>); internal::buffer&, string_view, basic_format_args<format_context>);
template FMT_API void internal::sprintf_format( template FMT_API void internal::sprintf_format(double, internal::buffer&,
double, internal::buffer &, core_format_specs); core_format_specs);
template FMT_API void internal::sprintf_format( template FMT_API void internal::sprintf_format(long double, internal::buffer&,
long double, internal::buffer &, core_format_specs); core_format_specs);
// Explicit instantiations for wchar_t. // Explicit instantiations for wchar_t.
template FMT_API wchar_t internal::thousands_sep_impl(locale_ref); template FMT_API wchar_t internal::thousands_sep_impl(locale_ref);
template FMT_API void internal::basic_buffer<wchar_t>::append( template FMT_API void internal::basic_buffer<wchar_t>::append(const wchar_t*,
const wchar_t *, const wchar_t *); const wchar_t*);
template FMT_API void internal::arg_map<wformat_context>::init( template FMT_API void internal::arg_map<wformat_context>::init(
const basic_format_args<wformat_context> &); const basic_format_args<wformat_context>&);
template FMT_API int internal::char_traits<wchar_t>::format_float( template FMT_API int internal::char_traits<wchar_t>::format_float(
wchar_t *, std::size_t, const wchar_t *, int, double); wchar_t*, std::size_t, const wchar_t*, int, double);
template FMT_API int internal::char_traits<wchar_t>::format_float( template FMT_API int internal::char_traits<wchar_t>::format_float(
wchar_t *, std::size_t, const wchar_t *, int, long double); wchar_t*, std::size_t, const wchar_t*, int, long double);
template FMT_API std::wstring internal::vformat<wchar_t>( template FMT_API std::wstring internal::vformat<wchar_t>(
wstring_view, basic_format_args<wformat_context>); wstring_view, basic_format_args<wformat_context>);

View File

@ -7,43 +7,43 @@
// Disable bogus MSVC warnings. // Disable bogus MSVC warnings.
#if !defined(_CRT_SECURE_NO_WARNINGS) && defined(_MSC_VER) #if !defined(_CRT_SECURE_NO_WARNINGS) && defined(_MSC_VER)
# define _CRT_SECURE_NO_WARNINGS # define _CRT_SECURE_NO_WARNINGS
#endif #endif
#include "fmt/posix.h" #include "fmt/posix.h"
#include <limits.h> #include <limits.h>
#include <sys/types.h>
#include <sys/stat.h> #include <sys/stat.h>
#include <sys/types.h>
#ifndef _WIN32 #ifndef _WIN32
# include <unistd.h> # include <unistd.h>
#else #else
# ifndef WIN32_LEAN_AND_MEAN # ifndef WIN32_LEAN_AND_MEAN
# define WIN32_LEAN_AND_MEAN # define WIN32_LEAN_AND_MEAN
# endif # endif
# include <windows.h> # include <io.h>
# include <io.h> # include <windows.h>
# define O_CREAT _O_CREAT # define O_CREAT _O_CREAT
# define O_TRUNC _O_TRUNC # define O_TRUNC _O_TRUNC
# ifndef S_IRUSR # ifndef S_IRUSR
# define S_IRUSR _S_IREAD # define S_IRUSR _S_IREAD
# endif # endif
# ifndef S_IWUSR # ifndef S_IWUSR
# define S_IWUSR _S_IWRITE # define S_IWUSR _S_IWRITE
# endif # endif
# ifdef __MINGW32__ # ifdef __MINGW32__
# define _SH_DENYNO 0x40 # define _SH_DENYNO 0x40
# endif # endif
#endif // _WIN32 #endif // _WIN32
#ifdef fileno #ifdef fileno
# undef fileno # undef fileno
#endif #endif
namespace { namespace {
@ -62,7 +62,7 @@ typedef ssize_t RWResult;
inline std::size_t convert_rwcount(std::size_t count) { return count; } inline std::size_t convert_rwcount(std::size_t count) { return count; }
#endif #endif
} } // namespace
FMT_BEGIN_NAMESPACE FMT_BEGIN_NAMESPACE
@ -72,19 +72,17 @@ buffered_file::~buffered_file() FMT_NOEXCEPT {
} }
buffered_file::buffered_file(cstring_view filename, cstring_view mode) { buffered_file::buffered_file(cstring_view filename, cstring_view mode) {
FMT_RETRY_VAL(file_, FMT_RETRY_VAL(file_, FMT_SYSTEM(fopen(filename.c_str(), mode.c_str())),
FMT_SYSTEM(fopen(filename.c_str(), mode.c_str())), FMT_NULL); FMT_NULL);
if (!file_) if (!file_)
FMT_THROW(system_error(errno, "cannot open file {}", filename.c_str())); FMT_THROW(system_error(errno, "cannot open file {}", filename.c_str()));
} }
void buffered_file::close() { void buffered_file::close() {
if (!file_) if (!file_) return;
return;
int result = FMT_SYSTEM(fclose(file_)); int result = FMT_SYSTEM(fclose(file_));
file_ = FMT_NULL; file_ = FMT_NULL;
if (result != 0) if (result != 0) FMT_THROW(system_error(errno, "cannot close file"));
FMT_THROW(system_error(errno, "cannot close file"));
} }
// A macro used to prevent expansion of fileno on broken versions of MinGW. // A macro used to prevent expansion of fileno on broken versions of MinGW.
@ -92,8 +90,7 @@ void buffered_file::close() {
int buffered_file::fileno() const { int buffered_file::fileno() const {
int fd = FMT_POSIX_CALL(fileno FMT_ARGS(file_)); int fd = FMT_POSIX_CALL(fileno FMT_ARGS(file_));
if (fd == -1) if (fd == -1) FMT_THROW(system_error(errno, "cannot get file descriptor"));
FMT_THROW(system_error(errno, "cannot get file descriptor"));
return fd; return fd;
} }
@ -117,14 +114,12 @@ file::~file() FMT_NOEXCEPT {
} }
void file::close() { void file::close() {
if (fd_ == -1) if (fd_ == -1) return;
return;
// Don't retry close in case of EINTR! // Don't retry close in case of EINTR!
// See http://linux.derkeiler.com/Mailing-Lists/Kernel/2005-09/3000.html // See http://linux.derkeiler.com/Mailing-Lists/Kernel/2005-09/3000.html
int result = FMT_POSIX_CALL(close(fd_)); int result = FMT_POSIX_CALL(close(fd_));
fd_ = -1; fd_ = -1;
if (result != 0) if (result != 0) FMT_THROW(system_error(errno, "cannot close file"));
FMT_THROW(system_error(errno, "cannot close file"));
} }
long long file::size() const { long long file::size() const {
@ -148,24 +143,22 @@ long long file::size() const {
if (FMT_POSIX_CALL(fstat(fd_, &file_stat)) == -1) if (FMT_POSIX_CALL(fstat(fd_, &file_stat)) == -1)
FMT_THROW(system_error(errno, "cannot get file attributes")); FMT_THROW(system_error(errno, "cannot get file attributes"));
static_assert(sizeof(long long) >= sizeof(file_stat.st_size), static_assert(sizeof(long long) >= sizeof(file_stat.st_size),
"return type of file::size is not large enough"); "return type of file::size is not large enough");
return file_stat.st_size; return file_stat.st_size;
#endif #endif
} }
std::size_t file::read(void *buffer, std::size_t count) { std::size_t file::read(void* buffer, std::size_t count) {
RWResult result = 0; RWResult result = 0;
FMT_RETRY(result, FMT_POSIX_CALL(read(fd_, buffer, convert_rwcount(count)))); FMT_RETRY(result, FMT_POSIX_CALL(read(fd_, buffer, convert_rwcount(count))));
if (result < 0) if (result < 0) FMT_THROW(system_error(errno, "cannot read from file"));
FMT_THROW(system_error(errno, "cannot read from file"));
return internal::to_unsigned(result); return internal::to_unsigned(result);
} }
std::size_t file::write(const void *buffer, std::size_t count) { std::size_t file::write(const void* buffer, std::size_t count) {
RWResult result = 0; RWResult result = 0;
FMT_RETRY(result, FMT_POSIX_CALL(write(fd_, buffer, convert_rwcount(count)))); FMT_RETRY(result, FMT_POSIX_CALL(write(fd_, buffer, convert_rwcount(count))));
if (result < 0) if (result < 0) FMT_THROW(system_error(errno, "cannot write to file"));
FMT_THROW(system_error(errno, "cannot write to file"));
return internal::to_unsigned(result); return internal::to_unsigned(result);
} }
@ -182,19 +175,18 @@ void file::dup2(int fd) {
int result = 0; int result = 0;
FMT_RETRY(result, FMT_POSIX_CALL(dup2(fd_, fd))); FMT_RETRY(result, FMT_POSIX_CALL(dup2(fd_, fd)));
if (result == -1) { if (result == -1) {
FMT_THROW(system_error(errno, FMT_THROW(system_error(errno, "cannot duplicate file descriptor {} to {}",
"cannot duplicate file descriptor {} to {}", fd_, fd)); fd_, fd));
} }
} }
void file::dup2(int fd, error_code &ec) FMT_NOEXCEPT { void file::dup2(int fd, error_code& ec) FMT_NOEXCEPT {
int result = 0; int result = 0;
FMT_RETRY(result, FMT_POSIX_CALL(dup2(fd_, fd))); FMT_RETRY(result, FMT_POSIX_CALL(dup2(fd_, fd)));
if (result == -1) if (result == -1) ec = error_code(errno);
ec = error_code(errno);
} }
void file::pipe(file &read_end, file &write_end) { void file::pipe(file& read_end, file& write_end) {
// Close the descriptors first to make sure that assignments don't throw // Close the descriptors first to make sure that assignments don't throw
// and there are no leaks. // and there are no leaks.
read_end.close(); read_end.close();
@ -209,20 +201,19 @@ void file::pipe(file &read_end, file &write_end) {
// http://pubs.opengroup.org/onlinepubs/009696799/functions/pipe.html // http://pubs.opengroup.org/onlinepubs/009696799/functions/pipe.html
int result = FMT_POSIX_CALL(pipe(fds)); int result = FMT_POSIX_CALL(pipe(fds));
#endif #endif
if (result != 0) if (result != 0) FMT_THROW(system_error(errno, "cannot create pipe"));
FMT_THROW(system_error(errno, "cannot create pipe"));
// The following assignments don't throw because read_fd and write_fd // The following assignments don't throw because read_fd and write_fd
// are closed. // are closed.
read_end = file(fds[0]); read_end = file(fds[0]);
write_end = file(fds[1]); write_end = file(fds[1]);
} }
buffered_file file::fdopen(const char *mode) { buffered_file file::fdopen(const char* mode) {
// Don't retry as fdopen doesn't return EINTR. // Don't retry as fdopen doesn't return EINTR.
FILE *f = FMT_POSIX_CALL(fdopen(fd_, mode)); FILE* f = FMT_POSIX_CALL(fdopen(fd_, mode));
if (!f) if (!f)
FMT_THROW(system_error(errno, FMT_THROW(
"cannot associate stream with file descriptor")); system_error(errno, "cannot associate stream with file descriptor"));
buffered_file bf(f); buffered_file bf(f);
fd_ = -1; fd_ = -1;
return bf; return bf;
@ -235,10 +226,8 @@ long getpagesize() {
return si.dwPageSize; return si.dwPageSize;
#else #else
long size = FMT_POSIX_CALL(sysconf(_SC_PAGESIZE)); long size = FMT_POSIX_CALL(sysconf(_SC_PAGESIZE));
if (size < 0) if (size < 0) FMT_THROW(system_error(errno, "cannot get memory page size"));
FMT_THROW(system_error(errno, "cannot get memory page size"));
return size; return size;
#endif #endif
} }
FMT_END_NAMESPACE FMT_END_NAMESPACE

View File

@ -9,14 +9,14 @@
#include "gtest.h" #include "gtest.h"
#if GTEST_HAS_DEATH_TEST #if GTEST_HAS_DEATH_TEST
# define EXPECT_DEBUG_DEATH_IF_SUPPORTED(statement, regex) \ # define EXPECT_DEBUG_DEATH_IF_SUPPORTED(statement, regex) \
EXPECT_DEBUG_DEATH(statement, regex) EXPECT_DEBUG_DEATH(statement, regex)
#else #else
# define EXPECT_DEBUG_DEATH_IF_SUPPORTED(statement, regex) \ # define EXPECT_DEBUG_DEATH_IF_SUPPORTED(statement, regex) \
GTEST_UNSUPPORTED_DEATH_TEST_(statement, regex, ) GTEST_UNSUPPORTED_DEATH_TEST_(statement, regex, )
#endif #endif
TEST(AssertTest, Fail) { TEST(AssertTest, Fail) {
EXPECT_DEBUG_DEATH_IF_SUPPORTED( EXPECT_DEBUG_DEATH_IF_SUPPORTED(FMT_ASSERT(false, "don't panic!"),
FMT_ASSERT(false, "don't panic!"), "don't panic!"); "don't panic!");
} }

View File

@ -34,18 +34,19 @@ std::tm make_second(int s) {
return time; return time;
} }
std::string format_tm(const std::tm &time, const char *spec, std::string format_tm(const std::tm& time, const char* spec,
const std::locale &loc) { const std::locale& loc) {
auto &facet = std::use_facet<std::time_put<char>>(loc); auto& facet = std::use_facet<std::time_put<char>>(loc);
std::ostringstream os; std::ostringstream os;
os.imbue(loc); os.imbue(loc);
facet.put(os, os, ' ', &time, spec, spec + std::strlen(spec)); facet.put(os, os, ' ', &time, spec, spec + std::strlen(spec));
return os.str(); return os.str();
} }
#define EXPECT_TIME(spec, time, duration) { \ #define EXPECT_TIME(spec, time, duration) \
std::locale loc("ja_JP.utf8"); \ { \
EXPECT_EQ(format_tm(time, spec, loc), \ std::locale loc("ja_JP.utf8"); \
EXPECT_EQ(format_tm(time, spec, loc), \
fmt::format(loc, "{:" spec "}", duration)); \ fmt::format(loc, "{:" spec "}", duration)); \
} }
@ -83,12 +84,12 @@ TEST(ChronoTest, FormatDefault) {
fmt::format("{}", std::chrono::duration<int, std::exa>(42))); fmt::format("{}", std::chrono::duration<int, std::exa>(42)));
EXPECT_EQ("42m", fmt::format("{}", std::chrono::minutes(42))); EXPECT_EQ("42m", fmt::format("{}", std::chrono::minutes(42)));
EXPECT_EQ("42h", fmt::format("{}", std::chrono::hours(42))); EXPECT_EQ("42h", fmt::format("{}", std::chrono::hours(42)));
EXPECT_EQ("42[15]s", EXPECT_EQ(
fmt::format("{}", "42[15]s",
std::chrono::duration<int, std::ratio<15, 1>>(42))); fmt::format("{}", std::chrono::duration<int, std::ratio<15, 1>>(42)));
EXPECT_EQ("42[15/4]s", EXPECT_EQ(
fmt::format("{}", "42[15/4]s",
std::chrono::duration<int, std::ratio<15, 4>>(42))); fmt::format("{}", std::chrono::duration<int, std::ratio<15, 4>>(42)));
} }
TEST(ChronoTest, Align) { TEST(ChronoTest, Align) {
@ -105,7 +106,6 @@ TEST(ChronoTest, Align) {
fmt::format("{:~^12%H:%M:%S}", std::chrono::seconds(12345))); fmt::format("{:~^12%H:%M:%S}", std::chrono::seconds(12345)));
EXPECT_EQ("03:25:45 ", EXPECT_EQ("03:25:45 ",
fmt::format("{:{}%H:%M:%S}", std::chrono::seconds(12345), 12)); fmt::format("{:{}%H:%M:%S}", std::chrono::seconds(12345), 12));
} }
TEST(ChronoTest, FormatSpecs) { TEST(ChronoTest, FormatSpecs) {
@ -162,13 +162,14 @@ TEST(ChronoTest, InvalidSpecs) {
} }
TEST(ChronoTest, Locale) { TEST(ChronoTest, Locale) {
const char *loc_name = "ja_JP.utf8"; const char* loc_name = "ja_JP.utf8";
bool has_locale = false; bool has_locale = false;
std::locale loc; std::locale loc;
try { try {
loc = std::locale(loc_name); loc = std::locale(loc_name);
has_locale = true; has_locale = true;
} catch (const std::runtime_error &) {} } catch (const std::runtime_error&) {
}
if (!has_locale) { if (!has_locale) {
fmt::print("{} locale is missing.\n", loc_name); fmt::print("{} locale is missing.\n", loc_name);
return; return;

View File

@ -11,9 +11,9 @@
#include <functional> #include <functional>
#include <iterator> #include <iterator>
#include <limits> #include <limits>
#include <memory>
#include <string> #include <string>
#include <type_traits> #include <type_traits>
#include <memory>
#include "test-assert.h" #include "test-assert.h"
@ -21,7 +21,7 @@
// Check if fmt/core.h compiles with windows.h included before it. // Check if fmt/core.h compiles with windows.h included before it.
#ifdef _WIN32 #ifdef _WIN32
# include <windows.h> # include <windows.h>
#endif #endif
#include "fmt/core.h" #include "fmt/core.h"
@ -30,9 +30,9 @@
#undef max #undef max
using fmt::basic_format_arg; using fmt::basic_format_arg;
using fmt::string_view;
using fmt::internal::basic_buffer; using fmt::internal::basic_buffer;
using fmt::internal::value; using fmt::internal::value;
using fmt::string_view;
using testing::_; using testing::_;
using testing::StrictMock; using testing::StrictMock;
@ -42,24 +42,23 @@ namespace {
struct test_struct {}; struct test_struct {};
template <typename Context, typename T> template <typename Context, typename T>
basic_format_arg<Context> make_arg(const T &value) { basic_format_arg<Context> make_arg(const T& value) {
return fmt::internal::make_arg<Context>(value); return fmt::internal::make_arg<Context>(value);
} }
} // namespace } // namespace
FMT_BEGIN_NAMESPACE FMT_BEGIN_NAMESPACE
template <typename Char> template <typename Char> struct formatter<test_struct, Char> {
struct formatter<test_struct, Char> {
template <typename ParseContext> template <typename ParseContext>
auto parse(ParseContext &ctx) -> decltype(ctx.begin()) { auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
return ctx.begin(); return ctx.begin();
} }
typedef std::back_insert_iterator<basic_buffer<Char>> iterator; typedef std::back_insert_iterator<basic_buffer<Char>> iterator;
auto format(test_struct, basic_format_context<iterator, char> &ctx) auto format(test_struct, basic_format_context<iterator, char>& ctx)
-> decltype(ctx.out()) { -> decltype(ctx.out()) {
const Char *test = "test"; const Char* test = "test";
return std::copy_n(test, std::strlen(test), ctx.out()); return std::copy_n(test, std::strlen(test), ctx.out());
} }
}; };
@ -67,31 +66,29 @@ FMT_END_NAMESPACE
#if !FMT_GCC_VERSION || FMT_GCC_VERSION >= 470 #if !FMT_GCC_VERSION || FMT_GCC_VERSION >= 470
TEST(BufferTest, Noncopyable) { TEST(BufferTest, Noncopyable) {
EXPECT_FALSE(std::is_copy_constructible<basic_buffer<char> >::value); EXPECT_FALSE(std::is_copy_constructible<basic_buffer<char>>::value);
#if !FMT_MSC_VER # if !FMT_MSC_VER
// std::is_copy_assignable is broken in MSVC2013. // std::is_copy_assignable is broken in MSVC2013.
EXPECT_FALSE(std::is_copy_assignable<basic_buffer<char> >::value); EXPECT_FALSE(std::is_copy_assignable<basic_buffer<char>>::value);
#endif # endif
} }
TEST(BufferTest, Nonmoveable) { TEST(BufferTest, Nonmoveable) {
EXPECT_FALSE(std::is_move_constructible<basic_buffer<char> >::value); EXPECT_FALSE(std::is_move_constructible<basic_buffer<char>>::value);
#if !FMT_MSC_VER # if !FMT_MSC_VER
// std::is_move_assignable is broken in MSVC2013. // std::is_move_assignable is broken in MSVC2013.
EXPECT_FALSE(std::is_move_assignable<basic_buffer<char> >::value); EXPECT_FALSE(std::is_move_assignable<basic_buffer<char>>::value);
#endif # endif
} }
#endif #endif
// A test buffer with a dummy grow method. // A test buffer with a dummy grow method.
template <typename T> template <typename T> struct test_buffer : basic_buffer<T> {
struct test_buffer : basic_buffer<T> {
void grow(std::size_t capacity) { this->set(FMT_NULL, capacity); } void grow(std::size_t capacity) { this->set(FMT_NULL, capacity); }
}; };
template <typename T> template <typename T> struct mock_buffer : basic_buffer<T> {
struct mock_buffer : basic_buffer<T> { MOCK_METHOD1(do_grow, void(std::size_t capacity));
MOCK_METHOD1(do_grow, void (std::size_t capacity));
void grow(std::size_t capacity) { void grow(std::size_t capacity) {
this->set(this->data(), capacity); this->set(this->data(), capacity);
@ -99,8 +96,8 @@ struct mock_buffer : basic_buffer<T> {
} }
mock_buffer() {} mock_buffer() {}
mock_buffer(T *data) { this->set(data, 0); } mock_buffer(T* data) { this->set(data, 0); }
mock_buffer(T *data, std::size_t capacity) { this->set(data, capacity); } mock_buffer(T* data, std::size_t capacity) { this->set(data, capacity); }
}; };
TEST(BufferTest, Ctor) { TEST(BufferTest, Ctor) {
@ -134,9 +131,9 @@ struct dying_buffer : test_buffer<int> {
TEST(BufferTest, VirtualDtor) { TEST(BufferTest, VirtualDtor) {
typedef StrictMock<dying_buffer> stict_mock_buffer; typedef StrictMock<dying_buffer> stict_mock_buffer;
stict_mock_buffer *mock_buffer = new stict_mock_buffer(); stict_mock_buffer* mock_buffer = new stict_mock_buffer();
EXPECT_CALL(*mock_buffer, die()); EXPECT_CALL(*mock_buffer, die());
basic_buffer<int> *buffer = mock_buffer; basic_buffer<int>* buffer = mock_buffer;
delete buffer; delete buffer;
} }
@ -147,7 +144,7 @@ TEST(BufferTest, Access) {
EXPECT_EQ(11, buffer[0]); EXPECT_EQ(11, buffer[0]);
buffer[3] = 42; buffer[3] = 42;
EXPECT_EQ(42, *(&buffer[0] + 3)); EXPECT_EQ(42, *(&buffer[0] + 3));
const basic_buffer<char> &const_buffer = buffer; const basic_buffer<char>& const_buffer = buffer;
EXPECT_EQ(42, const_buffer[3]); EXPECT_EQ(42, const_buffer[3]);
} }
@ -182,7 +179,7 @@ TEST(BufferTest, Clear) {
TEST(BufferTest, Append) { TEST(BufferTest, Append) {
char data[15]; char data[15];
mock_buffer<char> buffer(data, 10); mock_buffer<char> buffer(data, 10);
const char *test = "test"; const char* test = "test";
buffer.append(test, test + 5); buffer.append(test, test + 5);
EXPECT_STREQ(test, &buffer[0]); EXPECT_STREQ(test, &buffer[0]);
EXPECT_EQ(5u, buffer.size()); EXPECT_EQ(5u, buffer.size());
@ -197,7 +194,7 @@ TEST(BufferTest, Append) {
TEST(BufferTest, AppendAllocatesEnoughStorage) { TEST(BufferTest, AppendAllocatesEnoughStorage) {
char data[19]; char data[19];
mock_buffer<char> buffer(data, 10); mock_buffer<char> buffer(data, 10);
const char *test = "abcdefgh"; const char* test = "abcdefgh";
buffer.resize(10); buffer.resize(10);
EXPECT_CALL(buffer, do_grow(19)); EXPECT_CALL(buffer, do_grow(19));
buffer.append(test, test + 9); buffer.append(test, test + 9);
@ -211,15 +208,14 @@ TEST(ArgTest, FormatArgs) {
struct custom_context { struct custom_context {
typedef char char_type; typedef char char_type;
template <typename T> template <typename T> struct formatter_type {
struct formatter_type {
struct type { struct type {
template <typename ParseContext> template <typename ParseContext>
auto parse(ParseContext &ctx) -> decltype(ctx.begin()) { auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
return ctx.begin(); return ctx.begin();
} }
const char *format(const T &, custom_context& ctx) { const char* format(const T&, custom_context& ctx) {
ctx.called = true; ctx.called = true;
return FMT_NULL; return FMT_NULL;
} }
@ -231,7 +227,7 @@ struct custom_context {
fmt::format_parse_context parse_context() { fmt::format_parse_context parse_context() {
return fmt::format_parse_context(""); return fmt::format_parse_context("");
} }
void advance_to(const char *) {} void advance_to(const char*) {}
}; };
TEST(ArgTest, MakeValueWithCustomContext) { TEST(ArgTest, MakeValueWithCustomContext) {
@ -249,40 +245,35 @@ template <typename Char>
bool operator==(custom_value<Char> lhs, custom_value<Char> rhs) { bool operator==(custom_value<Char> lhs, custom_value<Char> rhs) {
return lhs.value == rhs.value; return lhs.value == rhs.value;
} }
} } // namespace internal
FMT_END_NAMESPACE FMT_END_NAMESPACE
// Use a unique result type to make sure that there are no undesirable // Use a unique result type to make sure that there are no undesirable
// conversions. // conversions.
struct test_result {}; struct test_result {};
template <typename T> template <typename T> struct mock_visitor {
struct mock_visitor { template <typename U> struct result { typedef test_result type; };
template <typename U>
struct result { typedef test_result type; };
mock_visitor() { mock_visitor() {
ON_CALL(*this, visit(_)).WillByDefault(testing::Return(test_result())); ON_CALL(*this, visit(_)).WillByDefault(testing::Return(test_result()));
} }
MOCK_METHOD1_T(visit, test_result (T value)); MOCK_METHOD1_T(visit, test_result(T value));
MOCK_METHOD0_T(unexpected, void ()); MOCK_METHOD0_T(unexpected, void());
test_result operator()(T value) { return visit(value); } test_result operator()(T value) { return visit(value); }
template <typename U> template <typename U> test_result operator()(U) {
test_result operator()(U) {
unexpected(); unexpected();
return test_result(); return test_result();
} }
}; };
template <typename T> template <typename T> struct visit_type { typedef T Type; };
struct visit_type { typedef T Type; };
#define VISIT_TYPE(Type_, visit_type_) \ #define VISIT_TYPE(Type_, visit_type_) \
template <> \ template <> struct visit_type<Type_> { typedef visit_type_ Type; }
struct visit_type<Type_> { typedef visit_type_ Type; }
VISIT_TYPE(signed char, int); VISIT_TYPE(signed char, int);
VISIT_TYPE(unsigned char, unsigned); VISIT_TYPE(unsigned char, unsigned);
@ -299,28 +290,30 @@ VISIT_TYPE(unsigned long, unsigned long long);
VISIT_TYPE(float, double); VISIT_TYPE(float, double);
#define CHECK_ARG_(Char, expected, value) { \ #define CHECK_ARG_(Char, expected, value) \
testing::StrictMock<mock_visitor<decltype(expected)>> visitor; \ { \
EXPECT_CALL(visitor, visit(expected)); \ testing::StrictMock<mock_visitor<decltype(expected)>> visitor; \
typedef std::back_insert_iterator<basic_buffer<Char>> iterator; \ EXPECT_CALL(visitor, visit(expected)); \
fmt::visit(visitor, \ typedef std::back_insert_iterator<basic_buffer<Char>> iterator; \
make_arg<fmt::basic_format_context<iterator, Char>>(value)); \ fmt::visit(visitor, \
} make_arg<fmt::basic_format_context<iterator, Char>>(value)); \
}
#define CHECK_ARG(value, typename_) { \ #define CHECK_ARG(value, typename_) \
typedef decltype(value) value_type; \ { \
typename_ visit_type<value_type>::Type expected = value; \ typedef decltype(value) value_type; \
CHECK_ARG_(char, expected, value) \ typename_ visit_type<value_type>::Type expected = value; \
CHECK_ARG_(wchar_t, expected, value) \ CHECK_ARG_(char, expected, value) \
} CHECK_ARG_(wchar_t, expected, value) \
}
template <typename T> template <typename T> class NumericArgTest : public testing::Test {};
class NumericArgTest : public testing::Test {};
typedef ::testing::Types< typedef ::testing::Types<bool, signed char, unsigned char, signed,
bool, signed char, unsigned char, signed, unsigned short, unsigned short, int, unsigned, long, unsigned long,
int, unsigned, long, unsigned long, long long, unsigned long long, long long, unsigned long long, float, double,
float, double, long double> Types; long double>
Types;
TYPED_TEST_CASE(NumericArgTest, Types); TYPED_TEST_CASE(NumericArgTest, Types);
template <typename T> template <typename T>
@ -330,7 +323,7 @@ typename std::enable_if<std::is_integral<T>::value, T>::type test_value() {
template <typename T> template <typename T>
typename std::enable_if<std::is_floating_point<T>::value, T>::type typename std::enable_if<std::is_floating_point<T>::value, T>::type
test_value() { test_value() {
return static_cast<T>(4.2); return static_cast<T>(4.2);
} }
@ -348,8 +341,8 @@ TEST(ArgTest, CharArg) {
TEST(ArgTest, StringArg) { TEST(ArgTest, StringArg) {
char str_data[] = "test"; char str_data[] = "test";
char *str = str_data; char* str = str_data;
const char *cstr = str; const char* cstr = str;
CHECK_ARG_(char, cstr, str); CHECK_ARG_(char, cstr, str);
string_view sref(str); string_view sref(str);
@ -358,8 +351,8 @@ TEST(ArgTest, StringArg) {
TEST(ArgTest, WStringArg) { TEST(ArgTest, WStringArg) {
wchar_t str_data[] = L"test"; wchar_t str_data[] = L"test";
wchar_t *str = str_data; wchar_t* str = str_data;
const wchar_t *cstr = str; const wchar_t* cstr = str;
fmt::wstring_view sref(str); fmt::wstring_view sref(str);
CHECK_ARG_(wchar_t, cstr, str); CHECK_ARG_(wchar_t, cstr, str);
@ -369,8 +362,8 @@ TEST(ArgTest, WStringArg) {
} }
TEST(ArgTest, PointerArg) { TEST(ArgTest, PointerArg) {
void *p = FMT_NULL; void* p = FMT_NULL;
const void *cp = FMT_NULL; const void* cp = FMT_NULL;
CHECK_ARG_(char, cp, p); CHECK_ARG_(char, cp, p);
CHECK_ARG_(wchar_t, cp, p); CHECK_ARG_(wchar_t, cp, p);
CHECK_ARG(cp, ); CHECK_ARG(cp, );
@ -384,7 +377,7 @@ struct check_custom {
test_buffer() : fmt::internal::basic_buffer<char>(data, 0, 10) {} test_buffer() : fmt::internal::basic_buffer<char>(data, 0, 10) {}
void grow(std::size_t) {} void grow(std::size_t) {}
} buffer; } buffer;
fmt::internal::basic_buffer<char> &base = buffer; fmt::internal::basic_buffer<char>& base = buffer;
fmt::format_context ctx(std::back_inserter(base), "", fmt::format_args()); fmt::format_context ctx(std::back_inserter(base), "", fmt::format_args());
h.format(ctx); h.format(ctx);
EXPECT_EQ("test", std::string(buffer.data, buffer.size())); EXPECT_EQ("test", std::string(buffer.data, buffer.size()));
@ -395,14 +388,14 @@ struct check_custom {
TEST(ArgTest, CustomArg) { TEST(ArgTest, CustomArg) {
test_struct test; test_struct test;
typedef mock_visitor<fmt::basic_format_arg<fmt::format_context>::handle> typedef mock_visitor<fmt::basic_format_arg<fmt::format_context>::handle>
visitor; visitor;
testing::StrictMock<visitor> v; testing::StrictMock<visitor> v;
EXPECT_CALL(v, visit(_)).WillOnce(testing::Invoke(check_custom())); EXPECT_CALL(v, visit(_)).WillOnce(testing::Invoke(check_custom()));
fmt::visit(v, make_arg<fmt::format_context>(test)); fmt::visit(v, make_arg<fmt::format_context>(test));
} }
TEST(ArgTest, VisitInvalidArg) { TEST(ArgTest, VisitInvalidArg) {
testing::StrictMock< mock_visitor<fmt::monostate> > visitor; testing::StrictMock<mock_visitor<fmt::monostate>> visitor;
EXPECT_CALL(visitor, visit(_)); EXPECT_CALL(visitor, visit(_));
fmt::basic_format_arg<fmt::format_context> arg; fmt::basic_format_arg<fmt::format_context> arg;
visit(visitor, arg); visit(visitor, arg);
@ -416,9 +409,8 @@ TEST(StringViewTest, Length) {
} }
// Check string_view's comparison operator. // Check string_view's comparison operator.
template <template <typename> class Op> template <template <typename> class Op> void check_op() {
void check_op() { const char* inputs[] = {"foo", "fop", "fo"};
const char *inputs[] = {"foo", "fop", "fo"};
std::size_t num_inputs = sizeof(inputs) / sizeof(*inputs); std::size_t num_inputs = sizeof(inputs) / sizeof(*inputs);
for (std::size_t i = 0; i < num_inputs; ++i) { for (std::size_t i = 0; i < num_inputs; ++i) {
for (std::size_t j = 0; j < num_inputs; ++j) { for (std::size_t j = 0; j < num_inputs; ++j) {
@ -446,7 +438,7 @@ enum basic_enum {};
TEST(CoreTest, ConvertToInt) { TEST(CoreTest, ConvertToInt) {
EXPECT_FALSE((fmt::convert_to_int<char, char>::value)); EXPECT_FALSE((fmt::convert_to_int<char, char>::value));
EXPECT_FALSE((fmt::convert_to_int<const char *, char>::value)); EXPECT_FALSE((fmt::convert_to_int<const char*, char>::value));
EXPECT_TRUE((fmt::convert_to_int<basic_enum, char>::value)); EXPECT_TRUE((fmt::convert_to_int<basic_enum, char>::value));
} }
@ -457,31 +449,31 @@ TEST(CoreTest, IsEnumConvertibleToInt) {
} }
namespace my_ns { namespace my_ns {
template <typename Char> template <typename Char> class my_string {
class my_string {
public: public:
my_string(const Char *s) : s_(s) {} my_string(const Char* s) : s_(s) {}
const Char * data() const FMT_NOEXCEPT { return s_.data(); } const Char* data() const FMT_NOEXCEPT { return s_.data(); }
std::size_t length() const FMT_NOEXCEPT { return s_.size(); } std::size_t length() const FMT_NOEXCEPT { return s_.size(); }
operator const Char*() const { return s_.c_str(); } operator const Char*() const { return s_.c_str(); }
private: private:
std::basic_string<Char> s_; std::basic_string<Char> s_;
}; };
template <typename Char> template <typename Char>
inline fmt::basic_string_view<Char> inline fmt::basic_string_view<Char> to_string_view(const my_string<Char>& s)
to_string_view(const my_string<Char> &s) FMT_NOEXCEPT { FMT_NOEXCEPT {
return { s.data(), s.length() }; return {s.data(), s.length()};
} }
struct non_string {}; struct non_string {};
} } // namespace my_ns
namespace FakeQt { namespace FakeQt {
class QString { class QString {
public: public:
QString(const wchar_t *s) : s_(std::make_shared<std::wstring>(s)) {} QString(const wchar_t* s) : s_(std::make_shared<std::wstring>(s)) {}
const wchar_t *utf16() const FMT_NOEXCEPT { return s_->data(); } const wchar_t* utf16() const FMT_NOEXCEPT { return s_->data(); }
int size() const FMT_NOEXCEPT { return static_cast<int>(s_->size()); } int size() const FMT_NOEXCEPT { return static_cast<int>(s_->size()); }
#ifdef FMT_STRING_VIEW #ifdef FMT_STRING_VIEW
operator FMT_STRING_VIEW<wchar_t>() const FMT_NOEXCEPT { return *s_; } operator FMT_STRING_VIEW<wchar_t>() const FMT_NOEXCEPT { return *s_; }
@ -490,15 +482,13 @@ class QString {
std::shared_ptr<std::wstring> s_; std::shared_ptr<std::wstring> s_;
}; };
inline fmt::basic_string_view<wchar_t> to_string_view( inline fmt::basic_string_view<wchar_t> to_string_view(const QString& s)
const QString &s) FMT_NOEXCEPT { FMT_NOEXCEPT {
return {s.utf16(), return {s.utf16(), static_cast<std::size_t>(s.size())};
static_cast<std::size_t>(s.size())};
}
} }
} // namespace FakeQt
template <typename T> template <typename T> class IsStringTest : public testing::Test {};
class IsStringTest : public testing::Test {};
typedef ::testing::Types<char, wchar_t, char16_t, char32_t> StringCharTypes; typedef ::testing::Types<char, wchar_t, char16_t, char32_t> StringCharTypes;
TYPED_TEST_CASE(IsStringTest, StringCharTypes); TYPED_TEST_CASE(IsStringTest, StringCharTypes);
@ -506,18 +496,18 @@ TYPED_TEST_CASE(IsStringTest, StringCharTypes);
namespace { namespace {
template <typename Char> template <typename Char>
struct derived_from_string_view : fmt::basic_string_view<Char> {}; struct derived_from_string_view : fmt::basic_string_view<Char> {};
} } // namespace
TYPED_TEST(IsStringTest, IsString) { TYPED_TEST(IsStringTest, IsString) {
EXPECT_TRUE((fmt::internal::is_string<TypeParam *>::value)); EXPECT_TRUE((fmt::internal::is_string<TypeParam*>::value));
EXPECT_TRUE((fmt::internal::is_string<const TypeParam *>::value)); EXPECT_TRUE((fmt::internal::is_string<const TypeParam*>::value));
EXPECT_TRUE((fmt::internal::is_string<TypeParam[2]>::value)); EXPECT_TRUE((fmt::internal::is_string<TypeParam[2]>::value));
EXPECT_TRUE((fmt::internal::is_string<const TypeParam[2]>::value)); EXPECT_TRUE((fmt::internal::is_string<const TypeParam[2]>::value));
EXPECT_TRUE((fmt::internal::is_string<std::basic_string<TypeParam>>::value)); EXPECT_TRUE((fmt::internal::is_string<std::basic_string<TypeParam>>::value));
EXPECT_TRUE( EXPECT_TRUE(
(fmt::internal::is_string<fmt::basic_string_view<TypeParam>>::value)); (fmt::internal::is_string<fmt::basic_string_view<TypeParam>>::value));
EXPECT_TRUE( EXPECT_TRUE(
(fmt::internal::is_string<derived_from_string_view<TypeParam>>::value)); (fmt::internal::is_string<derived_from_string_view<TypeParam>>::value));
#ifdef FMT_STRING_VIEW #ifdef FMT_STRING_VIEW
EXPECT_TRUE((fmt::internal::is_string<FMT_STRING_VIEW<TypeParam>>::value)); EXPECT_TRUE((fmt::internal::is_string<FMT_STRING_VIEW<TypeParam>>::value));
#endif #endif
@ -529,7 +519,7 @@ TYPED_TEST(IsStringTest, IsString) {
TEST(CoreTest, Format) { TEST(CoreTest, Format) {
// This should work without including fmt/format.h. // This should work without including fmt/format.h.
#ifdef FMT_FORMAT_H_ #ifdef FMT_FORMAT_H_
# error fmt/format.h must not be included in the core test # error fmt/format.h must not be included in the core test
#endif #endif
EXPECT_EQ(fmt::format("{}", 42), "42"); EXPECT_EQ(fmt::format("{}", 42), "42");
} }
@ -537,7 +527,7 @@ TEST(CoreTest, Format) {
TEST(CoreTest, FormatTo) { TEST(CoreTest, FormatTo) {
// This should work without including fmt/format.h. // This should work without including fmt/format.h.
#ifdef FMT_FORMAT_H_ #ifdef FMT_FORMAT_H_
# error fmt/format.h must not be included in the core test # error fmt/format.h must not be included in the core test
#endif #endif
std::string s; std::string s;
fmt::format_to(std::back_inserter(s), "{}", 42); fmt::format_to(std::back_inserter(s), "{}", 42);
@ -600,11 +590,12 @@ TEST(FormatterTest, FormatExplicitlyConvertibleToWStringView) {
} }
struct explicitly_convertible_to_string_like { struct explicitly_convertible_to_string_like {
template < template <typename String,
typename String, typename = typename std::enable_if<std::is_constructible<
typename = typename std::enable_if< String, const char*, std::size_t>::value>::type>
std::is_constructible<String, const char*, std::size_t>::value>::type> FMT_EXPLICIT operator String() const {
FMT_EXPLICIT operator String() const { return String("foo", 3u); } return String("foo", 3u);
}
}; };
TEST(FormatterTest, FormatExplicitlyConvertibleToStringLike) { TEST(FormatterTest, FormatExplicitlyConvertibleToStringLike) {

View File

@ -13,22 +13,21 @@
// A custom argument formatter that doesn't print `-` for floating-point values // A custom argument formatter that doesn't print `-` for floating-point values
// rounded to 0. // rounded to 0.
class custom_arg_formatter : class custom_arg_formatter
public fmt::arg_formatter<fmt::back_insert_range<fmt::internal::buffer>> { : public fmt::arg_formatter<fmt::back_insert_range<fmt::internal::buffer>> {
public: public:
typedef fmt::back_insert_range<fmt::internal::buffer> range; typedef fmt::back_insert_range<fmt::internal::buffer> range;
typedef fmt::arg_formatter<range> base; typedef fmt::arg_formatter<range> base;
custom_arg_formatter( custom_arg_formatter(fmt::format_context& ctx,
fmt::format_context &ctx, fmt::format_specs *s = FMT_NULL) fmt::format_specs* s = FMT_NULL)
: base(ctx, s) {} : base(ctx, s) {}
using base::operator(); using base::operator();
iterator operator()(double value) { iterator operator()(double value) {
// Comparing a float to 0.0 is safe. // Comparing a float to 0.0 is safe.
if (round(value * pow(10, spec()->precision)) == 0.0) if (round(value * pow(10, spec()->precision)) == 0.0) value = 0;
value = 0;
return base::operator()(value); return base::operator()(value);
} }
}; };
@ -41,7 +40,7 @@ std::string custom_vformat(fmt::string_view format_str, fmt::format_args args) {
} }
template <typename... Args> template <typename... Args>
std::string custom_format(const char *format_str, const Args & ... args) { std::string custom_format(const char* format_str, const Args&... args) {
auto va = fmt::make_format_args(args...); auto va = fmt::make_format_args(args...);
return custom_vformat(format_str, va); return custom_vformat(format_str, va);
} }

View File

@ -25,20 +25,18 @@
#undef max #undef max
#if FMT_HAS_CPP_ATTRIBUTE(noreturn) #if FMT_HAS_CPP_ATTRIBUTE(noreturn)
# define FMT_NORETURN [[noreturn]] # define FMT_NORETURN [[noreturn]]
#else #else
# define FMT_NORETURN # define FMT_NORETURN
#endif #endif
using fmt::internal::fp; using fmt::internal::fp;
template <bool is_iec559> template <bool is_iec559> void test_construct_from_double() {
void test_construct_from_double() {
fmt::print("warning: double is not IEC559, skipping FP tests\n"); fmt::print("warning: double is not IEC559, skipping FP tests\n");
} }
template <> template <> void test_construct_from_double<true>() {
void test_construct_from_double<true>() {
auto v = fp(1.23); auto v = fp(1.23);
EXPECT_EQ(v.f, 0x13ae147ae147aeu); EXPECT_EQ(v.f, 0x13ae147ae147aeu);
EXPECT_EQ(v.e, -52); EXPECT_EQ(v.e, -52);
@ -107,14 +105,10 @@ TEST(FPTest, Grisu2FormatCompilesWithNonIEEEDouble) {
grisu2_format(4.2f, buf, fmt::core_format_specs()); grisu2_format(4.2f, buf, fmt::core_format_specs());
} }
template <typename T> template <typename T> struct ValueExtractor : fmt::internal::function<T> {
struct ValueExtractor: fmt::internal::function<T> { T operator()(T value) { return value; }
T operator()(T value) {
return value;
}
template <typename U> template <typename U> FMT_NORETURN T operator()(U) {
FMT_NORETURN T operator()(U) {
throw std::runtime_error(fmt::format("invalid type {}", typeid(U).name())); throw std::runtime_error(fmt::format("invalid type {}", typeid(U).name()));
} }
}; };
@ -136,7 +130,7 @@ TEST(FormatTest, FormatNegativeNaN) {
} }
TEST(FormatTest, StrError) { TEST(FormatTest, StrError) {
char *message = FMT_NULL; char* message = FMT_NULL;
char buffer[BUFFER_SIZE]; char buffer[BUFFER_SIZE];
EXPECT_ASSERT(fmt::safe_strerror(EDOM, message = FMT_NULL, 0), EXPECT_ASSERT(fmt::safe_strerror(EDOM, message = FMT_NULL, 0),
"invalid buffer"); "invalid buffer");
@ -178,8 +172,8 @@ TEST(FormatTest, FormatErrorCode) {
} }
{ {
fmt::memory_buffer buffer; fmt::memory_buffer buffer;
std::string prefix( std::string prefix(fmt::inline_buffer_size - msg.size() - sep.size() + 1,
fmt::inline_buffer_size - msg.size() - sep.size() + 1, 'x'); 'x');
fmt::format_error_code(buffer, 42, prefix); fmt::format_error_code(buffer, 42, prefix);
EXPECT_EQ(msg, to_string(buffer)); EXPECT_EQ(msg, to_string(buffer));
} }
@ -188,8 +182,7 @@ TEST(FormatTest, FormatErrorCode) {
// Test maximum buffer size. // Test maximum buffer size.
msg = fmt::format("error {}", codes[i]); msg = fmt::format("error {}", codes[i]);
fmt::memory_buffer buffer; fmt::memory_buffer buffer;
std::string prefix( std::string prefix(fmt::inline_buffer_size - msg.size() - sep.size(), 'x');
fmt::inline_buffer_size - msg.size() - sep.size(), 'x');
fmt::format_error_code(buffer, codes[i], prefix); fmt::format_error_code(buffer, codes[i], prefix);
EXPECT_EQ(prefix + sep + msg, to_string(buffer)); EXPECT_EQ(prefix + sep + msg, to_string(buffer));
std::size_t size = fmt::inline_buffer_size; std::size_t size = fmt::inline_buffer_size;
@ -231,7 +224,7 @@ TEST(ColorsTest, ColorsPrint) {
EXPECT_WRITE(stderr, fmt::print(stderr, fmt::emphasis::bold, "bold error"), EXPECT_WRITE(stderr, fmt::print(stderr, fmt::emphasis::bold, "bold error"),
"\x1b[1mbold error\x1b[0m"); "\x1b[1mbold error\x1b[0m");
EXPECT_WRITE(stderr, fmt::print(stderr, fg(fmt::color::blue), "blue log"), EXPECT_WRITE(stderr, fmt::print(stderr, fg(fmt::color::blue), "blue log"),
"\x1b[38;2;000;000;255mblue log\x1b[0m"); "\x1b[38;2;000;000;255mblue log\x1b[0m");
EXPECT_WRITE(stdout, fmt::print(fmt::text_style(), "hi"), "hi"); EXPECT_WRITE(stdout, fmt::print(fmt::text_style(), "hi"), "hi");
EXPECT_WRITE(stdout, fmt::print(fg(fmt::terminal_color::red), "tred"), EXPECT_WRITE(stdout, fmt::print(fg(fmt::terminal_color::red), "tred"),
"\x1b[31mtred\x1b[0m"); "\x1b[31mtred\x1b[0m");
@ -250,18 +243,19 @@ TEST(ColorsTest, ColorsFormat) {
"\x1b[38;2;255;020;030mrgb(255,20,30)\x1b[0m"); "\x1b[38;2;255;020;030mrgb(255,20,30)\x1b[0m");
EXPECT_EQ(fmt::format(fg(fmt::color::blue), "blue"), EXPECT_EQ(fmt::format(fg(fmt::color::blue), "blue"),
"\x1b[38;2;000;000;255mblue\x1b[0m"); "\x1b[38;2;000;000;255mblue\x1b[0m");
EXPECT_EQ(fmt::format(fg(fmt::color::blue) | bg(fmt::color::red), "two color"), EXPECT_EQ(
"\x1b[38;2;000;000;255m\x1b[48;2;255;000;000mtwo color\x1b[0m"); fmt::format(fg(fmt::color::blue) | bg(fmt::color::red), "two color"),
EXPECT_EQ(fmt::format(fmt::emphasis::bold, "bold"), "\x1b[38;2;000;000;255m\x1b[48;2;255;000;000mtwo color\x1b[0m");
"\x1b[1mbold\x1b[0m"); EXPECT_EQ(fmt::format(fmt::emphasis::bold, "bold"), "\x1b[1mbold\x1b[0m");
EXPECT_EQ(fmt::format(fmt::emphasis::italic, "italic"), EXPECT_EQ(fmt::format(fmt::emphasis::italic, "italic"),
"\x1b[3mitalic\x1b[0m"); "\x1b[3mitalic\x1b[0m");
EXPECT_EQ(fmt::format(fmt::emphasis::underline, "underline"), EXPECT_EQ(fmt::format(fmt::emphasis::underline, "underline"),
"\x1b[4munderline\x1b[0m"); "\x1b[4munderline\x1b[0m");
EXPECT_EQ(fmt::format(fmt::emphasis::strikethrough, "strikethrough"), EXPECT_EQ(fmt::format(fmt::emphasis::strikethrough, "strikethrough"),
"\x1b[9mstrikethrough\x1b[0m"); "\x1b[9mstrikethrough\x1b[0m");
EXPECT_EQ(fmt::format(fg(fmt::color::blue) | fmt::emphasis::bold, "blue/bold"), EXPECT_EQ(
"\x1b[1m\x1b[38;2;000;000;255mblue/bold\x1b[0m"); fmt::format(fg(fmt::color::blue) | fmt::emphasis::bold, "blue/bold"),
"\x1b[1m\x1b[38;2;000;000;255mblue/bold\x1b[0m");
EXPECT_EQ(fmt::format(fmt::emphasis::bold, "bold error"), EXPECT_EQ(fmt::format(fmt::emphasis::bold, "bold error"),
"\x1b[1mbold error\x1b[0m"); "\x1b[1mbold error\x1b[0m");
EXPECT_EQ(fmt::format(fg(fmt::color::blue), "blue log"), EXPECT_EQ(fmt::format(fg(fmt::color::blue), "blue log"),

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -7,22 +7,22 @@
#include "gtest-extra.h" #include "gtest-extra.h"
#include <gtest/gtest-spi.h>
#include <algorithm> #include <algorithm>
#include <cstring> #include <cstring>
#include <memory> #include <memory>
#include <stdexcept> #include <stdexcept>
#include <gtest/gtest-spi.h>
#if defined(_WIN32) && !defined(__MINGW32__) #if defined(_WIN32) && !defined(__MINGW32__)
# include <crtdbg.h> // for _CrtSetReportMode # include <crtdbg.h> // for _CrtSetReportMode
#endif // _WIN32 #endif // _WIN32
#include "util.h" #include "util.h"
namespace { namespace {
// This is used to suppress coverity warnings about untrusted values. // This is used to suppress coverity warnings about untrusted values.
std::string sanitize(const std::string &s) { std::string sanitize(const std::string& s) {
std::string result; std::string result;
for (std::string::const_iterator i = s.begin(), end = s.end(); i != end; ++i) for (std::string::const_iterator i = s.begin(), end = s.end(); i != end; ++i)
result.push_back(static_cast<char>(*i & 0xff)); result.push_back(static_cast<char>(*i & 0xff));
@ -52,13 +52,9 @@ int SingleEvaluationTest::b_;
void do_nothing() {} void do_nothing() {}
void throw_exception() { void throw_exception() { throw std::runtime_error("test"); }
throw std::runtime_error("test");
}
void throw_system_error() { void throw_system_error() { throw fmt::system_error(EDOM, "test"); }
throw fmt::system_error(EDOM, "test");
}
// Tests that when EXPECT_THROW_MSG fails, it evaluates its message argument // Tests that when EXPECT_THROW_MSG fails, it evaluates its message argument
// exactly once. // exactly once.
@ -71,43 +67,50 @@ TEST_F(SingleEvaluationTest, FailedEXPECT_THROW_MSG) {
// Tests that when EXPECT_SYSTEM_ERROR fails, it evaluates its message argument // Tests that when EXPECT_SYSTEM_ERROR fails, it evaluates its message argument
// exactly once. // exactly once.
TEST_F(SingleEvaluationTest, FailedEXPECT_SYSTEM_ERROR) { TEST_F(SingleEvaluationTest, FailedEXPECT_SYSTEM_ERROR) {
EXPECT_NONFATAL_FAILURE( EXPECT_NONFATAL_FAILURE(EXPECT_SYSTEM_ERROR(throw_system_error(), EDOM, p_++),
EXPECT_SYSTEM_ERROR(throw_system_error(), EDOM, p_++), "01234"); "01234");
EXPECT_EQ(s_ + 1, p_); EXPECT_EQ(s_ + 1, p_);
} }
// Tests that when EXPECT_WRITE fails, it evaluates its message argument // Tests that when EXPECT_WRITE fails, it evaluates its message argument
// exactly once. // exactly once.
TEST_F(SingleEvaluationTest, FailedEXPECT_WRITE) { TEST_F(SingleEvaluationTest, FailedEXPECT_WRITE) {
EXPECT_NONFATAL_FAILURE( EXPECT_NONFATAL_FAILURE(EXPECT_WRITE(stdout, std::printf("test"), p_++),
EXPECT_WRITE(stdout, std::printf("test"), p_++), "01234"); "01234");
EXPECT_EQ(s_ + 1, p_); EXPECT_EQ(s_ + 1, p_);
} }
// Tests that assertion arguments are evaluated exactly once. // Tests that assertion arguments are evaluated exactly once.
TEST_F(SingleEvaluationTest, ExceptionTests) { TEST_F(SingleEvaluationTest, ExceptionTests) {
// successful EXPECT_THROW_MSG // successful EXPECT_THROW_MSG
EXPECT_THROW_MSG({ // NOLINT EXPECT_THROW_MSG(
a_++; { // NOLINT
throw_exception(); a_++;
}, std::exception, (b_++, "test")); throw_exception();
},
std::exception, (b_++, "test"));
EXPECT_EQ(1, a_); EXPECT_EQ(1, a_);
EXPECT_EQ(1, b_); EXPECT_EQ(1, b_);
// failed EXPECT_THROW_MSG, throws different type // failed EXPECT_THROW_MSG, throws different type
EXPECT_NONFATAL_FAILURE(EXPECT_THROW_MSG({ // NOLINT EXPECT_NONFATAL_FAILURE(EXPECT_THROW_MSG(
a_++; { // NOLINT
throw_exception(); a_++;
}, std::logic_error, (b_++, "test")), "throws a different type"); throw_exception();
},
std::logic_error, (b_++, "test")),
"throws a different type");
EXPECT_EQ(2, a_); EXPECT_EQ(2, a_);
EXPECT_EQ(2, b_); EXPECT_EQ(2, b_);
// failed EXPECT_THROW_MSG, throws an exception with different message // failed EXPECT_THROW_MSG, throws an exception with different message
EXPECT_NONFATAL_FAILURE(EXPECT_THROW_MSG({ // NOLINT EXPECT_NONFATAL_FAILURE(EXPECT_THROW_MSG(
a_++; { // NOLINT
throw_exception(); a_++;
}, std::exception, (b_++, "other")), throw_exception();
"throws an exception with a different message"); },
std::exception, (b_++, "other")),
"throws an exception with a different message");
EXPECT_EQ(3, a_); EXPECT_EQ(3, a_);
EXPECT_EQ(3, b_); EXPECT_EQ(3, b_);
@ -120,33 +123,40 @@ TEST_F(SingleEvaluationTest, ExceptionTests) {
TEST_F(SingleEvaluationTest, SystemErrorTests) { TEST_F(SingleEvaluationTest, SystemErrorTests) {
// successful EXPECT_SYSTEM_ERROR // successful EXPECT_SYSTEM_ERROR
EXPECT_SYSTEM_ERROR({ // NOLINT EXPECT_SYSTEM_ERROR(
a_++; { // NOLINT
throw_system_error(); a_++;
}, EDOM, (b_++, "test")); throw_system_error();
},
EDOM, (b_++, "test"));
EXPECT_EQ(1, a_); EXPECT_EQ(1, a_);
EXPECT_EQ(1, b_); EXPECT_EQ(1, b_);
// failed EXPECT_SYSTEM_ERROR, throws different type // failed EXPECT_SYSTEM_ERROR, throws different type
EXPECT_NONFATAL_FAILURE(EXPECT_SYSTEM_ERROR({ // NOLINT EXPECT_NONFATAL_FAILURE(EXPECT_SYSTEM_ERROR(
a_++; { // NOLINT
throw_exception(); a_++;
}, EDOM, (b_++, "test")), "throws a different type"); throw_exception();
},
EDOM, (b_++, "test")),
"throws a different type");
EXPECT_EQ(2, a_); EXPECT_EQ(2, a_);
EXPECT_EQ(2, b_); EXPECT_EQ(2, b_);
// failed EXPECT_SYSTEM_ERROR, throws an exception with different message // failed EXPECT_SYSTEM_ERROR, throws an exception with different message
EXPECT_NONFATAL_FAILURE(EXPECT_SYSTEM_ERROR({ // NOLINT EXPECT_NONFATAL_FAILURE(EXPECT_SYSTEM_ERROR(
a_++; { // NOLINT
throw_system_error(); a_++;
}, EDOM, (b_++, "other")), throw_system_error();
"throws an exception with a different message"); },
EDOM, (b_++, "other")),
"throws an exception with a different message");
EXPECT_EQ(3, a_); EXPECT_EQ(3, a_);
EXPECT_EQ(3, b_); EXPECT_EQ(3, b_);
// failed EXPECT_SYSTEM_ERROR, throws nothing // failed EXPECT_SYSTEM_ERROR, throws nothing
EXPECT_NONFATAL_FAILURE( EXPECT_NONFATAL_FAILURE(EXPECT_SYSTEM_ERROR(a_++, EDOM, (b_++, "test")),
EXPECT_SYSTEM_ERROR(a_++, EDOM, (b_++, "test")), "throws nothing"); "throws nothing");
EXPECT_EQ(4, a_); EXPECT_EQ(4, a_);
EXPECT_EQ(4, b_); EXPECT_EQ(4, b_);
} }
@ -154,18 +164,23 @@ TEST_F(SingleEvaluationTest, SystemErrorTests) {
// Tests that assertion arguments are evaluated exactly once. // Tests that assertion arguments are evaluated exactly once.
TEST_F(SingleEvaluationTest, WriteTests) { TEST_F(SingleEvaluationTest, WriteTests) {
// successful EXPECT_WRITE // successful EXPECT_WRITE
EXPECT_WRITE(stdout, { // NOLINT EXPECT_WRITE(stdout,
a_++; { // NOLINT
std::printf("test"); a_++;
}, (b_++, "test")); std::printf("test");
},
(b_++, "test"));
EXPECT_EQ(1, a_); EXPECT_EQ(1, a_);
EXPECT_EQ(1, b_); EXPECT_EQ(1, b_);
// failed EXPECT_WRITE // failed EXPECT_WRITE
EXPECT_NONFATAL_FAILURE(EXPECT_WRITE(stdout, { // NOLINT EXPECT_NONFATAL_FAILURE(EXPECT_WRITE(stdout,
a_++; { // NOLINT
std::printf("test"); a_++;
}, (b_++, "other")), "Actual: test"); std::printf("test");
},
(b_++, "other")),
"Actual: test");
EXPECT_EQ(2, a_); EXPECT_EQ(2, a_);
EXPECT_EQ(2, b_); EXPECT_EQ(2, b_);
} }
@ -178,8 +193,8 @@ TEST(ExpectThrowTest, DoesNotGenerateUnreachableCodeWarning) {
EXPECT_THROW_MSG(throw runtime_error(""), runtime_error, ""); EXPECT_THROW_MSG(throw runtime_error(""), runtime_error, "");
EXPECT_NONFATAL_FAILURE(EXPECT_THROW_MSG(n++, runtime_error, ""), ""); EXPECT_NONFATAL_FAILURE(EXPECT_THROW_MSG(n++, runtime_error, ""), "");
EXPECT_NONFATAL_FAILURE(EXPECT_THROW_MSG(throw 1, runtime_error, ""), ""); EXPECT_NONFATAL_FAILURE(EXPECT_THROW_MSG(throw 1, runtime_error, ""), "");
EXPECT_NONFATAL_FAILURE(EXPECT_THROW_MSG( EXPECT_NONFATAL_FAILURE(
throw runtime_error("a"), runtime_error, "b"), ""); EXPECT_THROW_MSG(throw runtime_error("a"), runtime_error, "b"), "");
} }
// Tests that the compiler will not complain about unreachable code in the // Tests that the compiler will not complain about unreachable code in the
@ -189,8 +204,9 @@ TEST(ExpectSystemErrorTest, DoesNotGenerateUnreachableCodeWarning) {
EXPECT_SYSTEM_ERROR(throw fmt::system_error(EDOM, "test"), EDOM, "test"); EXPECT_SYSTEM_ERROR(throw fmt::system_error(EDOM, "test"), EDOM, "test");
EXPECT_NONFATAL_FAILURE(EXPECT_SYSTEM_ERROR(n++, EDOM, ""), ""); EXPECT_NONFATAL_FAILURE(EXPECT_SYSTEM_ERROR(n++, EDOM, ""), "");
EXPECT_NONFATAL_FAILURE(EXPECT_SYSTEM_ERROR(throw 1, EDOM, ""), ""); EXPECT_NONFATAL_FAILURE(EXPECT_SYSTEM_ERROR(throw 1, EDOM, ""), "");
EXPECT_NONFATAL_FAILURE(EXPECT_SYSTEM_ERROR( EXPECT_NONFATAL_FAILURE(
throw fmt::system_error(EDOM, "aaa"), EDOM, "bbb"), ""); EXPECT_SYSTEM_ERROR(throw fmt::system_error(EDOM, "aaa"), EDOM, "bbb"),
"");
} }
TEST(AssertionSyntaxTest, ExceptionAssertionBehavesLikeSingleStatement) { TEST(AssertionSyntaxTest, ExceptionAssertionBehavesLikeSingleStatement) {
@ -267,10 +283,9 @@ TEST(ExpectTest, EXPECT_WRITE) {
EXPECT_WRITE(stdout, do_nothing(), ""); EXPECT_WRITE(stdout, do_nothing(), "");
EXPECT_WRITE(stdout, std::printf("test"), "test"); EXPECT_WRITE(stdout, std::printf("test"), "test");
EXPECT_WRITE(stderr, std::fprintf(stderr, "test"), "test"); EXPECT_WRITE(stderr, std::fprintf(stderr, "test"), "test");
EXPECT_NONFATAL_FAILURE( EXPECT_NONFATAL_FAILURE(EXPECT_WRITE(stdout, std::printf("that"), "this"),
EXPECT_WRITE(stdout, std::printf("that"), "this"), "Expected: this\n"
"Expected: this\n" " Actual: that");
" Actual: that");
} }
TEST(StreamingAssertionsTest, EXPECT_THROW_MSG) { TEST(StreamingAssertionsTest, EXPECT_THROW_MSG) {
@ -278,7 +293,8 @@ TEST(StreamingAssertionsTest, EXPECT_THROW_MSG) {
<< "unexpected failure"; << "unexpected failure";
EXPECT_NONFATAL_FAILURE( EXPECT_NONFATAL_FAILURE(
EXPECT_THROW_MSG(throw_exception(), std::exception, "other") EXPECT_THROW_MSG(throw_exception(), std::exception, "other")
<< "expected failure", "expected failure"); << "expected failure",
"expected failure");
} }
TEST(StreamingAssertionsTest, EXPECT_SYSTEM_ERROR) { TEST(StreamingAssertionsTest, EXPECT_SYSTEM_ERROR) {
@ -286,15 +302,15 @@ TEST(StreamingAssertionsTest, EXPECT_SYSTEM_ERROR) {
<< "unexpected failure"; << "unexpected failure";
EXPECT_NONFATAL_FAILURE( EXPECT_NONFATAL_FAILURE(
EXPECT_SYSTEM_ERROR(throw_system_error(), EDOM, "other") EXPECT_SYSTEM_ERROR(throw_system_error(), EDOM, "other")
<< "expected failure", "expected failure"); << "expected failure",
"expected failure");
} }
TEST(StreamingAssertionsTest, EXPECT_WRITE) { TEST(StreamingAssertionsTest, EXPECT_WRITE) {
EXPECT_WRITE(stdout, std::printf("test"), "test") EXPECT_WRITE(stdout, std::printf("test"), "test") << "unexpected failure";
<< "unexpected failure"; EXPECT_NONFATAL_FAILURE(EXPECT_WRITE(stdout, std::printf("test"), "other")
EXPECT_NONFATAL_FAILURE( << "expected failure",
EXPECT_WRITE(stdout, std::printf("test"), "other") "expected failure");
<< "expected failure", "expected failure");
} }
TEST(UtilTest, FormatSystemError) { TEST(UtilTest, FormatSystemError) {
@ -340,8 +356,8 @@ TEST(OutputRedirectTest, FlushErrorInCtor) {
EXPECT_EQ('x', fputc('x', f.get())); EXPECT_EQ('x', fputc('x', f.get()));
FMT_POSIX(close(write_fd)); FMT_POSIX(close(write_fd));
std::unique_ptr<OutputRedirect> redir{FMT_NULL}; std::unique_ptr<OutputRedirect> redir{FMT_NULL};
EXPECT_SYSTEM_ERROR_NOASSERT(redir.reset(new OutputRedirect(f.get())), EXPECT_SYSTEM_ERROR_NOASSERT(redir.reset(new OutputRedirect(f.get())), EBADF,
EBADF, "cannot flush stream"); "cannot flush stream");
redir.reset(FMT_NULL); redir.reset(FMT_NULL);
write_copy.dup2(write_fd); // "undo" close or dtor will fail write_copy.dup2(write_fd); // "undo" close or dtor will fail
} }
@ -352,8 +368,9 @@ TEST(OutputRedirectTest, DupErrorInCtor) {
file copy = file::dup(fd); file copy = file::dup(fd);
FMT_POSIX(close(fd)); FMT_POSIX(close(fd));
std::unique_ptr<OutputRedirect> redir{FMT_NULL}; std::unique_ptr<OutputRedirect> redir{FMT_NULL};
EXPECT_SYSTEM_ERROR_NOASSERT(redir.reset(new OutputRedirect(f.get())), EXPECT_SYSTEM_ERROR_NOASSERT(
EBADF, fmt::format("cannot duplicate file descriptor {}", fd)); redir.reset(new OutputRedirect(f.get())), EBADF,
fmt::format("cannot duplicate file descriptor {}", fd));
copy.dup2(fd); // "undo" close or dtor will fail copy.dup2(fd); // "undo" close or dtor will fail
} }
@ -382,8 +399,8 @@ TEST(OutputRedirectTest, FlushErrorInRestoreAndRead) {
// Put a character in a file buffer. // Put a character in a file buffer.
EXPECT_EQ('x', fputc('x', f.get())); EXPECT_EQ('x', fputc('x', f.get()));
FMT_POSIX(close(write_fd)); FMT_POSIX(close(write_fd));
EXPECT_SYSTEM_ERROR_NOASSERT(redir.restore_and_read(), EXPECT_SYSTEM_ERROR_NOASSERT(redir.restore_and_read(), EBADF,
EBADF, "cannot flush stream"); "cannot flush stream");
write_copy.dup2(write_fd); // "undo" close or dtor will fail write_copy.dup2(write_fd); // "undo" close or dtor will fail
} }
@ -396,15 +413,17 @@ TEST(OutputRedirectTest, ErrorInDtor) {
std::unique_ptr<OutputRedirect> redir(new OutputRedirect(f.get())); std::unique_ptr<OutputRedirect> redir(new OutputRedirect(f.get()));
// Put a character in a file buffer. // Put a character in a file buffer.
EXPECT_EQ('x', fputc('x', f.get())); EXPECT_EQ('x', fputc('x', f.get()));
EXPECT_WRITE(stderr, { EXPECT_WRITE(stderr,
// The close function must be called inside EXPECT_WRITE, otherwise {
// the system may recycle closed file descriptor when redirecting the // The close function must be called inside EXPECT_WRITE,
// output in EXPECT_STDERR and the second close will break output // otherwise the system may recycle closed file descriptor when
// redirection. // redirecting the output in EXPECT_STDERR and the second close
FMT_POSIX(close(write_fd)); // will break output redirection.
SUPPRESS_ASSERT(redir.reset(FMT_NULL)); FMT_POSIX(close(write_fd));
}, format_system_error(EBADF, "cannot flush stream")); SUPPRESS_ASSERT(redir.reset(FMT_NULL));
write_copy.dup2(write_fd); // "undo" close or dtor of buffered_file will fail },
format_system_error(EBADF, "cannot flush stream"));
write_copy.dup2(write_fd); // "undo" close or dtor of buffered_file will fail
} }
#endif // FMT_USE_FILE_DESCRIPTORS #endif // FMT_USE_FILE_DESCRIPTORS

View File

@ -12,25 +12,23 @@
using fmt::file; using fmt::file;
void OutputRedirect::flush() { void OutputRedirect::flush() {
#if EOF != -1 # if EOF != -1
# error "FMT_RETRY assumes return value of -1 indicating failure" # error "FMT_RETRY assumes return value of -1 indicating failure"
#endif # endif
int result = 0; int result = 0;
FMT_RETRY(result, fflush(file_)); FMT_RETRY(result, fflush(file_));
if (result != 0) if (result != 0) throw fmt::system_error(errno, "cannot flush stream");
throw fmt::system_error(errno, "cannot flush stream");
} }
void OutputRedirect::restore() { void OutputRedirect::restore() {
if (original_.descriptor() == -1) if (original_.descriptor() == -1) return; // Already restored.
return; // Already restored.
flush(); flush();
// Restore the original file. // Restore the original file.
original_.dup2(FMT_POSIX(fileno(file_))); original_.dup2(FMT_POSIX(fileno(file_)));
original_.close(); original_.close();
} }
OutputRedirect::OutputRedirect(FILE *f) : file_(f) { OutputRedirect::OutputRedirect(FILE* f) : file_(f) {
flush(); flush();
int fd = FMT_POSIX(fileno(f)); int fd = FMT_POSIX(fileno(f));
// Create a file object referring to the original file. // Create a file object referring to the original file.
@ -45,7 +43,7 @@ OutputRedirect::OutputRedirect(FILE *f) : file_(f) {
OutputRedirect::~OutputRedirect() FMT_NOEXCEPT { OutputRedirect::~OutputRedirect() FMT_NOEXCEPT {
try { try {
restore(); restore();
} catch (const std::exception &e) { } catch (const std::exception& e) {
std::fputs(e.what(), stderr); std::fputs(e.what(), stderr);
} }
} }
@ -56,8 +54,7 @@ std::string OutputRedirect::restore_and_read() {
// Read everything from the pipe. // Read everything from the pipe.
std::string content; std::string content;
if (read_end_.descriptor() == -1) if (read_end_.descriptor() == -1) return content; // Already read.
return content; // Already read.
enum { BUFFER_SIZE = 4096 }; enum { BUFFER_SIZE = 4096 };
char buffer[BUFFER_SIZE]; char buffer[BUFFER_SIZE];
std::size_t count = 0; std::size_t count = 0;
@ -69,7 +66,7 @@ std::string OutputRedirect::restore_and_read() {
return content; return content;
} }
std::string read(file &f, std::size_t count) { std::string read(file& f, std::size_t count) {
std::string buffer(count, '\0'); std::string buffer(count, '\0');
std::size_t n = 0, offset = 0; std::size_t n = 0, offset = 0;
do { do {

View File

@ -14,58 +14,56 @@
#include "fmt/core.h" #include "fmt/core.h"
#ifndef FMT_USE_FILE_DESCRIPTORS #ifndef FMT_USE_FILE_DESCRIPTORS
# define FMT_USE_FILE_DESCRIPTORS 0 # define FMT_USE_FILE_DESCRIPTORS 0
#endif #endif
#if FMT_USE_FILE_DESCRIPTORS #if FMT_USE_FILE_DESCRIPTORS
# include "fmt/posix.h" # include "fmt/posix.h"
#endif #endif
#define FMT_TEST_THROW_(statement, expected_exception, expected_message, fail) \ #define FMT_TEST_THROW_(statement, expected_exception, expected_message, fail) \
GTEST_AMBIGUOUS_ELSE_BLOCKER_ \ GTEST_AMBIGUOUS_ELSE_BLOCKER_ \
if (::testing::AssertionResult gtest_ar = ::testing::AssertionSuccess()) { \ if (::testing::AssertionResult gtest_ar = ::testing::AssertionSuccess()) { \
std::string gtest_expected_message = expected_message; \ std::string gtest_expected_message = expected_message; \
bool gtest_caught_expected = false; \ bool gtest_caught_expected = false; \
try { \ try { \
GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement); \ GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement); \
} \ } catch (expected_exception const& e) { \
catch (expected_exception const& e) { \ if (gtest_expected_message != e.what()) { \
if (gtest_expected_message != e.what()) { \ gtest_ar << #statement \
gtest_ar \ " throws an exception with a different message.\n" \
<< #statement " throws an exception with a different message.\n" \ << "Expected: " << gtest_expected_message << "\n" \
<< "Expected: " << gtest_expected_message << "\n" \ << " Actual: " << e.what(); \
<< " Actual: " << e.what(); \ goto GTEST_CONCAT_TOKEN_(gtest_label_testthrow_, __LINE__); \
goto GTEST_CONCAT_TOKEN_(gtest_label_testthrow_, __LINE__); \ } \
} \ gtest_caught_expected = true; \
gtest_caught_expected = true; \ } catch (...) { \
} \ gtest_ar << "Expected: " #statement \
catch (...) { \ " throws an exception of type " #expected_exception \
gtest_ar << \ ".\n Actual: it throws a different type."; \
"Expected: " #statement " throws an exception of type " \ goto GTEST_CONCAT_TOKEN_(gtest_label_testthrow_, __LINE__); \
#expected_exception ".\n Actual: it throws a different type."; \ } \
goto GTEST_CONCAT_TOKEN_(gtest_label_testthrow_, __LINE__); \ if (!gtest_caught_expected) { \
} \ gtest_ar << "Expected: " #statement \
if (!gtest_caught_expected) { \ " throws an exception of type " #expected_exception \
gtest_ar << \ ".\n Actual: it throws nothing."; \
"Expected: " #statement " throws an exception of type " \ goto GTEST_CONCAT_TOKEN_(gtest_label_testthrow_, __LINE__); \
#expected_exception ".\n Actual: it throws nothing."; \ } \
goto GTEST_CONCAT_TOKEN_(gtest_label_testthrow_, __LINE__); \ } else \
} \ GTEST_CONCAT_TOKEN_(gtest_label_testthrow_, __LINE__) \
} else \ : fail(gtest_ar.failure_message())
GTEST_CONCAT_TOKEN_(gtest_label_testthrow_, __LINE__): \
fail(gtest_ar.failure_message())
// Tests that the statement throws the expected exception and the exception's // Tests that the statement throws the expected exception and the exception's
// what() method returns expected message. // what() method returns expected message.
#define EXPECT_THROW_MSG(statement, expected_exception, expected_message) \ #define EXPECT_THROW_MSG(statement, expected_exception, expected_message) \
FMT_TEST_THROW_(statement, expected_exception, \ FMT_TEST_THROW_(statement, expected_exception, expected_message, \
expected_message, GTEST_NONFATAL_FAILURE_) GTEST_NONFATAL_FAILURE_)
std::string format_system_error(int error_code, fmt::string_view message); std::string format_system_error(int error_code, fmt::string_view message);
#define EXPECT_SYSTEM_ERROR(statement, error_code, message) \ #define EXPECT_SYSTEM_ERROR(statement, error_code, message) \
EXPECT_THROW_MSG(statement, fmt::system_error, \ EXPECT_THROW_MSG(statement, fmt::system_error, \
format_system_error(error_code, message)) format_system_error(error_code, message))
#if FMT_USE_FILE_DESCRIPTORS #if FMT_USE_FILE_DESCRIPTORS
@ -73,7 +71,7 @@ std::string format_system_error(int error_code, fmt::string_view message);
// The output it can handle is limited by the pipe capacity. // The output it can handle is limited by the pipe capacity.
class OutputRedirect { class OutputRedirect {
private: private:
FILE *file_; FILE* file_;
fmt::file original_; // Original file passed to redirector. fmt::file original_; // Original file passed to redirector.
fmt::file read_end_; // Read end of the pipe where the output is redirected. fmt::file read_end_; // Read end of the pipe where the output is redirected.
@ -83,7 +81,7 @@ class OutputRedirect {
void restore(); void restore();
public: public:
explicit OutputRedirect(FILE *file); explicit OutputRedirect(FILE* file);
~OutputRedirect() FMT_NOEXCEPT; ~OutputRedirect() FMT_NOEXCEPT;
// Restores the original file, reads output from the pipe into a string // Restores the original file, reads output from the pipe into a string
@ -91,29 +89,28 @@ class OutputRedirect {
std::string restore_and_read(); std::string restore_and_read();
}; };
#define FMT_TEST_WRITE_(statement, expected_output, file, fail) \ # define FMT_TEST_WRITE_(statement, expected_output, file, fail) \
GTEST_AMBIGUOUS_ELSE_BLOCKER_ \ GTEST_AMBIGUOUS_ELSE_BLOCKER_ \
if (::testing::AssertionResult gtest_ar = ::testing::AssertionSuccess()) { \ if (::testing::AssertionResult gtest_ar = ::testing::AssertionSuccess()) { \
std::string gtest_expected_output = expected_output; \ std::string gtest_expected_output = expected_output; \
OutputRedirect gtest_redir(file); \ OutputRedirect gtest_redir(file); \
GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement); \ GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement); \
std::string gtest_output = gtest_redir.restore_and_read(); \ std::string gtest_output = gtest_redir.restore_and_read(); \
if (gtest_output != gtest_expected_output) { \ if (gtest_output != gtest_expected_output) { \
gtest_ar \ gtest_ar << #statement " produces different output.\n" \
<< #statement " produces different output.\n" \ << "Expected: " << gtest_expected_output << "\n" \
<< "Expected: " << gtest_expected_output << "\n" \ << " Actual: " << gtest_output; \
<< " Actual: " << gtest_output; \ goto GTEST_CONCAT_TOKEN_(gtest_label_testthrow_, __LINE__); \
goto GTEST_CONCAT_TOKEN_(gtest_label_testthrow_, __LINE__); \ } \
} \ } else \
} else \ GTEST_CONCAT_TOKEN_(gtest_label_testthrow_, __LINE__) \
GTEST_CONCAT_TOKEN_(gtest_label_testthrow_, __LINE__): \ : fail(gtest_ar.failure_message())
fail(gtest_ar.failure_message())
// Tests that the statement writes the expected output to file. // Tests that the statement writes the expected output to file.
#define EXPECT_WRITE(file, statement, expected_output) \ # define EXPECT_WRITE(file, statement, expected_output) \
FMT_TEST_WRITE_(statement, expected_output, file, GTEST_NONFATAL_FAILURE_) FMT_TEST_WRITE_(statement, expected_output, file, GTEST_NONFATAL_FAILURE_)
#ifdef _MSC_VER # ifdef _MSC_VER
// Suppresses Windows assertions on invalid file descriptors, making // Suppresses Windows assertions on invalid file descriptors, making
// POSIX functions return proper error codes instead of crashing on Windows. // POSIX functions return proper error codes instead of crashing on Windows.
@ -122,38 +119,41 @@ class SuppressAssert {
_invalid_parameter_handler original_handler_; _invalid_parameter_handler original_handler_;
int original_report_mode_; int original_report_mode_;
static void handle_invalid_parameter(const wchar_t *, static void handle_invalid_parameter(const wchar_t*, const wchar_t*,
const wchar_t *, const wchar_t *, unsigned , uintptr_t) {} const wchar_t*, unsigned, uintptr_t) {}
public: public:
SuppressAssert() SuppressAssert()
: original_handler_(_set_invalid_parameter_handler(handle_invalid_parameter)), : original_handler_(
original_report_mode_(_CrtSetReportMode(_CRT_ASSERT, 0)) { _set_invalid_parameter_handler(handle_invalid_parameter)),
} original_report_mode_(_CrtSetReportMode(_CRT_ASSERT, 0)) {}
~SuppressAssert() { ~SuppressAssert() {
_set_invalid_parameter_handler(original_handler_); _set_invalid_parameter_handler(original_handler_);
_CrtSetReportMode(_CRT_ASSERT, original_report_mode_); _CrtSetReportMode(_CRT_ASSERT, original_report_mode_);
} }
}; };
# define SUPPRESS_ASSERT(statement) { SuppressAssert sa; statement; } # define SUPPRESS_ASSERT(statement) \
#else { \
# define SUPPRESS_ASSERT(statement) statement SuppressAssert sa; \
#endif // _MSC_VER statement; \
}
# else
# define SUPPRESS_ASSERT(statement) statement
# endif // _MSC_VER
#define EXPECT_SYSTEM_ERROR_NOASSERT(statement, error_code, message) \ # define EXPECT_SYSTEM_ERROR_NOASSERT(statement, error_code, message) \
EXPECT_SYSTEM_ERROR(SUPPRESS_ASSERT(statement), error_code, message) EXPECT_SYSTEM_ERROR(SUPPRESS_ASSERT(statement), error_code, message)
// Attempts to read count characters from a file. // Attempts to read count characters from a file.
std::string read(fmt::file &f, std::size_t count); std::string read(fmt::file& f, std::size_t count);
#define EXPECT_READ(file, expected_content) \ # define EXPECT_READ(file, expected_content) \
EXPECT_EQ(expected_content, read(file, std::strlen(expected_content))) EXPECT_EQ(expected_content, read(file, std::strlen(expected_content)))
#endif // FMT_USE_FILE_DESCRIPTORS #endif // FMT_USE_FILE_DESCRIPTORS
template <typename Mock> template <typename Mock> struct ScopedMock : testing::StrictMock<Mock> {
struct ScopedMock : testing::StrictMock<Mock> {
ScopedMock() { Mock::instance = this; } ScopedMock() { Mock::instance = this; }
~ScopedMock() { Mock::instance = FMT_NULL; } ~ScopedMock() { Mock::instance = FMT_NULL; }
}; };

View File

@ -8,8 +8,7 @@
#include "fmt/locale.h" #include "fmt/locale.h"
#include "gmock.h" #include "gmock.h"
template <typename Char> template <typename Char> struct numpunct : std::numpunct<Char> {
struct numpunct : std::numpunct<Char> {
protected: protected:
Char do_thousands_sep() const FMT_OVERRIDE { return '~'; } Char do_thousands_sep() const FMT_OVERRIDE { return '~'; }
}; };

View File

@ -8,25 +8,23 @@
#ifndef FMT_MOCK_ALLOCATOR_H_ #ifndef FMT_MOCK_ALLOCATOR_H_
#define FMT_MOCK_ALLOCATOR_H_ #define FMT_MOCK_ALLOCATOR_H_
#include "gmock.h"
#include "fmt/format.h" #include "fmt/format.h"
#include "gmock.h"
template <typename T> template <typename T> class mock_allocator {
class mock_allocator {
public: public:
mock_allocator() {} mock_allocator() {}
mock_allocator(const mock_allocator &) {} mock_allocator(const mock_allocator&) {}
typedef T value_type; typedef T value_type;
MOCK_METHOD1_T(allocate, T* (std::size_t n)); MOCK_METHOD1_T(allocate, T*(std::size_t n));
MOCK_METHOD2_T(deallocate, void (T* p, std::size_t n)); MOCK_METHOD2_T(deallocate, void(T* p, std::size_t n));
}; };
template <typename Allocator> template <typename Allocator> class allocator_ref {
class allocator_ref {
private: private:
Allocator *alloc_; Allocator* alloc_;
void move(allocator_ref &other) { void move(allocator_ref& other) {
alloc_ = other.alloc_; alloc_ = other.alloc_;
other.alloc_ = FMT_NULL; other.alloc_ = FMT_NULL;
} }
@ -34,24 +32,24 @@ class allocator_ref {
public: public:
typedef typename Allocator::value_type value_type; typedef typename Allocator::value_type value_type;
explicit allocator_ref(Allocator *alloc = FMT_NULL) : alloc_(alloc) {} explicit allocator_ref(Allocator* alloc = FMT_NULL) : alloc_(alloc) {}
allocator_ref(const allocator_ref &other) : alloc_(other.alloc_) {} allocator_ref(const allocator_ref& other) : alloc_(other.alloc_) {}
allocator_ref(allocator_ref &&other) { move(other); } allocator_ref(allocator_ref&& other) { move(other); }
allocator_ref& operator=(allocator_ref &&other) { allocator_ref& operator=(allocator_ref&& other) {
assert(this != &other); assert(this != &other);
move(other); move(other);
return *this; return *this;
} }
allocator_ref& operator=(const allocator_ref &other) { allocator_ref& operator=(const allocator_ref& other) {
alloc_ = other.alloc_; alloc_ = other.alloc_;
return *this; return *this;
} }
public: public:
Allocator *get() const { return alloc_; } Allocator* get() const { return alloc_; }
value_type* allocate(std::size_t n) { value_type* allocate(std::size_t n) {
return fmt::internal::allocate(*alloc_, n); return fmt::internal::allocate(*alloc_, n);

View File

@ -16,26 +16,26 @@
using fmt::format; using fmt::format;
using fmt::format_error; using fmt::format_error;
static std::ostream &operator<<(std::ostream &os, const Date &d) { static std::ostream& operator<<(std::ostream& os, const Date& d) {
os << d.year() << '-' << d.month() << '-' << d.day(); os << d.year() << '-' << d.month() << '-' << d.day();
return os; return os;
} }
static std::wostream &operator<<(std::wostream &os, const Date &d) { static std::wostream& operator<<(std::wostream& os, const Date& d) {
os << d.year() << L'-' << d.month() << L'-' << d.day(); os << d.year() << L'-' << d.month() << L'-' << d.day();
return os; return os;
} }
enum TestEnum {}; enum TestEnum {};
static std::ostream &operator<<(std::ostream &os, TestEnum) { static std::ostream& operator<<(std::ostream& os, TestEnum) {
return os << "TestEnum"; return os << "TestEnum";
} }
static std::wostream &operator<<(std::wostream &os, TestEnum) { static std::wostream& operator<<(std::wostream& os, TestEnum) {
return os << L"TestEnum"; return os << L"TestEnum";
} }
enum TestEnum2 {A}; enum TestEnum2 { A };
TEST(OStreamTest, Enum) { TEST(OStreamTest, Enum) {
EXPECT_FALSE((fmt::convert_to_int<TestEnum, char>::value)); EXPECT_FALSE((fmt::convert_to_int<TestEnum, char>::value));
@ -48,14 +48,14 @@ TEST(OStreamTest, Enum) {
typedef fmt::back_insert_range<fmt::internal::buffer> range; typedef fmt::back_insert_range<fmt::internal::buffer> range;
struct test_arg_formatter: fmt::arg_formatter<range> { struct test_arg_formatter : fmt::arg_formatter<range> {
test_arg_formatter(fmt::format_context &ctx, fmt::format_specs &s) test_arg_formatter(fmt::format_context& ctx, fmt::format_specs& s)
: fmt::arg_formatter<range>(ctx, &s) {} : fmt::arg_formatter<range>(ctx, &s) {}
}; };
TEST(OStreamTest, CustomArg) { TEST(OStreamTest, CustomArg) {
fmt::memory_buffer buffer; fmt::memory_buffer buffer;
fmt::internal::buffer &base = buffer; fmt::internal::buffer& base = buffer;
fmt::format_context ctx(std::back_inserter(base), "", fmt::format_args()); fmt::format_context ctx(std::back_inserter(base), "", fmt::format_args());
fmt::format_specs spec; fmt::format_specs spec;
test_arg_formatter af(ctx, spec); test_arg_formatter af(ctx, spec);
@ -75,20 +75,20 @@ TEST(OStreamTest, Format) {
TEST(OStreamTest, FormatSpecs) { TEST(OStreamTest, FormatSpecs) {
EXPECT_EQ("def ", format("{0:<5}", TestString("def"))); EXPECT_EQ("def ", format("{0:<5}", TestString("def")));
EXPECT_EQ(" def", format("{0:>5}", TestString("def"))); EXPECT_EQ(" def", format("{0:>5}", TestString("def")));
EXPECT_THROW_MSG(format("{0:=5}", TestString("def")), EXPECT_THROW_MSG(format("{0:=5}", TestString("def")), format_error,
format_error, "format specifier requires numeric argument"); "format specifier requires numeric argument");
EXPECT_EQ(" def ", format("{0:^5}", TestString("def"))); EXPECT_EQ(" def ", format("{0:^5}", TestString("def")));
EXPECT_EQ("def**", format("{0:*<5}", TestString("def"))); EXPECT_EQ("def**", format("{0:*<5}", TestString("def")));
EXPECT_THROW_MSG(format("{0:+}", TestString()), EXPECT_THROW_MSG(format("{0:+}", TestString()), format_error,
format_error, "format specifier requires numeric argument"); "format specifier requires numeric argument");
EXPECT_THROW_MSG(format("{0:-}", TestString()), EXPECT_THROW_MSG(format("{0:-}", TestString()), format_error,
format_error, "format specifier requires numeric argument"); "format specifier requires numeric argument");
EXPECT_THROW_MSG(format("{0: }", TestString()), EXPECT_THROW_MSG(format("{0: }", TestString()), format_error,
format_error, "format specifier requires numeric argument"); "format specifier requires numeric argument");
EXPECT_THROW_MSG(format("{0:#}", TestString()), EXPECT_THROW_MSG(format("{0:#}", TestString()), format_error,
format_error, "format specifier requires numeric argument"); "format specifier requires numeric argument");
EXPECT_THROW_MSG(format("{0:05}", TestString()), EXPECT_THROW_MSG(format("{0:05}", TestString()), format_error,
format_error, "format specifier requires numeric argument"); "format specifier requires numeric argument");
EXPECT_EQ("test ", format("{0:13}", TestString("test"))); EXPECT_EQ("test ", format("{0:13}", TestString("test")));
EXPECT_EQ("test ", format("{0:{1}}", TestString("test"), 13)); EXPECT_EQ("test ", format("{0:{1}}", TestString("test"), 13));
EXPECT_EQ("te", format("{0:.2}", TestString("test"))); EXPECT_EQ("te", format("{0:.2}", TestString("test")));
@ -96,7 +96,7 @@ TEST(OStreamTest, FormatSpecs) {
} }
struct EmptyTest {}; struct EmptyTest {};
static std::ostream &operator<<(std::ostream &os, EmptyTest) { static std::ostream& operator<<(std::ostream& os, EmptyTest) {
return os << ""; return os << "";
} }
@ -116,7 +116,7 @@ TEST(OStreamTest, Print) {
TEST(OStreamTest, WriteToOStream) { TEST(OStreamTest, WriteToOStream) {
std::ostringstream os; std::ostringstream os;
fmt::memory_buffer buffer; fmt::memory_buffer buffer;
const char *foo = "foo"; const char* foo = "foo";
buffer.append(foo, foo + std::strlen(foo)); buffer.append(foo, foo + std::strlen(foo));
fmt::internal::write(os, buffer); fmt::internal::write(os, buffer);
EXPECT_EQ("foo", os.str()); EXPECT_EQ("foo", os.str());
@ -125,8 +125,7 @@ TEST(OStreamTest, WriteToOStream) {
TEST(OStreamTest, WriteToOStreamMaxSize) { TEST(OStreamTest, WriteToOStreamMaxSize) {
std::size_t max_size = std::numeric_limits<std::size_t>::max(); std::size_t max_size = std::numeric_limits<std::size_t>::max();
std::streamsize max_streamsize = std::numeric_limits<std::streamsize>::max(); std::streamsize max_streamsize = std::numeric_limits<std::streamsize>::max();
if (max_size <= fmt::internal::to_unsigned(max_streamsize)) if (max_size <= fmt::internal::to_unsigned(max_streamsize)) return;
return;
struct test_buffer : fmt::internal::buffer { struct test_buffer : fmt::internal::buffer {
explicit test_buffer(std::size_t size) { resize(size); } explicit test_buffer(std::size_t size) { resize(size); }
@ -134,19 +133,19 @@ TEST(OStreamTest, WriteToOStreamMaxSize) {
} buffer(max_size); } buffer(max_size);
struct mock_streambuf : std::streambuf { struct mock_streambuf : std::streambuf {
MOCK_METHOD2(xsputn, std::streamsize (const void *s, std::streamsize n)); MOCK_METHOD2(xsputn, std::streamsize(const void* s, std::streamsize n));
std::streamsize xsputn(const char *s, std::streamsize n) { std::streamsize xsputn(const char* s, std::streamsize n) {
const void *v = s; const void* v = s;
return xsputn(v, n); return xsputn(v, n);
} }
} streambuf; } streambuf;
struct test_ostream : std::ostream { struct test_ostream : std::ostream {
explicit test_ostream(mock_streambuf &buffer) : std::ostream(&buffer) {} explicit test_ostream(mock_streambuf& buffer) : std::ostream(&buffer) {}
} os(streambuf); } os(streambuf);
testing::InSequence sequence; testing::InSequence sequence;
const char *data = FMT_NULL; const char* data = FMT_NULL;
typedef std::make_unsigned<std::streamsize>::type ustreamsize; typedef std::make_unsigned<std::streamsize>::type ustreamsize;
ustreamsize size = max_size; ustreamsize size = max_size;
do { do {
@ -173,11 +172,11 @@ TEST(OStreamTest, ConstexprString) {
namespace fmt_test { namespace fmt_test {
struct ABC {}; struct ABC {};
template <typename Output> Output &operator<<(Output &out, ABC) { template <typename Output> Output& operator<<(Output& out, ABC) {
out << "ABC"; out << "ABC";
return out; return out;
} }
} // namespace fmt_test } // namespace fmt_test
TEST(FormatTest, FormatToN) { TEST(FormatTest, FormatToN) {
char buffer[4]; char buffer[4];

View File

@ -7,21 +7,21 @@
// Disable bogus MSVC warnings. // Disable bogus MSVC warnings.
#ifdef _MSC_VER #ifdef _MSC_VER
# define _CRT_SECURE_NO_WARNINGS # define _CRT_SECURE_NO_WARNINGS
#endif #endif
#include "posix-mock.h" #include "posix-mock.h"
#include "../src/posix.cc" #include "../src/posix.cc"
#include <climits>
#include <errno.h> #include <errno.h>
#include <fcntl.h> #include <fcntl.h>
#include <climits>
#include <memory> #include <memory>
#ifdef _WIN32 #ifdef _WIN32
# include <io.h> # include <io.h>
# undef max # undef max
# undef ERROR # undef ERROR
#endif #endif
#include "gmock.h" #include "gmock.h"
@ -33,8 +33,8 @@ using fmt::error_code;
using fmt::file; using fmt::file;
using testing::_; using testing::_;
using testing::StrEq;
using testing::Return; using testing::Return;
using testing::StrEq;
namespace { namespace {
int open_count; int open_count;
@ -53,24 +53,24 @@ std::size_t write_nbyte;
bool sysconf_error; bool sysconf_error;
enum FStatSimulation { NONE, MAX_SIZE, ERROR } fstat_sim; enum FStatSimulation { NONE, MAX_SIZE, ERROR } fstat_sim;
} } // namespace
#define EMULATE_EINTR(func, error_result) \ #define EMULATE_EINTR(func, error_result) \
if (func##_count != 0) { \ if (func##_count != 0) { \
if (func##_count++ != 3) { \ if (func##_count++ != 3) { \
errno = EINTR; \ errno = EINTR; \
return error_result; \ return error_result; \
} \ } \
} }
#ifndef _MSC_VER #ifndef _MSC_VER
int test::open(const char *path, int oflag, int mode) { int test::open(const char* path, int oflag, int mode) {
EMULATE_EINTR(open, -1); EMULATE_EINTR(open, -1);
return ::open(path, oflag, mode); return ::open(path, oflag, mode);
} }
#else #else
errno_t test::sopen_s( errno_t test::sopen_s(int* pfh, const char* filename, int oflag, int shflag,
int* pfh, const char *filename, int oflag, int shflag, int pmode) { int pmode) {
EMULATE_EINTR(open, EINTR); EMULATE_EINTR(open, EINTR);
return _sopen_s(pfh, filename, oflag, shflag, pmode); return _sopen_s(pfh, filename, oflag, shflag, pmode);
} }
@ -80,8 +80,7 @@ errno_t test::sopen_s(
long test::sysconf(int name) { long test::sysconf(int name) {
long result = ::sysconf(name); long result = ::sysconf(name);
if (!sysconf_error) if (!sysconf_error) return result;
return result;
// Simulate an error. // Simulate an error.
errno = EINVAL; errno = EINVAL;
return -1; return -1;
@ -89,10 +88,9 @@ long test::sysconf(int name) {
static off_t max_file_size() { return std::numeric_limits<off_t>::max(); } static off_t max_file_size() { return std::numeric_limits<off_t>::max(); }
int test::fstat(int fd, struct stat *buf) { int test::fstat(int fd, struct stat* buf) {
int result = ::fstat(fd, buf); int result = ::fstat(fd, buf);
if (fstat_sim == MAX_SIZE) if (fstat_sim == MAX_SIZE) buf->st_size = max_file_size();
buf->st_size = max_file_size();
return result; return result;
} }
@ -132,18 +130,18 @@ int test::dup2(int fildes, int fildes2) {
return ::FMT_POSIX(dup2(fildes, fildes2)); return ::FMT_POSIX(dup2(fildes, fildes2));
} }
FILE *test::fdopen(int fildes, const char *mode) { FILE* test::fdopen(int fildes, const char* mode) {
EMULATE_EINTR(fdopen, FMT_NULL); EMULATE_EINTR(fdopen, FMT_NULL);
return ::FMT_POSIX(fdopen(fildes, mode)); return ::FMT_POSIX(fdopen(fildes, mode));
} }
test::ssize_t test::read(int fildes, void *buf, test::size_t nbyte) { test::ssize_t test::read(int fildes, void* buf, test::size_t nbyte) {
read_nbyte = nbyte; read_nbyte = nbyte;
EMULATE_EINTR(read, -1); EMULATE_EINTR(read, -1);
return ::FMT_POSIX(read(fildes, buf, nbyte)); return ::FMT_POSIX(read(fildes, buf, nbyte));
} }
test::ssize_t test::write(int fildes, const void *buf, test::size_t nbyte) { test::ssize_t test::write(int fildes, const void* buf, test::size_t nbyte) {
write_nbyte = nbyte; write_nbyte = nbyte;
EMULATE_EINTR(write, -1); EMULATE_EINTR(write, -1);
return ::FMT_POSIX(write(fildes, buf, nbyte)); return ::FMT_POSIX(write(fildes, buf, nbyte));
@ -155,23 +153,23 @@ int test::pipe(int fildes[2]) {
return ::pipe(fildes); return ::pipe(fildes);
} }
#else #else
int test::pipe(int *pfds, unsigned psize, int textmode) { int test::pipe(int* pfds, unsigned psize, int textmode) {
EMULATE_EINTR(pipe, -1); EMULATE_EINTR(pipe, -1);
return _pipe(pfds, psize, textmode); return _pipe(pfds, psize, textmode);
} }
#endif #endif
FILE *test::fopen(const char *filename, const char *mode) { FILE* test::fopen(const char* filename, const char* mode) {
EMULATE_EINTR(fopen, FMT_NULL); EMULATE_EINTR(fopen, FMT_NULL);
return ::fopen(filename, mode); return ::fopen(filename, mode);
} }
int test::fclose(FILE *stream) { int test::fclose(FILE* stream) {
EMULATE_EINTR(fclose, EOF); EMULATE_EINTR(fclose, EOF);
return ::fclose(stream); return ::fclose(stream);
} }
int (test::fileno)(FILE *stream) { int(test::fileno)(FILE* stream) {
EMULATE_EINTR(fileno, -1); EMULATE_EINTR(fileno, -1);
#ifdef fileno #ifdef fileno
return FMT_POSIX(fileno(stream)); return FMT_POSIX(fileno(stream));
@ -181,18 +179,18 @@ int (test::fileno)(FILE *stream) {
} }
#ifndef _WIN32 #ifndef _WIN32
# define EXPECT_RETRY(statement, func, message) \ # define EXPECT_RETRY(statement, func, message) \
func##_count = 1; \ func##_count = 1; \
statement; \ statement; \
EXPECT_EQ(4, func##_count); \ EXPECT_EQ(4, func##_count); \
func##_count = 0; func##_count = 0;
# define EXPECT_EQ_POSIX(expected, actual) EXPECT_EQ(expected, actual) # define EXPECT_EQ_POSIX(expected, actual) EXPECT_EQ(expected, actual)
#else #else
# define EXPECT_RETRY(statement, func, message) \ # define EXPECT_RETRY(statement, func, message) \
func##_count = 1; \ func##_count = 1; \
EXPECT_SYSTEM_ERROR(statement, EINTR, message); \ EXPECT_SYSTEM_ERROR(statement, EINTR, message); \
func##_count = 0; func##_count = 0;
# define EXPECT_EQ_POSIX(expected, actual) # define EXPECT_EQ_POSIX(expected, actual)
#endif #endif
static void write_file(fmt::cstring_view filename, fmt::string_view content) { static void write_file(fmt::cstring_view filename, fmt::string_view content) {
@ -208,8 +206,8 @@ TEST(UtilTest, GetPageSize) {
#else #else
EXPECT_EQ(sysconf(_SC_PAGESIZE), fmt::getpagesize()); EXPECT_EQ(sysconf(_SC_PAGESIZE), fmt::getpagesize());
sysconf_error = true; sysconf_error = true;
EXPECT_SYSTEM_ERROR( EXPECT_SYSTEM_ERROR(fmt::getpagesize(), EINVAL,
fmt::getpagesize(), EINVAL, "cannot get memory page size"); "cannot get memory page size");
sysconf_error = false; sysconf_error = false;
#endif #endif
} }
@ -217,8 +215,8 @@ TEST(UtilTest, GetPageSize) {
TEST(FileTest, OpenRetry) { TEST(FileTest, OpenRetry) {
write_file("test", "there must be something here"); write_file("test", "there must be something here");
std::unique_ptr<file> f{FMT_NULL}; std::unique_ptr<file> f{FMT_NULL};
EXPECT_RETRY(f.reset(new file("test", file::RDONLY)), EXPECT_RETRY(f.reset(new file("test", file::RDONLY)), open,
open, "cannot open file test"); "cannot open file test");
#ifndef _WIN32 #ifndef _WIN32
char c = 0; char c = 0;
f->read(&c, 1); f->read(&c, 1);
@ -230,12 +228,14 @@ TEST(FileTest, CloseNoRetryInDtor) {
file::pipe(read_end, write_end); file::pipe(read_end, write_end);
std::unique_ptr<file> f(new file(std::move(read_end))); std::unique_ptr<file> f(new file(std::move(read_end)));
int saved_close_count = 0; int saved_close_count = 0;
EXPECT_WRITE(stderr, { EXPECT_WRITE(stderr,
close_count = 1; {
f.reset(FMT_NULL); close_count = 1;
saved_close_count = close_count; f.reset(FMT_NULL);
close_count = 0; saved_close_count = close_count;
}, format_system_error(EINTR, "cannot close file") + "\n"); close_count = 0;
},
format_system_error(EINTR, "cannot close file") + "\n");
EXPECT_EQ(2, saved_close_count); EXPECT_EQ(2, saved_close_count);
} }
@ -256,8 +256,8 @@ TEST(FileTest, Size) {
EXPECT_EQ(content.size(), static_cast<unsigned long long>(f.size())); EXPECT_EQ(content.size(), static_cast<unsigned long long>(f.size()));
#ifdef _WIN32 #ifdef _WIN32
fmt::memory_buffer message; fmt::memory_buffer message;
fmt::internal::format_windows_error( fmt::internal::format_windows_error(message, ERROR_ACCESS_DENIED,
message, ERROR_ACCESS_DENIED, "cannot get file size"); "cannot get file size");
fstat_sim = ERROR; fstat_sim = ERROR;
EXPECT_THROW_MSG(f.size(), fmt::windows_error, fmt::to_string(message)); EXPECT_THROW_MSG(f.size(), fmt::windows_error, fmt::to_string(message));
fstat_sim = NONE; fstat_sim = NONE;
@ -284,8 +284,8 @@ TEST(FileTest, ReadRetry) {
write_end.close(); write_end.close();
char buffer[SIZE]; char buffer[SIZE];
std::size_t count = 0; std::size_t count = 0;
EXPECT_RETRY(count = read_end.read(buffer, SIZE), EXPECT_RETRY(count = read_end.read(buffer, SIZE), read,
read, "cannot read from file"); "cannot read from file");
EXPECT_EQ_POSIX(static_cast<std::streamsize>(SIZE), count); EXPECT_EQ_POSIX(static_cast<std::streamsize>(SIZE), count);
} }
@ -294,8 +294,8 @@ TEST(FileTest, WriteRetry) {
file::pipe(read_end, write_end); file::pipe(read_end, write_end);
enum { SIZE = 4 }; enum { SIZE = 4 };
std::size_t count = 0; std::size_t count = 0;
EXPECT_RETRY(count = write_end.write("test", SIZE), EXPECT_RETRY(count = write_end.write("test", SIZE), write,
write, "cannot write to file"); "cannot write to file");
write_end.close(); write_end.close();
#ifndef _WIN32 #ifndef _WIN32
EXPECT_EQ(static_cast<std::streamsize>(SIZE), count); EXPECT_EQ(static_cast<std::streamsize>(SIZE), count);
@ -312,8 +312,7 @@ TEST(FileTest, ConvertReadCount) {
file::pipe(read_end, write_end); file::pipe(read_end, write_end);
char c; char c;
std::size_t size = UINT_MAX; std::size_t size = UINT_MAX;
if (sizeof(unsigned) != sizeof(std::size_t)) if (sizeof(unsigned) != sizeof(std::size_t)) ++size;
++size;
read_count = 1; read_count = 1;
read_nbyte = 0; read_nbyte = 0;
EXPECT_THROW(read_end.read(&c, size), fmt::system_error); EXPECT_THROW(read_end.read(&c, size), fmt::system_error);
@ -326,8 +325,7 @@ TEST(FileTest, ConvertWriteCount) {
file::pipe(read_end, write_end); file::pipe(read_end, write_end);
char c; char c;
std::size_t size = UINT_MAX; std::size_t size = UINT_MAX;
if (sizeof(unsigned) != sizeof(std::size_t)) if (sizeof(unsigned) != sizeof(std::size_t)) ++size;
++size;
write_count = 1; write_count = 1;
write_nbyte = 0; write_nbyte = 0;
EXPECT_THROW(write_end.write(&c, size), fmt::system_error); EXPECT_THROW(write_end.write(&c, size), fmt::system_error);
@ -339,7 +337,8 @@ TEST(FileTest, ConvertWriteCount) {
TEST(FileTest, DupNoRetry) { TEST(FileTest, DupNoRetry) {
int stdout_fd = FMT_POSIX(fileno(stdout)); int stdout_fd = FMT_POSIX(fileno(stdout));
dup_count = 1; dup_count = 1;
EXPECT_SYSTEM_ERROR(file::dup(stdout_fd), EINTR, EXPECT_SYSTEM_ERROR(
file::dup(stdout_fd), EINTR,
fmt::format("cannot duplicate file descriptor {}", stdout_fd)); fmt::format("cannot duplicate file descriptor {}", stdout_fd));
dup_count = 0; dup_count = 0;
} }
@ -348,8 +347,8 @@ TEST(FileTest, Dup2Retry) {
int stdout_fd = FMT_POSIX(fileno(stdout)); int stdout_fd = FMT_POSIX(fileno(stdout));
file f1 = file::dup(stdout_fd), f2 = file::dup(stdout_fd); file f1 = file::dup(stdout_fd), f2 = file::dup(stdout_fd);
EXPECT_RETRY(f1.dup2(f2.descriptor()), dup2, EXPECT_RETRY(f1.dup2(f2.descriptor()), dup2,
fmt::format("cannot duplicate file descriptor {} to {}", fmt::format("cannot duplicate file descriptor {} to {}",
f1.descriptor(), f2.descriptor())); f1.descriptor(), f2.descriptor()));
} }
TEST(FileTest, Dup2NoExceptRetry) { TEST(FileTest, Dup2NoExceptRetry) {
@ -369,8 +368,8 @@ TEST(FileTest, Dup2NoExceptRetry) {
TEST(FileTest, PipeNoRetry) { TEST(FileTest, PipeNoRetry) {
file read_end, write_end; file read_end, write_end;
pipe_count = 1; pipe_count = 1;
EXPECT_SYSTEM_ERROR( EXPECT_SYSTEM_ERROR(file::pipe(read_end, write_end), EINTR,
file::pipe(read_end, write_end), EINTR, "cannot create pipe"); "cannot create pipe");
pipe_count = 0; pipe_count = 0;
} }
@ -378,16 +377,16 @@ TEST(FileTest, FdopenNoRetry) {
file read_end, write_end; file read_end, write_end;
file::pipe(read_end, write_end); file::pipe(read_end, write_end);
fdopen_count = 1; fdopen_count = 1;
EXPECT_SYSTEM_ERROR(read_end.fdopen("r"), EXPECT_SYSTEM_ERROR(read_end.fdopen("r"), EINTR,
EINTR, "cannot associate stream with file descriptor"); "cannot associate stream with file descriptor");
fdopen_count = 0; fdopen_count = 0;
} }
TEST(BufferedFileTest, OpenRetry) { TEST(BufferedFileTest, OpenRetry) {
write_file("test", "there must be something here"); write_file("test", "there must be something here");
std::unique_ptr<buffered_file> f{FMT_NULL}; std::unique_ptr<buffered_file> f{FMT_NULL};
EXPECT_RETRY(f.reset(new buffered_file("test", "r")), EXPECT_RETRY(f.reset(new buffered_file("test", "r")), fopen,
fopen, "cannot open file test"); "cannot open file test");
#ifndef _WIN32 #ifndef _WIN32
char c = 0; char c = 0;
if (fread(&c, 1, 1, f->get()) < 1) if (fread(&c, 1, 1, f->get()) < 1)
@ -400,12 +399,14 @@ TEST(BufferedFileTest, CloseNoRetryInDtor) {
file::pipe(read_end, write_end); file::pipe(read_end, write_end);
std::unique_ptr<buffered_file> f(new buffered_file(read_end.fdopen("r"))); std::unique_ptr<buffered_file> f(new buffered_file(read_end.fdopen("r")));
int saved_fclose_count = 0; int saved_fclose_count = 0;
EXPECT_WRITE(stderr, { EXPECT_WRITE(stderr,
fclose_count = 1; {
f.reset(FMT_NULL); fclose_count = 1;
saved_fclose_count = fclose_count; f.reset(FMT_NULL);
fclose_count = 0; saved_fclose_count = fclose_count;
}, format_system_error(EINTR, "cannot close file") + "\n"); fclose_count = 0;
},
format_system_error(EINTR, "cannot close file") + "\n");
EXPECT_EQ(2, saved_fclose_count); EXPECT_EQ(2, saved_fclose_count);
} }
@ -430,14 +431,14 @@ TEST(BufferedFileTest, FilenoNoRetry) {
} }
struct TestMock { struct TestMock {
static TestMock *instance; static TestMock* instance;
} *TestMock::instance; } * TestMock::instance;
TEST(ScopedMock, Scope) { TEST(ScopedMock, Scope) {
{ {
ScopedMock<TestMock> mock; ScopedMock<TestMock> mock;
EXPECT_EQ(&mock, TestMock::instance); EXPECT_EQ(&mock, TestMock::instance);
TestMock &copy = mock; TestMock& copy = mock;
static_cast<void>(copy); static_cast<void>(copy);
} }
EXPECT_EQ(FMT_NULL, TestMock::instance); EXPECT_EQ(FMT_NULL, TestMock::instance);
@ -448,20 +449,20 @@ TEST(ScopedMock, Scope) {
typedef fmt::Locale::Type LocaleType; typedef fmt::Locale::Type LocaleType;
struct LocaleMock { struct LocaleMock {
static LocaleMock *instance; static LocaleMock* instance;
MOCK_METHOD3(newlocale, LocaleType (int category_mask, const char *locale, MOCK_METHOD3(newlocale, LocaleType(int category_mask, const char* locale,
LocaleType base)); LocaleType base));
MOCK_METHOD1(freelocale, void (LocaleType locale)); MOCK_METHOD1(freelocale, void(LocaleType locale));
MOCK_METHOD3(strtod_l, double (const char *nptr, char **endptr, MOCK_METHOD3(strtod_l,
LocaleType locale)); double(const char* nptr, char** endptr, LocaleType locale));
} *LocaleMock::instance; } * LocaleMock::instance;
#ifdef _MSC_VER # ifdef _MSC_VER
# pragma warning(push) # pragma warning(push)
# pragma warning(disable: 4273) # pragma warning(disable : 4273)
_locale_t _create_locale(int category, const char *locale) { _locale_t _create_locale(int category, const char* locale) {
return LocaleMock::instance->newlocale(category, locale, 0); return LocaleMock::instance->newlocale(category, locale, 0);
} }
@ -469,38 +470,41 @@ void _free_locale(_locale_t locale) {
LocaleMock::instance->freelocale(locale); LocaleMock::instance->freelocale(locale);
} }
double _strtod_l(const char *nptr, char **endptr, _locale_t locale) { double _strtod_l(const char* nptr, char** endptr, _locale_t locale) {
return LocaleMock::instance->strtod_l(nptr, endptr, locale); return LocaleMock::instance->strtod_l(nptr, endptr, locale);
} }
# pragma warning(pop) # pragma warning(pop)
#endif # endif
#if defined(__THROW) && FMT_GCC_VERSION > 0 && FMT_GCC_VERSION <= 408 # if defined(__THROW) && FMT_GCC_VERSION > 0 && FMT_GCC_VERSION <= 408
#define FMT_LOCALE_THROW __THROW # define FMT_LOCALE_THROW __THROW
#else # else
#define FMT_LOCALE_THROW # define FMT_LOCALE_THROW
#endif # endif
LocaleType newlocale(int category_mask, const char *locale, LocaleType base) FMT_LOCALE_THROW { LocaleType newlocale(int category_mask, const char* locale,
LocaleType base) FMT_LOCALE_THROW {
return LocaleMock::instance->newlocale(category_mask, locale, base); return LocaleMock::instance->newlocale(category_mask, locale, base);
} }
#if defined(__APPLE__) || (defined(__FreeBSD__) && __FreeBSD_version < 1200002) # if defined(__APPLE__) || \
(defined(__FreeBSD__) && __FreeBSD_version < 1200002)
typedef int FreeLocaleResult; typedef int FreeLocaleResult;
#else # else
typedef void FreeLocaleResult; typedef void FreeLocaleResult;
#endif # endif
FreeLocaleResult freelocale(LocaleType locale) FMT_LOCALE_THROW { FreeLocaleResult freelocale(LocaleType locale) FMT_LOCALE_THROW {
LocaleMock::instance->freelocale(locale); LocaleMock::instance->freelocale(locale);
return FreeLocaleResult(); return FreeLocaleResult();
} }
double strtod_l(const char *nptr, char **endptr, LocaleType locale) FMT_LOCALE_THROW { double strtod_l(const char* nptr, char** endptr,
LocaleType locale) FMT_LOCALE_THROW {
return LocaleMock::instance->strtod_l(nptr, endptr, locale); return LocaleMock::instance->strtod_l(nptr, endptr, locale);
} }
#undef FMT_LOCALE_THROW # undef FMT_LOCALE_THROW
TEST(LocaleTest, LocaleMock) { TEST(LocaleTest, LocaleMock) {
ScopedMock<LocaleMock> mock; ScopedMock<LocaleMock> mock;
@ -510,9 +514,9 @@ TEST(LocaleTest, LocaleMock) {
} }
TEST(LocaleTest, Locale) { TEST(LocaleTest, Locale) {
#ifndef LC_NUMERIC_MASK # ifndef LC_NUMERIC_MASK
enum { LC_NUMERIC_MASK = LC_NUMERIC }; enum { LC_NUMERIC_MASK = LC_NUMERIC };
#endif # endif
ScopedMock<LocaleMock> mock; ScopedMock<LocaleMock> mock;
LocaleType impl = reinterpret_cast<LocaleType>(42); LocaleType impl = reinterpret_cast<LocaleType>(42);
EXPECT_CALL(mock, newlocale(LC_NUMERIC_MASK, StrEq("C"), FMT_NULL)) EXPECT_CALL(mock, newlocale(LC_NUMERIC_MASK, StrEq("C"), FMT_NULL))
@ -528,7 +532,7 @@ TEST(LocaleTest, Strtod) {
.WillOnce(Return(reinterpret_cast<LocaleType>(42))); .WillOnce(Return(reinterpret_cast<LocaleType>(42)));
EXPECT_CALL(mock, freelocale(_)); EXPECT_CALL(mock, freelocale(_));
fmt::Locale locale; fmt::Locale locale;
const char *str = "4.2"; const char* str = "4.2";
char end = 'x'; char end = 'x';
EXPECT_CALL(mock, strtod_l(str, _, locale.get())) EXPECT_CALL(mock, strtod_l(str, _, locale.get()))
.WillOnce(testing::DoAll(testing::SetArgPointee<1>(&end), Return(777))); .WillOnce(testing::DoAll(testing::SetArgPointee<1>(&end), Return(777)));

View File

@ -12,10 +12,10 @@
#include <stdio.h> #include <stdio.h>
#ifdef _WIN32 #ifdef _WIN32
# include <windows.h> # include <windows.h>
#else #else
# include <sys/param.h> // for FreeBSD version # include <sys/param.h> // for FreeBSD version
# include <sys/types.h> // for ssize_t # include <sys/types.h> // for ssize_t
#endif #endif
#ifndef _MSC_VER #ifndef _MSC_VER
@ -28,13 +28,13 @@ namespace test {
// Size type for read and write. // Size type for read and write.
typedef size_t size_t; typedef size_t size_t;
typedef ssize_t ssize_t; typedef ssize_t ssize_t;
int open(const char *path, int oflag, int mode); int open(const char* path, int oflag, int mode);
int fstat(int fd, struct stat *buf); int fstat(int fd, struct stat* buf);
#else #else
typedef unsigned size_t; typedef unsigned size_t;
typedef int ssize_t; typedef int ssize_t;
errno_t sopen_s( errno_t sopen_s(int* pfh, const char* filename, int oflag, int shflag,
int* pfh, const char *filename, int oflag, int shflag, int pmode); int pmode);
#endif #endif
#ifndef _WIN32 #ifndef _WIN32
@ -48,20 +48,20 @@ int close(int fildes);
int dup(int fildes); int dup(int fildes);
int dup2(int fildes, int fildes2); int dup2(int fildes, int fildes2);
FILE *fdopen(int fildes, const char *mode); FILE* fdopen(int fildes, const char* mode);
ssize_t read(int fildes, void *buf, size_t nbyte); ssize_t read(int fildes, void* buf, size_t nbyte);
ssize_t write(int fildes, const void *buf, size_t nbyte); ssize_t write(int fildes, const void* buf, size_t nbyte);
#ifndef _WIN32 #ifndef _WIN32
int pipe(int fildes[2]); int pipe(int fildes[2]);
#else #else
int pipe(int *pfds, unsigned psize, int textmode); int pipe(int* pfds, unsigned psize, int textmode);
#endif #endif
FILE *fopen(const char *filename, const char *mode); FILE* fopen(const char* filename, const char* mode);
int fclose(FILE *stream); int fclose(FILE* stream);
int (fileno)(FILE *stream); int(fileno)(FILE* stream);
} // namespace test } // namespace test
#define FMT_SYSTEM(call) test::call #define FMT_SYSTEM(call) test::call

View File

@ -14,7 +14,7 @@
#include "util.h" #include "util.h"
#ifdef fileno #ifdef fileno
# undef fileno # undef fileno
#endif #endif
using fmt::buffered_file; using fmt::buffered_file;
@ -44,9 +44,9 @@ static file open_file() {
} }
// Attempts to write a string to a file. // Attempts to write a string to a file.
static void write(file &f, fmt::string_view s) { static void write(file& f, fmt::string_view s) {
std::size_t num_chars_left = s.size(); std::size_t num_chars_left = s.size();
const char *ptr = s.data(); const char* ptr = s.data();
do { do {
std::size_t count = f.write(ptr, num_chars_left); std::size_t count = f.write(ptr, num_chars_left);
ptr += count; ptr += count;
@ -63,7 +63,7 @@ TEST(BufferedFileTest, DefaultCtor) {
TEST(BufferedFileTest, MoveCtor) { TEST(BufferedFileTest, MoveCtor) {
buffered_file bf = open_buffered_file(); buffered_file bf = open_buffered_file();
FILE *fp = bf.get(); FILE* fp = bf.get();
EXPECT_TRUE(fp != FMT_NULL); EXPECT_TRUE(fp != FMT_NULL);
buffered_file bf2(std::move(bf)); buffered_file bf2(std::move(bf));
EXPECT_EQ(fp, bf2.get()); EXPECT_EQ(fp, bf2.get());
@ -72,7 +72,7 @@ TEST(BufferedFileTest, MoveCtor) {
TEST(BufferedFileTest, MoveAssignment) { TEST(BufferedFileTest, MoveAssignment) {
buffered_file bf = open_buffered_file(); buffered_file bf = open_buffered_file();
FILE *fp = bf.get(); FILE* fp = bf.get();
EXPECT_TRUE(fp != FMT_NULL); EXPECT_TRUE(fp != FMT_NULL);
buffered_file bf2; buffered_file bf2;
bf2 = std::move(bf); bf2 = std::move(bf);
@ -89,13 +89,13 @@ TEST(BufferedFileTest, MoveAssignmentClosesFile) {
} }
TEST(BufferedFileTest, MoveFromTemporaryInCtor) { TEST(BufferedFileTest, MoveFromTemporaryInCtor) {
FILE *fp = FMT_NULL; FILE* fp = FMT_NULL;
buffered_file f(open_buffered_file(&fp)); buffered_file f(open_buffered_file(&fp));
EXPECT_EQ(fp, f.get()); EXPECT_EQ(fp, f.get());
} }
TEST(BufferedFileTest, MoveFromTemporaryInAssignment) { TEST(BufferedFileTest, MoveFromTemporaryInAssignment) {
FILE *fp = FMT_NULL; FILE* fp = FMT_NULL;
buffered_file f; buffered_file f;
f = open_buffered_file(&fp); f = open_buffered_file(&fp);
EXPECT_EQ(fp, f.get()); EXPECT_EQ(fp, f.get());
@ -119,14 +119,16 @@ TEST(BufferedFileTest, CloseFileInDtor) {
TEST(BufferedFileTest, CloseErrorInDtor) { TEST(BufferedFileTest, CloseErrorInDtor) {
std::unique_ptr<buffered_file> f(new buffered_file(open_buffered_file())); std::unique_ptr<buffered_file> f(new buffered_file(open_buffered_file()));
EXPECT_WRITE(stderr, { EXPECT_WRITE(stderr,
// The close function must be called inside EXPECT_WRITE, otherwise {
// the system may recycle closed file descriptor when redirecting the // The close function must be called inside EXPECT_WRITE,
// output in EXPECT_STDERR and the second close will break output // otherwise the system may recycle closed file descriptor when
// redirection. // redirecting the output in EXPECT_STDERR and the second close
FMT_POSIX(close(f->fileno())); // will break output redirection.
SUPPRESS_ASSERT(f.reset(FMT_NULL)); FMT_POSIX(close(f->fileno()));
}, format_system_error(EBADF, "cannot close file") + "\n"); SUPPRESS_ASSERT(f.reset(FMT_NULL));
},
format_system_error(EBADF, "cannot close file") + "\n");
} }
TEST(BufferedFileTest, Close) { TEST(BufferedFileTest, Close) {
@ -149,13 +151,15 @@ TEST(BufferedFileTest, Fileno) {
#ifndef __COVERITY__ #ifndef __COVERITY__
// fileno on a null FILE pointer either crashes or returns an error. // fileno on a null FILE pointer either crashes or returns an error.
// Disable Coverity because this is intentional. // Disable Coverity because this is intentional.
EXPECT_DEATH_IF_SUPPORTED({ EXPECT_DEATH_IF_SUPPORTED(
try { {
f.fileno(); try {
} catch (const fmt::system_error&) { f.fileno();
std::exit(1); } catch (const fmt::system_error&) {
} std::exit(1);
}, ""); }
},
"");
#endif #endif
f = open_buffered_file(); f = open_buffered_file();
EXPECT_TRUE(f.fileno() != -1); EXPECT_TRUE(f.fileno() != -1);
@ -169,7 +173,7 @@ TEST(FileTest, DefaultCtor) {
} }
TEST(FileTest, OpenBufferedFileInCtor) { TEST(FileTest, OpenBufferedFileInCtor) {
FILE *fp = safe_fopen("test-file", "w"); FILE* fp = safe_fopen("test-file", "w");
std::fputs(FILE_CONTENT, fp); std::fputs(FILE_CONTENT, fp);
std::fclose(fp); std::fclose(fp);
file f("test-file", file::RDONLY); file f("test-file", file::RDONLY);
@ -177,8 +181,8 @@ TEST(FileTest, OpenBufferedFileInCtor) {
} }
TEST(FileTest, OpenBufferedFileError) { TEST(FileTest, OpenBufferedFileError) {
EXPECT_SYSTEM_ERROR(file("nonexistent", file::RDONLY), EXPECT_SYSTEM_ERROR(file("nonexistent", file::RDONLY), ENOENT,
ENOENT, "cannot open file nonexistent"); "cannot open file nonexistent");
} }
TEST(FileTest, MoveCtor) { TEST(FileTest, MoveCtor) {
@ -208,7 +212,7 @@ TEST(FileTest, MoveAssignmentClosesFile) {
EXPECT_TRUE(isclosed(old_fd)); EXPECT_TRUE(isclosed(old_fd));
} }
static file OpenBufferedFile(int &fd) { static file OpenBufferedFile(int& fd) {
file f = open_file(); file f = open_file();
fd = f.descriptor(); fd = f.descriptor();
return f; return f;
@ -246,14 +250,16 @@ TEST(FileTest, CloseFileInDtor) {
TEST(FileTest, CloseErrorInDtor) { TEST(FileTest, CloseErrorInDtor) {
std::unique_ptr<file> f(new file(open_file())); std::unique_ptr<file> f(new file(open_file()));
EXPECT_WRITE(stderr, { EXPECT_WRITE(stderr,
// The close function must be called inside EXPECT_WRITE, otherwise {
// the system may recycle closed file descriptor when redirecting the // The close function must be called inside EXPECT_WRITE,
// output in EXPECT_STDERR and the second close will break output // otherwise the system may recycle closed file descriptor when
// redirection. // redirecting the output in EXPECT_STDERR and the second close
FMT_POSIX(close(f->descriptor())); // will break output redirection.
SUPPRESS_ASSERT(f.reset(FMT_NULL)); FMT_POSIX(close(f->descriptor()));
}, format_system_error(EBADF, "cannot close file") + "\n"); SUPPRESS_ASSERT(f.reset(FMT_NULL));
},
format_system_error(EBADF, "cannot close file") + "\n");
} }
TEST(FileTest, Close) { TEST(FileTest, Close) {
@ -309,8 +315,8 @@ TEST(FileTest, Dup) {
#ifndef __COVERITY__ #ifndef __COVERITY__
TEST(FileTest, DupError) { TEST(FileTest, DupError) {
int value = -1; int value = -1;
EXPECT_SYSTEM_ERROR_NOASSERT(file::dup(value), EXPECT_SYSTEM_ERROR_NOASSERT(file::dup(value), EBADF,
EBADF, "cannot duplicate file descriptor -1"); "cannot duplicate file descriptor -1");
} }
#endif #endif
@ -324,8 +330,9 @@ TEST(FileTest, Dup2) {
TEST(FileTest, Dup2Error) { TEST(FileTest, Dup2Error) {
file f = open_file(); file f = open_file();
EXPECT_SYSTEM_ERROR_NOASSERT(f.dup2(-1), EBADF, EXPECT_SYSTEM_ERROR_NOASSERT(
fmt::format("cannot duplicate file descriptor {} to -1", f.descriptor())); f.dup2(-1), EBADF,
fmt::format("cannot duplicate file descriptor {} to -1", f.descriptor()));
} }
TEST(FileTest, Dup2NoExcept) { TEST(FileTest, Dup2NoExcept) {
@ -363,8 +370,8 @@ TEST(FileTest, Fdopen) {
TEST(FileTest, FdopenError) { TEST(FileTest, FdopenError) {
file f; file f;
EXPECT_SYSTEM_ERROR_NOASSERT( EXPECT_SYSTEM_ERROR_NOASSERT(f.fdopen("r"), EBADF,
f.fdopen("r"), EBADF, "cannot associate stream with file descriptor"); "cannot associate stream with file descriptor");
} }
#ifdef FMT_LOCALE #ifdef FMT_LOCALE

View File

@ -5,6 +5,7 @@
// //
// For the license information refer to prepare.h. // For the license information refer to prepare.h.
#include <stdint.h>
#include <cctype> #include <cctype>
#include <cfloat> #include <cfloat>
#include <climits> #include <climits>
@ -13,12 +14,11 @@
#include <deque> #include <deque>
#include <list> #include <list>
#include <memory> #include <memory>
#include <stdint.h>
#include <string> #include <string>
// Check if fmt/prepare.h compiles with windows.h included before it. // Check if fmt/prepare.h compiles with windows.h included before it.
#ifdef _WIN32 #ifdef _WIN32
#include <windows.h> # include <windows.h>
#endif #endif
#include "fmt/prepare.h" #include "fmt/prepare.h"
@ -35,24 +35,24 @@ using testing::Return;
using testing::StrictMock; using testing::StrictMock;
class mock_parts_collector { class mock_parts_collector {
public: public:
MOCK_METHOD1(add, void(fmt::format_part<char>)); MOCK_METHOD1(add, void(fmt::format_part<char>));
MOCK_METHOD1(substitute_last, void(fmt::format_part<char>)); MOCK_METHOD1(substitute_last, void(fmt::format_part<char>));
MOCK_METHOD0(last, fmt::format_part<char>()); MOCK_METHOD0(last, fmt::format_part<char>());
}; };
FMT_BEGIN_NAMESPACE FMT_BEGIN_NAMESPACE
bool operator==(const internal::string_view_metadata &lhs, bool operator==(const internal::string_view_metadata& lhs,
const internal::string_view_metadata &rhs) { const internal::string_view_metadata& rhs) {
return std::tie(lhs.offset_, lhs.size_) == std::tie(rhs.offset_, rhs.size_); return std::tie(lhs.offset_, lhs.size_) == std::tie(rhs.offset_, rhs.size_);
} }
bool operator!=(const internal::string_view_metadata &lhs, bool operator!=(const internal::string_view_metadata& lhs,
const internal::string_view_metadata &rhs) { const internal::string_view_metadata& rhs) {
return !(lhs == rhs); return !(lhs == rhs);
} }
bool operator==(const format_part<char>::specification &lhs, bool operator==(const format_part<char>::specification& lhs,
const format_part<char>::specification &rhs) { const format_part<char>::specification& rhs) {
if (lhs.arg_id.which != rhs.arg_id.which) { if (lhs.arg_id.which != rhs.arg_id.which) {
return false; return false;
} }
@ -79,13 +79,13 @@ bool operator==(const format_part<char>::specification &lhs,
rhs.parsed_specs.flags, rhs.parsed_specs.type); rhs.parsed_specs.flags, rhs.parsed_specs.type);
} }
bool operator!=(const format_part<char>::specification &lhs, bool operator!=(const format_part<char>::specification& lhs,
const format_part<char>::specification &rhs) { const format_part<char>::specification& rhs) {
return !(lhs == rhs); return !(lhs == rhs);
} }
bool operator==(const format_part<char> &lhs, bool operator==(const format_part<char>& lhs,
const fmt::format_part<char> &rhs) { const fmt::format_part<char>& rhs) {
typedef format_part<char>::which_value which_value; typedef format_part<char>::which_value which_value;
if (lhs.which != rhs.which || if (lhs.which != rhs.which ||
@ -114,8 +114,8 @@ bool operator==(const format_part<char> &lhs,
return false; return false;
} }
bool operator!=(const fmt::format_part<char> &lhs, bool operator!=(const fmt::format_part<char>& lhs,
const fmt::format_part<char> &rhs) { const fmt::format_part<char>& rhs) {
return !(lhs == rhs); return !(lhs == rhs);
} }
FMT_END_NAMESPACE FMT_END_NAMESPACE
@ -303,13 +303,15 @@ TEST(PrepareTest, FormatPreparationHandler_OnArgId_AddsPartWithPassedNamedId) {
const auto expected_third_arg_id = fmt::string_view(format.data() + 6, 3); const auto expected_third_arg_id = fmt::string_view(format.data() + 6, 3);
const auto expected_third_arg_view_metadata = const auto expected_third_arg_view_metadata =
fmt::internal::string_view_metadata(6, 3); fmt::internal::string_view_metadata(6, 3);
EXPECT_CALL(parts, add(format_part( EXPECT_CALL(
named_argument_id(expected_first_arg_view_metadata)))); parts,
add(format_part(named_argument_id(expected_first_arg_view_metadata))));
EXPECT_CALL( EXPECT_CALL(
parts, parts,
add(format_part(named_argument_id(expected_second_arg_view_metadata)))); add(format_part(named_argument_id(expected_second_arg_view_metadata))));
EXPECT_CALL(parts, add(format_part( EXPECT_CALL(
named_argument_id(expected_third_arg_view_metadata)))); parts,
add(format_part(named_argument_id(expected_third_arg_view_metadata))));
handler.on_arg_id(expected_first_arg_id); handler.on_arg_id(expected_first_arg_id);
handler.on_arg_id(expected_second_arg_id); handler.on_arg_id(expected_second_arg_id);
@ -423,13 +425,13 @@ TEST(PrepareTest, CompileTimePreparedPartsTypeProvider) {
check_prepared_parts_type<3u>(FMT_STRING("text{}text")); check_prepared_parts_type<3u>(FMT_STRING("text{}text"));
check_prepared_parts_type<3u>(FMT_STRING("{:{}.{}} {:{}}")); check_prepared_parts_type<3u>(FMT_STRING("{:{}.{}} {:{}}"));
check_prepared_parts_type<3u>(FMT_STRING("{{{}}}")); // '{', 'argument', '}' check_prepared_parts_type<3u>(FMT_STRING("{{{}}}")); // '{', 'argument', '}'
check_prepared_parts_type<2u>(FMT_STRING("text{{")); // 'text', '{' check_prepared_parts_type<2u>(FMT_STRING("text{{")); // 'text', '{'
check_prepared_parts_type<3u>(FMT_STRING("text{{ ")); // 'text', '{', ' ' check_prepared_parts_type<3u>(FMT_STRING("text{{ ")); // 'text', '{', ' '
check_prepared_parts_type<2u>(FMT_STRING("}}text")); // '}', text check_prepared_parts_type<2u>(FMT_STRING("}}text")); // '}', text
check_prepared_parts_type<2u>(FMT_STRING("text}}text")); // 'text}', 'text' check_prepared_parts_type<2u>(FMT_STRING("text}}text")); // 'text}', 'text'
check_prepared_parts_type<4u>( check_prepared_parts_type<4u>(
FMT_STRING("text{{}}text")); // 'text', '{', '}', 'text' FMT_STRING("text{{}}text")); // 'text', '{', '}', 'text'
} }
#endif #endif
@ -500,13 +502,13 @@ TEST(PrepareTest, UserProvidedPartsContainerUnderlyingContainer) {
} }
class custom_parts_container { class custom_parts_container {
public: public:
typedef fmt::format_part<char> format_part_type; typedef fmt::format_part<char> format_part_type;
private: private:
typedef std::deque<format_part_type> parts; typedef std::deque<format_part_type> parts;
public: public:
void add(format_part_type part) { parts_.push_back(std::move(part)); } void add(format_part_type part) { parts_.push_back(std::move(part)); }
void substitute_last(format_part_type part) { void substitute_last(format_part_type part) {
@ -532,7 +534,7 @@ public:
return parts_.end(); return parts_.end();
} }
private: private:
parts parts_; parts parts_;
}; };
@ -547,10 +549,10 @@ TEST(PrepareTest, UserProvidedPartsContainer) {
} }
TEST(PrepareTest, PassConstCharPointerFormat) { TEST(PrepareTest, PassConstCharPointerFormat) {
const char *c_format = "test {}"; const char* c_format = "test {}";
const auto prepared = fmt::prepare<int>(c_format); const auto prepared = fmt::prepare<int>(c_format);
EXPECT_EQ("test 42", prepared.format(42)); EXPECT_EQ("test 42", prepared.format(42));
const wchar_t *wc_format = L"test {}"; const wchar_t* wc_format = L"test {}";
const auto wprepared = fmt::prepare<int>(wc_format); const auto wprepared = fmt::prepare<int>(wc_format);
EXPECT_EQ(L"test 42", wprepared.format(42)); EXPECT_EQ(L"test 42", wprepared.format(42));
} }
@ -607,10 +609,10 @@ TEST(PrepareTest, PassCompileString) {
template <typename T> struct user_allocator { template <typename T> struct user_allocator {
typedef T value_type; typedef T value_type;
typedef value_type *pointer; typedef value_type* pointer;
typedef const value_type *const_pointer; typedef const value_type* const_pointer;
typedef value_type &reference; typedef value_type& reference;
typedef const value_type &const_reference; typedef const value_type& const_reference;
typedef std::size_t size_type; typedef std::size_t size_type;
typedef std::ptrdiff_t difference_type; typedef std::ptrdiff_t difference_type;
@ -618,7 +620,7 @@ template <typename T> struct user_allocator {
user_allocator() = default; user_allocator() = default;
~user_allocator() = default; ~user_allocator() = default;
template <typename U> user_allocator(const user_allocator<U> &) {} template <typename U> user_allocator(const user_allocator<U>&) {}
pointer allocate(size_type cnt, pointer allocate(size_type cnt,
typename std::allocator<void>::const_pointer = 0) { typename std::allocator<void>::const_pointer = 0) {
@ -627,12 +629,12 @@ template <typename T> struct user_allocator {
void deallocate(pointer p, size_type cnt) { delete[] p; } void deallocate(pointer p, size_type cnt) { delete[] p; }
void construct(pointer p, const value_type &val) { new (p) value_type(val); } void construct(pointer p, const value_type& val) { new (p) value_type(val); }
void destroy(pointer p) { (*p).~value_type(); } void destroy(pointer p) { (*p).~value_type(); }
bool operator==(const user_allocator &other) const { return true; } bool operator==(const user_allocator& other) const { return true; }
bool operator!=(const user_allocator &other) const { return false; } bool operator!=(const user_allocator& other) const { return false; }
}; };
TEST(PrepareTest, PassUserTypeFormat) { TEST(PrepareTest, PassUserTypeFormat) {

View File

@ -35,17 +35,17 @@ static std::wstring make_positional(fmt::wstring_view format) {
// A wrapper around fmt::sprintf to workaround bogus warnings about invalid // A wrapper around fmt::sprintf to workaround bogus warnings about invalid
// format strings in MSVC. // format strings in MSVC.
template <typename... Args> template <typename... Args>
std::string test_sprintf(fmt::string_view format, const Args &... args) { std::string test_sprintf(fmt::string_view format, const Args&... args) {
return fmt::sprintf(format, args...); return fmt::sprintf(format, args...);
} }
template <typename... Args> template <typename... Args>
std::wstring test_sprintf(fmt::wstring_view format, const Args &... args) { std::wstring test_sprintf(fmt::wstring_view format, const Args&... args) {
return fmt::sprintf(format, args...); return fmt::sprintf(format, args...);
} }
#define EXPECT_PRINTF(expected_output, format, arg) \ #define EXPECT_PRINTF(expected_output, format, arg) \
EXPECT_EQ(expected_output, test_sprintf(format, arg)) \ EXPECT_EQ(expected_output, test_sprintf(format, arg)) \
<< "format: " << format; \ << "format: " << format; \
EXPECT_EQ(expected_output, fmt::sprintf(make_positional(format), arg)) EXPECT_EQ(expected_output, fmt::sprintf(make_positional(format), arg))
TEST(PrintfTest, NoArgs) { TEST(PrintfTest, NoArgs) {
@ -69,11 +69,10 @@ TEST(PrintfTest, Escape) {
TEST(PrintfTest, PositionalArgs) { TEST(PrintfTest, PositionalArgs) {
EXPECT_EQ("42", test_sprintf("%1$d", 42)); EXPECT_EQ("42", test_sprintf("%1$d", 42));
EXPECT_EQ("before 42", test_sprintf("before %1$d", 42)); EXPECT_EQ("before 42", test_sprintf("before %1$d", 42));
EXPECT_EQ("42 after", test_sprintf("%1$d after",42)); EXPECT_EQ("42 after", test_sprintf("%1$d after", 42));
EXPECT_EQ("before 42 after", test_sprintf("before %1$d after", 42)); EXPECT_EQ("before 42 after", test_sprintf("before %1$d after", 42));
EXPECT_EQ("answer = 42", test_sprintf("%1$s = %2$d", "answer", 42)); EXPECT_EQ("answer = 42", test_sprintf("%1$s = %2$d", "answer", 42));
EXPECT_EQ("42 is the answer", EXPECT_EQ("42 is the answer", test_sprintf("%2$d is the %1$s", "answer", 42));
test_sprintf("%2$d is the %1$s", "answer", 42));
EXPECT_EQ("abracadabra", test_sprintf("%1$s%2$s%1$s", "abra", "cad")); EXPECT_EQ("abracadabra", test_sprintf("%1$s%2$s%1$s", "abra", "cad"));
} }
@ -82,46 +81,46 @@ TEST(PrintfTest, AutomaticArgIndexing) {
} }
TEST(PrintfTest, NumberIsTooBigInArgIndex) { TEST(PrintfTest, NumberIsTooBigInArgIndex) {
EXPECT_THROW_MSG(test_sprintf(format("%{}$", BIG_NUM)), EXPECT_THROW_MSG(test_sprintf(format("%{}$", BIG_NUM)), format_error,
format_error, "number is too big"); "number is too big");
EXPECT_THROW_MSG(test_sprintf(format("%{}$d", BIG_NUM)), EXPECT_THROW_MSG(test_sprintf(format("%{}$d", BIG_NUM)), format_error,
format_error, "number is too big"); "number is too big");
} }
TEST(PrintfTest, SwitchArgIndexing) { TEST(PrintfTest, SwitchArgIndexing) {
EXPECT_THROW_MSG(test_sprintf("%1$d%", 1, 2), EXPECT_THROW_MSG(test_sprintf("%1$d%", 1, 2), format_error,
format_error, "cannot switch from manual to automatic argument indexing"); "cannot switch from manual to automatic argument indexing");
EXPECT_THROW_MSG(test_sprintf(format("%1$d%{}d", BIG_NUM), 1, 2), EXPECT_THROW_MSG(test_sprintf(format("%1$d%{}d", BIG_NUM), 1, 2),
format_error, "number is too big"); format_error, "number is too big");
EXPECT_THROW_MSG(test_sprintf("%1$d%d", 1, 2), EXPECT_THROW_MSG(test_sprintf("%1$d%d", 1, 2), format_error,
format_error, "cannot switch from manual to automatic argument indexing"); "cannot switch from manual to automatic argument indexing");
EXPECT_THROW_MSG(test_sprintf("%d%1$", 1, 2), EXPECT_THROW_MSG(test_sprintf("%d%1$", 1, 2), format_error,
format_error, "cannot switch from automatic to manual argument indexing"); "cannot switch from automatic to manual argument indexing");
EXPECT_THROW_MSG(test_sprintf(format("%d%{}$d", BIG_NUM), 1, 2), EXPECT_THROW_MSG(test_sprintf(format("%d%{}$d", BIG_NUM), 1, 2), format_error,
format_error, "number is too big"); "number is too big");
EXPECT_THROW_MSG(test_sprintf("%d%1$d", 1, 2), EXPECT_THROW_MSG(test_sprintf("%d%1$d", 1, 2), format_error,
format_error, "cannot switch from automatic to manual argument indexing"); "cannot switch from automatic to manual argument indexing");
// Indexing errors override width errors. // Indexing errors override width errors.
EXPECT_THROW_MSG(test_sprintf(format("%d%1${}d", BIG_NUM), 1, 2), EXPECT_THROW_MSG(test_sprintf(format("%d%1${}d", BIG_NUM), 1, 2),
format_error, "number is too big"); format_error, "number is too big");
EXPECT_THROW_MSG(test_sprintf(format("%1$d%{}d", BIG_NUM), 1, 2), EXPECT_THROW_MSG(test_sprintf(format("%1$d%{}d", BIG_NUM), 1, 2),
format_error, "number is too big"); format_error, "number is too big");
} }
TEST(PrintfTest, InvalidArgIndex) { TEST(PrintfTest, InvalidArgIndex) {
EXPECT_THROW_MSG(test_sprintf("%0$d", 42), format_error, EXPECT_THROW_MSG(test_sprintf("%0$d", 42), format_error,
"argument index out of range"); "argument index out of range");
EXPECT_THROW_MSG(test_sprintf("%2$d", 42), format_error, EXPECT_THROW_MSG(test_sprintf("%2$d", 42), format_error,
"argument index out of range"); "argument index out of range");
EXPECT_THROW_MSG(test_sprintf(format("%{}$d", INT_MAX), 42), EXPECT_THROW_MSG(test_sprintf(format("%{}$d", INT_MAX), 42), format_error,
format_error, "argument index out of range"); "argument index out of range");
EXPECT_THROW_MSG(test_sprintf("%2$", 42), EXPECT_THROW_MSG(test_sprintf("%2$", 42), format_error,
format_error, "argument index out of range"); "argument index out of range");
EXPECT_THROW_MSG(test_sprintf(format("%{}$d", BIG_NUM), 42), EXPECT_THROW_MSG(test_sprintf(format("%{}$d", BIG_NUM), 42), format_error,
format_error, "number is too big"); "number is too big");
} }
TEST(PrintfTest, DefaultAlignRight) { TEST(PrintfTest, DefaultAlignRight) {
@ -175,8 +174,8 @@ TEST(PrintfTest, HashFlag) {
EXPECT_PRINTF("0x42", "%#x", 0x42); EXPECT_PRINTF("0x42", "%#x", 0x42);
EXPECT_PRINTF("0X42", "%#X", 0x42); EXPECT_PRINTF("0X42", "%#X", 0x42);
EXPECT_PRINTF( EXPECT_PRINTF(fmt::format("0x{:x}", static_cast<unsigned>(-0x42)), "%#x",
fmt::format("0x{:x}", static_cast<unsigned>(-0x42)), "%#x", -0x42); -0x42);
EXPECT_PRINTF("0", "%#x", 0); EXPECT_PRINTF("0", "%#x", 0);
EXPECT_PRINTF("0x0042", "%#06x", 0x42); EXPECT_PRINTF("0x0042", "%#06x", 0x42);
@ -208,23 +207,23 @@ TEST(PrintfTest, Width) {
// Width cannot be specified twice. // Width cannot be specified twice.
EXPECT_THROW_MSG(test_sprintf("%5-5d", 42), format_error, EXPECT_THROW_MSG(test_sprintf("%5-5d", 42), format_error,
"invalid type specifier"); "invalid type specifier");
EXPECT_THROW_MSG(test_sprintf(format("%{}d", BIG_NUM), 42), EXPECT_THROW_MSG(test_sprintf(format("%{}d", BIG_NUM), 42), format_error,
format_error, "number is too big"); "number is too big");
EXPECT_THROW_MSG(test_sprintf(format("%1${}d", BIG_NUM), 42), EXPECT_THROW_MSG(test_sprintf(format("%1${}d", BIG_NUM), 42), format_error,
format_error, "number is too big"); "number is too big");
} }
TEST(PrintfTest, DynamicWidth) { TEST(PrintfTest, DynamicWidth) {
EXPECT_EQ(" 42", test_sprintf("%*d", 5, 42)); EXPECT_EQ(" 42", test_sprintf("%*d", 5, 42));
EXPECT_EQ("42 ", test_sprintf("%*d", -5, 42)); EXPECT_EQ("42 ", test_sprintf("%*d", -5, 42));
EXPECT_THROW_MSG(test_sprintf("%*d", 5.0, 42), format_error, EXPECT_THROW_MSG(test_sprintf("%*d", 5.0, 42), format_error,
"width is not integer"); "width is not integer");
EXPECT_THROW_MSG(test_sprintf("%*d"), format_error, EXPECT_THROW_MSG(test_sprintf("%*d"), format_error,
"argument index out of range"); "argument index out of range");
EXPECT_THROW_MSG(test_sprintf("%*d", BIG_NUM, 42), format_error, EXPECT_THROW_MSG(test_sprintf("%*d", BIG_NUM, 42), format_error,
"number is too big"); "number is too big");
} }
TEST(PrintfTest, IntPrecision) { TEST(PrintfTest, IntPrecision) {
@ -267,24 +266,22 @@ TEST(PrintfTest, DynamicPrecision) {
EXPECT_EQ("00042", test_sprintf("%.*d", 5, 42)); EXPECT_EQ("00042", test_sprintf("%.*d", 5, 42));
EXPECT_EQ("42", test_sprintf("%.*d", -5, 42)); EXPECT_EQ("42", test_sprintf("%.*d", -5, 42));
EXPECT_THROW_MSG(test_sprintf("%.*d", 5.0, 42), format_error, EXPECT_THROW_MSG(test_sprintf("%.*d", 5.0, 42), format_error,
"precision is not integer"); "precision is not integer");
EXPECT_THROW_MSG(test_sprintf("%.*d"), format_error, EXPECT_THROW_MSG(test_sprintf("%.*d"), format_error,
"argument index out of range"); "argument index out of range");
EXPECT_THROW_MSG(test_sprintf("%.*d", BIG_NUM, 42), format_error, EXPECT_THROW_MSG(test_sprintf("%.*d", BIG_NUM, 42), format_error,
"number is too big"); "number is too big");
if (sizeof(long long) != sizeof(int)) { if (sizeof(long long) != sizeof(int)) {
long long prec = static_cast<long long>(INT_MIN) - 1; long long prec = static_cast<long long>(INT_MIN) - 1;
EXPECT_THROW_MSG(test_sprintf("%.*d", prec, 42), format_error, EXPECT_THROW_MSG(test_sprintf("%.*d", prec, 42), format_error,
"number is too big"); "number is too big");
} }
} }
template <typename T> template <typename T> struct make_signed { typedef T type; };
struct make_signed { typedef T type; };
#define SPECIALIZE_MAKE_SIGNED(T, S) \ #define SPECIALIZE_MAKE_SIGNED(T, S) \
template <> \ template <> struct make_signed<T> { typedef S type; }
struct make_signed<T> { typedef S type; }
SPECIALIZE_MAKE_SIGNED(char, signed char); SPECIALIZE_MAKE_SIGNED(char, signed char);
SPECIALIZE_MAKE_SIGNED(unsigned char, signed char); SPECIALIZE_MAKE_SIGNED(unsigned char, signed char);
@ -295,7 +292,7 @@ SPECIALIZE_MAKE_SIGNED(unsigned long long, long long);
// Test length format specifier ``length_spec``. // Test length format specifier ``length_spec``.
template <typename T, typename U> template <typename T, typename U>
void TestLength(const char *length_spec, U value) { void TestLength(const char* length_spec, U value) {
long long signed_value = 0; long long signed_value = 0;
unsigned long long unsigned_value = 0; unsigned long long unsigned_value = 0;
// Apply integer promotion to the argument. // Apply integer promotion to the argument.
@ -335,8 +332,7 @@ void TestLength(const char *length_spec, U value) {
EXPECT_PRINTF(os.str(), fmt::format("%{}X", length_spec), value); EXPECT_PRINTF(os.str(), fmt::format("%{}X", length_spec), value);
} }
template <typename T> template <typename T> void TestLength(const char* length_spec) {
void TestLength(const char *length_spec) {
T min = std::numeric_limits<T>::min(), max = std::numeric_limits<T>::max(); T min = std::numeric_limits<T>::min(), max = std::numeric_limits<T>::max();
TestLength<T>(length_spec, 42); TestLength<T>(length_spec, 42);
TestLength<T>(length_spec, -42); TestLength<T>(length_spec, -42);
@ -430,32 +426,32 @@ TEST(PrintfTest, Char) {
EXPECT_PRINTF("x", "%c", 'x'); EXPECT_PRINTF("x", "%c", 'x');
int max = std::numeric_limits<int>::max(); int max = std::numeric_limits<int>::max();
EXPECT_PRINTF(fmt::format("{}", static_cast<char>(max)), "%c", max); EXPECT_PRINTF(fmt::format("{}", static_cast<char>(max)), "%c", max);
//EXPECT_PRINTF("x", "%lc", L'x'); // EXPECT_PRINTF("x", "%lc", L'x');
EXPECT_PRINTF(L"x", L"%c", L'x'); EXPECT_PRINTF(L"x", L"%c", L'x');
EXPECT_PRINTF(fmt::format(L"{}", static_cast<wchar_t>(max)), L"%c", max); EXPECT_PRINTF(fmt::format(L"{}", static_cast<wchar_t>(max)), L"%c", max);
} }
TEST(PrintfTest, String) { TEST(PrintfTest, String) {
EXPECT_PRINTF("abc", "%s", "abc"); EXPECT_PRINTF("abc", "%s", "abc");
const char *null_str = FMT_NULL; const char* null_str = FMT_NULL;
EXPECT_PRINTF("(null)", "%s", null_str); EXPECT_PRINTF("(null)", "%s", null_str);
EXPECT_PRINTF(" (null)", "%10s", null_str); EXPECT_PRINTF(" (null)", "%10s", null_str);
EXPECT_PRINTF(L"abc", L"%s", L"abc"); EXPECT_PRINTF(L"abc", L"%s", L"abc");
const wchar_t *null_wstr = FMT_NULL; const wchar_t* null_wstr = FMT_NULL;
EXPECT_PRINTF(L"(null)", L"%s", null_wstr); EXPECT_PRINTF(L"(null)", L"%s", null_wstr);
EXPECT_PRINTF(L" (null)", L"%10s", null_wstr); EXPECT_PRINTF(L" (null)", L"%10s", null_wstr);
} }
TEST(PrintfTest, Pointer) { TEST(PrintfTest, Pointer) {
int n; int n;
void *p = &n; void* p = &n;
EXPECT_PRINTF(fmt::format("{}", p), "%p", p); EXPECT_PRINTF(fmt::format("{}", p), "%p", p);
p = FMT_NULL; p = FMT_NULL;
EXPECT_PRINTF("(nil)", "%p", p); EXPECT_PRINTF("(nil)", "%p", p);
EXPECT_PRINTF(" (nil)", "%10p", p); EXPECT_PRINTF(" (nil)", "%10p", p);
const char *s = "test"; const char* s = "test";
EXPECT_PRINTF(fmt::format("{:p}", s), "%p", s); EXPECT_PRINTF(fmt::format("{:p}", s), "%p", s);
const char *null_str = FMT_NULL; const char* null_str = FMT_NULL;
EXPECT_PRINTF("(nil)", "%p", null_str); EXPECT_PRINTF("(nil)", "%p", null_str);
p = &n; p = &n;
@ -463,9 +459,9 @@ TEST(PrintfTest, Pointer) {
p = FMT_NULL; p = FMT_NULL;
EXPECT_PRINTF(L"(nil)", L"%p", p); EXPECT_PRINTF(L"(nil)", L"%p", p);
EXPECT_PRINTF(L" (nil)", L"%10p", p); EXPECT_PRINTF(L" (nil)", L"%10p", p);
const wchar_t *w = L"test"; const wchar_t* w = L"test";
EXPECT_PRINTF(fmt::format(L"{:p}", w), L"%p", w); EXPECT_PRINTF(fmt::format(L"{:p}", w), L"%p", w);
const wchar_t *null_wstr = FMT_NULL; const wchar_t* null_wstr = FMT_NULL;
EXPECT_PRINTF(L"(nil)", L"%p", null_wstr); EXPECT_PRINTF(L"(nil)", L"%p", null_wstr);
} }
@ -475,14 +471,12 @@ TEST(PrintfTest, Location) {
enum E { A = 42 }; enum E { A = 42 };
TEST(PrintfTest, Enum) { TEST(PrintfTest, Enum) { EXPECT_PRINTF("42", "%d", A); }
EXPECT_PRINTF("42", "%d", A);
}
#if FMT_USE_FILE_DESCRIPTORS #if FMT_USE_FILE_DESCRIPTORS
TEST(PrintfTest, Examples) { TEST(PrintfTest, Examples) {
const char *weekday = "Thursday"; const char* weekday = "Thursday";
const char *month = "August"; const char* month = "August";
int day = 21; int day = 21;
EXPECT_WRITE(stdout, fmt::printf("%1$s, %3$d %2$s", weekday, month, day), EXPECT_WRITE(stdout, fmt::printf("%1$s, %3$d %2$s", weekday, month, day),
"Thursday, 21 August"); "Thursday, 21 August");
@ -496,9 +490,7 @@ TEST(PrintfTest, PrintfError) {
} }
#endif #endif
TEST(PrintfTest, WideString) { TEST(PrintfTest, WideString) { EXPECT_EQ(L"abc", fmt::sprintf(L"%s", L"abc")); }
EXPECT_EQ(L"abc", fmt::sprintf(L"%s", L"abc"));
}
TEST(PrintfTest, PrintfCustom) { TEST(PrintfTest, PrintfCustom) {
EXPECT_EQ("abc", test_sprintf("%s", TestString("abc"))); EXPECT_EQ("abc", test_sprintf("%s", TestString("abc")));
@ -520,7 +512,7 @@ TEST(PrintfTest, VPrintf) {
EXPECT_WRITE(stdout, fmt::vfprintf(std::cout, "%d", args), "42"); EXPECT_WRITE(stdout, fmt::vfprintf(std::cout, "%d", args), "42");
} }
template<typename... Args> template <typename... Args>
void check_format_string_regression(fmt::string_view s, const Args&... args) { void check_format_string_regression(fmt::string_view s, const Args&... args) {
fmt::sprintf(s, args...); fmt::sprintf(s, args...);
} }
@ -530,40 +522,36 @@ TEST(PrintfTest, CheckFormatStringRegression) {
} }
TEST(PrintfTest, VSPrintfMakeArgsExample) { TEST(PrintfTest, VSPrintfMakeArgsExample) {
fmt::format_arg_store<fmt::printf_context, int, const char *> as{ fmt::format_arg_store<fmt::printf_context, int, const char*> as{42,
42, "something"}; "something"};
fmt::basic_format_args<fmt::printf_context> args(as); fmt::basic_format_args<fmt::printf_context> args(as);
EXPECT_EQ( EXPECT_EQ("[42] something happened", fmt::vsprintf("[%d] %s happened", args));
"[42] something happened", fmt::vsprintf("[%d] %s happened", args));
auto as2 = fmt::make_printf_args(42, "something"); auto as2 = fmt::make_printf_args(42, "something");
fmt::basic_format_args<fmt::printf_context> args2(as2); fmt::basic_format_args<fmt::printf_context> args2(as2);
EXPECT_EQ( EXPECT_EQ("[42] something happened",
"[42] something happened", fmt::vsprintf("[%d] %s happened", args2)); fmt::vsprintf("[%d] %s happened", args2));
//the older gcc versions can't cast the return value // the older gcc versions can't cast the return value
#if !defined(__GNUC__) || (__GNUC__ > 4) #if !defined(__GNUC__) || (__GNUC__ > 4)
EXPECT_EQ( EXPECT_EQ("[42] something happened",
"[42] something happened", fmt::vsprintf("[%d] %s happened",
fmt::vsprintf( fmt::make_printf_args(42, "something")));
"[%d] %s happened", fmt::make_printf_args(42, "something")));
#endif #endif
} }
TEST(PrintfTest, VSPrintfMakeWArgsExample) { TEST(PrintfTest, VSPrintfMakeWArgsExample) {
fmt::format_arg_store<fmt::wprintf_context, int, const wchar_t *> as{ fmt::format_arg_store<fmt::wprintf_context, int, const wchar_t*> as{
42, L"something"}; 42, L"something"};
fmt::basic_format_args<fmt::wprintf_context> args(as); fmt::basic_format_args<fmt::wprintf_context> args(as);
EXPECT_EQ( EXPECT_EQ(L"[42] something happened",
L"[42] something happened", fmt::vsprintf(L"[%d] %s happened", args));
fmt::vsprintf(L"[%d] %s happened", args)); auto as2 = fmt::make_wprintf_args(42, L"something");
auto as2 = fmt::make_wprintf_args(42, L"something");
fmt::basic_format_args<fmt::wprintf_context> args2(as2); fmt::basic_format_args<fmt::wprintf_context> args2(as2);
EXPECT_EQ( EXPECT_EQ(L"[42] something happened",
L"[42] something happened", fmt::vsprintf(L"[%d] %s happened", args2)); fmt::vsprintf(L"[%d] %s happened", args2));
// the older gcc versions can't cast the return value // the older gcc versions can't cast the return value
#if !defined(__GNUC__) || (__GNUC__ > 4) #if !defined(__GNUC__) || (__GNUC__ > 4)
EXPECT_EQ( EXPECT_EQ(L"[42] something happened",
L"[42] something happened", fmt::vsprintf(L"[%d] %s happened",
fmt::vsprintf( fmt::make_wprintf_args(42, L"something")));
L"[%d] %s happened", fmt::make_wprintf_args(42, L"something")));
#endif #endif
} }

View File

@ -13,13 +13,13 @@
#if (__cplusplus > 201402L) || \ #if (__cplusplus > 201402L) || \
(defined(_MSVC_LANG) && _MSVC_LANG > 201402L && _MSC_VER >= 1910) (defined(_MSVC_LANG) && _MSVC_LANG > 201402L && _MSC_VER >= 1910)
#include "fmt/ranges.h" # include "fmt/ranges.h"
#include "gtest.h" # include "gtest.h"
#include <vector> # include <array>
#include <array> # include <map>
#include <map> # include <string>
#include <string> # include <vector>
TEST(RangesTest, FormatVector) { TEST(RangesTest, FormatVector) {
std::vector<int32_t> iv{1, 2, 3, 5, 7, 11}; std::vector<int32_t> iv{1, 2, 3, 5, 7, 11};
@ -45,15 +45,14 @@ TEST(RangesTest, FormatPair) {
TEST(RangesTest, FormatTuple) { TEST(RangesTest, FormatTuple) {
std::tuple<int64_t, float, std::string, char> tu1{42, 3.14159265358979f, std::tuple<int64_t, float, std::string, char> tu1{42, 3.14159265358979f,
"this is tuple", 'i'}; "this is tuple", 'i'};
EXPECT_EQ("(42, 3.14159, \"this is tuple\", 'i')", fmt::format("{}", tu1)); EXPECT_EQ("(42, 3.14159, \"this is tuple\", 'i')", fmt::format("{}", tu1));
} }
struct my_struct { struct my_struct {
int32_t i; int32_t i;
std::string str; // can throw std::string str; // can throw
template <std::size_t N> template <std::size_t N> decltype(auto) get() const noexcept {
decltype(auto) get() const noexcept {
if constexpr (N == 0) if constexpr (N == 0)
return i; return i;
else if constexpr (N == 1) else if constexpr (N == 1)
@ -61,8 +60,7 @@ struct my_struct {
} }
}; };
template <std::size_t N> template <std::size_t N> decltype(auto) get(const my_struct& s) noexcept {
decltype(auto) get(const my_struct& s) noexcept {
return s.get<N>(); return s.get<N>();
} }
@ -71,8 +69,7 @@ namespace std {
template <> template <>
struct tuple_size<my_struct> : std::integral_constant<std::size_t, 2> {}; struct tuple_size<my_struct> : std::integral_constant<std::size_t, 2> {};
template <std::size_t N> template <std::size_t N> struct tuple_element<N, my_struct> {
struct tuple_element<N, my_struct> {
using type = decltype(std::declval<my_struct>().get<N>()); using type = decltype(std::declval<my_struct>().get<N>());
}; };

View File

@ -13,7 +13,7 @@
class assertion_failure : public std::logic_error { class assertion_failure : public std::logic_error {
public: public:
explicit assertion_failure(const char *message) : std::logic_error(message) {} explicit assertion_failure(const char* message) : std::logic_error(message) {}
}; };
#define FMT_ASSERT(condition, message) \ #define FMT_ASSERT(condition, message) \

View File

@ -9,23 +9,23 @@
#include "gtest.h" #include "gtest.h"
#ifdef _WIN32 #ifdef _WIN32
# include <windows.h> # include <windows.h>
#endif #endif
#ifdef _MSC_VER #ifdef _MSC_VER
# include <crtdbg.h> # include <crtdbg.h>
#else #else
# define _CrtSetReportFile(a, b) # define _CrtSetReportFile(a, b)
# define _CrtSetReportMode(a, b) # define _CrtSetReportMode(a, b)
#endif #endif
int main(int argc, char **argv) { int main(int argc, char** argv) {
#ifdef _WIN32 #ifdef _WIN32
// Don't display any error dialogs. This also suppresses message boxes // Don't display any error dialogs. This also suppresses message boxes
// on assertion failures in MinGW where _set_error_mode/CrtSetReportMode // on assertion failures in MinGW where _set_error_mode/CrtSetReportMode
// doesn't help. // doesn't help.
SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOGPFAULTERRORBOX | SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOGPFAULTERRORBOX |
SEM_NOOPENFILEERRORBOX); SEM_NOOPENFILEERRORBOX);
#endif #endif
// Disable message boxes on assertion failures. // Disable message boxes on assertion failures.
_CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_FILE | _CRTDBG_MODE_DEBUG); _CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_FILE | _CRTDBG_MODE_DEBUG);

View File

@ -6,17 +6,17 @@
// For the license information refer to format.h. // For the license information refer to format.h.
#ifdef WIN32 #ifdef WIN32
#define _CRT_SECURE_NO_WARNINGS # define _CRT_SECURE_NO_WARNINGS
#endif #endif
#include "gmock.h"
#include "fmt/locale.h"
#include "fmt/time.h" #include "fmt/time.h"
#include "fmt/locale.h"
#include "gmock.h"
TEST(TimeTest, Format) { TEST(TimeTest, Format) {
std::tm tm = std::tm(); std::tm tm = std::tm();
tm.tm_year = 116; tm.tm_year = 116;
tm.tm_mon = 3; tm.tm_mon = 3;
tm.tm_mday = 25; tm.tm_mday = 25;
EXPECT_EQ("The date is 2016-04-25.", EXPECT_EQ("The date is 2016-04-25.",
fmt::format("The date is {:%Y-%m-%d}.", tm)); fmt::format("The date is {:%Y-%m-%d}.", tm));
@ -24,8 +24,7 @@ TEST(TimeTest, Format) {
TEST(TimeTest, GrowBuffer) { TEST(TimeTest, GrowBuffer) {
std::string s = "{:"; std::string s = "{:";
for (int i = 0; i < 30; ++i) for (int i = 0; i < 30; ++i) s += "%c";
s += "%c";
s += "}\n"; s += "}\n";
std::time_t t = std::time(FMT_NULL); std::time_t t = std::time(FMT_NULL);
fmt::format(s, *std::localtime(&t)); fmt::format(s, *std::localtime(&t));
@ -39,19 +38,13 @@ TEST(TimeTest, FormatToEmptyContainer) {
EXPECT_EQ(s, "42"); EXPECT_EQ(s, "42");
} }
TEST(TimeTest, EmptyResult) { TEST(TimeTest, EmptyResult) { EXPECT_EQ("", fmt::format("{}", std::tm())); }
EXPECT_EQ("", fmt::format("{}", std::tm()));
}
static bool EqualTime(const std::tm &lhs, const std::tm &rhs) { static bool EqualTime(const std::tm& lhs, const std::tm& rhs) {
return lhs.tm_sec == rhs.tm_sec && return lhs.tm_sec == rhs.tm_sec && lhs.tm_min == rhs.tm_min &&
lhs.tm_min == rhs.tm_min && lhs.tm_hour == rhs.tm_hour && lhs.tm_mday == rhs.tm_mday &&
lhs.tm_hour == rhs.tm_hour && lhs.tm_mon == rhs.tm_mon && lhs.tm_year == rhs.tm_year &&
lhs.tm_mday == rhs.tm_mday && lhs.tm_wday == rhs.tm_wday && lhs.tm_yday == rhs.tm_yday &&
lhs.tm_mon == rhs.tm_mon &&
lhs.tm_year == rhs.tm_year &&
lhs.tm_wday == rhs.tm_wday &&
lhs.tm_yday == rhs.tm_yday &&
lhs.tm_isdst == rhs.tm_isdst; lhs.tm_isdst == rhs.tm_isdst;
} }

View File

@ -8,7 +8,7 @@
#include "util.h" #include "util.h"
#include <cstring> #include <cstring>
void increment(char *s) { void increment(char* s) {
for (int i = static_cast<int>(std::strlen(s)) - 1; i >= 0; --i) { for (int i = static_cast<int>(std::strlen(s)) - 1; i >= 0; --i) {
if (s[i] != '9') { if (s[i] != '9') {
++s[i]; ++s[i];
@ -30,15 +30,14 @@ std::string get_system_error(int error_code) {
#endif #endif
} }
const char *const FILE_CONTENT = "Don't panic!"; const char* const FILE_CONTENT = "Don't panic!";
fmt::buffered_file open_buffered_file(FILE **fp) { fmt::buffered_file open_buffered_file(FILE** fp) {
fmt::file read_end, write_end; fmt::file read_end, write_end;
fmt::file::pipe(read_end, write_end); fmt::file::pipe(read_end, write_end);
write_end.write(FILE_CONTENT, std::strlen(FILE_CONTENT)); write_end.write(FILE_CONTENT, std::strlen(FILE_CONTENT));
write_end.close(); write_end.close();
fmt::buffered_file f = read_end.fdopen("r"); fmt::buffered_file f = read_end.fdopen("r");
if (fp) if (fp) *fp = f.get();
*fp = f.get();
return f; return f;
} }

View File

@ -11,16 +11,16 @@
#include "fmt/posix.h" #include "fmt/posix.h"
enum {BUFFER_SIZE = 256}; enum { BUFFER_SIZE = 256 };
#ifdef _MSC_VER #ifdef _MSC_VER
# define FMT_VSNPRINTF vsprintf_s # define FMT_VSNPRINTF vsprintf_s
#else #else
# define FMT_VSNPRINTF vsnprintf # define FMT_VSNPRINTF vsnprintf
#endif #endif
template <std::size_t SIZE> template <std::size_t SIZE>
void safe_sprintf(char (&buffer)[SIZE], const char *format, ...) { void safe_sprintf(char (&buffer)[SIZE], const char* format, ...) {
std::va_list args; std::va_list args;
va_start(args, format); va_start(args, format);
FMT_VSNPRINTF(buffer, SIZE, format, args); FMT_VSNPRINTF(buffer, SIZE, format, args);
@ -28,19 +28,19 @@ void safe_sprintf(char (&buffer)[SIZE], const char *format, ...) {
} }
// Increment a number in a string. // Increment a number in a string.
void increment(char *s); void increment(char* s);
std::string get_system_error(int error_code); std::string get_system_error(int error_code);
extern const char *const FILE_CONTENT; extern const char* const FILE_CONTENT;
// Opens a buffered file for reading. // Opens a buffered file for reading.
fmt::buffered_file open_buffered_file(FILE **fp = FMT_NULL); fmt::buffered_file open_buffered_file(FILE** fp = FMT_NULL);
inline FILE *safe_fopen(const char *filename, const char *mode) { inline FILE* safe_fopen(const char* filename, const char* mode) {
#if defined(_WIN32) && !defined(__MINGW32__) #if defined(_WIN32) && !defined(__MINGW32__)
// Fix MSVC warning about "unsafe" fopen. // Fix MSVC warning about "unsafe" fopen.
FILE *f = 0; FILE* f = 0;
errno = fopen_s(&f, filename, mode); errno = fopen_s(&f, filename, mode);
return f; return f;
#else #else
@ -48,34 +48,33 @@ inline FILE *safe_fopen(const char *filename, const char *mode) {
#endif #endif
} }
template <typename Char> template <typename Char> class BasicTestString {
class BasicTestString {
private: private:
std::basic_string<Char> value_; std::basic_string<Char> value_;
static const Char EMPTY[]; static const Char EMPTY[];
public: public:
explicit BasicTestString(const Char *value = EMPTY) : value_(value) {} explicit BasicTestString(const Char* value = EMPTY) : value_(value) {}
const std::basic_string<Char> &value() const { return value_; } const std::basic_string<Char>& value() const { return value_; }
}; };
template <typename Char> template <typename Char> const Char BasicTestString<Char>::EMPTY[] = {0};
const Char BasicTestString<Char>::EMPTY[] = {0};
typedef BasicTestString<char> TestString; typedef BasicTestString<char> TestString;
typedef BasicTestString<wchar_t> TestWString; typedef BasicTestString<wchar_t> TestWString;
template <typename Char> template <typename Char>
std::basic_ostream<Char> &operator<<( std::basic_ostream<Char>& operator<<(std::basic_ostream<Char>& os,
std::basic_ostream<Char> &os, const BasicTestString<Char> &s) { const BasicTestString<Char>& s) {
os << s.value(); os << s.value();
return os; return os;
} }
class Date { class Date {
int year_, month_, day_; int year_, month_, day_;
public: public:
Date(int year, int month, int day) : year_(year), month_(month), day_(day) {} Date(int year, int month, int day) : year_(year), month_(month), day_(day) {}