Merge branch 'upstream-diy' into add-update-diy

# By Diy Upstream
* upstream-diy:
  diy 2021-05-26 (b21d798e)
This commit is contained in:
Vicente Adolfo Bolea Sanchez 2021-06-01 10:33:30 -04:00
commit 9c267d1770
12 changed files with 7632 additions and 3884 deletions

@ -8,35 +8,285 @@
#ifndef FMT_CHRONO_H_
#define FMT_CHRONO_H_
#include "format.h"
#include "locale.h"
#include <chrono>
#include <ctime>
#include <locale>
#include <sstream>
// enable safe chrono durations, unless explicitly disabled
#include "format.h"
#include "locale.h"
FMT_BEGIN_NAMESPACE
// Enable safe chrono durations, unless explicitly disabled.
#ifndef FMT_SAFE_DURATION_CAST
# define FMT_SAFE_DURATION_CAST 1
#endif
#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 (detail::const_check(F::is_signed && !T::is_signed)) {
// From may be negative, not allowed!
if (fmt::detail::is_negative(from)) {
ec = 1;
return {};
}
// From is positive. Can it always fit in To?
if (F::digits > T::digits &&
from > static_cast<From>(detail::max_value<To>())) {
ec = 1;
return {};
}
}
if (!F::is_signed && T::is_signed && F::digits >= T::digits &&
from > static_cast<From>(detail::max_value<To>())) {
ec = 1;
return {};
}
return static_cast<To>(from); // Lossless conversion.
}
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 (detail::const_check(Factor::num != 1)) {
const auto max1 = detail::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;
}
if (detail::const_check(Factor::den != 1)) count /= Factor::den;
auto tocount = lossless_integral_conversion<typename To::rep>(count, ec);
return ec ? To() : 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 = detail::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.
// Usage: f FMT_NOMACRO()
#define FMT_NOMACRO
namespace internal {
namespace detail {
inline null<> localtime_r FMT_NOMACRO(...) { return null<>(); }
inline null<> localtime_s(...) { return null<>(); }
inline null<> gmtime_r(...) { return null<>(); }
inline null<> gmtime_s(...) { return null<>(); }
} // namespace internal
} // namespace detail
// Thread-safe replacement for std::localtime
inline std::tm localtime(std::time_t time) {
@ -47,22 +297,22 @@ inline std::tm localtime(std::time_t time) {
dispatcher(std::time_t t) : time_(t) {}
bool run() {
using namespace fmt::internal;
using namespace fmt::detail;
return handle(localtime_r(&time_, &tm_));
}
bool handle(std::tm* tm) { return tm != nullptr; }
bool handle(internal::null<>) {
using namespace fmt::internal;
bool handle(detail::null<>) {
using namespace fmt::detail;
return fallback(localtime_s(&tm_, &time_));
}
bool fallback(int res) { return res == 0; }
#if !FMT_MSC_VER
bool fallback(internal::null<>) {
using namespace fmt::internal;
bool fallback(detail::null<>) {
using namespace fmt::detail;
std::tm* tm = std::localtime(&time_);
if (tm) tm_ = *tm;
return tm != nullptr;
@ -75,6 +325,11 @@ inline std::tm localtime(std::time_t time) {
return lt.tm_;
}
inline std::tm localtime(
std::chrono::time_point<std::chrono::system_clock> time_point) {
return localtime(std::chrono::system_clock::to_time_t(time_point));
}
// Thread-safe replacement for std::gmtime
inline std::tm gmtime(std::time_t time) {
struct dispatcher {
@ -84,21 +339,21 @@ inline std::tm gmtime(std::time_t time) {
dispatcher(std::time_t t) : time_(t) {}
bool run() {
using namespace fmt::internal;
using namespace fmt::detail;
return handle(gmtime_r(&time_, &tm_));
}
bool handle(std::tm* tm) { return tm != nullptr; }
bool handle(internal::null<>) {
using namespace fmt::internal;
bool handle(detail::null<>) {
using namespace fmt::detail;
return fallback(gmtime_s(&tm_, &time_));
}
bool fallback(int res) { return res == 0; }
#if !FMT_MSC_VER
bool fallback(internal::null<>) {
bool fallback(detail::null<>) {
std::tm* tm = std::gmtime(&time_);
if (tm) tm_ = *tm;
return tm != nullptr;
@ -111,17 +366,33 @@ inline std::tm gmtime(std::time_t time) {
return gt.tm_;
}
namespace internal {
inline std::size_t strftime(char* str, std::size_t count, const char* format,
const std::tm* time) {
inline std::tm gmtime(
std::chrono::time_point<std::chrono::system_clock> time_point) {
return gmtime(std::chrono::system_clock::to_time_t(time_point));
}
namespace detail {
inline size_t strftime(char* str, size_t count, const char* format,
const std::tm* time) {
return std::strftime(str, count, format, time);
}
inline std::size_t strftime(wchar_t* str, std::size_t count,
const wchar_t* format, const std::tm* time) {
inline size_t strftime(wchar_t* str, size_t count, const wchar_t* format,
const std::tm* time) {
return std::wcsftime(str, count, format, time);
}
} // namespace internal
} // namespace detail
template <typename Char>
struct formatter<std::chrono::time_point<std::chrono::system_clock>, Char>
: formatter<std::tm, Char> {
template <typename FormatContext>
auto format(std::chrono::time_point<std::chrono::system_clock> val,
FormatContext& ctx) -> decltype(ctx.out()) {
std::tm time = localtime(val);
return formatter<std::tm, Char>::format(time, ctx);
}
};
template <typename Char> struct formatter<std::tm, Char> {
template <typename ParseContext>
@ -130,7 +401,7 @@ template <typename Char> struct formatter<std::tm, Char> {
if (it != ctx.end() && *it == ':') ++it;
auto end = it;
while (end != ctx.end() && *end != '}') ++end;
tm_format.reserve(internal::to_unsigned(end - it + 1));
tm_format.reserve(detail::to_unsigned(end - it + 1));
tm_format.append(it, end);
tm_format.push_back('\0');
return end;
@ -139,11 +410,10 @@ template <typename Char> struct formatter<std::tm, Char> {
template <typename FormatContext>
auto format(const std::tm& tm, FormatContext& ctx) -> decltype(ctx.out()) {
basic_memory_buffer<Char> buf;
std::size_t start = buf.size();
size_t start = buf.size();
for (;;) {
std::size_t size = buf.capacity() - start;
std::size_t count =
internal::strftime(&buf[start], size, &tm_format[0], &tm);
size_t size = buf.capacity() - start;
size_t count = detail::strftime(&buf[start], size, &tm_format[0], &tm);
if (count != 0) {
buf.resize(start + count);
break;
@ -155,7 +425,7 @@ template <typename Char> struct formatter<std::tm, Char> {
// https://github.com/fmtlib/fmt/issues/367
break;
}
const std::size_t MIN_GROWTH = 10;
const size_t MIN_GROWTH = 10;
buf.reserve(buf.capacity() + (size > MIN_GROWTH ? size : MIN_GROWTH));
}
return std::copy(buf.begin(), buf.end(), ctx.out());
@ -164,7 +434,7 @@ template <typename Char> struct formatter<std::tm, Char> {
basic_memory_buffer<Char> tm_format;
};
namespace internal {
namespace detail {
template <typename Period> FMT_CONSTEXPR const char* get_units() {
return nullptr;
}
@ -220,12 +490,12 @@ FMT_CONSTEXPR const Char* parse_chrono_format(const Char* begin,
handler.on_text(ptr - 1, ptr);
break;
case 'n': {
const char newline[] = "\n";
const Char newline[] = {'\n'};
handler.on_text(newline, newline + 1);
break;
}
case 't': {
const char tab[] = "\t";
const Char tab[] = {'\t'};
handler.on_text(tab, tab + 1);
break;
}
@ -421,7 +691,7 @@ inline int to_nonnegative_int(T value, int upper) {
template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
inline T mod(T x, int y) {
return x % y;
return x % static_cast<T>(y);
}
template <typename T, FMT_ENABLE_IF(std::is_floating_point<T>::value)>
inline T mod(T x, int y) {
@ -484,18 +754,36 @@ inline std::chrono::duration<Rep, std::milli> get_milliseconds(
return std::chrono::duration<Rep, std::milli>(static_cast<Rep>(ms));
}
template <typename Rep, typename OutputIt>
OutputIt format_chrono_duration_value(OutputIt out, Rep val, int precision) {
if (precision >= 0) return format_to(out, "{:.{}f}", val, precision);
return format_to(out, std::is_floating_point<Rep>::value ? "{:g}" : "{}",
template <typename Char, typename Rep, typename OutputIt>
OutputIt format_duration_value(OutputIt out, Rep val, int precision) {
const Char pr_f[] = {'{', ':', '.', '{', '}', 'f', '}', 0};
if (precision >= 0) return format_to(out, pr_f, val, precision);
const Char fp_f[] = {'{', ':', 'g', '}', 0};
const Char format[] = {'{', '}', 0};
return format_to(out, std::is_floating_point<Rep>::value ? fp_f : format,
val);
}
template <typename Char, typename OutputIt>
OutputIt copy_unit(string_view unit, OutputIt out, Char) {
return std::copy(unit.begin(), unit.end(), out);
}
template <typename Period, typename OutputIt>
static OutputIt format_chrono_duration_unit(OutputIt out) {
if (const char* unit = get_units<Period>()) return format_to(out, "{}", unit);
if (Period::den == 1) return format_to(out, "[{}]s", Period::num);
return format_to(out, "[{}/{}]s", Period::num, Period::den);
template <typename OutputIt>
OutputIt copy_unit(string_view unit, OutputIt out, wchar_t) {
// This works when wchar_t is UTF-32 because units only contain characters
// that have the same representation in UTF-16 and UTF-32.
utf8_to_utf16 u(unit);
return std::copy(u.c_str(), u.c_str() + u.size(), out);
}
template <typename Char, typename Period, typename OutputIt>
OutputIt format_duration_unit(OutputIt out) {
if (const char* unit = get_units<Period>())
return copy_unit(string_view(unit), out, Char());
const Char num_f[] = {'[', '{', '}', ']', 's', 0};
if (const_check(Period::den == 1)) return format_to(out, num_f, Period::num);
const Char num_def_f[] = {'[', '{', '}', '/', '{', '}', ']', 's', 0};
return format_to(out, num_def_f, Period::num, Period::den);
}
template <typename FormatContext, typename OutputIt, typename Rep,
@ -518,7 +806,10 @@ struct chrono_formatter {
explicit chrono_formatter(FormatContext& ctx, OutputIt o,
std::chrono::duration<Rep, Period> d)
: context(ctx), out(o), val(d.count()), negative(false) {
: context(ctx),
out(o),
val(static_cast<rep>(d.count())),
negative(false) {
if (d.count() < 0) {
val = 0 - val;
negative = true;
@ -582,24 +873,24 @@ struct chrono_formatter {
void write(Rep value, int width) {
write_sign();
if (isnan(value)) return write_nan();
uint32_or_64_t<int> n = to_unsigned(
to_nonnegative_int(value, (std::numeric_limits<int>::max)()));
int num_digits = internal::count_digits(n);
uint32_or_64_or_128_t<int> n =
to_unsigned(to_nonnegative_int(value, max_value<int>()));
int num_digits = detail::count_digits(n);
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).end;
}
void write_nan() { std::copy_n("nan", 3, out); }
void write_pinf() { std::copy_n("inf", 3, out); }
void write_ninf() { std::copy_n("-inf", 4, out); }
void format_localized(const tm& time, const char* format) {
void format_localized(const tm& time, char format, char modifier = 0) {
if (isnan(val)) return write_nan();
auto locale = context.locale().template get<std::locale>();
auto& facet = std::use_facet<std::time_put<char_type>>(locale);
std::basic_ostringstream<char_type> os;
os.imbue(locale);
facet.put(os, os, ' ', &time, format, format + std::strlen(format));
facet.put(os, os, ' ', &time, format, modifier);
auto str = os.str();
std::copy(str.begin(), str.end(), out);
}
@ -629,7 +920,7 @@ struct chrono_formatter {
if (ns == numeric_system::standard) return write(hour(), 2);
auto time = tm();
time.tm_hour = to_nonnegative_int(hour(), 24);
format_localized(time, "%OH");
format_localized(time, 'H', 'O');
}
void on_12_hour(numeric_system ns) {
@ -638,7 +929,7 @@ struct chrono_formatter {
if (ns == numeric_system::standard) return write(hour12(), 2);
auto time = tm();
time.tm_hour = to_nonnegative_int(hour12(), 12);
format_localized(time, "%OI");
format_localized(time, 'I', 'O');
}
void on_minute(numeric_system ns) {
@ -647,7 +938,7 @@ struct chrono_formatter {
if (ns == numeric_system::standard) return write(minute(), 2);
auto time = tm();
time.tm_min = to_nonnegative_int(minute(), 60);
format_localized(time, "%OM");
format_localized(time, 'M', 'O');
}
void on_second(numeric_system ns) {
@ -672,13 +963,12 @@ struct chrono_formatter {
}
auto time = tm();
time.tm_sec = to_nonnegative_int(second(), 60);
format_localized(time, "%OS");
format_localized(time, 'S', 'O');
}
void on_12_hour_time() {
if (handle_nan_inf()) return;
format_localized(time(), "%r");
format_localized(time(), 'r');
}
void on_24_hour_time() {
@ -702,25 +992,27 @@ struct chrono_formatter {
void on_am_pm() {
if (handle_nan_inf()) return;
format_localized(time(), "%p");
format_localized(time(), 'p');
}
void on_duration_value() {
if (handle_nan_inf()) return;
write_sign();
out = format_chrono_duration_value(out, val, precision);
out = format_duration_value<char_type>(out, val, precision);
}
void on_duration_unit() { out = format_chrono_duration_unit<Period>(out); }
void on_duration_unit() {
out = format_duration_unit<char_type, Period>(out);
}
};
} // namespace internal
} // namespace detail
template <typename Rep, typename Period, typename Char>
struct formatter<std::chrono::duration<Rep, Period>, Char> {
private:
basic_format_specs<Char> specs;
int precision;
using arg_ref_type = internal::arg_ref<Char>;
using arg_ref_type = detail::arg_ref<Char>;
arg_ref_type width_ref;
arg_ref_type precision_ref;
mutable basic_string_view<Char> format_str;
@ -728,7 +1020,7 @@ struct formatter<std::chrono::duration<Rep, Period>, Char> {
struct spec_handler {
formatter& f;
basic_parse_context<Char>& context;
basic_format_parse_context<Char>& context;
basic_string_view<Char> format_str;
template <typename Id> FMT_CONSTEXPR arg_ref_type make_arg_ref(Id arg_id) {
@ -738,19 +1030,18 @@ struct formatter<std::chrono::duration<Rep, Period>, Char> {
FMT_CONSTEXPR arg_ref_type make_arg_ref(basic_string_view<Char> arg_id) {
context.check_arg_id(arg_id);
const auto str_val = internal::string_view_metadata(format_str, arg_id);
return arg_ref_type(str_val);
return arg_ref_type(arg_id);
}
FMT_CONSTEXPR arg_ref_type make_arg_ref(internal::auto_id) {
FMT_CONSTEXPR arg_ref_type make_arg_ref(detail::auto_id) {
return arg_ref_type(context.next_arg_id());
}
void on_error(const char* msg) { FMT_THROW(format_error(msg)); }
void on_fill(Char fill) { f.specs.fill[0] = fill; }
void on_fill(basic_string_view<Char> fill) { f.specs.fill = fill; }
void on_align(align_t align) { f.specs.align = align; }
void on_width(unsigned width) { f.specs.width = width; }
void on_precision(unsigned precision) { f.precision = precision; }
void on_width(int width) { f.specs.width = width; }
void on_precision(int _precision) { f.precision = _precision; }
void end_precision() {}
template <typename Id> void on_dynamic_width(Id arg_id) {
@ -762,38 +1053,38 @@ 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 {
iterator begin;
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();
if (begin == end || *begin == '}') return {begin, begin};
spec_handler handler{*this, ctx, format_str};
begin = internal::parse_align(begin, end, handler);
begin = detail::parse_align(begin, end, handler);
if (begin == end) return {begin, begin};
begin = internal::parse_width(begin, end, handler);
begin = detail::parse_width(begin, end, handler);
if (begin == end) return {begin, begin};
if (*begin == '.') {
if (std::is_floating_point<Rep>::value)
begin = internal::parse_precision(begin, end, handler);
begin = detail::parse_precision(begin, end, handler);
else
handler.on_error("precision not allowed for this argument type");
}
end = parse_chrono_format(begin, end, internal::chrono_format_checker());
end = parse_chrono_format(begin, end, detail::chrono_format_checker());
return {begin, end};
}
public:
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()) {
auto range = do_parse(ctx);
format_str = basic_string_view<Char>(
&*range.begin, internal::to_unsigned(range.end - range.begin));
&*range.begin, detail::to_unsigned(range.end - range.begin));
return range.end;
}
@ -804,23 +1095,21 @@ struct formatter<std::chrono::duration<Rep, Period>, Char> {
// is not specified.
basic_memory_buffer<Char> buf;
auto out = std::back_inserter(buf);
using range = internal::output_range<decltype(ctx.out()), Char>;
internal::basic_writer<range> w(range(ctx.out()));
internal::handle_dynamic_spec<internal::width_checker>(
specs.width, width_ref, ctx, format_str.begin());
internal::handle_dynamic_spec<internal::precision_checker>(
precision, precision_ref, ctx, format_str.begin());
detail::handle_dynamic_spec<detail::width_checker>(specs.width, width_ref,
ctx);
detail::handle_dynamic_spec<detail::precision_checker>(precision,
precision_ref, ctx);
if (begin == end || *begin == '}') {
out = internal::format_chrono_duration_value(out, d.count(), precision);
internal::format_chrono_duration_unit<Period>(out);
out = detail::format_duration_value<Char>(out, d.count(), precision);
detail::format_duration_unit<Char, Period>(out);
} else {
internal::chrono_formatter<FormatContext, decltype(out), Rep, Period> f(
detail::chrono_formatter<FormatContext, decltype(out), Rep, Period> f(
ctx, out, d);
f.precision = precision;
parse_chrono_format(begin, end, f);
}
w.write(buf.data(), buf.size(), specs);
return w.out();
return detail::write(
ctx.out(), basic_string_view<Char>(buf.data(), buf.size()), specs);
}
};

@ -198,7 +198,7 @@ struct rgb {
uint8_t b;
};
namespace internal {
namespace detail {
// color is a struct of either a rgb color or a terminal color.
struct color_type {
@ -221,7 +221,7 @@ struct color_type {
uint32_t rgb_color;
} value;
};
} // namespace internal
} // namespace detail
// Experimental text formatting support.
class text_style {
@ -298,22 +298,22 @@ class text_style {
FMT_CONSTEXPR bool has_emphasis() const FMT_NOEXCEPT {
return static_cast<uint8_t>(ems) != 0;
}
FMT_CONSTEXPR internal::color_type get_foreground() const FMT_NOEXCEPT {
assert(has_foreground() && "no foreground specified for this style");
FMT_CONSTEXPR detail::color_type get_foreground() const FMT_NOEXCEPT {
FMT_ASSERT(has_foreground(), "no foreground specified for this style");
return foreground_color;
}
FMT_CONSTEXPR internal::color_type get_background() const FMT_NOEXCEPT {
assert(has_background() && "no background specified for this style");
FMT_CONSTEXPR detail::color_type get_background() const FMT_NOEXCEPT {
FMT_ASSERT(has_background(), "no background specified for this style");
return background_color;
}
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;
}
private:
FMT_CONSTEXPR text_style(bool is_foreground,
internal::color_type text_color) FMT_NOEXCEPT
detail::color_type text_color) FMT_NOEXCEPT
: set_foreground_color(),
set_background_color(),
ems() {
@ -326,23 +326,23 @@ class text_style {
}
}
friend FMT_CONSTEXPR_DECL text_style fg(internal::color_type foreground)
friend FMT_CONSTEXPR_DECL text_style fg(detail::color_type foreground)
FMT_NOEXCEPT;
friend FMT_CONSTEXPR_DECL text_style bg(internal::color_type background)
friend FMT_CONSTEXPR_DECL text_style bg(detail::color_type background)
FMT_NOEXCEPT;
internal::color_type foreground_color;
internal::color_type background_color;
detail::color_type foreground_color;
detail::color_type background_color;
bool set_foreground_color;
bool set_background_color;
emphasis ems;
};
FMT_CONSTEXPR text_style fg(internal::color_type foreground) FMT_NOEXCEPT {
FMT_CONSTEXPR text_style fg(detail::color_type foreground) FMT_NOEXCEPT {
return text_style(/*is_foreground=*/true, foreground);
}
FMT_CONSTEXPR text_style bg(internal::color_type background) FMT_NOEXCEPT {
FMT_CONSTEXPR text_style bg(detail::color_type background) FMT_NOEXCEPT {
return text_style(/*is_foreground=*/false, background);
}
@ -350,21 +350,21 @@ FMT_CONSTEXPR text_style operator|(emphasis lhs, emphasis rhs) FMT_NOEXCEPT {
return text_style(lhs) | rhs;
}
namespace internal {
namespace detail {
template <typename Char> struct ansi_color_escape {
FMT_CONSTEXPR ansi_color_escape(internal::color_type text_color,
FMT_CONSTEXPR ansi_color_escape(detail::color_type text_color,
const char* esc) FMT_NOEXCEPT {
// If we have a terminal color, we need to output another escape code
// sequence.
if (!text_color.is_rgb) {
bool is_background = esc == internal::data::background_color;
bool is_background = esc == detail::data::background_color;
uint32_t value = text_color.value.term_color;
// Background ASCII codes are the same as the foreground ones but with
// 10 more.
if (is_background) value += 10u;
std::size_t index = 0;
size_t index = 0;
buffer[index++] = static_cast<Char>('\x1b');
buffer[index++] = static_cast<Char>('[');
@ -398,7 +398,7 @@ template <typename Char> struct ansi_color_escape {
if (em_bits & static_cast<uint8_t>(emphasis::strikethrough))
em_codes[3] = 9;
std::size_t index = 0;
size_t index = 0;
for (int i = 0; i < 4; ++i) {
if (!em_codes[i]) continue;
buffer[index++] = static_cast<Char>('\x1b');
@ -412,7 +412,7 @@ template <typename Char> struct ansi_color_escape {
FMT_CONSTEXPR const Char* begin() const FMT_NOEXCEPT { return buffer; }
FMT_CONSTEXPR const Char* end() const FMT_NOEXCEPT {
return buffer + std::strlen(buffer);
return buffer + std::char_traits<Char>::length(buffer);
}
private:
@ -429,14 +429,14 @@ template <typename Char> struct ansi_color_escape {
template <typename Char>
FMT_CONSTEXPR ansi_color_escape<Char> make_foreground_color(
internal::color_type foreground) FMT_NOEXCEPT {
return ansi_color_escape<Char>(foreground, internal::data::foreground_color);
detail::color_type foreground) FMT_NOEXCEPT {
return ansi_color_escape<Char>(foreground, detail::data::foreground_color);
}
template <typename Char>
FMT_CONSTEXPR ansi_color_escape<Char> make_background_color(
internal::color_type background) FMT_NOEXCEPT {
return ansi_color_escape<Char>(background, internal::data::background_color);
detail::color_type background) FMT_NOEXCEPT {
return ansi_color_escape<Char>(background, detail::data::background_color);
}
template <typename Char>
@ -455,90 +455,71 @@ inline void fputs<wchar_t>(const wchar_t* chars, FILE* stream) FMT_NOEXCEPT {
}
template <typename Char> inline void reset_color(FILE* stream) FMT_NOEXCEPT {
fputs(internal::data::reset_color, stream);
fputs(detail::data::reset_color, stream);
}
template <> inline void reset_color<wchar_t>(FILE* stream) FMT_NOEXCEPT {
fputs(internal::data::wreset_color, stream);
fputs(detail::data::wreset_color, stream);
}
template <typename Char>
inline void reset_color(basic_memory_buffer<Char>& buffer) FMT_NOEXCEPT {
inline void reset_color(buffer<Char>& buffer) FMT_NOEXCEPT {
const char* begin = data::reset_color;
const char* end = begin + sizeof(data::reset_color) - 1;
buffer.append(begin, end);
}
template <typename Char>
std::basic_string<Char> vformat(const text_style& ts,
basic_string_view<Char> format_str,
basic_format_args<buffer_context<Char> > args) {
basic_memory_buffer<Char> buffer;
void vformat_to(buffer<Char>& buf, const text_style& ts,
basic_string_view<Char> format_str,
basic_format_args<buffer_context<type_identity_t<Char>>> args) {
bool has_style = false;
if (ts.has_emphasis()) {
has_style = true;
ansi_color_escape<Char> escape = make_emphasis<Char>(ts.get_emphasis());
buffer.append(escape.begin(), escape.end());
auto emphasis = detail::make_emphasis<Char>(ts.get_emphasis());
buf.append(emphasis.begin(), emphasis.end());
}
if (ts.has_foreground()) {
has_style = true;
ansi_color_escape<Char> escape =
make_foreground_color<Char>(ts.get_foreground());
buffer.append(escape.begin(), escape.end());
auto foreground = detail::make_foreground_color<Char>(ts.get_foreground());
buf.append(foreground.begin(), foreground.end());
}
if (ts.has_background()) {
has_style = true;
ansi_color_escape<Char> escape =
make_background_color<Char>(ts.get_background());
buffer.append(escape.begin(), escape.end());
auto background = detail::make_background_color<Char>(ts.get_background());
buf.append(background.begin(), background.end());
}
internal::vformat_to(buffer, format_str, args);
if (has_style) {
reset_color<Char>(buffer);
}
return fmt::to_string(buffer);
detail::vformat_to(buf, format_str, args);
if (has_style) detail::reset_color<Char>(buf);
}
} // namespace internal
} // namespace detail
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,
basic_format_args<buffer_context<Char> > args) {
bool has_style = false;
if (ts.has_emphasis()) {
has_style = true;
internal::fputs<Char>(internal::make_emphasis<Char>(ts.get_emphasis()), 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);
}
basic_format_args<buffer_context<type_identity_t<Char>>> args) {
basic_memory_buffer<Char> buf;
detail::vformat_to(buf, ts, to_string_view(format), args);
buf.push_back(Char(0));
detail::fputs(buf.data(), f);
}
/**
\rst
Formats a string and prints it to the specified file stream using ANSI
escape sequences to specify text formatting.
Example:
**Example**::
fmt::print(fmt::emphasis::bold | fg(fmt::color::red),
"Elapsed time: {0:.2f} seconds", 1.23);
\endrst
*/
template <typename S, typename... Args,
FMT_ENABLE_IF(internal::is_string<S>::value)>
FMT_ENABLE_IF(detail::is_string<S>::value)>
void print(std::FILE* f, const text_style& ts, const S& format_str,
const Args&... args) {
internal::check_format_string<Args...>(format_str);
using context = buffer_context<char_t<S> >;
format_arg_store<context, Args...> as{args...};
vprint(f, ts, format_str, basic_format_args<context>(as));
vprint(f, ts, format_str,
fmt::make_args_checked<Args...>(format_str, args...));
}
/**
@ -549,16 +530,18 @@ void print(std::FILE* f, const text_style& ts, const S& format_str,
"Elapsed time: {0:.2f} seconds", 1.23);
*/
template <typename S, typename... Args,
FMT_ENABLE_IF(internal::is_string<S>::value)>
FMT_ENABLE_IF(detail::is_string<S>::value)>
void print(const text_style& ts, const S& format_str, const Args&... 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(
const text_style& ts, const S& format_str,
basic_format_args<buffer_context<Char> > args) {
return internal::vformat(ts, to_string_view(format_str), args);
basic_format_args<buffer_context<type_identity_t<Char>>> args) {
basic_memory_buffer<Char> buf;
detail::vformat_to(buf, ts, to_string_view(format_str), args);
return fmt::to_string(buf);
}
/**
@ -573,11 +556,46 @@ inline std::basic_string<Char> vformat(
"The answer is {}", 42);
\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,
const Args&... args) {
return internal::vformat(ts, to_string_view(format_str),
{internal::make_args_checked(format_str, args...)});
return vformat(ts, to_string_view(format_str),
fmt::make_args_checked<Args...>(format_str, args...));
}
/**
Formats a string with the given text_style and writes the output to ``out``.
*/
template <typename OutputIt, typename Char,
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value)>
OutputIt vformat_to(
OutputIt out, const text_style& ts, basic_string_view<Char> format_str,
basic_format_args<buffer_context<type_identity_t<Char>>> args) {
decltype(detail::get_buffer<Char>(out)) buf(detail::get_buffer_init(out));
detail::vformat_to(buf, ts, format_str, args);
return detail::get_iterator(buf);
}
/**
\rst
Formats arguments with the given text_style, writes the result to the output
iterator ``out`` and returns the iterator past the end of the output range.
**Example**::
std::vector<char> out;
fmt::format_to(std::back_inserter(out),
fmt::emphasis::bold | fg(fmt::color::red), "{}", 42);
\endrst
*/
template <typename OutputIt, typename S, typename... Args,
bool enable = detail::is_output_iterator<OutputIt, char_t<S>>::value&&
detail::is_string<S>::value>
inline auto format_to(OutputIt out, const text_style& ts, const S& format_str,
Args&&... args) ->
typename std::enable_if<enable, OutputIt>::type {
return vformat_to(out, ts, to_string_view(format_str),
fmt::make_args_checked<Args...>(format_str, args...));
}
FMT_END_NAMESPACE

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

@ -9,67 +9,54 @@
#define FMT_LOCALE_H_
#include <locale>
#include "format.h"
FMT_BEGIN_NAMESPACE
namespace internal {
namespace detail {
template <typename Char>
typename buffer_context<Char>::iterator vformat_to(
const std::locale& loc, buffer<Char>& buf,
basic_string_view<Char> format_str,
basic_format_args<buffer_context<Char>> args) {
using range = buffer_range<Char>;
return vformat_to<arg_formatter<range>>(buf, to_string_view(format_str), args,
internal::locale_ref(loc));
}
template <typename Char>
std::basic_string<Char> vformat(const std::locale& loc,
basic_string_view<Char> format_str,
basic_format_args<buffer_context<Char>> args) {
std::basic_string<Char> vformat(
const std::locale& loc, basic_string_view<Char> format_str,
basic_format_args<buffer_context<type_identity_t<Char>>> args) {
basic_memory_buffer<Char> buffer;
internal::vformat_to(loc, buffer, format_str, args);
detail::vformat_to(buffer, format_str, args, detail::locale_ref(loc));
return fmt::to_string(buffer);
}
} // namespace internal
} // namespace detail
template <typename S, typename Char = char_t<S>>
inline std::basic_string<Char> vformat(
const std::locale& loc, const S& format_str,
basic_format_args<buffer_context<Char>> args) {
return internal::vformat(loc, to_string_view(format_str), args);
basic_format_args<buffer_context<type_identity_t<Char>>> args) {
return detail::vformat(loc, to_string_view(format_str), args);
}
template <typename S, typename... Args, typename Char = char_t<S>>
inline std::basic_string<Char> format(const std::locale& loc,
const S& format_str, Args&&... args) {
return internal::vformat(
loc, to_string_view(format_str),
{internal::make_args_checked<Args...>(format_str, args...)});
return detail::vformat(loc, to_string_view(format_str),
fmt::make_args_checked<Args...>(format_str, args...));
}
template <typename S, typename OutputIt, typename... Args,
typename Char = enable_if_t<
internal::is_output_iterator<OutputIt>::value, char_t<S>>>
inline OutputIt vformat_to(OutputIt out, const std::locale& loc,
const S& format_str,
format_args_t<OutputIt, Char> args) {
using range = internal::output_range<OutputIt, Char>;
return vformat_to<arg_formatter<range>>(
range(out), to_string_view(format_str), args, internal::locale_ref(loc));
typename Char = char_t<S>,
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value)>
inline OutputIt vformat_to(
OutputIt out, const std::locale& loc, const S& format_str,
basic_format_args<buffer_context<type_identity_t<Char>>> args) {
decltype(detail::get_buffer<Char>(out)) buf(detail::get_buffer_init(out));
vformat_to(buf, to_string_view(format_str), args, detail::locale_ref(loc));
return detail::get_iterator(buf);
}
template <typename OutputIt, typename S, typename... Args,
FMT_ENABLE_IF(internal::is_output_iterator<OutputIt>::value&&
internal::is_string<S>::value)>
inline OutputIt format_to(OutputIt out, const std::locale& loc,
const S& format_str, Args&&... args) {
internal::check_format_string<Args...>(format_str);
using context = format_context_t<OutputIt, char_t<S>>;
format_arg_store<context, Args...> as{args...};
return vformat_to(out, loc, to_string_view(format_str),
basic_format_args<context>(as));
bool enable = detail::is_output_iterator<OutputIt, char_t<S>>::value>
inline auto format_to(OutputIt out, const std::locale& loc,
const S& format_str, Args&&... args) ->
typename std::enable_if<enable, OutputIt>::type {
const auto& vargs = fmt::make_args_checked<Args...>(format_str, args...);
return vformat_to(out, loc, to_string_view(format_str), vargs);
}
FMT_END_NAMESPACE

@ -0,0 +1,480 @@
// Formatting library for C++ - optional OS-specific functionality
//
// Copyright (c) 2012 - present, Victor Zverovich
// All rights reserved.
//
// For the license information refer to format.h.
#ifndef FMT_OS_H_
#define FMT_OS_H_
#if defined(__MINGW32__) || defined(__CYGWIN__)
// Workaround MinGW bug https://sourceforge.net/p/mingw/bugs/2024/.
# undef __STRICT_ANSI__
#endif
#include <cerrno>
#include <clocale> // for locale_t
#include <cstddef>
#include <cstdio>
#include <cstdlib> // for strtod_l
#if defined __APPLE__ || defined(__FreeBSD__)
# include <xlocale.h> // for LC_NUMERIC_MASK on OS X
#endif
#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(__APPLE__) || \
defined(__linux__)) && \
(!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
# if defined(_WIN32) && !defined(__MINGW32__)
// Fix warnings about deprecated symbols.
# define FMT_POSIX(call) _##call
# else
# define FMT_POSIX(call) call
# endif
#endif
// Calls to system functions are wrapped in FMT_SYSTEM for testability.
#ifdef FMT_SYSTEM
# define FMT_POSIX_CALL(call) FMT_SYSTEM(call)
#else
# define FMT_SYSTEM(call) ::call
# ifdef _WIN32
// Fix warnings about deprecated symbols.
# define FMT_POSIX_CALL(call) ::_##call
# else
# define FMT_POSIX_CALL(call) ::call
# endif
#endif
// Retries the expression while it evaluates to error_result and errno
// equals to EINTR.
#ifndef _WIN32
# define FMT_RETRY_VAL(result, expression, error_result) \
do { \
(result) = (expression); \
} while ((result) == (error_result) && errno == EINTR)
#else
# define FMT_RETRY_VAL(result, expression, error_result) result = (expression)
#endif
#define FMT_RETRY(result, expression) FMT_RETRY_VAL(result, expression, -1)
FMT_BEGIN_NAMESPACE
/**
\rst
A reference to a null-terminated string. It can be constructed from a C
string or ``std::string``.
You can use one of the following type aliases for common character types:
+---------------+-----------------------------+
| Type | Definition |
+===============+=============================+
| cstring_view | basic_cstring_view<char> |
+---------------+-----------------------------+
| wcstring_view | basic_cstring_view<wchar_t> |
+---------------+-----------------------------+
This class is most useful as a parameter type to allow passing
different types of strings to a function, for example::
template <typename... Args>
std::string format(cstring_view format_str, const Args & ... args);
format("{}", 42);
format(std::string("{}"), 42);
\endrst
*/
template <typename Char> class basic_cstring_view {
private:
const Char* data_;
public:
/** Constructs a string reference object from a C string. */
basic_cstring_view(const Char* s) : data_(s) {}
/**
\rst
Constructs a string reference from an ``std::string`` object.
\endrst
*/
basic_cstring_view(const std::basic_string<Char>& s) : data_(s.c_str()) {}
/** Returns the pointer to a C string. */
const Char* c_str() const { return data_; }
};
using cstring_view = basic_cstring_view<char>;
using wcstring_view = basic_cstring_view<wchar_t>;
// An error code.
class error_code {
private:
int value_;
public:
explicit error_code(int value = 0) FMT_NOEXCEPT : value_(value) {}
int get() const FMT_NOEXCEPT { return value_; }
};
#ifdef _WIN32
namespace detail {
// A converter from UTF-16 to UTF-8.
// It is only provided for Windows since other systems support UTF-8 natively.
class utf16_to_utf8 {
private:
memory_buffer buffer_;
public:
utf16_to_utf8() {}
FMT_API explicit utf16_to_utf8(wstring_view s);
operator string_view() const { return string_view(&buffer_[0], size()); }
size_t size() const { return buffer_.size() - 1; }
const char* c_str() const { return &buffer_[0]; }
std::string str() const { return std::string(&buffer_[0], size()); }
// Performs conversion returning a system error code instead of
// throwing exception on conversion error. This method may still throw
// in case of memory allocation error.
FMT_API int convert(wstring_view s);
};
FMT_API void format_windows_error(buffer<char>& out, int error_code,
string_view message) FMT_NOEXCEPT;
} // namespace detail
/** A Windows error. */
class windows_error : public system_error {
private:
FMT_API void init(int error_code, string_view format_str, format_args args);
public:
/**
\rst
Constructs a :class:`fmt::windows_error` object with the description
of the form
.. parsed-literal::
*<message>*: *<system-message>*
where *<message>* is the formatted message and *<system-message>* is the
system message corresponding to the error code.
*error_code* is a Windows error code as given by ``GetLastError``.
If *error_code* is not a valid error code such as -1, the system message
will look like "error -1".
**Example**::
// This throws a windows_error with the description
// cannot open file 'madeup': The system cannot find the file specified.
// or similar (system message may vary).
const char *filename = "madeup";
LPOFSTRUCT of = LPOFSTRUCT();
HFILE file = OpenFile(filename, &of, OF_READ);
if (file == HFILE_ERROR) {
throw fmt::windows_error(GetLastError(),
"cannot open file '{}'", filename);
}
\endrst
*/
template <typename... Args>
windows_error(int error_code, string_view message, const Args&... args) {
init(error_code, message, make_format_args(args...));
}
};
// Reports a Windows error without throwing an exception.
// Can be used to report errors from destructors.
FMT_API void report_windows_error(int error_code,
string_view message) FMT_NOEXCEPT;
#endif // _WIN32
// A buffered file.
class buffered_file {
private:
FILE* file_;
friend class file;
explicit buffered_file(FILE* f) : file_(f) {}
public:
buffered_file(const buffered_file&) = delete;
void operator=(const buffered_file&) = delete;
// Constructs a buffered_file object which doesn't represent any file.
buffered_file() FMT_NOEXCEPT : file_(nullptr) {}
// Destroys the object closing the file it represents if any.
FMT_API ~buffered_file() FMT_NOEXCEPT;
public:
buffered_file(buffered_file&& other) FMT_NOEXCEPT : file_(other.file_) {
other.file_ = nullptr;
}
buffered_file& operator=(buffered_file&& other) {
close();
file_ = other.file_;
other.file_ = nullptr;
return *this;
}
// Opens a file.
FMT_API buffered_file(cstring_view filename, cstring_view mode);
// Closes the file.
FMT_API void close();
// Returns the pointer to a FILE object representing this file.
FILE* get() const FMT_NOEXCEPT { return file_; }
// We place parentheses around fileno to workaround a bug in some versions
// of MinGW that define fileno as a macro.
FMT_API int(fileno)() const;
void vprint(string_view format_str, format_args args) {
fmt::vprint(file_, format_str, args);
}
template <typename... Args>
inline void print(string_view format_str, const Args&... args) {
vprint(format_str, make_format_args(args...));
}
};
#if FMT_USE_FCNTL
// A file. Closed file is represented by a file object with descriptor -1.
// Methods that are not declared with FMT_NOEXCEPT may throw
// fmt::system_error in case of failure. Note that some errors such as
// closing the file multiple times will cause a crash on Windows rather
// than an exception. You can get standard behavior by overriding the
// invalid parameter handler with _set_invalid_parameter_handler.
class file {
private:
int fd_; // File descriptor.
// Constructs a file object with a given descriptor.
explicit file(int fd) : fd_(fd) {}
public:
// Possible values for the oflag argument to the constructor.
enum {
RDONLY = FMT_POSIX(O_RDONLY), // Open for reading only.
WRONLY = FMT_POSIX(O_WRONLY), // Open for writing only.
RDWR = FMT_POSIX(O_RDWR), // Open for reading and writing.
CREATE = FMT_POSIX(O_CREAT), // Create if the file doesn't exist.
APPEND = FMT_POSIX(O_APPEND) // Open in append mode.
};
// Constructs a file object which doesn't represent any file.
file() FMT_NOEXCEPT : fd_(-1) {}
// Opens a file and constructs a file object representing this file.
FMT_API file(cstring_view path, int oflag);
public:
file(const file&) = delete;
void operator=(const file&) = delete;
file(file&& other) FMT_NOEXCEPT : fd_(other.fd_) { other.fd_ = -1; }
file& operator=(file&& other) FMT_NOEXCEPT {
close();
fd_ = other.fd_;
other.fd_ = -1;
return *this;
}
// Destroys the object closing the file it represents if any.
FMT_API ~file() FMT_NOEXCEPT;
// Returns the file descriptor.
int descriptor() const FMT_NOEXCEPT { return fd_; }
// Closes the file.
FMT_API void close();
// Returns the file size. The size has signed type for consistency with
// stat::st_size.
FMT_API long long size() const;
// Attempts to read count bytes from the file into the specified buffer.
FMT_API size_t read(void* buffer, size_t count);
// Attempts to write count bytes from the specified buffer to the file.
FMT_API size_t write(const void* buffer, size_t count);
// Duplicates a file descriptor with the dup function and returns
// the duplicate as a file object.
FMT_API static file dup(int fd);
// Makes fd be the copy of this file descriptor, closing fd first if
// necessary.
FMT_API void dup2(int fd);
// Makes fd be the copy of this file descriptor, closing fd first if
// necessary.
FMT_API void dup2(int fd, error_code& ec) FMT_NOEXCEPT;
// Creates a pipe setting up read_end and write_end file objects for reading
// and writing respectively.
FMT_API static void pipe(file& read_end, file& write_end);
// Creates a buffered_file object associated with this file and detaches
// this file object from the file.
FMT_API buffered_file fdopen(const char* mode);
};
// Returns the memory page size.
long getpagesize();
namespace detail {
struct buffer_size {
size_t value = 0;
buffer_size operator=(size_t val) const {
auto bs = buffer_size();
bs.value = val;
return bs;
}
};
struct ostream_params {
int oflag = file::WRONLY | file::CREATE;
size_t buffer_size = BUFSIZ > 32768 ? BUFSIZ : 32768;
ostream_params() {}
template <typename... T>
ostream_params(T... params, int oflag) : ostream_params(params...) {
this->oflag = oflag;
}
template <typename... T>
ostream_params(T... params, detail::buffer_size bs)
: ostream_params(params...) {
this->buffer_size = bs.value;
}
};
} // namespace detail
static constexpr detail::buffer_size buffer_size;
// A fast output stream which is not thread-safe.
class ostream final : private detail::buffer<char> {
private:
file file_;
void flush() {
if (size() == 0) return;
file_.write(data(), size());
clear();
}
FMT_API void grow(size_t) override final;
ostream(cstring_view path, const detail::ostream_params& params)
: file_(path, params.oflag) {
set(new char[params.buffer_size], params.buffer_size);
}
public:
ostream(ostream&& other)
: detail::buffer<char>(other.data(), other.size(), other.capacity()),
file_(std::move(other.file_)) {
other.set(nullptr, 0);
}
~ostream() {
flush();
delete[] data();
}
template <typename... T>
friend ostream output_file(cstring_view path, T... params);
void close() {
flush();
file_.close();
}
template <typename S, typename... Args>
void print(const S& format_str, const Args&... args) {
format_to(detail::buffer_appender<char>(*this), format_str, args...);
}
};
/**
Opens a file for writing. Supported parameters passed in `params`:
* ``<integer>``: Output flags (``file::WRONLY | file::CREATE`` by default)
* ``buffer_size=<integer>``: Output buffer size
*/
template <typename... T>
inline ostream output_file(cstring_view path, T... params) {
return {path, detail::ostream_params(params...)};
}
#endif // FMT_USE_FCNTL
#ifdef FMT_LOCALE
// A "C" numeric locale.
class locale {
private:
# ifdef _WIN32
using locale_t = _locale_t;
static void freelocale(locale_t loc) { _free_locale(loc); }
static double strtod_l(const char* nptr, char** endptr, _locale_t loc) {
return _strtod_l(nptr, endptr, loc);
}
# endif
locale_t locale_;
public:
using type = locale_t;
locale(const locale&) = delete;
void operator=(const locale&) = delete;
locale() {
# ifndef _WIN32
locale_ = FMT_SYSTEM(newlocale(LC_NUMERIC_MASK, "C", nullptr));
# else
locale_ = _create_locale(LC_NUMERIC, "C");
# endif
if (!locale_) FMT_THROW(system_error(errno, "cannot create locale"));
}
~locale() { freelocale(locale_); }
type get() const { return locale_; }
// Converts string to floating-point number and advances str past the end
// of the parsed input.
double strtod(const char*& str) const {
char* end = nullptr;
double result = strtod_l(str, &end, locale_);
str = end;
return result;
}
};
using Locale FMT_DEPRECATED_ALIAS = locale;
#endif // FMT_LOCALE
FMT_END_NAMESPACE
#endif // FMT_OS_H_

@ -9,10 +9,15 @@
#define FMT_OSTREAM_H_
#include <ostream>
#include "format.h"
FMT_BEGIN_NAMESPACE
namespace internal {
template <typename Char> class basic_printf_parse_context;
template <typename OutputIt, typename Char> class basic_printf_context;
namespace detail {
template <class Char> class formatbuf : public std::basic_streambuf<Char> {
private:
@ -44,21 +49,35 @@ template <class Char> class formatbuf : public std::basic_streambuf<Char> {
}
};
struct converter {
template <typename T, FMT_ENABLE_IF(is_integral<T>::value)> converter(T);
};
template <typename Char> struct test_stream : std::basic_ostream<Char> {
private:
struct null;
// Hide all operator<< from std::basic_ostream<Char>.
void operator<<(null);
void_t<> operator<<(converter);
};
// Hide insertion operators for built-in types.
template <typename Char, typename Traits>
void_t<> operator<<(std::basic_ostream<Char, Traits>&, Char);
template <typename Char, typename Traits>
void_t<> operator<<(std::basic_ostream<Char, Traits>&, char);
template <typename Traits>
void_t<> operator<<(std::basic_ostream<char, Traits>&, char);
template <typename Traits>
void_t<> operator<<(std::basic_ostream<char, Traits>&, signed char);
template <typename Traits>
void_t<> operator<<(std::basic_ostream<char, Traits>&, unsigned char);
// Checks if T has a user-defined operator<< (e.g. not a member of
// std::ostream).
template <typename T, typename Char> class is_streamable {
private:
template <typename U>
static decltype((void)(std::declval<test_stream<Char>&>()
<< std::declval<U>()),
std::true_type())
static bool_constant<!std::is_same<decltype(std::declval<test_stream<Char>&>()
<< std::declval<U>()),
void_t<>>::value>
test(int);
template <typename> static std::false_type test(...);
@ -71,12 +90,11 @@ template <typename T, typename Char> class is_streamable {
// Write the content of buf to os.
template <typename Char>
void write(std::basic_ostream<Char>& os, buffer<Char>& buf) {
void write_buffer(std::basic_ostream<Char>& os, buffer<Char>& buf) {
const Char* buf_data = buf.data();
using unsigned_streamsize = std::make_unsigned<std::streamsize>::type;
unsigned_streamsize size = buf.size();
unsigned_streamsize max_size =
to_unsigned((std::numeric_limits<std::streamsize>::max)());
unsigned_streamsize max_size = to_unsigned(max_value<std::streamsize>());
do {
unsigned_streamsize n = size <= max_size ? size : max_size;
os.write(buf_data, static_cast<std::streamsize>(n));
@ -86,34 +104,57 @@ void write(std::basic_ostream<Char>& os, buffer<Char>& buf) {
}
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);
std::basic_ostream<Char> output(&format_buf);
output.exceptions(std::ios_base::failbit | std::ios_base::badbit);
#if !defined(FMT_STATIC_THOUSANDS_SEPARATOR)
if (loc) output.imbue(loc.get<std::locale>());
#endif
output << value;
buf.resize(buf.size());
output.exceptions(std::ios_base::failbit | std::ios_base::badbit);
buf.try_resize(buf.size());
}
// Formats an object of type T that has an overloaded ostream operator<<.
template <typename T, typename Char>
struct fallback_formatter<T, Char, enable_if_t<is_streamable<T, Char>::value>>
: formatter<basic_string_view<Char>, Char> {
template <typename Context>
auto format(const T& value, Context& ctx) -> decltype(ctx.out()) {
: private formatter<basic_string_view<Char>, Char> {
FMT_CONSTEXPR auto parse(basic_format_parse_context<Char>& ctx)
-> decltype(ctx.begin()) {
return formatter<basic_string_view<Char>, Char>::parse(ctx);
}
template <typename ParseCtx,
FMT_ENABLE_IF(std::is_same<
ParseCtx, basic_printf_parse_context<Char>>::value)>
auto parse(ParseCtx& ctx) -> decltype(ctx.begin()) {
return ctx.begin();
}
template <typename OutputIt>
auto format(const T& value, basic_format_context<OutputIt, Char>& ctx)
-> OutputIt {
basic_memory_buffer<Char> buffer;
format_value(buffer, value);
format_value(buffer, value, ctx.locale());
basic_string_view<Char> str(buffer.data(), buffer.size());
return formatter<basic_string_view<Char>, Char>::format(str, ctx);
}
template <typename OutputIt>
auto format(const T& value, basic_printf_context<OutputIt, Char>& ctx)
-> OutputIt {
basic_memory_buffer<Char> buffer;
format_value(buffer, value, ctx.locale());
return std::copy(buffer.begin(), buffer.end(), ctx.out());
}
};
} // namespace internal
} // namespace detail
template <typename Char>
void vprint(std::basic_ostream<Char>& os, basic_string_view<Char> format_str,
basic_format_args<buffer_context<Char>> args) {
basic_format_args<buffer_context<type_identity_t<Char>>> args) {
basic_memory_buffer<Char> buffer;
internal::vformat_to(buffer, format_str, args);
internal::write(os, buffer);
detail::vformat_to(buffer, format_str, args);
detail::write_buffer(os, buffer);
}
/**
@ -126,10 +167,10 @@ void vprint(std::basic_ostream<Char>& os, basic_string_view<Char> format_str,
\endrst
*/
template <typename S, typename... Args,
typename Char = enable_if_t<internal::is_string<S>::value, char_t<S>>>
typename Char = enable_if_t<detail::is_string<S>::value, char_t<S>>>
void print(std::basic_ostream<Char>& os, const S& format_str, Args&&... args) {
vprint(os, to_string_view(format_str),
{internal::make_args_checked<Args...>(format_str, args...)});
fmt::make_args_checked<Args...>(format_str, args...));
}
FMT_END_NAMESPACE

@ -1,311 +1,2 @@
// A C++ interface to POSIX functions.
//
// Copyright (c) 2012 - 2016, Victor Zverovich
// All rights reserved.
//
// For the license information refer to format.h.
#ifndef FMT_POSIX_H_
#define FMT_POSIX_H_
#if defined(__MINGW32__) || defined(__CYGWIN__)
// Workaround MinGW bug https://sourceforge.net/p/mingw/bugs/2024/.
# undef __STRICT_ANSI__
#endif
#include <errno.h>
#include <fcntl.h> // for O_RDONLY
#include <locale.h> // for locale_t
#include <stdio.h>
#include <stdlib.h> // for strtod_l
#include <cstddef>
#if defined __APPLE__ || defined(__FreeBSD__)
# include <xlocale.h> // for LC_NUMERIC_MASK on OS X
#endif
#include "format.h"
#ifndef FMT_POSIX
# if defined(_WIN32) && !defined(__MINGW32__)
// Fix warnings about deprecated symbols.
# define FMT_POSIX(call) _##call
# else
# define FMT_POSIX(call) call
# endif
#endif
// Calls to system functions are wrapped in FMT_SYSTEM for testability.
#ifdef FMT_SYSTEM
# define FMT_POSIX_CALL(call) FMT_SYSTEM(call)
#else
# define FMT_SYSTEM(call) call
# ifdef _WIN32
// Fix warnings about deprecated symbols.
# define FMT_POSIX_CALL(call) ::_##call
# else
# define FMT_POSIX_CALL(call) ::call
# endif
#endif
// Retries the expression while it evaluates to error_result and errno
// equals to EINTR.
#ifndef _WIN32
# define FMT_RETRY_VAL(result, expression, error_result) \
do { \
result = (expression); \
} while (result == error_result && errno == EINTR)
#else
# define FMT_RETRY_VAL(result, expression, error_result) result = (expression)
#endif
#define FMT_RETRY(result, expression) FMT_RETRY_VAL(result, expression, -1)
FMT_BEGIN_NAMESPACE
/**
\rst
A reference to a null-terminated string. It can be constructed from a C
string or ``std::string``.
You can use one of the following type aliases for common character types:
+---------------+-----------------------------+
| Type | Definition |
+===============+=============================+
| cstring_view | basic_cstring_view<char> |
+---------------+-----------------------------+
| wcstring_view | basic_cstring_view<wchar_t> |
+---------------+-----------------------------+
This class is most useful as a parameter type to allow passing
different types of strings to a function, for example::
template <typename... Args>
std::string format(cstring_view format_str, const Args & ... args);
format("{}", 42);
format(std::string("{}"), 42);
\endrst
*/
template <typename Char> class basic_cstring_view {
private:
const Char* data_;
public:
/** Constructs a string reference object from a C string. */
basic_cstring_view(const Char* s) : data_(s) {}
/**
\rst
Constructs a string reference from an ``std::string`` object.
\endrst
*/
basic_cstring_view(const std::basic_string<Char>& s) : data_(s.c_str()) {}
/** Returns the pointer to a C string. */
const Char* c_str() const { return data_; }
};
using cstring_view = basic_cstring_view<char>;
using wcstring_view = basic_cstring_view<wchar_t>;
// An error code.
class error_code {
private:
int value_;
public:
explicit error_code(int value = 0) FMT_NOEXCEPT : value_(value) {}
int get() const FMT_NOEXCEPT { return value_; }
};
// A buffered file.
class buffered_file {
private:
FILE* file_;
friend class file;
explicit buffered_file(FILE* f) : file_(f) {}
public:
// Constructs a buffered_file object which doesn't represent any file.
buffered_file() FMT_NOEXCEPT : file_(nullptr) {}
// Destroys the object closing the file it represents if any.
FMT_API ~buffered_file() FMT_NOEXCEPT;
private:
buffered_file(const buffered_file&) = delete;
void operator=(const buffered_file&) = delete;
public:
buffered_file(buffered_file&& other) FMT_NOEXCEPT : file_(other.file_) {
other.file_ = nullptr;
}
buffered_file& operator=(buffered_file&& other) {
close();
file_ = other.file_;
other.file_ = nullptr;
return *this;
}
// Opens a file.
FMT_API buffered_file(cstring_view filename, cstring_view mode);
// Closes the file.
FMT_API void close();
// Returns the pointer to a FILE object representing this file.
FILE* get() const FMT_NOEXCEPT { return file_; }
// We place parentheses around fileno to workaround a bug in some versions
// of MinGW that define fileno as a macro.
FMT_API int(fileno)() const;
void vprint(string_view format_str, format_args args) {
fmt::vprint(file_, format_str, args);
}
template <typename... Args>
inline void print(string_view format_str, const Args&... args) {
vprint(format_str, make_format_args(args...));
}
};
// A file. Closed file is represented by a file object with descriptor -1.
// Methods that are not declared with FMT_NOEXCEPT may throw
// fmt::system_error in case of failure. Note that some errors such as
// closing the file multiple times will cause a crash on Windows rather
// than an exception. You can get standard behavior by overriding the
// invalid parameter handler with _set_invalid_parameter_handler.
class file {
private:
int fd_; // File descriptor.
// Constructs a file object with a given descriptor.
explicit file(int fd) : fd_(fd) {}
public:
// Possible values for the oflag argument to the constructor.
enum {
RDONLY = FMT_POSIX(O_RDONLY), // Open for reading only.
WRONLY = FMT_POSIX(O_WRONLY), // Open for writing only.
RDWR = FMT_POSIX(O_RDWR) // Open for reading and writing.
};
// Constructs a file object which doesn't represent any file.
file() FMT_NOEXCEPT : fd_(-1) {}
// Opens a file and constructs a file object representing this file.
FMT_API file(cstring_view path, int oflag);
private:
file(const file&) = delete;
void operator=(const file&) = delete;
public:
file(file&& other) FMT_NOEXCEPT : fd_(other.fd_) { other.fd_ = -1; }
file& operator=(file&& other) {
close();
fd_ = other.fd_;
other.fd_ = -1;
return *this;
}
// Destroys the object closing the file it represents if any.
FMT_API ~file() FMT_NOEXCEPT;
// Returns the file descriptor.
int descriptor() const FMT_NOEXCEPT { return fd_; }
// Closes the file.
FMT_API void close();
// Returns the file size. The size has signed type for consistency with
// stat::st_size.
FMT_API long long size() const;
// Attempts to read count bytes from the file into the specified buffer.
FMT_API std::size_t read(void* buffer, std::size_t count);
// Attempts to write count bytes from the specified buffer to the file.
FMT_API std::size_t write(const void* buffer, std::size_t count);
// Duplicates a file descriptor with the dup function and returns
// the duplicate as a file object.
FMT_API static file dup(int fd);
// Makes fd be the copy of this file descriptor, closing fd first if
// necessary.
FMT_API void dup2(int fd);
// Makes fd be the copy of this file descriptor, closing fd first if
// necessary.
FMT_API void dup2(int fd, error_code& ec) FMT_NOEXCEPT;
// Creates a pipe setting up read_end and write_end file objects for reading
// and writing respectively.
FMT_API static void pipe(file& read_end, file& write_end);
// Creates a buffered_file object associated with this file and detaches
// this file object from the file.
FMT_API buffered_file fdopen(const char* mode);
};
// Returns the memory page size.
long getpagesize();
#ifdef FMT_LOCALE
// A "C" numeric locale.
class Locale {
private:
# ifdef _WIN32
using locale_t = _locale_t;
enum { LC_NUMERIC_MASK = LC_NUMERIC };
static locale_t newlocale(int category_mask, const char* locale, locale_t) {
return _create_locale(category_mask, locale);
}
static void freelocale(locale_t locale) { _free_locale(locale); }
static double strtod_l(const char* nptr, char** endptr, _locale_t locale) {
return _strtod_l(nptr, endptr, locale);
}
# endif
locale_t locale_;
Locale(const Locale&) = delete;
void operator=(const Locale&) = delete;
public:
using type = locale_t;
Locale() : locale_(newlocale(LC_NUMERIC_MASK, "C", nullptr)) {
if (!locale_) FMT_THROW(system_error(errno, "cannot create locale"));
}
~Locale() { freelocale(locale_); }
type get() const { return locale_; }
// Converts string to floating-point number and advances str past the end
// of the parsed input.
double strtod(const char*& str) const {
char* end = nullptr;
double result = strtod_l(str, &end, locale_);
str = end;
return result;
}
};
#endif // FMT_LOCALE
FMT_END_NAMESPACE
#endif // FMT_POSIX_H_
#include "os.h"
#warning "fmt/posix.h is deprecated; use fmt/os.h instead"

@ -1,4 +1,4 @@
// Formatting library for C++
// Formatting library for C++ - legacy printf implementation
//
// Copyright (c) 2012 - 2016, Victor Zverovich
// All rights reserved.
@ -8,23 +8,19 @@
#ifndef FMT_PRINTF_H_
#define FMT_PRINTF_H_
#include <algorithm> // std::fill_n
#include <algorithm> // std::max
#include <limits> // std::numeric_limits
#include "ostream.h"
FMT_BEGIN_NAMESPACE
namespace internal {
// A helper function to suppress bogus "conditional expression is constant"
// warnings.
template <typename T> inline T const_check(T value) { return value; }
namespace detail {
// Checks if a value fits in int - used to avoid warnings about comparing
// signed and unsigned integers.
template <bool IsSigned> struct int_checker {
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;
}
static bool fits_in_int(bool) { return true; }
@ -32,8 +28,8 @@ template <bool IsSigned> struct int_checker {
template <> struct int_checker<true> {
template <typename T> static bool fits_in_int(T value) {
return value >= std::numeric_limits<int>::min() &&
value <= std::numeric_limits<int>::max();
return value >= (std::numeric_limits<int>::min)() &&
value <= max_value<int>();
}
static bool fits_in_int(int) { return true; }
};
@ -94,11 +90,11 @@ template <typename T, typename Context> class arg_converter {
if (const_check(sizeof(target_type) <= sizeof(int))) {
// Extra casts are used to silence warnings.
if (is_signed) {
arg_ = internal::make_arg<Context>(
arg_ = detail::make_arg<Context>(
static_cast<int>(static_cast<target_type>(value)));
} else {
using unsigned_type = typename make_unsigned_or_bool<target_type>::type;
arg_ = internal::make_arg<Context>(
arg_ = detail::make_arg<Context>(
static_cast<unsigned>(static_cast<unsigned_type>(value)));
}
} else {
@ -106,9 +102,9 @@ template <typename T, typename Context> class arg_converter {
// glibc's printf doesn't sign extend arguments of smaller types:
// std::printf("%lld", -42); // prints "4294967254"
// but we don't have to do the same because it's a UB.
arg_ = internal::make_arg<Context>(static_cast<long long>(value));
arg_ = detail::make_arg<Context>(static_cast<long long>(value));
} else {
arg_ = internal::make_arg<Context>(
arg_ = detail::make_arg<Context>(
static_cast<typename make_unsigned_or_bool<U>::type>(value));
}
}
@ -137,7 +133,7 @@ template <typename Context> class char_converter {
template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
void operator()(T value) {
arg_ = internal::make_arg<Context>(
arg_ = detail::make_arg<Context>(
static_cast<typename Context::char_type>(value));
}
@ -145,6 +141,13 @@ template <typename Context> class char_converter {
void operator()(T) {} // No conversion needed for non-integral types.
};
// An argument visitor that return a pointer to a C string if argument is a
// string or null otherwise.
template <typename Char> struct get_cstring {
template <typename T> const Char* operator()(T) { return nullptr; }
const Char* operator()(const Char* s) { return s; }
};
// Checks if an argument is a valid printf width specifier and sets
// left alignment if it is negative.
template <typename Char> class printf_width_handler {
@ -158,12 +161,12 @@ template <typename Char> class printf_width_handler {
template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
unsigned operator()(T value) {
auto width = static_cast<uint32_or_64_t<T>>(value);
if (internal::is_negative(value)) {
auto width = static_cast<uint32_or_64_or_128_t<T>>(value);
if (detail::is_negative(value)) {
specs_.align = align::left;
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"));
return static_cast<unsigned>(width);
}
@ -176,23 +179,25 @@ template <typename Char> class printf_width_handler {
};
template <typename Char, typename Context>
void printf(buffer<Char>& buf, basic_string_view<Char> format,
basic_format_args<Context> args) {
Context(std::back_inserter(buf), format, args).format();
void vprintf(buffer<Char>& buf, basic_string_view<Char> format,
basic_format_args<Context> args) {
Context(buffer_appender<Char>(buf), format, args).format();
}
} // namespace detail
template <typename OutputIt, typename Char, typename Context>
internal::truncating_iterator<OutputIt> printf(
internal::truncating_iterator<OutputIt> it, basic_string_view<Char> format,
basic_format_args<Context> args) {
return Context(it, format, args).format();
// For printing into memory_buffer.
template <typename Char, typename Context>
FMT_DEPRECATED void printf(detail::buffer<Char>& buf,
basic_string_view<Char> format,
basic_format_args<Context> args) {
return detail::vprintf(buf, format, args);
}
} // namespace internal
using internal::printf; // For printing into memory_buffer.
template <typename Range> class printf_arg_formatter;
using detail::vprintf;
template <typename Char>
class basic_printf_parse_context : public basic_format_parse_context<Char> {
using basic_format_parse_context<Char>::basic_format_parse_context;
};
template <typename OutputIt, typename Char> class basic_printf_context;
/**
@ -200,15 +205,15 @@ template <typename OutputIt, typename Char> class basic_printf_context;
The ``printf`` argument formatter.
\endrst
*/
template <typename Range>
class printf_arg_formatter : public internal::arg_formatter_base<Range> {
template <typename OutputIt, typename Char>
class printf_arg_formatter : public detail::arg_formatter_base<OutputIt, Char> {
public:
using iterator = typename Range::iterator;
using iterator = OutputIt;
private:
using char_type = typename Range::value_type;
using base = internal::arg_formatter_base<Range>;
using context_type = basic_printf_context<iterator, char_type>;
using char_type = Char;
using base = detail::arg_formatter_base<OutputIt, Char>;
using context_type = basic_printf_context<OutputIt, Char>;
context_type& context_;
@ -233,9 +238,9 @@ class printf_arg_formatter : public internal::arg_formatter_base<Range> {
\endrst
*/
printf_arg_formatter(iterator iter, format_specs& specs, context_type& ctx)
: base(Range(iter), &specs, internal::locale_ref()), context_(ctx) {}
: base(iter, &specs, detail::locale_ref()), context_(ctx) {}
template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
template <typename T, FMT_ENABLE_IF(fmt::detail::is_integral<T>::value)>
iterator operator()(T value) {
// MSVC2013 fails to compile separate overloads for bool and char_type so
// use std::is_same instead.
@ -250,7 +255,11 @@ class printf_arg_formatter : public internal::arg_formatter_base<Range> {
return (*this)(static_cast<int>(value));
fmt_specs.sign = sign::none;
fmt_specs.alt = false;
fmt_specs.align = align::right;
fmt_specs.fill[0] = ' '; // Ignore '0' flag for char types.
// align::numeric needs to be overwritten here since the '0' flag is
// ignored for non-numeric types
if (fmt_specs.align == align::none || fmt_specs.align == align::numeric)
fmt_specs.align = align::right;
return base::operator()(value);
} else {
return base::operator()(value);
@ -307,6 +316,8 @@ class printf_arg_formatter : public internal::arg_formatter_base<Range> {
};
template <typename T> struct printf_formatter {
printf_formatter() = delete;
template <typename ParseContext>
auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
return ctx.begin();
@ -314,17 +325,21 @@ template <typename T> struct printf_formatter {
template <typename FormatContext>
auto format(const T& value, FormatContext& ctx) -> decltype(ctx.out()) {
internal::format_value(internal::get_container(ctx.out()), value);
detail::format_value(detail::get_container(ctx.out()), value);
return ctx.out();
}
};
/** This template formats data and writes the output to a writer. */
/**
This template formats data and writes the output through an output iterator.
*/
template <typename OutputIt, typename Char> class basic_printf_context {
public:
/** The character type for the output. */
using char_type = Char;
using iterator = OutputIt;
using format_arg = basic_format_arg<basic_printf_context>;
using parse_context_type = basic_printf_parse_context<Char>;
template <typename T> using formatter_type = printf_formatter<T>;
private:
@ -332,24 +347,23 @@ template <typename OutputIt, typename Char> class basic_printf_context {
OutputIt out_;
basic_format_args<basic_printf_context> args_;
basic_parse_context<Char> parse_ctx_;
parse_context_type parse_ctx_;
static void parse_flags(format_specs& specs, const Char*& it,
const Char* end);
// Returns the argument with specified index or, if arg_index is equal
// to the maximum unsigned value, the next argument.
format_arg get_arg(unsigned arg_index = std::numeric_limits<unsigned>::max());
// Returns the argument with specified index or, if arg_index is -1, the next
// argument.
format_arg get_arg(int arg_index = -1);
// Parses argument index, flags and width and returns the argument index.
unsigned parse_header(const Char*& it, const Char* end, format_specs& specs);
int parse_header(const Char*& it, const Char* end, format_specs& specs);
public:
/**
\rst
Constructs a ``printf_context`` object. References to the arguments and
the writer are stored in the context object so make sure they have
appropriate lifetimes.
Constructs a ``printf_context`` object. References to the arguments are
stored in the context object so make sure they have appropriate lifetimes.
\endrst
*/
basic_printf_context(OutputIt out, basic_string_view<char_type> format_str,
@ -359,17 +373,18 @@ template <typename OutputIt, typename Char> class basic_printf_context {
OutputIt out() { return out_; }
void advance_to(OutputIt it) { out_ = it; }
format_arg arg(unsigned id) const { return args_.get(id); }
detail::locale_ref locale() { return {}; }
basic_parse_context<Char>& parse_context() { return parse_ctx_; }
format_arg arg(int id) const { return args_.get(id); }
parse_context_type& parse_context() { return parse_ctx_; }
FMT_CONSTEXPR void on_error(const char* message) {
parse_ctx_.on_error(message);
}
/** Formats stored arguments and writes the output to the range. */
template <typename ArgFormatter =
printf_arg_formatter<internal::buffer_range<Char>>>
template <typename ArgFormatter = printf_arg_formatter<OutputIt, Char>>
OutputIt format();
};
@ -389,7 +404,9 @@ void basic_printf_context<OutputIt, Char>::parse_flags(format_specs& specs,
specs.fill[0] = '0';
break;
case ' ':
specs.sign = sign::space;
if (specs.sign != sign::plus) {
specs.sign = sign::space;
}
break;
case '#':
specs.alt = true;
@ -402,24 +419,25 @@ void basic_printf_context<OutputIt, Char>::parse_flags(format_specs& specs,
template <typename OutputIt, typename Char>
typename basic_printf_context<OutputIt, Char>::format_arg
basic_printf_context<OutputIt, Char>::get_arg(unsigned arg_index) {
if (arg_index == std::numeric_limits<unsigned>::max())
basic_printf_context<OutputIt, Char>::get_arg(int arg_index) {
if (arg_index < 0)
arg_index = parse_ctx_.next_arg_id();
else
parse_ctx_.check_arg_id(--arg_index);
return internal::get_arg(*this, arg_index);
return detail::get_arg(*this, arg_index);
}
template <typename OutputIt, typename Char>
unsigned basic_printf_context<OutputIt, Char>::parse_header(
const Char*& it, const Char* end, format_specs& specs) {
unsigned arg_index = std::numeric_limits<unsigned>::max();
int basic_printf_context<OutputIt, Char>::parse_header(const Char*& it,
const Char* end,
format_specs& specs) {
int arg_index = -1;
char_type c = *it;
if (c >= '0' && c <= '9') {
// Parse an argument index (if followed by '$') or a width possibly
// preceded with '0' flag(s).
internal::error_handler eh;
unsigned value = parse_nonnegative_int(it, end, eh);
detail::error_handler eh;
int value = parse_nonnegative_int(it, end, eh);
if (it != end && *it == '$') { // value is an argument index
++it;
arg_index = value;
@ -437,12 +455,12 @@ unsigned basic_printf_context<OutputIt, Char>::parse_header(
// Parse width.
if (it != end) {
if (*it >= '0' && *it <= '9') {
internal::error_handler eh;
detail::error_handler eh;
specs.width = parse_nonnegative_int(it, end, eh);
} else if (*it == '*') {
++it;
specs.width = visit_format_arg(
internal::printf_width_handler<char_type>(specs), get_arg());
specs.width = static_cast<int>(visit_format_arg(
detail::printf_width_handler<char_type>(specs), get_arg()));
}
}
return arg_index;
@ -469,38 +487,53 @@ OutputIt basic_printf_context<OutputIt, Char>::format() {
specs.align = align::right;
// Parse argument index, flags and width.
unsigned arg_index = parse_header(it, end, specs);
int arg_index = parse_header(it, end, specs);
if (arg_index == 0) on_error("argument not found");
// Parse precision.
if (it != end && *it == '.') {
++it;
c = it != end ? *it : 0;
if ('0' <= c && c <= '9') {
internal::error_handler eh;
specs.precision = static_cast<int>(parse_nonnegative_int(it, end, eh));
detail::error_handler eh;
specs.precision = parse_nonnegative_int(it, end, eh);
} else if (c == '*') {
++it;
specs.precision =
visit_format_arg(internal::printf_precision_handler(), get_arg());
specs.precision = static_cast<int>(
visit_format_arg(detail::printf_precision_handler(), get_arg()));
} else {
specs.precision = 0;
}
}
format_arg arg = get_arg(arg_index);
if (specs.alt && visit_format_arg(internal::is_zero_int(), arg))
// For d, i, o, u, x, and X conversion specifiers, if a precision is
// specified, the '0' flag is ignored
if (specs.precision >= 0 && arg.is_integral())
specs.fill[0] =
' '; // Ignore '0' flag for non-numeric types or if '-' present.
if (specs.precision >= 0 && arg.type() == detail::type::cstring_type) {
auto str = visit_format_arg(detail::get_cstring<Char>(), arg);
auto str_end = str + specs.precision;
auto nul = std::find(str, str_end, Char());
arg = detail::make_arg<basic_printf_context>(basic_string_view<Char>(
str,
detail::to_unsigned(nul != str_end ? nul - str : specs.precision)));
}
if (specs.alt && visit_format_arg(detail::is_zero_int(), arg))
specs.alt = false;
if (specs.fill[0] == '0') {
if (arg.is_arithmetic())
if (arg.is_arithmetic() && specs.align != align::left)
specs.align = align::numeric;
else
specs.fill[0] = ' '; // Ignore '0' flag for non-numeric types.
specs.fill[0] = ' '; // Ignore '0' flag for non-numeric types or if '-'
// flag is also present.
}
// Parse length and convert the argument to the required type.
c = it != end ? *it++ : 0;
char_type t = it != end ? *it : 0;
using internal::convert_arg;
using detail::convert_arg;
switch (c) {
case 'h':
if (t == 'h') {
@ -524,7 +557,7 @@ OutputIt basic_printf_context<OutputIt, Char>::format() {
convert_arg<intmax_t>(arg, t);
break;
case 'z':
convert_arg<std::size_t>(arg, t);
convert_arg<size_t>(arg, t);
break;
case 't':
convert_arg<std::ptrdiff_t>(arg, t);
@ -549,7 +582,7 @@ OutputIt basic_printf_context<OutputIt, Char>::format() {
specs.type = 'd';
break;
case 'c':
visit_format_arg(internal::char_converter<basic_printf_context>(arg),
visit_format_arg(detail::char_converter<basic_printf_context>(arg),
arg);
break;
}
@ -558,15 +591,14 @@ OutputIt basic_printf_context<OutputIt, Char>::format() {
start = it;
// Format argument.
visit_format_arg(ArgFormatter(out, specs, *this), arg);
out = visit_format_arg(ArgFormatter(out, specs, *this), arg);
}
return std::copy(start, it, out);
}
template <typename Char>
using basic_printf_context_t =
basic_printf_context<std::back_insert_iterator<internal::buffer<Char>>,
Char>;
basic_printf_context<detail::buffer_appender<Char>, Char>;
using printf_context = basic_printf_context_t<char>;
using wprintf_context = basic_printf_context_t<wchar_t>;
@ -600,9 +632,10 @@ inline format_arg_store<wprintf_context, Args...> make_wprintf_args(
template <typename S, typename Char = char_t<S>>
inline std::basic_string<Char> vsprintf(
const S& format, basic_format_args<basic_printf_context_t<Char>> args) {
const S& format,
basic_format_args<basic_printf_context_t<type_identity_t<Char>>> args) {
basic_memory_buffer<Char> buffer;
printf(buffer, to_string_view(format), args);
vprintf(buffer, to_string_view(format), args);
return to_string(buffer);
}
@ -616,18 +649,19 @@ inline std::basic_string<Char> vsprintf(
\endrst
*/
template <typename S, typename... Args,
typename Char = enable_if_t<internal::is_string<S>::value, char_t<S>>>
typename Char = enable_if_t<detail::is_string<S>::value, char_t<S>>>
inline std::basic_string<Char> sprintf(const S& format, const Args&... args) {
using context = basic_printf_context_t<Char>;
return vsprintf(to_string_view(format), {make_format_args<context>(args...)});
return vsprintf(to_string_view(format), make_format_args<context>(args...));
}
template <typename S, typename Char = char_t<S>>
inline int vfprintf(std::FILE* f, const S& format,
basic_format_args<basic_printf_context_t<Char>> args) {
inline int vfprintf(
std::FILE* f, const S& format,
basic_format_args<basic_printf_context_t<type_identity_t<Char>>> args) {
basic_memory_buffer<Char> buffer;
printf(buffer, to_string_view(format), args);
std::size_t size = buffer.size();
vprintf(buffer, to_string_view(format), args);
size_t size = buffer.size();
return std::fwrite(buffer.data(), sizeof(Char), size, f) < size
? -1
: static_cast<int>(size);
@ -643,16 +677,17 @@ inline int vfprintf(std::FILE* f, const S& format,
\endrst
*/
template <typename S, typename... Args,
typename Char = enable_if_t<internal::is_string<S>::value, char_t<S>>>
typename Char = enable_if_t<detail::is_string<S>::value, char_t<S>>>
inline int fprintf(std::FILE* f, const S& format, const Args&... args) {
using context = basic_printf_context_t<Char>;
return vfprintf(f, to_string_view(format),
{make_format_args<context>(args...)});
make_format_args<context>(args...));
}
template <typename S, typename Char = char_t<S>>
inline int vprintf(const S& format,
basic_format_args<basic_printf_context_t<Char>> args) {
inline int vprintf(
const S& format,
basic_format_args<basic_printf_context_t<type_identity_t<Char>>> args) {
return vfprintf(stdout, to_string_view(format), args);
}
@ -666,19 +701,20 @@ inline int vprintf(const S& format,
\endrst
*/
template <typename S, typename... Args,
FMT_ENABLE_IF(internal::is_string<S>::value)>
FMT_ENABLE_IF(detail::is_string<S>::value)>
inline int printf(const S& format_str, const Args&... args) {
using context = basic_printf_context_t<char_t<S>>;
return vprintf(to_string_view(format_str),
{make_format_args<context>(args...)});
make_format_args<context>(args...));
}
template <typename S, typename Char = char_t<S>>
inline int vfprintf(std::basic_ostream<Char>& os, const S& format,
basic_format_args<basic_printf_context_t<Char>> args) {
inline int vfprintf(
std::basic_ostream<Char>& os, const S& format,
basic_format_args<basic_printf_context_t<type_identity_t<Char>>> args) {
basic_memory_buffer<Char> buffer;
printf(buffer, to_string_view(format), args);
internal::write(os, buffer);
vprintf(buffer, to_string_view(format), args);
detail::write_buffer(os, buffer);
return static_cast<int>(buffer.size());
}
@ -686,9 +722,9 @@ inline int vfprintf(std::basic_ostream<Char>& os, const S& format,
template <typename ArgFormatter, typename Char,
typename Context =
basic_printf_context<typename ArgFormatter::iterator, Char>>
typename ArgFormatter::iterator vprintf(internal::buffer<Char>& out,
basic_string_view<Char> format_str,
basic_format_args<Context> args) {
typename ArgFormatter::iterator vprintf(
detail::buffer<Char>& out, basic_string_view<Char> format_str,
basic_format_args<type_identity_t<Context>> args) {
typename ArgFormatter::iterator iter(out);
Context(iter, format_str, args).template format<ArgFormatter>();
return iter;
@ -708,7 +744,7 @@ inline int fprintf(std::basic_ostream<Char>& os, const S& format_str,
const Args&... args) {
using context = basic_printf_context_t<Char>;
return vfprintf(os, to_string_view(format_str),
{make_format_args<context>(args...)});
make_format_args<context>(args...));
}
FMT_END_NAMESPACE

@ -12,7 +12,9 @@
#ifndef FMT_RANGES_H_
#define FMT_RANGES_H_
#include <initializer_list>
#include <type_traits>
#include "format.h"
// output only up to N items from the range.
@ -31,7 +33,7 @@ template <typename Char> struct formatting_base {
template <typename Char, typename Enable = void>
struct formatting_range : formatting_base<Char> {
static FMT_CONSTEXPR_DECL const std::size_t range_length_limit =
static FMT_CONSTEXPR_DECL const size_t range_length_limit =
FMT_RANGE_OUTPUT_LENGTH_LIMIT; // output only up to N items from the
// range.
Char prefix;
@ -52,7 +54,7 @@ struct formatting_tuple : formatting_base<Char> {
static FMT_CONSTEXPR_DECL const bool add_prepostfix_space = false;
};
namespace internal {
namespace detail {
template <typename RangeT, typename OutputIterator>
OutputIterator copy(const RangeT& range, OutputIterator out) {
@ -104,10 +106,7 @@ struct is_range_<
/// tuple_size and tuple_element check.
template <typename T> class is_tuple_like_ {
template <typename U>
static auto check(U* p)
-> decltype(std::tuple_size<U>::value,
(void)std::declval<typename std::tuple_element<0, U>::type>(),
int());
static auto check(U* p) -> decltype(std::tuple_size<U>::value, int());
template <typename> static void check(...);
public:
@ -119,26 +118,24 @@ template <typename T> class is_tuple_like_ {
#if defined(__cpp_lib_integer_sequence) || FMT_MSC_VER >= 1900
template <typename T, T... N>
using integer_sequence = std::integer_sequence<T, N...>;
template <std::size_t... N> using index_sequence = std::index_sequence<N...>;
template <std::size_t N>
using make_index_sequence = std::make_index_sequence<N>;
template <size_t... N> using index_sequence = std::index_sequence<N...>;
template <size_t N> using make_index_sequence = std::make_index_sequence<N>;
#else
template <typename T, T... N> struct integer_sequence {
using value_type = T;
static FMT_CONSTEXPR std::size_t size() { return sizeof...(N); }
static FMT_CONSTEXPR size_t size() { return sizeof...(N); }
};
template <std::size_t... N>
using index_sequence = integer_sequence<std::size_t, N...>;
template <size_t... N> using index_sequence = integer_sequence<size_t, N...>;
template <typename T, std::size_t N, T... Ns>
template <typename T, size_t N, T... Ns>
struct make_integer_sequence : make_integer_sequence<T, N - 1, N - 1, Ns...> {};
template <typename T, T... Ns>
struct make_integer_sequence<T, 0, Ns...> : integer_sequence<T, Ns...> {};
template <std::size_t N>
using make_index_sequence = make_integer_sequence<std::size_t, N>;
template <size_t N>
using make_index_sequence = make_integer_sequence<size_t, N>;
#endif
template <class Tuple, class F, size_t... Is>
@ -160,6 +157,9 @@ template <class Tuple, class F> void for_each(Tuple&& tup, F&& f) {
for_each(indexes, std::forward<Tuple>(tup), std::forward<F>(f));
}
template <typename Range>
using value_type = remove_cvref_t<decltype(*std::declval<Range>().begin())>;
template <typename Arg, FMT_ENABLE_IF(!is_like_std_string<
typename std::decay<Arg>::type>::value)>
FMT_CONSTEXPR const char* format_str_quoted(bool add_space, const Arg&) {
@ -185,12 +185,11 @@ FMT_CONSTEXPR const char* format_str_quoted(bool add_space, const char) {
FMT_CONSTEXPR const wchar_t* format_str_quoted(bool add_space, const wchar_t) {
return add_space ? L" '{}'" : L"'{}'";
}
} // namespace internal
} // namespace detail
template <typename T> struct is_tuple_like {
static FMT_CONSTEXPR_DECL const bool value =
internal::is_tuple_like_<T>::value && !internal::is_range_<T>::value;
detail::is_tuple_like_<T>::value && !detail::is_range_<T>::value;
};
template <typename TupleT, typename Char>
@ -203,17 +202,17 @@ struct formatter<TupleT, Char, enable_if_t<fmt::is_tuple_like<TupleT>::value>> {
if (formatting.add_prepostfix_space) {
*out++ = ' ';
}
out = internal::copy(formatting.delimiter, out);
out = detail::copy(formatting.delimiter, out);
}
out = format_to(out,
internal::format_str_quoted(
detail::format_str_quoted(
(formatting.add_delimiter_spaces && i > 0), v),
v);
++i;
}
formatting_tuple<Char>& formatting;
std::size_t& i;
size_t& i;
typename std::add_lvalue_reference<decltype(
std::declval<FormatContext>().out())>::type out;
};
@ -229,14 +228,14 @@ struct formatter<TupleT, Char, enable_if_t<fmt::is_tuple_like<TupleT>::value>> {
template <typename FormatContext = format_context>
auto format(const TupleT& values, FormatContext& ctx) -> decltype(ctx.out()) {
auto out = ctx.out();
std::size_t i = 0;
internal::copy(formatting.prefix, out);
size_t i = 0;
detail::copy(formatting.prefix, out);
internal::for_each(values, format_each<FormatContext>{formatting, i, out});
detail::for_each(values, format_each<FormatContext>{formatting, i, out});
if (formatting.add_prepostfix_space) {
*out++ = ' ';
}
internal::copy(formatting.postfix, out);
detail::copy(formatting.postfix, out);
return ctx.out();
}
@ -244,14 +243,23 @@ struct formatter<TupleT, Char, enable_if_t<fmt::is_tuple_like<TupleT>::value>> {
template <typename T, typename Char> struct is_range {
static FMT_CONSTEXPR_DECL const bool value =
internal::is_range_<T>::value &&
!internal::is_like_std_string<T>::value &&
!std::is_convertible<T, std::basic_string<Char>>::value;
detail::is_range_<T>::value && !detail::is_like_std_string<T>::value &&
!std::is_convertible<T, std::basic_string<Char>>::value &&
!std::is_constructible<detail::std_string_view<Char>, T>::value;
};
template <typename RangeT, typename Char>
struct formatter<RangeT, Char,
enable_if_t<fmt::is_range<RangeT, Char>::value>> {
template <typename T, typename Char>
struct formatter<
T, Char,
enable_if_t<fmt::is_range<T, Char>::value
// Workaround a bug in MSVC 2017 and earlier.
#if !FMT_MSC_VER || FMT_MSC_VER >= 1927
&&
(has_formatter<detail::value_type<T>, format_context>::value ||
detail::has_fallback_formatter<detail::value_type<T>,
format_context>::value)
#endif
>> {
formatting_range<Char> formatting;
template <typename ParseContext>
@ -260,17 +268,18 @@ struct formatter<RangeT, Char,
}
template <typename FormatContext>
typename FormatContext::iterator format(const RangeT& values,
FormatContext& ctx) {
auto out = internal::copy(formatting.prefix, ctx.out());
std::size_t i = 0;
for (auto it = values.begin(), end = values.end(); it != end; ++it) {
typename FormatContext::iterator format(const T& values, FormatContext& ctx) {
auto out = detail::copy(formatting.prefix, ctx.out());
size_t i = 0;
auto it = values.begin();
auto end = values.end();
for (; it != end; ++it) {
if (i > 0) {
if (formatting.add_prepostfix_space) *out++ = ' ';
out = internal::copy(formatting.delimiter, out);
out = detail::copy(formatting.delimiter, out);
}
out = format_to(out,
internal::format_str_quoted(
detail::format_str_quoted(
(formatting.add_delimiter_spaces && i > 0), *it),
*it);
if (++i > formatting.range_length_limit) {
@ -279,10 +288,109 @@ struct formatter<RangeT, Char,
}
}
if (formatting.add_prepostfix_space) *out++ = ' ';
return internal::copy(formatting.postfix, out);
return detail::copy(formatting.postfix, out);
}
};
template <typename Char, typename... T> struct tuple_arg_join : detail::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, detail::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,
detail::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};
}
/**
\rst
Returns an object that formats `initializer_list` with elements separated by
`sep`.
**Example**::
fmt::print("{}", fmt::join({1, 2, 3}, ", "));
// Output: "1, 2, 3"
\endrst
*/
template <typename T>
arg_join<const T*, const T*, char> join(std::initializer_list<T> list,
string_view sep) {
return join(std::begin(list), std::end(list), sep);
}
template <typename T>
arg_join<const T*, const T*, wchar_t> join(std::initializer_list<T> list,
wstring_view sep) {
return join(std::begin(list), std::end(list), sep);
}
FMT_END_NAMESPACE
#endif // FMT_RANGES_H_