Bump bundled fmt to 12.0.0

Ref: e424e3f2e6
This commit is contained in:
Steffen Winter
2025-09-18 11:55:42 +00:00
committed by Steffen
parent bdddfc46a2
commit 21ca9093af
13 changed files with 1460 additions and 1350 deletions

View File

@@ -71,7 +71,7 @@ class dynamic_arg_list {
* It can be implicitly converted into `fmt::basic_format_args` for passing
* into type-erased formatting functions such as `fmt::vformat`.
*/
template <typename Context> class dynamic_format_arg_store {
FMT_EXPORT template <typename Context> class dynamic_format_arg_store {
private:
using char_type = typename Context::char_type;
@@ -212,7 +212,7 @@ template <typename Context> class dynamic_format_arg_store {
}
/// Returns the number of elements in the store.
size_t size() const noexcept { return data_.size(); }
auto size() const noexcept -> size_t { return data_.size(); }
};
FMT_END_NAMESPACE

View File

@@ -21,7 +21,7 @@
#endif
// The fmt library version in the form major * 10000 + minor * 100 + patch.
#define FMT_VERSION 110104
#define FMT_VERSION 120000
// Detect compiler versions.
#if defined(__clang__) && !defined(__ibmxl__)
@@ -201,28 +201,6 @@
# define FMT_NODISCARD
#endif
#ifdef FMT_DEPRECATED
// Use the provided definition.
#elif FMT_HAS_CPP14_ATTRIBUTE(deprecated)
# define FMT_DEPRECATED [[deprecated]]
#else
# define FMT_DEPRECATED /* deprecated */
#endif
#ifdef FMT_ALWAYS_INLINE
// Use the provided definition.
#elif FMT_GCC_VERSION || FMT_CLANG_VERSION
# define FMT_ALWAYS_INLINE inline __attribute__((always_inline))
#else
# define FMT_ALWAYS_INLINE inline
#endif
// A version of FMT_ALWAYS_INLINE to prevent code bloat in debug mode.
#ifdef NDEBUG
# define FMT_INLINE FMT_ALWAYS_INLINE
#else
# define FMT_INLINE inline
#endif
#if FMT_GCC_VERSION || FMT_CLANG_VERSION
# define FMT_VISIBILITY(value) __attribute__((visibility(value)))
#else
@@ -249,10 +227,32 @@
# define FMT_MSC_WARNING(...)
#endif
// Enable minimal optimizations for more compact code in debug mode.
FMT_PRAGMA_GCC(push_options)
#if !defined(__OPTIMIZE__) && !defined(__CUDACC__) && !defined(FMT_MODULE)
FMT_PRAGMA_GCC(optimize("Og"))
# define FMT_GCC_OPTIMIZED
#endif
FMT_PRAGMA_CLANG(diagnostic push)
#ifdef FMT_ALWAYS_INLINE
// Use the provided definition.
#elif FMT_GCC_VERSION || FMT_CLANG_VERSION
# define FMT_ALWAYS_INLINE inline __attribute__((always_inline))
#else
# define FMT_ALWAYS_INLINE inline
#endif
// A version of FMT_ALWAYS_INLINE to prevent code bloat in debug mode.
#if defined(NDEBUG) || defined(FMT_GCC_OPTIMIZED)
# define FMT_INLINE FMT_ALWAYS_INLINE
#else
# define FMT_INLINE inline
#endif
#ifndef FMT_BEGIN_NAMESPACE
# define FMT_BEGIN_NAMESPACE \
namespace fmt { \
inline namespace v11 {
inline namespace v12 {
# define FMT_END_NAMESPACE \
} \
}
@@ -297,13 +297,6 @@
using unused = int[]; \
(void)unused { 0, (expr, 0)... }
// Enable minimal optimizations for more compact code in debug mode.
FMT_PRAGMA_GCC(push_options)
#if !defined(__OPTIMIZE__) && !defined(__CUDACC__) && !defined(FMT_MODULE)
FMT_PRAGMA_GCC(optimize("Og"))
#endif
FMT_PRAGMA_CLANG(diagnostic push)
FMT_BEGIN_NAMESPACE
// Implementations of enable_if_t and other metafunctions for older systems.
@@ -325,8 +318,8 @@ using underlying_t = typename std::underlying_type<T>::type;
template <typename T> using decay_t = typename std::decay<T>::type;
using nullptr_t = decltype(nullptr);
#if FMT_GCC_VERSION && FMT_GCC_VERSION < 500
// A workaround for gcc 4.9 to make void_t work in a SFINAE context.
#if (FMT_GCC_VERSION && FMT_GCC_VERSION < 500) || FMT_MSC_VERSION
// A workaround for gcc 4.9 & MSVC v141 to make void_t work in a SFINAE context.
template <typename...> struct void_t_impl {
using type = void;
};
@@ -355,6 +348,9 @@ template <typename T> constexpr auto max_of(T a, T b) -> T {
return a > b ? a : b;
}
FMT_NORETURN FMT_API void assert_fail(const char* file, int line,
const char* message);
namespace detail {
// Suppresses "unused variable" warnings with the method described in
// https://herbsutter.com/2009/10/18/mailbag-shutting-up-compiler-warnings/.
@@ -395,7 +391,7 @@ FMT_NORETURN FMT_API void assert_fail(const char* file, int line,
# define FMT_ASSERT(condition, message) \
((condition) /* void() fails with -Winvalid-constexpr on clang 4.0.1 */ \
? (void)0 \
: fmt::detail::assert_fail(__FILE__, __LINE__, (message)))
: ::fmt::assert_fail(__FILE__, __LINE__, (message)))
#endif
#ifdef FMT_USE_INT128
@@ -462,12 +458,13 @@ enum { use_utf8 = !FMT_WIN32 || is_utf8_enabled };
static_assert(!FMT_UNICODE || use_utf8,
"Unicode support requires compiling with /utf-8");
template <typename T> constexpr const char* narrow(const T*) { return nullptr; }
constexpr FMT_ALWAYS_INLINE const char* narrow(const char* s) { return s; }
template <typename T> constexpr auto narrow(T*) -> char* { return nullptr; }
constexpr FMT_ALWAYS_INLINE auto narrow(const char* s) -> const char* {
return s;
}
template <typename Char>
FMT_CONSTEXPR auto compare(const Char* s1, const Char* s2, std::size_t n)
-> int {
FMT_CONSTEXPR auto compare(const Char* s1, const Char* s2, size_t n) -> int {
if (!is_constant_evaluated() && sizeof(Char) == 1) return memcmp(s1, s2, n);
for (; n != 0; ++s1, ++s2, --n) {
if (*s1 < *s2) return -1;
@@ -526,20 +523,20 @@ template <typename Char> class basic_string_view {
constexpr basic_string_view() noexcept : data_(nullptr), size_(0) {}
/// Constructs a string reference object from a C string and a size.
/// Constructs a string view object from a C string and a size.
constexpr basic_string_view(const Char* s, size_t count) noexcept
: data_(s), size_(count) {}
constexpr basic_string_view(nullptr_t) = delete;
/// Constructs a string reference object from a C string.
/// Constructs a string view object from a C string.
#if FMT_GCC_VERSION
FMT_ALWAYS_INLINE
#endif
FMT_CONSTEXPR20 basic_string_view(const Char* s) : data_(s) {
#if FMT_HAS_BUILTIN(__builtin_strlen) || FMT_GCC_VERSION || FMT_CLANG_VERSION
if (std::is_same<Char, char>::value) {
size_ = __builtin_strlen(detail::narrow(s));
if (std::is_same<Char, char>::value && !detail::is_constant_evaluated()) {
size_ = __builtin_strlen(detail::narrow(s)); // strlen is not constexpr.
return;
}
#endif
@@ -548,7 +545,7 @@ template <typename Char> class basic_string_view {
size_ = len;
}
/// Constructs a string reference from a `std::basic_string` or a
/// Constructs a string view from a `std::basic_string` or a
/// `std::basic_string_view` object.
template <typename S,
FMT_ENABLE_IF(detail::is_std_string_like<S>::value&& std::is_same<
@@ -585,7 +582,6 @@ template <typename Char> class basic_string_view {
return starts_with(basic_string_view<Char>(s));
}
// Lexicographically compare this string reference to other.
FMT_CONSTEXPR auto compare(basic_string_view other) const -> int {
int result =
detail::compare(data_, other.data_, min_of(size_, other.size_));
@@ -616,19 +612,6 @@ template <typename Char> class basic_string_view {
using string_view = basic_string_view<char>;
/// Specifies if `T` is an extended character type. Can be specialized by users.
template <typename T> struct is_xchar : std::false_type {};
template <> struct is_xchar<wchar_t> : std::true_type {};
template <> struct is_xchar<char16_t> : std::true_type {};
template <> struct is_xchar<char32_t> : std::true_type {};
#ifdef __cpp_char8_t
template <> struct is_xchar<char8_t> : std::true_type {};
#endif
// DEPRECATED! Will be replaced with an alias to prevent specializations.
template <typename T> struct is_char : is_xchar<T> {};
template <> struct is_char<char> : std::true_type {};
template <typename T> class basic_appender;
using appender = basic_appender<char>;
@@ -781,7 +764,7 @@ class basic_specs {
(static_cast<unsigned>(p) << precision_shift);
}
constexpr bool dynamic() const {
constexpr auto dynamic() const -> bool {
return (data_ & (width_mask | precision_mask)) != 0;
}
@@ -921,14 +904,47 @@ template <typename Char = char> class parse_context {
FMT_CONSTEXPR void check_dynamic_spec(int arg_id);
};
#ifndef FMT_USE_LOCALE
# define FMT_USE_LOCALE (FMT_OPTIMIZE_SIZE <= 1)
#endif
// A type-erased reference to std::locale to avoid the heavy <locale> include.
class locale_ref {
#if FMT_USE_LOCALE
private:
const void* locale_; // A type-erased pointer to std::locale.
public:
constexpr locale_ref() : locale_(nullptr) {}
template <typename Locale, FMT_ENABLE_IF(sizeof(Locale::collate) != 0)>
locale_ref(const Locale& loc);
inline explicit operator bool() const noexcept { return locale_ != nullptr; }
#endif // FMT_USE_LOCALE
public:
template <typename Locale> auto get() const -> Locale;
};
FMT_END_EXPORT
namespace detail {
// Specifies if `T` is a code unit type.
template <typename T> struct is_code_unit : std::false_type {};
template <> struct is_code_unit<char> : std::true_type {};
template <> struct is_code_unit<wchar_t> : std::true_type {};
template <> struct is_code_unit<char16_t> : std::true_type {};
template <> struct is_code_unit<char32_t> : std::true_type {};
#ifdef __cpp_char8_t
template <> struct is_code_unit<char8_t> : bool_constant<is_utf8_enabled> {};
#endif
// Constructs fmt::basic_string_view<Char> from types implicitly convertible
// to it, deducing Char. Explicitly convertible types such as the ones returned
// from FMT_STRING are intentionally excluded.
template <typename Char, FMT_ENABLE_IF(is_char<Char>::value)>
template <typename Char, FMT_ENABLE_IF(is_code_unit<Char>::value)>
constexpr auto to_string_view(const Char* s) -> basic_string_view<Char> {
return s;
}
@@ -1032,6 +1048,11 @@ enum {
struct view {};
template <typename T, typename Enable = std::true_type>
struct is_view : std::false_type {};
template <typename T>
struct is_view<T, bool_constant<sizeof(T) != 0>> : std::is_base_of<view, T> {};
template <typename Char, typename T> struct named_arg;
template <typename T> struct is_named_arg : std::false_type {};
template <typename T> struct is_static_named_arg : std::false_type {};
@@ -1052,11 +1073,11 @@ template <bool B1, bool B2, bool... Tail> constexpr auto count() -> int {
return (B1 ? 1 : 0) + count<B2, Tail...>();
}
template <typename... Args> constexpr auto count_named_args() -> int {
return count<is_named_arg<Args>::value...>();
template <typename... T> constexpr auto count_named_args() -> int {
return count<is_named_arg<T>::value...>();
}
template <typename... Args> constexpr auto count_static_named_args() -> int {
return count<is_static_named_arg<Args>::value...>();
template <typename... T> constexpr auto count_static_named_args() -> int {
return count<is_static_named_arg<T>::value...>();
}
template <typename Char> struct named_arg_info {
@@ -1064,6 +1085,16 @@ template <typename Char> struct named_arg_info {
int id;
};
// named_args is non-const to suppress a bogus -Wmaybe-uninitialized in gcc 13.
template <typename Char>
FMT_CONSTEXPR void check_for_duplicate(named_arg_info<Char>* named_args,
int named_arg_index,
basic_string_view<Char> arg_name) {
for (int i = 0; i < named_arg_index; ++i) {
if (named_args[i].name == arg_name) report_error("duplicate named arg");
}
}
template <typename Char, typename T, FMT_ENABLE_IF(!is_named_arg<T>::value)>
void init_named_arg(named_arg_info<Char>*, int& arg_index, int&, const T&) {
++arg_index;
@@ -1071,6 +1102,7 @@ void init_named_arg(named_arg_info<Char>*, int& arg_index, int&, const T&) {
template <typename Char, typename T, FMT_ENABLE_IF(is_named_arg<T>::value)>
void init_named_arg(named_arg_info<Char>* named_args, int& arg_index,
int& named_arg_index, const T& arg) {
check_for_duplicate<Char>(named_args, named_arg_index, arg.name);
named_args[named_arg_index++] = {arg.name, arg_index++};
}
@@ -1084,12 +1116,13 @@ template <typename T, typename Char,
FMT_ENABLE_IF(is_static_named_arg<T>::value)>
FMT_CONSTEXPR void init_static_named_arg(named_arg_info<Char>* named_args,
int& arg_index, int& named_arg_index) {
check_for_duplicate<Char>(named_args, named_arg_index, T::name);
named_args[named_arg_index++] = {T::name, arg_index++};
}
// To minimize the number of types we need to deal with, long is translated
// either to int or to long long depending on its size.
enum { long_short = sizeof(long) == sizeof(int) };
enum { long_short = sizeof(long) == sizeof(int) && FMT_BUILTIN_TYPES };
using long_type = conditional_t<long_short, int, long long>;
using ulong_type = conditional_t<long_short, unsigned, unsigned long long>;
@@ -1156,7 +1189,7 @@ template <typename Char> struct type_mapper {
static auto map(ubitint<N>)
-> conditional_t<N <= 64, unsigned long long, void>;
template <typename T, FMT_ENABLE_IF(is_char<T>::value)>
template <typename T, FMT_ENABLE_IF(is_code_unit<T>::value)>
static auto map(T) -> conditional_t<
std::is_same<T, char>::value || std::is_same<T, Char>::value, Char, void>;
@@ -1662,12 +1695,12 @@ template <typename... T> struct arg_pack {};
template <typename Char, int NUM_ARGS, int NUM_NAMED_ARGS, bool DYNAMIC_NAMES>
class format_string_checker {
private:
type types_[max_of(1, NUM_ARGS)];
named_arg_info<Char> named_args_[max_of(1, NUM_NAMED_ARGS)];
type types_[max_of<size_t>(1, NUM_ARGS)];
named_arg_info<Char> named_args_[max_of<size_t>(1, NUM_NAMED_ARGS)];
compile_parse_context<Char> context_;
using parse_func = auto (*)(parse_context<Char>&) -> const Char*;
parse_func parse_funcs_[max_of(1, NUM_ARGS)];
parse_func parse_funcs_[max_of<size_t>(1, NUM_ARGS)];
public:
template <typename... T>
@@ -1706,7 +1739,17 @@ class format_string_checker {
-> const Char* {
context_.advance_to(begin);
if (id >= 0 && id < NUM_ARGS) return parse_funcs_[id](context_);
while (begin != end && *begin != '}') ++begin;
// If id is out of range, it means we do not know the type and cannot parse
// the format at compile time. Instead, skip over content until we finish
// the format spec, accounting for any nested replacements.
for (int bracket_count = 0;
begin != end && (bracket_count > 0 || *begin != '}'); ++begin) {
if (*begin == '{')
++bracket_count;
else if (*begin == '}')
--bracket_count;
}
return begin;
}
@@ -2006,6 +2049,17 @@ struct has_back_insert_iterator_container_append<
.append(std::declval<InputIt>(),
std::declval<InputIt>()))>> : std::true_type {};
template <typename OutputIt, typename InputIt, typename = void>
struct has_back_insert_iterator_container_insert_at_end : std::false_type {};
template <typename OutputIt, typename InputIt>
struct has_back_insert_iterator_container_insert_at_end<
OutputIt, InputIt,
void_t<decltype(get_container(std::declval<OutputIt>())
.insert(get_container(std::declval<OutputIt>()).end(),
std::declval<InputIt>(),
std::declval<InputIt>()))>> : std::true_type {};
// An optimized version of std::copy with the output value type (T).
template <typename T, typename InputIt, typename OutputIt,
FMT_ENABLE_IF(is_back_insert_iterator<OutputIt>::value&&
@@ -2020,6 +2074,8 @@ FMT_CONSTEXPR20 auto copy(InputIt begin, InputIt end, OutputIt out)
template <typename T, typename InputIt, typename OutputIt,
FMT_ENABLE_IF(is_back_insert_iterator<OutputIt>::value &&
!has_back_insert_iterator_container_append<
OutputIt, InputIt>::value &&
has_back_insert_iterator_container_insert_at_end<
OutputIt, InputIt>::value)>
FMT_CONSTEXPR20 auto copy(InputIt begin, InputIt end, OutputIt out)
-> OutputIt {
@@ -2029,7 +2085,11 @@ FMT_CONSTEXPR20 auto copy(InputIt begin, InputIt end, OutputIt out)
}
template <typename T, typename InputIt, typename OutputIt,
FMT_ENABLE_IF(!is_back_insert_iterator<OutputIt>::value)>
FMT_ENABLE_IF(!(is_back_insert_iterator<OutputIt>::value &&
(has_back_insert_iterator_container_append<
OutputIt, InputIt>::value ||
has_back_insert_iterator_container_insert_at_end<
OutputIt, InputIt>::value)))>
FMT_CONSTEXPR auto copy(InputIt begin, InputIt end, OutputIt out) -> OutputIt {
while (begin != end) *out++ = static_cast<T>(*begin++);
return out;
@@ -2149,7 +2209,7 @@ template <typename Context> class value {
static_assert(N <= 64, "unsupported _BitInt");
}
template <typename T, FMT_ENABLE_IF(is_char<T>::value)>
template <typename T, FMT_ENABLE_IF(is_code_unit<T>::value)>
constexpr FMT_INLINE value(T x FMT_BUILTIN) : char_value(x) {
static_assert(
std::is_same<T, char>::value || std::is_same<T, char_type>::value,
@@ -2225,7 +2285,7 @@ template <typename Context> class value {
custom.value = const_cast<value_type*>(&x);
#endif
}
custom.format = format_custom<value_type, formatter<value_type, char_type>>;
custom.format = format_custom<value_type>;
}
template <typename T, FMT_ENABLE_IF(!has_formatter<T, char_type>())>
@@ -2236,10 +2296,10 @@ template <typename Context> class value {
}
// Formats an argument of a custom type, such as a user-defined class.
template <typename T, typename Formatter>
template <typename T>
static void format_custom(void* arg, parse_context<char_type>& parse_ctx,
Context& ctx) {
auto f = Formatter();
auto f = formatter<T, char_type>();
parse_ctx.advance_to(f.parse(parse_ctx));
using qualified_type =
conditional_t<has_formatter<const T, char_type>(), const T, T>;
@@ -2266,35 +2326,14 @@ struct is_output_iterator<
enable_if_t<std::is_assignable<decltype(*std::declval<decay_t<It>&>()++),
T>::value>> : std::true_type {};
#ifndef FMT_USE_LOCALE
# define FMT_USE_LOCALE (FMT_OPTIMIZE_SIZE <= 1)
#endif
// A type-erased reference to an std::locale to avoid a heavy <locale> include.
class locale_ref {
#if FMT_USE_LOCALE
private:
const void* locale_; // A type-erased pointer to std::locale.
public:
constexpr locale_ref() : locale_(nullptr) {}
template <typename Locale> locale_ref(const Locale& loc);
inline explicit operator bool() const noexcept { return locale_ != nullptr; }
#endif // FMT_USE_LOCALE
public:
template <typename Locale> auto get() const -> Locale;
};
template <typename> constexpr auto encode_types() -> unsigned long long {
return 0;
}
template <typename Context, typename Arg, typename... Args>
template <typename Context, typename First, typename... T>
constexpr auto encode_types() -> unsigned long long {
return static_cast<unsigned>(stored_type_constant<Arg, Context>::value) |
(encode_types<Context, Args...>() << packed_arg_bits);
return static_cast<unsigned>(stored_type_constant<First, Context>::value) |
(encode_types<Context, T...>() << packed_arg_bits);
}
template <typename Context, typename... T, size_t NUM_ARGS = sizeof...(T)>
@@ -2311,8 +2350,9 @@ template <typename Context, int NUM_ARGS, int NUM_NAMED_ARGS,
unsigned long long DESC>
struct named_arg_store {
// args_[0].named_args points to named_args to avoid bloating format_args.
arg_t<Context, NUM_ARGS> args[1 + NUM_ARGS];
named_arg_info<typename Context::char_type> named_args[NUM_NAMED_ARGS];
arg_t<Context, NUM_ARGS> args[1u + NUM_ARGS];
named_arg_info<typename Context::char_type>
named_args[static_cast<size_t>(NUM_NAMED_ARGS)];
template <typename... T>
FMT_CONSTEXPR FMT_ALWAYS_INLINE named_arg_store(T&... values)
@@ -2331,8 +2371,8 @@ struct named_arg_store {
}
named_arg_store(const named_arg_store& rhs) = delete;
named_arg_store& operator=(const named_arg_store& rhs) = delete;
named_arg_store& operator=(named_arg_store&& rhs) = delete;
auto operator=(const named_arg_store& rhs) -> named_arg_store& = delete;
auto operator=(named_arg_store&& rhs) -> named_arg_store& = delete;
operator const arg_t<Context, NUM_ARGS>*() const { return args + 1; }
};
@@ -2345,7 +2385,7 @@ struct format_arg_store {
// +1 to workaround a bug in gcc 7.5 that causes duplicated-branches warning.
using type =
conditional_t<NUM_NAMED_ARGS == 0,
arg_t<Context, NUM_ARGS>[max_of(1, NUM_ARGS)],
arg_t<Context, NUM_ARGS>[max_of<size_t>(1, NUM_ARGS)],
named_arg_store<Context, NUM_ARGS, NUM_NAMED_ARGS, DESC>>;
type args;
};
@@ -2629,22 +2669,17 @@ class context {
private:
appender out_;
format_args args_;
FMT_NO_UNIQUE_ADDRESS detail::locale_ref loc_;
FMT_NO_UNIQUE_ADDRESS locale_ref loc_;
public:
/// The character type for the output.
using char_type = char;
using char_type = char; ///< The character type for the output.
using iterator = appender;
using format_arg = basic_format_arg<context>;
using parse_context_type FMT_DEPRECATED = parse_context<>;
template <typename T> using formatter_type FMT_DEPRECATED = formatter<T>;
enum { builtin_types = FMT_BUILTIN_TYPES };
/// Constructs a `context` object. References to the arguments are stored
/// in the object so make sure they have appropriate lifetimes.
FMT_CONSTEXPR context(iterator out, format_args args,
detail::locale_ref loc = {})
FMT_CONSTEXPR context(iterator out, format_args args, locale_ref loc = {})
: out_(out), args_(args), loc_(loc) {}
context(context&&) = default;
context(const context&) = delete;
@@ -2665,7 +2700,7 @@ class context {
// Advances the begin iterator to `it`.
FMT_CONSTEXPR void advance_to(iterator) {}
FMT_CONSTEXPR auto locale() const -> detail::locale_ref { return loc_; }
FMT_CONSTEXPR auto locale() const -> locale_ref { return loc_; }
};
template <typename Char = char> struct runtime_format_string {
@@ -2703,7 +2738,7 @@ template <typename... T> struct fstring {
template <size_t N>
FMT_CONSTEVAL FMT_ALWAYS_INLINE fstring(const char (&s)[N]) : str(s, N - 1) {
using namespace detail;
static_assert(count<(std::is_base_of<view, remove_reference_t<T>>::value &&
static_assert(count<(is_view<remove_cvref_t<T>>::value &&
std::is_reference<T>::value)...>() == 0,
"passing views as lvalues is disallowed");
if (FMT_USE_CONSTEVAL) parse_format_string<char>(s, checker(s, arg_pack()));
@@ -2752,9 +2787,6 @@ template <typename T, typename Char = char>
concept formattable = is_formattable<remove_reference_t<T>, Char>::value;
#endif
template <typename T, typename Char>
using has_formatter FMT_DEPRECATED = std::is_constructible<formatter<T, Char>>;
// A formatter specialization for natively supported types.
template <typename T, typename Char>
struct formatter<T, Char,
@@ -2951,9 +2983,9 @@ FMT_INLINE void println(format_string<T...> fmt, T&&... args) {
return fmt::println(stdout, fmt, static_cast<T&&>(args)...);
}
FMT_END_EXPORT
FMT_PRAGMA_CLANG(diagnostic pop)
FMT_PRAGMA_GCC(pop_options)
FMT_END_EXPORT
FMT_END_NAMESPACE
#ifdef FMT_HEADER_ONLY

View File

@@ -22,21 +22,6 @@
#include "format.h"
namespace fmt_detail {
struct time_zone {
template <typename Duration, typename T>
auto to_sys(T)
-> std::chrono::time_point<std::chrono::system_clock, Duration> {
return {};
}
};
template <typename... T> inline auto current_zone(T...) -> time_zone* {
return nullptr;
}
template <typename... T> inline void _tzset(T...) {}
} // namespace fmt_detail
FMT_BEGIN_NAMESPACE
// Enable safe chrono durations, unless explicitly disabled.
@@ -53,6 +38,7 @@ FMT_BEGIN_NAMESPACE
// Copyright Paul Dreik 2019
namespace safe_duration_cast {
// DEPRECATED!
template <typename To, typename From,
FMT_ENABLE_IF(!std::is_same<From, To>::value &&
std::numeric_limits<From>::is_signed ==
@@ -176,17 +162,6 @@ auto safe_duration_cast(std::chrono::duration<FromRep, FromPeriod> from,
int& ec) -> To {
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:
@@ -297,8 +272,6 @@ namespace detail {
#define FMT_NOMACRO
template <typename T = void> struct null {};
inline auto localtime_r FMT_NOMACRO(...) -> null<> { return null<>(); }
inline auto localtime_s(...) -> null<> { return null<>(); }
inline auto gmtime_r(...) -> null<> { return null<>(); }
inline auto gmtime_s(...) -> null<> { return null<>(); }
@@ -341,7 +314,7 @@ inline auto get_classic_locale() -> const std::locale& {
}
template <typename CodeUnit> struct codecvt_result {
static constexpr const size_t max_size = 32;
static constexpr size_t max_size = 32;
CodeUnit buf[max_size];
CodeUnit* end;
};
@@ -435,14 +408,11 @@ auto write(OutputIt out, const std::tm& time, const std::locale& loc,
return write_encoded_tm_str(out, string_view(buf.data(), buf.size()), loc);
}
template <typename Rep1, typename Rep2>
struct is_same_arithmetic_type
: public std::integral_constant<bool,
(std::is_integral<Rep1>::value &&
std::is_integral<Rep2>::value) ||
(std::is_floating_point<Rep1>::value &&
std::is_floating_point<Rep2>::value)> {
};
template <typename T, typename U>
using is_similar_arithmetic_type =
bool_constant<(std::is_integral<T>::value && std::is_integral<U>::value) ||
(std::is_floating_point<T>::value &&
std::is_floating_point<U>::value)>;
FMT_NORETURN inline void throw_duration_error() {
FMT_THROW(format_error("cannot format duration"));
@@ -461,11 +431,7 @@ auto duration_cast(std::chrono::duration<FromRep, FromPeriod> from) -> To {
using common_rep = typename std::common_type<FromRep, typename To::rep,
decltype(factor::num)>::type;
int ec = 0;
auto count = safe_duration_cast::lossless_integral_conversion<common_rep>(
from.count(), ec);
if (ec) throw_duration_error();
common_rep count = from.count(); // This conversion is lossless.
// Multiply from.count() by factor and check for overflow.
if (const_check(factor::num != 1)) {
@@ -476,6 +442,7 @@ auto duration_cast(std::chrono::duration<FromRep, FromPeriod> from) -> To {
count *= factor::num;
}
if (const_check(factor::den != 1)) count /= factor::den;
int ec = 0;
auto to =
To(safe_duration_cast::lossless_integral_conversion<typename To::rep>(
count, ec));
@@ -489,6 +456,8 @@ template <typename To, typename FromRep, typename FromPeriod,
std::is_floating_point<typename To::rep>::value)>
auto duration_cast(std::chrono::duration<FromRep, FromPeriod> from) -> To {
#if FMT_SAFE_DURATION_CAST
// Preserve infinity and NaN.
if (!isfinite(from.count())) return static_cast<To>(from.count());
// Throwing version of safe_duration_cast is only available for
// integer to integer or float to float casts.
int ec;
@@ -501,11 +470,11 @@ auto duration_cast(std::chrono::duration<FromRep, FromPeriod> from) -> To {
#endif
}
template <
typename To, typename FromRep, typename FromPeriod,
FMT_ENABLE_IF(!is_same_arithmetic_type<FromRep, typename To::rep>::value)>
template <typename To, typename FromRep, typename FromPeriod,
FMT_ENABLE_IF(
!is_similar_arithmetic_type<FromRep, typename To::rep>::value)>
auto duration_cast(std::chrono::duration<FromRep, FromPeriod> from) -> To {
// Mixed integer <-> float cast is not supported by safe_duration_cast.
// Mixed integer <-> float cast is not supported by safe duration_cast.
return std::chrono::duration_cast<To>(from);
}
@@ -519,68 +488,10 @@ auto to_time_t(sys_time<Duration> time_point) -> std::time_t {
.count();
}
// Workaround a bug in libstdc++ which sets __cpp_lib_chrono to 201907 without
// providing current_zone(): https://github.com/fmtlib/fmt/issues/4160.
template <typename T> FMT_CONSTEXPR auto has_current_zone() -> bool {
using namespace std::chrono;
using namespace fmt_detail;
return !std::is_same<decltype(current_zone()), fmt_detail::time_zone*>::value;
}
} // namespace detail
FMT_BEGIN_EXPORT
/**
* Converts given time since epoch as `std::time_t` value into calendar time,
* expressed in local time. Unlike `std::localtime`, this function is
* thread-safe on most platforms.
*/
inline auto localtime(std::time_t time) -> std::tm {
struct dispatcher {
std::time_t time_;
std::tm tm_;
inline dispatcher(std::time_t t) : time_(t) {}
inline auto run() -> bool {
using namespace fmt::detail;
return handle(localtime_r(&time_, &tm_));
}
inline auto handle(std::tm* tm) -> bool { return tm != nullptr; }
inline auto handle(detail::null<>) -> bool {
using namespace fmt::detail;
return fallback(localtime_s(&tm_, &time_));
}
inline auto fallback(int res) -> bool { return res == 0; }
#if !FMT_MSC_VERSION
inline auto fallback(detail::null<>) -> bool {
using namespace fmt::detail;
std::tm* tm = std::localtime(&time_);
if (tm) tm_ = *tm;
return tm != nullptr;
}
#endif
};
dispatcher lt(time);
// Too big time values may be unsupported.
if (!lt.run()) FMT_THROW(format_error("time_t value out of range"));
return lt.tm_;
}
#if FMT_USE_LOCAL_TIME
template <typename Duration,
FMT_ENABLE_IF(detail::has_current_zone<Duration>())>
inline auto localtime(std::chrono::local_time<Duration> time) -> std::tm {
using namespace std::chrono;
using namespace fmt_detail;
return localtime(detail::to_time_t(current_zone()->to_sys<Duration>(time)));
}
#endif
/**
* Converts given time since epoch as `std::time_t` value into calendar time,
* expressed in Coordinated Universal Time (UTC). Unlike `std::gmtime`, this
@@ -652,7 +563,7 @@ inline void write_digit2_separated(char* buf, unsigned a, unsigned b,
// Add ASCII '0' to each digit byte and insert separators.
digits |= 0x3030003030003030 | (usep << 16) | (usep << 40);
constexpr const size_t len = 8;
constexpr size_t len = 8;
if (const_check(is_big_endian())) {
char tmp[len];
std::memcpy(tmp, &digits, len);
@@ -911,7 +822,14 @@ template <typename Derived> struct null_chrono_spec_handler {
FMT_CONSTEXPR void on_tz_name() { unsupported(); }
};
struct tm_format_checker : null_chrono_spec_handler<tm_format_checker> {
class tm_format_checker : public null_chrono_spec_handler<tm_format_checker> {
private:
bool has_timezone_ = false;
public:
constexpr explicit tm_format_checker(bool has_timezone)
: has_timezone_(has_timezone) {}
FMT_NORETURN inline void unsupported() {
FMT_THROW(format_error("no format"));
}
@@ -949,8 +867,12 @@ struct tm_format_checker : null_chrono_spec_handler<tm_format_checker> {
FMT_CONSTEXPR void on_24_hour_time() {}
FMT_CONSTEXPR void on_iso_time() {}
FMT_CONSTEXPR void on_am_pm() {}
FMT_CONSTEXPR void on_utc_offset(numeric_system) {}
FMT_CONSTEXPR void on_tz_name() {}
FMT_CONSTEXPR void on_utc_offset(numeric_system) {
if (!has_timezone_) FMT_THROW(format_error("no timezone"));
}
FMT_CONSTEXPR void on_tz_name() {
if (!has_timezone_) FMT_THROW(format_error("no timezone"));
}
};
inline auto tm_wday_full_name(int wday) -> const char* {
@@ -980,24 +902,27 @@ inline auto tm_mon_short_name(int mon) -> const char* {
}
template <typename T, typename = void>
struct has_member_data_tm_gmtoff : std::false_type {};
struct has_tm_gmtoff : std::false_type {};
template <typename T>
struct has_member_data_tm_gmtoff<T, void_t<decltype(T::tm_gmtoff)>>
: std::true_type {};
struct has_tm_gmtoff<T, void_t<decltype(T::tm_gmtoff)>> : std::true_type {};
template <typename T, typename = void>
struct has_member_data_tm_zone : std::false_type {};
template <typename T, typename = void> struct has_tm_zone : std::false_type {};
template <typename T>
struct has_member_data_tm_zone<T, void_t<decltype(T::tm_zone)>>
: std::true_type {};
struct has_tm_zone<T, void_t<decltype(T::tm_zone)>> : std::true_type {};
inline void tzset_once() {
static bool init = []() {
using namespace fmt_detail;
_tzset();
return false;
}();
ignore_unused(init);
template <typename T, FMT_ENABLE_IF(has_tm_zone<T>::value)>
auto set_tm_zone(T& time, char* tz) -> bool {
time.tm_zone = tz;
return true;
}
template <typename T, FMT_ENABLE_IF(!has_tm_zone<T>::value)>
auto set_tm_zone(T&, char*) -> bool {
return false;
}
inline auto utc() -> char* {
static char tz[] = "UTC";
return tz;
}
// Converts value to Int and checks that it's in the range [0, upper).
@@ -1005,7 +930,7 @@ template <typename T, typename Int, FMT_ENABLE_IF(std::is_integral<T>::value)>
inline auto to_nonnegative_int(T value, Int upper) -> Int {
if (!std::is_unsigned<Int>::value &&
(value < 0 || to_unsigned(value) > to_unsigned(upper))) {
FMT_THROW(fmt::format_error("chrono value is out of range"));
FMT_THROW(format_error("chrono value is out of range"));
}
return static_cast<Int>(value);
}
@@ -1090,7 +1015,7 @@ void write_fractional_seconds(OutputIt& out, Duration d, int precision = -1) {
// Format subseconds which are given as a floating point type with an
// appropriate number of digits. We cannot pass the Duration here, as we
// explicitly need to pass the Rep value in the chrono_formatter.
// explicitly need to pass the Rep value in the duration_formatter.
template <typename Duration>
void write_floating_seconds(memory_buffer& buf, Duration duration,
int num_fractional_digits = -1) {
@@ -1124,7 +1049,7 @@ class tm_writer {
static constexpr int days_per_week = 7;
const std::locale& loc_;
const bool is_classic_;
bool is_classic_;
OutputIt out_;
const Duration* subsecs_;
const std::tm& tm_;
@@ -1160,8 +1085,8 @@ class tm_writer {
}
auto tm_hour12() const noexcept -> int {
const auto h = tm_hour();
const auto z = h < 12 ? h : h - 12;
auto h = tm_hour();
auto z = h < 12 ? h : h - 12;
return z == 0 ? 12 : z;
}
@@ -1177,11 +1102,11 @@ class tm_writer {
// Algorithm: https://en.wikipedia.org/wiki/ISO_week_date.
auto iso_year_weeks(long long curr_year) const noexcept -> int {
const auto prev_year = curr_year - 1;
const auto curr_p =
auto prev_year = curr_year - 1;
auto curr_p =
(curr_year + curr_year / 4 - curr_year / 100 + curr_year / 400) %
days_per_week;
const auto prev_p =
auto prev_p =
(prev_year + prev_year / 4 - prev_year / 100 + prev_year / 400) %
days_per_week;
return 52 + ((curr_p == 4 || prev_p == 3) ? 1 : 0);
@@ -1191,15 +1116,15 @@ class tm_writer {
days_per_week;
}
auto tm_iso_week_year() const noexcept -> long long {
const auto year = tm_year();
const auto w = iso_week_num(tm_yday(), tm_wday());
auto year = tm_year();
auto w = iso_week_num(tm_yday(), tm_wday());
if (w < 1) return year - 1;
if (w > iso_year_weeks(year)) return year + 1;
return year;
}
auto tm_iso_week_of_year() const noexcept -> int {
const auto year = tm_year();
const auto w = iso_week_num(tm_yday(), tm_wday());
auto year = tm_year();
auto w = iso_week_num(tm_yday(), tm_wday());
if (w < 1) return iso_year_weeks(year - 1);
if (w > iso_year_weeks(year)) return 1;
return w;
@@ -1236,9 +1161,8 @@ class tm_writer {
uint32_or_64_or_128_t<long long> n = to_unsigned(year);
const int num_digits = count_digits(n);
if (negative && pad == pad_type::zero) *out_++ = '-';
if (width > num_digits) {
if (width > num_digits)
out_ = detail::write_padding(out_, pad, width - num_digits);
}
if (negative && pad != pad_type::zero) *out_++ = '-';
out_ = format_decimal<Char>(out_, n, num_digits);
}
@@ -1259,45 +1183,22 @@ class tm_writer {
write2(static_cast<int>(offset % 60));
}
template <typename T, FMT_ENABLE_IF(has_member_data_tm_gmtoff<T>::value)>
void format_utc_offset_impl(const T& tm, numeric_system ns) {
template <typename T, FMT_ENABLE_IF(has_tm_gmtoff<T>::value)>
void format_utc_offset(const T& tm, numeric_system ns) {
write_utc_offset(tm.tm_gmtoff, ns);
}
template <typename T, FMT_ENABLE_IF(!has_member_data_tm_gmtoff<T>::value)>
void format_utc_offset_impl(const T& tm, numeric_system ns) {
#if defined(_WIN32) && defined(_UCRT)
tzset_once();
long offset = 0;
_get_timezone(&offset);
if (tm.tm_isdst) {
long dstbias = 0;
_get_dstbias(&dstbias);
offset += dstbias;
}
write_utc_offset(-offset, ns);
#else
if (ns == numeric_system::standard) return format_localized('z');
// Extract timezone offset from timezone conversion functions.
std::tm gtm = tm;
std::time_t gt = std::mktime(&gtm);
std::tm ltm = gmtime(gt);
std::time_t lt = std::mktime(&ltm);
long long offset = gt - lt;
write_utc_offset(offset, ns);
#endif
template <typename T, FMT_ENABLE_IF(!has_tm_gmtoff<T>::value)>
void format_utc_offset(const T&, numeric_system ns) {
write_utc_offset(0, ns);
}
template <typename T, FMT_ENABLE_IF(has_member_data_tm_zone<T>::value)>
void format_tz_name_impl(const T& tm) {
if (is_classic_)
out_ = write_tm_str<Char>(out_, tm.tm_zone, loc_);
else
format_localized('Z');
template <typename T, FMT_ENABLE_IF(has_tm_zone<T>::value)>
void format_tz_name(const T& tm) {
out_ = write_tm_str<Char>(out_, tm.tm_zone, loc_);
}
template <typename T, FMT_ENABLE_IF(!has_member_data_tm_zone<T>::value)>
void format_tz_name_impl(const T&) {
format_localized('Z');
template <typename T, FMT_ENABLE_IF(!has_tm_zone<T>::value)>
void format_tz_name(const T&) {
out_ = std::copy_n(utc(), 3, out_);
}
void format_localized(char format, char modifier = 0) {
@@ -1408,8 +1309,8 @@ class tm_writer {
out_ = copy<Char>(std::begin(buf) + offset, std::end(buf), out_);
}
void on_utc_offset(numeric_system ns) { format_utc_offset_impl(tm_, ns); }
void on_tz_name() { format_tz_name_impl(tm_); }
void on_utc_offset(numeric_system ns) { format_utc_offset(tm_, ns); }
void on_tz_name() { format_tz_name(tm_); }
void on_year(numeric_system ns, pad_type pad) {
if (is_classic_ || ns == numeric_system::standard)
@@ -1483,11 +1384,10 @@ class tm_writer {
void on_day_of_year(pad_type pad) {
auto yday = tm_yday() + 1;
auto digit1 = yday / 100;
if (digit1 != 0) {
if (digit1 != 0)
write1(digit1);
} else {
else
out_ = detail::write_padding(out_, pad);
}
write2(yday % 100, pad);
}
@@ -1624,18 +1524,16 @@ template <typename Rep, typename Period,
FMT_ENABLE_IF(std::is_integral<Rep>::value)>
inline auto get_milliseconds(std::chrono::duration<Rep, Period> d)
-> std::chrono::duration<Rep, std::milli> {
// this may overflow and/or the result may not fit in the
// target type.
// This may overflow and/or the result may not fit in the target type.
#if FMT_SAFE_DURATION_CAST
using CommonSecondsType =
using common_seconds_type =
typename std::common_type<decltype(d), std::chrono::seconds>::type;
const auto d_as_common = detail::duration_cast<CommonSecondsType>(d);
const auto d_as_whole_seconds =
auto d_as_common = detail::duration_cast<common_seconds_type>(d);
auto d_as_whole_seconds =
detail::duration_cast<std::chrono::seconds>(d_as_common);
// this conversion should be nonproblematic
const auto diff = d_as_common - d_as_whole_seconds;
const auto ms =
detail::duration_cast<std::chrono::duration<Rep, std::milli>>(diff);
// This conversion should be nonproblematic.
auto diff = d_as_common - d_as_whole_seconds;
auto ms = detail::duration_cast<std::chrono::duration<Rep, std::milli>>(diff);
return ms;
#else
auto s = detail::duration_cast<std::chrono::seconds>(d);
@@ -1707,32 +1605,28 @@ class get_locale {
}
};
template <typename FormatContext, typename OutputIt, typename Rep,
typename Period>
struct chrono_formatter {
FormatContext& context;
OutputIt out;
int precision;
bool localized = false;
template <typename Char, typename Rep, typename Period>
struct duration_formatter {
using iterator = basic_appender<Char>;
iterator out;
// rep is unsigned to avoid overflow.
using rep =
conditional_t<std::is_integral<Rep>::value && sizeof(Rep) < sizeof(int),
unsigned, typename make_unsigned_or_unchanged<Rep>::type>;
rep val;
int precision;
locale_ref locale;
bool localized = false;
using seconds = std::chrono::duration<rep>;
seconds s;
using milliseconds = std::chrono::duration<rep, std::milli>;
bool negative;
using char_type = typename FormatContext::char_type;
using tm_writer_type = tm_writer<OutputIt, char_type>;
using tm_writer_type = tm_writer<iterator, Char>;
chrono_formatter(FormatContext& ctx, OutputIt o,
std::chrono::duration<Rep, Period> d)
: context(ctx),
out(o),
val(static_cast<rep>(d.count())),
negative(false) {
duration_formatter(iterator o, std::chrono::duration<Rep, Period> d,
locale_ref loc)
: out(o), val(static_cast<rep>(d.count())), locale(loc), negative(false) {
if (d.count() < 0) {
val = 0 - val;
negative = true;
@@ -1746,19 +1640,16 @@ struct chrono_formatter {
// returns true if nan or inf, writes to out.
auto handle_nan_inf() -> bool {
if (isfinite(val)) {
return false;
}
if (isfinite(val)) return false;
if (isnan(val)) {
write_nan();
return true;
}
// must be +-inf
if (val > 0) {
write_pinf();
} else {
write_ninf();
}
if (val > 0)
std::copy_n("inf", 3, out);
else
std::copy_n("-inf", 4, out);
return true;
}
@@ -1786,10 +1677,9 @@ struct chrono_formatter {
}
void write_sign() {
if (negative) {
*out++ = '-';
negative = false;
}
if (!negative) return;
*out++ = '-';
negative = false;
}
void write(Rep value, int width, pad_type pad = pad_type::zero) {
@@ -1801,24 +1691,22 @@ struct chrono_formatter {
if (width > num_digits) {
out = detail::write_padding(out, pad, width - num_digits);
}
out = format_decimal<char_type>(out, n, num_digits);
out = format_decimal<Char>(out, n, num_digits);
}
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); }
template <typename Callback, typename... Args>
void format_tm(const tm& time, Callback cb, Args... args) {
if (isnan(val)) return write_nan();
get_locale loc(localized, context.locale());
get_locale loc(localized, locale);
auto w = tm_writer_type(loc, out, time);
(w.*cb)(args...);
out = w.out();
}
void on_text(const char_type* begin, const char_type* end) {
copy<char_type>(begin, end, out);
void on_text(const Char* begin, const Char* end) {
copy<Char>(begin, end, out);
}
// These are not implemented because durations don't have date information.
@@ -1888,13 +1776,12 @@ struct chrono_formatter {
write_floating_seconds(buf, std::chrono::duration<rep, Period>(val),
precision);
if (negative) *out++ = '-';
if (buf.size() < 2 || buf[1] == '.') {
if (buf.size() < 2 || buf[1] == '.')
out = detail::write_padding(out, pad);
}
out = copy<char_type>(buf.begin(), buf.end(), out);
out = copy<Char>(buf.begin(), buf.end(), out);
} else {
write(second(), 2, pad);
write_fractional_seconds<char_type>(
write_fractional_seconds<Char>(
out, std::chrono::duration<rep, Period>(val), precision);
}
return;
@@ -1936,12 +1823,10 @@ struct chrono_formatter {
void on_duration_value() {
if (handle_nan_inf()) return;
write_sign();
out = format_duration_value<char_type>(out, val, precision);
out = format_duration_value<Char>(out, val, precision);
}
void on_duration_unit() {
out = format_duration_unit<char_type, Period>(out);
}
void on_duration_unit() { out = format_duration_unit<Char, Period>(out); }
};
} // namespace detail
@@ -2011,12 +1896,11 @@ class year_month_day {
constexpr auto month() const noexcept -> fmt::month { return month_; }
constexpr auto day() const noexcept -> fmt::day { return day_; }
};
#endif
#endif // __cpp_lib_chrono >= 201907
template <typename Char>
struct formatter<weekday, Char> : private formatter<std::tm, Char> {
private:
bool localized_ = false;
bool use_tm_formatter_ = false;
public:
@@ -2024,8 +1908,7 @@ struct formatter<weekday, Char> : private formatter<std::tm, Char> {
auto it = ctx.begin(), end = ctx.end();
if (it != end && *it == 'L') {
++it;
localized_ = true;
return it;
this->set_localized();
}
use_tm_formatter_ = it != end && *it != '}';
return use_tm_formatter_ ? formatter<std::tm, Char>::parse(ctx) : it;
@@ -2036,7 +1919,7 @@ struct formatter<weekday, Char> : private formatter<std::tm, Char> {
auto time = std::tm();
time.tm_wday = static_cast<int>(wd.c_encoding());
if (use_tm_formatter_) return formatter<std::tm, Char>::format(time, ctx);
detail::get_locale loc(localized_, ctx.locale());
detail::get_locale loc(this->localized(), ctx.locale());
auto w = detail::tm_writer<decltype(ctx.out()), Char>(loc, ctx.out(), time);
w.on_abbr_weekday();
return w.out();
@@ -2070,7 +1953,6 @@ struct formatter<day, Char> : private formatter<std::tm, Char> {
template <typename Char>
struct formatter<month, Char> : private formatter<std::tm, Char> {
private:
bool localized_ = false;
bool use_tm_formatter_ = false;
public:
@@ -2078,8 +1960,7 @@ struct formatter<month, Char> : private formatter<std::tm, Char> {
auto it = ctx.begin(), end = ctx.end();
if (it != end && *it == 'L') {
++it;
localized_ = true;
return it;
this->set_localized();
}
use_tm_formatter_ = it != end && *it != '}';
return use_tm_formatter_ ? formatter<std::tm, Char>::parse(ctx) : it;
@@ -2090,7 +1971,7 @@ struct formatter<month, Char> : private formatter<std::tm, Char> {
auto time = std::tm();
time.tm_mon = static_cast<int>(static_cast<unsigned>(m)) - 1;
if (use_tm_formatter_) return formatter<std::tm, Char>::format(time, ctx);
detail::get_locale loc(localized_, ctx.locale());
detail::get_locale loc(this->localized(), ctx.locale());
auto w = detail::tm_writer<decltype(ctx.out()), Char>(loc, ctx.out(), time);
w.on_abbr_month();
return w.out();
@@ -2154,7 +2035,6 @@ struct formatter<std::chrono::duration<Rep, Period>, Char> {
format_specs specs_;
detail::arg_ref<Char> width_ref_;
detail::arg_ref<Char> precision_ref_;
bool localized_ = false;
basic_string_view<Char> fmt_;
public:
@@ -2177,7 +2057,7 @@ struct formatter<std::chrono::duration<Rep, Period>, Char> {
it = detail::parse_precision(it, end, specs_, precision_ref_, ctx);
}
if (it != end && *it == 'L') {
localized_ = true;
specs_.set_localized();
++it;
}
end = detail::parse_chrono_format(it, end, checker);
@@ -2204,11 +2084,10 @@ struct formatter<std::chrono::duration<Rep, Period>, Char> {
out = detail::format_duration_value<Char>(out, d.count(), precision);
detail::format_duration_unit<Char, Period>(out);
} else {
using chrono_formatter =
detail::chrono_formatter<FormatContext, decltype(out), Rep, Period>;
auto f = chrono_formatter(ctx, out, d);
auto f =
detail::duration_formatter<Char, Rep, Period>(out, d, ctx.locale());
f.precision = precision;
f.localized = localized_;
f.localized = specs_.localized();
detail::parse_chrono_format(begin, end, f);
}
return detail::write(
@@ -2220,30 +2099,15 @@ template <typename Char> struct formatter<std::tm, Char> {
private:
format_specs specs_;
detail::arg_ref<Char> width_ref_;
basic_string_view<Char> fmt_ =
detail::string_literal<Char, '%', 'F', ' ', '%', 'T'>();
protected:
basic_string_view<Char> fmt_;
auto localized() const -> bool { return specs_.localized(); }
FMT_CONSTEXPR void set_localized() { specs_.set_localized(); }
template <typename Duration, typename FormatContext>
auto do_format(const std::tm& tm, FormatContext& ctx,
const Duration* subsecs) const -> decltype(ctx.out()) {
auto specs = specs_;
auto buf = basic_memory_buffer<Char>();
auto out = basic_appender<Char>(buf);
detail::handle_dynamic_spec(specs.dynamic_width(), specs.width, width_ref_,
ctx);
auto loc_ref = ctx.locale();
detail::get_locale loc(static_cast<bool>(loc_ref), loc_ref);
auto w =
detail::tm_writer<decltype(out), Char, Duration>(loc, out, tm, subsecs);
detail::parse_chrono_format(fmt_.begin(), fmt_.end(), w);
return detail::write(
ctx.out(), basic_string_view<Char>(buf.data(), buf.size()), specs);
}
public:
FMT_CONSTEXPR auto parse(parse_context<Char>& ctx) -> const Char* {
FMT_CONSTEXPR auto do_parse(parse_context<Char>& ctx, bool has_timezone)
-> const Char* {
auto it = ctx.begin(), end = ctx.end();
if (it == end || *it == '}') return it;
@@ -2256,12 +2120,41 @@ template <typename Char> struct formatter<std::tm, Char> {
if (it == end) return it;
}
end = detail::parse_chrono_format(it, end, detail::tm_format_checker());
if (*it == 'L') {
specs_.set_localized();
++it;
}
end = detail::parse_chrono_format(it, end,
detail::tm_format_checker(has_timezone));
// Replace the default format string only if the new spec is not empty.
if (end != it) fmt_ = {it, detail::to_unsigned(end - it)};
return end;
}
template <typename Duration, typename FormatContext>
auto do_format(const std::tm& tm, FormatContext& ctx,
const Duration* subsecs) const -> decltype(ctx.out()) {
auto specs = specs_;
auto buf = basic_memory_buffer<Char>();
auto out = basic_appender<Char>(buf);
detail::handle_dynamic_spec(specs.dynamic_width(), specs.width, width_ref_,
ctx);
auto loc_ref = specs.localized() ? ctx.locale() : locale_ref();
detail::get_locale loc(static_cast<bool>(loc_ref), loc_ref);
auto w = detail::tm_writer<basic_appender<Char>, Char, Duration>(
loc, out, tm, subsecs);
detail::parse_chrono_format(fmt_.begin(), fmt_.end(), w);
return detail::write(
ctx.out(), basic_string_view<Char>(buf.data(), buf.size()), specs);
}
public:
FMT_CONSTEXPR auto parse(parse_context<Char>& ctx) -> const Char* {
return do_parse(ctx, detail::has_tm_gmtoff<std::tm>::value);
}
template <typename FormatContext>
auto format(const std::tm& tm, FormatContext& ctx) const
-> decltype(ctx.out()) {
@@ -2269,10 +2162,11 @@ template <typename Char> struct formatter<std::tm, Char> {
}
};
// DEPRECATED! Reversed order of template parameters.
template <typename Char, typename Duration>
struct formatter<sys_time<Duration>, Char> : formatter<std::tm, Char> {
FMT_CONSTEXPR formatter() {
this->fmt_ = detail::string_literal<Char, '%', 'F', ' ', '%', 'T'>();
struct formatter<sys_time<Duration>, Char> : private formatter<std::tm, Char> {
FMT_CONSTEXPR auto parse(parse_context<Char>& ctx) -> const Char* {
return this->do_parse(ctx, true);
}
template <typename FormatContext>
@@ -2283,6 +2177,7 @@ struct formatter<sys_time<Duration>, Char> : formatter<std::tm, Char> {
if (detail::const_check(
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);
}
Duration epoch = val.time_since_epoch();
@@ -2290,11 +2185,13 @@ struct formatter<sys_time<Duration>, Char> : formatter<std::tm, Char> {
epoch - detail::duration_cast<std::chrono::seconds>(epoch));
if (subsecs.count() < 0) {
auto second = detail::duration_cast<Duration>(std::chrono::seconds(1));
if (tm.tm_sec != 0)
if (tm.tm_sec != 0) {
--tm.tm_sec;
else
} else {
tm = gmtime(val - second);
subsecs += detail::duration_cast<Duration>(std::chrono::seconds(1));
detail::set_tm_zone(tm, detail::utc());
}
subsecs += second;
}
return formatter<std::tm, Char>::do_format(tm, ctx, &subsecs);
}
@@ -2312,23 +2209,29 @@ struct formatter<utc_time<Duration>, Char>
};
template <typename Duration, typename Char>
struct formatter<local_time<Duration>, Char> : formatter<std::tm, Char> {
FMT_CONSTEXPR formatter() {
this->fmt_ = detail::string_literal<Char, '%', 'F', ' ', '%', 'T'>();
struct formatter<local_time<Duration>, Char>
: private formatter<std::tm, Char> {
FMT_CONSTEXPR auto parse(parse_context<Char>& ctx) -> const Char* {
return this->do_parse(ctx, false);
}
template <typename FormatContext>
auto format(local_time<Duration> val, FormatContext& ctx) const
-> decltype(ctx.out()) {
auto time_since_epoch = val.time_since_epoch();
auto seconds_since_epoch =
detail::duration_cast<std::chrono::seconds>(time_since_epoch);
// Use gmtime to prevent time zone conversion since local_time has an
// unspecified time zone.
std::tm t = gmtime(seconds_since_epoch.count());
using period = typename Duration::period;
if (period::num == 1 && period::den == 1 &&
!std::is_floating_point<typename Duration::rep>::value) {
return formatter<std::tm, Char>::format(localtime(val), ctx);
return formatter<std::tm, Char>::format(t, ctx);
}
auto epoch = val.time_since_epoch();
auto subsecs = detail::duration_cast<Duration>(
epoch - detail::duration_cast<std::chrono::seconds>(epoch));
return formatter<std::tm, Char>::do_format(localtime(val), ctx, &subsecs);
auto subsecs =
detail::duration_cast<Duration>(time_since_epoch - seconds_since_epoch);
return formatter<std::tm, Char>::do_format(t, ctx, &subsecs);
}
};

View File

@@ -190,11 +190,11 @@ enum class emphasis : uint8_t {
// rgb is a struct for red, green and blue colors.
// Using the name "rgb" makes some editors show the color in a tooltip.
struct rgb {
FMT_CONSTEXPR rgb() : r(0), g(0), b(0) {}
FMT_CONSTEXPR rgb(uint8_t r_, uint8_t g_, uint8_t b_) : r(r_), g(g_), b(b_) {}
FMT_CONSTEXPR rgb(uint32_t hex)
constexpr rgb() : r(0), g(0), b(0) {}
constexpr rgb(uint8_t r_, uint8_t g_, uint8_t b_) : r(r_), g(g_), b(b_) {}
constexpr rgb(uint32_t hex)
: r((hex >> 16) & 0xFF), g((hex >> 8) & 0xFF), b(hex & 0xFF) {}
FMT_CONSTEXPR rgb(color hex)
constexpr rgb(color hex)
: r((uint32_t(hex) >> 16) & 0xFF),
g((uint32_t(hex) >> 8) & 0xFF),
b(uint32_t(hex) & 0xFF) {}
@@ -205,97 +205,135 @@ struct rgb {
namespace detail {
// color is a struct of either a rgb color or a terminal color.
// A bit-packed variant of an RGB color, a terminal color, or unset color.
// see text_style for the bit-packing scheme.
struct color_type {
FMT_CONSTEXPR color_type() noexcept : is_rgb(), value{} {}
FMT_CONSTEXPR color_type(color rgb_color) noexcept : is_rgb(true), value{} {
value.rgb_color = static_cast<uint32_t>(rgb_color);
constexpr color_type() noexcept = default;
constexpr color_type(color rgb_color) noexcept
: value_(static_cast<uint32_t>(rgb_color) | (1 << 24)) {}
constexpr color_type(rgb rgb_color) noexcept
: color_type(static_cast<color>(
(static_cast<uint32_t>(rgb_color.r) << 16) |
(static_cast<uint32_t>(rgb_color.g) << 8) | rgb_color.b)) {}
constexpr color_type(terminal_color term_color) noexcept
: value_(static_cast<uint32_t>(term_color) | (3 << 24)) {}
constexpr auto is_terminal_color() const noexcept -> bool {
return (value_ & (1 << 25)) != 0;
}
FMT_CONSTEXPR color_type(rgb rgb_color) noexcept : is_rgb(true), value{} {
value.rgb_color = (static_cast<uint32_t>(rgb_color.r) << 16) |
(static_cast<uint32_t>(rgb_color.g) << 8) | rgb_color.b;
constexpr auto value() const noexcept -> uint32_t {
return value_ & 0xFFFFFF;
}
FMT_CONSTEXPR color_type(terminal_color term_color) noexcept
: is_rgb(), value{} {
value.term_color = static_cast<uint8_t>(term_color);
}
bool is_rgb;
union color_union {
uint8_t term_color;
uint32_t rgb_color;
} value;
constexpr color_type(uint32_t value) noexcept : value_(value) {}
uint32_t value_ = 0;
};
} // namespace detail
/// A text style consisting of foreground and background colors and emphasis.
class text_style {
// The information is packed as follows:
// ┌──┐
// │ 0│─┐
// │..│ ├── foreground color value
// │23│─┘
// ├──┤
// │24│─┬── discriminator for the above value. 00 if unset, 01 if it's
// │25│─┘ an RGB color, or 11 if it's a terminal color (10 is unused)
// ├──┤
// │26│──── overflow bit, always zero (see below)
// ├──┤
// │27│─┐
// │..│ │
// │50│ │
// ├──┤ │
// │51│ ├── background color (same format as the foreground color)
// │52│ │
// ├──┤ │
// │53│─┘
// ├──┤
// │54│─┐
// │..│ ├── emphases
// │61│─┘
// ├──┤
// │62│─┬── unused
// │63│─┘
// └──┘
// The overflow bits are there to make operator|= efficient.
// When ORing, we must throw if, for either the foreground or background,
// one style specifies a terminal color and the other specifies any color
// (terminal or RGB); in other words, if one discriminator is 11 and the
// other is 11 or 01.
//
// We do that check by adding the styles. Consider what adding does to each
// possible pair of discriminators:
// 00 + 00 = 000
// 01 + 00 = 001
// 11 + 00 = 011
// 01 + 01 = 010
// 11 + 01 = 100 (!!)
// 11 + 11 = 110 (!!)
// In the last two cases, the ones we want to catch, the third bit——the
// overflow bit——is set. Bingo.
//
// We must take into account the possible carry bit from the bits
// before the discriminator. The only potentially problematic case is
// 11 + 00 = 011 (a carry bit would make it 100, not good!), but a carry
// bit is impossible in that case, because 00 (unset color) means the
// 24 bits that precede the discriminator are all zero.
//
// This test can be applied to both colors simultaneously.
public:
FMT_CONSTEXPR text_style(emphasis em = emphasis()) noexcept
: set_foreground_color(), set_background_color(), ems(em) {}
: style_(static_cast<uint64_t>(em) << 54) {}
FMT_CONSTEXPR auto operator|=(const text_style& rhs) -> text_style& {
if (!set_foreground_color) {
set_foreground_color = rhs.set_foreground_color;
foreground_color = rhs.foreground_color;
} else if (rhs.set_foreground_color) {
if (!foreground_color.is_rgb || !rhs.foreground_color.is_rgb)
report_error("can't OR a terminal color");
foreground_color.value.rgb_color |= rhs.foreground_color.value.rgb_color;
}
if (!set_background_color) {
set_background_color = rhs.set_background_color;
background_color = rhs.background_color;
} else if (rhs.set_background_color) {
if (!background_color.is_rgb || !rhs.background_color.is_rgb)
report_error("can't OR a terminal color");
background_color.value.rgb_color |= rhs.background_color.value.rgb_color;
}
ems = static_cast<emphasis>(static_cast<uint8_t>(ems) |
static_cast<uint8_t>(rhs.ems));
FMT_CONSTEXPR auto operator|=(text_style rhs) -> text_style& {
if (((style_ + rhs.style_) & ((1ULL << 26) | (1ULL << 53))) != 0)
report_error("can't OR a terminal color");
style_ |= rhs.style_;
return *this;
}
friend FMT_CONSTEXPR auto operator|(text_style lhs, const text_style& rhs)
friend FMT_CONSTEXPR auto operator|(text_style lhs, text_style rhs)
-> text_style {
return lhs |= rhs;
}
FMT_CONSTEXPR auto operator==(text_style rhs) const noexcept -> bool {
return style_ == rhs.style_;
}
FMT_CONSTEXPR auto operator!=(text_style rhs) const noexcept -> bool {
return !(*this == rhs);
}
FMT_CONSTEXPR auto has_foreground() const noexcept -> bool {
return set_foreground_color;
return (style_ & (1 << 24)) != 0;
}
FMT_CONSTEXPR auto has_background() const noexcept -> bool {
return set_background_color;
return (style_ & (1ULL << 51)) != 0;
}
FMT_CONSTEXPR auto has_emphasis() const noexcept -> bool {
return static_cast<uint8_t>(ems) != 0;
return (style_ >> 54) != 0;
}
FMT_CONSTEXPR auto get_foreground() const noexcept -> detail::color_type {
FMT_ASSERT(has_foreground(), "no foreground specified for this style");
return foreground_color;
return style_ & 0x3FFFFFF;
}
FMT_CONSTEXPR auto get_background() const noexcept -> detail::color_type {
FMT_ASSERT(has_background(), "no background specified for this style");
return background_color;
return (style_ >> 27) & 0x3FFFFFF;
}
FMT_CONSTEXPR auto get_emphasis() const noexcept -> emphasis {
FMT_ASSERT(has_emphasis(), "no emphasis specified for this style");
return ems;
return static_cast<emphasis>(style_ >> 54);
}
private:
FMT_CONSTEXPR text_style(bool is_foreground,
detail::color_type text_color) noexcept
: set_foreground_color(), set_background_color(), ems() {
if (is_foreground) {
foreground_color = text_color;
set_foreground_color = true;
} else {
background_color = text_color;
set_background_color = true;
}
}
FMT_CONSTEXPR text_style(uint64_t style) noexcept : style_(style) {}
friend FMT_CONSTEXPR auto fg(detail::color_type foreground) noexcept
-> text_style;
@@ -303,23 +341,19 @@ class text_style {
friend FMT_CONSTEXPR auto bg(detail::color_type background) noexcept
-> text_style;
detail::color_type foreground_color;
detail::color_type background_color;
bool set_foreground_color;
bool set_background_color;
emphasis ems;
uint64_t style_ = 0;
};
/// Creates a text style from the foreground (text) color.
FMT_CONSTEXPR inline auto fg(detail::color_type foreground) noexcept
-> text_style {
return text_style(true, foreground);
return foreground.value_;
}
/// Creates a text style from the background color.
FMT_CONSTEXPR inline auto bg(detail::color_type background) noexcept
-> text_style {
return text_style(false, background);
return static_cast<uint64_t>(background.value_) << 27;
}
FMT_CONSTEXPR inline auto operator|(emphasis lhs, emphasis rhs) noexcept
@@ -334,37 +368,35 @@ template <typename Char> struct ansi_color_escape {
const char* esc) noexcept {
// If we have a terminal color, we need to output another escape code
// sequence.
if (!text_color.is_rgb) {
if (text_color.is_terminal_color()) {
bool is_background = esc == string_view("\x1b[48;2;");
uint32_t value = text_color.value.term_color;
uint32_t value = text_color.value();
// Background ASCII codes are the same as the foreground ones but with
// 10 more.
if (is_background) value += 10u;
size_t index = 0;
buffer[index++] = static_cast<Char>('\x1b');
buffer[index++] = static_cast<Char>('[');
buffer[size++] = static_cast<Char>('\x1b');
buffer[size++] = static_cast<Char>('[');
if (value >= 100u) {
buffer[index++] = static_cast<Char>('1');
buffer[size++] = static_cast<Char>('1');
value %= 100u;
}
buffer[index++] = static_cast<Char>('0' + value / 10u);
buffer[index++] = static_cast<Char>('0' + value % 10u);
buffer[size++] = static_cast<Char>('0' + value / 10u);
buffer[size++] = static_cast<Char>('0' + value % 10u);
buffer[index++] = static_cast<Char>('m');
buffer[index++] = static_cast<Char>('\0');
buffer[size++] = static_cast<Char>('m');
return;
}
for (int i = 0; i < 7; i++) {
buffer[i] = static_cast<Char>(esc[i]);
}
rgb color(text_color.value.rgb_color);
rgb color(text_color.value());
to_esc(color.r, buffer + 7, ';');
to_esc(color.g, buffer + 11, ';');
to_esc(color.b, buffer + 15, 'm');
buffer[19] = static_cast<Char>(0);
size = 19;
}
FMT_CONSTEXPR ansi_color_escape(emphasis em) noexcept {
uint8_t em_codes[num_emphases] = {};
@@ -377,26 +409,28 @@ template <typename Char> struct ansi_color_escape {
if (has_emphasis(em, emphasis::conceal)) em_codes[6] = 8;
if (has_emphasis(em, emphasis::strikethrough)) em_codes[7] = 9;
size_t index = 0;
buffer[size++] = static_cast<Char>('\x1b');
buffer[size++] = static_cast<Char>('[');
for (size_t i = 0; i < num_emphases; ++i) {
if (!em_codes[i]) continue;
buffer[index++] = static_cast<Char>('\x1b');
buffer[index++] = static_cast<Char>('[');
buffer[index++] = static_cast<Char>('0' + em_codes[i]);
buffer[index++] = static_cast<Char>('m');
buffer[size++] = static_cast<Char>('0' + em_codes[i]);
buffer[size++] = static_cast<Char>(';');
}
buffer[index++] = static_cast<Char>(0);
buffer[size - 1] = static_cast<Char>('m');
}
FMT_CONSTEXPR operator const Char*() const noexcept { return buffer; }
FMT_CONSTEXPR auto begin() const noexcept -> const Char* { return buffer; }
FMT_CONSTEXPR20 auto end() const noexcept -> const Char* {
return buffer + basic_string_view<Char>(buffer).size();
FMT_CONSTEXPR auto end() const noexcept -> const Char* {
return buffer + size;
}
private:
static constexpr size_t num_emphases = 8;
Char buffer[7u + 3u * num_emphases + 1u];
Char buffer[7u + 4u * num_emphases];
size_t size = 0;
static FMT_CONSTEXPR void to_esc(uint8_t c, Char* out,
char delimiter) noexcept {
@@ -441,32 +475,26 @@ template <typename T> struct styled_arg : view {
};
template <typename Char>
void vformat_to(buffer<Char>& buf, const text_style& ts,
basic_string_view<Char> fmt,
void vformat_to(buffer<Char>& buf, text_style ts, basic_string_view<Char> fmt,
basic_format_args<buffered_context<Char>> args) {
bool has_style = false;
if (ts.has_emphasis()) {
has_style = true;
auto emphasis = make_emphasis<Char>(ts.get_emphasis());
buf.append(emphasis.begin(), emphasis.end());
}
if (ts.has_foreground()) {
has_style = true;
auto foreground = make_foreground_color<Char>(ts.get_foreground());
buf.append(foreground.begin(), foreground.end());
}
if (ts.has_background()) {
has_style = true;
auto background = make_background_color<Char>(ts.get_background());
buf.append(background.begin(), background.end());
}
vformat_to(buf, fmt, args);
if (has_style) reset_color<Char>(buf);
if (ts != text_style()) reset_color<Char>(buf);
}
} // namespace detail
inline void vprint(FILE* f, const text_style& ts, string_view fmt,
format_args args) {
inline void vprint(FILE* f, text_style ts, string_view fmt, format_args args) {
auto buf = memory_buffer();
detail::vformat_to(buf, ts, fmt, args);
print(f, FMT_STRING("{}"), string_view(buf.begin(), buf.size()));
@@ -482,8 +510,7 @@ inline void vprint(FILE* f, const text_style& ts, string_view fmt,
* "Elapsed time: {0:.2f} seconds", 1.23);
*/
template <typename... T>
void print(FILE* f, const text_style& ts, format_string<T...> fmt,
T&&... args) {
void print(FILE* f, text_style ts, format_string<T...> fmt, T&&... args) {
vprint(f, ts, fmt.str, vargs<T...>{{args...}});
}
@@ -497,11 +524,11 @@ void print(FILE* f, const text_style& ts, format_string<T...> fmt,
* "Elapsed time: {0:.2f} seconds", 1.23);
*/
template <typename... T>
void print(const text_style& ts, format_string<T...> fmt, T&&... args) {
void print(text_style ts, format_string<T...> fmt, T&&... args) {
return print(stdout, ts, fmt, std::forward<T>(args)...);
}
inline auto vformat(const text_style& ts, string_view fmt, format_args args)
inline auto vformat(text_style ts, string_view fmt, format_args args)
-> std::string {
auto buf = memory_buffer();
detail::vformat_to(buf, ts, fmt, args);
@@ -521,7 +548,7 @@ inline auto vformat(const text_style& ts, string_view fmt, format_args args)
* ```
*/
template <typename... T>
inline auto format(const text_style& ts, format_string<T...> fmt, T&&... args)
inline auto format(text_style ts, format_string<T...> fmt, T&&... args)
-> std::string {
return fmt::vformat(ts, fmt.str, vargs<T...>{{args...}});
}
@@ -529,8 +556,8 @@ inline auto format(const text_style& ts, format_string<T...> fmt, T&&... args)
/// Formats a string with the given text_style and writes the output to `out`.
template <typename OutputIt,
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, char>::value)>
auto vformat_to(OutputIt out, const text_style& ts, string_view fmt,
format_args args) -> OutputIt {
auto vformat_to(OutputIt out, text_style ts, string_view fmt, format_args args)
-> OutputIt {
auto&& buf = detail::get_buffer<char>(out);
detail::vformat_to(buf, ts, fmt, args);
return detail::get_iterator(buf, out);
@@ -548,8 +575,8 @@ auto vformat_to(OutputIt out, const text_style& ts, string_view fmt,
*/
template <typename OutputIt, typename... T,
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, char>::value)>
inline auto format_to(OutputIt out, const text_style& ts,
format_string<T...> fmt, T&&... args) -> OutputIt {
inline auto format_to(OutputIt out, text_style ts, format_string<T...> fmt,
T&&... args) -> OutputIt {
return vformat_to(out, ts, fmt.str, vargs<T...>{{args...}});
}

View File

@@ -22,8 +22,6 @@ FMT_EXPORT class compiled_string {};
template <typename S>
struct is_compiled_string : std::is_base_of<compiled_string, S> {};
namespace detail {
/**
* Converts a string literal `s` into a format string that will be parsed at
* compile time and converted into efficient formatting code. Requires C++17
@@ -41,18 +39,40 @@ namespace detail {
# define FMT_COMPILE(s) FMT_STRING(s)
#endif
/**
* Converts a string literal into a format string that will be parsed at
* compile time and converted into efficient formatting code. Requires support
* for class types in constant template parameters (a C++20 feature).
*
* **Example**:
*
* // Converts 42 into std::string using the most efficient method and no
* // runtime format string processing.
* using namespace fmt::literals;
* std::string s = fmt::format("{}"_cf, 42);
*/
#if FMT_USE_NONTYPE_TEMPLATE_ARGS
inline namespace literals {
template <detail::fixed_string Str> constexpr auto operator""_cf() {
return FMT_COMPILE(Str.data);
}
} // namespace literals
#endif
namespace detail {
template <typename T, typename... Tail>
auto first(const T& value, const Tail&...) -> const T& {
constexpr auto first(const T& value, const Tail&...) -> const T& {
return value;
}
#if defined(__cpp_if_constexpr) && defined(__cpp_return_type_deduction)
template <typename... Args> struct type_list {};
template <typename... T> struct type_list {};
// Returns a reference to the argument at index N from [first, rest...].
template <int N, typename T, typename... Args>
constexpr const auto& get([[maybe_unused]] const T& first,
[[maybe_unused]] const Args&... rest) {
constexpr auto get([[maybe_unused]] const T& first,
[[maybe_unused]] const Args&... rest) -> const auto& {
static_assert(N < 1 + sizeof...(Args), "index is out of bounds");
if constexpr (N == 0)
return first;
@@ -84,8 +104,8 @@ FMT_CONSTEXPR auto get_arg_index_by_name(basic_string_view<Char> name) -> int {
}
template <typename Char, typename... Args>
constexpr int get_arg_index_by_name(basic_string_view<Char> name,
type_list<Args...>) {
constexpr auto get_arg_index_by_name(basic_string_view<Char> name,
type_list<Args...>) -> int {
return get_arg_index_by_name<Args...>(name);
}
@@ -105,8 +125,8 @@ template <typename Char> struct text {
basic_string_view<Char> data;
using char_type = Char;
template <typename OutputIt, typename... Args>
constexpr OutputIt format(OutputIt out, const Args&...) const {
template <typename OutputIt, typename... T>
constexpr auto format(OutputIt out, const T&...) const -> OutputIt {
return write<Char>(out, data);
}
};
@@ -115,8 +135,8 @@ template <typename Char>
struct is_compiled_format<text<Char>> : std::true_type {};
template <typename Char>
constexpr text<Char> make_text(basic_string_view<Char> s, size_t pos,
size_t size) {
constexpr auto make_text(basic_string_view<Char> s, size_t pos, size_t size)
-> text<Char> {
return {{&s[pos], size}};
}
@@ -124,8 +144,8 @@ template <typename Char> struct code_unit {
Char value;
using char_type = Char;
template <typename OutputIt, typename... Args>
constexpr OutputIt format(OutputIt out, const Args&...) const {
template <typename OutputIt, typename... T>
constexpr auto format(OutputIt out, const T&...) const -> OutputIt {
*out++ = value;
return out;
}
@@ -133,7 +153,7 @@ template <typename Char> struct code_unit {
// This ensures that the argument type is convertible to `const T&`.
template <typename T, int N, typename... Args>
constexpr const T& get_arg_checked(const Args&... args) {
constexpr auto get_arg_checked(const Args&... args) -> const T& {
const auto& arg = detail::get<N>(args...);
if constexpr (detail::is_named_arg<remove_cvref_t<decltype(arg)>>()) {
return arg.value;
@@ -146,13 +166,13 @@ template <typename Char>
struct is_compiled_format<code_unit<Char>> : std::true_type {};
// A replacement field that refers to argument N.
template <typename Char, typename T, int N> struct field {
template <typename Char, typename V, int N> struct field {
using char_type = Char;
template <typename OutputIt, typename... Args>
constexpr OutputIt format(OutputIt out, const Args&... args) const {
const T& arg = get_arg_checked<T, N>(args...);
if constexpr (std::is_convertible<T, basic_string_view<Char>>::value) {
template <typename OutputIt, typename... T>
constexpr auto format(OutputIt out, const T&... args) const -> OutputIt {
const V& arg = get_arg_checked<V, N>(args...);
if constexpr (std::is_convertible<V, basic_string_view<Char>>::value) {
auto s = basic_string_view<Char>(arg);
return copy<Char>(s.begin(), s.end(), out);
} else {
@@ -170,10 +190,10 @@ template <typename Char> struct runtime_named_field {
basic_string_view<Char> name;
template <typename OutputIt, typename T>
constexpr static bool try_format_argument(
constexpr static auto try_format_argument(
OutputIt& out,
// [[maybe_unused]] due to unused-but-set-parameter warning in GCC 7,8,9
[[maybe_unused]] basic_string_view<Char> arg_name, const T& arg) {
[[maybe_unused]] basic_string_view<Char> arg_name, const T& arg) -> bool {
if constexpr (is_named_arg<typename std::remove_cv<T>::type>::value) {
if (arg_name == arg.name) {
out = write<Char>(out, arg.value);
@@ -183,8 +203,8 @@ template <typename Char> struct runtime_named_field {
return false;
}
template <typename OutputIt, typename... Args>
constexpr OutputIt format(OutputIt out, const Args&... args) const {
template <typename OutputIt, typename... T>
constexpr auto format(OutputIt out, const T&... args) const -> OutputIt {
bool found = (try_format_argument(out, name, args) || ...);
if (!found) {
FMT_THROW(format_error("argument with specified name is not found"));
@@ -197,17 +217,17 @@ template <typename Char>
struct is_compiled_format<runtime_named_field<Char>> : std::true_type {};
// A replacement field that refers to argument N and has format specifiers.
template <typename Char, typename T, int N> struct spec_field {
template <typename Char, typename V, int N> struct spec_field {
using char_type = Char;
formatter<T, Char> fmt;
formatter<V, Char> fmt;
template <typename OutputIt, typename... Args>
constexpr FMT_INLINE OutputIt format(OutputIt out,
const Args&... args) const {
template <typename OutputIt, typename... T>
constexpr FMT_INLINE auto format(OutputIt out, const T&... args) const
-> OutputIt {
const auto& vargs =
fmt::make_format_args<basic_format_context<OutputIt, Char>>(args...);
basic_format_context<OutputIt, Char> ctx(out, vargs);
return fmt.format(get_arg_checked<T, N>(args...), ctx);
return fmt.format(get_arg_checked<V, N>(args...), ctx);
}
};
@@ -219,8 +239,8 @@ template <typename L, typename R> struct concat {
R rhs;
using char_type = typename L::char_type;
template <typename OutputIt, typename... Args>
constexpr OutputIt format(OutputIt out, const Args&... args) const {
template <typename OutputIt, typename... T>
constexpr auto format(OutputIt out, const T&... args) const -> OutputIt {
out = lhs.format(out, args...);
return rhs.format(out, args...);
}
@@ -230,14 +250,14 @@ template <typename L, typename R>
struct is_compiled_format<concat<L, R>> : std::true_type {};
template <typename L, typename R>
constexpr concat<L, R> make_concat(L lhs, R rhs) {
constexpr auto make_concat(L lhs, R rhs) -> concat<L, R> {
return {lhs, rhs};
}
struct unknown_format {};
template <typename Char>
constexpr size_t parse_text(basic_string_view<Char> str, size_t pos) {
constexpr auto parse_text(basic_string_view<Char> str, size_t pos) -> size_t {
for (size_t size = str.size(); pos != size; ++pos) {
if (str[pos] == '{' || str[pos] == '}') break;
}
@@ -270,8 +290,8 @@ template <typename T, typename Char> struct parse_specs_result {
enum { manual_indexing_id = -1 };
template <typename T, typename Char>
constexpr parse_specs_result<T, Char> parse_specs(basic_string_view<Char> str,
size_t pos, int next_arg_id) {
constexpr auto parse_specs(basic_string_view<Char> str, size_t pos,
int next_arg_id) -> parse_specs_result<T, Char> {
str.remove_prefix(pos);
auto ctx =
compile_parse_context<Char>(str, max_value<int>(), nullptr, next_arg_id);
@@ -285,16 +305,16 @@ template <typename Char> struct arg_id_handler {
arg_id_kind kind;
arg_ref<Char> arg_id;
constexpr int on_auto() {
constexpr auto on_auto() -> int {
FMT_ASSERT(false, "handler cannot be used with automatic indexing");
return 0;
}
constexpr int on_index(int id) {
constexpr auto on_index(int id) -> int {
kind = arg_id_kind::index;
arg_id = arg_ref<Char>(id);
return 0;
}
constexpr int on_name(basic_string_view<Char> id) {
constexpr auto on_name(basic_string_view<Char> id) -> int {
kind = arg_id_kind::name;
arg_id = arg_ref<Char>(id);
return 0;
@@ -433,27 +453,28 @@ FMT_BEGIN_EXPORT
#if defined(__cpp_if_constexpr) && defined(__cpp_return_type_deduction)
template <typename CompiledFormat, typename... Args,
template <typename CompiledFormat, typename... T,
typename Char = typename CompiledFormat::char_type,
FMT_ENABLE_IF(detail::is_compiled_format<CompiledFormat>::value)>
FMT_INLINE std::basic_string<Char> format(const CompiledFormat& cf,
const Args&... args) {
FMT_INLINE FMT_CONSTEXPR_STRING auto format(const CompiledFormat& cf,
const T&... args)
-> std::basic_string<Char> {
auto s = std::basic_string<Char>();
cf.format(std::back_inserter(s), args...);
return s;
}
template <typename OutputIt, typename CompiledFormat, typename... Args,
template <typename OutputIt, typename CompiledFormat, typename... T,
FMT_ENABLE_IF(detail::is_compiled_format<CompiledFormat>::value)>
constexpr FMT_INLINE OutputIt format_to(OutputIt out, const CompiledFormat& cf,
const Args&... args) {
constexpr FMT_INLINE auto format_to(OutputIt out, const CompiledFormat& cf,
const T&... args) -> OutputIt {
return cf.format(out, args...);
}
template <typename S, typename... Args,
template <typename S, typename... T,
FMT_ENABLE_IF(is_compiled_string<S>::value)>
FMT_INLINE std::basic_string<typename S::char_type> format(const S&,
Args&&... args) {
FMT_INLINE FMT_CONSTEXPR_STRING auto format(const S&, T&&... args)
-> std::basic_string<typename S::char_type> {
if constexpr (std::is_same<typename S::char_type, char>::value) {
constexpr auto str = basic_string_view<typename S::char_type>(S());
if constexpr (str.size() == 2 && str[0] == '{' && str[1] == '}') {
@@ -466,72 +487,97 @@ FMT_INLINE std::basic_string<typename S::char_type> format(const S&,
}
}
}
constexpr auto compiled = detail::compile<Args...>(S());
constexpr auto compiled = detail::compile<T...>(S());
if constexpr (std::is_same<remove_cvref_t<decltype(compiled)>,
detail::unknown_format>()) {
return fmt::format(
static_cast<basic_string_view<typename S::char_type>>(S()),
std::forward<Args>(args)...);
std::forward<T>(args)...);
} else {
return fmt::format(compiled, std::forward<Args>(args)...);
return fmt::format(compiled, std::forward<T>(args)...);
}
}
template <typename OutputIt, typename S, typename... Args,
template <typename OutputIt, typename S, typename... T,
FMT_ENABLE_IF(is_compiled_string<S>::value)>
FMT_CONSTEXPR OutputIt format_to(OutputIt out, const S&, Args&&... args) {
constexpr auto compiled = detail::compile<Args...>(S());
FMT_CONSTEXPR auto format_to(OutputIt out, const S&, T&&... args) -> OutputIt {
constexpr auto compiled = detail::compile<T...>(S());
if constexpr (std::is_same<remove_cvref_t<decltype(compiled)>,
detail::unknown_format>()) {
return fmt::format_to(
out, static_cast<basic_string_view<typename S::char_type>>(S()),
std::forward<Args>(args)...);
std::forward<T>(args)...);
} else {
return fmt::format_to(out, compiled, std::forward<Args>(args)...);
return fmt::format_to(out, compiled, std::forward<T>(args)...);
}
}
#endif
template <typename OutputIt, typename S, typename... Args,
template <typename OutputIt, typename S, typename... T,
FMT_ENABLE_IF(is_compiled_string<S>::value)>
auto format_to_n(OutputIt out, size_t n, const S& fmt, Args&&... args)
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<Args>(args)...);
fmt::format_to(std::back_inserter(buf), fmt, std::forward<T>(args)...);
return {buf.out(), buf.count()};
}
template <typename S, typename... Args,
template <typename S, typename... T,
FMT_ENABLE_IF(is_compiled_string<S>::value)>
FMT_CONSTEXPR20 auto formatted_size(const S& fmt, const Args&... args)
-> size_t {
FMT_CONSTEXPR20 auto formatted_size(const S& fmt, T&&... args) -> size_t {
auto buf = detail::counting_buffer<>();
fmt::format_to(appender(buf), fmt, args...);
fmt::format_to(appender(buf), fmt, std::forward<T>(args)...);
return buf.count();
}
template <typename S, typename... Args,
template <typename S, typename... T,
FMT_ENABLE_IF(is_compiled_string<S>::value)>
void print(std::FILE* f, const S& fmt, const Args&... args) {
void print(std::FILE* f, const S& fmt, T&&... args) {
auto buf = memory_buffer();
fmt::format_to(appender(buf), fmt, args...);
fmt::format_to(appender(buf), fmt, std::forward<T>(args)...);
detail::print(f, {buf.data(), buf.size()});
}
template <typename S, typename... Args,
template <typename S, typename... T,
FMT_ENABLE_IF(is_compiled_string<S>::value)>
void print(const S& fmt, const Args&... args) {
print(stdout, fmt, args...);
void print(const S& fmt, T&&... args) {
print(stdout, fmt, std::forward<T>(args)...);
}
#if FMT_USE_NONTYPE_TEMPLATE_ARGS
inline namespace literals {
template <detail::fixed_string Str> constexpr auto operator""_cf() {
return FMT_COMPILE(Str.data);
}
} // namespace literals
#endif
template <size_t N> class static_format_result {
private:
char data[N];
public:
template <typename S, typename... T,
FMT_ENABLE_IF(is_compiled_string<S>::value)>
explicit FMT_CONSTEXPR static_format_result(const S& fmt, T&&... args) {
*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; }
};
/**
* Formats arguments according to the format string `fmt_str` and produces
* a string of the exact required size at compile time. Both the format string
* and the arguments must be compile-time expressions.
*
* The resulting string can be accessed as a C string via `c_str()` or as
* a `fmt::string_view` via `str()`.
*
* **Example**:
*
* // Produces the static string "42" at compile time.
* static constexpr auto result = FMT_STATIC_FORMAT("{}", 42);
* const char* s = result.c_str();
*/
#define FMT_STATIC_FORMAT(fmt_str, ...) \
fmt::static_format_result< \
fmt::formatted_size(FMT_COMPILE(fmt_str), __VA_ARGS__) + 1>( \
FMT_COMPILE(fmt_str), __VA_ARGS__)
FMT_END_EXPORT
FMT_END_NAMESPACE

View File

@@ -22,7 +22,7 @@
#include "format.h"
#if FMT_USE_LOCALE
#if FMT_USE_LOCALE && !defined(FMT_MODULE)
# include <locale>
#endif
@@ -31,14 +31,49 @@
#endif
FMT_BEGIN_NAMESPACE
namespace detail {
#ifndef FMT_CUSTOM_ASSERT_FAIL
FMT_FUNC void assert_fail(const char* file, int line, const char* message) {
// Use unchecked std::fprintf to avoid triggering another assertion when
// writing to stderr fails.
fprintf(stderr, "%s:%d: assertion failed: %s", file, line, message);
abort();
}
#endif
#if FMT_USE_LOCALE
namespace detail {
using std::locale;
using std::numpunct;
using std::use_facet;
} // namespace detail
template <typename Locale, enable_if_t<(sizeof(Locale::collate) != 0), int>>
locale_ref::locale_ref(const Locale& loc) : locale_(&loc) {
static_assert(std::is_same<Locale, std::locale>::value, "");
}
#else
namespace detail {
struct locale {};
template <typename Char> struct numpunct {
auto grouping() const -> std::string { return "\03"; }
auto thousands_sep() const -> Char { return ','; }
auto decimal_point() const -> Char { return '.'; }
};
template <typename Facet> Facet use_facet(locale) { return {}; }
} // namespace detail
#endif // FMT_USE_LOCALE
template <typename Locale> auto locale_ref::get() const -> Locale {
using namespace detail;
static_assert(std::is_same<Locale, locale>::value, "");
#if FMT_USE_LOCALE
if (locale_) return *static_cast<const locale*>(locale_);
#endif
return locale();
}
namespace detail {
FMT_FUNC void format_error_code(detail::buffer<char>& out, int error_code,
string_view message) noexcept {
@@ -79,33 +114,6 @@ inline void fwrite_all(const void* ptr, size_t count, FILE* stream) {
FMT_THROW(system_error(errno, FMT_STRING("cannot write to file")));
}
#if FMT_USE_LOCALE
using std::locale;
using std::numpunct;
using std::use_facet;
template <typename Locale>
locale_ref::locale_ref(const Locale& loc) : locale_(&loc) {
static_assert(std::is_same<Locale, locale>::value, "");
}
#else
struct locale {};
template <typename Char> struct numpunct {
auto grouping() const -> std::string { return "\03"; }
auto thousands_sep() const -> Char { return ','; }
auto decimal_point() const -> Char { return '.'; }
};
template <typename Facet> Facet use_facet(locale) { return {}; }
#endif // FMT_USE_LOCALE
template <typename Locale> auto locale_ref::get() const -> Locale {
static_assert(std::is_same<Locale, locale>::value, "");
#if FMT_USE_LOCALE
if (locale_) return *static_cast<const locale*>(locale_);
#endif
return locale();
}
template <typename Char>
FMT_FUNC auto thousands_sep_impl(locale_ref loc) -> thousands_sep_result<Char> {
auto&& facet = use_facet<numpunct<Char>>(loc.get<locale>());
@@ -133,14 +141,13 @@ FMT_FUNC auto write_loc(appender out, loc_value value,
} // namespace detail
FMT_FUNC void report_error(const char* message) {
#if FMT_USE_EXCEPTIONS
// Use FMT_THROW instead of throw to avoid bogus unreachable code warnings
// from MSVC.
FMT_THROW(format_error(message));
#else
fputs(message, stderr);
abort();
#if FMT_MSC_VERSION || defined(__NVCC__)
// Silence unreachable code warnings in MSVC and NVCC because these
// are nearly impossible to fix in a generic code.
volatile bool b = true;
if (!b) return;
#endif
FMT_THROW(format_error(message));
}
template <typename Locale> typename Locale::id format_facet<Locale>::id;
@@ -174,11 +181,11 @@ inline auto operator==(basic_fp<F> x, basic_fp<F> y) -> bool {
}
// Compilers should be able to optimize this into the ror instruction.
FMT_CONSTEXPR inline auto rotr(uint32_t n, uint32_t r) noexcept -> uint32_t {
FMT_INLINE auto rotr(uint32_t n, uint32_t r) noexcept -> uint32_t {
r &= 31;
return (n >> r) | (n << (32 - r));
}
FMT_CONSTEXPR inline auto rotr(uint64_t n, uint32_t r) noexcept -> uint64_t {
FMT_INLINE auto rotr(uint64_t n, uint32_t r) noexcept -> uint64_t {
r &= 63;
return (n >> r) | (n << (64 - r));
}
@@ -212,7 +219,7 @@ inline auto floor_log10_pow2_minus_log10_4_over_3(int e) noexcept -> int {
return (e * 631305 - 261663) >> 21;
}
FMT_INLINE_VARIABLE constexpr struct {
FMT_INLINE_VARIABLE constexpr struct div_small_pow10_infos_struct {
uint32_t divisor;
int shift_amount;
} div_small_pow10_infos[] = {{10, 16}, {100, 16}};
@@ -275,7 +282,7 @@ template <> struct cache_accessor<float> {
static auto get_cached_power(int k) noexcept -> uint64_t {
FMT_ASSERT(k >= float_info<float>::min_k && k <= float_info<float>::max_k,
"k is out of range");
static constexpr const uint64_t pow10_significands[] = {
static constexpr uint64_t pow10_significands[] = {
0x81ceb32c4b43fcf5, 0xa2425ff75e14fc32, 0xcad2f7f5359a3b3f,
0xfd87b5f28300ca0e, 0x9e74d1b791e07e49, 0xc612062576589ddb,
0xf79687aed3eec552, 0x9abe14cd44753b53, 0xc16d9a0095928a28,
@@ -370,7 +377,7 @@ template <> struct cache_accessor<double> {
FMT_ASSERT(k >= float_info<double>::min_k && k <= float_info<double>::max_k,
"k is out of range");
static constexpr const uint128_fallback pow10_significands[] = {
static constexpr uint128_fallback pow10_significands[] = {
#if FMT_USE_FULL_CACHE_DRAGONBOX
{0xff77b1fcbebcdc4f, 0x25e8e89c13bb0f7b},
{0x9faacf3df73609b1, 0x77b191618c54e9ad},
@@ -1037,7 +1044,7 @@ template <> struct cache_accessor<double> {
#if FMT_USE_FULL_CACHE_DRAGONBOX
return pow10_significands[k - float_info<double>::min_k];
#else
static constexpr const uint64_t powers_of_5_64[] = {
static constexpr uint64_t powers_of_5_64[] = {
0x0000000000000001, 0x0000000000000005, 0x0000000000000019,
0x000000000000007d, 0x0000000000000271, 0x0000000000000c35,
0x0000000000003d09, 0x000000000001312d, 0x000000000005f5e1,
@@ -1097,7 +1104,7 @@ template <> struct cache_accessor<double> {
return {r.high(), r.low() == 0};
}
static auto compute_delta(cache_entry_type const& cache, int beta) noexcept
static auto compute_delta(const cache_entry_type& cache, int beta) noexcept
-> uint32_t {
return static_cast<uint32_t>(cache.high() >> (64 - 1 - beta));
}
@@ -1149,8 +1156,8 @@ auto is_left_endpoint_integer_shorter_interval(int exponent) noexcept -> bool {
exponent <= case_shorter_interval_left_endpoint_upper_threshold;
}
// Remove trailing zeros from n and return the number of zeros removed (float)
FMT_INLINE int remove_trailing_zeros(uint32_t& n, int s = 0) noexcept {
// Remove trailing zeros from n and return the number of zeros removed (float).
FMT_INLINE auto remove_trailing_zeros(uint32_t& n, int s = 0) noexcept -> int {
FMT_ASSERT(n != 0, "");
// Modular inverse of 5 (mod 2^32): (mod_inv_5 * 5) mod 2^32 = 1.
constexpr uint32_t mod_inv_5 = 0xcccccccd;
@@ -1170,22 +1177,19 @@ FMT_INLINE int remove_trailing_zeros(uint32_t& n, int s = 0) noexcept {
return s;
}
// Removes trailing zeros and returns the number of zeros removed (double)
FMT_INLINE int remove_trailing_zeros(uint64_t& n) noexcept {
// Removes trailing zeros and returns the number of zeros removed (double).
FMT_INLINE auto remove_trailing_zeros(uint64_t& n) noexcept -> int {
FMT_ASSERT(n != 0, "");
// This magic number is ceil(2^90 / 10^8).
constexpr uint64_t magic_number = 12379400392853802749ull;
auto nm = umul128(n, magic_number);
// Is n is divisible by 10^8?
if ((nm.high() & ((1ull << (90 - 64)) - 1)) == 0 && nm.low() < magic_number) {
constexpr uint32_t ten_pow_8 = 100000000u;
if ((n % ten_pow_8) == 0) {
// If yes, work with the quotient...
auto n32 = static_cast<uint32_t>(nm.high() >> (90 - 64));
auto n32 = static_cast<uint32_t>(n / ten_pow_8);
// ... and use the 32 bit variant of the function
int s = remove_trailing_zeros(n32, 8);
int num_zeros = remove_trailing_zeros(n32, 8);
n = n32;
return s;
return num_zeros;
}
// If n is not divisible by 10^8, work with n itself.
@@ -1210,7 +1214,7 @@ FMT_INLINE int remove_trailing_zeros(uint64_t& n) noexcept {
// The main algorithm for shorter interval case
template <typename T>
FMT_INLINE decimal_fp<T> shorter_interval_case(int exponent) noexcept {
FMT_INLINE auto shorter_interval_case(int exponent) noexcept -> decimal_fp<T> {
decimal_fp<T> ret_value;
// Compute k and beta
const int minus_k = floor_log10_pow2_minus_log10_4_over_3(exponent);
@@ -1454,8 +1458,8 @@ FMT_FUNC void vformat_to(buffer<char>& buf, string_view fmt, format_args args,
auto out = appender(buf);
if (fmt.size() == 2 && equal2(fmt.data(), "{}"))
return args.get(0).visit(default_arg_formatter<char>{out});
parse_format_string(
fmt, format_handler<char>{parse_context<char>(fmt), {out, args, loc}});
parse_format_string(fmt,
format_handler<>{parse_context<>(fmt), {out, args, loc}});
}
template <typename T> struct span {
@@ -1526,9 +1530,8 @@ template <typename F> class glibc_file : public file_base<F> {
}
void init_buffer() {
if (this->file_->_IO_write_ptr) return;
if (this->file_->_IO_write_ptr < this->file_->_IO_write_end) return;
// Force buffer initialization by placing and removing a char in a buffer.
assume(this->file_->_IO_write_ptr >= this->file_->_IO_write_end);
putc_unlocked(0, this->file_);
--this->file_->_IO_write_ptr;
}
@@ -1547,10 +1550,11 @@ template <typename F> class glibc_file : public file_base<F> {
void advance_write_buffer(size_t size) { this->file_->_IO_write_ptr += size; }
bool needs_flush() const {
auto needs_flush() const -> bool {
if ((this->file_->_flags & line_buffered) == 0) return false;
char* end = this->file_->_IO_write_end;
return memchr(end, '\n', to_unsigned(this->file_->_IO_write_ptr - end));
auto size = max_of<ptrdiff_t>(this->file_->_IO_write_ptr - end, 0);
return memchr(end, '\n', static_cast<size_t>(size));
}
void flush() { fflush_unlocked(this->file_); }
@@ -1574,7 +1578,7 @@ template <typename F> class apple_file : public file_base<F> {
void init_buffer() {
if (this->file_->_p) return;
// Force buffer initialization by placing and removing a char in a buffer.
putc_unlocked(0, this->file_);
if (!FMT_CLANG_ANALYZER) putc_unlocked(0, this->file_);
--this->file_->_p;
++this->file_->_w;
}
@@ -1595,7 +1599,7 @@ template <typename F> class apple_file : public file_base<F> {
this->file_->_w -= size;
}
bool needs_flush() const {
auto needs_flush() const -> bool {
if ((this->file_->_flags & line_buffered) == 0) return false;
return memchr(this->file_->_p + this->file_->_w, '\n',
to_unsigned(-this->file_->_w));

View File

File diff suppressed because it is too large Load Diff

View File

@@ -29,7 +29,8 @@
# if (FMT_HAS_INCLUDE(<fcntl.h>) || defined(__APPLE__) || \
defined(__linux__)) && \
(!defined(WINAPI_FAMILY) || \
(WINAPI_FAMILY == WINAPI_FAMILY_DESKTOP_APP))
(WINAPI_FAMILY == WINAPI_FAMILY_DESKTOP_APP)) && \
!defined(__wasm__)
# include <fcntl.h> // for O_RDONLY
# define FMT_USE_FCNTL 1
# else

View File

@@ -33,8 +33,8 @@
FMT_BEGIN_NAMESPACE
namespace detail {
// Generate a unique explicit instantion in every translation unit using a tag
// type in an anonymous namespace.
// Generate a unique explicit instantiation in every translation unit using a
// tag type in an anonymous namespace.
namespace {
struct file_access_tag {};
} // namespace
@@ -158,7 +158,8 @@ void print(std::ostream& os, format_string<T...> fmt, T&&... args) {
FMT_EXPORT template <typename... T>
void println(std::ostream& os, format_string<T...> fmt, T&&... args) {
fmt::print(os, "{}\n", fmt::format(fmt, std::forward<T>(args)...));
fmt::print(os, FMT_STRING("{}\n"),
fmt::format(fmt, std::forward<T>(args)...));
}
FMT_END_NAMESPACE

View File

@@ -9,7 +9,7 @@
#define FMT_PRINTF_H_
#ifndef FMT_MODULE
# include <algorithm> // std::max
# include <algorithm> // std::find
# include <limits> // std::numeric_limits
#endif
@@ -18,10 +18,6 @@
FMT_BEGIN_NAMESPACE
FMT_BEGIN_EXPORT
template <typename T> struct printf_formatter {
printf_formatter() = delete;
};
template <typename Char> class basic_printf_context {
private:
basic_appender<Char> out_;
@@ -33,8 +29,6 @@ template <typename Char> class basic_printf_context {
public:
using char_type = Char;
using parse_context_type = parse_context<Char>;
template <typename T> using formatter_type = printf_formatter<T>;
enum { builtin_types = 1 };
/// Constructs a `printf_context` object. References to the arguments are
@@ -46,7 +40,7 @@ template <typename Char> class basic_printf_context {
auto out() -> basic_appender<Char> { return out_; }
void advance_to(basic_appender<Char>) {}
auto locale() -> detail::locale_ref { return {}; }
auto locale() -> locale_ref { return {}; }
auto arg(int id) const -> basic_format_arg<basic_printf_context> {
return args_.get(id);
@@ -74,10 +68,9 @@ inline auto find<false, char>(const char* first, const char* last, char value,
// Checks if a value fits in int - used to avoid warnings about comparing
// signed and unsigned integers.
template <bool IsSigned> struct int_checker {
template <bool IS_SIGNED> struct int_checker {
template <typename T> static auto fits_in_int(T value) -> bool {
unsigned max = to_unsigned(max_value<int>());
return value <= max;
return value <= to_unsigned(max_value<int>());
}
inline static auto fits_in_int(bool) -> bool { return true; }
};
@@ -95,7 +88,7 @@ struct printf_precision_handler {
auto operator()(T value) -> int {
if (!int_checker<std::numeric_limits<T>::is_signed>::fits_in_int(value))
report_error("number is too big");
return (std::max)(static_cast<int>(value), 0);
return max_of(static_cast<int>(value), 0);
}
template <typename T, FMT_ENABLE_IF(!std::is_integral<T>::value)>
@@ -410,7 +403,9 @@ void vprintf(buffer<Char>& buf, basic_string_view<Char> format,
arg_index = parse_ctx.next_arg_id();
else
parse_ctx.check_arg_id(--arg_index);
return detail::get_arg(context, arg_index);
auto arg = context.arg(arg_index);
if (!arg) report_error("argument not found");
return arg;
};
const Char* start = parse_ctx.begin();
@@ -571,15 +566,19 @@ inline auto vsprintf(basic_string_view<Char> fmt,
*
* std::string message = fmt::sprintf("The answer is %d", 42);
*/
template <typename S, typename... T, typename Char = detail::char_t<S>>
inline auto sprintf(const S& fmt, const T&... args) -> std::basic_string<Char> {
return vsprintf(detail::to_string_view(fmt),
fmt::make_format_args<basic_printf_context<Char>>(args...));
template <typename... T>
inline auto sprintf(string_view fmt, const T&... args) -> std::string {
return vsprintf(fmt, make_printf_args(args...));
}
template <typename... T>
FMT_DEPRECATED auto sprintf(basic_string_view<wchar_t> fmt, const T&... args)
-> std::wstring {
return vsprintf(fmt, make_printf_args<wchar_t>(args...));
}
template <typename Char>
inline auto vfprintf(std::FILE* f, basic_string_view<Char> fmt,
typename vprintf_args<Char>::type args) -> int {
auto vfprintf(std::FILE* f, basic_string_view<Char> fmt,
typename vprintf_args<Char>::type args) -> int {
auto buf = basic_memory_buffer<Char>();
detail::vprintf(buf, fmt, args);
size_t size = buf.size();
@@ -596,17 +595,14 @@ inline auto vfprintf(std::FILE* f, basic_string_view<Char> fmt,
*
* fmt::fprintf(stderr, "Don't %s!", "panic");
*/
template <typename S, typename... T, typename Char = detail::char_t<S>>
inline auto fprintf(std::FILE* f, const S& fmt, const T&... args) -> int {
return vfprintf(f, detail::to_string_view(fmt),
make_printf_args<Char>(args...));
template <typename... T>
inline auto fprintf(std::FILE* f, string_view fmt, const T&... args) -> int {
return vfprintf(f, fmt, make_printf_args(args...));
}
template <typename Char>
FMT_DEPRECATED inline auto vprintf(basic_string_view<Char> fmt,
typename vprintf_args<Char>::type args)
-> int {
return vfprintf(stdout, fmt, args);
template <typename... T>
FMT_DEPRECATED auto fprintf(std::FILE* f, basic_string_view<wchar_t> fmt,
const T&... args) -> int {
return vfprintf(f, fmt, make_printf_args<wchar_t>(args...));
}
/**
@@ -621,11 +617,6 @@ template <typename... T>
inline auto printf(string_view fmt, const T&... args) -> int {
return vfprintf(stdout, fmt, make_printf_args(args...));
}
template <typename... T>
FMT_DEPRECATED inline auto printf(basic_string_view<wchar_t> fmt,
const T&... args) -> int {
return vfprintf(stdout, fmt, make_printf_args<wchar_t>(args...));
}
FMT_END_EXPORT
FMT_END_NAMESPACE

View File

@@ -11,7 +11,6 @@
#ifndef FMT_MODULE
# include <initializer_list>
# include <iterator>
# include <string>
# include <tuple>
# include <type_traits>
# include <utility>
@@ -31,7 +30,7 @@ template <typename T> class is_map {
template <typename> static void check(...);
public:
static constexpr const bool value =
static constexpr bool value =
!std::is_void<decltype(check<T>(nullptr))>::value;
};
@@ -40,17 +39,16 @@ template <typename T> class is_set {
template <typename> static void check(...);
public:
static constexpr const bool value =
static constexpr bool value =
!std::is_void<decltype(check<T>(nullptr))>::value && !is_map<T>::value;
};
// C array overload
template <typename T, std::size_t N>
template <typename T, size_t N>
auto range_begin(const T (&arr)[N]) -> const T* {
return arr;
}
template <typename T, std::size_t N>
auto range_end(const T (&arr)[N]) -> const T* {
template <typename T, size_t N> auto range_end(const T (&arr)[N]) -> const T* {
return arr + N;
}
@@ -120,7 +118,7 @@ template <typename T> class is_tuple_like_ {
template <typename> static void check(...);
public:
static constexpr const bool value =
static constexpr bool value =
!std::is_void<decltype(check<T>(nullptr))>::value;
};
@@ -154,7 +152,7 @@ using tuple_index_sequence = make_index_sequence<std::tuple_size<T>::value>;
template <typename T, typename C, bool = is_tuple_like_<T>::value>
class is_tuple_formattable_ {
public:
static constexpr const bool value = false;
static constexpr bool value = false;
};
template <typename T, typename C> class is_tuple_formattable_<T, C, true> {
template <size_t... Is>
@@ -170,7 +168,7 @@ template <typename T, typename C> class is_tuple_formattable_<T, C, true> {
C>::value)...>{}));
public:
static constexpr const bool value =
static constexpr bool value =
decltype(check(tuple_index_sequence<T>{}))::value;
};
@@ -208,7 +206,7 @@ template <typename Char, typename... T>
using result_t = std::tuple<formatter<remove_cvref_t<T>, Char>...>;
using std::get;
template <typename Tuple, typename Char, std::size_t... Is>
template <typename Tuple, typename Char, size_t... Is>
auto get_formatters(index_sequence<Is...>)
-> result_t<Char, decltype(get<Is>(std::declval<Tuple>()))...>;
} // namespace tuple
@@ -219,7 +217,7 @@ template <typename R> struct range_reference_type_impl {
using type = decltype(*detail::range_begin(std::declval<R&>()));
};
template <typename T, std::size_t N> struct range_reference_type_impl<T[N]> {
template <typename T, size_t N> struct range_reference_type_impl<T[N]> {
using type = T&;
};
@@ -281,14 +279,15 @@ template <typename FormatContext> struct format_tuple_element {
} // namespace detail
FMT_EXPORT
template <typename T> struct is_tuple_like {
static constexpr const bool value =
static constexpr bool value =
detail::is_tuple_like_<T>::value && !detail::is_range_<T>::value;
};
FMT_EXPORT
template <typename T, typename C> struct is_tuple_formattable {
static constexpr const bool value =
detail::is_tuple_formattable_<T, C>::value;
static constexpr bool value = detail::is_tuple_formattable_<T, C>::value;
};
template <typename Tuple, typename Char>
@@ -343,8 +342,9 @@ struct formatter<Tuple, Char,
}
};
FMT_EXPORT
template <typename T, typename Char> struct is_range {
static constexpr const bool value =
static constexpr bool value =
detail::is_range_<T>::value && !detail::has_to_string_view<T>::value;
};
@@ -368,6 +368,7 @@ template <typename P1, typename... Pn>
struct conjunction<P1, Pn...>
: conditional_t<bool(P1::value), conjunction<Pn...>, P1> {};
FMT_EXPORT
template <typename T, typename Char, typename Enable = void>
struct range_formatter;
@@ -670,7 +671,8 @@ struct formatter<join_view<It, Sentinel, Char>, Char> {
}
};
template <typename Char, typename Tuple> struct tuple_join_view : detail::view {
FMT_EXPORT
template <typename Tuple, typename Char> struct tuple_join_view : detail::view {
const Tuple& tuple;
basic_string_view<Char> sep;
@@ -685,15 +687,15 @@ template <typename Char, typename Tuple> struct tuple_join_view : detail::view {
# define FMT_TUPLE_JOIN_SPECIFIERS 0
#endif
template <typename Char, typename Tuple>
struct formatter<tuple_join_view<Char, Tuple>, Char,
template <typename Tuple, typename Char>
struct formatter<tuple_join_view<Tuple, Char>, Char,
enable_if_t<is_tuple_like<Tuple>::value>> {
FMT_CONSTEXPR auto parse(parse_context<Char>& ctx) -> const Char* {
return do_parse(ctx, std::tuple_size<Tuple>());
}
template <typename FormatContext>
auto format(const tuple_join_view<Char, Tuple>& value,
auto format(const tuple_join_view<Tuple, Char>& value,
FormatContext& ctx) const -> typename FormatContext::iterator {
return do_format(value, ctx, std::tuple_size<Tuple>());
}
@@ -725,14 +727,14 @@ struct formatter<tuple_join_view<Char, Tuple>, Char,
}
template <typename FormatContext>
auto do_format(const tuple_join_view<Char, Tuple>&, FormatContext& ctx,
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<Char, Tuple>& value, FormatContext& ctx,
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;
@@ -754,7 +756,7 @@ template <typename T> class is_container_adaptor_like {
template <typename> static void check(...);
public:
static constexpr const bool value =
static constexpr bool value =
!std::is_void<decltype(check<T>(nullptr))>::value;
};
@@ -774,13 +776,13 @@ struct formatter<
: formatter<detail::all<typename T::container_type>, Char> {
using all = detail::all<typename T::container_type>;
template <typename FormatContext>
auto format(const T& t, FormatContext& ctx) const -> decltype(ctx.out()) {
auto format(const T& value, FormatContext& ctx) const -> decltype(ctx.out()) {
struct getter : T {
static auto get(const T& t) -> all {
return {t.*(&getter::c)}; // Access c through the derived class.
static auto get(const T& v) -> all {
return {v.*(&getter::c)}; // Access c through the derived class.
}
};
return formatter<all>::format(getter::get(t), ctx);
return formatter<all>::format(getter::get(value), ctx);
}
};
@@ -825,7 +827,7 @@ auto join(Range&& r, string_view sep)
*/
template <typename Tuple, FMT_ENABLE_IF(is_tuple_like<Tuple>::value)>
FMT_CONSTEXPR auto join(const Tuple& tuple, string_view sep)
-> tuple_join_view<char, Tuple> {
-> tuple_join_view<Tuple, char> {
return {tuple, sep};
}

View File

@@ -15,15 +15,13 @@
# include <atomic>
# include <bitset>
# include <complex>
# include <cstdlib>
# include <exception>
# include <functional>
# include <functional> // std::reference_wrapper
# include <memory>
# include <thread>
# include <type_traits>
# include <typeinfo>
# include <utility>
# include <vector>
# include <typeinfo> // std::type_info
# include <utility> // std::make_index_sequence
// Check FMT_CPLUSPLUS to suppress a bogus warning in MSVC.
# if FMT_CPLUSPLUS >= 201703L
@@ -62,27 +60,26 @@
# endif
#endif
// For older Xcode versions, __cpp_lib_xxx flags are inaccurately defined.
#ifndef FMT_CPP_LIB_FILESYSTEM
# ifdef __cpp_lib_filesystem
# define FMT_CPP_LIB_FILESYSTEM __cpp_lib_filesystem
# else
# define FMT_CPP_LIB_FILESYSTEM 0
# endif
#ifdef FMT_CPP_LIB_FILESYSTEM
// Use the provided definition.
#elif defined(__cpp_lib_filesystem)
# define FMT_CPP_LIB_FILESYSTEM __cpp_lib_filesystem
#else
# define FMT_CPP_LIB_FILESYSTEM 0
#endif
#ifndef FMT_CPP_LIB_VARIANT
# ifdef __cpp_lib_variant
# define FMT_CPP_LIB_VARIANT __cpp_lib_variant
# else
# define FMT_CPP_LIB_VARIANT 0
# endif
#ifdef FMT_CPP_LIB_VARIANT
// Use the provided definition.
#elif defined(__cpp_lib_variant)
# define FMT_CPP_LIB_VARIANT __cpp_lib_variant
#else
# define FMT_CPP_LIB_VARIANT 0
#endif
FMT_BEGIN_NAMESPACE
namespace detail {
#if FMT_CPP_LIB_FILESYSTEM
FMT_BEGIN_NAMESPACE
namespace detail {
template <typename Char, typename PathChar>
auto get_path_string(const std::filesystem::path& p,
@@ -111,9 +108,168 @@ void write_escaped_path(basic_memory_buffer<Char>& quoted,
}
}
#endif // FMT_CPP_LIB_FILESYSTEM
#if defined(__cpp_lib_expected) || FMT_CPP_LIB_VARIANT
template <typename Char, typename OutputIt, typename T>
auto write_escaped_alternative(OutputIt out, const T& v) -> 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);
return write<Char>(out, v);
}
#endif
#if FMT_CPP_LIB_VARIANT
template <typename> struct is_variant_like_ : std::false_type {};
template <typename... Types>
struct is_variant_like_<std::variant<Types...>> : std::true_type {};
template <typename Variant, typename Char> class is_variant_formattable {
template <size_t... Is>
static auto check(std::index_sequence<Is...>) -> std::conjunction<
is_formattable<std::variant_alternative_t<Is, Variant>, Char>...>;
public:
static constexpr bool value = decltype(check(
std::make_index_sequence<std::variant_size<Variant>::value>()))::value;
};
#endif // FMT_CPP_LIB_VARIANT
#if FMT_USE_RTTI
template <typename OutputIt>
auto write_demangled_name(OutputIt out, const std::type_info& ti) -> OutputIt {
# ifdef FMT_HAS_ABI_CXA_DEMANGLE
int status = 0;
size_t size = 0;
std::unique_ptr<char, void (*)(void*)> demangled_name_ptr(
abi::__cxa_demangle(ti.name(), nullptr, &size, &status), &std::free);
string_view demangled_name_view;
if (demangled_name_ptr) {
demangled_name_view = demangled_name_ptr.get();
// Normalization of stdlib inline namespace names.
// libc++ inline namespaces.
// std::__1::* -> std::*
// std::__1::__fs::* -> std::*
// libstdc++ inline namespaces.
// std::__cxx11::* -> std::*
// std::filesystem::__cxx11::* -> std::filesystem::*
if (demangled_name_view.starts_with("std::")) {
char* begin = demangled_name_ptr.get();
char* to = begin + 5; // std::
for (char *from = to, *end = begin + demangled_name_view.size();
from < end;) {
// This is safe, because demangled_name is NUL-terminated.
if (from[0] == '_' && from[1] == '_') {
char* next = from + 1;
while (next < end && *next != ':') next++;
if (next[0] == ':' && next[1] == ':') {
from = next + 2;
continue;
}
}
*to++ = *from++;
}
demangled_name_view = {begin, detail::to_unsigned(to - begin)};
}
} else {
demangled_name_view = string_view(ti.name());
}
return detail::write_bytes<char>(out, demangled_name_view);
# elif FMT_MSC_VERSION
const string_view demangled_name(ti.name());
for (size_t i = 0; i < demangled_name.size(); ++i) {
auto sub = demangled_name;
sub.remove_prefix(i);
if (sub.starts_with("enum ")) {
i += 4;
continue;
}
if (sub.starts_with("class ") || sub.starts_with("union ")) {
i += 5;
continue;
}
if (sub.starts_with("struct ")) {
i += 6;
continue;
}
if (*sub.begin() != ' ') *out++ = *sub.begin();
}
return out;
# else
return detail::write_bytes<char>(out, string_view(ti.name()));
# endif
}
#endif // FMT_USE_RTTI
template <typename T, typename Enable = void>
struct has_flip : std::false_type {};
template <typename T>
struct has_flip<T, void_t<decltype(std::declval<T>().flip())>>
: std::true_type {};
template <typename T> struct is_bit_reference_like {
static constexpr bool value = std::is_convertible<T, bool>::value &&
std::is_nothrow_assignable<T, bool>::value &&
has_flip<T>::value;
};
// Workaround for libc++ incompatibility with C++ standard.
// According to the Standard, `bitset::operator[] const` returns bool.
#if defined(_LIBCPP_VERSION) && !defined(FMT_IMPORT_STD)
template <typename C>
struct is_bit_reference_like<std::__bit_const_reference<C>> {
static constexpr bool value = true;
};
#endif
template <typename T, typename Enable = void>
struct has_format_as : std::false_type {};
template <typename T>
struct has_format_as<T, void_t<decltype(format_as(std::declval<const T&>()))>>
: std::true_type {};
template <typename T, typename Enable = void>
struct has_format_as_member : std::false_type {};
template <typename T>
struct has_format_as_member<
T, void_t<decltype(formatter<T>::format_as(std::declval<const T&>()))>>
: std::true_type {};
} // namespace detail
FMT_EXPORT
template <typename T, typename Deleter>
auto ptr(const std::unique_ptr<T, Deleter>& p) -> const void* {
return p.get();
}
template <typename T> auto ptr(const std::shared_ptr<T>& p) -> const void* {
return p.get();
}
#if FMT_CPP_LIB_FILESYSTEM
class path : public std::filesystem::path {
public:
auto display_string() const -> std::string {
const std::filesystem::path& base = *this;
return fmt::format(FMT_STRING("{}"), base);
}
auto system_string() const -> std::string { return string(); }
auto generic_display_string() const -> std::string {
const std::filesystem::path& base = *this;
return fmt::format(FMT_STRING("{:g}"), base);
}
auto generic_system_string() const -> std::string { return generic_string(); }
};
template <typename Char> struct formatter<std::filesystem::path, Char> {
private:
format_specs specs_;
@@ -163,40 +319,20 @@ template <typename Char> struct formatter<std::filesystem::path, Char> {
}
};
class path : public std::filesystem::path {
public:
auto display_string() const -> std::string {
const std::filesystem::path& base = *this;
return fmt::format(FMT_STRING("{}"), base);
}
auto system_string() const -> std::string { return string(); }
auto generic_display_string() const -> std::string {
const std::filesystem::path& base = *this;
return fmt::format(FMT_STRING("{:g}"), base);
}
auto generic_system_string() const -> std::string { return generic_string(); }
};
FMT_END_NAMESPACE
#endif // FMT_CPP_LIB_FILESYSTEM
FMT_BEGIN_NAMESPACE
FMT_EXPORT
template <std::size_t N, typename Char>
template <size_t N, typename Char>
struct formatter<std::bitset<N>, Char>
: nested_formatter<basic_string_view<Char>, Char> {
private:
// Functor because C++11 doesn't support generic lambdas.
// This is a functor because C++11 doesn't support generic lambdas.
struct writer {
const std::bitset<N>& bs;
template <typename OutputIt>
FMT_CONSTEXPR auto operator()(OutputIt out) -> OutputIt {
for (auto pos = N; pos > 0; --pos) {
for (auto pos = N; pos > 0; --pos)
out = detail::write<Char>(out, bs[pos - 1] ? Char('1') : Char('0'));
}
return out;
}
};
@@ -209,14 +345,10 @@ struct formatter<std::bitset<N>, Char>
}
};
FMT_EXPORT
template <typename Char>
struct formatter<std::thread::id, Char> : basic_ostream_formatter<Char> {};
FMT_END_NAMESPACE
#ifdef __cpp_lib_optional
FMT_BEGIN_NAMESPACE
FMT_EXPORT
template <typename T, typename Char>
struct formatter<std::optional<T>, Char,
std::enable_if_t<is_formattable<T, Char>::value>> {
@@ -255,31 +387,9 @@ struct formatter<std::optional<T>, Char,
return detail::write(out, ')');
}
};
FMT_END_NAMESPACE
#endif // __cpp_lib_optional
#if defined(__cpp_lib_expected) || FMT_CPP_LIB_VARIANT
FMT_BEGIN_NAMESPACE
namespace detail {
template <typename Char, typename OutputIt, typename T>
auto write_escaped_alternative(OutputIt out, const T& v) -> 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);
return write<Char>(out, v);
}
} // namespace detail
FMT_END_NAMESPACE
#endif
#ifdef __cpp_lib_expected
FMT_BEGIN_NAMESPACE
FMT_EXPORT
template <typename T, typename E, typename Char>
struct formatter<std::expected<T, E>, Char,
std::enable_if_t<(std::is_void<T>::value ||
@@ -306,12 +416,9 @@ struct formatter<std::expected<T, E>, Char,
return out;
}
};
FMT_END_NAMESPACE
#endif // __cpp_lib_expected
#ifdef __cpp_lib_source_location
FMT_BEGIN_NAMESPACE
FMT_EXPORT
template <> struct formatter<std::source_location> {
FMT_CONSTEXPR auto parse(parse_context<>& ctx) { return ctx.begin(); }
@@ -329,45 +436,14 @@ template <> struct formatter<std::source_location> {
return out;
}
};
FMT_END_NAMESPACE
#endif
#if FMT_CPP_LIB_VARIANT
FMT_BEGIN_NAMESPACE
namespace detail {
template <typename T>
using variant_index_sequence =
std::make_index_sequence<std::variant_size<T>::value>;
template <typename> struct is_variant_like_ : std::false_type {};
template <typename... Types>
struct is_variant_like_<std::variant<Types...>> : std::true_type {};
// formattable element check.
template <typename T, typename C> class is_variant_formattable_ {
template <std::size_t... Is>
static std::conjunction<
is_formattable<std::variant_alternative_t<Is, T>, C>...>
check(std::index_sequence<Is...>);
public:
static constexpr const bool value =
decltype(check(variant_index_sequence<T>{}))::value;
};
} // namespace detail
template <typename T> struct is_variant_like {
static constexpr const bool value = detail::is_variant_like_<T>::value;
static constexpr bool value = detail::is_variant_like_<T>::value;
};
template <typename T, typename C> struct is_variant_formattable {
static constexpr const bool value =
detail::is_variant_formattable_<T, C>::value;
};
FMT_EXPORT
template <typename Char> struct formatter<std::monostate, Char> {
FMT_CONSTEXPR auto parse(parse_context<Char>& ctx) -> const Char* {
return ctx.begin();
@@ -380,12 +456,11 @@ template <typename Char> struct formatter<std::monostate, Char> {
}
};
FMT_EXPORT
template <typename Variant, typename Char>
struct formatter<
Variant, Char,
std::enable_if_t<std::conjunction_v<
is_variant_like<Variant>, is_variant_formattable<Variant, Char>>>> {
struct formatter<Variant, Char,
std::enable_if_t<std::conjunction_v<
is_variant_like<Variant>,
detail::is_variant_formattable<Variant, Char>>>> {
FMT_CONSTEXPR auto parse(parse_context<Char>& ctx) -> const Char* {
return ctx.begin();
}
@@ -410,15 +485,14 @@ struct formatter<
return out;
}
};
FMT_END_NAMESPACE
#endif // FMT_CPP_LIB_VARIANT
FMT_BEGIN_NAMESPACE
FMT_EXPORT
template <> struct formatter<std::error_code> {
private:
format_specs specs_;
detail::arg_ref<char> width_ref_;
bool debug_ = false;
public:
FMT_CONSTEXPR auto parse(parse_context<>& ctx) -> const char* {
@@ -426,11 +500,19 @@ template <> struct formatter<std::error_code> {
if (it == end) return it;
it = detail::parse_align(it, end, specs_);
if (it == end) return it;
char c = *it;
if ((c >= '0' && c <= '9') || c == '{')
if (it != end && ((c >= '0' && c <= '9') || c == '{'))
it = detail::parse_width(it, end, specs_, width_ref_, ctx);
if (it != end && *it == '?') {
debug_ = true;
++it;
}
if (it != end && *it == 's') {
specs_.set_type(presentation_type::string);
++it;
}
return it;
}
@@ -440,113 +522,48 @@ template <> struct formatter<std::error_code> {
auto specs = specs_;
detail::handle_dynamic_spec(specs.dynamic_width(), specs.width, width_ref_,
ctx);
memory_buffer buf;
buf.append(string_view(ec.category().name()));
buf.push_back(':');
detail::write<char>(appender(buf), ec.value());
return detail::write<char>(ctx.out(), string_view(buf.data(), buf.size()),
specs);
auto buf = memory_buffer();
if (specs_.type() == presentation_type::string) {
buf.append(ec.message());
} else {
buf.append(string_view(ec.category().name()));
buf.push_back(':');
detail::write<char>(appender(buf), ec.value());
}
auto quoted = memory_buffer();
auto str = string_view(buf.data(), buf.size());
if (debug_) {
detail::write_escaped_string<char>(std::back_inserter(quoted), str);
str = string_view(quoted.data(), quoted.size());
}
return detail::write<char>(ctx.out(), str, specs);
}
};
#if FMT_USE_RTTI
namespace detail {
template <typename Char, typename OutputIt>
auto write_demangled_name(OutputIt out, const std::type_info& ti) -> OutputIt {
# ifdef FMT_HAS_ABI_CXA_DEMANGLE
int status = 0;
std::size_t size = 0;
std::unique_ptr<char, void (*)(void*)> demangled_name_ptr(
abi::__cxa_demangle(ti.name(), nullptr, &size, &status), &std::free);
string_view demangled_name_view;
if (demangled_name_ptr) {
demangled_name_view = demangled_name_ptr.get();
// Normalization of stdlib inline namespace names.
// libc++ inline namespaces.
// std::__1::* -> std::*
// std::__1::__fs::* -> std::*
// libstdc++ inline namespaces.
// std::__cxx11::* -> std::*
// std::filesystem::__cxx11::* -> std::filesystem::*
if (demangled_name_view.starts_with("std::")) {
char* begin = demangled_name_ptr.get();
char* to = begin + 5; // std::
for (char *from = to, *end = begin + demangled_name_view.size();
from < end;) {
// This is safe, because demangled_name is NUL-terminated.
if (from[0] == '_' && from[1] == '_') {
char* next = from + 1;
while (next < end && *next != ':') next++;
if (next[0] == ':' && next[1] == ':') {
from = next + 2;
continue;
}
}
*to++ = *from++;
}
demangled_name_view = {begin, detail::to_unsigned(to - begin)};
}
} else {
demangled_name_view = string_view(ti.name());
}
return detail::write_bytes<Char>(out, demangled_name_view);
# elif FMT_MSC_VERSION
const string_view demangled_name(ti.name());
for (std::size_t i = 0; i < demangled_name.size(); ++i) {
auto sub = demangled_name;
sub.remove_prefix(i);
if (sub.starts_with("enum ")) {
i += 4;
continue;
}
if (sub.starts_with("class ") || sub.starts_with("union ")) {
i += 5;
continue;
}
if (sub.starts_with("struct ")) {
i += 6;
continue;
}
if (*sub.begin() != ' ') *out++ = *sub.begin();
}
return out;
# else
return detail::write_bytes<Char>(out, string_view(ti.name()));
# endif
}
} // namespace detail
FMT_EXPORT
template <typename Char>
struct formatter<std::type_info, Char // DEPRECATED! Mixing code unit types.
> {
template <> struct formatter<std::type_info> {
public:
FMT_CONSTEXPR auto parse(parse_context<Char>& ctx) -> const Char* {
FMT_CONSTEXPR auto parse(parse_context<>& ctx) -> const char* {
return ctx.begin();
}
template <typename Context>
auto format(const std::type_info& ti, Context& ctx) const
-> decltype(ctx.out()) {
return detail::write_demangled_name<Char>(ctx.out(), ti);
return detail::write_demangled_name(ctx.out(), ti);
}
};
#endif
#endif // FMT_USE_RTTI
FMT_EXPORT
template <typename T, typename Char>
template <typename T>
struct formatter<
T, Char, // DEPRECATED! Mixing code unit types.
T, char,
typename std::enable_if<std::is_base_of<std::exception, T>::value>::type> {
private:
bool with_typename_ = false;
public:
FMT_CONSTEXPR auto parse(parse_context<Char>& ctx) -> const Char* {
FMT_CONSTEXPR auto parse(parse_context<>& ctx) -> const char* {
auto it = ctx.begin();
auto end = ctx.end();
if (it == end || *it == '}') return it;
@@ -563,47 +580,18 @@ struct formatter<
auto out = ctx.out();
#if FMT_USE_RTTI
if (with_typename_) {
out = detail::write_demangled_name<Char>(out, typeid(ex));
out = detail::write_demangled_name(out, typeid(ex));
*out++ = ':';
*out++ = ' ';
}
#endif
return detail::write_bytes<Char>(out, string_view(ex.what()));
return detail::write_bytes<char>(out, string_view(ex.what()));
}
};
namespace detail {
template <typename T, typename Enable = void>
struct has_flip : std::false_type {};
template <typename T>
struct has_flip<T, void_t<decltype(std::declval<T>().flip())>>
: std::true_type {};
template <typename T> struct is_bit_reference_like {
static constexpr const bool value =
std::is_convertible<T, bool>::value &&
std::is_nothrow_assignable<T, bool>::value && has_flip<T>::value;
};
#ifdef _LIBCPP_VERSION
// Workaround for libc++ incompatibility with C++ standard.
// According to the Standard, `bitset::operator[] const` returns bool.
template <typename C>
struct is_bit_reference_like<std::__bit_const_reference<C>> {
static constexpr const bool value = true;
};
#endif
} // namespace detail
// 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.
FMT_EXPORT
template <typename BitRef, typename Char>
struct formatter<BitRef, Char,
enable_if_t<detail::is_bit_reference_like<BitRef>::value>>
@@ -615,15 +603,6 @@ struct formatter<BitRef, Char,
}
};
template <typename T, typename Deleter>
auto ptr(const std::unique_ptr<T, Deleter>& p) -> const void* {
return p.get();
}
template <typename T> auto ptr(const std::shared_ptr<T>& p) -> const void* {
return p.get();
}
FMT_EXPORT
template <typename T, typename Char>
struct formatter<std::atomic<T>, Char,
enable_if_t<is_formattable<T, Char>::value>>
@@ -636,7 +615,6 @@ struct formatter<std::atomic<T>, Char,
};
#ifdef __cpp_lib_atomic_flag_test
FMT_EXPORT
template <typename Char>
struct formatter<std::atomic_flag, Char> : formatter<bool, Char> {
template <typename FormatContext>
@@ -647,7 +625,6 @@ struct formatter<std::atomic_flag, Char> : formatter<bool, Char> {
};
#endif // __cpp_lib_atomic_flag_test
FMT_EXPORT
template <typename T, typename Char> struct formatter<std::complex<T>, Char> {
private:
detail::dynamic_format_specs<Char> specs_;
@@ -710,10 +687,13 @@ template <typename T, typename Char> struct formatter<std::complex<T>, Char> {
}
};
FMT_EXPORT
template <typename T, typename Char>
struct formatter<std::reference_wrapper<T>, Char,
enable_if_t<is_formattable<remove_cvref_t<T>, Char>::value>>
// Guard against format_as because reference_wrapper is
// implicitly convertible to T&.
enable_if_t<is_formattable<remove_cvref_t<T>, Char>::value &&
!detail::has_format_as<T>::value &&
!detail::has_format_as_member<T>::value>>
: formatter<remove_cvref_t<T>, Char> {
template <typename FormatContext>
auto format(std::reference_wrapper<T> ref, FormatContext& ctx) const
@@ -723,4 +703,5 @@ struct formatter<std::reference_wrapper<T>, Char,
};
FMT_END_NAMESPACE
#endif // FMT_STD_H_

View File

@@ -55,6 +55,16 @@ inline auto write_loc(basic_appender<wchar_t> out, loc_value value,
#endif
return false;
}
template <typename Char>
void vformat_to(buffer<Char>& buf, basic_string_view<Char> fmt,
basic_format_args<buffered_context<Char>> args,
locale_ref loc = {}) {
static_assert(!std::is_same<Char, char>::value, "");
auto out = basic_appender<Char>(buf);
parse_format_string(
fmt, format_handler<Char>{parse_context<Char>(fmt), {out, args, loc}});
}
} // namespace detail
FMT_BEGIN_EXPORT
@@ -112,14 +122,6 @@ inline auto runtime(wstring_view s) -> runtime_format_string<wchar_t> {
return {{s}};
}
template <> struct is_char<wchar_t> : std::true_type {};
template <> struct is_char<char16_t> : std::true_type {};
template <> struct is_char<char32_t> : std::true_type {};
#ifdef __cpp_char8_t
template <> struct is_char<char8_t> : bool_constant<detail::is_utf8_enabled> {};
#endif
template <typename... T>
constexpr auto make_wformat_args(T&... args)
-> decltype(fmt::make_format_args<wformat_context>(args...)) {
@@ -155,13 +157,13 @@ auto join(std::initializer_list<T> list, wstring_view 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<wchar_t, Tuple> {
-> tuple_join_view<Tuple, wchar_t> {
return {tuple, sep};
}
template <typename Char, FMT_ENABLE_IF(!std::is_same<Char, char>::value)>
auto vformat(basic_string_view<Char> fmt,
typename detail::vformat_args<Char>::type args)
basic_format_args<buffered_context<Char>> args)
-> std::basic_string<Char> {
auto buf = basic_memory_buffer<Char>();
detail::vformat_to(buf, fmt, args);
@@ -191,24 +193,20 @@ auto format(const S& fmt, T&&... args) -> std::basic_string<Char> {
fmt::make_format_args<buffered_context<Char>>(args...));
}
template <typename Locale, typename S,
typename Char = detail::format_string_char_t<S>,
FMT_ENABLE_IF(detail::is_locale<Locale>::value&&
detail::is_exotic_char<Char>::value)>
inline auto vformat(const Locale& loc, const S& fmt,
typename detail::vformat_args<Char>::type args)
template <typename S, typename Char = detail::format_string_char_t<S>,
FMT_ENABLE_IF(detail::is_exotic_char<Char>::value)>
inline auto vformat(locale_ref loc, const S& fmt,
basic_format_args<buffered_context<Char>> args)
-> std::basic_string<Char> {
auto buf = basic_memory_buffer<Char>();
detail::vformat_to(buf, detail::to_string_view(fmt), args,
detail::locale_ref(loc));
detail::vformat_to(buf, detail::to_string_view(fmt), args, loc);
return {buf.data(), buf.size()};
}
template <typename Locale, typename S, typename... T,
template <typename S, typename... T,
typename Char = detail::format_string_char_t<S>,
FMT_ENABLE_IF(detail::is_locale<Locale>::value&&
detail::is_exotic_char<Char>::value)>
inline auto format(const Locale& loc, const S& fmt, T&&... args)
FMT_ENABLE_IF(detail::is_exotic_char<Char>::value)>
inline auto format(locale_ref loc, const S& fmt, T&&... args)
-> std::basic_string<Char> {
return vformat(loc, detail::to_string_view(fmt),
fmt::make_format_args<buffered_context<Char>>(args...));
@@ -219,7 +217,7 @@ template <typename OutputIt, typename S,
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value&&
detail::is_exotic_char<Char>::value)>
auto vformat_to(OutputIt out, const S& fmt,
typename detail::vformat_args<Char>::type args) -> OutputIt {
basic_format_args<buffered_context<Char>> args) -> OutputIt {
auto&& buf = detail::get_buffer<Char>(out);
detail::vformat_to(buf, detail::to_string_view(fmt), args);
return detail::get_iterator(buf, out);
@@ -235,27 +233,24 @@ inline auto format_to(OutputIt out, const S& fmt, T&&... args) -> OutputIt {
fmt::make_format_args<buffered_context<Char>>(args...));
}
template <typename Locale, typename S, typename OutputIt, typename... Args,
template <typename S, typename OutputIt, typename... Args,
typename Char = detail::format_string_char_t<S>,
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value&&
detail::is_locale<Locale>::value&&
detail::is_exotic_char<Char>::value)>
inline auto vformat_to(OutputIt out, const Locale& loc, const S& fmt,
typename detail::vformat_args<Char>::type args)
detail::is_exotic_char<Char>::value)>
inline auto vformat_to(OutputIt out, locale_ref loc, const S& fmt,
basic_format_args<buffered_context<Char>> args)
-> OutputIt {
auto&& buf = detail::get_buffer<Char>(out);
vformat_to(buf, detail::to_string_view(fmt), args, detail::locale_ref(loc));
vformat_to(buf, detail::to_string_view(fmt), args, loc);
return detail::get_iterator(buf, out);
}
template <typename Locale, typename OutputIt, typename S, typename... T,
template <typename OutputIt, typename S, typename... T,
typename Char = detail::format_string_char_t<S>,
bool enable = detail::is_output_iterator<OutputIt, Char>::value &&
detail::is_locale<Locale>::value &&
detail::is_exotic_char<Char>::value>
inline auto format_to(OutputIt out, const Locale& loc, const S& fmt,
T&&... args) ->
typename std::enable_if<enable, OutputIt>::type {
inline auto format_to(OutputIt out, locale_ref loc, const S& fmt, T&&... args)
-> typename std::enable_if<enable, OutputIt>::type {
return vformat_to(out, loc, detail::to_string_view(fmt),
fmt::make_format_args<buffered_context<Char>>(args...));
}
@@ -264,7 +259,7 @@ template <typename OutputIt, typename Char, typename... Args,
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value&&
detail::is_exotic_char<Char>::value)>
inline auto vformat_to_n(OutputIt out, size_t n, basic_string_view<Char> fmt,
typename detail::vformat_args<Char>::type args)
basic_format_args<buffered_context<Char>> args)
-> format_to_n_result<OutputIt> {
using traits = detail::fixed_buffer_traits;
auto buf = detail::iterator_buffer<OutputIt, Char, traits>(out, n);
@@ -322,7 +317,7 @@ template <typename... T> void println(wformat_string<T...> fmt, T&&... args) {
return print(L"{}\n", fmt::format(fmt, std::forward<T>(args)...));
}
inline auto vformat(const text_style& ts, wstring_view fmt, wformat_args args)
inline auto vformat(text_style ts, wstring_view fmt, wformat_args args)
-> std::wstring {
auto buf = wmemory_buffer();
detail::vformat_to(buf, ts, fmt, args);
@@ -330,23 +325,11 @@ inline auto vformat(const text_style& ts, wstring_view fmt, wformat_args args)
}
template <typename... T>
inline auto format(const text_style& ts, wformat_string<T...> fmt, T&&... args)
inline auto format(text_style ts, wformat_string<T...> fmt, T&&... args)
-> std::wstring {
return fmt::vformat(ts, fmt, fmt::make_wformat_args(args...));
}
template <typename... T>
FMT_DEPRECATED void print(std::FILE* f, const text_style& ts,
wformat_string<T...> fmt, const T&... args) {
vprint(f, ts, fmt, fmt::make_wformat_args(args...));
}
template <typename... T>
FMT_DEPRECATED void print(const text_style& ts, wformat_string<T...> fmt,
const T&... args) {
return print(stdout, ts, fmt, args...);
}
inline void vprint(std::wostream& os, wstring_view fmt, wformat_args args) {
auto buffer = basic_memory_buffer<wchar_t>();
detail::vformat_to(buffer, fmt, args);