Bump bundled fmt to version 6.1.0

This commit is contained in:
gabime 2019-12-04 23:39:17 +02:00
parent 1aa9ea92e2
commit 0db4b04ad3
12 changed files with 2389 additions and 1688 deletions

View File

@ -16,16 +16,291 @@
#include <locale> #include <locale>
#include <sstream> #include <sstream>
// enable safe chrono durations, unless explicitly disabled FMT_BEGIN_NAMESPACE
// Enable safe chrono durations, unless explicitly disabled.
#ifndef FMT_SAFE_DURATION_CAST #ifndef FMT_SAFE_DURATION_CAST
# define FMT_SAFE_DURATION_CAST 1 # define FMT_SAFE_DURATION_CAST 1
#endif #endif
#if FMT_SAFE_DURATION_CAST #if FMT_SAFE_DURATION_CAST
# include "safe-duration-cast.h"
#endif
FMT_BEGIN_NAMESPACE // For conversion between std::chrono::durations without undefined
// behaviour or erroneous results.
// This is a stripped down version of duration_cast, for inclusion in fmt.
// See https://github.com/pauldreik/safe_duration_cast
//
// Copyright Paul Dreik 2019
namespace safe_duration_cast {
template <typename To, typename From,
FMT_ENABLE_IF(!std::is_same<From, To>::value &&
std::numeric_limits<From>::is_signed ==
std::numeric_limits<To>::is_signed)>
FMT_CONSTEXPR To lossless_integral_conversion(const From from, int& ec) {
ec = 0;
using F = std::numeric_limits<From>;
using T = std::numeric_limits<To>;
static_assert(F::is_integer, "From must be integral");
static_assert(T::is_integer, "To must be integral");
// A and B are both signed, or both unsigned.
if (F::digits <= T::digits) {
// From fits in To without any problem.
} else {
// From does not always fit in To, resort to a dynamic check.
if (from < T::min() || from > T::max()) {
// outside range.
ec = 1;
return {};
}
}
return static_cast<To>(from);
}
/**
* converts From to To, without loss. If the dynamic value of from
* can't be converted to To without loss, ec is set.
*/
template <typename To, typename From,
FMT_ENABLE_IF(!std::is_same<From, To>::value &&
std::numeric_limits<From>::is_signed !=
std::numeric_limits<To>::is_signed)>
FMT_CONSTEXPR To lossless_integral_conversion(const From from, int& ec) {
ec = 0;
using F = std::numeric_limits<From>;
using T = std::numeric_limits<To>;
static_assert(F::is_integer, "From must be integral");
static_assert(T::is_integer, "To must be integral");
if (F::is_signed && !T::is_signed) {
// From may be negative, not allowed!
if (fmt::internal::is_negative(from)) {
ec = 1;
return {};
}
// From is positive. Can it always fit in To?
if (F::digits <= T::digits) {
// yes, From always fits in To.
} else {
// from may not fit in To, we have to do a dynamic check
if (from > static_cast<From>(T::max())) {
ec = 1;
return {};
}
}
}
if (!F::is_signed && T::is_signed) {
// can from be held in To?
if (F::digits < T::digits) {
// yes, From always fits in To.
} else {
// from may not fit in To, we have to do a dynamic check
if (from > static_cast<From>(T::max())) {
// outside range.
ec = 1;
return {};
}
}
}
// reaching here means all is ok for lossless conversion.
return static_cast<To>(from);
} // function
template <typename To, typename From,
FMT_ENABLE_IF(std::is_same<From, To>::value)>
FMT_CONSTEXPR To lossless_integral_conversion(const From from, int& ec) {
ec = 0;
return from;
} // function
// clang-format off
/**
* converts From to To if possible, otherwise ec is set.
*
* input | output
* ---------------------------------|---------------
* NaN | NaN
* Inf | Inf
* normal, fits in output | converted (possibly lossy)
* normal, does not fit in output | ec is set
* subnormal | best effort
* -Inf | -Inf
*/
// clang-format on
template <typename To, typename From,
FMT_ENABLE_IF(!std::is_same<From, To>::value)>
FMT_CONSTEXPR To safe_float_conversion(const From from, int& ec) {
ec = 0;
using T = std::numeric_limits<To>;
static_assert(std::is_floating_point<From>::value, "From must be floating");
static_assert(std::is_floating_point<To>::value, "To must be floating");
// catch the only happy case
if (std::isfinite(from)) {
if (from >= T::lowest() && from <= T::max()) {
return static_cast<To>(from);
}
// not within range.
ec = 1;
return {};
}
// nan and inf will be preserved
return static_cast<To>(from);
} // function
template <typename To, typename From,
FMT_ENABLE_IF(std::is_same<From, To>::value)>
FMT_CONSTEXPR To safe_float_conversion(const From from, int& ec) {
ec = 0;
static_assert(std::is_floating_point<From>::value, "From must be floating");
return from;
}
/**
* safe duration cast between integral durations
*/
template <typename To, typename FromRep, typename FromPeriod,
FMT_ENABLE_IF(std::is_integral<FromRep>::value),
FMT_ENABLE_IF(std::is_integral<typename To::rep>::value)>
To safe_duration_cast(std::chrono::duration<FromRep, FromPeriod> from,
int& ec) {
using From = std::chrono::duration<FromRep, FromPeriod>;
ec = 0;
// the basic idea is that we need to convert from count() in the from type
// to count() in the To type, by multiplying it with this:
struct Factor
: std::ratio_divide<typename From::period, typename To::period> {};
static_assert(Factor::num > 0, "num must be positive");
static_assert(Factor::den > 0, "den must be positive");
// the conversion is like this: multiply from.count() with Factor::num
// /Factor::den and convert it to To::rep, all this without
// overflow/underflow. let's start by finding a suitable type that can hold
// both To, From and Factor::num
using IntermediateRep =
typename std::common_type<typename From::rep, typename To::rep,
decltype(Factor::num)>::type;
// safe conversion to IntermediateRep
IntermediateRep count =
lossless_integral_conversion<IntermediateRep>(from.count(), ec);
if (ec) {
return {};
}
// multiply with Factor::num without overflow or underflow
if (Factor::num != 1) {
const auto max1 = internal::max_value<IntermediateRep>() / Factor::num;
if (count > max1) {
ec = 1;
return {};
}
const auto min1 = std::numeric_limits<IntermediateRep>::min() / Factor::num;
if (count < min1) {
ec = 1;
return {};
}
count *= Factor::num;
}
// this can't go wrong, right? den>0 is checked earlier.
if (Factor::den != 1) {
count /= Factor::den;
}
// convert to the to type, safely
using ToRep = typename To::rep;
const ToRep tocount = lossless_integral_conversion<ToRep>(count, ec);
if (ec) {
return {};
}
return To{tocount};
}
/**
* safe duration_cast between floating point durations
*/
template <typename To, typename FromRep, typename FromPeriod,
FMT_ENABLE_IF(std::is_floating_point<FromRep>::value),
FMT_ENABLE_IF(std::is_floating_point<typename To::rep>::value)>
To safe_duration_cast(std::chrono::duration<FromRep, FromPeriod> from,
int& ec) {
using From = std::chrono::duration<FromRep, FromPeriod>;
ec = 0;
if (std::isnan(from.count())) {
// nan in, gives nan out. easy.
return To{std::numeric_limits<typename To::rep>::quiet_NaN()};
}
// maybe we should also check if from is denormal, and decide what to do about
// it.
// +-inf should be preserved.
if (std::isinf(from.count())) {
return To{from.count()};
}
// the basic idea is that we need to convert from count() in the from type
// to count() in the To type, by multiplying it with this:
struct Factor
: std::ratio_divide<typename From::period, typename To::period> {};
static_assert(Factor::num > 0, "num must be positive");
static_assert(Factor::den > 0, "den must be positive");
// the conversion is like this: multiply from.count() with Factor::num
// /Factor::den and convert it to To::rep, all this without
// overflow/underflow. let's start by finding a suitable type that can hold
// both To, From and Factor::num
using IntermediateRep =
typename std::common_type<typename From::rep, typename To::rep,
decltype(Factor::num)>::type;
// force conversion of From::rep -> IntermediateRep to be safe,
// even if it will never happen be narrowing in this context.
IntermediateRep count =
safe_float_conversion<IntermediateRep>(from.count(), ec);
if (ec) {
return {};
}
// multiply with Factor::num without overflow or underflow
if (Factor::num != 1) {
constexpr auto max1 = internal::max_value<IntermediateRep>() /
static_cast<IntermediateRep>(Factor::num);
if (count > max1) {
ec = 1;
return {};
}
constexpr auto min1 = std::numeric_limits<IntermediateRep>::lowest() /
static_cast<IntermediateRep>(Factor::num);
if (count < min1) {
ec = 1;
return {};
}
count *= static_cast<IntermediateRep>(Factor::num);
}
// this can't go wrong, right? den>0 is checked earlier.
if (Factor::den != 1) {
using common_t = typename std::common_type<IntermediateRep, intmax_t>::type;
count /= static_cast<common_t>(Factor::den);
}
// convert to the to type, safely
using ToRep = typename To::rep;
const ToRep tocount = safe_float_conversion<ToRep>(count, ec);
if (ec) {
return {};
}
return To{tocount};
}
} // namespace safe_duration_cast
#endif
// Prevents expansion of a preceding token as a function-style macro. // Prevents expansion of a preceding token as a function-style macro.
// Usage: f FMT_NOMACRO() // Usage: f FMT_NOMACRO()
@ -403,7 +678,7 @@ inline bool isfinite(T value) {
return std::isfinite(value); return std::isfinite(value);
} }
// Convers value to int and checks that it's in the range [0, upper). // Converts value to int and checks that it's in the range [0, upper).
template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)> template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
inline int to_nonnegative_int(T value, int upper) { inline int to_nonnegative_int(T value, int upper) {
FMT_ASSERT(value >= 0 && value <= upper, "invalid value"); FMT_ASSERT(value >= 0 && value <= upper, "invalid value");
@ -582,8 +857,8 @@ struct chrono_formatter {
void write(Rep value, int width) { void write(Rep value, int width) {
write_sign(); write_sign();
if (isnan(value)) return write_nan(); if (isnan(value)) return write_nan();
uint32_or_64_t<int> n = to_unsigned( uint32_or_64_or_128_t<int> n =
to_nonnegative_int(value, (std::numeric_limits<int>::max)())); to_unsigned(to_nonnegative_int(value, max_value<int>()));
int num_digits = internal::count_digits(n); int num_digits = internal::count_digits(n);
if (width > num_digits) out = std::fill_n(out, width - num_digits, '0'); if (width > num_digits) 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);
@ -728,7 +1003,7 @@ struct formatter<std::chrono::duration<Rep, Period>, Char> {
struct spec_handler { struct spec_handler {
formatter& f; formatter& f;
basic_parse_context<Char>& context; basic_format_parse_context<Char>& context;
basic_string_view<Char> format_str; basic_string_view<Char> format_str;
template <typename Id> FMT_CONSTEXPR arg_ref_type make_arg_ref(Id arg_id) { template <typename Id> FMT_CONSTEXPR arg_ref_type make_arg_ref(Id arg_id) {
@ -738,8 +1013,7 @@ struct formatter<std::chrono::duration<Rep, Period>, Char> {
FMT_CONSTEXPR arg_ref_type make_arg_ref(basic_string_view<Char> arg_id) { FMT_CONSTEXPR arg_ref_type make_arg_ref(basic_string_view<Char> arg_id) {
context.check_arg_id(arg_id); context.check_arg_id(arg_id);
const auto str_val = internal::string_view_metadata(format_str, arg_id); return arg_ref_type(arg_id);
return arg_ref_type(str_val);
} }
FMT_CONSTEXPR arg_ref_type make_arg_ref(internal::auto_id) { FMT_CONSTEXPR arg_ref_type make_arg_ref(internal::auto_id) {
@ -750,7 +1024,7 @@ struct formatter<std::chrono::duration<Rep, Period>, Char> {
void on_fill(Char fill) { f.specs.fill[0] = fill; } void on_fill(Char fill) { f.specs.fill[0] = fill; }
void on_align(align_t align) { f.specs.align = align; } void on_align(align_t align) { f.specs.align = align; }
void on_width(unsigned width) { f.specs.width = width; } void on_width(unsigned width) { f.specs.width = width; }
void on_precision(unsigned precision) { f.precision = precision; } void on_precision(unsigned _precision) { f.precision = _precision; }
void end_precision() {} void end_precision() {}
template <typename Id> void on_dynamic_width(Id arg_id) { template <typename Id> void on_dynamic_width(Id arg_id) {
@ -762,13 +1036,13 @@ struct formatter<std::chrono::duration<Rep, Period>, Char> {
} }
}; };
using iterator = typename basic_parse_context<Char>::iterator; using iterator = typename basic_format_parse_context<Char>::iterator;
struct parse_range { struct parse_range {
iterator begin; iterator begin;
iterator end; iterator end;
}; };
FMT_CONSTEXPR parse_range do_parse(basic_parse_context<Char>& ctx) { FMT_CONSTEXPR parse_range do_parse(basic_format_parse_context<Char>& ctx) {
auto begin = ctx.begin(), end = ctx.end(); auto begin = ctx.begin(), end = ctx.end();
if (begin == end || *begin == '}') return {begin, begin}; if (begin == end || *begin == '}') return {begin, begin};
spec_handler handler{*this, ctx, format_str}; spec_handler handler{*this, ctx, format_str};
@ -789,7 +1063,7 @@ struct formatter<std::chrono::duration<Rep, Period>, Char> {
public: public:
formatter() : precision(-1) {} formatter() : precision(-1) {}
FMT_CONSTEXPR auto parse(basic_parse_context<Char>& ctx) FMT_CONSTEXPR auto parse(basic_format_parse_context<Char>& ctx)
-> decltype(ctx.begin()) { -> decltype(ctx.begin()) {
auto range = do_parse(ctx); auto range = do_parse(ctx);
format_str = basic_string_view<Char>( format_str = basic_string_view<Char>(
@ -806,10 +1080,10 @@ struct formatter<std::chrono::duration<Rep, Period>, Char> {
auto out = std::back_inserter(buf); auto out = std::back_inserter(buf);
using range = internal::output_range<decltype(ctx.out()), Char>; using range = internal::output_range<decltype(ctx.out()), Char>;
internal::basic_writer<range> w(range(ctx.out())); internal::basic_writer<range> w(range(ctx.out()));
internal::handle_dynamic_spec<internal::width_checker>( internal::handle_dynamic_spec<internal::width_checker>(specs.width,
specs.width, width_ref, ctx, format_str.begin()); width_ref, ctx);
internal::handle_dynamic_spec<internal::precision_checker>( internal::handle_dynamic_spec<internal::precision_checker>(
precision, precision_ref, ctx, format_str.begin()); precision, precision_ref, ctx);
if (begin == end || *begin == '}') { if (begin == end || *begin == '}') {
out = internal::format_chrono_duration_value(out, d.count(), precision); out = internal::format_chrono_duration_value(out, d.count(), precision);
internal::format_chrono_duration_unit<Period>(out); internal::format_chrono_duration_unit<Period>(out);

View File

@ -299,15 +299,15 @@ class text_style {
return static_cast<uint8_t>(ems) != 0; return static_cast<uint8_t>(ems) != 0;
} }
FMT_CONSTEXPR internal::color_type get_foreground() const FMT_NOEXCEPT { FMT_CONSTEXPR internal::color_type get_foreground() const FMT_NOEXCEPT {
assert(has_foreground() && "no foreground specified for this style"); FMT_ASSERT(has_foreground(), "no foreground specified for this style");
return foreground_color; return foreground_color;
} }
FMT_CONSTEXPR internal::color_type get_background() const FMT_NOEXCEPT { FMT_CONSTEXPR internal::color_type get_background() const FMT_NOEXCEPT {
assert(has_background() && "no background specified for this style"); FMT_ASSERT(has_background(), "no background specified for this style");
return background_color; return background_color;
} }
FMT_CONSTEXPR emphasis get_emphasis() const FMT_NOEXCEPT { FMT_CONSTEXPR emphasis get_emphasis() const FMT_NOEXCEPT {
assert(has_emphasis() && "no emphasis specified for this style"); FMT_ASSERT(has_emphasis(), "no emphasis specified for this style");
return ems; return ems;
} }
@ -470,58 +470,41 @@ inline void reset_color(basic_memory_buffer<Char>& buffer) FMT_NOEXCEPT {
} }
template <typename Char> template <typename Char>
std::basic_string<Char> vformat(const text_style& ts, void vformat_to(basic_memory_buffer<Char>& buf, const text_style& ts,
basic_string_view<Char> format_str, basic_string_view<Char> format_str,
basic_format_args<buffer_context<Char> > args) { basic_format_args<buffer_context<Char>> args) {
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 = make_emphasis<Char>(ts.get_emphasis()); auto emphasis = internal::make_emphasis<Char>(ts.get_emphasis());
buffer.append(escape.begin(), escape.end()); buf.append(emphasis.begin(), emphasis.end());
} }
if (ts.has_foreground()) { if (ts.has_foreground()) {
has_style = true; has_style = true;
ansi_color_escape<Char> escape = auto foreground =
make_foreground_color<Char>(ts.get_foreground()); internal::make_foreground_color<Char>(ts.get_foreground());
buffer.append(escape.begin(), escape.end()); buf.append(foreground.begin(), foreground.end());
} }
if (ts.has_background()) { if (ts.has_background()) {
has_style = true; has_style = true;
ansi_color_escape<Char> escape = auto background =
make_background_color<Char>(ts.get_background()); internal::make_background_color<Char>(ts.get_background());
buffer.append(escape.begin(), escape.end()); buf.append(background.begin(), background.end());
} }
internal::vformat_to(buffer, format_str, args); vformat_to(buf, format_str, args);
if (has_style) { if (has_style) {
reset_color<Char>(buffer); internal::reset_color<Char>(buf);
} }
return fmt::to_string(buffer);
} }
} // namespace internal } // namespace internal
template <typename S, typename Char = char_t<S> > template <typename S, typename Char = char_t<S>>
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<buffer_context<Char> > args) { basic_format_args<buffer_context<Char>> args) {
bool has_style = false; basic_memory_buffer<Char> buf;
if (ts.has_emphasis()) { internal::vformat_to(buf, ts, to_string_view(format), args);
has_style = true; buf.push_back(Char(0));
internal::fputs<Char>(internal::make_emphasis<Char>(ts.get_emphasis()), f); internal::fputs(buf.data(), f);
}
if (ts.has_foreground()) {
has_style = true;
internal::fputs<Char>(
internal::make_foreground_color<Char>(ts.get_foreground()), f);
}
if (ts.has_background()) {
has_style = true;
internal::fputs<Char>(
internal::make_background_color<Char>(ts.get_background()), f);
}
vprint(f, format, args);
if (has_style) {
internal::reset_color<Char>(f);
}
} }
/** /**
@ -536,7 +519,7 @@ template <typename S, typename... Args,
void print(std::FILE* f, const text_style& ts, const S& format_str, void print(std::FILE* f, const text_style& ts, const S& format_str,
const Args&... args) { const Args&... args) {
internal::check_format_string<Args...>(format_str); internal::check_format_string<Args...>(format_str);
using context = buffer_context<char_t<S> >; using context = buffer_context<char_t<S>>;
format_arg_store<context, Args...> as{args...}; format_arg_store<context, Args...> as{args...};
vprint(f, ts, format_str, basic_format_args<context>(as)); vprint(f, ts, format_str, basic_format_args<context>(as));
} }
@ -554,11 +537,13 @@ void print(const text_style& ts, const S& format_str, const Args&... args) {
return print(stdout, ts, format_str, args...); return print(stdout, ts, format_str, args...);
} }
template <typename S, typename Char = char_t<S> > template <typename S, typename Char = char_t<S>>
inline std::basic_string<Char> vformat( inline std::basic_string<Char> vformat(
const text_style& ts, const S& format_str, const text_style& ts, const S& format_str,
basic_format_args<buffer_context<Char> > args) { basic_format_args<buffer_context<Char>> args) {
return internal::vformat(ts, to_string_view(format_str), args); basic_memory_buffer<Char> buf;
internal::vformat_to(buf, ts, to_string_view(format_str), args);
return fmt::to_string(buf);
} }
/** /**
@ -573,11 +558,11 @@ inline std::basic_string<Char> vformat(
"The answer is {}", 42); "The answer is {}", 42);
\endrst \endrst
*/ */
template <typename S, typename... Args, typename Char = char_t<S> > template <typename S, typename... Args, typename Char = char_t<S>>
inline std::basic_string<Char> format(const text_style& ts, const S& format_str, inline std::basic_string<Char> format(const text_style& ts, const S& format_str,
const Args&... args) { const Args&... args) {
return internal::vformat(ts, to_string_view(format_str), return vformat(ts, to_string_view(format_str),
{internal::make_args_checked(format_str, args...)}); {internal::make_args_checked<Args...>(format_str, args...)});
} }
FMT_END_NAMESPACE FMT_END_NAMESPACE

View File

@ -14,250 +14,44 @@
FMT_BEGIN_NAMESPACE FMT_BEGIN_NAMESPACE
namespace internal { namespace internal {
// Part of a compiled format string. It can be either literal text or a
// replacement field.
template <typename Char> struct format_part { template <typename Char> struct format_part {
public: enum class kind { arg_index, arg_name, text, replacement };
struct named_argument_id {
FMT_CONSTEXPR named_argument_id(internal::string_view_metadata id) struct replacement {
: id(id) {} arg_ref<Char> arg_id;
internal::string_view_metadata id; dynamic_format_specs<Char> specs;
}; };
struct argument_id { kind part_kind;
FMT_CONSTEXPR argument_id() : argument_id(0u) {}
FMT_CONSTEXPR argument_id(unsigned id)
: which(which_arg_id::index), val(id) {}
FMT_CONSTEXPR argument_id(internal::string_view_metadata id)
: which(which_arg_id::named_index), val(id) {}
enum class which_arg_id { index, named_index };
which_arg_id which;
union value {
FMT_CONSTEXPR value() : index(0u) {}
FMT_CONSTEXPR value(unsigned id) : index(id) {}
FMT_CONSTEXPR value(internal::string_view_metadata id)
: named_index(id) {}
unsigned index;
internal::string_view_metadata named_index;
} val;
};
struct specification {
FMT_CONSTEXPR specification() : arg_id(0u) {}
FMT_CONSTEXPR specification(unsigned id) : arg_id(id) {}
FMT_CONSTEXPR specification(internal::string_view_metadata id)
: arg_id(id) {}
argument_id arg_id;
internal::dynamic_format_specs<Char> parsed_specs;
};
FMT_CONSTEXPR format_part()
: which(kind::argument_id), end_of_argument_id(0u), val(0u) {}
FMT_CONSTEXPR format_part(internal::string_view_metadata text)
: which(kind::text), end_of_argument_id(0u), val(text) {}
FMT_CONSTEXPR format_part(unsigned id)
: which(kind::argument_id), end_of_argument_id(0u), val(id) {}
FMT_CONSTEXPR format_part(named_argument_id arg_id)
: which(kind::named_argument_id), end_of_argument_id(0u), val(arg_id) {}
FMT_CONSTEXPR format_part(specification spec)
: which(kind::specification), end_of_argument_id(0u), val(spec) {}
enum class kind { argument_id, named_argument_id, text, specification };
kind which;
std::size_t end_of_argument_id;
union value { union value {
FMT_CONSTEXPR value() : arg_id(0u) {} unsigned arg_index;
FMT_CONSTEXPR value(unsigned id) : arg_id(id) {} basic_string_view<Char> str;
FMT_CONSTEXPR value(named_argument_id named_id) replacement repl;
: named_arg_id(named_id.id) {}
FMT_CONSTEXPR value(internal::string_view_metadata t) : text(t) {} FMT_CONSTEXPR value(unsigned index = 0) : arg_index(index) {}
FMT_CONSTEXPR value(specification s) : spec(s) {} FMT_CONSTEXPR value(basic_string_view<Char> s) : str(s) {}
unsigned arg_id; FMT_CONSTEXPR value(replacement r) : repl(r) {}
internal::string_view_metadata named_arg_id;
internal::string_view_metadata text;
specification spec;
} val; } val;
}; // Position past the end of the argument id.
const Char* arg_id_end = nullptr;
template <typename Char, typename PartsContainer> FMT_CONSTEXPR format_part(kind k = kind::arg_index, value v = {})
class format_preparation_handler : public internal::error_handler { : part_kind(k), val(v) {}
private:
using part = format_part<Char>;
public: static FMT_CONSTEXPR format_part make_arg_index(unsigned index) {
using iterator = typename basic_string_view<Char>::iterator; return format_part(kind::arg_index, index);
FMT_CONSTEXPR format_preparation_handler(basic_string_view<Char> format,
PartsContainer& parts)
: parts_(parts), format_(format), parse_context_(format) {}
FMT_CONSTEXPR void on_text(const Char* begin, const Char* end) {
if (begin == end) return;
const auto offset = begin - format_.data();
const auto size = end - begin;
parts_.push_back(part(string_view_metadata(offset, size)));
} }
static FMT_CONSTEXPR format_part make_arg_name(basic_string_view<Char> name) {
FMT_CONSTEXPR void on_arg_id() { return format_part(kind::arg_name, name);
parts_.push_back(part(parse_context_.next_arg_id()));
} }
static FMT_CONSTEXPR format_part make_text(basic_string_view<Char> text) {
FMT_CONSTEXPR void on_arg_id(unsigned id) { return format_part(kind::text, text);
parse_context_.check_arg_id(id);
parts_.push_back(part(id));
} }
static FMT_CONSTEXPR format_part make_replacement(replacement repl) {
FMT_CONSTEXPR void on_arg_id(basic_string_view<Char> id) { return format_part(kind::replacement, repl);
const auto view = string_view_metadata(format_, id);
const auto arg_id = typename part::named_argument_id(view);
parts_.push_back(part(arg_id));
} }
FMT_CONSTEXPR void on_replacement_field(const Char* ptr) {
parts_.back().end_of_argument_id = ptr - format_.begin();
}
FMT_CONSTEXPR const Char* on_format_specs(const Char* begin,
const Char* end) {
const auto specs_offset = to_unsigned(begin - format_.begin());
using parse_context = basic_parse_context<Char>;
internal::dynamic_format_specs<Char> parsed_specs;
dynamic_specs_handler<parse_context> handler(parsed_specs, parse_context_);
begin = parse_format_specs(begin, end, handler);
if (*begin != '}') on_error("missing '}' in format string");
auto& last_part = parts_.back();
auto specs = last_part.which == part::kind::argument_id
? typename part::specification(last_part.val.arg_id)
: typename part::specification(last_part.val.named_arg_id);
specs.parsed_specs = parsed_specs;
last_part = part(specs);
last_part.end_of_argument_id = specs_offset;
return begin;
}
private:
PartsContainer& parts_;
basic_string_view<Char> format_;
basic_parse_context<Char> parse_context_;
};
template <typename Format, typename PreparedPartsProvider, typename... Args>
class prepared_format {
public:
using char_type = char_t<Format>;
using format_part_t = format_part<char_type>;
constexpr prepared_format(Format f)
: format_(std::move(f)), parts_provider_(to_string_view(format_)) {}
prepared_format() = delete;
using context = buffer_context<char_type>;
template <typename Range, typename Context>
auto vformat_to(Range out, basic_format_args<Context> args) const ->
typename Context::iterator {
const auto format_view = internal::to_string_view(format_);
basic_parse_context<char_type> parse_ctx(format_view);
Context ctx(out.begin(), args);
const auto& parts = parts_provider_.parts();
for (auto part_it = parts.begin(); part_it != parts.end(); ++part_it) {
const auto& part = *part_it;
const auto& value = part.val;
switch (part.which) {
case format_part_t::kind::text: {
const auto text = value.text.to_view(format_view.data());
auto output = ctx.out();
auto&& it = internal::reserve(output, text.size());
it = std::copy_n(text.begin(), text.size(), it);
ctx.advance_to(output);
} break;
case format_part_t::kind::argument_id: {
advance_parse_context_to_specification(parse_ctx, part);
format_arg<Range>(parse_ctx, ctx, value.arg_id);
} break;
case format_part_t::kind::named_argument_id: {
advance_parse_context_to_specification(parse_ctx, part);
const auto named_arg_id =
value.named_arg_id.to_view(format_view.data());
format_arg<Range>(parse_ctx, ctx, named_arg_id);
} break;
case format_part_t::kind::specification: {
const auto& arg_id_value = value.spec.arg_id.val;
const auto arg = value.spec.arg_id.which ==
format_part_t::argument_id::which_arg_id::index
? ctx.arg(arg_id_value.index)
: ctx.arg(arg_id_value.named_index.to_view(
to_string_view(format_).data()));
auto specs = value.spec.parsed_specs;
handle_dynamic_spec<internal::width_checker>(
specs.width, specs.width_ref, ctx, format_view.begin());
handle_dynamic_spec<internal::precision_checker>(
specs.precision, specs.precision_ref, ctx, format_view.begin());
check_prepared_specs(specs, arg.type());
advance_parse_context_to_specification(parse_ctx, part);
ctx.advance_to(
visit_format_arg(arg_formatter<Range>(ctx, nullptr, &specs), arg));
} break;
}
}
return ctx.out();
}
private:
void advance_parse_context_to_specification(
basic_parse_context<char_type>& parse_ctx,
const format_part_t& part) const {
const auto view = to_string_view(format_);
const auto specification_begin = view.data() + part.end_of_argument_id;
advance_to(parse_ctx, specification_begin);
}
template <typename Range, typename Context, typename Id>
void format_arg(basic_parse_context<char_type>& parse_ctx, Context& ctx,
Id arg_id) const {
parse_ctx.check_arg_id(arg_id);
const auto stopped_at =
visit_format_arg(arg_formatter<Range>(ctx), ctx.arg(arg_id));
ctx.advance_to(stopped_at);
}
template <typename Char>
void check_prepared_specs(const basic_format_specs<Char>& specs,
internal::type arg_type) const {
internal::error_handler h;
numeric_specs_checker<internal::error_handler> checker(h, arg_type);
if (specs.align == align::numeric) checker.require_numeric_argument();
if (specs.sign != sign::none) checker.check_sign();
if (specs.alt) checker.require_numeric_argument();
if (specs.precision >= 0) checker.check_precision();
}
private:
Format format_;
PreparedPartsProvider parts_provider_;
}; };
template <typename Char> struct part_counter { template <typename Char> struct part_counter {
@ -276,13 +70,13 @@ template <typename Char> struct part_counter {
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) {
// Find the matching brace. // Find the matching brace.
unsigned braces_counter = 0; unsigned brace_counter = 0;
for (; begin != end; ++begin) { for (; begin != end; ++begin) {
if (*begin == '{') { if (*begin == '{') {
++braces_counter; ++brace_counter;
} else if (*begin == '}') { } else if (*begin == '}') {
if (braces_counter == 0u) break; if (brace_counter == 0u) break;
--braces_counter; --brace_counter;
} }
} }
return begin; return begin;
@ -291,156 +85,486 @@ template <typename Char> struct part_counter {
FMT_CONSTEXPR void on_error(const char*) {} FMT_CONSTEXPR void on_error(const char*) {}
}; };
template <typename Format> class compiletime_prepared_parts_type_provider { // Counts the number of parts in a format string.
private: template <typename Char>
using char_type = char_t<Format>; FMT_CONSTEXPR unsigned count_parts(basic_string_view<Char> format_str) {
part_counter<Char> counter;
parse_format_string<true>(format_str, counter);
return counter.num_parts;
}
static FMT_CONSTEXPR unsigned count_parts() { template <typename Char, typename PartHandler>
FMT_CONSTEXPR_DECL const auto text = to_string_view(Format{}); class format_string_compiler : public error_handler {
part_counter<char_type> counter; private:
internal::parse_format_string</*IS_CONSTEXPR=*/true>(text, counter); using part = format_part<Char>;
return counter.num_parts;
PartHandler handler_;
part part_;
basic_string_view<Char> format_str_;
basic_format_parse_context<Char> parse_context_;
public:
FMT_CONSTEXPR format_string_compiler(basic_string_view<Char> format_str,
PartHandler handler)
: handler_(handler),
format_str_(format_str),
parse_context_(format_str) {}
FMT_CONSTEXPR void on_text(const Char* begin, const Char* end) {
if (begin != end)
handler_(part::make_text({begin, to_unsigned(end - begin)}));
} }
// Workaround for old compilers. Compiletime parts preparation will not be FMT_CONSTEXPR void on_arg_id() {
// performed with them anyway. part_ = part::make_arg_index(parse_context_.next_arg_id());
}
FMT_CONSTEXPR void on_arg_id(unsigned id) {
parse_context_.check_arg_id(id);
part_ = part::make_arg_index(id);
}
FMT_CONSTEXPR void on_arg_id(basic_string_view<Char> id) {
part_ = part::make_arg_name(id);
}
FMT_CONSTEXPR void on_replacement_field(const Char* ptr) {
part_.arg_id_end = ptr;
handler_(part_);
}
FMT_CONSTEXPR const Char* on_format_specs(const Char* begin,
const Char* end) {
auto repl = typename part::replacement();
dynamic_specs_handler<basic_format_parse_context<Char>> handler(
repl.specs, parse_context_);
auto it = parse_format_specs(begin, end, handler);
if (*it != '}') on_error("missing '}' in format string");
repl.arg_id = part_.part_kind == part::kind::arg_index
? arg_ref<Char>(part_.val.arg_index)
: arg_ref<Char>(part_.val.str);
auto part = part::make_replacement(repl);
part.arg_id_end = begin;
handler_(part);
return it;
}
};
// Compiles a format string and invokes handler(part) for each parsed part.
template <bool IS_CONSTEXPR, typename Char, typename PartHandler>
FMT_CONSTEXPR void compile_format_string(basic_string_view<Char> format_str,
PartHandler handler) {
parse_format_string<IS_CONSTEXPR>(
format_str,
format_string_compiler<Char, PartHandler>(format_str, handler));
}
template <typename Range, typename Context, typename Id>
void format_arg(
basic_format_parse_context<typename Range::value_type>& parse_ctx,
Context& ctx, Id arg_id) {
ctx.advance_to(
visit_format_arg(arg_formatter<Range>(ctx, &parse_ctx), ctx.arg(arg_id)));
}
// vformat_to is defined in a subnamespace to prevent ADL.
namespace cf {
template <typename Context, typename Range, typename CompiledFormat>
auto vformat_to(Range out, CompiledFormat& cf, basic_format_args<Context> args)
-> typename Context::iterator {
using char_type = typename Context::char_type;
basic_format_parse_context<char_type> parse_ctx(
to_string_view(cf.format_str_));
Context ctx(out.begin(), args);
const auto& parts = cf.parts();
for (auto part_it = std::begin(parts); part_it != std::end(parts);
++part_it) {
const auto& part = *part_it;
const auto& value = part.val;
using format_part_t = format_part<char_type>;
switch (part.part_kind) {
case format_part_t::kind::text: {
const auto text = value.str;
auto output = ctx.out();
auto&& it = reserve(output, text.size());
it = std::copy_n(text.begin(), text.size(), it);
ctx.advance_to(output);
break;
}
case format_part_t::kind::arg_index:
advance_to(parse_ctx, part.arg_id_end);
internal::format_arg<Range>(parse_ctx, ctx, value.arg_index);
break;
case format_part_t::kind::arg_name:
advance_to(parse_ctx, part.arg_id_end);
internal::format_arg<Range>(parse_ctx, ctx, value.str);
break;
case format_part_t::kind::replacement: {
const auto& arg_id_value = value.repl.arg_id.val;
const auto arg = value.repl.arg_id.kind == arg_id_kind::index
? ctx.arg(arg_id_value.index)
: ctx.arg(arg_id_value.name);
auto specs = value.repl.specs;
handle_dynamic_spec<width_checker>(specs.width, specs.width_ref, ctx);
handle_dynamic_spec<precision_checker>(specs.precision,
specs.precision_ref, ctx);
error_handler h;
numeric_specs_checker<error_handler> checker(h, arg.type());
if (specs.align == align::numeric) checker.require_numeric_argument();
if (specs.sign != sign::none) checker.check_sign();
if (specs.alt) checker.require_numeric_argument();
if (specs.precision >= 0) checker.check_precision();
advance_to(parse_ctx, part.arg_id_end);
ctx.advance_to(
visit_format_arg(arg_formatter<Range>(ctx, nullptr, &specs), arg));
break;
}
}
}
return ctx.out();
}
} // namespace cf
struct basic_compiled_format {};
template <typename S, typename = void>
struct compiled_format_base : basic_compiled_format {
using char_type = char_t<S>;
using parts_container = std::vector<internal::format_part<char_type>>;
parts_container compiled_parts;
explicit compiled_format_base(basic_string_view<char_type> format_str) {
compile_format_string<false>(format_str,
[this](const format_part<char_type>& part) {
compiled_parts.push_back(part);
});
}
const parts_container& parts() const { return compiled_parts; }
};
template <typename Char, unsigned N> struct format_part_array {
format_part<Char> data[N] = {};
FMT_CONSTEXPR format_part_array() = default;
};
template <typename Char, unsigned N>
FMT_CONSTEXPR format_part_array<Char, N> compile_to_parts(
basic_string_view<Char> format_str) {
format_part_array<Char, N> parts;
unsigned counter = 0;
// This is not a lambda for compatibility with older compilers.
struct {
format_part<Char>* parts;
unsigned* counter;
FMT_CONSTEXPR void operator()(const format_part<Char>& part) {
parts[(*counter)++] = part;
}
} collector{parts.data, &counter};
compile_format_string<true>(format_str, collector);
if (counter < N) {
parts.data[counter] =
format_part<Char>::make_text(basic_string_view<Char>());
}
return parts;
}
template <typename T> constexpr const T& constexpr_max(const T& a, const T& b) {
return (a < b) ? b : a;
}
template <typename S>
struct compiled_format_base<S, enable_if_t<is_compile_string<S>::value>>
: basic_compiled_format {
using char_type = char_t<S>;
FMT_CONSTEXPR explicit compiled_format_base(basic_string_view<char_type>) {}
// Workaround for old compilers. Format string compilation will not be
// performed there anyway.
#if FMT_USE_CONSTEXPR #if FMT_USE_CONSTEXPR
static FMT_CONSTEXPR_DECL const unsigned number_of_format_parts = static FMT_CONSTEXPR_DECL const unsigned num_format_parts =
compiletime_prepared_parts_type_provider::count_parts(); constexpr_max(count_parts(to_string_view(S())), 1u);
#else #else
static const unsigned number_of_format_parts = 0u; static const unsigned num_format_parts = 1;
#endif #endif
public: using parts_container = format_part<char_type>[num_format_parts];
template <unsigned N> struct format_parts_array {
using value_type = format_part<char_type>;
FMT_CONSTEXPR format_parts_array() : arr{} {} const parts_container& parts() const {
static FMT_CONSTEXPR_DECL const auto compiled_parts =
FMT_CONSTEXPR value_type& operator[](unsigned ind) { return arr[ind]; } compile_to_parts<char_type, num_format_parts>(
internal::to_string_view(S()));
FMT_CONSTEXPR const value_type* begin() const { return arr; } return compiled_parts.data;
FMT_CONSTEXPR const value_type* end() const { return begin() + N; }
private:
value_type arr[N];
};
struct empty {
// Parts preparator will search for it
using value_type = format_part<char_type>;
};
using type = conditional_t<number_of_format_parts != 0,
format_parts_array<number_of_format_parts>, empty>;
};
template <typename Parts> class compiletime_prepared_parts_collector {
private:
using format_part = typename Parts::value_type;
public:
FMT_CONSTEXPR explicit compiletime_prepared_parts_collector(Parts& parts)
: parts_{parts}, counter_{0u} {}
FMT_CONSTEXPR void push_back(format_part part) { parts_[counter_++] = part; }
FMT_CONSTEXPR format_part& back() { return parts_[counter_ - 1]; }
private:
Parts& parts_;
unsigned counter_;
};
template <typename PartsContainer, typename Char>
FMT_CONSTEXPR PartsContainer prepare_parts(basic_string_view<Char> format) {
PartsContainer parts;
internal::parse_format_string</*IS_CONSTEXPR=*/false>(
format, format_preparation_handler<Char, PartsContainer>(format, parts));
return parts;
}
template <typename PartsContainer, typename Char>
FMT_CONSTEXPR PartsContainer
prepare_compiletime_parts(basic_string_view<Char> format) {
using collector = compiletime_prepared_parts_collector<PartsContainer>;
PartsContainer parts;
collector c(parts);
internal::parse_format_string</*IS_CONSTEXPR=*/true>(
format, format_preparation_handler<Char, collector>(format, c));
return parts;
}
template <typename PartsContainer> class runtime_parts_provider {
public:
runtime_parts_provider() = delete;
template <typename Char>
runtime_parts_provider(basic_string_view<Char> format)
: parts_(prepare_parts<PartsContainer>(format)) {}
const PartsContainer& parts() const { return parts_; }
private:
PartsContainer parts_;
};
template <typename Format, typename PartsContainer>
struct compiletime_parts_provider {
compiletime_parts_provider() = delete;
template <typename Char>
FMT_CONSTEXPR compiletime_parts_provider(basic_string_view<Char>) {}
const PartsContainer& parts() const {
static FMT_CONSTEXPR_DECL const PartsContainer prepared_parts =
prepare_compiletime_parts<PartsContainer>(
internal::to_string_view(Format{}));
return prepared_parts;
} }
}; };
template <typename S, typename... Args>
class compiled_format : private compiled_format_base<S> {
public:
using typename compiled_format_base<S>::char_type;
private:
basic_string_view<char_type> format_str_;
template <typename Context, typename Range, typename CompiledFormat>
friend auto cf::vformat_to(Range out, CompiledFormat& cf,
basic_format_args<Context> args) ->
typename Context::iterator;
public:
compiled_format() = delete;
explicit constexpr compiled_format(basic_string_view<char_type> format_str)
: compiled_format_base<S>(format_str), format_str_(format_str) {}
};
#ifdef __cpp_if_constexpr
template <typename... Args> struct type_list {};
// Returns a reference to the argument at index N from [first, rest...].
template <int N, typename T, typename... Args>
constexpr const auto& get(const T& first, const Args&... rest) {
static_assert(N < 1 + sizeof...(Args), "index is out of bounds");
if constexpr (N == 0)
return first;
else
return get<N - 1>(rest...);
}
template <int N, typename> struct get_type_impl;
template <int N, typename... Args> struct get_type_impl<N, type_list<Args...>> {
using type = remove_cvref_t<decltype(get<N>(std::declval<Args>()...))>;
};
template <int N, typename T>
using get_type = typename get_type_impl<N, T>::type;
template <typename Char> struct text {
basic_string_view<Char> data;
using char_type = Char;
template <typename OutputIt, typename... Args>
OutputIt format(OutputIt out, const Args&...) const {
// TODO: reserve
return copy_str<Char>(data.begin(), data.end(), out);
}
};
template <typename Char>
constexpr text<Char> make_text(basic_string_view<Char> s, size_t pos,
size_t size) {
return {{&s[pos], size}};
}
template <typename Char, typename OutputIt, typename T,
std::enable_if_t<std::is_integral_v<T>, int> = 0>
OutputIt format_default(OutputIt out, T value) {
// TODO: reserve
format_int fi(value);
return std::copy(fi.data(), fi.data() + fi.size(), out);
}
template <typename Char, typename OutputIt>
OutputIt format_default(OutputIt out, double value) {
writer w(out);
w.write(value);
return w.out();
}
template <typename Char, typename OutputIt>
OutputIt format_default(OutputIt out, Char value) {
*out++ = value;
return out;
}
template <typename Char, typename OutputIt>
OutputIt format_default(OutputIt out, const Char* value) {
auto length = std::char_traits<Char>::length(value);
return copy_str<Char>(value, value + length, out);
}
// A replacement field that refers to argument N.
template <typename Char, typename T, int N> struct field {
using char_type = Char;
template <typename OutputIt, typename... Args>
OutputIt format(OutputIt out, const Args&... args) const {
// This ensures that the argument type is convertile to `const T&`.
const T& arg = get<N>(args...);
return format_default<Char>(out, arg);
}
};
template <typename L, typename R> struct concat {
L lhs;
R rhs;
using char_type = typename L::char_type;
template <typename OutputIt, typename... Args>
OutputIt format(OutputIt out, const Args&... args) const {
out = lhs.format(out, args...);
return rhs.format(out, args...);
}
};
template <typename L, typename R>
constexpr concat<L, R> make_concat(L lhs, R rhs) {
return {lhs, rhs};
}
struct unknown_format {};
template <typename Char>
constexpr size_t parse_text(basic_string_view<Char> str, size_t pos) {
for (size_t size = str.size(); pos != size; ++pos) {
if (str[pos] == '{' || str[pos] == '}') break;
}
return pos;
}
template <typename Args, size_t POS, int ID, typename S>
constexpr auto compile_format_string(S format_str);
template <typename Args, size_t POS, int ID, typename T, typename S>
constexpr auto parse_tail(T head, S format_str) {
if constexpr (POS != to_string_view(format_str).size()) {
constexpr auto tail = compile_format_string<Args, POS, ID>(format_str);
if constexpr (std::is_same<remove_cvref_t<decltype(tail)>,
unknown_format>())
return tail;
else
return make_concat(head, tail);
} else {
return head;
}
}
// Compiles a non-empty format string and returns the compiled representation
// or unknown_format() on unrecognized input.
template <typename Args, size_t POS, int ID, typename S>
constexpr auto compile_format_string(S format_str) {
using char_type = typename S::char_type;
constexpr basic_string_view<char_type> str = format_str;
if constexpr (str[POS] == '{') {
if (POS + 1 == str.size())
throw format_error("unmatched '{' in format string");
if constexpr (str[POS + 1] == '{') {
return parse_tail<Args, POS + 2, ID>(make_text(str, POS, 1), format_str);
} else if constexpr (str[POS + 1] == '}') {
using type = get_type<ID, Args>;
if constexpr (std::is_same<type, int>::value) {
return parse_tail<Args, POS + 2, ID + 1>(field<char_type, type, ID>(),
format_str);
} else {
return unknown_format();
}
} else {
return unknown_format();
}
} else if constexpr (str[POS] == '}') {
if (POS + 1 == str.size())
throw format_error("unmatched '}' in format string");
return parse_tail<Args, POS + 2, ID>(make_text(str, POS, 1), format_str);
} else {
constexpr auto end = parse_text(str, POS + 1);
return parse_tail<Args, end, ID>(make_text(str, POS, end - POS),
format_str);
}
}
#endif // __cpp_if_constexpr
} // namespace internal } // namespace internal
#if FMT_USE_CONSTEXPR #if FMT_USE_CONSTEXPR
# ifdef __cpp_if_constexpr
template <typename... Args, typename S, template <typename... Args, typename S,
FMT_ENABLE_IF(is_compile_string<S>::value)> FMT_ENABLE_IF(is_compile_string<S>::value)>
FMT_CONSTEXPR auto compile(S format_str) -> internal::prepared_format< constexpr auto compile(S format_str) {
S, constexpr basic_string_view<typename S::char_type> str = format_str;
internal::compiletime_parts_provider< if constexpr (str.size() == 0) {
S, return internal::make_text(str, 0, 0);
typename internal::compiletime_prepared_parts_type_provider<S>::type>, } else {
Args...> { constexpr auto result =
return format_str; internal::compile_format_string<internal::type_list<Args...>, 0, 0>(
} format_str);
#endif if constexpr (std::is_same<remove_cvref_t<decltype(result)>,
internal::unknown_format>()) {
template <typename... Args, typename Char, size_t N> return internal::compiled_format<S, Args...>(to_string_view(format_str));
auto compile(const Char (&format_str)[N]) -> internal::prepared_format< } else {
std::basic_string<Char>, return result;
internal::runtime_parts_provider<std::vector<internal::format_part<Char>>>, }
Args...> { }
return std::basic_string<Char>(format_str, N - 1);
} }
template <typename CompiledFormat, typename... Args, template <typename CompiledFormat, typename... Args,
typename Char = typename CompiledFormat::char_type> typename Char = typename CompiledFormat::char_type,
FMT_ENABLE_IF(!std::is_base_of<internal::basic_compiled_format,
CompiledFormat>::value)>
std::basic_string<Char> format(const CompiledFormat& cf, const Args&... args) { std::basic_string<Char> format(const CompiledFormat& cf, const Args&... args) {
basic_memory_buffer<Char> buffer; basic_memory_buffer<Char> buffer;
using range = internal::buffer_range<Char>; using range = buffer_range<Char>;
using context = buffer_context<Char>; using context = buffer_context<Char>;
cf.template vformat_to<range, context>(range(buffer), cf.format(std::back_inserter(buffer), args...);
{make_format_args<context>(args...)});
return to_string(buffer); return to_string(buffer);
} }
template <typename OutputIt, typename CompiledFormat, typename... Args> template <typename OutputIt, typename CompiledFormat, typename... Args,
FMT_ENABLE_IF(!std::is_base_of<internal::basic_compiled_format,
CompiledFormat>::value)>
OutputIt format_to(OutputIt out, const CompiledFormat& cf,
const Args&... args) {
return cf.format(out, args...);
}
# else
template <typename... Args, typename S,
FMT_ENABLE_IF(is_compile_string<S>::value)>
constexpr auto compile(S format_str) -> internal::compiled_format<S, Args...> {
return internal::compiled_format<S, Args...>(to_string_view(format_str));
}
# endif // __cpp_if_constexpr
#endif // FMT_USE_CONSTEXPR
// Compiles the format string which must be a string literal.
template <typename... Args, typename Char, size_t N>
auto compile(const Char (&format_str)[N])
-> internal::compiled_format<const Char*, Args...> {
return internal::compiled_format<const Char*, Args...>(
basic_string_view<Char>(format_str, N - 1));
}
template <typename CompiledFormat, typename... Args,
typename Char = typename CompiledFormat::char_type,
FMT_ENABLE_IF(std::is_base_of<internal::basic_compiled_format,
CompiledFormat>::value)>
std::basic_string<Char> format(const CompiledFormat& cf, const Args&... args) {
basic_memory_buffer<Char> buffer;
using range = buffer_range<Char>;
using context = buffer_context<Char>;
internal::cf::vformat_to<context>(range(buffer), cf,
{make_format_args<context>(args...)});
return to_string(buffer);
}
template <typename OutputIt, typename CompiledFormat, typename... Args,
FMT_ENABLE_IF(std::is_base_of<internal::basic_compiled_format,
CompiledFormat>::value)>
OutputIt format_to(OutputIt out, const CompiledFormat& cf, OutputIt format_to(OutputIt out, const CompiledFormat& cf,
const Args&... args) { const Args&... args) {
using char_type = typename CompiledFormat::char_type; using char_type = typename CompiledFormat::char_type;
using range = internal::output_range<OutputIt, char_type>; using range = internal::output_range<OutputIt, char_type>;
using context = format_context_t<OutputIt, char_type>; using context = format_context_t<OutputIt, char_type>;
return cf.template vformat_to<range, context>( return internal::cf::vformat_to<context>(
range(out), {make_format_args<context>(args...)}); range(out), cf, {make_format_args<context>(args...)});
} }
template <typename OutputIt, typename CompiledFormat, typename... Args, template <typename OutputIt, typename CompiledFormat, typename... Args,
@ -455,10 +579,7 @@ format_to_n_result<OutputIt> format_to_n(OutputIt out, size_t n,
template <typename CompiledFormat, typename... Args> template <typename CompiledFormat, typename... Args>
std::size_t formatted_size(const CompiledFormat& cf, const Args&... args) { std::size_t formatted_size(const CompiledFormat& cf, const Args&... args) {
return fmt::format_to( return format_to(internal::counting_iterator(), cf, args...).count();
internal::counting_iterator<typename CompiledFormat::char_type>(),
cf, args...)
.count();
} }
FMT_END_NAMESPACE FMT_END_NAMESPACE

View File

@ -8,7 +8,6 @@
#ifndef FMT_CORE_H_ #ifndef FMT_CORE_H_
#define FMT_CORE_H_ #define FMT_CORE_H_
#include <cassert>
#include <cstdio> // std::FILE #include <cstdio> // std::FILE
#include <cstring> #include <cstring>
#include <iterator> #include <iterator>
@ -16,7 +15,7 @@
#include <type_traits> #include <type_traits>
// The fmt library version in the form major * 10000 + minor * 100 + patch. // The fmt library version in the form major * 10000 + minor * 100 + patch.
#define FMT_VERSION 60000 #define FMT_VERSION 60100
#ifdef __has_feature #ifdef __has_feature
# define FMT_HAS_FEATURE(x) __has_feature(x) # define FMT_HAS_FEATURE(x) __has_feature(x)
@ -49,6 +48,12 @@
# define FMT_HAS_GXX_CXX11 0 # define FMT_HAS_GXX_CXX11 0
#endif #endif
#ifdef __NVCC__
# define FMT_NVCC __NVCC__
#else
# define FMT_NVCC 0
#endif
#ifdef _MSC_VER #ifdef _MSC_VER
# define FMT_MSC_VER _MSC_VER # define FMT_MSC_VER _MSC_VER
#else #else
@ -60,7 +65,8 @@
#ifndef FMT_USE_CONSTEXPR #ifndef FMT_USE_CONSTEXPR
# define FMT_USE_CONSTEXPR \ # define FMT_USE_CONSTEXPR \
(FMT_HAS_FEATURE(cxx_relaxed_constexpr) || FMT_MSC_VER >= 1910 || \ (FMT_HAS_FEATURE(cxx_relaxed_constexpr) || FMT_MSC_VER >= 1910 || \
(FMT_GCC_VERSION >= 600 && __cplusplus >= 201402L)) (FMT_GCC_VERSION >= 600 && __cplusplus >= 201402L)) && \
!FMT_NVCC
#endif #endif
#if FMT_USE_CONSTEXPR #if FMT_USE_CONSTEXPR
# define FMT_CONSTEXPR constexpr # define FMT_CONSTEXPR constexpr
@ -133,6 +139,13 @@
# endif # endif
#endif #endif
// Workaround broken [[deprecated]] in the Intel compiler and NVCC.
#if defined(__INTEL_COMPILER) || FMT_NVCC
# define FMT_DEPRECATED_ALIAS
#else
# define FMT_DEPRECATED_ALIAS FMT_DEPRECATED
#endif
#ifndef FMT_BEGIN_NAMESPACE #ifndef FMT_BEGIN_NAMESPACE
# if FMT_HAS_FEATURE(cxx_inline_namespaces) || FMT_GCC_VERSION >= 404 || \ # if FMT_HAS_FEATURE(cxx_inline_namespaces) || FMT_GCC_VERSION >= 404 || \
FMT_MSC_VER >= 1900 FMT_MSC_VER >= 1900
@ -154,9 +167,9 @@
#if !defined(FMT_HEADER_ONLY) && defined(_WIN32) #if !defined(FMT_HEADER_ONLY) && defined(_WIN32)
# ifdef FMT_EXPORT # ifdef FMT_EXPORT
# define FMT_API __declspec(dllexport) # define FMT_API __pragma(warning(suppress : 4275)) __declspec(dllexport)
# elif defined(FMT_SHARED) # elif defined(FMT_SHARED)
# define FMT_API __declspec(dllimport) # define FMT_API __pragma(warning(suppress : 4275)) __declspec(dllimport)
# define FMT_EXTERN_TEMPLATE_API FMT_API # define FMT_EXTERN_TEMPLATE_API FMT_API
# endif # endif
#endif #endif
@ -173,10 +186,6 @@
# define FMT_EXTERN # define FMT_EXTERN
#endif #endif
#ifndef FMT_ASSERT
# define FMT_ASSERT(condition, message) assert((condition) && message)
#endif
// libc++ supports string_view in pre-c++17. // libc++ supports string_view in pre-c++17.
#if (FMT_HAS_INCLUDE(<string_view>) && \ #if (FMT_HAS_INCLUDE(<string_view>) && \
(__cplusplus > 201402L || defined(_LIBCPP_VERSION))) || \ (__cplusplus > 201402L || defined(_LIBCPP_VERSION))) || \
@ -200,6 +209,8 @@ template <typename T>
using remove_reference_t = typename std::remove_reference<T>::type; using remove_reference_t = typename std::remove_reference<T>::type;
template <typename T> template <typename T>
using remove_const_t = typename std::remove_const<T>::type; using remove_const_t = typename std::remove_const<T>::type;
template <typename T>
using remove_cvref_t = typename std::remove_cv<remove_reference_t<T>>::type;
struct monostate {}; struct monostate {};
@ -213,6 +224,19 @@ namespace internal {
// A workaround for gcc 4.8 to make void_t work in a SFINAE context. // A workaround for gcc 4.8 to make void_t work in a SFINAE context.
template <typename... Ts> struct void_t_impl { using type = void; }; template <typename... Ts> struct void_t_impl { using type = void; };
void assert_fail(const char* file, int line, const char* message);
#ifndef FMT_ASSERT
# ifdef NDEBUG
# define FMT_ASSERT(condition, message)
# else
# define FMT_ASSERT(condition, message) \
((condition) \
? void() \
: fmt::internal::assert_fail(__FILE__, __LINE__, (message)))
# endif
#endif
#if defined(FMT_USE_STRING_VIEW) #if defined(FMT_USE_STRING_VIEW)
template <typename Char> using std_string_view = std::basic_string_view<Char>; template <typename Char> using std_string_view = std::basic_string_view<Char>;
#elif defined(FMT_USE_EXPERIMENTAL_STRING_VIEW) #elif defined(FMT_USE_EXPERIMENTAL_STRING_VIEW)
@ -222,7 +246,21 @@ using std_string_view = std::experimental::basic_string_view<Char>;
template <typename T> struct std_string_view {}; template <typename T> struct std_string_view {};
#endif #endif
// Casts nonnegative integer to unsigned. #ifdef FMT_USE_INT128
// Do nothing.
#elif defined(__SIZEOF_INT128__)
# define FMT_USE_INT128 1
using int128_t = __int128_t;
using uint128_t = __uint128_t;
#else
# define FMT_USE_INT128 0
#endif
#if !FMT_USE_INT128
struct int128_t {};
struct uint128_t {};
#endif
// Casts a nonnegative integer to unsigned.
template <typename Int> template <typename Int>
FMT_CONSTEXPR typename std::make_unsigned<Int>::type to_unsigned(Int value) { FMT_CONSTEXPR typename std::make_unsigned<Int>::type to_unsigned(Int value) {
FMT_ASSERT(value >= 0, "negative value"); FMT_ASSERT(value >= 0, "negative value");
@ -266,10 +304,11 @@ template <typename Char> class basic_string_view {
: data_(s), size_(std::char_traits<Char>::length(s)) {} : data_(s), size_(std::char_traits<Char>::length(s)) {}
/** Constructs a string reference from a ``std::basic_string`` object. */ /** Constructs a string reference from a ``std::basic_string`` object. */
template <typename Alloc> template <typename Traits, typename Alloc>
FMT_CONSTEXPR basic_string_view(const std::basic_string<Char, Alloc>& s) FMT_CONSTEXPR basic_string_view(
FMT_NOEXCEPT : data_(s.data()), const std::basic_string<Char, Traits, Alloc>& s) FMT_NOEXCEPT
size_(s.size()) {} : data_(s.data()),
size_(s.size()) {}
template < template <
typename S, typename S,
@ -286,6 +325,8 @@ template <typename Char> class basic_string_view {
FMT_CONSTEXPR iterator begin() const { return data_; } FMT_CONSTEXPR iterator begin() const { return data_; }
FMT_CONSTEXPR iterator end() const { return data_ + size_; } FMT_CONSTEXPR iterator end() const { return data_ + size_; }
FMT_CONSTEXPR const Char& operator[](size_t pos) const { return data_[pos]; }
FMT_CONSTEXPR void remove_prefix(size_t n) { FMT_CONSTEXPR void remove_prefix(size_t n) {
data_ += n; data_ += n;
size_ -= n; size_ -= n;
@ -357,10 +398,10 @@ inline basic_string_view<Char> to_string_view(const Char* s) {
return s; return s;
} }
template <typename Char, typename Traits, typename Allocator> template <typename Char, typename Traits, typename Alloc>
inline basic_string_view<Char> to_string_view( inline basic_string_view<Char> to_string_view(
const std::basic_string<Char, Traits, Allocator>& s) { const std::basic_string<Char, Traits, Alloc>& s) {
return {s.data(), s.size()}; return s;
} }
template <typename Char> template <typename Char>
@ -405,8 +446,8 @@ template <typename S> struct char_t_impl<S, enable_if_t<is_string<S>::value>> {
}; };
struct error_handler { struct error_handler {
FMT_CONSTEXPR error_handler() {} FMT_CONSTEXPR error_handler() = default;
FMT_CONSTEXPR error_handler(const error_handler&) {} FMT_CONSTEXPR error_handler(const error_handler&) = default;
// This function is intentionally not constexpr to give a compile-time error. // This function is intentionally not constexpr to give a compile-time error.
FMT_NORETURN FMT_API void on_error(const char* message); FMT_NORETURN FMT_API void on_error(const char* message);
@ -416,10 +457,24 @@ struct error_handler {
/** String's character type. */ /** String's character type. */
template <typename S> using char_t = typename internal::char_t_impl<S>::type; template <typename S> using char_t = typename internal::char_t_impl<S>::type;
// Parsing context consisting of a format string range being parsed and an /**
// argument counter for automatic indexing. \rst
Parsing context consisting of a format string range being parsed and an
argument counter for automatic indexing.
You can use one of the following type aliases for common character types:
+-----------------------+-------------------------------------+
| Type | Definition |
+=======================+=====================================+
| format_parse_context | basic_format_parse_context<char> |
+-----------------------+-------------------------------------+
| wformat_parse_context | basic_format_parse_context<wchar_t> |
+-----------------------+-------------------------------------+
\endrst
*/
template <typename Char, typename ErrorHandler = internal::error_handler> template <typename Char, typename ErrorHandler = internal::error_handler>
class basic_parse_context : private ErrorHandler { class basic_format_parse_context : private ErrorHandler {
private: private:
basic_string_view<Char> format_str_; basic_string_view<Char> format_str_;
int next_arg_id_; int next_arg_id_;
@ -428,38 +483,47 @@ class basic_parse_context : private ErrorHandler {
using char_type = Char; using char_type = Char;
using iterator = typename basic_string_view<Char>::iterator; using iterator = typename basic_string_view<Char>::iterator;
explicit FMT_CONSTEXPR basic_parse_context(basic_string_view<Char> format_str, explicit FMT_CONSTEXPR basic_format_parse_context(
ErrorHandler eh = ErrorHandler()) basic_string_view<Char> format_str, ErrorHandler eh = ErrorHandler())
: ErrorHandler(eh), format_str_(format_str), next_arg_id_(0) {} : ErrorHandler(eh), format_str_(format_str), next_arg_id_(0) {}
// Returns an iterator to the beginning of the format string range being /**
// parsed. Returns an iterator to the beginning of the format string range being
parsed.
*/
FMT_CONSTEXPR iterator begin() const FMT_NOEXCEPT { FMT_CONSTEXPR iterator begin() const FMT_NOEXCEPT {
return format_str_.begin(); return format_str_.begin();
} }
// Returns an iterator past the end of the format string range being parsed. /**
Returns an iterator past the end of the format string range being parsed.
*/
FMT_CONSTEXPR iterator end() const FMT_NOEXCEPT { return format_str_.end(); } FMT_CONSTEXPR iterator end() const FMT_NOEXCEPT { return format_str_.end(); }
// Advances the begin iterator to ``it``. /** Advances the begin iterator to ``it``. */
FMT_CONSTEXPR void advance_to(iterator it) { FMT_CONSTEXPR void advance_to(iterator it) {
format_str_.remove_prefix(internal::to_unsigned(it - begin())); format_str_.remove_prefix(internal::to_unsigned(it - begin()));
} }
// Returns the next argument index. /**
Reports an error if using the manual argument indexing; otherwise returns
the next argument index and switches to the automatic indexing.
*/
FMT_CONSTEXPR int next_arg_id() { FMT_CONSTEXPR int next_arg_id() {
if (next_arg_id_ >= 0) return next_arg_id_++; if (next_arg_id_ >= 0) return next_arg_id_++;
on_error("cannot switch from manual to automatic argument indexing"); on_error("cannot switch from manual to automatic argument indexing");
return 0; return 0;
} }
FMT_CONSTEXPR bool check_arg_id(int) { /**
if (next_arg_id_ > 0) { Reports an error if using the automatic argument indexing; otherwise
switches to the manual indexing.
*/
FMT_CONSTEXPR void check_arg_id(int) {
if (next_arg_id_ > 0)
on_error("cannot switch from automatic to manual argument indexing"); on_error("cannot switch from automatic to manual argument indexing");
return false; else
} next_arg_id_ = -1;
next_arg_id_ = -1;
return true;
} }
FMT_CONSTEXPR void check_arg_id(basic_string_view<Char>) {} FMT_CONSTEXPR void check_arg_id(basic_string_view<Char>) {}
@ -471,11 +535,14 @@ class basic_parse_context : private ErrorHandler {
FMT_CONSTEXPR ErrorHandler error_handler() const { return *this; } FMT_CONSTEXPR ErrorHandler error_handler() const { return *this; }
}; };
using format_parse_context = basic_parse_context<char>; using format_parse_context = basic_format_parse_context<char>;
using wformat_parse_context = basic_parse_context<wchar_t>; using wformat_parse_context = basic_format_parse_context<wchar_t>;
using parse_context FMT_DEPRECATED = basic_parse_context<char>; template <typename Char, typename ErrorHandler = internal::error_handler>
using wparse_context FMT_DEPRECATED = basic_parse_context<wchar_t>; using basic_parse_context FMT_DEPRECATED_ALIAS =
basic_format_parse_context<Char, ErrorHandler>;
using parse_context FMT_DEPRECATED_ALIAS = basic_format_parse_context<char>;
using wparse_context FMT_DEPRECATED_ALIAS = basic_format_parse_context<wchar_t>;
template <typename Context> class basic_format_arg; template <typename Context> class basic_format_arg;
template <typename Context> class basic_format_args; template <typename Context> class basic_format_args;
@ -492,20 +559,17 @@ struct FMT_DEPRECATED convert_to_int
: bool_constant<!std::is_arithmetic<T>::value && : bool_constant<!std::is_arithmetic<T>::value &&
std::is_convertible<T, int>::value> {}; std::is_convertible<T, int>::value> {};
namespace internal {
// Specifies if T has an enabled formatter specialization. A type can be // Specifies if T has an enabled formatter specialization. A type can be
// formattable even if it doesn't have a formatter e.g. via a conversion. // formattable even if it doesn't have a formatter e.g. via a conversion.
template <typename T, typename Context> template <typename T, typename Context>
using has_formatter = using has_formatter =
std::is_constructible<typename Context::template formatter_type<T>>; std::is_constructible<typename Context::template formatter_type<T>>;
namespace internal {
/** A contiguous memory buffer with an optional growing ability. */ /** A contiguous memory buffer with an optional growing ability. */
template <typename T> class buffer { template <typename T> class buffer {
private: private:
buffer(const buffer&) = delete;
void operator=(const buffer&) = delete;
T* ptr_; T* ptr_;
std::size_t size_; std::size_t size_;
std::size_t capacity_; std::size_t capacity_;
@ -532,7 +596,9 @@ template <typename T> class buffer {
using value_type = T; using value_type = T;
using const_reference = const T&; using const_reference = const T&;
virtual ~buffer() {} buffer(const buffer&) = delete;
void operator=(const buffer&) = delete;
virtual ~buffer() = default;
T* begin() FMT_NOEXCEPT { return ptr_; } T* begin() FMT_NOEXCEPT { return ptr_; }
T* end() FMT_NOEXCEPT { return ptr_ + size_; } T* end() FMT_NOEXCEPT { return ptr_ + size_; }
@ -626,10 +692,13 @@ enum type {
uint_type, uint_type,
long_long_type, long_long_type,
ulong_long_type, ulong_long_type,
int128_type,
uint128_type,
bool_type, bool_type,
char_type, char_type,
last_integer_type = char_type, last_integer_type = char_type,
// followed by floating-point types. // followed by floating-point types.
float_type,
double_type, double_type,
long_double_type, long_double_type,
last_numeric_type = long_double_type, last_numeric_type = long_double_type,
@ -652,20 +721,23 @@ FMT_TYPE_CONSTANT(int, int_type);
FMT_TYPE_CONSTANT(unsigned, uint_type); FMT_TYPE_CONSTANT(unsigned, uint_type);
FMT_TYPE_CONSTANT(long long, long_long_type); FMT_TYPE_CONSTANT(long long, long_long_type);
FMT_TYPE_CONSTANT(unsigned long long, ulong_long_type); FMT_TYPE_CONSTANT(unsigned long long, ulong_long_type);
FMT_TYPE_CONSTANT(int128_t, int128_type);
FMT_TYPE_CONSTANT(uint128_t, uint128_type);
FMT_TYPE_CONSTANT(bool, bool_type); FMT_TYPE_CONSTANT(bool, bool_type);
FMT_TYPE_CONSTANT(Char, char_type); FMT_TYPE_CONSTANT(Char, char_type);
FMT_TYPE_CONSTANT(float, float_type);
FMT_TYPE_CONSTANT(double, double_type); FMT_TYPE_CONSTANT(double, double_type);
FMT_TYPE_CONSTANT(long double, long_double_type); FMT_TYPE_CONSTANT(long double, long_double_type);
FMT_TYPE_CONSTANT(const Char*, cstring_type); FMT_TYPE_CONSTANT(const Char*, cstring_type);
FMT_TYPE_CONSTANT(basic_string_view<Char>, string_type); FMT_TYPE_CONSTANT(basic_string_view<Char>, string_type);
FMT_TYPE_CONSTANT(const void*, pointer_type); FMT_TYPE_CONSTANT(const void*, pointer_type);
FMT_CONSTEXPR bool is_integral(type t) { FMT_CONSTEXPR bool is_integral_type(type t) {
FMT_ASSERT(t != named_arg_type, "invalid argument type"); FMT_ASSERT(t != named_arg_type, "invalid argument type");
return t > none_type && t <= last_integer_type; return t > none_type && t <= last_integer_type;
} }
FMT_CONSTEXPR bool is_arithmetic(type t) { FMT_CONSTEXPR bool is_arithmetic_type(type t) {
FMT_ASSERT(t != named_arg_type, "invalid argument type"); FMT_ASSERT(t != named_arg_type, "invalid argument type");
return t > none_type && t <= last_numeric_type; return t > none_type && t <= last_numeric_type;
} }
@ -676,7 +748,7 @@ template <typename Char> struct string_value {
}; };
template <typename Context> struct custom_value { template <typename Context> struct custom_value {
using parse_context = basic_parse_context<typename Context::char_type>; using parse_context = basic_format_parse_context<typename Context::char_type>;
const void* value; const void* value;
void (*format)(const void* arg, parse_context& parse_ctx, Context& ctx); void (*format)(const void* arg, parse_context& parse_ctx, Context& ctx);
}; };
@ -691,8 +763,11 @@ template <typename Context> class value {
unsigned uint_value; unsigned uint_value;
long long long_long_value; long long long_long_value;
unsigned long long ulong_long_value; unsigned long long ulong_long_value;
int128_t int128_value;
uint128_t uint128_value;
bool bool_value; bool bool_value;
char_type char_value; char_type char_value;
float float_value;
double double_value; double double_value;
long double long_double_value; long double long_double_value;
const void* pointer; const void* pointer;
@ -705,6 +780,9 @@ template <typename Context> class value {
FMT_CONSTEXPR value(unsigned val) : uint_value(val) {} FMT_CONSTEXPR value(unsigned val) : uint_value(val) {}
value(long long val) : long_long_value(val) {} value(long long val) : long_long_value(val) {}
value(unsigned long long val) : ulong_long_value(val) {} value(unsigned long long val) : ulong_long_value(val) {}
value(int128_t val) : int128_value(val) {}
value(uint128_t val) : uint128_value(val) {}
value(float val) : float_value(val) {}
value(double val) : double_value(val) {} value(double val) : double_value(val) {}
value(long double val) : long_double_value(val) {} value(long double val) : long_double_value(val) {}
value(bool val) : bool_value(val) {} value(bool val) : bool_value(val) {}
@ -732,9 +810,9 @@ template <typename Context> class value {
private: private:
// Formats an argument of a custom type, such as a user-defined class. // Formats an argument of a custom type, such as a user-defined class.
template <typename T, typename Formatter> template <typename T, typename Formatter>
static void format_custom_arg(const void* arg, static void format_custom_arg(
basic_parse_context<char_type>& parse_ctx, const void* arg, basic_format_parse_context<char_type>& parse_ctx,
Context& ctx) { Context& ctx) {
Formatter f; Formatter f;
parse_ctx.advance_to(f.parse(parse_ctx)); parse_ctx.advance_to(f.parse(parse_ctx));
ctx.advance_to(f.format(*static_cast<const T*>(arg), ctx)); ctx.advance_to(f.format(*static_cast<const T*>(arg), ctx));
@ -764,6 +842,8 @@ template <typename Context> struct arg_mapper {
FMT_CONSTEXPR ulong_type map(unsigned long val) { return val; } FMT_CONSTEXPR ulong_type map(unsigned long val) { return val; }
FMT_CONSTEXPR long long map(long long val) { return val; } FMT_CONSTEXPR long long map(long long val) { return val; }
FMT_CONSTEXPR unsigned long long map(unsigned long long val) { return val; } FMT_CONSTEXPR unsigned long long map(unsigned long long val) { return val; }
FMT_CONSTEXPR int128_t map(int128_t val) { return val; }
FMT_CONSTEXPR uint128_t map(uint128_t val) { return val; }
FMT_CONSTEXPR bool map(bool val) { return val; } FMT_CONSTEXPR bool map(bool val) { return val; }
template <typename T, FMT_ENABLE_IF(is_char<T>::value)> template <typename T, FMT_ENABLE_IF(is_char<T>::value)>
@ -774,7 +854,7 @@ template <typename Context> struct arg_mapper {
return val; return val;
} }
FMT_CONSTEXPR double map(float val) { return static_cast<double>(val); } FMT_CONSTEXPR float map(float val) { return val; }
FMT_CONSTEXPR double map(double val) { return val; } FMT_CONSTEXPR double map(double val) { return val; }
FMT_CONSTEXPR long double map(long double val) { return val; } FMT_CONSTEXPR long double map(long double val) { return val; }
@ -793,6 +873,15 @@ template <typename Context> struct arg_mapper {
FMT_CONSTEXPR basic_string_view<char_type> map(const T& val) { FMT_CONSTEXPR basic_string_view<char_type> map(const T& val) {
return basic_string_view<char_type>(val); return basic_string_view<char_type>(val);
} }
template <
typename T,
FMT_ENABLE_IF(
std::is_constructible<std_string_view<char_type>, T>::value &&
!std::is_constructible<basic_string_view<char_type>, T>::value &&
!is_string<T>::value)>
FMT_CONSTEXPR basic_string_view<char_type> map(const T& val) {
return std_string_view<char_type>(val);
}
FMT_CONSTEXPR const char* map(const signed char* val) { FMT_CONSTEXPR const char* map(const signed char* val) {
static_assert(std::is_same<char_type, char>::value, "invalid string type"); static_assert(std::is_same<char_type, char>::value, "invalid string type");
return reinterpret_cast<const char*>(val); return reinterpret_cast<const char*>(val);
@ -818,11 +907,14 @@ template <typename Context> struct arg_mapper {
FMT_ENABLE_IF(std::is_enum<T>::value && FMT_ENABLE_IF(std::is_enum<T>::value &&
!has_formatter<T, Context>::value && !has_formatter<T, Context>::value &&
!has_fallback_formatter<T, Context>::value)> !has_fallback_formatter<T, Context>::value)>
FMT_CONSTEXPR int map(const T& val) { FMT_CONSTEXPR auto map(const T& val) -> decltype(
return static_cast<int>(val); map(static_cast<typename std::underlying_type<T>::type>(val))) {
return map(static_cast<typename std::underlying_type<T>::type>(val));
} }
template <typename T, template <typename T,
FMT_ENABLE_IF(!is_string<T>::value && !is_char<T>::value && FMT_ENABLE_IF(!is_string<T>::value && !is_char<T>::value &&
!std::is_constructible<basic_string_view<char_type>,
T>::value &&
(has_formatter<T, Context>::value || (has_formatter<T, Context>::value ||
has_fallback_formatter<T, Context>::value))> has_fallback_formatter<T, Context>::value))>
FMT_CONSTEXPR const T& map(const T& val) { FMT_CONSTEXPR const T& map(const T& val) {
@ -841,12 +933,13 @@ template <typename Context> struct arg_mapper {
// A type constant after applying arg_mapper<Context>. // A type constant after applying arg_mapper<Context>.
template <typename T, typename Context> template <typename T, typename Context>
using mapped_type_constant = using mapped_type_constant =
type_constant<decltype(arg_mapper<Context>().map(std::declval<T>())), type_constant<decltype(arg_mapper<Context>().map(std::declval<const T&>())),
typename Context::char_type>; typename Context::char_type>;
enum { packed_arg_bits = 5 };
// Maximum number of arguments with packed types. // Maximum number of arguments with packed types.
enum { max_packed_args = 15 }; enum { max_packed_args = 63 / packed_arg_bits };
enum : unsigned long long { is_unpacked_bit = 1ull << 63 }; enum : unsigned long long { is_unpacked_bit = 1ULL << 63 };
template <typename Context> class arg_map; template <typename Context> class arg_map;
} // namespace internal } // namespace internal
@ -877,7 +970,8 @@ template <typename Context> class basic_format_arg {
public: public:
explicit handle(internal::custom_value<Context> custom) : custom_(custom) {} explicit handle(internal::custom_value<Context> custom) : custom_(custom) {}
void format(basic_parse_context<char_type>& parse_ctx, Context& ctx) const { void format(basic_format_parse_context<char_type>& parse_ctx,
Context& ctx) const {
custom_.format(custom_.value, parse_ctx, ctx); custom_.format(custom_.value, parse_ctx, ctx);
} }
@ -893,8 +987,8 @@ template <typename Context> class basic_format_arg {
internal::type type() const { return type_; } internal::type type() const { return type_; }
bool is_integral() const { return internal::is_integral(type_); } bool is_integral() const { return internal::is_integral_type(type_); }
bool is_arithmetic() const { return internal::is_arithmetic(type_); } bool is_arithmetic() const { return internal::is_arithmetic_type(type_); }
}; };
/** /**
@ -923,10 +1017,22 @@ FMT_CONSTEXPR auto visit_format_arg(Visitor&& vis,
return vis(arg.value_.long_long_value); return vis(arg.value_.long_long_value);
case internal::ulong_long_type: case internal::ulong_long_type:
return vis(arg.value_.ulong_long_value); return vis(arg.value_.ulong_long_value);
#if FMT_USE_INT128
case internal::int128_type:
return vis(arg.value_.int128_value);
case internal::uint128_type:
return vis(arg.value_.uint128_value);
#else
case internal::int128_type:
case internal::uint128_type:
break;
#endif
case internal::bool_type: case internal::bool_type:
return vis(arg.value_.bool_value); return vis(arg.value_.bool_value);
case internal::char_type: case internal::char_type:
return vis(arg.value_.char_value); return vis(arg.value_.char_value);
case internal::float_type:
return vis(arg.value_.float_value);
case internal::double_type: case internal::double_type:
return vis(arg.value_.double_value); return vis(arg.value_.double_value);
case internal::long_double_type: case internal::long_double_type:
@ -948,9 +1054,6 @@ namespace internal {
// A map from argument names to their values for named arguments. // A map from argument names to their values for named arguments.
template <typename Context> class arg_map { template <typename Context> class arg_map {
private: private:
arg_map(const arg_map&) = delete;
void operator=(const arg_map&) = delete;
using char_type = typename Context::char_type; using char_type = typename Context::char_type;
struct entry { struct entry {
@ -968,6 +1071,8 @@ template <typename Context> class arg_map {
} }
public: public:
arg_map(const arg_map&) = delete;
void operator=(const arg_map&) = delete;
arg_map() : map_(nullptr), size_(0) {} arg_map() : map_(nullptr), size_(0) {}
void init(const basic_format_args<Context>& args); void init(const basic_format_args<Context>& args);
~arg_map() { delete[] map_; } ~arg_map() { delete[] map_; }
@ -990,6 +1095,8 @@ class locale_ref {
locale_ref() : locale_(nullptr) {} locale_ref() : locale_(nullptr) {}
template <typename Locale> explicit locale_ref(const Locale& loc); template <typename Locale> explicit locale_ref(const Locale& loc);
explicit operator bool() const FMT_NOEXCEPT { return locale_ != nullptr; }
template <typename Locale> Locale get() const; template <typename Locale> Locale get() const;
}; };
@ -998,7 +1105,7 @@ template <typename> constexpr unsigned long long encode_types() { return 0; }
template <typename Context, typename Arg, typename... Args> template <typename Context, typename Arg, typename... Args>
constexpr unsigned long long encode_types() { constexpr unsigned long long encode_types() {
return mapped_type_constant<Arg, Context>::value | return mapped_type_constant<Arg, Context>::value |
(encode_types<Context, Args...>() << 4); (encode_types<Context, Args...>() << packed_arg_bits);
} }
template <typename Context, typename T> template <typename Context, typename T>
@ -1034,14 +1141,13 @@ template <typename OutputIt, typename Char> class basic_format_context {
internal::arg_map<basic_format_context> map_; internal::arg_map<basic_format_context> map_;
internal::locale_ref loc_; internal::locale_ref loc_;
basic_format_context(const basic_format_context&) = delete;
void operator=(const basic_format_context&) = delete;
public: public:
using iterator = OutputIt; using iterator = OutputIt;
using format_arg = basic_format_arg<basic_format_context>; using format_arg = basic_format_arg<basic_format_context>;
template <typename T> using formatter_type = formatter<T, char_type>; template <typename T> using formatter_type = formatter<T, char_type>;
basic_format_context(const basic_format_context&) = delete;
void operator=(const basic_format_context&) = delete;
/** /**
Constructs a ``basic_format_context`` object. References to the arguments are Constructs a ``basic_format_context`` object. References to the arguments are
stored in the object so make sure they have appropriate lifetimes. stored in the object so make sure they have appropriate lifetimes.
@ -1143,8 +1249,9 @@ template <typename Context> class basic_format_args {
bool is_packed() const { return (types_ & internal::is_unpacked_bit) == 0; } bool is_packed() const { return (types_ & internal::is_unpacked_bit) == 0; }
internal::type type(int index) const { internal::type type(int index) const {
int shift = index * 4; int shift = index * internal::packed_arg_bits;
return static_cast<internal::type>((types_ & (0xfull << shift)) >> shift); unsigned int mask = (1 << internal::packed_arg_bits) - 1;
return static_cast<internal::type>((types_ >> shift) & mask);
} }
friend class internal::arg_map<Context>; friend class internal::arg_map<Context>;
@ -1371,7 +1478,7 @@ inline std::basic_string<Char> format(const S& format_str, Args&&... args) {
} }
FMT_API void vprint(std::FILE* f, string_view format_str, format_args args); FMT_API void vprint(std::FILE* f, string_view format_str, format_args args);
FMT_API void vprint(std::FILE* f, wstring_view format_str, wformat_args args); FMT_API void vprint(string_view format_str, format_args args);
/** /**
\rst \rst
@ -1391,9 +1498,6 @@ inline void print(std::FILE* f, const S& format_str, Args&&... args) {
internal::make_args_checked<Args...>(format_str, args...)); internal::make_args_checked<Args...>(format_str, args...));
} }
FMT_API void vprint(string_view format_str, format_args args);
FMT_API void vprint(wstring_view format_str, wformat_args args);
/** /**
\rst \rst
Prints formatted data to ``stdout``. Prints formatted data to ``stdout``.

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -46,9 +46,13 @@ template <class Char> class formatbuf : public std::basic_streambuf<Char> {
template <typename Char> struct test_stream : std::basic_ostream<Char> { template <typename Char> struct test_stream : std::basic_ostream<Char> {
private: private:
struct null;
// Hide all operator<< from std::basic_ostream<Char>. // Hide all operator<< from std::basic_ostream<Char>.
void operator<<(null); void_t<> operator<<(null<>);
void_t<> operator<<(const Char*);
template <typename T, FMT_ENABLE_IF(std::is_convertible<T, int>::value &&
!std::is_enum<T>::value)>
void_t<> operator<<(T);
}; };
// Checks if T has a user-defined operator<< (e.g. not a member of // Checks if T has a user-defined operator<< (e.g. not a member of
@ -56,9 +60,9 @@ template <typename Char> struct test_stream : std::basic_ostream<Char> {
template <typename T, typename Char> class is_streamable { template <typename T, typename Char> class is_streamable {
private: private:
template <typename U> template <typename U>
static decltype((void)(std::declval<test_stream<Char>&>() static bool_constant<!std::is_same<decltype(std::declval<test_stream<Char>&>()
<< std::declval<U>()), << std::declval<U>()),
std::true_type()) void_t<>>::value>
test(int); test(int);
template <typename> static std::false_type test(...); template <typename> static std::false_type test(...);
@ -75,8 +79,7 @@ void write(std::basic_ostream<Char>& os, buffer<Char>& buf) {
const Char* buf_data = buf.data(); const Char* buf_data = buf.data();
using unsigned_streamsize = std::make_unsigned<std::streamsize>::type; using unsigned_streamsize = std::make_unsigned<std::streamsize>::type;
unsigned_streamsize size = buf.size(); unsigned_streamsize size = buf.size();
unsigned_streamsize max_size = unsigned_streamsize max_size = to_unsigned(max_value<std::streamsize>());
to_unsigned((std::numeric_limits<std::streamsize>::max)());
do { do {
unsigned_streamsize n = size <= max_size ? size : max_size; unsigned_streamsize n = size <= max_size ? size : max_size;
os.write(buf_data, static_cast<std::streamsize>(n)); os.write(buf_data, static_cast<std::streamsize>(n));
@ -86,9 +89,11 @@ void write(std::basic_ostream<Char>& os, buffer<Char>& buf) {
} }
template <typename Char, typename T> template <typename Char, typename T>
void format_value(buffer<Char>& buf, const T& value) { void format_value(buffer<Char>& buf, const T& value,
locale_ref loc = locale_ref()) {
formatbuf<Char> format_buf(buf); formatbuf<Char> format_buf(buf);
std::basic_ostream<Char> output(&format_buf); std::basic_ostream<Char> output(&format_buf);
if (loc) output.imbue(loc.get<std::locale>());
output.exceptions(std::ios_base::failbit | std::ios_base::badbit); output.exceptions(std::ios_base::failbit | std::ios_base::badbit);
output << value; output << value;
buf.resize(buf.size()); buf.resize(buf.size());
@ -101,7 +106,7 @@ struct fallback_formatter<T, Char, enable_if_t<is_streamable<T, Char>::value>>
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;
format_value(buffer, value); format_value(buffer, value, ctx.locale());
basic_string_view<Char> str(buffer.data(), buffer.size()); basic_string_view<Char> str(buffer.data(), buffer.size());
return formatter<basic_string_view<Char>, Char>::format(str, ctx); return formatter<basic_string_view<Char>, Char>::format(str, ctx);
} }

View File

@ -13,11 +13,10 @@
# undef __STRICT_ANSI__ # undef __STRICT_ANSI__
#endif #endif
#include <errno.h> #include <cerrno>
#include <fcntl.h> // for O_RDONLY #include <clocale> // for locale_t
#include <locale.h> // for locale_t #include <cstdio>
#include <stdio.h> #include <cstdlib> // for strtod_l
#include <stdlib.h> // for strtod_l
#include <cstddef> #include <cstddef>
@ -27,6 +26,18 @@
#include "format.h" #include "format.h"
// UWP doesn't provide _pipe.
#if FMT_HAS_INCLUDE("winapifamily.h")
# include <winapifamily.h>
#endif
#if FMT_HAS_INCLUDE("fcntl.h") && \
(!defined(WINAPI_FAMILY) || (WINAPI_FAMILY == WINAPI_FAMILY_DESKTOP_APP))
# include <fcntl.h> // for O_RDONLY
# define FMT_USE_FCNTL 1
#else
# define FMT_USE_FCNTL 0
#endif
#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.
@ -54,8 +65,8 @@
#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
@ -132,16 +143,15 @@ class buffered_file {
explicit buffered_file(FILE* f) : file_(f) {} explicit buffered_file(FILE* f) : file_(f) {}
public: public:
buffered_file(const buffered_file&) = delete;
void operator=(const buffered_file&) = delete;
// Constructs a buffered_file object which doesn't represent any file. // Constructs a buffered_file object which doesn't represent any file.
buffered_file() FMT_NOEXCEPT : file_(nullptr) {} buffered_file() FMT_NOEXCEPT : file_(nullptr) {}
// Destroys the object closing the file it represents if any. // Destroys the object closing the file it represents if any.
FMT_API ~buffered_file() FMT_NOEXCEPT; FMT_API ~buffered_file() FMT_NOEXCEPT;
private:
buffered_file(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_ = nullptr; other.file_ = nullptr;
@ -177,6 +187,7 @@ class buffered_file {
} }
}; };
#if FMT_USE_FCNTL
// A file. Closed file is represented by a file object with descriptor -1. // A file. Closed file is represented by a file object with descriptor -1.
// Methods that are not declared with FMT_NOEXCEPT may throw // Methods that are not declared with FMT_NOEXCEPT may throw
// fmt::system_error in case of failure. Note that some errors such as // fmt::system_error in case of failure. Note that some errors such as
@ -204,14 +215,13 @@ class file {
// Opens a file and constructs a file object representing this file. // Opens a file and constructs a file object representing this file.
FMT_API file(cstring_view path, int oflag); FMT_API file(cstring_view path, int oflag);
private: public:
file(const file&) = delete; file(const file&) = delete;
void operator=(const file&) = delete; void operator=(const file&) = delete;
public:
file(file&& other) FMT_NOEXCEPT : fd_(other.fd_) { other.fd_ = -1; } file(file&& other) FMT_NOEXCEPT : fd_(other.fd_) { other.fd_ = -1; }
file& operator=(file&& other) { file& operator=(file&& other) FMT_NOEXCEPT {
close(); close();
fd_ = other.fd_; fd_ = other.fd_;
other.fd_ = -1; other.fd_ = -1;
@ -260,6 +270,7 @@ class file {
// Returns the memory page size. // Returns the memory page size.
long getpagesize(); long getpagesize();
#endif // FMT_USE_FCNTL
#ifdef FMT_LOCALE #ifdef FMT_LOCALE
// A "C" numeric locale. // A "C" numeric locale.
@ -283,11 +294,10 @@ class Locale {
locale_t locale_; locale_t locale_;
Locale(const Locale&) = delete;
void operator=(const Locale&) = delete;
public: public:
using type = locale_t; using type = locale_t;
Locale(const Locale&) = delete;
void operator=(const Locale&) = delete;
Locale() : locale_(newlocale(LC_NUMERIC_MASK, "C", nullptr)) { Locale() : locale_(newlocale(LC_NUMERIC_MASK, "C", nullptr)) {
if (!locale_) FMT_THROW(system_error(errno, "cannot create locale")); if (!locale_) FMT_THROW(system_error(errno, "cannot create locale"));

View File

@ -1,4 +1,4 @@
// Formatting library for C++ // Formatting library for C++ - legacy printf implementation
// //
// Copyright (c) 2012 - 2016, Victor Zverovich // Copyright (c) 2012 - 2016, Victor Zverovich
// All rights reserved. // All rights reserved.
@ -8,7 +8,7 @@
#ifndef FMT_PRINTF_H_ #ifndef FMT_PRINTF_H_
#define FMT_PRINTF_H_ #define FMT_PRINTF_H_
#include <algorithm> // std::fill_n #include <algorithm> // std::max
#include <limits> // std::numeric_limits #include <limits> // std::numeric_limits
#include "ostream.h" #include "ostream.h"
@ -16,15 +16,11 @@
FMT_BEGIN_NAMESPACE FMT_BEGIN_NAMESPACE
namespace internal { namespace internal {
// A helper function to suppress bogus "conditional expression is constant"
// warnings.
template <typename T> inline T const_check(T value) { return value; }
// 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> struct int_checker { template <bool IsSigned> 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 = max_value<int>();
return value <= max; return value <= max;
} }
static bool fits_in_int(bool) { return true; } static bool fits_in_int(bool) { return true; }
@ -33,7 +29,7 @@ template <bool IsSigned> struct int_checker {
template <> struct int_checker<true> { template <> 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 <= max_value<int>();
} }
static bool fits_in_int(int) { return true; } static bool fits_in_int(int) { return true; }
}; };
@ -158,12 +154,12 @@ template <typename Char> class printf_width_handler {
template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)> template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
unsigned operator()(T value) { unsigned operator()(T value) {
auto width = static_cast<uint32_or_64_t<T>>(value); auto width = static_cast<uint32_or_64_or_128_t<T>>(value);
if (internal::is_negative(value)) { if (internal::is_negative(value)) {
specs_.align = align::left; specs_.align = align::left;
width = 0 - width; width = 0 - width;
} }
unsigned int_max = std::numeric_limits<int>::max(); unsigned int_max = max_value<int>();
if (width > int_max) FMT_THROW(format_error("number is too big")); if (width > int_max) FMT_THROW(format_error("number is too big"));
return static_cast<unsigned>(width); return static_cast<unsigned>(width);
} }
@ -235,7 +231,7 @@ class printf_arg_formatter : public internal::arg_formatter_base<Range> {
printf_arg_formatter(iterator iter, format_specs& specs, context_type& ctx) printf_arg_formatter(iterator iter, format_specs& specs, context_type& ctx)
: base(Range(iter), &specs, internal::locale_ref()), context_(ctx) {} : base(Range(iter), &specs, internal::locale_ref()), context_(ctx) {}
template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)> template <typename T, FMT_ENABLE_IF(fmt::internal::is_integral<T>::value)>
iterator operator()(T value) { iterator 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.
@ -332,14 +328,14 @@ template <typename OutputIt, typename Char> class basic_printf_context {
OutputIt out_; OutputIt out_;
basic_format_args<basic_printf_context> args_; basic_format_args<basic_printf_context> args_;
basic_parse_context<Char> parse_ctx_; basic_format_parse_context<Char> parse_ctx_;
static void parse_flags(format_specs& specs, const Char*& it, static void parse_flags(format_specs& specs, const Char*& it,
const Char* end); 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 = internal::max_value<unsigned>());
// 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& specs); unsigned parse_header(const Char*& it, const Char* end, format_specs& specs);
@ -361,15 +357,14 @@ template <typename OutputIt, typename Char> class basic_printf_context {
format_arg arg(unsigned id) const { return args_.get(id); } format_arg arg(unsigned id) const { return args_.get(id); }
basic_parse_context<Char>& parse_context() { return parse_ctx_; } basic_format_parse_context<Char>& parse_context() { return parse_ctx_; }
FMT_CONSTEXPR void on_error(const char* message) { FMT_CONSTEXPR void on_error(const char* message) {
parse_ctx_.on_error(message); parse_ctx_.on_error(message);
} }
/** Formats stored arguments and writes the output to the range. */ /** Formats stored arguments and writes the output to the range. */
template <typename ArgFormatter = template <typename ArgFormatter = printf_arg_formatter<buffer_range<Char>>>
printf_arg_formatter<internal::buffer_range<Char>>>
OutputIt format(); OutputIt format();
}; };
@ -403,7 +398,7 @@ void basic_printf_context<OutputIt, Char>::parse_flags(format_specs& specs,
template <typename OutputIt, typename Char> template <typename OutputIt, typename Char>
typename basic_printf_context<OutputIt, Char>::format_arg typename basic_printf_context<OutputIt, Char>::format_arg
basic_printf_context<OutputIt, Char>::get_arg(unsigned arg_index) { basic_printf_context<OutputIt, Char>::get_arg(unsigned arg_index) {
if (arg_index == std::numeric_limits<unsigned>::max()) if (arg_index == internal::max_value<unsigned>())
arg_index = parse_ctx_.next_arg_id(); arg_index = parse_ctx_.next_arg_id();
else else
parse_ctx_.check_arg_id(--arg_index); parse_ctx_.check_arg_id(--arg_index);
@ -413,7 +408,7 @@ basic_printf_context<OutputIt, Char>::get_arg(unsigned arg_index) {
template <typename OutputIt, typename Char> template <typename OutputIt, typename Char>
unsigned basic_printf_context<OutputIt, Char>::parse_header( unsigned basic_printf_context<OutputIt, Char>::parse_header(
const Char*& it, const Char* end, format_specs& specs) { const Char*& it, const Char* end, format_specs& specs) {
unsigned arg_index = std::numeric_limits<unsigned>::max(); unsigned arg_index = internal::max_value<unsigned>();
char_type c = *it; char_type c = *it;
if (c >= '0' && c <= '9') { if (c >= '0' && c <= '9') {
// Parse an argument index (if followed by '$') or a width possibly // Parse an argument index (if followed by '$') or a width possibly
@ -470,6 +465,7 @@ OutputIt basic_printf_context<OutputIt, Char>::format() {
// Parse argument index, flags and width. // Parse argument index, flags and width.
unsigned arg_index = parse_header(it, end, specs); unsigned arg_index = parse_header(it, end, specs);
if (arg_index == 0) on_error("argument index out of range");
// Parse precision. // Parse precision.
if (it != end && *it == '.') { if (it != end && *it == '.') {

View File

@ -246,7 +246,8 @@ template <typename T, typename Char> struct is_range {
static FMT_CONSTEXPR_DECL const bool value = static FMT_CONSTEXPR_DECL const bool value =
internal::is_range_<T>::value && internal::is_range_<T>::value &&
!internal::is_like_std_string<T>::value && !internal::is_like_std_string<T>::value &&
!std::is_convertible<T, std::basic_string<Char>>::value; !std::is_convertible<T, std::basic_string<Char>>::value &&
!std::is_constructible<internal::std_string_view<Char>, T>::value;
}; };
template <typename RangeT, typename Char> template <typename RangeT, typename Char>
@ -283,6 +284,82 @@ struct formatter<RangeT, Char,
} }
}; };
template <typename Char, typename... T> struct tuple_arg_join : internal::view {
const std::tuple<T...>& tuple;
basic_string_view<Char> sep;
tuple_arg_join(const std::tuple<T...>& t, basic_string_view<Char> s)
: tuple{t}, sep{s} {}
};
template <typename Char, typename... T>
struct formatter<tuple_arg_join<Char, T...>, Char> {
template <typename ParseContext>
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
return ctx.begin();
}
template <typename FormatContext>
typename FormatContext::iterator format(
const tuple_arg_join<Char, T...>& value, FormatContext& ctx) {
return format(value, ctx, internal::make_index_sequence<sizeof...(T)>{});
}
private:
template <typename FormatContext, size_t... N>
typename FormatContext::iterator format(
const tuple_arg_join<Char, T...>& value, FormatContext& ctx,
internal::index_sequence<N...>) {
return format_args(value, ctx, std::get<N>(value.tuple)...);
}
template <typename FormatContext>
typename FormatContext::iterator format_args(
const tuple_arg_join<Char, T...>&, FormatContext& ctx) {
// NOTE: for compilers that support C++17, this empty function instantiation
// can be replaced with a constexpr branch in the variadic overload.
return ctx.out();
}
template <typename FormatContext, typename Arg, typename... Args>
typename FormatContext::iterator format_args(
const tuple_arg_join<Char, T...>& value, FormatContext& ctx,
const Arg& arg, const Args&... args) {
using base = formatter<typename std::decay<Arg>::type, Char>;
auto out = ctx.out();
out = base{}.format(arg, ctx);
if (sizeof...(Args) > 0) {
out = std::copy(value.sep.begin(), value.sep.end(), out);
ctx.advance_to(out);
return format_args(value, ctx, args...);
}
return out;
}
};
/**
\rst
Returns an object that formats `tuple` with elements separated by `sep`.
**Example**::
std::tuple<int, char> t = {1, 'a'};
fmt::print("{}", fmt::join(t, ", "));
// Output: "1, a"
\endrst
*/
template <typename... T>
FMT_CONSTEXPR tuple_arg_join<char, T...> join(const std::tuple<T...>& tuple,
string_view sep) {
return {tuple, sep};
}
template <typename... T>
FMT_CONSTEXPR tuple_arg_join<wchar_t, T...> join(const std::tuple<T...>& tuple,
wstring_view sep) {
return {tuple, sep};
}
FMT_END_NAMESPACE FMT_END_NAMESPACE
#endif // FMT_RANGES_H_ #endif // FMT_RANGES_H_

View File

@ -1,293 +0,0 @@
/*
* For conversion between std::chrono::durations without undefined
* behaviour or erroneous results.
* This is a stripped down version of duration_cast, for inclusion in fmt.
* See https://github.com/pauldreik/safe_duration_cast
*
* Copyright Paul Dreik 2019
*
* This file is licensed under the fmt license, see format.h
*/
#include <chrono>
#include <cmath>
#include <limits>
#include <type_traits>
#include "format.h"
FMT_BEGIN_NAMESPACE
namespace safe_duration_cast {
template <typename To, typename From,
FMT_ENABLE_IF(!std::is_same<From, To>::value &&
std::numeric_limits<From>::is_signed ==
std::numeric_limits<To>::is_signed)>
FMT_CONSTEXPR To lossless_integral_conversion(const From from, int& ec) {
ec = 0;
using F = std::numeric_limits<From>;
using T = std::numeric_limits<To>;
static_assert(F::is_integer, "From must be integral");
static_assert(T::is_integer, "To must be integral");
// A and B are both signed, or both unsigned.
if (F::digits <= T::digits) {
// From fits in To without any problem.
} else {
// From does not always fit in To, resort to a dynamic check.
if (from < T::min() || from > T::max()) {
// outside range.
ec = 1;
return {};
}
}
return static_cast<To>(from);
}
/**
* converts From to To, without loss. If the dynamic value of from
* can't be converted to To without loss, ec is set.
*/
template <typename To, typename From,
FMT_ENABLE_IF(!std::is_same<From, To>::value &&
std::numeric_limits<From>::is_signed !=
std::numeric_limits<To>::is_signed)>
FMT_CONSTEXPR To lossless_integral_conversion(const From from, int& ec) {
ec = 0;
using F = std::numeric_limits<From>;
using T = std::numeric_limits<To>;
static_assert(F::is_integer, "From must be integral");
static_assert(T::is_integer, "To must be integral");
if (F::is_signed && !T::is_signed) {
// From may be negative, not allowed!
if (from < 0) {
ec = 1;
return {};
}
// From is positive. Can it always fit in To?
if (F::digits <= T::digits) {
// yes, From always fits in To.
} else {
// from may not fit in To, we have to do a dynamic check
if (from > static_cast<From>(T::max())) {
ec = 1;
return {};
}
}
}
if (!F::is_signed && T::is_signed) {
// can from be held in To?
if (F::digits < T::digits) {
// yes, From always fits in To.
} else {
// from may not fit in To, we have to do a dynamic check
if (from > static_cast<From>(T::max())) {
// outside range.
ec = 1;
return {};
}
}
}
// reaching here means all is ok for lossless conversion.
return static_cast<To>(from);
} // function
template <typename To, typename From,
FMT_ENABLE_IF(std::is_same<From, To>::value)>
FMT_CONSTEXPR To lossless_integral_conversion(const From from, int& ec) {
ec = 0;
return from;
} // function
// clang-format off
/**
* converts From to To if possible, otherwise ec is set.
*
* input | output
* ---------------------------------|---------------
* NaN | NaN
* Inf | Inf
* normal, fits in output | converted (possibly lossy)
* normal, does not fit in output | ec is set
* subnormal | best effort
* -Inf | -Inf
*/
// clang-format on
template <typename To, typename From,
FMT_ENABLE_IF(!std::is_same<From, To>::value)>
FMT_CONSTEXPR To safe_float_conversion(const From from, int& ec) {
ec = 0;
using T = std::numeric_limits<To>;
static_assert(std::is_floating_point<From>::value, "From must be floating");
static_assert(std::is_floating_point<To>::value, "To must be floating");
// catch the only happy case
if (std::isfinite(from)) {
if (from >= T::lowest() && from <= T::max()) {
return static_cast<To>(from);
}
// not within range.
ec = 1;
return {};
}
// nan and inf will be preserved
return static_cast<To>(from);
} // function
template <typename To, typename From,
FMT_ENABLE_IF(std::is_same<From, To>::value)>
FMT_CONSTEXPR To safe_float_conversion(const From from, int& ec) {
ec = 0;
static_assert(std::is_floating_point<From>::value, "From must be floating");
return from;
}
/**
* safe duration cast between integral durations
*/
template <typename To, typename FromRep, typename FromPeriod,
FMT_ENABLE_IF(std::is_integral<FromRep>::value),
FMT_ENABLE_IF(std::is_integral<typename To::rep>::value)>
To safe_duration_cast(std::chrono::duration<FromRep, FromPeriod> from,
int& ec) {
using From = std::chrono::duration<FromRep, FromPeriod>;
ec = 0;
// the basic idea is that we need to convert from count() in the from type
// to count() in the To type, by multiplying it with this:
using Factor = std::ratio_divide<typename From::period, typename To::period>;
static_assert(Factor::num > 0, "num must be positive");
static_assert(Factor::den > 0, "den must be positive");
// the conversion is like this: multiply from.count() with Factor::num
// /Factor::den and convert it to To::rep, all this without
// overflow/underflow. let's start by finding a suitable type that can hold
// both To, From and Factor::num
using IntermediateRep =
typename std::common_type<typename From::rep, typename To::rep,
decltype(Factor::num)>::type;
// safe conversion to IntermediateRep
IntermediateRep count =
lossless_integral_conversion<IntermediateRep>(from.count(), ec);
if (ec) {
return {};
}
// multiply with Factor::num without overflow or underflow
if (Factor::num != 1) {
constexpr auto max1 =
std::numeric_limits<IntermediateRep>::max() / Factor::num;
if (count > max1) {
ec = 1;
return {};
}
constexpr auto min1 =
std::numeric_limits<IntermediateRep>::min() / Factor::num;
if (count < min1) {
ec = 1;
return {};
}
count *= Factor::num;
}
// this can't go wrong, right? den>0 is checked earlier.
if (Factor::den != 1) {
count /= Factor::den;
}
// convert to the to type, safely
using ToRep = typename To::rep;
const ToRep tocount = lossless_integral_conversion<ToRep>(count, ec);
if (ec) {
return {};
}
return To{tocount};
}
/**
* safe duration_cast between floating point durations
*/
template <typename To, typename FromRep, typename FromPeriod,
FMT_ENABLE_IF(std::is_floating_point<FromRep>::value),
FMT_ENABLE_IF(std::is_floating_point<typename To::rep>::value)>
To safe_duration_cast(std::chrono::duration<FromRep, FromPeriod> from,
int& ec) {
using From = std::chrono::duration<FromRep, FromPeriod>;
ec = 0;
if (std::isnan(from.count())) {
// nan in, gives nan out. easy.
return To{std::numeric_limits<typename To::rep>::quiet_NaN()};
}
// maybe we should also check if from is denormal, and decide what to do about
// it.
// +-inf should be preserved.
if (std::isinf(from.count())) {
return To{from.count()};
}
// the basic idea is that we need to convert from count() in the from type
// to count() in the To type, by multiplying it with this:
using Factor = std::ratio_divide<typename From::period, typename To::period>;
static_assert(Factor::num > 0, "num must be positive");
static_assert(Factor::den > 0, "den must be positive");
// the conversion is like this: multiply from.count() with Factor::num
// /Factor::den and convert it to To::rep, all this without
// overflow/underflow. let's start by finding a suitable type that can hold
// both To, From and Factor::num
using IntermediateRep =
typename std::common_type<typename From::rep, typename To::rep,
decltype(Factor::num)>::type;
// force conversion of From::rep -> IntermediateRep to be safe,
// even if it will never happen be narrowing in this context.
IntermediateRep count =
safe_float_conversion<IntermediateRep>(from.count(), ec);
if (ec) {
return {};
}
// multiply with Factor::num without overflow or underflow
if (Factor::num != 1) {
constexpr auto max1 = std::numeric_limits<IntermediateRep>::max() /
static_cast<IntermediateRep>(Factor::num);
if (count > max1) {
ec = 1;
return {};
}
constexpr auto min1 = std::numeric_limits<IntermediateRep>::lowest() /
static_cast<IntermediateRep>(Factor::num);
if (count < min1) {
ec = 1;
return {};
}
count *= static_cast<IntermediateRep>(Factor::num);
}
// this can't go wrong, right? den>0 is checked earlier.
if (Factor::den != 1) {
using common_t = typename std::common_type<IntermediateRep, intmax_t>::type;
count /= static_cast<common_t>(Factor::den);
}
// convert to the to type, safely
using ToRep = typename To::rep;
const ToRep tocount = safe_float_conversion<ToRep>(count, ec);
if (ec) {
return {};
}
return To{tocount};
}
} // namespace safe_duration_cast
FMT_END_NAMESPACE

View File

@ -9,27 +9,60 @@
#if !defined(SPDLOG_FMT_EXTERNAL) #if !defined(SPDLOG_FMT_EXTERNAL)
#include "spdlog/fmt/bundled/format-inl.h" #include "spdlog/fmt/bundled/format-inl.h"
FMT_BEGIN_NAMESPACE FMT_BEGIN_NAMESPACE
template struct internal::basic_data<void>; template struct FMT_API internal::basic_data<void>;
template FMT_API internal::locale_ref::locale_ref(const std::locale &loc);
// Workaround a bug in MSVC2013 that prevents instantiation of format_float.
int (*instantiate_format_float)(double, int, internal::float_specs,
internal::buffer<char>&) =
internal::format_float;
#ifndef FMT_STATIC_THOUSANDS_SEPARATOR
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;
#endif
// Explicit instantiations for char. // Explicit instantiations for char.
template FMT_API std::string internal::grouping_impl<char>(locale_ref);
template FMT_API char internal::thousands_sep_impl(locale_ref); template FMT_API char internal::thousands_sep_impl(locale_ref);
template FMT_API char internal::decimal_point_impl(locale_ref); template FMT_API char internal::decimal_point_impl(locale_ref);
template FMT_API void internal::buffer<char>::append(const char *, const char *);
template FMT_API void internal::arg_map<format_context>::init(const basic_format_args<format_context> &args); template FMT_API void internal::buffer<char>::append(const char*, const char*);
template FMT_API std::string internal::vformat<char>(string_view, basic_format_args<format_context>);
template FMT_API format_context::iterator internal::vformat_to(internal::buffer<char> &, string_view, basic_format_args<format_context>); template FMT_API void internal::arg_map<format_context>::init(
template FMT_API char *internal::sprintf_format(double, internal::buffer<char> &, sprintf_specs); const basic_format_args<format_context>& args);
template FMT_API char *internal::sprintf_format(long double, internal::buffer<char> &, sprintf_specs);
template FMT_API std::string internal::vformat<char>(
string_view, basic_format_args<format_context>);
template FMT_API format_context::iterator internal::vformat_to(
internal::buffer<char>&, string_view, basic_format_args<format_context>);
template FMT_API int internal::snprintf_float(double, int,
internal::float_specs,
internal::buffer<char>&);
template FMT_API int internal::snprintf_float(long double, int,
internal::float_specs,
internal::buffer<char>&);
template FMT_API int internal::format_float(double, int, internal::float_specs,
internal::buffer<char>&);
template FMT_API int internal::format_float(long double, int,
internal::float_specs,
internal::buffer<char>&);
// Explicit instantiations for wchar_t. // Explicit instantiations for wchar_t.
template FMT_API std::string internal::grouping_impl<wchar_t>(locale_ref);
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 wchar_t internal::decimal_point_impl(locale_ref); template FMT_API wchar_t internal::decimal_point_impl(locale_ref);
template FMT_API void internal::buffer<wchar_t>::append(const wchar_t *, const wchar_t *);
template FMT_API void internal::arg_map<wformat_context>::init(const basic_format_args<wformat_context> &); template FMT_API void internal::buffer<wchar_t>::append(const wchar_t*,
template FMT_API std::wstring internal::vformat<wchar_t>(wstring_view, basic_format_args<wformat_context>); const wchar_t*);
template FMT_API std::wstring internal::vformat<wchar_t>(
wstring_view, basic_format_args<wformat_context>);
FMT_END_NAMESPACE FMT_END_NAMESPACE
#endif #endif