[upstream_utils] Upgrade to fmt 12.2.0 (#9018)

This commit is contained in:
Tyler Veness
2026-06-25 16:27:48 -07:00
committed by GitHub
parent 27ce84d367
commit a99c090332
21 changed files with 1786 additions and 1362 deletions

View File

@@ -925,7 +925,7 @@ See http://www.wtfpl.net/ for more details.
======
fmtlib
======
Copyright (c) 2012 - present, Victor Zverovich
Copyright (c) 2012 - present, Victor Zverovich and {fmt} contributors
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the

View File

@@ -34,7 +34,7 @@ def copy_upstream_src(wpilib_root: Path):
def main():
name = "fmt"
url = "https://github.com/fmtlib/fmt"
tag = "12.1.0"
tag = "12.2.0"
fmt = Lib(name, url, tag, copy_upstream_src)
fmt.main()

View File

@@ -1,22 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Tyler Veness <calcmogul@gmail.com>
Date: Fri, 31 Oct 2025 14:53:37 -0700
Subject: [PATCH] Suppress nodiscard
---
include/fmt/base.h | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/include/fmt/base.h b/include/fmt/base.h
index 0d57867782fcf571e7581b2bc70fa04bf43e4ccc..6a222b50eb515486a47dd5f37b94665e7f3ffef8 100644
--- a/include/fmt/base.h
+++ b/include/fmt/base.h
@@ -927,7 +927,7 @@ class locale_ref {
template <typename Locale, FMT_ENABLE_IF(sizeof(Locale::collate) != 0)>
locale_ref(const Locale& loc) : locale_(&loc) {
// Check if std::isalpha is found via ADL to reduce the chance of misuse.
- isalpha('x', loc);
+ static_cast<void>(isalpha('x', loc));
}
inline explicit operator bool() const noexcept { return locale_ != nullptr; }

View File

@@ -1,6 +1,6 @@
// Formatting library for C++ - dynamic argument lists
//
// Copyright (c) 2012 - present, Victor Zverovich
// Copyright (c) 2012 - present, Victor Zverovich and {fmt} contributors
// All rights reserved.
//
// For the license information refer to format.h.
@@ -113,8 +113,7 @@ FMT_EXPORT template <typename Context> class dynamic_format_arg_store {
data_.emplace_back(arg);
}
template <typename T>
void emplace_arg(const detail::named_arg<char_type, T>& arg) {
template <typename T> void emplace_arg(const named_arg<T, char_type>& arg) {
if (named_info_.empty())
data_.insert(data_.begin(), basic_format_arg<Context>(nullptr, 0));
data_.emplace_back(detail::unwrap(arg.value));
@@ -152,7 +151,7 @@ FMT_EXPORT template <typename Context> class dynamic_format_arg_store {
* std::string result = fmt::vformat("{} and {} and {}", store);
*/
template <typename T> void push_back(const T& arg) {
if (detail::const_check(need_copy<T>::value))
if FMT_CONSTEXPR20 (need_copy<T>::value)
emplace_arg(dynamic_args_.push<stored_t<T>>(arg));
else
emplace_arg(detail::unwrap(arg));
@@ -183,11 +182,10 @@ FMT_EXPORT template <typename Context> class dynamic_format_arg_store {
* formatting function. `std::reference_wrapper` is supported to avoid
* copying of the argument. The name is always copied into the store.
*/
template <typename T>
void push_back(const detail::named_arg<char_type, T>& arg) {
template <typename T> void push_back(const named_arg<T, char_type>& arg) {
const char_type* arg_name =
dynamic_args_.push<std::basic_string<char_type>>(arg.name).c_str();
if (detail::const_check(need_copy<T>::value)) {
if FMT_CONSTEXPR20 (need_copy<T>::value) {
emplace_arg(
fmt::arg(arg_name, dynamic_args_.push<stored_t<T>>(arg.value)));
} else {

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
// Formatting library for C++ - chrono support
//
// Copyright (c) 2012 - present, Victor Zverovich
// Copyright (c) 2012 - present, Victor Zverovich and {fmt} contributors
// All rights reserved.
//
// For the license information refer to format.h.
@@ -52,7 +52,7 @@ FMT_CONSTEXPR auto lossless_integral_conversion(const From from, int& ec)
static_assert(T::is_integer, "To must be integral");
// A and B are both signed, or both unsigned.
if (detail::const_check(F::digits <= T::digits)) {
if FMT_CONSTEXPR20 (F::digits <= T::digits) {
// From fits in To without any problem.
} else {
// From does not always fit in To, resort to a dynamic check.
@@ -79,22 +79,21 @@ FMT_CONSTEXPR auto lossless_integral_conversion(const From from, int& ec)
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)) {
if FMT_CONSTEXPR20 (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 (detail::const_check(F::digits > T::digits) &&
if (F::digits > T::digits &&
from > static_cast<From>(detail::max_value<To>())) {
ec = 1;
return {};
}
}
if (detail::const_check(!F::is_signed && T::is_signed &&
F::digits >= T::digits) &&
if (!F::is_signed && T::is_signed && F::digits >= T::digits &&
from > static_cast<From>(detail::max_value<To>())) {
ec = 1;
return {};
@@ -188,7 +187,7 @@ auto safe_duration_cast(std::chrono::duration<FromRep, FromPeriod> from,
}
// multiply with Factor::num without overflow or underflow
if (detail::const_check(Factor::num != 1)) {
if FMT_CONSTEXPR20 (Factor::num != 1) {
constexpr auto max1 = detail::max_value<IntermediateRep>() /
static_cast<IntermediateRep>(Factor::num);
if (count > max1) {
@@ -205,7 +204,7 @@ auto safe_duration_cast(std::chrono::duration<FromRep, FromPeriod> from,
}
// this can't go wrong, right? den>0 is checked earlier.
if (detail::const_check(Factor::den != 1)) {
if FMT_CONSTEXPR20 (Factor::den != 1) {
using common_t = typename std::common_type<IntermediateRep, intmax_t>::type;
count /= static_cast<common_t>(Factor::den);
}
@@ -337,7 +336,7 @@ void write_codecvt(codecvt_result<CodeUnit>& out, string_view in,
template <typename OutputIt>
auto write_encoded_tm_str(OutputIt out, string_view in, const std::locale& loc)
-> OutputIt {
if (const_check(detail::use_utf8) && loc != get_classic_locale()) {
if (detail::use_utf8 && loc != get_classic_locale()) {
// char16_t and char32_t codecvts are broken in MSVC (linkage errors) and
// gcc-4.
#if FMT_MSC_VERSION != 0 || \
@@ -434,14 +433,14 @@ auto duration_cast(std::chrono::duration<FromRep, FromPeriod> from) -> To {
common_rep count = from.count(); // This conversion is lossless.
// Multiply from.count() by factor and check for overflow.
if (const_check(factor::num != 1)) {
if FMT_CONSTEXPR20 (factor::num != 1) {
if (count > max_value<common_rep>() / factor::num) throw_duration_error();
const auto min = (std::numeric_limits<common_rep>::min)() / factor::num;
if (const_check(!std::is_unsigned<common_rep>::value) && count < min)
if (!std::is_unsigned<common_rep>::value && count < min)
throw_duration_error();
count *= factor::num;
}
if (const_check(factor::den != 1)) count /= factor::den;
if FMT_CONSTEXPR20 (factor::den != 1) count /= factor::den;
int ec = 0;
auto to =
To(safe_duration_cast::lossless_integral_conversion<typename To::rep>(
@@ -544,8 +543,7 @@ namespace detail {
// https://johnnylee-sde.github.io/Fast-unsigned-integer-to-time-string/.
inline void write_digit2_separated(char* buf, unsigned a, unsigned b,
unsigned c, char sep) {
unsigned long long digits =
a | (b << 24) | (static_cast<unsigned long long>(c) << 48);
ullong digits = a | (b << 24) | (static_cast<ullong>(c) << 48);
// Convert each value to BCD.
// We have x = a * 10 + b and we want to convert it to BCD y = a * 16 + b.
// The difference is
@@ -559,12 +557,12 @@ inline void write_digit2_separated(char* buf, unsigned a, unsigned b,
// Put low nibbles to high bytes and high nibbles to low bytes.
digits = ((digits & 0x00f00000f00000f0) >> 4) |
((digits & 0x000f00000f00000f) << 8);
auto usep = static_cast<unsigned long long>(sep);
auto usep = static_cast<ullong>(sep);
// Add ASCII '0' to each digit byte and insert separators.
digits |= 0x3030003030003030 | (usep << 16) | (usep << 40);
constexpr size_t len = 8;
if (const_check(is_big_endian())) {
if (is_big_endian()) {
char tmp[len];
std::memcpy(tmp, &digits, len);
std::reverse_copy(tmp, tmp + len, buf);
@@ -1194,6 +1192,7 @@ class tm_writer {
template <typename T, FMT_ENABLE_IF(has_tm_zone<T>::value)>
void format_tz_name(const T& tm) {
if (!tm.tm_zone) FMT_THROW(format_error("no timezone"));
out_ = write_tm_str<Char>(out_, tm.tm_zone, loc_);
}
template <typename T, FMT_ENABLE_IF(!has_tm_zone<T>::value)>
@@ -1576,7 +1575,7 @@ auto format_duration_unit(OutputIt out) -> OutputIt {
return copy_unit(string_view(unit), out, Char());
*out++ = '[';
out = write<Char>(out, Period::num);
if (const_check(Period::den != 1)) {
if FMT_CONSTEXPR20 (Period::den != 1) {
*out++ = '/';
out = write<Char>(out, Period::den);
}
@@ -2179,9 +2178,9 @@ struct formatter<sys_time<Duration>, Char> : private formatter<std::tm, Char> {
-> decltype(ctx.out()) {
std::tm tm = gmtime(val);
using period = typename Duration::period;
if (detail::const_check(
period::num == 1 && period::den == 1 &&
!std::is_floating_point<typename Duration::rep>::value)) {
if FMT_CONSTEXPR20 (period::num == 1 && period::den == 1 &&
!std::is_floating_point<
typename Duration::rep>::value) {
detail::set_tm_zone(tm, detail::utc());
return formatter<std::tm, Char>::format(tm, ctx);
}

View File

@@ -1,6 +1,6 @@
// Formatting library for C++ - color support
//
// Copyright (c) 2018 - present, Victor Zverovich and fmt contributors
// Copyright (c) 2018 - present, Victor Zverovich and {fmt} contributors
// All rights reserved.
//
// For the license information refer to format.h.
@@ -155,7 +155,7 @@ enum class color : uint32_t {
white_smoke = 0xF5F5F5, // rgb(245,245,245)
yellow = 0xFFFF00, // rgb(255,255,0)
yellow_green = 0x9ACD32 // rgb(154,205,50)
}; // enum class color
}; // enum class color
enum class terminal_color : uint8_t {
black = 30,
@@ -471,7 +471,7 @@ template <typename Char> inline void reset_color(buffer<Char>& buffer) {
template <typename T> struct styled_arg : view {
const T& value;
text_style style;
styled_arg(const T& v, text_style s) : value(v), style(s) {}
FMT_CONSTEXPR styled_arg(const T& v, text_style s) : value(v), style(s) {}
};
template <typename Char>
@@ -528,6 +528,42 @@ void print(text_style ts, format_string<T...> fmt, T&&... args) {
return print(stdout, ts, fmt, std::forward<T>(args)...);
}
inline void vprintln(FILE* f, text_style ts, string_view fmt,
format_args args) {
auto buf = memory_buffer();
detail::vformat_to(buf, ts, fmt, args);
buf.push_back('\n');
print(f, FMT_STRING("{}"), string_view(buf.begin(), buf.size()));
}
/**
* Formats a string and prints it to the specified file stream followed by a
* newline, using ANSI escape sequences to specify text formatting.
*
* **Example**:
*
* fmt::println(fmt::emphasis::bold | fg(fmt::color::red),
* "Elapsed time: {0:.2f} seconds", 1.23);
*/
template <typename... T>
void println(FILE* f, text_style ts, format_string<T...> fmt, T&&... args) {
vprintln(f, ts, fmt.str, vargs<T...>{{args...}});
}
/**
* Formats a string and prints it to stdout followed by a newline, using ANSI
* escape sequences to specify text formatting.
*
* **Example**:
*
* fmt::println(fmt::emphasis::bold | fg(fmt::color::red),
* "Elapsed time: {0:.2f} seconds", 1.23);
*/
template <typename... T>
void println(text_style ts, format_string<T...> fmt, T&&... args) {
return println(stdout, ts, fmt, std::forward<T>(args)...);
}
inline auto vformat(text_style ts, string_view fmt, format_args args)
-> std::string {
auto buf = memory_buffer();
@@ -583,8 +619,8 @@ inline auto format_to(OutputIt out, text_style ts, format_string<T...> fmt,
template <typename T, typename Char>
struct formatter<detail::styled_arg<T>, Char> : formatter<T, Char> {
template <typename FormatContext>
auto format(const detail::styled_arg<T>& arg, FormatContext& ctx) const
-> decltype(ctx.out()) {
FMT_CONSTEXPR auto format(const detail::styled_arg<T>& arg,
FormatContext& ctx) const -> decltype(ctx.out()) {
const auto& ts = arg.style;
auto out = ctx.out();

View File

@@ -1,6 +1,6 @@
// Formatting library for C++ - experimental format string compilation
//
// Copyright (c) 2012 - present, Victor Zverovich and fmt contributors
// Copyright (c) 2012 - present, Victor Zverovich and {fmt} contributors
// All rights reserved.
//
// For the license information refer to format.h.
@@ -267,13 +267,15 @@ constexpr auto parse_text(basic_string_view<Char> str, size_t pos) -> size_t {
return pos;
}
template <typename Args, size_t POS, int ID, typename S>
template <typename Args, size_t POS, int ID, bool DYNAMIC_NAMES, typename S>
constexpr auto compile_format_string(S fmt);
template <typename Args, size_t POS, int ID, typename T, typename S>
template <typename Args, size_t POS, int ID, bool DYNAMIC_NAMES, typename T,
typename S>
constexpr auto parse_tail(T head, S fmt) {
if constexpr (POS != basic_string_view<typename S::char_type>(fmt).size()) {
constexpr auto tail = compile_format_string<Args, POS, ID>(fmt);
constexpr auto tail =
compile_format_string<Args, POS, ID, DYNAMIC_NAMES>(fmt);
if constexpr (std::is_same<remove_cvref_t<decltype(tail)>,
unknown_format>())
return tail;
@@ -347,13 +349,13 @@ struct field_type<T, enable_if_t<detail::is_named_arg<T>::value>> {
};
template <typename T, typename Args, size_t END_POS, int ARG_INDEX, int NEXT_ID,
typename S>
bool DYNAMIC_NAMES, typename S>
constexpr auto parse_replacement_field_then_tail(S fmt) {
using char_type = typename S::char_type;
constexpr auto str = basic_string_view<char_type>(fmt);
constexpr char_type c = END_POS != str.size() ? str[END_POS] : char_type();
if constexpr (c == '}') {
return parse_tail<Args, END_POS + 1, NEXT_ID>(
return parse_tail<Args, END_POS + 1, NEXT_ID, DYNAMIC_NAMES>(
field<char_type, typename field_type<T>::type, ARG_INDEX>(), fmt);
} else if constexpr (c != ':') {
FMT_THROW(format_error("expected ':'"));
@@ -364,7 +366,8 @@ constexpr auto parse_replacement_field_then_tail(S fmt) {
FMT_THROW(format_error("expected '}'"));
return 0;
} else {
return parse_tail<Args, result.end + 1, result.next_arg_id>(
return parse_tail<Args, result.end + 1, result.next_arg_id,
DYNAMIC_NAMES>(
spec_field<char_type, typename field_type<T>::type, ARG_INDEX>{
result.fmt},
fmt);
@@ -374,7 +377,7 @@ constexpr auto parse_replacement_field_then_tail(S fmt) {
// Compiles a non-empty format string and returns the compiled representation
// or unknown_format() on unrecognized input.
template <typename Args, size_t POS, int ID, typename S>
template <typename Args, size_t POS, int ID, bool DYNAMIC_NAMES, typename S>
constexpr auto compile_format_string(S fmt) {
using char_type = typename S::char_type;
constexpr auto str = basic_string_view<char_type>(fmt);
@@ -382,14 +385,15 @@ constexpr auto compile_format_string(S fmt) {
if constexpr (POS + 1 == str.size())
FMT_THROW(format_error("unmatched '{' in format string"));
if constexpr (str[POS + 1] == '{') {
return parse_tail<Args, POS + 2, ID>(make_text(str, POS, 1), fmt);
return parse_tail<Args, POS + 2, ID, DYNAMIC_NAMES>(
make_text(str, POS, 1), fmt);
} else if constexpr (str[POS + 1] == '}' || str[POS + 1] == ':') {
static_assert(ID != manual_indexing_id,
"cannot switch from manual to automatic argument indexing");
constexpr auto next_id =
ID != manual_indexing_id ? ID + 1 : manual_indexing_id;
return parse_replacement_field_then_tail<get_type<ID, Args>, Args,
POS + 1, ID, next_id>(fmt);
return parse_replacement_field_then_tail<
get_type<ID, Args>, Args, POS + 1, ID, next_id, DYNAMIC_NAMES>(fmt);
} else {
constexpr auto arg_id_result =
parse_arg_id<ID>(str.data() + POS + 1, str.data() + str.size());
@@ -402,21 +406,24 @@ constexpr auto compile_format_string(S fmt) {
ID == manual_indexing_id || ID == 0,
"cannot switch from automatic to manual argument indexing");
constexpr auto arg_index = arg_id_result.arg_id.index;
return parse_replacement_field_then_tail<get_type<arg_index, Args>,
Args, arg_id_end_pos,
arg_index, manual_indexing_id>(
fmt);
return parse_replacement_field_then_tail<
get_type<arg_index, Args>, Args, arg_id_end_pos, arg_index,
manual_indexing_id, DYNAMIC_NAMES>(fmt);
} else if constexpr (arg_id_result.kind == arg_id_kind::name) {
constexpr auto arg_index =
get_arg_index_by_name(arg_id_result.arg_id.name, Args{});
static_assert(arg_index >= 0 || DYNAMIC_NAMES,
"named argument not found");
if constexpr (arg_index >= 0) {
constexpr auto next_id =
ID != manual_indexing_id ? ID + 1 : manual_indexing_id;
return parse_replacement_field_then_tail<
decltype(get_type<arg_index, Args>::value), Args, arg_id_end_pos,
arg_index, next_id>(fmt);
arg_index, next_id, DYNAMIC_NAMES>(fmt);
} else if constexpr (c == '}') {
return parse_tail<Args, arg_id_end_pos + 1, ID>(
return parse_tail<Args, arg_id_end_pos + 1, ID, DYNAMIC_NAMES>(
runtime_named_field<char_type>{arg_id_result.arg_id.name}, fmt);
} else if constexpr (c == ':') {
return unknown_format(); // no type info for specs parsing
@@ -426,13 +433,16 @@ constexpr auto compile_format_string(S fmt) {
} else if constexpr (str[POS] == '}') {
if constexpr (POS + 1 == str.size())
FMT_THROW(format_error("unmatched '}' in format string"));
return parse_tail<Args, POS + 2, ID>(make_text(str, POS, 1), fmt);
return parse_tail<Args, POS + 2, ID, DYNAMIC_NAMES>(make_text(str, POS, 1),
fmt);
} else {
constexpr auto end = parse_text(str, POS + 1);
if constexpr (end - POS > 1) {
return parse_tail<Args, end, ID>(make_text(str, POS, end - POS), fmt);
return parse_tail<Args, end, ID, DYNAMIC_NAMES>(
make_text(str, POS, end - POS), fmt);
} else {
return parse_tail<Args, end, ID>(code_unit<char_type>{str[POS]}, fmt);
return parse_tail<Args, end, ID, DYNAMIC_NAMES>(
code_unit<char_type>{str[POS]}, fmt);
}
}
}
@@ -444,8 +454,11 @@ constexpr auto compile(S fmt) {
if constexpr (str.size() == 0) {
return detail::make_text(str, 0, 0);
} else {
constexpr auto result =
detail::compile_format_string<detail::type_list<Args...>, 0, 0>(fmt);
constexpr int num_static_named_args =
detail::count_static_named_args<Args...>();
constexpr auto result = detail::compile_format_string<
detail::type_list<Args...>, 0, 0,
num_static_named_args != detail::count_named_args<Args...>()>(fmt);
return result;
}
}
@@ -522,7 +535,7 @@ auto format_to_n(OutputIt out, size_t n, const S& fmt, T&&... args)
-> format_to_n_result<OutputIt> {
using traits = detail::fixed_buffer_traits;
auto buf = detail::iterator_buffer<OutputIt, char, traits>(out, n);
fmt::format_to(std::back_inserter(buf), fmt, std::forward<T>(args)...);
fmt::format_to(appender(buf), fmt, std::forward<T>(args)...);
return {buf.out(), buf.count()};
}
@@ -559,8 +572,8 @@ template <size_t N> class static_format_result {
*fmt::format_to(data, fmt, std::forward<T>(args)...) = '\0';
}
auto str() const -> fmt::string_view { return {data, N - 1}; }
auto c_str() const -> const char* { return data; }
FMT_CONSTEXPR auto str() const -> fmt::string_view { return {data, N - 1}; }
FMT_CONSTEXPR auto c_str() const -> const char* { return data; }
};
/**

View File

@@ -1,5 +1,14 @@
// This file is only provided for compatibility and may be removed in future
// versions. Use fmt/base.h if you don't need fmt::format and fmt/format.h
// otherwise.
// Formatting library for C++ - core API
//
// Copyright (c) 2012 - present, Victor Zverovich and {fmt} contributors
// All rights reserved.
//
// For the license information refer to format.h.
#include "format.h"
#include "base.h"
// Using fmt::format via fmt/core.h has been deprecated since version 11
// and now requires an explicit opt in.
#ifdef FMT_DEPRECATED_HEAVY_CORE
# include "format.h"
#endif

View File

@@ -0,0 +1,201 @@
// Formatting library for C++ - the C API
//
// Copyright (c) 2012 - present, Victor Zverovich and {fmt} contributors
// All rights reserved.
//
// For the license information refer to format.h.
#ifndef FMT_C_H_
#define FMT_C_H_
#include <stdbool.h> // bool
#include <stddef.h> // size_t
#include <stdio.h> // FILE
#ifdef __cplusplus
extern "C" {
#endif
typedef enum {
fmt_int = 1,
fmt_uint,
fmt_bool = 7,
fmt_char,
fmt_float,
fmt_double,
fmt_long_double,
fmt_cstring,
fmt_pointer = 14
} fmt_type;
typedef struct {
fmt_type type;
union {
long long int_value;
unsigned long long uint_value; // Used for FMT_PTR and custom data
bool bool_value;
char char_value;
float float_value;
double double_value;
long double long_double_value;
const char* cstring;
const void* pointer;
} value;
} fmt_arg;
enum { fmt_error = -1, fmt_error_invalid_arg = -2 };
int fmt_vformat(char* buffer, size_t size, const char* fmt, const fmt_arg* args,
size_t num_args);
int fmt_vprint(FILE* stream, const char* fmt, const fmt_arg* args,
size_t num_args);
#ifdef __cplusplus
}
#endif
#ifndef __cplusplus
static inline fmt_arg fmt_from_int(long long x) {
return (fmt_arg){.type = fmt_int, .value.int_value = x};
}
static inline fmt_arg fmt_from_uint(unsigned long long x) {
return (fmt_arg){.type = fmt_uint, .value.uint_value = x};
}
static inline fmt_arg fmt_from_bool(bool x) {
return (fmt_arg){.type = fmt_bool, .value.bool_value = x};
}
static inline fmt_arg fmt_from_char(char x) {
return (fmt_arg){.type = fmt_char, .value.char_value = x};
}
static inline fmt_arg fmt_from_float(float x) {
return (fmt_arg){.type = fmt_float, .value.float_value = x};
}
static inline fmt_arg fmt_from_double(double x) {
return (fmt_arg){.type = fmt_double, .value.double_value = x};
}
static inline fmt_arg fmt_from_long_double(long double x) {
return (fmt_arg){.type = fmt_long_double, .value.long_double_value = x};
}
static inline fmt_arg fmt_from_str(const char* x) {
return (fmt_arg){.type = fmt_cstring, .value.cstring = x};
}
static inline fmt_arg fmt_from_ptr(const void* x) {
return (fmt_arg){.type = fmt_pointer, .value.pointer = x};
}
void fmt_unsupported_type(void);
# if !defined(_MSC_VER) || defined(__clang__)
typedef signed char fmt_signed_char;
# else
typedef enum {} fmt_signed_char;
# endif
// Require modern MSVC with conformant preprocessor.
# if defined(_MSC_VER) && !defined(__clang__) && \
(!defined(_MSVC_TRADITIONAL) || _MSVC_TRADITIONAL)
# error "C API requires MSVC 2019+ with /Zc:preprocessor flag."
# endif
# define FMT_MAKE_ARG(x) \
_Generic((x), \
fmt_signed_char: fmt_from_int, \
unsigned char: fmt_from_uint, \
short: fmt_from_int, \
unsigned short: fmt_from_uint, \
int: fmt_from_int, \
unsigned int: fmt_from_uint, \
long: fmt_from_int, \
unsigned long: fmt_from_uint, \
long long: fmt_from_int, \
unsigned long long: fmt_from_uint, \
bool: fmt_from_bool, \
char: fmt_from_char, \
float: fmt_from_float, \
double: fmt_from_double, \
long double: fmt_from_long_double, \
char*: fmt_from_str, \
const char*: fmt_from_str, \
void*: fmt_from_ptr, \
const void*: fmt_from_ptr, \
default: fmt_unsupported_type)(x)
# define FMT_CAT(a, b) FMT_CAT_(a, b)
# define FMT_CAT_(a, b) a##b
# define FMT_NARG_(_unused, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, \
_12, _13, _14, _15, _16, N, ...) \
N
# define FMT_NARG(_unused, ...) \
FMT_NARG_(, ##__VA_ARGS__, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, \
3, 2, 1, 0)
# define FMT_MAP_0(...)
# define FMT_MAP_1(f, a) f(a)
# define FMT_MAP_2(f, a, b) f(a), f(b)
# define FMT_MAP_3(f, a, b, c) f(a), f(b), f(c)
# define FMT_MAP_4(f, a, b, c, d) f(a), f(b), f(c), f(d)
# define FMT_MAP_5(f, a, b, c, d, e) f(a), f(b), f(c), f(d), f(e)
# define FMT_MAP_6(f, a, b, c, d, e, g) f(a), f(b), f(c), f(d), f(e), f(g)
# define FMT_MAP_7(f, a, b, c, d, e, g, h) \
f(a), f(b), f(c), f(d), f(e), f(g), f(h)
# define FMT_MAP_8(f, a, b, c, d, e, g, h, i) \
f(a), f(b), f(c), f(d), f(e), f(g), f(h), f(i)
# define FMT_MAP_9(f, a, b, c, d, e, g, h, i, j) \
f(a), f(b), f(c), f(d), f(e), f(g), f(h), f(i), f(j)
# define FMT_MAP_10(f, a, b, c, d, e, g, h, i, j, k) \
f(a), f(b), f(c), f(d), f(e), f(g), f(h), f(i), f(j), f(k)
# define FMT_MAP_11(f, a, b, c, d, e, g, h, i, j, k, l) \
f(a), f(b), f(c), f(d), f(e), f(g), f(h), f(i), f(j), f(k), f(l)
# define FMT_MAP_12(f, a, b, c, d, e, g, h, i, j, k, l, m) \
f(a), f(b), f(c), f(d), f(e), f(g), f(h), f(i), f(j), f(k), f(l), f(m)
# define FMT_MAP_13(f, a, b, c, d, e, g, h, i, j, k, l, m, n) \
f(a), f(b), f(c), f(d), f(e), f(g), f(h), f(i), f(j), f(k), f(l), f(m), f(n)
# define FMT_MAP_14(f, a, b, c, d, e, g, h, i, j, k, l, m, n, o) \
f(a), f(b), f(c), f(d), f(e), f(g), f(h), f(i), f(j), f(k), f(l), f(m), \
f(n), f(o)
# define FMT_MAP_15(f, a, b, c, d, e, g, h, i, j, k, l, m, n, o, p) \
f(a), f(b), f(c), f(d), f(e), f(g), f(h), f(i), f(j), f(k), f(l), f(m), \
f(n), f(o), f(p)
# define FMT_MAP_16(f, a, b, c, d, e, g, h, i, j, k, l, m, n, o, p, q) \
f(a), f(b), f(c), f(d), f(e), f(g), f(h), f(i), f(j), f(k), f(l), f(m), \
f(n), f(o), f(p), f(q)
# define FMT_MAP(f, ...) \
FMT_CAT(FMT_MAP_, FMT_NARG(, ##__VA_ARGS__))(f, ##__VA_ARGS__)
// select between two expressions depending on whether __VA_ARGS__ is empty
// expands to e if __VA_ARGS__ is empty and n otherwise
# define FMT_VA_SELECT(e, n, ...) \
FMT_NARG_(, ##__VA_ARGS__, n, n, n, n, n, n, n, n, n, n, n, n, n, n, n, n, \
e)
# define FMT_MAKE_NULL(...) NULL
# define FMT_MAKE_ARGLIST(...) \
(fmt_arg[]) { FMT_MAP(FMT_MAKE_ARG, ##__VA_ARGS__) }
# define FMT_EXPAND(v) v
# define FMT_FORMAT_ARGS(fmt, ...) \
(fmt), \
FMT_EXPAND(FMT_VA_SELECT(FMT_MAKE_NULL, FMT_MAKE_ARGLIST, \
##__VA_ARGS__)(__VA_ARGS__)), \
FMT_NARG(, ##__VA_ARGS__)
# define fmt_format(buffer, size, fmt, ...) \
fmt_vformat((buffer), (size), FMT_FORMAT_ARGS((fmt), ##__VA_ARGS__))
# define fmt_print(stream, fmt, ...) \
fmt_vprint((stream), FMT_FORMAT_ARGS((fmt), ##__VA_ARGS__))
#endif // __cplusplus
#endif // FMT_C_H_

File diff suppressed because it is too large Load Diff

View File

@@ -1,7 +1,7 @@
/*
Formatting library for C++
Copyright (c) 2012 - present, Victor Zverovich
Copyright (c) 2012 - present, Victor Zverovich and {fmt} contributors
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
@@ -47,14 +47,12 @@
#endif
#ifndef FMT_MODULE
# include <stdint.h> // uint32_t
# include <stdlib.h> // malloc, free
# include <string.h> // memcpy
# include <cmath> // std::signbit
# include <cstddef> // std::byte
# include <cstdint> // uint32_t
# include <cstring> // std::memcpy
# include <limits> // std::numeric_limits
# include <new> // std::bad_alloc
# include <cmath> // std::signbit
# include <limits> // std::numeric_limits
# if defined(__GLIBCXX__) && !defined(_GLIBCXX_USE_DUAL_ABI)
// Workaround for pre gcc 5 libstdc++.
# include <memory> // std::allocator_traits
@@ -122,14 +120,6 @@
# define FMT_NOINLINE
#endif
#ifdef FMT_DEPRECATED
// Use the provided definition.
#elif FMT_HAS_CPP14_ATTRIBUTE(deprecated)
# define FMT_DEPRECATED [[deprecated]]
#else
# define FMT_DEPRECATED /* deprecated */
#endif
// Detect constexpr std::string.
#if !FMT_USE_CONSTEVAL
# define FMT_USE_CONSTEXPR_STRING 0
@@ -224,7 +214,6 @@ namespace detail {
inline auto clz(uint32_t x) -> int {
FMT_ASSERT(x != 0, "");
FMT_MSC_WARNING(suppress : 6102) // Suppress a bogus static analysis warning.
unsigned long r = 0;
_BitScanReverse(&r, x);
return 31 ^ static_cast<int>(r);
@@ -233,7 +222,6 @@ inline auto clz(uint32_t x) -> int {
inline auto clzll(uint64_t x) -> int {
FMT_ASSERT(x != 0, "");
FMT_MSC_WARNING(suppress : 6102) // Suppress a bogus static analysis warning.
unsigned long r = 0;
# ifdef _WIN64
_BitScanReverse64(&r, x);
@@ -283,7 +271,7 @@ FMT_CONSTEXPR20 auto bit_cast(const From& from) -> To {
#endif
auto to = To();
// The cast suppresses a bogus -Wclass-memaccess on GCC.
std::memcpy(static_cast<void*>(&to), &from, sizeof(to));
memcpy(static_cast<void*>(&to), &from, sizeof(to));
return to;
}
@@ -302,13 +290,13 @@ inline auto is_big_endian() -> bool {
#endif
}
class uint128_fallback {
class uint128 {
private:
uint64_t lo_, hi_;
public:
constexpr uint128_fallback(uint64_t hi, uint64_t lo) : lo_(lo), hi_(hi) {}
constexpr uint128_fallback(uint64_t value = 0) : lo_(value), hi_(0) {}
constexpr uint128(uint64_t hi, uint64_t lo) : lo_(lo), hi_(hi) {}
constexpr uint128(uint64_t value = 0) : lo_(value), hi_(0) {}
constexpr auto high() const noexcept -> uint64_t { return hi_; }
constexpr auto low() const noexcept -> uint64_t { return lo_; }
@@ -318,92 +306,84 @@ class uint128_fallback {
return static_cast<T>(lo_);
}
friend constexpr auto operator==(const uint128_fallback& lhs,
const uint128_fallback& rhs) -> bool {
friend constexpr auto operator==(const uint128& lhs, const uint128& rhs)
-> bool {
return lhs.hi_ == rhs.hi_ && lhs.lo_ == rhs.lo_;
}
friend constexpr auto operator!=(const uint128_fallback& lhs,
const uint128_fallback& rhs) -> bool {
friend constexpr auto operator!=(const uint128& lhs, const uint128& rhs)
-> bool {
return !(lhs == rhs);
}
friend constexpr auto operator>(const uint128_fallback& lhs,
const uint128_fallback& rhs) -> bool {
friend constexpr auto operator>(const uint128& lhs, const uint128& rhs)
-> bool {
return lhs.hi_ != rhs.hi_ ? lhs.hi_ > rhs.hi_ : lhs.lo_ > rhs.lo_;
}
friend constexpr auto operator|(const uint128_fallback& lhs,
const uint128_fallback& rhs)
-> uint128_fallback {
friend constexpr auto operator|(const uint128& lhs, const uint128& rhs)
-> uint128 {
return {lhs.hi_ | rhs.hi_, lhs.lo_ | rhs.lo_};
}
friend constexpr auto operator&(const uint128_fallback& lhs,
const uint128_fallback& rhs)
-> uint128_fallback {
friend constexpr auto operator&(const uint128& lhs, const uint128& rhs)
-> uint128 {
return {lhs.hi_ & rhs.hi_, lhs.lo_ & rhs.lo_};
}
friend constexpr auto operator~(const uint128_fallback& n)
-> uint128_fallback {
return {~n.hi_, ~n.lo_};
}
friend FMT_CONSTEXPR auto operator+(const uint128_fallback& lhs,
const uint128_fallback& rhs)
-> uint128_fallback {
auto result = uint128_fallback(lhs);
friend FMT_CONSTEXPR auto operator+(const uint128& lhs, const uint128& rhs)
-> uint128 {
auto result = uint128(lhs);
result += rhs;
return result;
}
friend FMT_CONSTEXPR auto operator*(const uint128_fallback& lhs, uint32_t rhs)
-> uint128_fallback {
friend FMT_CONSTEXPR auto operator*(const uint128& lhs, uint32_t rhs)
-> uint128 {
FMT_ASSERT(lhs.hi_ == 0, "");
uint64_t hi = (lhs.lo_ >> 32) * rhs;
uint64_t lo = (lhs.lo_ & ~uint32_t()) * rhs;
uint64_t new_lo = (hi << 32) + lo;
return {(hi >> 32) + (new_lo < lo ? 1 : 0), new_lo};
}
friend constexpr auto operator-(const uint128_fallback& lhs, uint64_t rhs)
-> uint128_fallback {
friend constexpr auto operator-(const uint128& lhs, uint64_t rhs) -> uint128 {
return {lhs.hi_ - (lhs.lo_ < rhs ? 1 : 0), lhs.lo_ - rhs};
}
FMT_CONSTEXPR auto operator>>(int shift) const -> uint128_fallback {
FMT_CONSTEXPR auto operator>>(int shift) const -> uint128 {
if (shift == 64) return {0, hi_};
if (shift > 64) return uint128_fallback(0, hi_) >> (shift - 64);
if (shift > 64) return uint128(0, hi_) >> (shift - 64);
return {hi_ >> shift, (hi_ << (64 - shift)) | (lo_ >> shift)};
}
FMT_CONSTEXPR auto operator<<(int shift) const -> uint128_fallback {
FMT_CONSTEXPR auto operator<<(int shift) const -> uint128 {
if (shift == 64) return {lo_, 0};
if (shift > 64) return uint128_fallback(lo_, 0) << (shift - 64);
if (shift > 64) return uint128(lo_, 0) << (shift - 64);
return {hi_ << shift | (lo_ >> (64 - shift)), (lo_ << shift)};
}
FMT_CONSTEXPR auto operator>>=(int shift) -> uint128_fallback& {
FMT_CONSTEXPR auto operator>>=(int shift) -> uint128& {
return *this = *this >> shift;
}
FMT_CONSTEXPR void operator+=(uint128_fallback n) {
FMT_CONSTEXPR void operator+=(uint128 n) {
uint64_t new_lo = lo_ + n.lo_;
uint64_t new_hi = hi_ + n.hi_ + (new_lo < lo_ ? 1 : 0);
FMT_ASSERT(new_hi >= hi_, "");
lo_ = new_lo;
hi_ = new_hi;
}
FMT_CONSTEXPR void operator&=(uint128_fallback n) {
FMT_CONSTEXPR void operator&=(uint128 n) {
lo_ &= n.lo_;
hi_ &= n.hi_;
}
FMT_CONSTEXPR20 auto operator+=(uint64_t n) noexcept -> uint128_fallback& {
FMT_CONSTEXPR20 auto operator+=(uint64_t n) noexcept -> uint128& {
if (is_constant_evaluated()) {
lo_ += n;
hi_ += (lo_ < n ? 1 : 0);
return *this;
}
#if FMT_HAS_BUILTIN(__builtin_addcll) && !defined(__ibmxl__)
unsigned long long carry;
ullong carry;
lo_ = __builtin_addcll(lo_, n, 0, &carry);
hi_ += carry;
#elif FMT_HAS_BUILTIN(__builtin_ia32_addcarryx_u64) && !defined(__ibmxl__)
unsigned long long result;
ullong result;
auto carry = __builtin_ia32_addcarryx_u64(0, lo_, n, &result);
lo_ = result;
hi_ += carry;
#elif defined(_MSC_VER) && defined(_M_X64)
#elif defined(_MSC_VER) && defined(_M_AMD64)
auto carry = _addcarry_u64(0, lo_, n, &lo_);
_addcarry_u64(carry, hi_, 0, &hi_);
#else
@@ -414,7 +394,7 @@ class uint128_fallback {
}
};
using uint128_t = conditional_t<FMT_USE_INT128, uint128_opt, uint128_fallback>;
using uint128_t = conditional_t<FMT_USE_INT128, native_uint128, uint128>;
#ifdef UINTPTR_MAX
using uintptr_t = ::uintptr_t;
@@ -431,12 +411,12 @@ template <typename T> constexpr auto num_bits() -> int {
return std::numeric_limits<T>::digits;
}
// std::numeric_limits<T>::digits may return 0 for 128-bit ints.
template <> constexpr auto num_bits<int128_opt>() -> int { return 128; }
template <> constexpr auto num_bits<uint128_opt>() -> int { return 128; }
template <> constexpr auto num_bits<uint128_fallback>() -> int { return 128; }
template <> constexpr auto num_bits<native_int128>() -> int { return 128; }
template <> constexpr auto num_bits<native_uint128>() -> int { return 128; }
template <> constexpr auto num_bits<uint128>() -> int { return 128; }
// A heterogeneous bit_cast used for converting 96-bit long double to uint128_t
// and 128-bit pointers to uint128_fallback.
// and 128-bit pointers to uint128.
template <typename To, typename From, FMT_ENABLE_IF(sizeof(To) > sizeof(From))>
inline auto bit_cast(const From& from) -> To {
constexpr auto size = static_cast<int>(sizeof(From) / sizeof(unsigned short));
@@ -444,7 +424,7 @@ inline auto bit_cast(const From& from) -> To {
unsigned short value[static_cast<unsigned>(size)];
} data = bit_cast<data_t>(from);
auto result = To();
if (const_check(is_big_endian())) {
if (is_big_endian()) {
for (int i = 0; i < size; ++i)
result = (result << num_bits<unsigned short>()) | data.value[i];
} else {
@@ -493,8 +473,8 @@ template <typename OutputIt,
#if FMT_CLANG_VERSION >= 307 && !FMT_ICC_VERSION
__attribute__((no_sanitize("undefined")))
#endif
FMT_CONSTEXPR20 inline auto
reserve(OutputIt it, size_t n) -> typename OutputIt::value_type* {
FMT_CONSTEXPR20 inline auto reserve(OutputIt it, size_t n) ->
typename OutputIt::value_type* {
auto& c = get_container(it);
size_t size = c.size();
c.resize(size + n);
@@ -564,10 +544,15 @@ FMT_CONSTEXPR20 auto fill_n(T* out, Size count, char value) -> T* {
if (is_constant_evaluated()) return fill_n<T*, Size, T>(out, count, value);
static_assert(sizeof(T) == 1,
"sizeof(T) must be 1 to use char for initialization");
std::memset(out, value, to_unsigned(count));
memset(out, value, to_unsigned(count));
return out + count;
}
template <typename T, typename V, typename OutputIt>
FMT_CONSTEXPR auto copy(basic_string_view<V> s, OutputIt out) -> OutputIt {
return copy<T>(s.begin(), s.end(), out);
}
template <typename OutChar, typename InputIt, typename OutputIt>
FMT_CONSTEXPR FMT_NOINLINE auto copy_noinline(InputIt begin, InputIt end,
OutputIt out) -> OutputIt {
@@ -690,13 +675,13 @@ FMT_CONSTEXPR inline auto display_width_of(uint32_t cp) noexcept -> size_t {
}
template <typename T> struct is_integral : std::is_integral<T> {};
template <> struct is_integral<int128_opt> : std::true_type {};
template <> struct is_integral<native_int128> : std::true_type {};
template <> struct is_integral<uint128_t> : std::true_type {};
template <typename T>
using is_signed =
std::integral_constant<bool, std::numeric_limits<T>::is_signed ||
std::is_same<T, int128_opt>::value>;
std::is_same<T, native_int128>::value>;
template <typename T>
using is_integer =
@@ -736,21 +721,17 @@ using fast_float_t = conditional_t<sizeof(T) == sizeof(double), double, float>;
template <typename T>
using is_double_double = bool_constant<std::numeric_limits<T>::digits == 106>;
#ifndef FMT_USE_FULL_CACHE_DRAGONBOX
# define FMT_USE_FULL_CACHE_DRAGONBOX 0
#endif
FMT_API auto allocate(size_t size) -> void*;
// An allocator that uses malloc/free to allow removing dependency on the C++
// standard libary runtime. std::decay is used for back_inserter to be found by
// standard library runtime. std::decay is used for back_inserter to be found by
// ADL when applied to memory_buffer.
template <typename T> struct allocator : private std::decay<void> {
using value_type = T;
auto allocate(size_t n) -> T* {
FMT_ASSERT(n <= max_value<size_t>() / sizeof(T), "");
T* p = static_cast<T*>(malloc(n * sizeof(T)));
if (!p) FMT_THROW(std::bad_alloc());
return p;
return static_cast<T*>(detail::allocate(n * sizeof(T)));
}
void deallocate(T* p, size_t) { free(p); }
@@ -984,7 +965,7 @@ FMT_API void print(FILE*, string_view);
namespace detail {
template <typename Char, size_t N> struct fixed_string {
FMT_CONSTEXPR20 fixed_string(const Char (&s)[N]) {
FMT_CONSTEXPR fixed_string(const Char (&s)[N]) {
detail::copy<Char, const Char*, Char*>(static_cast<const Char*>(s), s + N,
data);
}
@@ -1031,12 +1012,12 @@ using uint64_or_128_t = conditional_t<num_bits<T>() <= 64, uint64_t, uint128_t>;
(factor) * 100000, (factor) * 1000000, (factor) * 10000000, \
(factor) * 100000000, (factor) * 1000000000
// Converts value in the range [0, 100) to a string.
// GCC generates slightly better code when value is pointer-size.
inline auto digits2(size_t value) -> const char* {
// Converts value in the range [0, 100) to a string. GCC generates a bit better
// code when value is pointer-size (https://www.godbolt.org/z/5fEPMT1cc).
inline auto digits2(size_t value) noexcept -> const char* {
// Align data since unaligned access may be slower when crossing a
// hardware-specific boundary.
alignas(2) static const char data[] =
alignas(2) static constexpr char data[] =
"0001020304050607080910111213141516171819"
"2021222324252627282930313233343536373839"
"4041424344454647484950515253545556575859"
@@ -1045,9 +1026,25 @@ inline auto digits2(size_t value) -> const char* {
return &data[value * 2];
}
// Given i in [0, 100), let x be the first 7 digits after
// the decimal point of i / 100 in base 2, the first 2 bytes
// after digits2_i(x) is the string representation of i.
inline auto digits2_i(size_t value) noexcept -> const char* {
alignas(2) static constexpr char data[] =
"00010203 0405060707080910 1112"
"131414151617 18192021 222324 "
"25262728 2930313232333435 3637"
"383939404142 43444546 474849 "
"50515253 5455565757585960 6162"
"636464656667 68697071 727374 "
"75767778 7980818282838485 8687"
"888989909192 93949596 979899 ";
return &data[value * 2];
}
template <typename Char> constexpr auto getsign(sign s) -> Char {
return static_cast<char>(((' ' << 24) | ('+' << 16) | ('-' << 8)) >>
(static_cast<int>(s) * 8));
return static_cast<Char>(static_cast<char>(
((' ' << 24) | ('+' << 16) | ('-' << 8)) >> (static_cast<int>(s) * 8)));
}
template <typename T> FMT_CONSTEXPR auto count_digits_fallback(T n) -> int {
@@ -1065,7 +1062,7 @@ template <typename T> FMT_CONSTEXPR auto count_digits_fallback(T n) -> int {
}
}
#if FMT_USE_INT128
FMT_CONSTEXPR inline auto count_digits(uint128_opt n) -> int {
FMT_CONSTEXPR inline auto count_digits(native_uint128 n) -> int {
return count_digits_fallback(n);
}
#endif
@@ -1153,7 +1150,9 @@ FMT_CONSTEXPR20 inline auto count_digits(uint32_t n) -> int {
template <typename Int> constexpr auto digits10() noexcept -> int {
return std::numeric_limits<Int>::digits10;
}
template <> constexpr auto digits10<int128_opt>() noexcept -> int { return 38; }
template <> constexpr auto digits10<native_int128>() noexcept -> int {
return 38;
}
template <> constexpr auto digits10<uint128_t>() noexcept -> int { return 38; }
template <typename Char> struct thousands_sep_result {
@@ -1166,7 +1165,7 @@ FMT_API auto thousands_sep_impl(locale_ref loc) -> thousands_sep_result<Char>;
template <typename Char>
inline auto thousands_sep(locale_ref loc) -> thousands_sep_result<Char> {
auto result = thousands_sep_impl<char>(loc);
return {result.grouping, Char(result.thousands_sep)};
return {std::move(result.grouping), Char(result.thousands_sep)};
}
template <>
inline auto thousands_sep(locale_ref loc) -> thousands_sep_result<wchar_t> {
@@ -1213,6 +1212,16 @@ FMT_CONSTEXPR20 FMT_INLINE void write2digits(Char* out, size_t value) {
*out = static_cast<Char>('0' + value % 10);
}
template <typename Char>
FMT_INLINE void write2digits_i(Char* out, size_t value) {
if (std::is_same<Char, char>::value && !FMT_OPTIMIZE_SIZE) {
memcpy(out, digits2_i(value), 2);
return;
}
*out++ = static_cast<Char>(digits2_i(value)[0]);
*out = static_cast<Char>(digits2_i(value)[1]);
}
// Formats a decimal unsigned integer value writing to out pointing to a buffer
// of specified size. The caller must ensure that the buffer is large enough.
template <typename Char, typename UInt>
@@ -1221,12 +1230,19 @@ FMT_CONSTEXPR20 auto do_format_decimal(Char* out, UInt value, int size)
FMT_ASSERT(size >= count_digits(value), "invalid digit count");
unsigned n = to_unsigned(size);
while (value >= 100) {
// Integer division is slow so do it for a group of two digits instead
// of for every digit. The idea comes from the talk by Alexandrescu
// "Three Optimization Tips for C++". See speed-test for a comparison.
n -= 2;
write2digits(out + n, static_cast<unsigned>(value % 100));
value /= 100;
if (!is_constant_evaluated() && sizeof(UInt) == 4) {
auto p = value * static_cast<uint64_t>((1ull << 39) / 100 + 1);
write2digits_i(out + n, p >> (39 - 7) & ((1 << 7) - 1));
value = static_cast<UInt>(p >> 39) +
(static_cast<UInt>(value >= (100u << 25)) << 25);
} else {
// Integer division is slow so do it for a group of two digits instead
// of for every digit. The idea comes from the talk by Alexandrescu
// "Three Optimization Tips for C++". See speed-test for a comparison.
write2digits(out + n, static_cast<unsigned>(value % 100));
value /= 100;
}
}
if (value >= 10) {
n -= 2;
@@ -1311,7 +1327,13 @@ class utf8_to_utf16 {
inline auto str() const -> std::wstring { return {&buffer_[0], size()}; }
};
enum class to_utf8_error_policy { abort, replace };
enum class to_utf8_error_policy { abort, replace, wtf };
inline void to_utf8_3bytes(buffer<char>& buf, uint32_t cp) {
buf.push_back(static_cast<char>(0xe0 | (cp >> 12)));
buf.push_back(static_cast<char>(0x80 | ((cp & 0xfff) >> 6)));
buf.push_back(static_cast<char>(0x80 | (cp & 0x3f)));
}
// A converter from UTF-16/UTF-32 (host endian) to UTF-8.
template <typename WChar, typename Buffer = memory_buffer> class to_utf8 {
@@ -1353,8 +1375,13 @@ template <typename WChar, typename Buffer = memory_buffer> class to_utf8 {
// Handle a surrogate pair.
++p;
if (p == s.end() || (c & 0xfc00) != 0xd800 || (*p & 0xfc00) != 0xdc00) {
if (policy == to_utf8_error_policy::abort) return false;
buf.append(string_view("\xEF\xBF\xBD"));
switch (policy) {
case to_utf8_error_policy::abort: return false;
case to_utf8_error_policy::replace:
buf.append(string_view("\xEF\xBF\xBD"));
break;
case to_utf8_error_policy::wtf: to_utf8_3bytes(buf, c); break;
}
--p;
continue;
}
@@ -1366,9 +1393,7 @@ template <typename WChar, typename Buffer = memory_buffer> class to_utf8 {
buf.push_back(static_cast<char>(0xc0 | (c >> 6)));
buf.push_back(static_cast<char>(0x80 | (c & 0x3f)));
} else if ((c >= 0x800 && c <= 0xd7ff) || (c >= 0xe000 && c <= 0xffff)) {
buf.push_back(static_cast<char>(0xe0 | (c >> 12)));
buf.push_back(static_cast<char>(0x80 | ((c & 0xfff) >> 6)));
buf.push_back(static_cast<char>(0x80 | (c & 0x3f)));
to_utf8_3bytes(buf, c);
} else if (c >= 0x10000 && c <= 0x10ffff) {
buf.push_back(static_cast<char>(0xf0 | (c >> 18)));
buf.push_back(static_cast<char>(0x80 | ((c & 0x3ffff) >> 12)));
@@ -1383,11 +1408,11 @@ template <typename WChar, typename Buffer = memory_buffer> class to_utf8 {
};
// Computes 128-bit result of multiplication of two 64-bit unsigned integers.
FMT_INLINE auto umul128(uint64_t x, uint64_t y) noexcept -> uint128_fallback {
FMT_INLINE auto umul128(uint64_t x, uint64_t y) noexcept -> uint128 {
#if FMT_USE_INT128
auto p = static_cast<uint128_opt>(x) * static_cast<uint128_opt>(y);
auto p = static_cast<native_uint128>(x) * static_cast<native_uint128>(y);
return {static_cast<uint64_t>(p >> 64), static_cast<uint64_t>(p)};
#elif defined(_MSC_VER) && defined(_M_X64)
#elif defined(_MSC_VER) && defined(_M_AMD64)
auto hi = uint64_t();
auto lo = _umul128(x, y, &hi);
return {hi, lo};
@@ -1428,9 +1453,9 @@ inline auto floor_log2_pow10(int e) noexcept -> int {
// Computes upper 64 bits of multiplication of two 64-bit unsigned integers.
inline auto umul128_upper64(uint64_t x, uint64_t y) noexcept -> uint64_t {
#if FMT_USE_INT128
auto p = static_cast<uint128_opt>(x) * static_cast<uint128_opt>(y);
auto p = static_cast<native_uint128>(x) * static_cast<native_uint128>(y);
return static_cast<uint64_t>(p >> 64);
#elif defined(_MSC_VER) && defined(_M_X64)
#elif defined(_MSC_VER) && defined(_M_AMD64)
return __umulh(x, y);
#else
return umul128(x, y).high();
@@ -1439,40 +1464,39 @@ inline auto umul128_upper64(uint64_t x, uint64_t y) noexcept -> uint64_t {
// Computes upper 128 bits of multiplication of a 64-bit unsigned integer and a
// 128-bit unsigned integer.
inline auto umul192_upper128(uint64_t x, uint128_fallback y) noexcept
-> uint128_fallback {
uint128_fallback r = umul128(x, y.high());
inline auto umul192_upper128(uint64_t x, uint128 y) noexcept -> uint128 {
uint128 r = umul128(x, y.high());
r += umul128_upper64(x, y.low());
return r;
}
FMT_API auto get_cached_power(int k) noexcept -> uint128_fallback;
FMT_API auto get_cached_power(int k) noexcept -> uint128;
// Type-specific information that Dragonbox uses.
template <typename T, typename Enable = void> struct float_info;
template <> struct float_info<float> {
using carrier_uint = uint32_t;
static const int exponent_bits = 8;
static const int kappa = 1;
static const int big_divisor = 100;
static const int small_divisor = 10;
static const int min_k = -31;
static const int max_k = 46;
static const int shorter_interval_tie_lower_threshold = -35;
static const int shorter_interval_tie_upper_threshold = -35;
static constexpr int exponent_bits = 8;
static constexpr int kappa = 1;
static constexpr int big_divisor = 100;
static constexpr int small_divisor = 10;
static constexpr int min_k = -31;
enum { max_k = 46 };
static constexpr int shorter_interval_tie_lower_threshold = -35;
static constexpr int shorter_interval_tie_upper_threshold = -35;
};
template <> struct float_info<double> {
using carrier_uint = uint64_t;
static const int exponent_bits = 11;
static const int kappa = 2;
static const int big_divisor = 1000;
static const int small_divisor = 100;
static const int min_k = -292;
static const int max_k = 341;
static const int shorter_interval_tie_lower_threshold = -77;
static const int shorter_interval_tie_upper_threshold = -77;
static constexpr int exponent_bits = 11;
static constexpr int kappa = 2;
static constexpr int big_divisor = 1000;
static constexpr int small_divisor = 100;
static constexpr int min_k = -292;
enum { max_k = 341 };
static constexpr int shorter_interval_tie_lower_threshold = -77;
static constexpr int shorter_interval_tie_upper_threshold = -77;
};
// An 80- or 128-bit floating point number.
@@ -1608,7 +1632,7 @@ template <typename F> struct basic_fp {
}
};
using fp = basic_fp<unsigned long long>;
using fp = basic_fp<ullong>;
// Normalizes the value converted from double and multiplied by (1 << SHIFT).
template <int SHIFT = 0, typename F>
@@ -1739,7 +1763,7 @@ FMT_API auto is_printable(uint32_t cp) -> bool;
inline auto needs_escape(uint32_t cp) -> bool {
if (cp < 0x20 || cp == 0x7f || cp == '"' || cp == '\\') return true;
if (const_check(FMT_OPTIMIZE_SIZE > 1)) return false;
if FMT_CONSTEXPR20 (FMT_OPTIMIZE_SIZE > 1) return false;
return !is_printable(cp);
}
@@ -1754,7 +1778,7 @@ auto find_escape(const Char* begin, const Char* end)
-> find_escape_result<Char> {
for (; begin != end; ++begin) {
uint32_t cp = static_cast<unsigned_char<Char>>(*begin);
if (const_check(sizeof(Char) == 1) && cp >= 0x80) continue;
if (sizeof(Char) == 1 && cp >= 0x80) continue;
if (needs_escape(cp)) return {begin, begin + 1, cp};
}
return {begin, nullptr, 0};
@@ -1762,7 +1786,7 @@ auto find_escape(const Char* begin, const Char* end)
inline auto find_escape(const char* begin, const char* end)
-> find_escape_result<char> {
if (const_check(!use_utf8)) return find_escape<char>(begin, end);
if FMT_CONSTEXPR20 (!use_utf8) return find_escape<char>(begin, end);
auto result = find_escape_result<char>{end, nullptr, 0};
for_each_codepoint(string_view(begin, to_unsigned(end - begin)),
[&](uint32_t cp, string_view sv) {
@@ -1890,7 +1914,7 @@ template <typename Char> class digit_grouping {
explicit digit_grouping(locale_ref loc, bool localized = true) {
if (!localized) return;
auto sep = thousands_sep<Char>(loc);
grouping_ = sep.grouping;
grouping_ = std::move(sep.grouping);
if (sep.thousands_sep) thousands_sep_.assign(1, sep.thousands_sep);
}
digit_grouping(std::string grouping, std::basic_string<Char> sep)
@@ -2335,9 +2359,8 @@ FMT_CONSTEXPR auto parse_align(const Char* begin, const Char* end,
++begin;
}
break;
} else if (p == begin) {
break;
}
if (p == begin) break;
p = begin;
}
specs.set_align(alignment);
@@ -2534,7 +2557,7 @@ FMT_CONSTEXPR20 auto write_fixed(OutputIt out, const DecimalFP& f,
auto grouping = Grouping(loc, specs.localized());
size += grouping.count_separators(exp);
return write_padded<Char, align::right>(
out, specs, to_unsigned(size), [&](iterator it) {
out, specs, static_cast<size_t>(size), [&](iterator it) {
if (s != sign::none) *it++ = detail::getsign<Char>(s);
it = write_significand(it, f.significand, significand_size, exp,
decimal_point, grouping);
@@ -2550,7 +2573,7 @@ FMT_CONSTEXPR20 auto write_fixed(OutputIt out, const DecimalFP& f,
bool pointy = num_zeros != 0 || significand_size != 0 || specs.alt();
size += 1 + (pointy ? 1 : 0) + num_zeros;
return write_padded<Char, align::right>(
out, specs, to_unsigned(size), [&](iterator it) {
out, specs, static_cast<size_t>(size), [&](iterator it) {
if (s != sign::none) *it++ = detail::getsign<Char>(s);
*it++ = Char('0');
if (!pointy) return it;
@@ -2594,7 +2617,7 @@ FMT_CONSTEXPR20 auto do_write_float(OutputIt out, const DecimalFP& f,
*it++ = Char(exp_char);
return write_exponent<Char>(exp, it);
};
auto usize = to_unsigned(size);
size_t usize = static_cast<size_t>(size);
return specs.width > 0
? write_padded<Char, align::right>(out, specs, usize, write)
: base_iterator(out, write(reserve(out, usize)));
@@ -3138,8 +3161,9 @@ constexpr auto fractional_part_rounding_thresholds(int index) -> uint32_t {
// It is equal to ceil(2^31 + 2^32/10^(k + 1)).
// These are stored in a string literal because we cannot have static arrays
// in constexpr functions and non-static ones are poorly optimized.
return U"\x9999999a\x828f5c29\x80418938\x80068db9\x8000a7c6\x800010c7"
U"\x800001ae\x8000002b"[index];
return uint32_t(u"\x9999\x828f\x8041\x8006\x8000\x8000\x8000\x8000"[index])
<< 16u |
uint32_t(u"\x999a\x5c29\x8938\x8db9\xa7c6\x10c7\x01ae\x002b"[index]);
}
template <typename Float>
@@ -3544,8 +3568,8 @@ FMT_CONSTEXPR20 auto write(OutputIt out, T value) -> OutputIt {
memcpy(ptr, prefix, 2);
ptr += 2;
} else {
*ptr++ = prefix[0];
*ptr++ = prefix[1];
*ptr++ = static_cast<Char>(prefix[0]);
*ptr++ = static_cast<Char>(prefix[1]);
}
if (abs_exponent >= 100) {
*ptr++ = static_cast<Char>('0' + abs_exponent / 100);
@@ -3697,12 +3721,12 @@ template <typename Char> struct arg_formatter {
struct dynamic_spec_getter {
template <typename T, FMT_ENABLE_IF(is_integer<T>::value)>
FMT_CONSTEXPR auto operator()(T value) -> unsigned long long {
return is_negative(value) ? ~0ull : static_cast<unsigned long long>(value);
FMT_CONSTEXPR auto operator()(T value) -> ullong {
return is_negative(value) ? ~0ull : static_cast<ullong>(value);
}
template <typename T, FMT_ENABLE_IF(!is_integer<T>::value)>
FMT_CONSTEXPR auto operator()(T) -> unsigned long long {
FMT_CONSTEXPR auto operator()(T) -> ullong {
report_error("width/precision is not integer");
return 0;
}
@@ -3716,7 +3740,7 @@ FMT_CONSTEXPR void handle_dynamic_spec(
auto arg =
kind == arg_id_kind::index ? ctx.arg(ref.index) : ctx.arg(ref.name);
if (!arg) report_error("argument not found");
unsigned long long result = arg.visit(dynamic_spec_getter());
ullong result = arg.visit(dynamic_spec_getter());
if (result > to_unsigned(max_value<int>()))
report_error("width/precision is out of range");
value = static_cast<int>(result);
@@ -3751,7 +3775,7 @@ struct udl_arg {
template <typename Char> struct udl_arg {
const Char* str;
template <typename T> auto operator=(T&& value) const -> named_arg<Char, T> {
template <typename T> auto operator=(T&& value) const -> named_arg<T, Char> {
return {str, std::forward<T>(value)};
}
};
@@ -3864,7 +3888,7 @@ template <typename OutputIt, typename Char> class generic_context {
constexpr auto out() const -> iterator { return out_; }
void advance_to(iterator it) {
FMT_CONSTEXPR void advance_to(iterator it) {
if (!detail::is_back_insert_iterator<iterator>()) out_ = it;
}
@@ -3906,8 +3930,8 @@ template <typename Locale> class format_facet : public Locale::facet {
explicit format_facet(string_view sep = "", std::string grouping = "\3",
std::string decimal_point = ".")
: separator_(sep.data(), sep.size()),
grouping_(grouping),
decimal_point_(decimal_point) {}
grouping_(std::move(grouping)),
decimal_point_(std::move(decimal_point)) {}
auto put(appender out, loc_value val, const format_specs& specs) const
-> bool {
@@ -3943,12 +3967,6 @@ template <typename Char, typename Traits, typename Allocator>
class formatter<std::basic_string<Char, Traits, Allocator>, Char>
: public formatter<basic_string_view<Char>, Char> {};
template <int N, typename Char>
struct formatter<detail::bitint<N>, Char> : formatter<long long, Char> {};
template <int N, typename Char>
struct formatter<detail::ubitint<N>, Char>
: formatter<unsigned long long, Char> {};
template <typename Char>
struct formatter<detail::float128, Char>
: detail::native_formatter<detail::float128, Char,
@@ -3997,19 +4015,6 @@ constexpr auto format_as(Enum e) noexcept -> underlying_t<Enum> {
}
} // namespace enums
#ifdef __cpp_lib_byte
template <typename Char>
struct formatter<std::byte, Char> : formatter<unsigned, Char> {
static auto format_as(std::byte b) -> unsigned char {
return static_cast<unsigned char>(b);
}
template <typename Context>
auto format(std::byte b, Context& ctx) const -> decltype(ctx.out()) {
return formatter<unsigned, Char>::format(format_as(b), ctx);
}
};
#endif
struct bytes {
string_view data;
@@ -4142,6 +4147,14 @@ template <typename T, typename Char = char> struct nested_formatter {
inline namespace literals {
#if FMT_USE_NONTYPE_TEMPLATE_ARGS
/**
* User-defined literal equivalent of `fmt::arg`, but with compile-time checks.
*
* **Example**:
*
* using namespace fmt::literals;
* fmt::print("The answer is {answer}.", "answer"_a=42);
*/
template <detail::fixed_string S> constexpr auto operator""_a() {
using char_t = remove_cvref_t<decltype(*S.data)>;
return detail::udl_arg<char_t, sizeof(S.data) / sizeof(char_t), S>();
@@ -4166,7 +4179,7 @@ class format_int {
private:
// Buffer should be large enough to hold all digits (digits10 + 1),
// a sign and a null character.
enum { buffer_size = std::numeric_limits<unsigned long long>::digits10 + 3 };
enum { buffer_size = std::numeric_limits<ullong>::digits10 + 3 };
mutable char buffer_[buffer_size];
char* str_;
@@ -4196,7 +4209,7 @@ class format_int {
: str_(format_unsigned(value)) {}
FMT_CONSTEXPR20 explicit format_int(unsigned long value)
: str_(format_unsigned(value)) {}
FMT_CONSTEXPR20 explicit format_int(unsigned long long value)
FMT_CONSTEXPR20 explicit format_int(ullong value)
: str_(format_unsigned(value)) {}
/// Returns the number of characters written to the output buffer.
@@ -4248,7 +4261,11 @@ class format_int {
* // A compile-time error because 'd' is an invalid specifier for strings.
* std::string s = fmt::format(FMT_STRING("{:d}"), "foo");
*/
#define FMT_STRING(s) FMT_STRING_IMPL(s, fmt::detail::compile_string)
#if FMT_USE_CONSTEVAL
# define FMT_STRING(s) s
#else
# define FMT_STRING(s) FMT_STRING_IMPL(s, fmt::detail::compile_string)
#endif // FMT_USE_CONSTEVAL
FMT_API auto vsystem_error(int error_code, string_view fmt, format_args args)
-> std::system_error;

View File

@@ -1,6 +1,6 @@
// Formatting library for C++ - optional OS-specific functionality
//
// Copyright (c) 2012 - present, Victor Zverovich
// Copyright (c) 2012 - present, Victor Zverovich and {fmt} contributors
// All rights reserved.
//
// For the license information refer to format.h.
@@ -161,14 +161,6 @@ inline auto system_category() noexcept -> const std::error_category& {
}
#endif // _WIN32
// std::system is not available on some platforms such as iOS (#2248).
#ifdef __OSX__
template <typename S, typename... Args, typename Char = char_t<S>>
void say(const S& fmt, Args&&... args) {
std::system(format("say \"{}\"", format(fmt, args...)).c_str());
}
#endif
// A buffered file.
class buffered_file {
private:

View File

@@ -1,6 +1,6 @@
// Formatting library for C++ - std::ostream support
//
// Copyright (c) 2012 - present, Victor Zverovich
// Copyright (c) 2012 - present, Victor Zverovich and {fmt} contributors
// All rights reserved.
//
// For the license information refer to format.h.
@@ -38,7 +38,7 @@ namespace detail {
namespace {
struct file_access_tag {};
} // namespace
template <typename Tag, typename BufType, FILE* BufType::*FileMemberPtr>
template <typename Tag, typename BufType, FILE* BufType::* FileMemberPtr>
class file_access {
friend auto get_file(BufType& obj) -> FILE* { return obj.*FileMemberPtr; }
};
@@ -150,7 +150,7 @@ inline void vprint(std::ostream& os, string_view fmt, format_args args) {
FMT_EXPORT template <typename... T>
void print(std::ostream& os, format_string<T...> fmt, T&&... args) {
fmt::vargs<T...> vargs = {{args...}};
if (detail::const_check(detail::use_utf8)) return vprint(os, fmt.str, vargs);
if FMT_CONSTEXPR20 (detail::use_utf8) return vprint(os, fmt.str, vargs);
auto buffer = memory_buffer();
detail::vformat_to(buffer, fmt.str, vargs);
detail::write_buffer(os, buffer);

View File

@@ -1,6 +1,6 @@
// Formatting library for C++ - legacy printf implementation
//
// Copyright (c) 2012 - 2016, Victor Zverovich
// Copyright (c) 2012 - present, Victor Zverovich and {fmt} contributors
// All rights reserved.
//
// For the license information refer to format.h.
@@ -136,7 +136,7 @@ template <typename T, typename Context> class arg_converter {
void operator()(U value) {
bool is_signed = type_ == 'd' || type_ == 'i';
using target_type = conditional_t<std::is_same<T, void>::value, U, T>;
if (const_check(sizeof(target_type) <= sizeof(int))) {
if FMT_CONSTEXPR20 (sizeof(target_type) <= sizeof(int)) {
// Extra casts are used to silence warnings.
using unsigned_type = typename make_unsigned_or_bool<target_type>::type;
if (is_signed)
@@ -330,6 +330,7 @@ template <typename Char, typename GetArg>
auto parse_header(const Char*& it, const Char* end, format_specs& specs,
GetArg get_arg) -> int {
int arg_index = -1;
if (it == end) return arg_index;
Char c = *it;
if (c >= '0' && c <= '9') {
// Parse an argument index (if followed by '$') or a width possibly
@@ -357,8 +358,21 @@ auto parse_header(const Char*& it, const Char* end, format_specs& specs,
if (specs.width == -1) report_error("number is too big");
} else if (*it == '*') {
++it;
specs.width = static_cast<int>(
get_arg(-1).visit(detail::printf_width_handler(specs)));
// Check for positional width argument like *1$
if (it != end && *it >= '0' && *it <= '9') {
int width_index = parse_nonnegative_int(it, end, -1);
if (it != end && *it == '$') {
++it;
specs.width = static_cast<int>(
get_arg(width_index).visit(detail::printf_width_handler(specs)));
} else {
// Invalid format, rewind and treat as non-positional
report_error("invalid format specifier");
}
} else {
specs.width = static_cast<int>(
get_arg(-1).visit(detail::printf_width_handler(specs)));
}
}
}
return arg_index;
@@ -424,6 +438,8 @@ void vprintf(buffer<Char>& buf, basic_string_view<Char> format,
}
write(out, basic_string_view<Char>(start, to_unsigned(it - 1 - start)));
if (it == end) report_error("invalid format string");
auto specs = format_specs();
specs.set_align(align::right);
@@ -439,15 +455,28 @@ void vprintf(buffer<Char>& buf, basic_string_view<Char> format,
specs.precision = parse_nonnegative_int(it, end, 0);
} else if (c == '*') {
++it;
specs.precision =
static_cast<int>(get_arg(-1).visit(printf_precision_handler()));
// Check for positional precision argument like .*1$
if (it != end && *it >= '0' && *it <= '9') {
int precision_index = parse_nonnegative_int(it, end, -1);
if (it != end && *it == '$') {
++it;
specs.precision = static_cast<int>(
get_arg(precision_index).visit(printf_precision_handler()));
} else {
// Invalid format, rewind and treat as non-positional
report_error("invalid format specifier");
}
} else {
specs.precision =
static_cast<int>(get_arg(-1).visit(printf_precision_handler()));
}
} else {
specs.precision = 0;
}
}
auto arg = get_arg(arg_index);
// For d, i, o, u, x, and X conversion specifiers, if a precision is
// For d, i, o, u, x and X conversion specifiers, if a precision is
// specified, the '0' flag is ignored
if (specs.precision >= 0 && is_integral_type(arg.type())) {
// Ignore '0' for non-numeric types or if '-' present.
@@ -560,7 +589,7 @@ inline auto vsprintf(basic_string_view<Char> fmt,
/**
* Formats `args` according to specifications in `fmt` and returns the result
* as as string.
* as string.
*
* **Example**:
*

View File

@@ -69,11 +69,12 @@ struct has_member_fn_begin_end_t<T, void_t<decltype(*std::declval<T>().begin()),
// Member function overloads.
template <typename T>
auto range_begin(T&& rng) -> decltype(static_cast<T&&>(rng).begin()) {
FMT_CONSTEXPR auto range_begin(T&& rng)
-> decltype(static_cast<T&&>(rng).begin()) {
return static_cast<T&&>(rng).begin();
}
template <typename T>
auto range_end(T&& rng) -> decltype(static_cast<T&&>(rng).end()) {
FMT_CONSTEXPR auto range_end(T&& rng) -> decltype(static_cast<T&&>(rng).end()) {
return static_cast<T&&>(rng).end();
}
@@ -129,6 +130,13 @@ template <typename T> class is_tuple_like_ {
!std::is_void<decltype(check<T>(nullptr))>::value;
};
template <typename T, typename _ = void>
struct is_optional_like_ : std::false_type {};
template <typename T>
struct is_optional_like_<T, void_t<decltype(std::declval<T>().has_value()),
decltype(std::declval<T>().value())>>
: std::true_type {};
// Check for integer_sequence
#if defined(__cpp_lib_integer_sequence) || FMT_MSC_VERSION >= 1900
template <typename T, T... N>
@@ -343,8 +351,9 @@ struct formatter<Tuple, Char,
FMT_EXPORT
template <typename T, typename Char> struct is_range {
static constexpr bool value =
detail::is_range_<T>::value && !detail::has_to_string_view<T>::value;
static constexpr bool value = detail::is_range_<T>::value &&
!detail::is_optional_like_<T>::value &&
!detail::has_to_string_view<T>::value;
};
namespace detail {
@@ -460,7 +469,8 @@ struct range_formatter<
}
template <typename R, typename FormatContext>
auto format(R&& range, FormatContext& ctx) const -> decltype(ctx.out()) {
FMT_CONSTEXPR auto format(R&& range, FormatContext& ctx) const
-> decltype(ctx.out()) {
auto out = ctx.out();
auto it = detail::range_begin(range);
auto end = detail::range_end(range);
@@ -505,8 +515,7 @@ struct formatter<
using nonlocking = void;
FMT_CONSTEXPR formatter() {
if (detail::const_check(range_format_kind<R, Char>::value !=
range_format::set))
if FMT_CONSTEXPR20 (range_format_kind<R, Char>::value != range_format::set)
return;
range_formatter_.set_brackets(detail::string_literal<Char, '{'>{},
detail::string_literal<Char, '}'>{});
@@ -517,7 +526,7 @@ struct formatter<
}
template <typename FormatContext>
auto format(range_type& range, FormatContext& ctx) const
FMT_CONSTEXPR auto format(range_type& range, FormatContext& ctx) const
-> decltype(ctx.out()) {
return range_formatter_.format(range, ctx);
}
@@ -607,13 +616,14 @@ struct formatter<
auto format(range_type& range, FormatContext& ctx) const
-> decltype(ctx.out()) {
auto out = ctx.out();
if (detail::const_check(range_format_kind<R, Char>::value ==
range_format::debug_string))
if FMT_CONSTEXPR20 (range_format_kind<R, Char>::value ==
range_format::debug_string) {
*out++ = '"';
}
out = underlying_.format(
string_type{detail::range_begin(range), detail::range_end(range)}, ctx);
if (detail::const_check(range_format_kind<R, Char>::value ==
range_format::debug_string))
if FMT_CONSTEXPR20 (range_format_kind<R, Char>::value ==
range_format::debug_string)
*out++ = '"';
return out;
}
@@ -625,7 +635,7 @@ struct join_view : detail::view {
Sentinel end;
basic_string_view<Char> sep;
join_view(It b, Sentinel e, basic_string_view<Char> s)
FMT_CONSTEXPR join_view(It b, Sentinel e, basic_string_view<Char> s)
: begin(std::move(b)), end(e), sep(s) {}
};
@@ -652,7 +662,8 @@ struct formatter<join_view<It, Sentinel, Char>, Char> {
}
template <typename FormatContext>
auto format(view& value, FormatContext& ctx) const -> decltype(ctx.out()) {
FMT_CONSTEXPR auto format(view& value, FormatContext& ctx) const
-> decltype(ctx.out()) {
using iter =
conditional_t<std::is_copy_constructible<view>::value, It, It&>;
iter it = value.begin;
@@ -675,7 +686,7 @@ template <typename Tuple, typename Char> struct tuple_join_view : detail::view {
const Tuple& tuple;
basic_string_view<Char> sep;
tuple_join_view(const Tuple& t, basic_string_view<Char> s)
FMT_CONSTEXPR tuple_join_view(const Tuple& t, basic_string_view<Char> s)
: tuple(t), sep{s} {}
};
@@ -694,8 +705,9 @@ struct formatter<tuple_join_view<Tuple, Char>, Char,
}
template <typename FormatContext>
auto format(const tuple_join_view<Tuple, Char>& value,
FormatContext& ctx) const -> typename FormatContext::iterator {
FMT_CONSTEXPR auto format(const tuple_join_view<Tuple, Char>& value,
FormatContext& ctx) const ->
typename FormatContext::iterator {
return do_format(value, ctx, std::tuple_size<Tuple>());
}
@@ -726,15 +738,17 @@ struct formatter<tuple_join_view<Tuple, Char>, Char,
}
template <typename FormatContext>
auto do_format(const tuple_join_view<Tuple, Char>&, FormatContext& ctx,
std::integral_constant<size_t, 0>) const ->
FMT_CONSTEXPR auto do_format(const tuple_join_view<Tuple, Char>&,
FormatContext& ctx,
std::integral_constant<size_t, 0>) const ->
typename FormatContext::iterator {
return ctx.out();
}
template <typename FormatContext, size_t N>
auto do_format(const tuple_join_view<Tuple, Char>& value, FormatContext& ctx,
std::integral_constant<size_t, N>) const ->
FMT_CONSTEXPR auto do_format(const tuple_join_view<Tuple, Char>& value,
FormatContext& ctx,
std::integral_constant<size_t, N>) const ->
typename FormatContext::iterator {
using std::get;
auto out =
@@ -748,9 +762,20 @@ struct formatter<tuple_join_view<Tuple, Char>, Char,
};
namespace detail {
// Check if T has an interface like a container adaptor (e.g. std::stack,
// std::queue, std::priority_queue).
template <typename T> class is_container_adaptor_like {
template <typename Container> struct all {
const Container& c;
auto begin() const -> typename Container::const_iterator { return c.begin(); }
auto end() const -> typename Container::const_iterator { return c.end(); }
};
} // namespace detail
/**
* Specifies if `T` is a container adaptor (like `std::stack`) that should be
* formatted as the underlying container.
*/
FMT_EXPORT
template <typename T> struct is_container_adaptor {
private:
template <typename U> static auto check(U* p) -> typename U::container_type;
template <typename> static void check(...);
@@ -759,17 +784,10 @@ template <typename T> class is_container_adaptor_like {
!std::is_void<decltype(check<T>(nullptr))>::value;
};
template <typename Container> struct all {
const Container& c;
auto begin() const -> typename Container::const_iterator { return c.begin(); }
auto end() const -> typename Container::const_iterator { return c.end(); }
};
} // namespace detail
template <typename T, typename Char>
struct formatter<
T, Char,
enable_if_t<conjunction<detail::is_container_adaptor_like<T>,
enable_if_t<conjunction<is_container_adaptor<T>,
bool_constant<range_format_kind<T, Char>::value ==
range_format::disabled>>::value>>
: formatter<detail::all<typename T::container_type>, Char> {
@@ -809,7 +827,7 @@ auto join(It begin, Sentinel end, string_view sep) -> join_view<It, Sentinel> {
* // Output: 01, 02, 03
*/
template <typename Range, FMT_ENABLE_IF(!is_tuple_like<Range>::value)>
auto join(Range&& r, string_view sep)
FMT_CONSTEXPR auto join(Range&& r, string_view sep)
-> join_view<decltype(detail::range_begin(r)),
decltype(detail::range_end(r))> {
return {detail::range_begin(r), detail::range_end(r), sep};
@@ -840,7 +858,7 @@ FMT_CONSTEXPR auto join(const Tuple& tuple FMT_LIFETIMEBOUND, string_view sep)
* // Output: "1, 2, 3"
*/
template <typename T>
auto join(std::initializer_list<T> list, string_view sep)
FMT_DEPRECATED auto join(std::initializer_list<T> list, string_view sep)
-> join_view<const T*, const T*> {
return join(std::begin(list), std::end(list), sep);
}

View File

@@ -1,6 +1,6 @@
// Formatting library for C++ - formatters for standard library types
//
// Copyright (c) 2012 - present, Victor Zverovich
// Copyright (c) 2012 - present, Victor Zverovich and {fmt} contributors
// All rights reserved.
//
// For the license information refer to format.h.
@@ -15,7 +15,8 @@
# include <atomic>
# include <bitset>
# include <complex>
# include <exception>
# include <cstddef> // std::byte
# include <exception> // std::exception
# include <functional> // std::reference_wrapper
# include <memory>
# include <thread>
@@ -79,15 +80,34 @@
FMT_BEGIN_NAMESPACE
namespace detail {
#ifdef FMT_USE_BITINT
// Use the provided definition.
#elif FMT_CLANG_VERSION >= 1500 && !defined(__CUDACC__)
# define FMT_USE_BITINT 1
#else
# define FMT_USE_BITINT 0
#endif
#if FMT_USE_BITINT
FMT_PRAGMA_CLANG(diagnostic ignored "-Wbit-int-extension")
template <int N> using bitint = _BitInt(N);
template <int N> using ubitint = unsigned _BitInt(N);
#else
template <int N> struct bitint {};
template <int N> struct ubitint {};
#endif // FMT_USE_BITINT
#if FMT_CPP_LIB_FILESYSTEM
template <typename Char, typename PathChar>
auto get_path_string(const std::filesystem::path& p,
const std::basic_string<PathChar>& native) {
if constexpr (std::is_same_v<Char, char> && std::is_same_v<PathChar, wchar_t>)
return to_utf8<wchar_t>(native, to_utf8_error_policy::replace);
else
if constexpr (std::is_same_v<Char, char> &&
std::is_same_v<PathChar, wchar_t>) {
return to_utf8<wchar_t>(native, to_utf8_error_policy::wtf);
} else {
return p.string<Char>();
}
}
template <typename Char, typename PathChar>
@@ -113,8 +133,8 @@ void write_escaped_path(basic_memory_buffer<Char>& quoted,
#if defined(__cpp_lib_expected) || FMT_CPP_LIB_VARIANT
template <typename Char, typename OutputIt, typename T, typename FormatContext>
auto write_escaped_alternative(OutputIt out, const T& v, FormatContext& ctx)
-> OutputIt {
FMT_CONSTEXPR auto write_escaped_alternative(OutputIt out, const T& v,
FormatContext& ctx) -> OutputIt {
if constexpr (has_to_string_view<T>::value)
return write_escaped_string<Char>(out, detail::to_string_view(v));
if constexpr (std::is_same_v<T, Char>) return write_escaped_char(out, v);
@@ -434,6 +454,26 @@ struct formatter<std::expected<T, E>, Char,
return out;
}
};
template <typename E, typename Char>
struct formatter<std::unexpected<E>, Char,
std::enable_if_t<is_formattable<E, Char>::value>> {
FMT_CONSTEXPR auto parse(parse_context<Char>& ctx) -> const Char* {
return ctx.begin();
}
template <typename FormatContext>
auto format(const std::unexpected<E>& value, FormatContext& ctx) const
-> decltype(ctx.out()) {
auto out = ctx.out();
out = detail::write<Char>(out, "unexpected(");
out = detail::write_escaped_alternative<Char>(out, value.error(), ctx);
*out++ = ')';
return out;
}
};
#endif // __cpp_lib_expected
#ifdef __cpp_lib_source_location
@@ -468,7 +508,7 @@ template <typename Char> struct formatter<std::monostate, Char> {
}
template <typename FormatContext>
auto format(const std::monostate&, FormatContext& ctx) const
FMT_CONSTEXPR auto format(const std::monostate&, FormatContext& ctx) const
-> decltype(ctx.out()) {
return detail::write<Char>(ctx.out(), "monostate");
}
@@ -484,7 +524,7 @@ struct formatter<Variant, Char,
}
template <typename FormatContext>
auto format(const Variant& value, FormatContext& ctx) const
FMT_CONSTEXPR20 auto format(const Variant& value, FormatContext& ctx) const
-> decltype(ctx.out()) {
auto out = ctx.out();
@@ -609,6 +649,30 @@ struct formatter<
}
};
template <int N, typename Char>
struct formatter<detail::bitint<N>, Char> : formatter<long long, Char> {
static_assert(N <= 64, "unsupported _BitInt");
static auto format_as(detail::bitint<N> x) -> long long {
return static_cast<long long>(x);
}
template <typename Context>
auto format(detail::bitint<N> x, Context& ctx) const -> decltype(ctx.out()) {
return formatter<long long, Char>::format(format_as(x), ctx);
}
};
template <int N, typename Char>
struct formatter<detail::ubitint<N>, Char> : formatter<ullong, Char> {
static_assert(N <= 64, "unsupported _BitInt");
static auto format_as(detail::ubitint<N> x) -> ullong {
return static_cast<ullong>(x);
}
template <typename Context>
auto format(detail::ubitint<N> x, Context& ctx) const -> decltype(ctx.out()) {
return formatter<ullong, Char>::format(format_as(x), ctx);
}
};
// We can't use std::vector<bool, Allocator>::reference and
// std::bitset<N>::reference because the compiler can't deduce Allocator and N
// in partial specialization.
@@ -623,6 +687,20 @@ struct formatter<BitRef, Char,
}
};
#ifdef __cpp_lib_byte
template <typename Char>
struct formatter<std::byte, Char> : formatter<unsigned, Char> {
FMT_CONSTEXPR static auto format_as(std::byte b) -> unsigned char {
return static_cast<unsigned char>(b);
}
template <typename Context>
FMT_CONSTEXPR auto format(std::byte b, Context& ctx) const
-> decltype(ctx.out()) {
return formatter<unsigned, Char>::format(format_as(b), ctx);
}
};
#endif
template <typename T, typename Char>
struct formatter<std::atomic<T>, Char,
enable_if_t<is_formattable<T, Char>::value>>
@@ -645,6 +723,11 @@ struct formatter<std::atomic_flag, Char> : formatter<bool, Char> {
};
#endif // __cpp_lib_atomic_flag_test
template <typename T> struct is_tuple_like;
template <typename T>
struct is_tuple_like<std::complex<T>> : std::false_type {};
template <typename T, typename Char> struct formatter<std::complex<T>, Char> {
private:
detail::dynamic_format_specs<Char> specs_;

View File

@@ -1,6 +1,6 @@
// Formatting library for C++ - optional wchar_t and exotic character support
//
// Copyright (c) 2012 - present, Victor Zverovich
// Copyright (c) 2012 - present, Victor Zverovich and {fmt} contributors
// All rights reserved.
//
// For the license information refer to format.h.
@@ -109,7 +109,7 @@ template <typename Char, typename... T> struct basic_fstring {
}
basic_fstring(runtime_format_string<Char> fmt) : str_(fmt.str) {}
operator basic_string_view<Char>() const { return str_; }
FMT_DEPRECATED operator basic_string_view<Char>() const { return str_; }
auto get() const -> basic_string_view<Char> { return str_; }
};
@@ -136,29 +136,42 @@ inline auto operator""_a(const wchar_t* s, size_t) -> detail::udl_arg<wchar_t> {
} // namespace literals
#endif
template <typename It, typename Sentinel>
auto join(It begin, Sentinel end, wstring_view sep)
-> join_view<It, Sentinel, wchar_t> {
return {begin, end, sep};
template <typename T>
auto arg(const wchar_t* name, const T& arg) -> named_arg<T, wchar_t> {
return {name, arg};
}
template <typename Range, FMT_ENABLE_IF(!is_tuple_like<Range>::value)>
auto join(Range&& range, wstring_view sep)
-> join_view<decltype(std::begin(range)), decltype(std::end(range)),
wchar_t> {
return join(std::begin(range), std::end(range), sep);
template <typename It, typename Sentinel, typename S,
typename Char = typename decltype(detail::to_string_view(
std::declval<S>()))::value_type,
FMT_ENABLE_IF(detail::is_exotic_char<Char>::value)>
auto join(It begin, Sentinel end, S&& sep) -> join_view<It, Sentinel, Char> {
return {begin, end, detail::to_string_view(sep)};
}
template <typename Range, typename S,
typename Char = typename decltype(detail::to_string_view(
std::declval<S>()))::value_type,
FMT_ENABLE_IF(detail::is_exotic_char<Char>::value &&
!is_tuple_like<Range>::value)>
auto join(Range&& range, S&& sep)
-> join_view<decltype(std::begin(range)), decltype(std::end(range)), Char> {
return {std::begin(range), std::end(range), detail::to_string_view(sep)};
}
template <typename T>
auto join(std::initializer_list<T> list, wstring_view sep)
FMT_DEPRECATED auto join(std::initializer_list<T> list, wstring_view sep)
-> join_view<const T*, const T*, wchar_t> {
return join(std::begin(list), std::end(list), sep);
}
template <typename Tuple, FMT_ENABLE_IF(is_tuple_like<Tuple>::value)>
auto join(const Tuple& tuple, basic_string_view<wchar_t> sep)
-> tuple_join_view<Tuple, wchar_t> {
return {tuple, sep};
template <typename Tuple, typename S,
typename Char = typename decltype(detail::to_string_view(
std::declval<S>()))::value_type,
FMT_ENABLE_IF(detail::is_exotic_char<Char>::value&&
is_tuple_like<Tuple>::value)>
auto join(const Tuple& tuple, S&& sep) -> tuple_join_view<Tuple, Char> {
return {tuple, detail::to_string_view(sep)};
}
template <typename Char, FMT_ENABLE_IF(!std::is_same<Char, char>::value)>
@@ -172,14 +185,13 @@ auto vformat(basic_string_view<Char> fmt,
template <typename... T>
auto format(wformat_string<T...> fmt, T&&... args) -> std::wstring {
return vformat(fmt::wstring_view(fmt), fmt::make_wformat_args(args...));
return vformat(fmt.get(), fmt::make_wformat_args(args...));
}
template <typename OutputIt, typename... T>
auto format_to(OutputIt out, wformat_string<T...> fmt, T&&... args)
-> OutputIt {
return vformat_to(out, fmt::wstring_view(fmt),
fmt::make_wformat_args(args...));
return vformat_to(out, fmt.get(), fmt::make_wformat_args(args...));
}
// Pass char_t as a default template parameter instead of using
@@ -267,10 +279,18 @@ inline auto vformat_to_n(OutputIt out, size_t n, basic_string_view<Char> fmt,
return {buf.out(), buf.count()};
}
template <typename OutputIt, typename... T,
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, wchar_t>::value)>
FMT_INLINE auto format_to_n(OutputIt out, size_t n, wformat_string<T...> fmt,
T&&... args) -> format_to_n_result<OutputIt> {
return vformat_to_n(out, n, fmt.get(), fmt::make_wformat_args(args...));
}
template <typename OutputIt, typename S, typename... T,
typename Char = detail::format_string_char_t<S>,
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value&&
detail::is_exotic_char<Char>::value)>
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value &&
!std::is_same<Char, char>::value &&
!std::is_same<Char, wchar_t>::value)>
inline auto format_to_n(OutputIt out, size_t n, const S& fmt, T&&... args)
-> format_to_n_result<OutputIt> {
return vformat_to_n(out, n, fmt::basic_string_view<Char>(fmt),
@@ -301,11 +321,11 @@ inline void vprint(wstring_view fmt, wformat_args args) {
template <typename... T>
void print(std::FILE* f, wformat_string<T...> fmt, T&&... args) {
return vprint(f, wstring_view(fmt), fmt::make_wformat_args(args...));
return vprint(f, fmt.get(), fmt::make_wformat_args(args...));
}
template <typename... T> void print(wformat_string<T...> fmt, T&&... args) {
return vprint(wstring_view(fmt), fmt::make_wformat_args(args...));
return vprint(fmt.get(), fmt::make_wformat_args(args...));
}
template <typename... T>
@@ -327,7 +347,7 @@ inline auto vformat(text_style ts, wstring_view fmt, wformat_args args)
template <typename... T>
inline auto format(text_style ts, wformat_string<T...> fmt, T&&... args)
-> std::wstring {
return fmt::vformat(ts, fmt, fmt::make_wformat_args(args...));
return fmt::vformat(ts, fmt.get(), fmt::make_wformat_args(args...));
}
inline void vprint(std::wostream& os, wstring_view fmt, wformat_args args) {
@@ -338,7 +358,8 @@ inline void vprint(std::wostream& os, wstring_view fmt, wformat_args args) {
template <typename... T>
void print(std::wostream& os, wformat_string<T...> fmt, T&&... args) {
vprint(os, fmt, fmt::make_format_args<buffered_context<wchar_t>>(args...));
vprint(os, fmt.get(),
fmt::make_format_args<buffered_context<wchar_t>>(args...));
}
template <typename... T>

View File

@@ -0,0 +1,67 @@
// Formatting library for C++ - the C API
//
// Copyright (c) 2012 - present, Victor Zverovich and {fmt} contributors
// All rights reserved.
//
// For the license information refer to format.h.
#include "fmt/fmt-c.h"
#include <fmt/base.h>
constexpr size_t max_c_format_args = 16;
static int convert_c_format_args(
fmt::basic_format_arg<fmt::format_context>* format_args,
const fmt_arg* args, size_t num_args) {
if (num_args > max_c_format_args) return fmt_error_invalid_arg;
for (size_t i = 0; i < num_args; ++i) {
switch (args[i].type) {
case fmt_int: format_args[i] = args[i].value.int_value; break;
case fmt_uint: format_args[i] = args[i].value.uint_value; break;
case fmt_bool: format_args[i] = args[i].value.bool_value; break;
case fmt_char: format_args[i] = args[i].value.char_value; break;
case fmt_float: format_args[i] = args[i].value.float_value; break;
case fmt_double: format_args[i] = args[i].value.double_value; break;
case fmt_long_double:
format_args[i] = args[i].value.long_double_value;
break;
case fmt_cstring: format_args[i] = args[i].value.cstring; break;
case fmt_pointer: format_args[i] = args[i].value.pointer; break;
default: return fmt_error_invalid_arg;
}
}
return 0;
}
extern "C" int fmt_vformat(char* buffer, size_t size, const char* fmt,
const fmt_arg* args, size_t num_args) {
fmt::basic_format_arg<fmt::format_context> format_args[max_c_format_args];
int error = convert_c_format_args(format_args, args, num_args);
if (error != 0) return error;
FMT_TRY {
auto result = fmt::vformat_to_n(
buffer, size, fmt,
fmt::format_args(format_args, static_cast<int>(num_args)));
return static_cast<int>(result.size);
}
FMT_CATCH(...) {}
return fmt_error;
}
extern "C" int fmt_vprint(FILE* stream, const char* fmt, const fmt_arg* args,
size_t num_args) {
fmt::basic_format_arg<fmt::format_context> format_args[max_c_format_args];
int error = convert_c_format_args(format_args, args, num_args);
if (error != 0) return error;
FMT_TRY {
fmt::vprint(stream, fmt,
fmt::format_args(format_args, static_cast<int>(num_args)));
return 0;
}
FMT_CATCH(...) {}
return fmt_error;
}

View File

@@ -1,6 +1,6 @@
// Formatting library for C++
//
// Copyright (c) 2012 - 2016, Victor Zverovich
// Copyright (c) 2012 - present, Victor Zverovich and {fmt} contributors
// All rights reserved.
//
// For the license information refer to format.h.

View File

@@ -1,6 +1,6 @@
// Formatting library for C++ - optional OS-specific functionality
//
// Copyright (c) 2012 - 2016, Victor Zverovich
// Copyright (c) 2012 - present, Victor Zverovich and {fmt} contributors
// All rights reserved.
//
// For the license information refer to format.h.
@@ -13,7 +13,6 @@
#include "fmt/os.h"
#ifndef FMT_MODULE
# include <climits>
# if FMT_USE_FCNTL
# include <sys/stat.h>
@@ -35,6 +34,8 @@
# ifdef _WIN32
# include <windows.h>
# include <climits> // CHAR_BIT
# endif
#endif
@@ -61,6 +62,7 @@
namespace {
#ifdef _WIN32
// Return type of read and write functions.
using rwresult = int;
@@ -69,18 +71,6 @@ using rwresult = int;
inline unsigned convert_rwcount(size_t count) {
return count <= UINT_MAX ? static_cast<unsigned>(count) : UINT_MAX;
}
#elif FMT_USE_FCNTL
// Return type of read and write functions.
using rwresult = ssize_t;
inline auto convert_rwcount(size_t count) -> size_t { return count; }
#endif
} // namespace
FMT_BEGIN_NAMESPACE
#ifdef _WIN32
namespace detail {
class system_message {
system_message(const system_message&) = delete;
@@ -109,8 +99,8 @@ class system_message {
}
~system_message() { LocalFree(message_); }
explicit operator bool() const noexcept { return result_ != 0; }
operator basic_string_view<wchar_t>() const noexcept {
return basic_string_view<wchar_t>(message_, result_);
operator fmt::basic_string_view<wchar_t>() const noexcept {
return fmt::basic_string_view<wchar_t>(message_, result_);
}
};
@@ -120,7 +110,7 @@ class utf8_system_category final : public std::error_category {
std::string message(int error_code) const override {
auto&& msg = system_message(error_code);
if (msg) {
auto utf8_message = to_utf8<wchar_t>();
auto utf8_message = fmt::detail::to_utf8<wchar_t>();
if (utf8_message.convert(msg)) {
return utf8_message.str();
}
@@ -129,10 +119,22 @@ class utf8_system_category final : public std::error_category {
}
};
} // namespace detail
#elif FMT_USE_FCNTL
// Return type of read and write functions.
using rwresult = ssize_t;
inline auto convert_rwcount(size_t count) -> size_t { return count; }
#endif
} // namespace
FMT_BEGIN_NAMESPACE
#ifdef _WIN32
FMT_API const std::error_category& system_category() noexcept {
static const detail::utf8_system_category category;
static const utf8_system_category category;
return category;
}
@@ -162,6 +164,7 @@ void detail::format_windows_error(detail::buffer<char>& out, int error_code,
void report_windows_error(int error_code, const char* message) noexcept {
do_report_error(detail::format_windows_error, error_code, message);
}
#endif // _WIN32
buffered_file::~buffered_file() noexcept {