Also restore doctest/scripts/version.txt removed in previous commit.
| simdutf | ef7d39c | Apache 2 | NO | build system only |
| expected | v1.0 | Public Domain / CC0 | NO | |
| frozen | 1.0.1 | Apache 2 | NO | |
-| fmt | 11.0.0 | MIT | NO | |
+| fmt | 12.1.0 | MIT | NO | |
| doctest | 2.4.11 | MIT | NO | |
| function2 | 4.1.0 | Boost | NO | |
| ankerl/svector | 1.0.2 | MIT | NO | |
--- /dev/null
+2.4.11
\ No newline at end of file
-Copyright (c) 2012 - present, Victor Zverovich
+Copyright (c) 2012 - present, Victor Zverovich and {fmt} contributors
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
#include "format.h" // std_string_view
FMT_BEGIN_NAMESPACE
-
namespace detail {
template <typename T> struct is_reference_wrapper : std::false_type {};
* 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
-#if FMT_GCC_VERSION && FMT_GCC_VERSION < 409
- // Workaround a GCC template argument substitution bug.
- : public basic_format_args<Context>
-#endif
-{
+FMT_EXPORT template <typename Context> class dynamic_format_arg_store {
private:
using char_type = typename Context::char_type;
template <typename T> struct need_copy {
static constexpr detail::type mapped_type =
- detail::mapped_type_constant<T, Context>::value;
+ detail::mapped_type_constant<T, char_type>::value;
enum {
value = !(detail::is_reference_wrapper<T>::value ||
};
template <typename T>
- using stored_type = conditional_t<
+ using stored_t = conditional_t<
std::is_convertible<T, std::basic_string<char_type>>::value &&
!detail::is_reference_wrapper<T>::value,
std::basic_string<char_type>, T>;
friend class basic_format_args<Context>;
- auto get_types() const -> unsigned long long {
- return detail::is_unpacked_bit | data_.size() |
- (named_info_.empty()
- ? 0ULL
- : static_cast<unsigned long long>(detail::has_named_args_bit));
- }
-
auto data() const -> const basic_format_arg<Context>* {
return named_info_.empty() ? data_.data() : data_.data() + 1;
}
template <typename T> void emplace_arg(const T& arg) {
- data_.emplace_back(detail::make_arg<Context>(arg));
+ data_.emplace_back(arg);
}
template <typename T>
void emplace_arg(const detail::named_arg<char_type, T>& arg) {
- if (named_info_.empty()) {
- constexpr const detail::named_arg_info<char_type>* zero_ptr{nullptr};
- data_.insert(data_.begin(), {zero_ptr, 0});
- }
- data_.emplace_back(detail::make_arg<Context>(detail::unwrap(arg.value)));
+ if (named_info_.empty())
+ data_.insert(data_.begin(), basic_format_arg<Context>(nullptr, 0));
+ data_.emplace_back(detail::unwrap(arg.value));
auto pop_one = [](std::vector<basic_format_arg<Context>>* data) {
data->pop_back();
};
std::unique_ptr<std::vector<basic_format_arg<Context>>, decltype(pop_one)>
guard{&data_, pop_one};
named_info_.push_back({arg.name, static_cast<int>(data_.size() - 2u)});
- data_[0].value_.named_args = {named_info_.data(), named_info_.size()};
+ data_[0] = {named_info_.data(), named_info_.size()};
guard.release();
}
public:
constexpr dynamic_format_arg_store() = default;
+ operator basic_format_args<Context>() const {
+ return basic_format_args<Context>(data(), static_cast<int>(data_.size()),
+ !named_info_.empty());
+ }
+
/**
* Adds an argument into the dynamic store for later passing to a formatting
* function.
*/
template <typename T> void push_back(const T& arg) {
if (detail::const_check(need_copy<T>::value))
- emplace_arg(dynamic_args_.push<stored_type<T>>(arg));
+ emplace_arg(dynamic_args_.push<stored_t<T>>(arg));
else
emplace_arg(detail::unwrap(arg));
}
dynamic_args_.push<std::basic_string<char_type>>(arg.name).c_str();
if (detail::const_check(need_copy<T>::value)) {
emplace_arg(
- fmt::arg(arg_name, dynamic_args_.push<stored_type<T>>(arg.value)));
+ fmt::arg(arg_name, dynamic_args_.push<stored_t<T>>(arg.value)));
} else {
emplace_arg(fmt::arg(arg_name, arg.value));
}
void clear() {
data_.clear();
named_info_.clear();
- dynamic_args_ = detail::dynamic_arg_list();
+ dynamic_args_ = {};
}
/// Reserves space to store at least `new_cap` arguments including
/// `new_cap_named` named arguments.
void reserve(size_t new_cap, size_t new_cap_named) {
FMT_ASSERT(new_cap >= new_cap_named,
- "Set of arguments includes set of named arguments");
+ "set of arguments includes set of named arguments");
data_.reserve(new_cap);
named_info_.reserve(new_cap_named);
}
+
+ /// Returns the number of elements in the store.
+ auto size() const noexcept -> size_t { return data_.size(); }
};
FMT_END_NAMESPACE
#ifndef FMT_MODULE
# include <limits.h> // CHAR_BIT
# include <stdio.h> // FILE
-# include <string.h> // strlen
+# include <string.h> // memcmp
-// <cstddef> is also included transitively from <type_traits>.
-# include <cstddef> // std::byte
# include <type_traits> // std::enable_if
#endif
// The fmt library version in the form major * 10000 + minor * 100 + patch.
-#define FMT_VERSION 110000
+#define FMT_VERSION 120100
// Detect compiler versions.
#if defined(__clang__) && !defined(__ibmxl__)
#else
# define FMT_HAS_INCLUDE(x) 0
#endif
+#ifdef __has_builtin
+# define FMT_HAS_BUILTIN(x) __has_builtin(x)
+#else
+# define FMT_HAS_BUILTIN(x) 0
+#endif
#ifdef __has_cpp_attribute
# define FMT_HAS_CPP_ATTRIBUTE(x) __has_cpp_attribute(x)
#else
// Detect C++14 relaxed constexpr.
#ifdef FMT_USE_CONSTEXPR
// Use the provided definition.
-#elif FMT_GCC_VERSION >= 600 && FMT_CPLUSPLUS >= 201402L
-// GCC only allows throw in constexpr since version 6:
-// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=67371.
+#elif FMT_GCC_VERSION >= 702 && FMT_CPLUSPLUS >= 201402L
+// GCC only allows constexpr member functions in non-literal types since 7.2:
+// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=66297.
# define FMT_USE_CONSTEXPR 1
#elif FMT_ICC_VERSION
# define FMT_USE_CONSTEXPR 0 // https://github.com/fmtlib/fmt/issues/1628
#endif
// Detect consteval, C++20 constexpr extensions and std::is_constant_evaluated.
-#if !defined(__cpp_lib_is_constant_evaluated)
+#ifdef FMT_USE_CONSTEVAL
+// Use the provided definition.
+#elif !defined(__cpp_lib_is_constant_evaluated)
# define FMT_USE_CONSTEVAL 0
#elif FMT_CPLUSPLUS < 201709L
# define FMT_USE_CONSTEVAL 0
# define FMT_CONSTEXPR20
#endif
-#if defined(FMT_USE_NONTYPE_TEMPLATE_ARGS)
-// Use the provided definition.
-#elif defined(__NVCOMPILER)
-# define FMT_USE_NONTYPE_TEMPLATE_ARGS 0
-#elif FMT_GCC_VERSION >= 903 && FMT_CPLUSPLUS >= 201709L
-# define FMT_USE_NONTYPE_TEMPLATE_ARGS 1
-#elif defined(__cpp_nontype_template_args) && \
- __cpp_nontype_template_args >= 201911L
-# define FMT_USE_NONTYPE_TEMPLATE_ARGS 1
-#elif FMT_CLANG_VERSION >= 1200 && FMT_CPLUSPLUS >= 202002L
-# define FMT_USE_NONTYPE_TEMPLATE_ARGS 1
-#else
-# define FMT_USE_NONTYPE_TEMPLATE_ARGS 0
-#endif
-
-#ifdef FMT_USE_CONCEPTS
-// Use the provided definition.
-#elif defined(__cpp_concepts)
-# define FMT_USE_CONCEPTS 1
-#else
-# define FMT_USE_CONCEPTS 0
-#endif
-
// Check if exceptions are disabled.
-#ifdef FMT_EXCEPTIONS
+#ifdef FMT_USE_EXCEPTIONS
// Use the provided definition.
#elif defined(__GNUC__) && !defined(__EXCEPTIONS)
-# define FMT_EXCEPTIONS 0
+# define FMT_USE_EXCEPTIONS 0
+#elif defined(__clang__) && !defined(__cpp_exceptions)
+# define FMT_USE_EXCEPTIONS 0
#elif FMT_MSC_VERSION && !_HAS_EXCEPTIONS
-# define FMT_EXCEPTIONS 0
+# define FMT_USE_EXCEPTIONS 0
#else
-# define FMT_EXCEPTIONS 1
+# define FMT_USE_EXCEPTIONS 1
#endif
-#if FMT_EXCEPTIONS
+#if FMT_USE_EXCEPTIONS
# define FMT_TRY try
# define FMT_CATCH(x) catch (x)
#else
# define FMT_CATCH(x) if (false)
#endif
+#ifdef FMT_NO_UNIQUE_ADDRESS
+// Use the provided definition.
+#elif FMT_CPLUSPLUS < 202002L
+// Not supported.
+#elif FMT_HAS_CPP_ATTRIBUTE(no_unique_address)
+# define FMT_NO_UNIQUE_ADDRESS [[no_unique_address]]
+// VS2019 v16.10 and later except clang-cl (https://reviews.llvm.org/D110485).
+#elif FMT_MSC_VERSION >= 1929 && !FMT_CLANG_VERSION
+# define FMT_NO_UNIQUE_ADDRESS [[msvc::no_unique_address]]
+#endif
+#ifndef FMT_NO_UNIQUE_ADDRESS
+# define FMT_NO_UNIQUE_ADDRESS
+#endif
+
#if FMT_HAS_CPP17_ATTRIBUTE(fallthrough)
# define FMT_FALLTHROUGH [[fallthrough]]
#elif defined(__clang__)
# define FMT_NORETURN
#endif
-#ifndef FMT_NODISCARD
-# if FMT_HAS_CPP17_ATTRIBUTE(nodiscard)
-# define FMT_NODISCARD [[nodiscard]]
-# else
-# define FMT_NODISCARD
-# endif
-#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_INLINE
+#ifdef FMT_NODISCARD
// Use the provided definition.
-#elif FMT_GCC_VERSION || FMT_CLANG_VERSION
-# define FMT_ALWAYS_INLINE inline __attribute__((always_inline))
+#elif FMT_HAS_CPP17_ATTRIBUTE(nodiscard)
+# define FMT_NODISCARD [[nodiscard]]
#else
-# define FMT_ALWAYS_INLINE inline
-#endif
-// A version of FMT_INLINE to prevent code bloat in debug mode.
-#ifdef NDEBUG
-# define FMT_INLINE FMT_ALWAYS_INLINE
-#else
-# define FMT_INLINE inline
+# define FMT_NODISCARD
#endif
#if FMT_GCC_VERSION || FMT_CLANG_VERSION
# define FMT_VISIBILITY(value)
#endif
-#ifndef FMT_GCC_PRAGMA
+// Detect pragmas.
+#define FMT_PRAGMA_IMPL(x) _Pragma(#x)
+#if FMT_GCC_VERSION >= 504 && !defined(__NVCOMPILER)
// Workaround a _Pragma bug https://gcc.gnu.org/bugzilla/show_bug.cgi?id=59884
// and an nvhpc warning: https://github.com/fmtlib/fmt/pull/2582.
-# if FMT_GCC_VERSION >= 504 && !defined(__NVCOMPILER)
-# define FMT_GCC_PRAGMA(arg) _Pragma(arg)
-# else
-# define FMT_GCC_PRAGMA(arg)
-# endif
+# define FMT_PRAGMA_GCC(x) FMT_PRAGMA_IMPL(GCC x)
+#else
+# define FMT_PRAGMA_GCC(x)
#endif
-
-// GCC < 5 requires this-> in decltype.
-#if FMT_GCC_VERSION && FMT_GCC_VERSION < 500
-# define FMT_DECLTYPE_THIS this->
+#if FMT_CLANG_VERSION
+# define FMT_PRAGMA_CLANG(x) FMT_PRAGMA_IMPL(clang x)
#else
-# define FMT_DECLTYPE_THIS
+# define FMT_PRAGMA_CLANG(x)
#endif
-
#if FMT_MSC_VERSION
# define FMT_MSC_WARNING(...) __pragma(warning(__VA_ARGS__))
-# define FMT_UNCHECKED_ITERATOR(It) \
- using _Unchecked_type = It // Mark iterator as checked.
#else
# define FMT_MSC_WARNING(...)
-# define FMT_UNCHECKED_ITERATOR(It) using unchecked_type = It
+#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)
+FMT_PRAGMA_GCC(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 v10 {
+ inline namespace v12 {
# define FMT_END_NAMESPACE \
} \
}
# define FMT_END_EXPORT
#endif
-#if !defined(FMT_HEADER_ONLY) && defined(_WIN32)
+#ifdef _WIN32
+# define FMT_WIN32 1
+#else
+# define FMT_WIN32 0
+#endif
+
+#if !defined(FMT_HEADER_ONLY) && FMT_WIN32
# if defined(FMT_LIB_EXPORT)
# define FMT_API __declspec(dllexport)
# elif defined(FMT_SHARED)
# define FMT_API
#endif
-#ifndef FMT_UNICODE
-# define FMT_UNICODE 1
+#ifndef FMT_OPTIMIZE_SIZE
+# define FMT_OPTIMIZE_SIZE 0
#endif
-// Check if rtti is available.
-#ifndef FMT_USE_RTTI
-// __RTTI is for EDG compilers. _CPPRTTI is for MSVC.
-# if defined(__GXX_RTTI) || FMT_HAS_FEATURE(cxx_rtti) || defined(_CPPRTTI) || \
- defined(__INTEL_RTTI__) || defined(__RTTI)
-# define FMT_USE_RTTI 1
-# else
-# define FMT_USE_RTTI 0
-# endif
+// FMT_BUILTIN_TYPE=0 may result in smaller library size at the cost of higher
+// per-call binary size by passing built-in types through the extension API.
+#ifndef FMT_BUILTIN_TYPES
+# define FMT_BUILTIN_TYPES 1
#endif
-#define FMT_FWD(...) static_cast<decltype(__VA_ARGS__)&&>(__VA_ARGS__)
-
-// Enable minimal optimizations for more compact code in debug mode.
-FMT_GCC_PRAGMA("GCC push_options")
-#if !defined(__OPTIMIZE__) && !defined(__CUDACC__)
-FMT_GCC_PRAGMA("GCC optimize(\"Og\")")
-#endif
+#define FMT_APPLY_VARIADIC(expr) \
+ using unused = int[]; \
+ (void)unused { 0, (expr, 0)... }
FMT_BEGIN_NAMESPACE
using remove_const_t = typename std::remove_const<T>::type;
template <typename T>
using remove_cvref_t = typename std::remove_cv<remove_reference_t<T>>::type;
-template <typename T> struct type_identity {
- using type = T;
-};
-template <typename T> using type_identity_t = typename type_identity<T>::type;
template <typename T>
using make_unsigned_t = typename std::make_unsigned<T>::type;
template <typename T>
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.8 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;
};
# define FMT_ENABLE_IF(...) fmt::enable_if_t<(__VA_ARGS__), int> = 0
#endif
-// This is defined in base.h instead of format.h to avoid injecting in std.
-// It is a template to avoid undesirable implicit conversions to std::byte.
-#ifdef __cpp_lib_byte
-template <typename T, FMT_ENABLE_IF(std::is_same<T, std::byte>::value)>
-inline auto format_as(T b) -> unsigned char {
- return static_cast<unsigned char>(b);
+template <typename T> constexpr auto min_of(T a, T b) -> T {
+ return a < b ? a : b;
}
-#endif
+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
constexpr auto is_constant_evaluated(bool default_value = false) noexcept
-> bool {
-// Workaround for incompatibility between libstdc++ consteval-based
-// std::is_constant_evaluated() implementation and clang-14:
-// https://github.com/fmtlib/fmt/issues/3247.
+// Workaround for incompatibility between clang 14 and libstdc++ consteval-based
+// std::is_constant_evaluated: https://github.com/fmtlib/fmt/issues/3247.
#if FMT_CPLUSPLUS >= 202002L && FMT_GLIBCXX_RELEASE >= 12 && \
(FMT_CLANG_VERSION >= 1400 && FMT_CLANG_VERSION < 1500)
ignore_unused(default_value);
}
// Suppresses "conditional expression is constant" warnings.
-template <typename T> constexpr auto const_check(T value) -> T { return value; }
+template <typename T> FMT_ALWAYS_INLINE constexpr auto const_check(T val) -> T {
+ return val;
+}
FMT_NORETURN FMT_API void assert_fail(const char* file, int line,
const char* message);
# 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
-// Do nothing.
+// Use the provided definition.
#elif defined(__SIZEOF_INT128__) && !defined(__NVCC__) && \
!(FMT_CLANG_VERSION && FMT_MSC_VERSION)
# define FMT_USE_INT128 1
using int128_opt = __int128_t; // An optional native 128-bit integer.
using uint128_opt = __uint128_t;
-template <typename T> inline auto convert_for_visit(T value) -> T {
- return value;
-}
+inline auto map(int128_opt x) -> int128_opt { return x; }
+inline auto map(uint128_opt x) -> uint128_opt { return x; }
#else
# define FMT_USE_INT128 0
#endif
enum class int128_opt {};
enum class uint128_opt {};
// Reduce template instantiations.
-template <typename T> auto convert_for_visit(T) -> monostate { return {}; }
+inline auto map(int128_opt) -> monostate { return {}; }
+inline auto map(uint128_opt) -> monostate { return {}; }
+#endif
+
+#ifdef FMT_USE_BITINT
+// Use the provided definition.
+#elif FMT_CLANG_VERSION >= 1500 && !defined(__CUDACC__)
+# define FMT_USE_BITINT 1
+#else
+# define FMT_USE_BITINT 0
#endif
+#if FMT_USE_BITINT
+FMT_PRAGMA_CLANG(diagnostic ignored "-Wbit-int-extension")
+template <int N> using bitint = _BitInt(N);
+template <int N> using ubitint = unsigned _BitInt(N);
+#else
+template <int N> struct bitint {};
+template <int N> struct ubitint {};
+#endif // FMT_USE_BITINT
+
// Casts a nonnegative integer to unsigned.
template <typename Int>
FMT_CONSTEXPR auto to_unsigned(Int value) -> make_unsigned_t<Int> {
return static_cast<make_unsigned_t<Int>>(value);
}
+template <typename Char>
+using unsigned_char = conditional_t<sizeof(Char) == 1, unsigned char, unsigned>;
+
// A heuristic to detect std::string and std::[experimental::]string_view.
// It is mainly used to avoid dependency on <[experimental/]string_view>.
template <typename T, typename Enable = void>
template <typename T>
struct is_std_string_like<T, void_t<decltype(std::declval<T>().find_first_of(
typename T::value_type(), 0))>>
- : std::true_type {};
+ : std::is_convertible<decltype(std::declval<T>().data()),
+ const typename T::value_type*> {};
-// Returns true iff the literal encoding is UTF-8.
-constexpr auto is_utf8_enabled() -> bool {
- // Avoid an MSVC sign extension bug: https://github.com/fmtlib/fmt/pull/2297.
- using uchar = unsigned char;
- return sizeof("\u00A7") == 3 && uchar("\u00A7"[0]) == 0xC2 &&
- uchar("\u00A7"[1]) == 0xA7;
-}
-constexpr auto use_utf8() -> bool {
- return !FMT_MSC_VERSION || is_utf8_enabled();
-}
+// Check if the literal encoding is UTF-8.
+enum { is_utf8_enabled = "\u00A7"[1] == '\xA7' };
+enum { use_utf8 = !FMT_WIN32 || is_utf8_enabled };
+
+#ifndef FMT_UNICODE
+# define FMT_UNICODE 1
+#endif
-static_assert(!FMT_UNICODE || use_utf8(),
+static_assert(!FMT_UNICODE || use_utf8,
"Unicode support requires compiling with /utf-8");
-template <typename Char> FMT_CONSTEXPR auto length(const Char* s) -> size_t {
- size_t len = 0;
- while (*s++) ++len;
- return len;
+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;
if (*s1 > *s2) return 1;
return 0;
}
+namespace adl {
+using namespace std;
+
+template <typename Container>
+auto invoke_back_inserter()
+ -> decltype(back_inserter(std::declval<Container&>()));
+} // namespace adl
+
template <typename It, typename Enable = std::true_type>
struct is_back_insert_iterator : std::false_type {};
+
template <typename It>
struct is_back_insert_iterator<
- It,
- bool_constant<std::is_same<
- decltype(back_inserter(std::declval<typename It::container_type&>())),
- It>::value>> : std::true_type {};
+ It, bool_constant<std::is_same<
+ decltype(adl::invoke_back_inserter<typename It::container_type>()),
+ It>::value>> : std::true_type {};
// Extracts a reference to the container from *insert_iterator.
template <typename OutputIt>
-inline auto get_container(OutputIt it) -> typename OutputIt::container_type& {
+inline FMT_CONSTEXPR20 auto get_container(OutputIt it) ->
+ typename OutputIt::container_type& {
struct accessor : OutputIt {
- accessor(OutputIt base) : OutputIt(base) {}
+ FMT_CONSTEXPR20 accessor(OutputIt base) : OutputIt(base) {}
using OutputIt::container;
};
return *accessor(it).container;
}
} // namespace detail
-// Checks whether T is a container with contiguous storage.
-template <typename T> struct is_contiguous : std::false_type {};
+// Parsing-related public API and forward declarations.
+FMT_BEGIN_EXPORT
/**
* An implementation of `std::basic_string_view` for pre-C++17. It provides a
* compiled with a different `-std` option than the client code (which is not
* recommended).
*/
-FMT_EXPORT
template <typename Char> class basic_string_view {
private:
const Char* data_;
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(std::nullptr_t) = delete;
+ constexpr basic_string_view(nullptr_t) = delete;
- /// Constructs a string reference object from a C string.
- FMT_CONSTEXPR20
- basic_string_view(const Char* s)
- : data_(s),
- size_(detail::const_check(std::is_same<Char, char>::value &&
- !detail::is_constant_evaluated(false))
- ? strlen(reinterpret_cast<const char*>(s))
- : detail::length(s)) {}
-
- /// Constructs a string reference from a `std::basic_string` or a
+ /// 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 && !detail::is_constant_evaluated()) {
+ size_ = __builtin_strlen(detail::narrow(s)); // strlen is not constexpr.
+ return;
+ }
+#endif
+ size_t len = 0;
+ while (*s++) ++len;
+ size_ = len;
+ }
+
+ /// 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<
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 {
- size_t str_size = size_ < other.size_ ? size_ : other.size_;
- int result = detail::compare(data_, other.data_, str_size);
- if (result == 0)
- result = size_ == other.size_ ? 0 : (size_ < other.size_ ? -1 : 1);
- return result;
+ int result =
+ detail::compare(data_, other.data_, min_of(size_, other.size_));
+ if (result != 0) return result;
+ return size_ == other.size_ ? 0 : (size_ < other.size_ ? -1 : 1);
}
FMT_CONSTEXPR friend auto operator==(basic_string_view lhs,
}
};
-FMT_EXPORT
using string_view = basic_string_view<char>;
-/// Specifies if `T` is a character type. Can be specialized by users.
-FMT_EXPORT
-template <typename T> struct is_char : std::false_type {};
-template <> struct is_char<char> : std::true_type {};
-
-namespace detail {
-
-// 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)>
-auto to_string_view(const Char* s) -> basic_string_view<Char> {
- return s;
-}
-template <typename T, FMT_ENABLE_IF(is_std_string_like<T>::value)>
-auto to_string_view(const T& s) -> basic_string_view<typename T::value_type> {
- return s;
-}
-template <typename Char>
-constexpr auto to_string_view(basic_string_view<Char> s)
- -> basic_string_view<Char> {
- return s;
-}
-
-template <typename T, typename Enable = void>
-struct has_to_string_view : std::false_type {};
-// detail:: is intentional since to_string_view is not an extension point.
-template <typename T>
-struct has_to_string_view<
- T, void_t<decltype(detail::to_string_view(std::declval<T>()))>>
- : std::true_type {};
-
-template <typename Char, Char... C> struct string_literal {
- static constexpr Char value[sizeof...(C)] = {C...};
- constexpr operator basic_string_view<Char>() const {
- return {value, sizeof...(C)};
- }
-};
-#if FMT_CPLUSPLUS < 201703L
-template <typename Char, Char... C>
-constexpr Char string_literal<Char, C...>::value[sizeof...(C)];
-#endif
+template <typename T> class basic_appender;
+using appender = basic_appender<char>;
-enum class type {
- none_type,
- // Integer types should go first,
- int_type,
- uint_type,
- long_long_type,
- ulong_long_type,
- int128_type,
- uint128_type,
- bool_type,
- char_type,
- last_integer_type = char_type,
- // followed by floating-point types.
- float_type,
- double_type,
- long_double_type,
- last_numeric_type = long_double_type,
- cstring_type,
- string_type,
- pointer_type,
- custom_type
-};
+// Checks whether T is a container with contiguous storage.
+template <typename T> struct is_contiguous : std::false_type {};
-// Maps core type T to the corresponding type enum constant.
-template <typename T, typename Char>
-struct type_constant : std::integral_constant<type, type::custom_type> {};
+class context;
+template <typename OutputIt, typename Char> class generic_context;
+template <typename Char> class parse_context;
-#define FMT_TYPE_CONSTANT(Type, constant) \
- template <typename Char> \
- struct type_constant<Type, Char> \
- : std::integral_constant<type, type::constant> {}
+// Longer aliases for C++20 compatibility.
+template <typename Char> using basic_format_parse_context = parse_context<Char>;
+using format_parse_context = parse_context<char>;
+template <typename OutputIt, typename Char>
+using basic_format_context =
+ conditional_t<std::is_same<OutputIt, appender>::value, context,
+ generic_context<OutputIt, Char>>;
+using format_context = context;
-FMT_TYPE_CONSTANT(int, int_type);
-FMT_TYPE_CONSTANT(unsigned, uint_type);
-FMT_TYPE_CONSTANT(long long, long_long_type);
-FMT_TYPE_CONSTANT(unsigned long long, ulong_long_type);
-FMT_TYPE_CONSTANT(int128_opt, int128_type);
-FMT_TYPE_CONSTANT(uint128_opt, uint128_type);
-FMT_TYPE_CONSTANT(bool, bool_type);
-FMT_TYPE_CONSTANT(Char, char_type);
-FMT_TYPE_CONSTANT(float, float_type);
-FMT_TYPE_CONSTANT(double, double_type);
-FMT_TYPE_CONSTANT(long double, long_double_type);
-FMT_TYPE_CONSTANT(const Char*, cstring_type);
-FMT_TYPE_CONSTANT(basic_string_view<Char>, string_type);
-FMT_TYPE_CONSTANT(const void*, pointer_type);
+template <typename Char>
+using buffered_context =
+ conditional_t<std::is_same<Char, char>::value, context,
+ generic_context<basic_appender<Char>, Char>>;
-constexpr auto is_integral_type(type t) -> bool {
- return t > type::none_type && t <= type::last_integer_type;
-}
-constexpr auto is_arithmetic_type(type t) -> bool {
- return t > type::none_type && t <= type::last_numeric_type;
-}
+template <typename Context> class basic_format_arg;
+template <typename Context> class basic_format_args;
-constexpr auto set(type rhs) -> int { return 1 << static_cast<int>(rhs); }
-constexpr auto in(type t, int set) -> bool {
- return ((set >> static_cast<int>(t)) & 1) != 0;
-}
+// A separate type would result in shorter symbols but break ABI compatibility
+// between clang and gcc on ARM (#1919).
+using format_args = basic_format_args<context>;
-// Bitsets of types.
-enum {
- sint_set =
- set(type::int_type) | set(type::long_long_type) | set(type::int128_type),
- uint_set = set(type::uint_type) | set(type::ulong_long_type) |
- set(type::uint128_type),
- bool_set = set(type::bool_type),
- char_set = set(type::char_type),
- float_set = set(type::float_type) | set(type::double_type) |
- set(type::long_double_type),
- string_set = set(type::string_type),
- cstring_set = set(type::cstring_type),
- pointer_set = set(type::pointer_type)
+// A formatter for objects of type T.
+template <typename T, typename Char = char, typename Enable = void>
+struct formatter {
+ // A deleted default constructor indicates a disabled formatter.
+ formatter() = delete;
};
-} // namespace detail
/// Reports a format error at compile time or, via a `format_error` exception,
/// at runtime.
// This function is intentionally not constexpr to give a compile-time error.
FMT_NORETURN FMT_API void report_error(const char* message);
-FMT_DEPRECATED FMT_NORETURN inline void throw_format_error(
- const char* message) {
- report_error(message);
-}
+enum class presentation_type : unsigned char {
+ // Common specifiers:
+ none = 0,
+ debug = 1, // '?'
+ string = 2, // 's' (string, bool)
-/// String's character (code unit) type.
-template <typename S,
- typename V = decltype(detail::to_string_view(std::declval<S>()))>
-using char_t = typename V::value_type;
+ // Integral, bool and character specifiers:
+ dec = 3, // 'd'
+ hex, // 'x' or 'X'
+ oct, // 'o'
+ bin, // 'b' or 'B'
+ chr, // 'c'
-/**
- * Parsing context consisting of a format string range being parsed and an
- * argument counter for automatic indexing.
- * You can use the `format_parse_context` type alias for `char` instead.
- */
-FMT_EXPORT
-template <typename Char> class basic_format_parse_context {
+ // String and pointer specifiers:
+ pointer = 3, // 'p'
+
+ // Floating-point specifiers:
+ exp = 1, // 'e' or 'E' (1 since there is no FP debug presentation)
+ fixed, // 'f' or 'F'
+ general, // 'g' or 'G'
+ hexfloat // 'a' or 'A'
+};
+
+enum class align { none, left, right, center, numeric };
+enum class sign { none, minus, plus, space };
+enum class arg_id_kind { none, index, name };
+
+// Basic format specifiers for built-in and string types.
+class basic_specs {
private:
- basic_string_view<Char> format_str_;
- int next_arg_id_;
+ // Data is arranged as follows:
+ //
+ // 0 1 2 3
+ // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ // |type |align| w | p | s |u|#|L| f | unused |
+ // +-----+-----+---+---+---+-+-+-+-----+---------------------------+
+ //
+ // w - dynamic width info
+ // p - dynamic precision info
+ // s - sign
+ // u - uppercase (e.g. 'X' for 'x')
+ // # - alternate form ('#')
+ // L - localized
+ // f - fill size
+ //
+ // Bitfields are not used because of compiler bugs such as gcc bug 61414.
+ enum : unsigned {
+ type_mask = 0x00007,
+ align_mask = 0x00038,
+ width_mask = 0x000C0,
+ precision_mask = 0x00300,
+ sign_mask = 0x00C00,
+ uppercase_mask = 0x01000,
+ alternate_mask = 0x02000,
+ localized_mask = 0x04000,
+ fill_size_mask = 0x38000,
+
+ align_shift = 3,
+ width_shift = 6,
+ precision_shift = 8,
+ sign_shift = 10,
+ fill_size_shift = 15,
+
+ max_fill_size = 4
+ };
- FMT_CONSTEXPR void do_check_arg_id(int id);
+ unsigned data_ = 1 << fill_size_shift;
+ static_assert(sizeof(basic_specs::data_) * CHAR_BIT >= 18, "");
- public:
- using char_type = Char;
- using iterator = const Char*;
+ // Character (code unit) type is erased to prevent template bloat.
+ char fill_data_[max_fill_size] = {' '};
- explicit constexpr basic_format_parse_context(
- basic_string_view<Char> format_str, int next_arg_id = 0)
- : format_str_(format_str), next_arg_id_(next_arg_id) {}
+ FMT_CONSTEXPR void set_fill_size(size_t size) {
+ data_ = (data_ & ~fill_size_mask) |
+ (static_cast<unsigned>(size) << fill_size_shift);
+ }
- /// Returns an iterator to the beginning of the format string range being
- /// parsed.
- constexpr auto begin() const noexcept -> iterator {
- return format_str_.begin();
+ public:
+ constexpr auto type() const -> presentation_type {
+ return static_cast<presentation_type>(data_ & type_mask);
+ }
+ FMT_CONSTEXPR void set_type(presentation_type t) {
+ data_ = (data_ & ~type_mask) | static_cast<unsigned>(t);
}
- /// Returns an iterator past the end of the format string range being parsed.
- constexpr auto end() const noexcept -> iterator { return format_str_.end(); }
+ constexpr auto align() const -> align {
+ return static_cast<fmt::align>((data_ & align_mask) >> align_shift);
+ }
+ FMT_CONSTEXPR void set_align(fmt::align a) {
+ data_ = (data_ & ~align_mask) | (static_cast<unsigned>(a) << align_shift);
+ }
- /// Advances the begin iterator to `it`.
- FMT_CONSTEXPR void advance_to(iterator it) {
- format_str_.remove_prefix(detail::to_unsigned(it - begin()));
+ constexpr auto dynamic_width() const -> arg_id_kind {
+ return static_cast<arg_id_kind>((data_ & width_mask) >> width_shift);
+ }
+ FMT_CONSTEXPR void set_dynamic_width(arg_id_kind w) {
+ data_ = (data_ & ~width_mask) | (static_cast<unsigned>(w) << width_shift);
}
- /// Reports an error if using the manual argument indexing; otherwise returns
- /// the next argument index and switches to the automatic indexing.
+ FMT_CONSTEXPR auto dynamic_precision() const -> arg_id_kind {
+ return static_cast<arg_id_kind>((data_ & precision_mask) >>
+ precision_shift);
+ }
+ FMT_CONSTEXPR void set_dynamic_precision(arg_id_kind p) {
+ data_ = (data_ & ~precision_mask) |
+ (static_cast<unsigned>(p) << precision_shift);
+ }
+
+ constexpr auto dynamic() const -> bool {
+ return (data_ & (width_mask | precision_mask)) != 0;
+ }
+
+ constexpr auto sign() const -> sign {
+ return static_cast<fmt::sign>((data_ & sign_mask) >> sign_shift);
+ }
+ FMT_CONSTEXPR void set_sign(fmt::sign s) {
+ data_ = (data_ & ~sign_mask) | (static_cast<unsigned>(s) << sign_shift);
+ }
+
+ constexpr auto upper() const -> bool { return (data_ & uppercase_mask) != 0; }
+ FMT_CONSTEXPR void set_upper() { data_ |= uppercase_mask; }
+
+ constexpr auto alt() const -> bool { return (data_ & alternate_mask) != 0; }
+ FMT_CONSTEXPR void set_alt() { data_ |= alternate_mask; }
+ FMT_CONSTEXPR void clear_alt() { data_ &= ~alternate_mask; }
+
+ constexpr auto localized() const -> bool {
+ return (data_ & localized_mask) != 0;
+ }
+ FMT_CONSTEXPR void set_localized() { data_ |= localized_mask; }
+
+ constexpr auto fill_size() const -> size_t {
+ return (data_ & fill_size_mask) >> fill_size_shift;
+ }
+
+ template <typename Char, FMT_ENABLE_IF(std::is_same<Char, char>::value)>
+ constexpr auto fill() const -> const Char* {
+ return fill_data_;
+ }
+ template <typename Char, FMT_ENABLE_IF(!std::is_same<Char, char>::value)>
+ constexpr auto fill() const -> const Char* {
+ return nullptr;
+ }
+
+ template <typename Char> constexpr auto fill_unit() const -> Char {
+ using uchar = unsigned char;
+ return static_cast<Char>(static_cast<uchar>(fill_data_[0]) |
+ (static_cast<uchar>(fill_data_[1]) << 8) |
+ (static_cast<uchar>(fill_data_[2]) << 16));
+ }
+
+ FMT_CONSTEXPR void set_fill(char c) {
+ fill_data_[0] = c;
+ set_fill_size(1);
+ }
+
+ template <typename Char>
+ FMT_CONSTEXPR void set_fill(basic_string_view<Char> s) {
+ auto size = s.size();
+ set_fill_size(size);
+ if (size == 1) {
+ unsigned uchar = static_cast<detail::unsigned_char<Char>>(s[0]);
+ fill_data_[0] = static_cast<char>(uchar);
+ fill_data_[1] = static_cast<char>(uchar >> 8);
+ fill_data_[2] = static_cast<char>(uchar >> 16);
+ return;
+ }
+ FMT_ASSERT(size <= max_fill_size, "invalid fill");
+ for (size_t i = 0; i < size; ++i)
+ fill_data_[i & 3] = static_cast<char>(s[i]);
+ }
+
+ FMT_CONSTEXPR void copy_fill_from(const basic_specs& specs) {
+ set_fill_size(specs.fill_size());
+ for (size_t i = 0; i < max_fill_size; ++i)
+ fill_data_[i] = specs.fill_data_[i];
+ }
+};
+
+// Format specifiers for built-in and string types.
+struct format_specs : basic_specs {
+ int width;
+ int precision;
+
+ constexpr format_specs() : width(0), precision(-1) {}
+};
+
+/**
+ * Parsing context consisting of a format string range being parsed and an
+ * argument counter for automatic indexing.
+ */
+template <typename Char = char> class parse_context {
+ private:
+ basic_string_view<Char> fmt_;
+ int next_arg_id_;
+
+ enum { use_constexpr_cast = !FMT_GCC_VERSION || FMT_GCC_VERSION >= 1200 };
+
+ FMT_CONSTEXPR void do_check_arg_id(int arg_id);
+
+ public:
+ using char_type = Char;
+ using iterator = const Char*;
+
+ constexpr explicit parse_context(basic_string_view<Char> fmt,
+ int next_arg_id = 0)
+ : fmt_(fmt), next_arg_id_(next_arg_id) {}
+
+ /// Returns an iterator to the beginning of the format string range being
+ /// parsed.
+ constexpr auto begin() const noexcept -> iterator { return fmt_.begin(); }
+
+ /// Returns an iterator past the end of the format string range being parsed.
+ constexpr auto end() const noexcept -> iterator { return fmt_.end(); }
+
+ /// Advances the begin iterator to `it`.
+ FMT_CONSTEXPR void advance_to(iterator it) {
+ fmt_.remove_prefix(detail::to_unsigned(it - begin()));
+ }
+
+ /// Reports an error if using the manual argument indexing; otherwise returns
+ /// the next argument index and switches to the automatic indexing.
FMT_CONSTEXPR auto next_arg_id() -> int {
if (next_arg_id_ < 0) {
report_error("cannot switch from manual to automatic argument indexing");
FMT_CONSTEXPR void check_dynamic_spec(int arg_id);
};
-FMT_EXPORT
-using format_parse_context = basic_format_parse_context<char>;
+#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) : locale_(&loc) {
+ // Check if std::isalpha is found via ADL to reduce the chance of misuse.
+ isalpha('x', 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_code_unit<Char>::value)>
+constexpr auto to_string_view(const Char* s) -> basic_string_view<Char> {
+ return s;
+}
+template <typename T, FMT_ENABLE_IF(is_std_string_like<T>::value)>
+constexpr auto to_string_view(const T& s)
+ -> basic_string_view<typename T::value_type> {
+ return s;
+}
+template <typename Char>
+constexpr auto to_string_view(basic_string_view<Char> s)
+ -> basic_string_view<Char> {
+ return s;
+}
+
+template <typename T, typename Enable = void>
+struct has_to_string_view : std::false_type {};
+// detail:: is intentional since to_string_view is not an extension point.
+template <typename T>
+struct has_to_string_view<
+ T, void_t<decltype(detail::to_string_view(std::declval<T>()))>>
+ : std::true_type {};
+
+/// String's character (code unit) type. detail:: is intentional to prevent ADL.
+template <typename S,
+ typename V = decltype(detail::to_string_view(std::declval<S>()))>
+using char_t = typename V::value_type;
+
+enum class type {
+ none_type,
+ // Integer types should go first,
+ int_type,
+ uint_type,
+ long_long_type,
+ ulong_long_type,
+ int128_type,
+ uint128_type,
+ bool_type,
+ char_type,
+ last_integer_type = char_type,
+ // followed by floating-point types.
+ float_type,
+ double_type,
+ long_double_type,
+ last_numeric_type = long_double_type,
+ cstring_type,
+ string_type,
+ pointer_type,
+ custom_type
+};
+
+// Maps core type T to the corresponding type enum constant.
+template <typename T, typename Char>
+struct type_constant : std::integral_constant<type, type::custom_type> {};
+
+#define FMT_TYPE_CONSTANT(Type, constant) \
+ template <typename Char> \
+ struct type_constant<Type, Char> \
+ : std::integral_constant<type, type::constant> {}
+
+FMT_TYPE_CONSTANT(int, int_type);
+FMT_TYPE_CONSTANT(unsigned, uint_type);
+FMT_TYPE_CONSTANT(long long, long_long_type);
+FMT_TYPE_CONSTANT(unsigned long long, ulong_long_type);
+FMT_TYPE_CONSTANT(int128_opt, int128_type);
+FMT_TYPE_CONSTANT(uint128_opt, uint128_type);
+FMT_TYPE_CONSTANT(bool, bool_type);
+FMT_TYPE_CONSTANT(Char, char_type);
+FMT_TYPE_CONSTANT(float, float_type);
+FMT_TYPE_CONSTANT(double, double_type);
+FMT_TYPE_CONSTANT(long double, long_double_type);
+FMT_TYPE_CONSTANT(const Char*, cstring_type);
+FMT_TYPE_CONSTANT(basic_string_view<Char>, string_type);
+FMT_TYPE_CONSTANT(const void*, pointer_type);
+
+constexpr auto is_integral_type(type t) -> bool {
+ return t > type::none_type && t <= type::last_integer_type;
+}
+constexpr auto is_arithmetic_type(type t) -> bool {
+ return t > type::none_type && t <= type::last_numeric_type;
+}
+
+constexpr auto set(type rhs) -> int { return 1 << static_cast<int>(rhs); }
+constexpr auto in(type t, int set) -> bool {
+ return ((set >> static_cast<int>(t)) & 1) != 0;
+}
+
+// Bitsets of types.
+enum {
+ sint_set =
+ set(type::int_type) | set(type::long_long_type) | set(type::int128_type),
+ uint_set = set(type::uint_type) | set(type::ulong_long_type) |
+ set(type::uint128_type),
+ bool_set = set(type::bool_type),
+ char_set = set(type::char_type),
+ float_set = set(type::float_type) | set(type::double_type) |
+ set(type::long_double_type),
+ string_set = set(type::string_type),
+ cstring_set = set(type::cstring_type),
+ pointer_set = set(type::pointer_type)
+};
+
+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 {};
+
+template <typename Char, typename T>
+struct is_named_arg<named_arg<Char, T>> : std::true_type {};
+
+template <typename Char, typename T> struct named_arg : view {
+ const Char* name;
+ const T& value;
+
+ named_arg(const Char* n, const T& v) : name(n), value(v) {}
+ static_assert(!is_named_arg<T>::value, "nested named arguments");
+};
+
+template <bool B = false> constexpr auto count() -> int { return B ? 1 : 0; }
+template <bool B1, bool B2, bool... Tail> constexpr auto count() -> int {
+ return (B1 ? 1 : 0) + count<B2, Tail...>();
+}
+
+template <typename... T> constexpr auto count_named_args() -> int {
+ return count<is_named_arg<T>::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 {
+ const Char* name;
+ 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;
+}
+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++};
+}
+
+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>*, int& arg_index,
+ int&) {
+ ++arg_index;
+}
+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) && FMT_BUILTIN_TYPES };
+using long_type = conditional_t<long_short, int, long long>;
+using ulong_type = conditional_t<long_short, unsigned, unsigned long long>;
+
+template <typename T>
+using format_as_result =
+ remove_cvref_t<decltype(format_as(std::declval<const T&>()))>;
+template <typename T>
+using format_as_member_result =
+ remove_cvref_t<decltype(formatter<T>::format_as(std::declval<const T&>()))>;
+
+template <typename T, typename Enable = std::true_type>
+struct use_format_as : std::false_type {};
+// format_as member is only used to avoid injection into the std namespace.
+template <typename T, typename Enable = std::true_type>
+struct use_format_as_member : std::false_type {};
+
+// Only map owning types because mapping views can be unsafe.
+template <typename T>
+struct use_format_as<
+ T, bool_constant<std::is_arithmetic<format_as_result<T>>::value>>
+ : std::true_type {};
+template <typename T>
+struct use_format_as_member<
+ T, bool_constant<std::is_arithmetic<format_as_member_result<T>>::value>>
+ : std::true_type {};
+
+template <typename T, typename U = remove_const_t<T>>
+using use_formatter =
+ bool_constant<(std::is_class<T>::value || std::is_enum<T>::value ||
+ std::is_union<T>::value || std::is_array<T>::value) &&
+ !has_to_string_view<T>::value && !is_named_arg<T>::value &&
+ !use_format_as<T>::value && !use_format_as_member<U>::value>;
+
+template <typename Char, typename T, typename U = remove_const_t<T>>
+auto has_formatter_impl(T* p, buffered_context<Char>* ctx = nullptr)
+ -> decltype(formatter<U, Char>().format(*p, *ctx), std::true_type());
+template <typename Char> auto has_formatter_impl(...) -> std::false_type;
+
+// T can be const-qualified to check if it is const-formattable.
+template <typename T, typename Char> constexpr auto has_formatter() -> bool {
+ return decltype(has_formatter_impl<Char>(static_cast<T*>(nullptr)))::value;
+}
+
+// Maps formatting argument types to natively supported types or user-defined
+// types with formatters. Returns void on errors to be SFINAE-friendly.
+template <typename Char> struct type_mapper {
+ static auto map(signed char) -> int;
+ static auto map(unsigned char) -> unsigned;
+ static auto map(short) -> int;
+ static auto map(unsigned short) -> unsigned;
+ static auto map(int) -> int;
+ static auto map(unsigned) -> unsigned;
+ static auto map(long) -> long_type;
+ static auto map(unsigned long) -> ulong_type;
+ static auto map(long long) -> long long;
+ static auto map(unsigned long long) -> unsigned long long;
+ static auto map(int128_opt) -> int128_opt;
+ static auto map(uint128_opt) -> uint128_opt;
+ static auto map(bool) -> bool;
+
+ template <int N>
+ static auto map(bitint<N>) -> conditional_t<N <= 64, long long, void>;
+ template <int N>
+ static auto map(ubitint<N>)
+ -> conditional_t<N <= 64, unsigned long long, void>;
+
+ 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>;
+
+ static auto map(float) -> float;
+ static auto map(double) -> double;
+ static auto map(long double) -> long double;
+
+ static auto map(Char*) -> const Char*;
+ static auto map(const Char*) -> const Char*;
+ template <typename T, typename C = char_t<T>,
+ FMT_ENABLE_IF(!std::is_pointer<T>::value)>
+ static auto map(const T&) -> conditional_t<std::is_same<C, Char>::value,
+ basic_string_view<C>, void>;
+
+ static auto map(void*) -> const void*;
+ static auto map(const void*) -> const void*;
+ static auto map(volatile void*) -> const void*;
+ static auto map(const volatile void*) -> const void*;
+ static auto map(nullptr_t) -> const void*;
+ template <typename T, FMT_ENABLE_IF(std::is_pointer<T>::value ||
+ std::is_member_pointer<T>::value)>
+ static auto map(const T&) -> void;
+
+ template <typename T, FMT_ENABLE_IF(use_format_as<T>::value)>
+ static auto map(const T& x) -> decltype(map(format_as(x)));
+ template <typename T, FMT_ENABLE_IF(use_format_as_member<T>::value)>
+ static auto map(const T& x) -> decltype(map(formatter<T>::format_as(x)));
+
+ template <typename T, FMT_ENABLE_IF(use_formatter<T>::value)>
+ static auto map(T&) -> conditional_t<has_formatter<T, Char>(), T&, void>;
+
+ template <typename T, FMT_ENABLE_IF(is_named_arg<T>::value)>
+ static auto map(const T& named_arg) -> decltype(map(named_arg.value));
+};
+
+// detail:: is used to workaround a bug in MSVC 2017.
+template <typename T, typename Char>
+using mapped_t = decltype(detail::type_mapper<Char>::map(std::declval<T&>()));
+
+// A type constant after applying type_mapper.
+template <typename T, typename Char = char>
+using mapped_type_constant = type_constant<mapped_t<T, Char>, Char>;
+
+template <typename T, typename Context,
+ type TYPE =
+ mapped_type_constant<T, typename Context::char_type>::value>
+using stored_type_constant = std::integral_constant<
+ type, Context::builtin_types || TYPE == type::int_type ? TYPE
+ : type::custom_type>;
// A parse context with extra data used only in compile-time checks.
template <typename Char>
-class compile_parse_context : public basic_format_parse_context<Char> {
+class compile_parse_context : public parse_context<Char> {
private:
int num_args_;
const type* types_;
- using base = basic_format_parse_context<Char>;
+ using base = parse_context<Char>;
public:
- explicit FMT_CONSTEXPR compile_parse_context(
- basic_string_view<Char> format_str, int num_args, const type* types,
- int next_arg_id = 0)
- : base(format_str, next_arg_id), num_args_(num_args), types_(types) {}
+ FMT_CONSTEXPR explicit compile_parse_context(basic_string_view<Char> fmt,
+ int num_args, const type* types,
+ int next_arg_id = 0)
+ : base(fmt, next_arg_id), num_args_(num_args), types_(types) {}
constexpr auto num_args() const -> int { return num_args_; }
constexpr auto arg_type(int id) const -> type { return types_[id]; }
using base::check_arg_id;
FMT_CONSTEXPR void check_dynamic_spec(int arg_id) {
- detail::ignore_unused(arg_id);
+ ignore_unused(arg_id);
if (arg_id < num_args_ && types_ && !is_integral_type(types_[arg_id]))
report_error("width/precision is not integer");
}
};
-/// A contiguous memory buffer with an optional growing ability. It is an
-/// internal class and shouldn't be used directly, only via `memory_buffer`.
-template <typename T> class buffer {
- private:
- T* ptr_;
- size_t size_;
- size_t capacity_;
-
- using grow_fun = void (*)(buffer& buf, size_t capacity);
- grow_fun grow_;
-
- protected:
- // Don't initialize ptr_ since it is not accessed to save a few cycles.
- FMT_MSC_WARNING(suppress : 26495)
- FMT_CONSTEXPR20 buffer(grow_fun grow, size_t sz) noexcept
- : size_(sz), capacity_(sz), grow_(grow) {}
-
- constexpr buffer(grow_fun grow, T* p = nullptr, size_t sz = 0,
- size_t cap = 0) noexcept
- : ptr_(p), size_(sz), capacity_(cap), grow_(grow) {}
-
- FMT_CONSTEXPR20 ~buffer() = default;
- buffer(buffer&&) = default;
-
- /// Sets the buffer data and capacity.
- FMT_CONSTEXPR void set(T* buf_data, size_t buf_capacity) noexcept {
- ptr_ = buf_data;
- capacity_ = buf_capacity;
- }
-
- public:
- using value_type = T;
- using const_reference = const T&;
-
- buffer(const buffer&) = delete;
- void operator=(const buffer&) = delete;
-
- auto begin() noexcept -> T* { return ptr_; }
- auto end() noexcept -> T* { return ptr_ + size_; }
-
- auto begin() const noexcept -> const T* { return ptr_; }
- auto end() const noexcept -> const T* { return ptr_ + size_; }
-
- /// Returns the size of this buffer.
- constexpr auto size() const noexcept -> size_t { return size_; }
-
- /// Returns the capacity of this buffer.
- constexpr auto capacity() const noexcept -> size_t { return capacity_; }
-
- /// Returns a pointer to the buffer data (not null-terminated).
- FMT_CONSTEXPR auto data() noexcept -> T* { return ptr_; }
- FMT_CONSTEXPR auto data() const noexcept -> const T* { return ptr_; }
-
- /// Clears this buffer.
- void clear() { size_ = 0; }
-
- // Tries resizing the buffer to contain `count` elements. If T is a POD type
- // the new elements may not be initialized.
- FMT_CONSTEXPR void try_resize(size_t count) {
- try_reserve(count);
- size_ = count <= capacity_ ? count : capacity_;
- }
-
- // Tries increasing the buffer capacity to `new_capacity`. It can increase the
- // capacity by a smaller amount than requested but guarantees there is space
- // for at least one additional element either by increasing the capacity or by
- // flushing the buffer if it is full.
- FMT_CONSTEXPR void try_reserve(size_t new_capacity) {
- if (new_capacity > capacity_) grow_(*this, new_capacity);
- }
-
- FMT_CONSTEXPR void push_back(const T& value) {
- try_reserve(size_ + 1);
- ptr_[size_++] = value;
- }
-
- /// Appends data to the end of the buffer.
- template <typename U> void append(const U* begin, const U* end) {
- while (begin != end) {
- auto count = to_unsigned(end - begin);
- try_reserve(size_ + count);
- auto free_cap = capacity_ - size_;
- if (free_cap < count) count = free_cap;
- if (std::is_same<T, U>::value) {
- memcpy(ptr_ + size_, begin, count * sizeof(T));
- } else {
- T* out = ptr_ + size_;
- for (size_t i = 0; i < count; ++i) out[i] = begin[i];
- }
- size_ += count;
- begin += count;
- }
- }
-
- template <typename Idx> FMT_CONSTEXPR auto operator[](Idx index) -> T& {
- return ptr_[index];
- }
- template <typename Idx>
- FMT_CONSTEXPR auto operator[](Idx index) const -> const T& {
- return ptr_[index];
- }
-};
-
-struct buffer_traits {
- explicit buffer_traits(size_t) {}
- auto count() const -> size_t { return 0; }
- auto limit(size_t size) -> size_t { return size; }
-};
-
-class fixed_buffer_traits {
- private:
- size_t count_ = 0;
- size_t limit_;
-
- public:
- explicit fixed_buffer_traits(size_t limit) : limit_(limit) {}
- auto count() const -> size_t { return count_; }
- auto limit(size_t size) -> size_t {
- size_t n = limit_ > count_ ? limit_ - count_ : 0;
- count_ += size;
- return size < n ? size : n;
- }
-};
-
-// A buffer that writes to an output iterator when flushed.
-template <typename OutputIt, typename T, typename Traits = buffer_traits>
-class iterator_buffer : public Traits, public buffer<T> {
- private:
- OutputIt out_;
- enum { buffer_size = 256 };
- T data_[buffer_size];
-
- static FMT_CONSTEXPR void grow(buffer<T>& buf, size_t) {
- if (buf.size() == buffer_size) static_cast<iterator_buffer&>(buf).flush();
- }
-
- void flush() {
- auto size = this->size();
- this->clear();
- const T* begin = data_;
- const T* end = begin + this->limit(size);
- while (begin != end) *out_++ = *begin++;
- }
-
- public:
- explicit iterator_buffer(OutputIt out, size_t n = buffer_size)
- : Traits(n), buffer<T>(grow, data_, 0, buffer_size), out_(out) {}
- iterator_buffer(iterator_buffer&& other) noexcept
- : Traits(other),
- buffer<T>(grow, data_, 0, buffer_size),
- out_(other.out_) {}
- ~iterator_buffer() {
- // Don't crash if flush fails during unwinding.
- FMT_TRY { flush(); }
- FMT_CATCH(...) {}
- }
-
- auto out() -> OutputIt {
- flush();
- return out_;
- }
- auto count() const -> size_t { return Traits::count() + this->size(); }
-};
-
-template <typename T>
-class iterator_buffer<T*, T, fixed_buffer_traits> : public fixed_buffer_traits,
- public buffer<T> {
- private:
- T* out_;
- enum { buffer_size = 256 };
- T data_[buffer_size];
-
- static FMT_CONSTEXPR void grow(buffer<T>& buf, size_t) {
- if (buf.size() == buf.capacity())
- static_cast<iterator_buffer&>(buf).flush();
- }
-
- void flush() {
- size_t n = this->limit(this->size());
- if (this->data() == out_) {
- out_ += n;
- this->set(data_, buffer_size);
- }
- this->clear();
- }
-
- public:
- explicit iterator_buffer(T* out, size_t n = buffer_size)
- : fixed_buffer_traits(n), buffer<T>(grow, out, 0, n), out_(out) {}
- iterator_buffer(iterator_buffer&& other) noexcept
- : fixed_buffer_traits(other),
- buffer<T>(static_cast<iterator_buffer&&>(other)),
- out_(other.out_) {
- if (this->data() != out_) {
- this->set(data_, buffer_size);
- this->clear();
- }
- }
- ~iterator_buffer() { flush(); }
-
- auto out() -> T* {
- flush();
- return out_;
- }
- auto count() const -> size_t {
- return fixed_buffer_traits::count() + this->size();
- }
-};
-
-template <typename T> class iterator_buffer<T*, T> : public buffer<T> {
- public:
- explicit iterator_buffer(T* out, size_t = 0)
- : buffer<T>([](buffer<T>&, size_t) {}, out, 0, ~size_t()) {}
+// An argument reference.
+template <typename Char> union arg_ref {
+ FMT_CONSTEXPR arg_ref(int idx = 0) : index(idx) {}
+ FMT_CONSTEXPR arg_ref(basic_string_view<Char> n) : name(n) {}
- auto out() -> T* { return &*this->end(); }
+ int index;
+ basic_string_view<Char> name;
};
-// A buffer that writes to a container with the contiguous storage.
-template <typename OutputIt>
-class iterator_buffer<
- OutputIt,
- enable_if_t<detail::is_back_insert_iterator<OutputIt>::value &&
- is_contiguous<typename OutputIt::container_type>::value,
- typename OutputIt::container_type::value_type>>
- : public buffer<typename OutputIt::container_type::value_type> {
- private:
- using container_type = typename OutputIt::container_type;
- using value_type = typename container_type::value_type;
- container_type& container_;
-
- static FMT_CONSTEXPR void grow(buffer<value_type>& buf, size_t capacity) {
- auto& self = static_cast<iterator_buffer&>(buf);
- self.container_.resize(capacity);
- self.set(&self.container_[0], capacity);
- }
-
- public:
- explicit iterator_buffer(container_type& c)
- : buffer<value_type>(grow, c.size()), container_(c) {}
- explicit iterator_buffer(OutputIt out, size_t = 0)
- : iterator_buffer(get_container(out)) {}
-
- auto out() -> OutputIt { return back_inserter(container_); }
+// Format specifiers with width and precision resolved at formatting rather
+// than parsing time to allow reusing the same parsed specifiers with
+// different sets of arguments (precompilation of format strings).
+template <typename Char = char> struct dynamic_format_specs : format_specs {
+ arg_ref<Char> width_ref;
+ arg_ref<Char> precision_ref;
};
-// A buffer that counts the number of code units written discarding the output.
-template <typename T = char> class counting_buffer : public buffer<T> {
- private:
- enum { buffer_size = 256 };
- T data_[buffer_size];
- size_t count_ = 0;
-
- static FMT_CONSTEXPR void grow(buffer<T>& buf, size_t) {
- if (buf.size() != buffer_size) return;
- static_cast<counting_buffer&>(buf).count_ += buf.size();
- buf.clear();
- }
-
- public:
- counting_buffer() : buffer<T>(grow, data_, 0, buffer_size) {}
-
- auto count() -> size_t { return count_ + this->size(); }
-};
-} // namespace detail
+// Converts a character to ASCII. Returns '\0' on conversion failure.
+template <typename Char, FMT_ENABLE_IF(std::is_integral<Char>::value)>
+constexpr auto to_ascii(Char c) -> char {
+ return c <= 0xff ? static_cast<char>(c) : '\0';
+}
+// Returns the number of code units in a code point or 1 on error.
template <typename Char>
-FMT_CONSTEXPR void basic_format_parse_context<Char>::do_check_arg_id(int id) {
- // Argument id is only checked at compile-time during parsing because
- // formatting has its own validation.
- if (detail::is_constant_evaluated() &&
- (!FMT_GCC_VERSION || FMT_GCC_VERSION >= 1200)) {
- using context = detail::compile_parse_context<Char>;
- if (id >= static_cast<context*>(this)->num_args())
- report_error("argument not found");
- }
+FMT_CONSTEXPR auto code_point_length(const Char* begin) -> int {
+ if (const_check(sizeof(Char) != 1)) return 1;
+ auto c = static_cast<unsigned char>(*begin);
+ return static_cast<int>((0x3a55000000000000ull >> (2 * (c >> 3))) & 3) + 1;
}
+// Parses the range [begin, end) as an unsigned integer. This function assumes
+// that the range is non-empty and the first character is a digit.
template <typename Char>
-FMT_CONSTEXPR void basic_format_parse_context<Char>::check_dynamic_spec(
- int arg_id) {
- if (detail::is_constant_evaluated() &&
- (!FMT_GCC_VERSION || FMT_GCC_VERSION >= 1200)) {
- using context = detail::compile_parse_context<Char>;
- static_cast<context*>(this)->check_dynamic_spec(arg_id);
- }
+FMT_CONSTEXPR auto parse_nonnegative_int(const Char*& begin, const Char* end,
+ int error_value) noexcept -> int {
+ FMT_ASSERT(begin != end && '0' <= *begin && *begin <= '9', "");
+ unsigned value = 0, prev = 0;
+ auto p = begin;
+ do {
+ prev = value;
+ value = value * 10 + unsigned(*p - '0');
+ ++p;
+ } while (p != end && '0' <= *p && *p <= '9');
+ auto num_digits = p - begin;
+ begin = p;
+ int digits10 = static_cast<int>(sizeof(int) * CHAR_BIT * 3 / 10);
+ if (num_digits <= digits10) return static_cast<int>(value);
+ // Check for overflow.
+ unsigned max = INT_MAX;
+ return num_digits == digits10 + 1 &&
+ prev * 10ull + unsigned(p[-1] - '0') <= max
+ ? static_cast<int>(value)
+ : error_value;
}
-FMT_EXPORT template <typename Context> class basic_format_arg;
-FMT_EXPORT template <typename Context> class basic_format_args;
-FMT_EXPORT template <typename Context> class dynamic_format_arg_store;
-
-// A formatter for objects of type T.
-FMT_EXPORT
-template <typename T, typename Char = char, typename Enable = void>
-struct formatter {
- // A deleted default constructor indicates a disabled formatter.
- formatter() = delete;
-};
-
-// Specifies if T has an enabled formatter specialization. A type can be
-// formattable even if it doesn't have a formatter e.g. via a conversion.
-template <typename T, typename Context>
-using has_formatter =
- std::is_constructible<typename Context::template formatter_type<T>>;
-
-// An output iterator that appends to a buffer. It is used instead of
-// back_insert_iterator to reduce symbol sizes and avoid <iterator> dependency.
-template <typename T> class basic_appender {
- private:
- detail::buffer<T>* buffer_;
-
- friend auto get_container(basic_appender app) -> detail::buffer<T>& {
- return *app.buffer_;
- }
-
- public:
- using iterator_category = int;
- using value_type = T;
- using difference_type = ptrdiff_t;
- using pointer = T*;
- using reference = T&;
- FMT_UNCHECKED_ITERATOR(basic_appender);
-
- FMT_CONSTEXPR basic_appender(detail::buffer<T>& buf) : buffer_(&buf) {}
-
- auto operator=(T c) -> basic_appender& {
- buffer_->push_back(c);
- return *this;
+FMT_CONSTEXPR inline auto parse_align(char c) -> align {
+ switch (c) {
+ case '<': return align::left;
+ case '>': return align::right;
+ case '^': return align::center;
}
- auto operator*() -> basic_appender& { return *this; }
- auto operator++() -> basic_appender& { return *this; }
- auto operator++(int) -> basic_appender { return *this; }
-};
-
-using appender = basic_appender<char>;
-
-namespace detail {
-
-template <typename T, typename Enable = void>
-struct locking : std::true_type {};
-template <typename T>
-struct locking<T, void_t<typename formatter<remove_cvref_t<T>>::nonlocking>>
- : std::false_type {};
-
-template <typename T = int> FMT_CONSTEXPR inline auto is_locking() -> bool {
- return locking<T>::value;
-}
-template <typename T1, typename T2, typename... Tail>
-FMT_CONSTEXPR inline auto is_locking() -> bool {
- return locking<T1>::value || is_locking<T2, Tail...>();
-}
-
-// An optimized version of std::copy with the output value type (T).
-template <typename T, typename InputIt>
-auto copy(InputIt begin, InputIt end, appender out) -> appender {
- get_container(out).append(begin, end);
- return out;
-}
-
-template <typename T, typename InputIt, typename OutputIt,
- FMT_ENABLE_IF(is_back_insert_iterator<OutputIt>::value)>
-auto copy(InputIt begin, InputIt end, OutputIt out) -> OutputIt {
- get_container(out).append(begin, end);
- return out;
-}
-
-template <typename T, typename InputIt, typename OutputIt,
- FMT_ENABLE_IF(!is_back_insert_iterator<OutputIt>::value)>
-FMT_CONSTEXPR auto copy(InputIt begin, InputIt end, OutputIt out) -> OutputIt {
- while (begin != end) *out++ = static_cast<T>(*begin++);
- return out;
-}
-
-template <typename T>
-FMT_CONSTEXPR auto copy(const T* begin, const T* end, T* out) -> T* {
- if (is_constant_evaluated()) return copy<T, const T*, T*>(begin, end, out);
- auto size = to_unsigned(end - begin);
- if (size > 0) memcpy(out, begin, size * sizeof(T));
- return out + size;
-}
-
-template <typename T, typename V, typename OutputIt>
-FMT_CONSTEXPR auto copy(basic_string_view<V> s, OutputIt out) -> OutputIt {
- return copy<T>(s.begin(), s.end(), out);
-}
-
-template <typename Context, typename T>
-constexpr auto has_const_formatter_impl(T*)
- -> decltype(typename Context::template formatter_type<T>().format(
- std::declval<const T&>(), std::declval<Context&>()),
- true) {
- return true;
-}
-template <typename Context>
-constexpr auto has_const_formatter_impl(...) -> bool {
- return false;
-}
-template <typename T, typename Context>
-constexpr auto has_const_formatter() -> bool {
- return has_const_formatter_impl<Context>(static_cast<T*>(nullptr));
-}
-
-// Maps an output iterator to a buffer.
-template <typename T, typename OutputIt>
-auto get_buffer(OutputIt out) -> iterator_buffer<OutputIt, T> {
- return iterator_buffer<OutputIt, T>(out);
-}
-template <typename T> auto get_buffer(basic_appender<T> out) -> buffer<T>& {
- return get_container(out);
-}
-
-template <typename Buf, typename OutputIt>
-auto get_iterator(Buf& buf, OutputIt) -> decltype(buf.out()) {
- return buf.out();
-}
-template <typename T, typename OutputIt>
-auto get_iterator(buffer<T>&, OutputIt out) -> OutputIt {
- return out;
-}
-
-struct view {};
-
-template <typename Char, typename T> struct named_arg : view {
- const Char* name;
- const T& value;
- named_arg(const Char* n, const T& v) : name(n), value(v) {}
-};
-
-template <typename Char> struct named_arg_info {
- const Char* name;
- int id;
-};
-
-template <typename T> struct is_named_arg : std::false_type {};
-template <typename T> struct is_statically_named_arg : std::false_type {};
-
-template <typename T, typename Char>
-struct is_named_arg<named_arg<Char, T>> : std::true_type {};
-
-template <bool B = false> constexpr auto count() -> size_t { return B ? 1 : 0; }
-template <bool B1, bool B2, bool... Tail> constexpr auto count() -> size_t {
- return (B1 ? 1 : 0) + count<B2, Tail...>();
-}
-
-template <typename... Args> constexpr auto count_named_args() -> size_t {
- return count<is_named_arg<Args>::value...>();
+ return align::none;
}
-template <typename... Args>
-constexpr auto count_statically_named_args() -> size_t {
- return count<is_statically_named_arg<Args>::value...>();
+template <typename Char> constexpr auto is_name_start(Char c) -> bool {
+ return ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || c == '_';
}
-struct unformattable {};
-struct unformattable_char : unformattable {};
-struct unformattable_pointer : unformattable {};
-
-template <typename Char> struct string_value {
- const Char* data;
- size_t size;
-};
-
-template <typename Char> struct named_arg_value {
- const named_arg_info<Char>* data;
- size_t size;
-};
-
-template <typename Context> struct custom_value {
- using parse_context = typename Context::parse_context_type;
- void* value;
- void (*format)(void* arg, parse_context& parse_ctx, Context& ctx);
-};
-
-// A formatting argument value.
-template <typename Context> class value {
- public:
- using char_type = typename Context::char_type;
-
- union {
- monostate no_value;
- int int_value;
- unsigned uint_value;
- long long long_long_value;
- unsigned long long ulong_long_value;
- int128_opt int128_value;
- uint128_opt uint128_value;
- bool bool_value;
- char_type char_value;
- float float_value;
- double double_value;
- long double long_double_value;
- const void* pointer;
- string_value<char_type> string;
- custom_value<Context> custom;
- named_arg_value<char_type> named_args;
- };
-
- constexpr FMT_ALWAYS_INLINE value() : no_value() {}
- constexpr FMT_ALWAYS_INLINE value(int val) : int_value(val) {}
- constexpr FMT_ALWAYS_INLINE value(unsigned val) : uint_value(val) {}
- constexpr FMT_ALWAYS_INLINE value(long long val) : long_long_value(val) {}
- constexpr FMT_ALWAYS_INLINE value(unsigned long long val)
- : ulong_long_value(val) {}
- FMT_ALWAYS_INLINE value(int128_opt val) : int128_value(val) {}
- FMT_ALWAYS_INLINE value(uint128_opt val) : uint128_value(val) {}
- constexpr FMT_ALWAYS_INLINE value(float val) : float_value(val) {}
- constexpr FMT_ALWAYS_INLINE value(double val) : double_value(val) {}
- FMT_ALWAYS_INLINE value(long double val) : long_double_value(val) {}
- constexpr FMT_ALWAYS_INLINE value(bool val) : bool_value(val) {}
- constexpr FMT_ALWAYS_INLINE value(char_type val) : char_value(val) {}
- FMT_CONSTEXPR FMT_ALWAYS_INLINE value(const char_type* val) {
- string.data = val;
- if (is_constant_evaluated()) string.size = {};
- }
- FMT_CONSTEXPR FMT_ALWAYS_INLINE value(basic_string_view<char_type> val) {
- string.data = val.data();
- string.size = val.size();
- }
- FMT_ALWAYS_INLINE value(const void* val) : pointer(val) {}
- FMT_ALWAYS_INLINE value(const named_arg_info<char_type>* args, size_t size)
- : named_args{args, size} {}
-
- template <typename T> FMT_CONSTEXPR20 FMT_ALWAYS_INLINE value(T& val) {
- using value_type = remove_const_t<T>;
- // T may overload operator& e.g. std::vector<bool>::reference in libc++.
-#if defined(__cpp_if_constexpr)
- if constexpr (std::is_same<decltype(&val), T*>::value)
- custom.value = const_cast<value_type*>(&val);
-#endif
- if (!is_constant_evaluated())
- custom.value = const_cast<char*>(&reinterpret_cast<const char&>(val));
- // Get the formatter type through the context to allow different contexts
- // have different extension points, e.g. `formatter<T>` for `format` and
- // `printf_formatter<T>` for `printf`.
- custom.format = format_custom_arg<
- value_type, typename Context::template formatter_type<value_type>>;
- }
- value(unformattable);
- value(unformattable_char);
- value(unformattable_pointer);
-
- private:
- // Formats an argument of a custom type, such as a user-defined class.
- template <typename T, typename Formatter>
- static void format_custom_arg(void* arg,
- typename Context::parse_context_type& parse_ctx,
- Context& ctx) {
- auto f = Formatter();
- parse_ctx.advance_to(f.parse(parse_ctx));
- using qualified_type =
- conditional_t<has_const_formatter<T, Context>(), const T, T>;
- // format must be const for compatibility with std::format and compilation.
- const auto& cf = f;
- ctx.advance_to(cf.format(*static_cast<qualified_type*>(arg), ctx));
+template <typename Char, typename Handler>
+FMT_CONSTEXPR auto parse_arg_id(const Char* begin, const Char* end,
+ Handler&& handler) -> const Char* {
+ Char c = *begin;
+ if (c >= '0' && c <= '9') {
+ int index = 0;
+ if (c != '0')
+ index = parse_nonnegative_int(begin, end, INT_MAX);
+ else
+ ++begin;
+ if (begin == end || (*begin != '}' && *begin != ':'))
+ report_error("invalid format string");
+ else
+ handler.on_index(index);
+ return begin;
}
-};
-
-// 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) };
-using long_type = conditional_t<long_short, int, long long>;
-using ulong_type = conditional_t<long_short, unsigned, unsigned long long>;
-
-template <typename T> struct format_as_result {
- template <typename U,
- FMT_ENABLE_IF(std::is_enum<U>::value || std::is_class<U>::value)>
- static auto map(U*) -> remove_cvref_t<decltype(format_as(std::declval<U>()))>;
- static auto map(...) -> void;
-
- using type = decltype(map(static_cast<T*>(nullptr)));
-};
-template <typename T> using format_as_t = typename format_as_result<T>::type;
-
-template <typename T>
-struct has_format_as
- : bool_constant<!std::is_same<format_as_t<T>, void>::value> {};
-
-#define FMT_MAP_API FMT_CONSTEXPR FMT_ALWAYS_INLINE
-
-// Maps formatting arguments to core types.
-// arg_mapper reports errors by returning unformattable instead of using
-// static_assert because it's used in the is_formattable trait.
-template <typename Context> struct arg_mapper {
- using char_type = typename Context::char_type;
-
- FMT_MAP_API auto map(signed char val) -> int { return val; }
- FMT_MAP_API auto map(unsigned char val) -> unsigned { return val; }
- FMT_MAP_API auto map(short val) -> int { return val; }
- FMT_MAP_API auto map(unsigned short val) -> unsigned { return val; }
- FMT_MAP_API auto map(int val) -> int { return val; }
- FMT_MAP_API auto map(unsigned val) -> unsigned { return val; }
- FMT_MAP_API auto map(long val) -> long_type { return val; }
- FMT_MAP_API auto map(unsigned long val) -> ulong_type { return val; }
- FMT_MAP_API auto map(long long val) -> long long { return val; }
- FMT_MAP_API auto map(unsigned long long val) -> unsigned long long {
- return val;
- }
- FMT_MAP_API auto map(int128_opt val) -> int128_opt { return val; }
- FMT_MAP_API auto map(uint128_opt val) -> uint128_opt { return val; }
- FMT_MAP_API auto map(bool val) -> bool { return val; }
-
- template <typename T, FMT_ENABLE_IF(std::is_same<T, char>::value ||
- std::is_same<T, char_type>::value)>
- FMT_MAP_API auto map(T val) -> char_type {
- return val;
- }
- template <typename T, enable_if_t<(std::is_same<T, wchar_t>::value ||
-#ifdef __cpp_char8_t
- std::is_same<T, char8_t>::value ||
-#endif
- std::is_same<T, char16_t>::value ||
- std::is_same<T, char32_t>::value) &&
- !std::is_same<T, char_type>::value,
- int> = 0>
- FMT_MAP_API auto map(T) -> unformattable_char {
- return {};
- }
-
- FMT_MAP_API auto map(float val) -> float { return val; }
- FMT_MAP_API auto map(double val) -> double { return val; }
- FMT_MAP_API auto map(long double val) -> long double { return val; }
-
- FMT_MAP_API auto map(char_type* val) -> const char_type* { return val; }
- FMT_MAP_API auto map(const char_type* val) -> const char_type* { return val; }
- template <typename T, typename Char = char_t<T>,
- FMT_ENABLE_IF(std::is_same<Char, char_type>::value &&
- !std::is_pointer<T>::value)>
- FMT_MAP_API auto map(const T& val) -> basic_string_view<Char> {
- return to_string_view(val);
- }
- template <typename T, typename Char = char_t<T>,
- FMT_ENABLE_IF(!std::is_same<Char, char_type>::value &&
- !std::is_pointer<T>::value)>
- FMT_MAP_API auto map(const T&) -> unformattable_char {
- return {};
- }
-
- FMT_MAP_API auto map(void* val) -> const void* { return val; }
- FMT_MAP_API auto map(const void* val) -> const void* { return val; }
- FMT_MAP_API auto map(std::nullptr_t val) -> const void* { return val; }
-
- // Use SFINAE instead of a const T* parameter to avoid a conflict with the
- // array overload.
- template <
- typename T,
- FMT_ENABLE_IF(
- std::is_pointer<T>::value || std::is_member_pointer<T>::value ||
- std::is_function<typename std::remove_pointer<T>::type>::value ||
- (std::is_array<T>::value &&
- !std::is_convertible<T, const char_type*>::value))>
- FMT_CONSTEXPR auto map(const T&) -> unformattable_pointer {
- return {};
- }
-
- template <typename T, std::size_t N,
- FMT_ENABLE_IF(!std::is_same<T, wchar_t>::value)>
- FMT_MAP_API auto map(const T (&values)[N]) -> const T (&)[N] {
- return values;
- }
-
- // Only map owning types because mapping views can be unsafe.
- template <typename T, typename U = format_as_t<T>,
- FMT_ENABLE_IF(std::is_arithmetic<U>::value)>
- FMT_MAP_API auto map(const T& val) -> decltype(FMT_DECLTYPE_THIS map(U())) {
- return map(format_as(val));
- }
-
- template <typename T, typename U = remove_const_t<T>>
- struct formattable : bool_constant<has_const_formatter<U, Context>() ||
- (has_formatter<U, Context>::value &&
- !std::is_const<T>::value)> {};
-
- template <typename T, FMT_ENABLE_IF(formattable<T>::value)>
- FMT_MAP_API auto do_map(T& val) -> T& {
- return val;
- }
- template <typename T, FMT_ENABLE_IF(!formattable<T>::value)>
- FMT_MAP_API auto do_map(T&) -> unformattable {
- return {};
- }
-
- // is_fundamental is used to allow formatters for extended FP types.
- template <typename T, typename U = remove_const_t<T>,
- FMT_ENABLE_IF(
- (std::is_class<U>::value || std::is_enum<U>::value ||
- std::is_union<U>::value || std::is_fundamental<U>::value) &&
- !has_to_string_view<U>::value && !is_char<U>::value &&
- !is_named_arg<U>::value && !std::is_integral<U>::value &&
- !std::is_arithmetic<format_as_t<U>>::value)>
- FMT_MAP_API auto map(T& val) -> decltype(FMT_DECLTYPE_THIS do_map(val)) {
- return do_map(val);
+ if (FMT_OPTIMIZE_SIZE > 1 || !is_name_start(c)) {
+ report_error("invalid format string");
+ return begin;
}
+ auto it = begin;
+ do {
+ ++it;
+ } while (it != end && (is_name_start(*it) || ('0' <= *it && *it <= '9')));
+ handler.on_name({begin, to_unsigned(it - begin)});
+ return it;
+}
+
+template <typename Char> struct dynamic_spec_handler {
+ parse_context<Char>& ctx;
+ arg_ref<Char>& ref;
+ arg_id_kind& kind;
- template <typename T, FMT_ENABLE_IF(is_named_arg<T>::value)>
- FMT_MAP_API auto map(const T& named_arg)
- -> decltype(FMT_DECLTYPE_THIS map(named_arg.value)) {
- return map(named_arg.value);
+ FMT_CONSTEXPR void on_index(int id) {
+ ref = id;
+ kind = arg_id_kind::index;
+ ctx.check_arg_id(id);
+ ctx.check_dynamic_spec(id);
+ }
+ FMT_CONSTEXPR void on_name(basic_string_view<Char> id) {
+ ref = id;
+ kind = arg_id_kind::name;
+ ctx.check_arg_id(id);
}
-
- auto map(...) -> unformattable { return {}; }
};
-// A type constant after applying arg_mapper<Context>.
-template <typename T, typename Context>
-using mapped_type_constant =
- type_constant<decltype(arg_mapper<Context>().map(std::declval<const T&>())),
- typename Context::char_type>;
-
-enum { packed_arg_bits = 4 };
-// Maximum number of arguments with packed types.
-enum { max_packed_args = 62 / packed_arg_bits };
-enum : unsigned long long { is_unpacked_bit = 1ULL << 63 };
-enum : unsigned long long { has_named_args_bit = 1ULL << 62 };
-
-template <typename It, typename T, typename Enable = void>
-struct is_output_iterator : std::false_type {};
-
-template <> struct is_output_iterator<appender, char> : std::true_type {};
-
-template <typename It, typename T>
-struct is_output_iterator<
- It, T, void_t<decltype(*std::declval<It&>()++ = std::declval<T>())>>
- : std::true_type {};
-
-// A type-erased reference to an std::locale to avoid a heavy <locale> include.
-class locale_ref {
- private:
- const void* locale_; // A type-erased pointer to std::locale.
-
- public:
- constexpr locale_ref() : locale_(nullptr) {}
- template <typename Locale> explicit locale_ref(const Locale& loc);
-
- explicit operator bool() const noexcept { return locale_ != nullptr; }
-
- template <typename Locale> auto get() const -> Locale;
+template <typename Char> struct parse_dynamic_spec_result {
+ const Char* end;
+ arg_id_kind kind;
};
-template <typename> constexpr auto encode_types() -> unsigned long long {
- return 0;
+// Parses integer | "{" [arg_id] "}".
+template <typename Char>
+FMT_CONSTEXPR auto parse_dynamic_spec(const Char* begin, const Char* end,
+ int& value, arg_ref<Char>& ref,
+ parse_context<Char>& ctx)
+ -> parse_dynamic_spec_result<Char> {
+ FMT_ASSERT(begin != end, "");
+ auto kind = arg_id_kind::none;
+ if ('0' <= *begin && *begin <= '9') {
+ int val = parse_nonnegative_int(begin, end, -1);
+ if (val == -1) report_error("number is too big");
+ value = val;
+ } else {
+ if (*begin == '{') {
+ ++begin;
+ if (begin != end) {
+ Char c = *begin;
+ if (c == '}' || c == ':') {
+ int id = ctx.next_arg_id();
+ ref = id;
+ kind = arg_id_kind::index;
+ ctx.check_dynamic_spec(id);
+ } else {
+ begin = parse_arg_id(begin, end,
+ dynamic_spec_handler<Char>{ctx, ref, kind});
+ }
+ }
+ if (begin != end && *begin == '}') return {++begin, kind};
+ }
+ report_error("invalid format string");
+ }
+ return {begin, kind};
}
-template <typename Context, typename Arg, typename... Args>
-constexpr auto encode_types() -> unsigned long long {
- return static_cast<unsigned>(mapped_type_constant<Arg, Context>::value) |
- (encode_types<Context, Args...>() << packed_arg_bits);
+template <typename Char>
+FMT_CONSTEXPR auto parse_width(const Char* begin, const Char* end,
+ format_specs& specs, arg_ref<Char>& width_ref,
+ parse_context<Char>& ctx) -> const Char* {
+ auto result = parse_dynamic_spec(begin, end, specs.width, width_ref, ctx);
+ specs.set_dynamic_width(result.kind);
+ return result.end;
}
-template <typename Context, typename... T, size_t NUM_ARGS = sizeof...(T)>
-constexpr unsigned long long make_descriptor() {
- return NUM_ARGS <= max_packed_args ? encode_types<Context, T...>()
- : is_unpacked_bit | NUM_ARGS;
+template <typename Char>
+FMT_CONSTEXPR auto parse_precision(const Char* begin, const Char* end,
+ format_specs& specs,
+ arg_ref<Char>& precision_ref,
+ parse_context<Char>& ctx) -> const Char* {
+ ++begin;
+ if (begin == end) {
+ report_error("invalid precision");
+ return begin;
+ }
+ auto result =
+ parse_dynamic_spec(begin, end, specs.precision, precision_ref, ctx);
+ specs.set_dynamic_precision(result.kind);
+ return result.end;
}
-// This type is intentionally undefined, only used for errors.
-template <typename T, typename Char>
-#if FMT_CLANG_VERSION && FMT_CLANG_VERSION <= 1500
-// https://github.com/fmtlib/fmt/issues/3796
-struct type_is_unformattable_for {
-};
-#else
-struct type_is_unformattable_for;
-#endif
+enum class state { start, align, sign, hash, zero, width, precision, locale };
-template <bool PACKED, typename Context, typename T, FMT_ENABLE_IF(PACKED)>
-FMT_CONSTEXPR auto make_arg(T& val) -> value<Context> {
- using arg_type = remove_cvref_t<decltype(arg_mapper<Context>().map(val))>;
+// Parses standard format specifiers.
+template <typename Char>
+FMT_CONSTEXPR auto parse_format_specs(const Char* begin, const Char* end,
+ dynamic_format_specs<Char>& specs,
+ parse_context<Char>& ctx, type arg_type)
+ -> const Char* {
+ auto c = '\0';
+ if (end - begin > 1) {
+ auto next = to_ascii(begin[1]);
+ c = parse_align(next) == align::none ? to_ascii(*begin) : '\0';
+ } else {
+ if (begin == end) return begin;
+ c = to_ascii(*begin);
+ }
- // Use enum instead of constexpr because the latter may generate code.
- enum {
- formattable_char = !std::is_same<arg_type, unformattable_char>::value
- };
- static_assert(formattable_char, "Mixing character types is disallowed.");
+ struct {
+ state current_state = state::start;
+ FMT_CONSTEXPR void operator()(state s, bool valid = true) {
+ if (current_state >= s || !valid)
+ report_error("invalid format specifier");
+ current_state = s;
+ }
+ } enter_state;
- // Formatting of arbitrary pointers is disallowed. If you want to format a
- // pointer cast it to `void*` or `const void*`. In particular, this forbids
- // formatting of `[const] volatile char*` printed as bool by iostreams.
- enum {
- formattable_pointer = !std::is_same<arg_type, unformattable_pointer>::value
- };
- static_assert(formattable_pointer,
- "Formatting of non-void pointers is disallowed.");
+ using pres = presentation_type;
+ constexpr auto integral_set = sint_set | uint_set | bool_set | char_set;
+ struct {
+ const Char*& begin;
+ format_specs& specs;
+ type arg_type;
- enum { formattable = !std::is_same<arg_type, unformattable>::value };
-#if defined(__cpp_if_constexpr)
- if constexpr (!formattable)
- type_is_unformattable_for<T, typename Context::char_type> _;
-#endif
- static_assert(
- formattable,
- "Cannot format an argument. To make type T formattable provide a "
- "formatter<T> specialization: https://fmt.dev/latest/api.html#udt");
- return {arg_mapper<Context>().map(val)};
-}
+ FMT_CONSTEXPR auto operator()(pres pres_type, int set) -> const Char* {
+ if (!in(arg_type, set)) report_error("invalid format specifier");
+ specs.set_type(pres_type);
+ return begin + 1;
+ }
+ } parse_presentation_type{begin, specs, arg_type};
-template <typename Context, typename T>
-FMT_CONSTEXPR auto make_arg(T& val) -> basic_format_arg<Context> {
- auto arg = basic_format_arg<Context>();
- arg.type_ = mapped_type_constant<T, Context>::value;
- arg.value_ = make_arg<true, Context>(val);
- return arg;
+ for (;;) {
+ switch (c) {
+ case '<':
+ case '>':
+ case '^':
+ enter_state(state::align);
+ specs.set_align(parse_align(c));
+ ++begin;
+ break;
+ case '+':
+ case ' ':
+ specs.set_sign(c == ' ' ? sign::space : sign::plus);
+ FMT_FALLTHROUGH;
+ case '-':
+ enter_state(state::sign, in(arg_type, sint_set | float_set));
+ ++begin;
+ break;
+ case '#':
+ enter_state(state::hash, is_arithmetic_type(arg_type));
+ specs.set_alt();
+ ++begin;
+ break;
+ case '0':
+ enter_state(state::zero);
+ if (!is_arithmetic_type(arg_type))
+ report_error("format specifier requires numeric argument");
+ if (specs.align() == align::none) {
+ // Ignore 0 if align is specified for compatibility with std::format.
+ specs.set_align(align::numeric);
+ specs.set_fill('0');
+ }
+ ++begin;
+ break;
+ // clang-format off
+ case '1': case '2': case '3': case '4': case '5':
+ case '6': case '7': case '8': case '9': case '{':
+ // clang-format on
+ enter_state(state::width);
+ begin = parse_width(begin, end, specs, specs.width_ref, ctx);
+ break;
+ case '.':
+ enter_state(state::precision,
+ in(arg_type, float_set | string_set | cstring_set));
+ begin = parse_precision(begin, end, specs, specs.precision_ref, ctx);
+ break;
+ case 'L':
+ enter_state(state::locale, is_arithmetic_type(arg_type));
+ specs.set_localized();
+ ++begin;
+ break;
+ case 'd': return parse_presentation_type(pres::dec, integral_set);
+ case 'X': specs.set_upper(); FMT_FALLTHROUGH;
+ case 'x': return parse_presentation_type(pres::hex, integral_set);
+ case 'o': return parse_presentation_type(pres::oct, integral_set);
+ case 'B': specs.set_upper(); FMT_FALLTHROUGH;
+ case 'b': return parse_presentation_type(pres::bin, integral_set);
+ case 'E': specs.set_upper(); FMT_FALLTHROUGH;
+ case 'e': return parse_presentation_type(pres::exp, float_set);
+ case 'F': specs.set_upper(); FMT_FALLTHROUGH;
+ case 'f': return parse_presentation_type(pres::fixed, float_set);
+ case 'G': specs.set_upper(); FMT_FALLTHROUGH;
+ case 'g': return parse_presentation_type(pres::general, float_set);
+ case 'A': specs.set_upper(); FMT_FALLTHROUGH;
+ case 'a': return parse_presentation_type(pres::hexfloat, float_set);
+ case 'c':
+ if (arg_type == type::bool_type) report_error("invalid format specifier");
+ return parse_presentation_type(pres::chr, integral_set);
+ case 's':
+ return parse_presentation_type(pres::string,
+ bool_set | string_set | cstring_set);
+ case 'p':
+ return parse_presentation_type(pres::pointer, pointer_set | cstring_set);
+ case '?':
+ return parse_presentation_type(pres::debug,
+ char_set | string_set | cstring_set);
+ case '}': return begin;
+ default: {
+ if (*begin == '}') return begin;
+ // Parse fill and alignment.
+ auto fill_end = begin + code_point_length(begin);
+ if (end - fill_end <= 0) {
+ report_error("invalid format specifier");
+ return begin;
+ }
+ if (*begin == '{') {
+ report_error("invalid fill character '{'");
+ return begin;
+ }
+ auto alignment = parse_align(to_ascii(*fill_end));
+ enter_state(state::align, alignment != align::none);
+ specs.set_fill(
+ basic_string_view<Char>(begin, to_unsigned(fill_end - begin)));
+ specs.set_align(alignment);
+ begin = fill_end + 1;
+ }
+ }
+ if (begin == end) return begin;
+ c = to_ascii(*begin);
+ }
}
-template <bool PACKED, typename Context, typename T, FMT_ENABLE_IF(!PACKED)>
-FMT_CONSTEXPR inline auto make_arg(T& val) -> basic_format_arg<Context> {
- return make_arg<Context>(val);
+template <typename Char, typename Handler>
+FMT_CONSTEXPR FMT_INLINE auto parse_replacement_field(const Char* begin,
+ const Char* end,
+ Handler&& handler)
+ -> const Char* {
+ ++begin;
+ if (begin == end) {
+ handler.on_error("invalid format string");
+ return end;
+ }
+ int arg_id = 0;
+ switch (*begin) {
+ case '}':
+ handler.on_replacement_field(handler.on_arg_id(), begin);
+ return begin + 1;
+ case '{': handler.on_text(begin, begin + 1); return begin + 1;
+ case ':': arg_id = handler.on_arg_id(); break;
+ default: {
+ struct id_adapter {
+ Handler& handler;
+ int arg_id;
+
+ FMT_CONSTEXPR void on_index(int id) { arg_id = handler.on_arg_id(id); }
+ FMT_CONSTEXPR void on_name(basic_string_view<Char> id) {
+ arg_id = handler.on_arg_id(id);
+ }
+ } adapter = {handler, 0};
+ begin = parse_arg_id(begin, end, adapter);
+ arg_id = adapter.arg_id;
+ Char c = begin != end ? *begin : Char();
+ if (c == '}') {
+ handler.on_replacement_field(arg_id, begin);
+ return begin + 1;
+ }
+ if (c != ':') {
+ handler.on_error("missing '}' in format string");
+ return end;
+ }
+ break;
+ }
+ }
+ begin = handler.on_format_specs(arg_id, begin + 1, end);
+ if (begin == end || *begin != '}')
+ return handler.on_error("unknown format specifier"), end;
+ return begin + 1;
}
-template <typename Context, size_t NUM_ARGS>
-using arg_t = conditional_t<NUM_ARGS <= max_packed_args, value<Context>,
- basic_format_arg<Context>>;
+template <typename Char, typename Handler>
+FMT_CONSTEXPR void parse_format_string(basic_string_view<Char> fmt,
+ Handler&& handler) {
+ auto begin = fmt.data(), end = begin + fmt.size();
+ auto p = begin;
+ while (p != end) {
+ auto c = *p++;
+ if (c == '{') {
+ handler.on_text(begin, p - 1);
+ begin = p = parse_replacement_field(p - 1, end, handler);
+ } else if (c == '}') {
+ if (p == end || *p != '}')
+ return handler.on_error("unmatched '}' in format string");
+ handler.on_text(begin, p);
+ begin = ++p;
+ }
+ }
+ handler.on_text(begin, end);
+}
-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;
+// Checks char specs and returns true iff the presentation type is char-like.
+FMT_CONSTEXPR inline auto check_char_specs(const format_specs& specs) -> bool {
+ auto type = specs.type();
+ if (type != presentation_type::none && type != presentation_type::chr &&
+ type != presentation_type::debug) {
+ return false;
+ }
+ if (specs.align() == align::numeric || specs.sign() != sign::none ||
+ specs.alt()) {
+ report_error("invalid format specifier for char");
+ }
+ return true;
}
-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) {
- named_args[named_arg_index++] = {arg.name, arg_index++};
+
+// A base class for compile-time strings.
+struct compile_string {};
+
+template <typename T, typename Char>
+FMT_VISIBILITY("hidden") // Suppress an ld warning on macOS (#3769).
+FMT_CONSTEXPR auto invoke_parse(parse_context<Char>& ctx) -> const Char* {
+ using mapped_type = remove_cvref_t<mapped_t<T, Char>>;
+ constexpr bool formattable =
+ std::is_constructible<formatter<mapped_type, Char>>::value;
+ if (!formattable) return ctx.begin(); // Error is reported in the value ctor.
+ using formatted_type = conditional_t<formattable, mapped_type, int>;
+ return formatter<formatted_type, Char>().parse(ctx);
}
-// An array of references to arguments. It can be implicitly converted to
-// `fmt::basic_format_args` for passing into type-erased formatting functions
-// such as `fmt::vformat`.
-template <typename Context, size_t NUM_ARGS, size_t NUM_NAMED_ARGS,
- unsigned long long DESC>
-struct format_arg_store {
- // args_[0].named_args points to named_args to avoid bloating format_args.
- // +1 to workaround a bug in gcc 7.5 that causes duplicated-branches warning.
- static constexpr size_t ARGS_ARR_SIZE = 1 + (NUM_ARGS != 0 ? NUM_ARGS : +1);
+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<size_t>(1, NUM_ARGS)];
+ named_arg_info<Char> named_args_[max_of<size_t>(1, NUM_NAMED_ARGS)];
+ compile_parse_context<Char> context_;
- arg_t<Context, NUM_ARGS> args[ARGS_ARR_SIZE];
- named_arg_info<typename Context::char_type> named_args[NUM_NAMED_ARGS];
+ using parse_func = auto (*)(parse_context<Char>&) -> const Char*;
+ parse_func parse_funcs_[max_of<size_t>(1, NUM_ARGS)];
+ public:
template <typename... T>
- FMT_MAP_API format_arg_store(T&... values)
- : args{{named_args, NUM_NAMED_ARGS},
- make_arg<NUM_ARGS <= max_packed_args, Context>(values)...} {
- using dummy = int[];
+ FMT_CONSTEXPR explicit format_string_checker(basic_string_view<Char> fmt,
+ arg_pack<T...>)
+ : types_{mapped_type_constant<T, Char>::value...},
+ named_args_{},
+ context_(fmt, NUM_ARGS, types_),
+ parse_funcs_{&invoke_parse<T, Char>...} {
int arg_index = 0, named_arg_index = 0;
- (void)dummy{
- 0,
- (init_named_arg(named_args, arg_index, named_arg_index, values), 0)...};
+ FMT_APPLY_VARIADIC(
+ init_static_named_arg<T>(named_args_, arg_index, named_arg_index));
+ ignore_unused(arg_index, named_arg_index);
}
- format_arg_store(format_arg_store&& rhs) {
- args[0] = {named_args, NUM_NAMED_ARGS};
- for (size_t i = 1; i < ARGS_ARR_SIZE; ++i) args[i] = rhs.args[i];
- for (size_t i = 0; i < NUM_NAMED_ARGS; ++i)
- named_args[i] = rhs.named_args[i];
+ FMT_CONSTEXPR void on_text(const Char*, const Char*) {}
+
+ FMT_CONSTEXPR auto on_arg_id() -> int { return context_.next_arg_id(); }
+ FMT_CONSTEXPR auto on_arg_id(int id) -> int {
+ context_.check_arg_id(id);
+ return id;
+ }
+ FMT_CONSTEXPR auto on_arg_id(basic_string_view<Char> id) -> int {
+ for (int i = 0; i < NUM_NAMED_ARGS; ++i) {
+ if (named_args_[i].name == id) return named_args_[i].id;
+ }
+ if (!DYNAMIC_NAMES) on_error("argument not found");
+ return -1;
}
- format_arg_store(const format_arg_store& rhs) = delete;
- format_arg_store& operator=(const format_arg_store& rhs) = delete;
- format_arg_store& operator=(format_arg_store&& rhs) = delete;
-};
+ FMT_CONSTEXPR void on_replacement_field(int id, const Char* begin) {
+ on_format_specs(id, begin, begin); // Call parse() on empty specs.
+ }
-// A specialization of format_arg_store without named arguments.
-// It is a plain struct to reduce binary size in debug mode.
-template <typename Context, size_t NUM_ARGS, unsigned long long DESC>
-struct format_arg_store<Context, NUM_ARGS, 0, DESC> {
- // +1 to workaround a bug in gcc 7.5 that causes duplicated-branches warning.
- arg_t<Context, NUM_ARGS> args[NUM_ARGS != 0 ? NUM_ARGS : +1];
+ FMT_CONSTEXPR auto on_format_specs(int id, const Char* begin, const Char* end)
+ -> const Char* {
+ context_.advance_to(begin);
+ if (id >= 0 && id < NUM_ARGS) return parse_funcs_[id](context_);
+
+ // 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;
+ }
+
+ FMT_NORETURN FMT_CONSTEXPR void on_error(const char* message) {
+ report_error(message);
+ }
};
-} // namespace detail
-FMT_BEGIN_EXPORT
+/// A contiguous memory buffer with an optional growing ability. It is an
+/// internal class and shouldn't be used directly, only via `memory_buffer`.
+template <typename T> class buffer {
+ private:
+ T* ptr_;
+ size_t size_;
+ size_t capacity_;
+
+ using grow_fun = void (*)(buffer& buf, size_t capacity);
+ grow_fun grow_;
+
+ protected:
+ // Don't initialize ptr_ since it is not accessed to save a few cycles.
+ FMT_MSC_WARNING(suppress : 26495)
+ FMT_CONSTEXPR buffer(grow_fun grow, size_t sz) noexcept
+ : size_(sz), capacity_(sz), grow_(grow) {}
+
+ constexpr buffer(grow_fun grow, T* p = nullptr, size_t sz = 0,
+ size_t cap = 0) noexcept
+ : ptr_(p), size_(sz), capacity_(cap), grow_(grow) {}
-// A formatting argument. Context is a template parameter for the compiled API
-// where output can be unbuffered.
-template <typename Context> class basic_format_arg {
- private:
- detail::value<Context> value_;
- detail::type type_;
+ FMT_CONSTEXPR20 ~buffer() = default;
+ buffer(buffer&&) = default;
- template <typename ContextType, typename T>
- friend FMT_CONSTEXPR auto detail::make_arg(T& value)
- -> basic_format_arg<ContextType>;
+ /// Sets the buffer data and capacity.
+ FMT_CONSTEXPR void set(T* buf_data, size_t buf_capacity) noexcept {
+ ptr_ = buf_data;
+ capacity_ = buf_capacity;
+ }
- friend class basic_format_args<Context>;
- friend class dynamic_format_arg_store<Context>;
+ public:
+ using value_type = T;
+ using const_reference = const T&;
- using char_type = typename Context::char_type;
+ buffer(const buffer&) = delete;
+ void operator=(const buffer&) = delete;
- template <typename, size_t, size_t, unsigned long long>
- friend struct detail::format_arg_store;
+ auto begin() noexcept -> T* { return ptr_; }
+ auto end() noexcept -> T* { return ptr_ + size_; }
- basic_format_arg(const detail::named_arg_info<char_type>* args, size_t size)
- : value_(args, size) {}
+ auto begin() const noexcept -> const T* { return ptr_; }
+ auto end() const noexcept -> const T* { return ptr_ + size_; }
- public:
- class handle {
- public:
- explicit handle(detail::custom_value<Context> custom) : custom_(custom) {}
+ /// Returns the size of this buffer.
+ constexpr auto size() const noexcept -> size_t { return size_; }
- void format(typename Context::parse_context_type& parse_ctx,
- Context& ctx) const {
- custom_.format(custom_.value, parse_ctx, ctx);
- }
+ /// Returns the capacity of this buffer.
+ constexpr auto capacity() const noexcept -> size_t { return capacity_; }
- private:
- detail::custom_value<Context> custom_;
- };
+ /// Returns a pointer to the buffer data (not null-terminated).
+ FMT_CONSTEXPR auto data() noexcept -> T* { return ptr_; }
+ FMT_CONSTEXPR auto data() const noexcept -> const T* { return ptr_; }
- constexpr basic_format_arg() : type_(detail::type::none_type) {}
+ /// Clears this buffer.
+ FMT_CONSTEXPR void clear() { size_ = 0; }
- constexpr explicit operator bool() const noexcept {
- return type_ != detail::type::none_type;
+ // Tries resizing the buffer to contain `count` elements. If T is a POD type
+ // the new elements may not be initialized.
+ FMT_CONSTEXPR void try_resize(size_t count) {
+ try_reserve(count);
+ size_ = min_of(count, capacity_);
}
- auto type() const -> detail::type { return type_; }
+ // Tries increasing the buffer capacity to `new_capacity`. It can increase the
+ // capacity by a smaller amount than requested but guarantees there is space
+ // for at least one additional element either by increasing the capacity or by
+ // flushing the buffer if it is full.
+ FMT_CONSTEXPR void try_reserve(size_t new_capacity) {
+ if (new_capacity > capacity_) grow_(*this, new_capacity);
+ }
- auto is_integral() const -> bool { return detail::is_integral_type(type_); }
- auto is_arithmetic() const -> bool {
- return detail::is_arithmetic_type(type_);
+ FMT_CONSTEXPR void push_back(const T& value) {
+ try_reserve(size_ + 1);
+ ptr_[size_++] = value;
}
- /**
- * Visits an argument dispatching to the appropriate visit method based on
- * the argument type. For example, if the argument type is `double` then
- * `vis(value)` will be called with the value of type `double`.
- */
- template <typename Visitor>
- FMT_CONSTEXPR auto visit(Visitor&& vis) -> decltype(vis(0)) {
- switch (type_) {
- case detail::type::none_type:
- break;
- case detail::type::int_type:
- return vis(value_.int_value);
- case detail::type::uint_type:
- return vis(value_.uint_value);
- case detail::type::long_long_type:
- return vis(value_.long_long_value);
- case detail::type::ulong_long_type:
- return vis(value_.ulong_long_value);
- case detail::type::int128_type:
- return vis(detail::convert_for_visit(value_.int128_value));
- case detail::type::uint128_type:
- return vis(detail::convert_for_visit(value_.uint128_value));
- case detail::type::bool_type:
- return vis(value_.bool_value);
- case detail::type::char_type:
- return vis(value_.char_value);
- case detail::type::float_type:
- return vis(value_.float_value);
- case detail::type::double_type:
- return vis(value_.double_value);
- case detail::type::long_double_type:
- return vis(value_.long_double_value);
- case detail::type::cstring_type:
- return vis(value_.string.data);
- case detail::type::string_type:
- using sv = basic_string_view<typename Context::char_type>;
- return vis(sv(value_.string.data, value_.string.size));
- case detail::type::pointer_type:
- return vis(value_.pointer);
- case detail::type::custom_type:
- return vis(typename basic_format_arg<Context>::handle(value_.custom));
+ /// Appends data to the end of the buffer.
+ template <typename U>
+// Workaround for MSVC2019 to fix error C2893: Failed to specialize function
+// template 'void fmt::v11::detail::buffer<T>::append(const U *,const U *)'.
+#if !FMT_MSC_VERSION || FMT_MSC_VERSION >= 1940
+ FMT_CONSTEXPR20
+#endif
+ void
+ append(const U* begin, const U* end) {
+ while (begin != end) {
+ auto size = size_;
+ auto free_cap = capacity_ - size;
+ auto count = to_unsigned(end - begin);
+ if (free_cap < count) {
+ grow_(*this, size + count);
+ size = size_;
+ free_cap = capacity_ - size;
+ count = count < free_cap ? count : free_cap;
+ }
+ // A loop is faster than memcpy on small sizes.
+ T* out = ptr_ + size;
+ for (size_t i = 0; i < count; ++i) out[i] = begin[i];
+ size_ += count;
+ begin += count;
}
- return vis(monostate());
}
- auto format_custom(const char_type* parse_begin,
- typename Context::parse_context_type& parse_ctx,
- Context& ctx) -> bool {
- if (type_ != detail::type::custom_type) return false;
- parse_ctx.advance_to(parse_begin);
- value_.custom.format(value_.custom.value, parse_ctx, ctx);
- return true;
+ template <typename Idx> FMT_CONSTEXPR auto operator[](Idx index) -> T& {
+ return ptr_[index];
+ }
+ template <typename Idx>
+ FMT_CONSTEXPR auto operator[](Idx index) const -> const T& {
+ return ptr_[index];
}
};
-template <typename Visitor, typename Context>
-FMT_DEPRECATED FMT_CONSTEXPR auto visit_format_arg(
- Visitor&& vis, const basic_format_arg<Context>& arg) -> decltype(vis(0)) {
- return arg.visit(static_cast<Visitor&&>(vis));
-}
+struct buffer_traits {
+ constexpr explicit buffer_traits(size_t) {}
+ constexpr auto count() const -> size_t { return 0; }
+ constexpr auto limit(size_t size) const -> size_t { return size; }
+};
+
+class fixed_buffer_traits {
+ private:
+ size_t count_ = 0;
+ size_t limit_;
-/**
- * A view of a collection of formatting arguments. To avoid lifetime issues it
- * should only be used as a parameter type in type-erased functions such as
- * `vformat`:
- *
- * void vlog(fmt::string_view fmt, fmt::format_args args); // OK
- * fmt::format_args args = fmt::make_format_args(); // Dangling reference
- */
-template <typename Context> class basic_format_args {
public:
- using size_type = int;
- using format_arg = basic_format_arg<Context>;
+ constexpr explicit fixed_buffer_traits(size_t limit) : limit_(limit) {}
+ constexpr auto count() const -> size_t { return count_; }
+ FMT_CONSTEXPR auto limit(size_t size) -> size_t {
+ size_t n = limit_ > count_ ? limit_ - count_ : 0;
+ count_ += size;
+ return min_of(size, n);
+ }
+};
+// A buffer that writes to an output iterator when flushed.
+template <typename OutputIt, typename T, typename Traits = buffer_traits>
+class iterator_buffer : public Traits, public buffer<T> {
private:
- // A descriptor that contains information about formatting arguments.
- // If the number of arguments is less or equal to max_packed_args then
- // argument types are passed in the descriptor. This reduces binary code size
- // per formatting function call.
- unsigned long long desc_;
- union {
- // If is_packed() returns true then argument values are stored in values_;
- // otherwise they are stored in args_. This is done to improve cache
- // locality and reduce compiled code size since storing larger objects
- // may require more code (at least on x86-64) even if the same amount of
- // data is actually copied to stack. It saves ~10% on the bloat test.
- const detail::value<Context>* values_;
- const format_arg* args_;
- };
+ OutputIt out_;
+ enum { buffer_size = 256 };
+ T data_[buffer_size];
- constexpr auto is_packed() const -> bool {
- return (desc_ & detail::is_unpacked_bit) == 0;
- }
- constexpr auto has_named_args() const -> bool {
- return (desc_ & detail::has_named_args_bit) != 0;
+ static FMT_CONSTEXPR void grow(buffer<T>& buf, size_t) {
+ if (buf.size() == buffer_size) static_cast<iterator_buffer&>(buf).flush();
}
- FMT_CONSTEXPR auto type(int index) const -> detail::type {
- int shift = index * detail::packed_arg_bits;
- unsigned int mask = (1 << detail::packed_arg_bits) - 1;
- return static_cast<detail::type>((desc_ >> shift) & mask);
+ void flush() {
+ auto size = this->size();
+ this->clear();
+ const T* begin = data_;
+ const T* end = begin + this->limit(size);
+ while (begin != end) *out_++ = *begin++;
}
public:
- constexpr basic_format_args() : desc_(0), args_(nullptr) {}
-
- /// Constructs a `basic_format_args` object from `format_arg_store`.
- template <size_t NUM_ARGS, size_t NUM_NAMED_ARGS, unsigned long long DESC,
- FMT_ENABLE_IF(NUM_ARGS <= detail::max_packed_args)>
- constexpr FMT_ALWAYS_INLINE basic_format_args(
- const detail::format_arg_store<Context, NUM_ARGS, NUM_NAMED_ARGS, DESC>&
- store)
- : desc_(DESC), values_(store.args + (NUM_NAMED_ARGS != 0 ? 1 : 0)) {}
-
- template <size_t NUM_ARGS, size_t NUM_NAMED_ARGS, unsigned long long DESC,
- FMT_ENABLE_IF(NUM_ARGS > detail::max_packed_args)>
- constexpr basic_format_args(
- const detail::format_arg_store<Context, NUM_ARGS, NUM_NAMED_ARGS, DESC>&
- store)
- : desc_(DESC), args_(store.args + (NUM_NAMED_ARGS != 0 ? 1 : 0)) {}
+ explicit iterator_buffer(OutputIt out, size_t n = buffer_size)
+ : Traits(n), buffer<T>(grow, data_, 0, buffer_size), out_(out) {}
+ iterator_buffer(iterator_buffer&& other) noexcept
+ : Traits(other),
+ buffer<T>(grow, data_, 0, buffer_size),
+ out_(other.out_) {}
+ ~iterator_buffer() {
+ // Don't crash if flush fails during unwinding.
+ FMT_TRY { flush(); }
+ FMT_CATCH(...) {}
+ }
- /// Constructs a `basic_format_args` object from `dynamic_format_arg_store`.
- constexpr basic_format_args(const dynamic_format_arg_store<Context>& store)
- : desc_(store.get_types()), args_(store.data()) {}
+ auto out() -> OutputIt {
+ flush();
+ return out_;
+ }
+ auto count() const -> size_t { return Traits::count() + this->size(); }
+};
- /// Constructs a `basic_format_args` object from a dynamic list of arguments.
- constexpr basic_format_args(const format_arg* args, int count)
- : desc_(detail::is_unpacked_bit | detail::to_unsigned(count)),
- args_(args) {}
+template <typename T>
+class iterator_buffer<T*, T, fixed_buffer_traits> : public fixed_buffer_traits,
+ public buffer<T> {
+ private:
+ T* out_;
+ enum { buffer_size = 256 };
+ T data_[buffer_size];
- /// Returns the argument with the specified id.
- FMT_CONSTEXPR auto get(int id) const -> format_arg {
- format_arg arg;
- if (!is_packed()) {
- if (id < max_size()) arg = args_[id];
- return arg;
- }
- if (static_cast<unsigned>(id) >= detail::max_packed_args) return arg;
- arg.type_ = type(id);
- if (arg.type_ == detail::type::none_type) return arg;
- arg.value_ = values_[id];
- return arg;
+ static FMT_CONSTEXPR void grow(buffer<T>& buf, size_t) {
+ if (buf.size() == buf.capacity())
+ static_cast<iterator_buffer&>(buf).flush();
}
- template <typename Char>
- auto get(basic_string_view<Char> name) const -> format_arg {
- int id = get_id(name);
- return id >= 0 ? get(id) : format_arg();
+ void flush() {
+ size_t n = this->limit(this->size());
+ if (this->data() == out_) {
+ out_ += n;
+ this->set(data_, buffer_size);
+ }
+ this->clear();
}
- template <typename Char>
- FMT_CONSTEXPR auto get_id(basic_string_view<Char> name) const -> int {
- if (!has_named_args()) return -1;
- const auto& named_args =
- (is_packed() ? values_[-1] : args_[-1].value_).named_args;
- for (size_t i = 0; i < named_args.size; ++i) {
- if (named_args.data[i].name == name) return named_args.data[i].id;
+ public:
+ explicit iterator_buffer(T* out, size_t n = buffer_size)
+ : fixed_buffer_traits(n), buffer<T>(grow, out, 0, n), out_(out) {}
+ iterator_buffer(iterator_buffer&& other) noexcept
+ : fixed_buffer_traits(other),
+ buffer<T>(static_cast<iterator_buffer&&>(other)),
+ out_(other.out_) {
+ if (this->data() != out_) {
+ this->set(data_, buffer_size);
+ this->clear();
}
- return -1;
}
+ ~iterator_buffer() { flush(); }
- auto max_size() const -> int {
- unsigned long long max_packed = detail::max_packed_args;
- return static_cast<int>(is_packed() ? max_packed
- : desc_ & ~detail::is_unpacked_bit);
+ auto out() -> T* {
+ flush();
+ return out_;
+ }
+ auto count() const -> size_t {
+ return fixed_buffer_traits::count() + this->size();
}
};
-// A formatting context.
-class context {
- private:
- appender out_;
- basic_format_args<context> args_;
- detail::locale_ref loc_;
-
+template <typename T> class iterator_buffer<T*, T> : public buffer<T> {
public:
- /// The character type for the output.
- using char_type = char;
+ explicit iterator_buffer(T* out, size_t = 0)
+ : buffer<T>([](buffer<T>&, size_t) {}, out, 0, ~size_t()) {}
- using iterator = appender;
- using format_arg = basic_format_arg<context>;
- using parse_context_type = basic_format_parse_context<char>;
- template <typename T> using formatter_type = formatter<T, char>;
-
- /// Constructs a `basic_format_context` object. References to the arguments
- /// are stored in the object so make sure they have appropriate lifetimes.
- FMT_CONSTEXPR context(iterator out, basic_format_args<context> ctx_args,
- detail::locale_ref loc = {})
- : out_(out), args_(ctx_args), loc_(loc) {}
- context(context&&) = default;
- context(const context&) = delete;
- void operator=(const context&) = delete;
+ auto out() -> T* { return &*this->end(); }
+};
+
+template <typename Container>
+class container_buffer : public buffer<typename Container::value_type> {
+ private:
+ using value_type = typename Container::value_type;
- FMT_CONSTEXPR auto arg(int id) const -> format_arg { return args_.get(id); }
- auto arg(string_view name) -> format_arg { return args_.get(name); }
- FMT_CONSTEXPR auto arg_id(string_view name) -> int {
- return args_.get_id(name);
+ static FMT_CONSTEXPR void grow(buffer<value_type>& buf, size_t capacity) {
+ auto& self = static_cast<container_buffer&>(buf);
+ self.container.resize(capacity);
+ self.set(&self.container[0], capacity);
}
- auto args() const -> const basic_format_args<context>& { return args_; }
- // Returns an iterator to the beginning of the output range.
- FMT_CONSTEXPR auto out() -> iterator { return out_; }
+ public:
+ Container& container;
- // Advances the begin iterator to `it`.
- void advance_to(iterator) {}
+ explicit container_buffer(Container& c)
+ : buffer<value_type>(grow, c.size()), container(c) {}
+};
+
+// A buffer that writes to a container with the contiguous storage.
+template <typename OutputIt>
+class iterator_buffer<
+ OutputIt,
+ enable_if_t<is_back_insert_iterator<OutputIt>::value &&
+ is_contiguous<typename OutputIt::container_type>::value,
+ typename OutputIt::container_type::value_type>>
+ : public container_buffer<typename OutputIt::container_type> {
+ private:
+ using base = container_buffer<typename OutputIt::container_type>;
- FMT_CONSTEXPR auto locale() -> detail::locale_ref { return loc_; }
+ public:
+ explicit iterator_buffer(typename OutputIt::container_type& c) : base(c) {}
+ explicit iterator_buffer(OutputIt out, size_t = 0)
+ : base(get_container(out)) {}
+
+ auto out() -> OutputIt { return OutputIt(this->container); }
};
-template <typename OutputIt, typename Char> class generic_context;
+// A buffer that counts the number of code units written discarding the output.
+template <typename T = char> class counting_buffer : public buffer<T> {
+ private:
+ enum { buffer_size = 256 };
+ T data_[buffer_size];
+ size_t count_ = 0;
-// Longer aliases for C++20 compatibility.
-template <typename OutputIt, typename Char>
-using basic_format_context =
- conditional_t<std::is_same<OutputIt, appender>::value, context,
- generic_context<OutputIt, Char>>;
-using format_context = context;
+ static FMT_CONSTEXPR void grow(buffer<T>& buf, size_t) {
+ if (buf.size() != buffer_size) return;
+ static_cast<counting_buffer&>(buf).count_ += buf.size();
+ buf.clear();
+ }
-template <typename Char>
-using buffered_context = basic_format_context<basic_appender<Char>, Char>;
+ public:
+ FMT_CONSTEXPR counting_buffer() : buffer<T>(grow, data_, 0, buffer_size) {}
-template <typename T, typename Char = char>
-using is_formattable = bool_constant<!std::is_base_of<
- detail::unformattable, decltype(detail::arg_mapper<buffered_context<Char>>()
- .map(std::declval<T&>()))>::value>;
+ constexpr auto count() const noexcept -> size_t {
+ return count_ + this->size();
+ }
+};
-#if FMT_USE_CONCEPTS
-template <typename T, typename Char = char>
-concept formattable = is_formattable<remove_reference_t<T>, Char>::value;
-#endif
+template <typename T>
+struct is_back_insert_iterator<basic_appender<T>> : std::true_type {};
+
+template <typename OutputIt, typename InputIt, typename = void>
+struct has_back_insert_iterator_container_append : std::false_type {};
+template <typename OutputIt, typename InputIt>
+struct has_back_insert_iterator_container_append<
+ OutputIt, InputIt,
+ void_t<decltype(get_container(std::declval<OutputIt>())
+ .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 {};
-/**
- * Constructs an object that stores references to arguments and can be
- * implicitly converted to `format_args`. `Context` can be omitted in which case
- * it defaults to `format_context`. See `arg` for lifetime considerations.
- */
-// Take arguments by lvalue references to avoid some lifetime issues, e.g.
-// auto args = make_format_args(std::string());
-template <typename Context = format_context, typename... T,
- size_t NUM_ARGS = sizeof...(T),
- size_t NUM_NAMED_ARGS = detail::count_named_args<T...>(),
- unsigned long long DESC = detail::make_descriptor<Context, T...>(),
- FMT_ENABLE_IF(NUM_NAMED_ARGS == 0)>
-constexpr FMT_ALWAYS_INLINE auto make_format_args(T&... args)
- -> detail::format_arg_store<Context, NUM_ARGS, 0, DESC> {
- return {{detail::make_arg<NUM_ARGS <= detail::max_packed_args, Context>(
- args)...}};
-}
-
-#ifndef FMT_DOC
-template <typename Context = format_context, typename... T,
- size_t NUM_NAMED_ARGS = detail::count_named_args<T...>(),
- unsigned long long DESC =
- detail::make_descriptor<Context, T...>() |
- static_cast<unsigned long long>(detail::has_named_args_bit),
- FMT_ENABLE_IF(NUM_NAMED_ARGS != 0)>
-constexpr auto make_format_args(T&... args)
- -> detail::format_arg_store<Context, sizeof...(T), NUM_NAMED_ARGS, DESC> {
- return {args...};
+// 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&&
+ has_back_insert_iterator_container_append<
+ OutputIt, InputIt>::value)>
+FMT_CONSTEXPR20 auto copy(InputIt begin, InputIt end, OutputIt out)
+ -> OutputIt {
+ get_container(out).append(begin, end);
+ return out;
}
-#endif
-/**
- * Returns a named argument to be used in a formatting function.
- * It should only be used in a call to a formatting function or
- * `dynamic_format_arg_store::push_back`.
- *
- * **Example**:
- *
- * fmt::print("The answer is {answer}.", fmt::arg("answer", 42));
- */
-template <typename Char, typename T>
-inline auto arg(const Char* name, const T& arg) -> detail::named_arg<Char, T> {
- static_assert(!detail::is_named_arg<T>(), "nested named arguments");
- return {name, arg};
+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 {
+ auto& c = get_container(out);
+ c.insert(c.end(), begin, end);
+ return out;
}
-FMT_END_EXPORT
-/// An alias for `basic_format_args<format_context>`.
-// A separate type would result in shorter symbols but break ABI compatibility
-// between clang and gcc on ARM (#1919).
-FMT_EXPORT using format_args = basic_format_args<format_context>;
-
-// We cannot use enum classes as bit fields because of a gcc bug, so we put them
-// in namespaces instead (https://gcc.gnu.org/bugzilla/show_bug.cgi?id=61414).
-// Additionally, if an underlying type is specified, older gcc incorrectly warns
-// that the type is too small. Both bugs are fixed in gcc 9.3.
-#if FMT_GCC_VERSION && FMT_GCC_VERSION < 903
-# define FMT_ENUM_UNDERLYING_TYPE(type)
-#else
-# define FMT_ENUM_UNDERLYING_TYPE(type) : type
-#endif
-namespace align {
-enum type FMT_ENUM_UNDERLYING_TYPE(unsigned char){none, left, right, center,
- numeric};
-}
-using align_t = align::type;
-namespace sign {
-enum type FMT_ENUM_UNDERLYING_TYPE(unsigned char){none, minus, plus, space};
+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_CONSTEXPR auto copy(InputIt begin, InputIt end, OutputIt out) -> OutputIt {
+ while (begin != end) *out++ = static_cast<T>(*begin++);
+ return out;
}
-using sign_t = sign::type;
-namespace detail {
-
-template <typename Char>
-using unsigned_char = typename conditional_t<std::is_integral<Char>::value,
- std::make_unsigned<Char>,
- type_identity<unsigned>>::type;
-
-// Character (code unit) type is erased to prevent template bloat.
-struct fill_t {
- private:
- enum { max_size = 4 };
- char data_[max_size] = {' '};
- unsigned char size_ = 1;
+template <typename T, typename V, typename OutputIt>
+FMT_CONSTEXPR auto copy(basic_string_view<V> s, OutputIt out) -> OutputIt {
+ return copy<T>(s.begin(), s.end(), out);
+}
- public:
- template <typename Char>
- FMT_CONSTEXPR void operator=(basic_string_view<Char> s) {
- auto size = s.size();
- size_ = static_cast<unsigned char>(size);
- if (size == 1) {
- unsigned uchar = static_cast<unsigned_char<Char>>(s[0]);
- data_[0] = static_cast<char>(uchar);
- data_[1] = static_cast<char>(uchar >> 8);
- return;
- }
- FMT_ASSERT(size <= max_size, "invalid fill");
- for (size_t i = 0; i < size; ++i) data_[i] = static_cast<char>(s[i]);
- }
+template <typename It, typename Enable = std::true_type>
+struct is_buffer_appender : std::false_type {};
+template <typename It>
+struct is_buffer_appender<
+ It, bool_constant<
+ is_back_insert_iterator<It>::value &&
+ std::is_base_of<buffer<typename It::container_type::value_type>,
+ typename It::container_type>::value>>
+ : std::true_type {};
- FMT_CONSTEXPR void operator=(char c) {
- data_[0] = c;
- size_ = 1;
- }
+// Maps an output iterator to a buffer.
+template <typename T, typename OutputIt,
+ FMT_ENABLE_IF(!is_buffer_appender<OutputIt>::value)>
+auto get_buffer(OutputIt out) -> iterator_buffer<OutputIt, T> {
+ return iterator_buffer<OutputIt, T>(out);
+}
+template <typename T, typename OutputIt,
+ FMT_ENABLE_IF(is_buffer_appender<OutputIt>::value)>
+auto get_buffer(OutputIt out) -> buffer<T>& {
+ return get_container(out);
+}
- constexpr auto size() const -> size_t { return size_; }
+template <typename Buf, typename OutputIt>
+auto get_iterator(Buf& buf, OutputIt) -> decltype(buf.out()) {
+ return buf.out();
+}
+template <typename T, typename OutputIt>
+auto get_iterator(buffer<T>&, OutputIt out) -> OutputIt {
+ return out;
+}
- template <typename Char> constexpr auto get() const -> Char {
- using uchar = unsigned char;
- return static_cast<Char>(static_cast<uchar>(data_[0]) |
- (static_cast<uchar>(data_[1]) << 8));
- }
+// This type is intentionally undefined, only used for errors.
+template <typename T, typename Char> struct type_is_unformattable_for;
- template <typename Char, FMT_ENABLE_IF(std::is_same<Char, char>::value)>
- constexpr auto data() const -> const Char* {
- return data_;
- }
- template <typename Char, FMT_ENABLE_IF(!std::is_same<Char, char>::value)>
- constexpr auto data() const -> const Char* {
- return nullptr;
- }
+template <typename Char> struct string_value {
+ const Char* data;
+ size_t size;
+ auto str() const -> basic_string_view<Char> { return {data, size}; }
};
-} // namespace detail
-
-enum class presentation_type : unsigned char {
- // Common specifiers:
- none = 0,
- debug = 1, // '?'
- string = 2, // 's' (string, bool)
-
- // Integral, bool and character specifiers:
- dec = 3, // 'd'
- hex, // 'x' or 'X'
- oct, // 'o'
- bin, // 'b' or 'B'
- chr, // 'c'
-
- // String and pointer specifiers:
- pointer = 3, // 'p'
- // Floating-point specifiers:
- exp = 1, // 'e' or 'E' (1 since there is no FP debug presentation)
- fixed, // 'f' or 'F'
- general, // 'g' or 'G'
- hexfloat // 'a' or 'A'
+template <typename Context> struct custom_value {
+ using char_type = typename Context::char_type;
+ void* value;
+ void (*format)(void* arg, parse_context<char_type>& parse_ctx, Context& ctx);
};
-// Format specifiers for built-in and string types.
-struct format_specs {
- int width;
- int precision;
- presentation_type type;
- align_t align : 4;
- sign_t sign : 3;
- bool upper : 1; // An uppercase version e.g. 'X' for 'x'.
- bool alt : 1; // Alternate form ('#').
- bool localized : 1;
- detail::fill_t fill;
-
- constexpr format_specs()
- : width(0),
- precision(-1),
- type(presentation_type::none),
- align(align::none),
- sign(sign::none),
- upper(false),
- alt(false),
- localized(false) {}
+template <typename Char> struct named_arg_value {
+ const named_arg_info<Char>* data;
+ size_t size;
};
-namespace detail {
-
-enum class arg_id_kind { none, index, name };
+struct custom_tag {};
-// An argument reference.
-template <typename Char> struct arg_ref {
- FMT_CONSTEXPR arg_ref() : kind(arg_id_kind::none), val() {}
+#if !FMT_BUILTIN_TYPES
+# define FMT_BUILTIN , monostate
+#else
+# define FMT_BUILTIN
+#endif
- FMT_CONSTEXPR explicit arg_ref(int index)
- : kind(arg_id_kind::index), val(index) {}
- FMT_CONSTEXPR explicit arg_ref(basic_string_view<Char> name)
- : kind(arg_id_kind::name), val(name) {}
+// A formatting argument value.
+template <typename Context> class value {
+ public:
+ using char_type = typename Context::char_type;
- FMT_CONSTEXPR auto operator=(int idx) -> arg_ref& {
- kind = arg_id_kind::index;
- val.index = idx;
- return *this;
- }
+ union {
+ monostate no_value;
+ int int_value;
+ unsigned uint_value;
+ long long long_long_value;
+ unsigned long long ulong_long_value;
+ int128_opt int128_value;
+ uint128_opt uint128_value;
+ bool bool_value;
+ char_type char_value;
+ float float_value;
+ double double_value;
+ long double long_double_value;
+ const void* pointer;
+ string_value<char_type> string;
+ custom_value<Context> custom;
+ named_arg_value<char_type> named_args;
+ };
- arg_id_kind kind;
- union value {
- FMT_CONSTEXPR value(int idx = 0) : index(idx) {}
- FMT_CONSTEXPR value(basic_string_view<Char> n) : name(n) {}
+ constexpr FMT_INLINE value() : no_value() {}
+ constexpr FMT_INLINE value(signed char x) : int_value(x) {}
+ constexpr FMT_INLINE value(unsigned char x FMT_BUILTIN) : uint_value(x) {}
+ constexpr FMT_INLINE value(signed short x) : int_value(x) {}
+ constexpr FMT_INLINE value(unsigned short x FMT_BUILTIN) : uint_value(x) {}
+ constexpr FMT_INLINE value(int x) : int_value(x) {}
+ constexpr FMT_INLINE value(unsigned x FMT_BUILTIN) : uint_value(x) {}
+ FMT_CONSTEXPR FMT_INLINE value(long x FMT_BUILTIN) : value(long_type(x)) {}
+ FMT_CONSTEXPR FMT_INLINE value(unsigned long x FMT_BUILTIN)
+ : value(ulong_type(x)) {}
+ constexpr FMT_INLINE value(long long x FMT_BUILTIN) : long_long_value(x) {}
+ constexpr FMT_INLINE value(unsigned long long x FMT_BUILTIN)
+ : ulong_long_value(x) {}
+ FMT_INLINE value(int128_opt x FMT_BUILTIN) : int128_value(x) {}
+ FMT_INLINE value(uint128_opt x FMT_BUILTIN) : uint128_value(x) {}
+ constexpr FMT_INLINE value(bool x FMT_BUILTIN) : bool_value(x) {}
+
+ template <int N>
+ constexpr FMT_INLINE value(bitint<N> x FMT_BUILTIN) : long_long_value(x) {
+ static_assert(N <= 64, "unsupported _BitInt");
+ }
+ template <int N>
+ constexpr FMT_INLINE value(ubitint<N> x FMT_BUILTIN) : ulong_long_value(x) {
+ static_assert(N <= 64, "unsupported _BitInt");
+ }
+
+ 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,
+ "mixing character types is disallowed");
+ }
+
+ constexpr FMT_INLINE value(float x FMT_BUILTIN) : float_value(x) {}
+ constexpr FMT_INLINE value(double x FMT_BUILTIN) : double_value(x) {}
+ FMT_INLINE value(long double x FMT_BUILTIN) : long_double_value(x) {}
+
+ FMT_CONSTEXPR FMT_INLINE value(char_type* x FMT_BUILTIN) {
+ string.data = x;
+ if (is_constant_evaluated()) string.size = 0;
+ }
+ FMT_CONSTEXPR FMT_INLINE value(const char_type* x FMT_BUILTIN) {
+ string.data = x;
+ if (is_constant_evaluated()) string.size = 0;
+ }
+ template <typename T, typename C = char_t<T>,
+ FMT_ENABLE_IF(!std::is_pointer<T>::value)>
+ FMT_CONSTEXPR value(const T& x FMT_BUILTIN) {
+ static_assert(std::is_same<C, char_type>::value,
+ "mixing character types is disallowed");
+ auto sv = to_string_view(x);
+ string.data = sv.data();
+ string.size = sv.size();
+ }
+ FMT_INLINE value(void* x FMT_BUILTIN) : pointer(x) {}
+ FMT_INLINE value(const void* x FMT_BUILTIN) : pointer(x) {}
+ FMT_INLINE value(volatile void* x FMT_BUILTIN)
+ : pointer(const_cast<const void*>(x)) {}
+ FMT_INLINE value(const volatile void* x FMT_BUILTIN)
+ : pointer(const_cast<const void*>(x)) {}
+ FMT_INLINE value(nullptr_t) : pointer(nullptr) {}
+
+ template <typename T, FMT_ENABLE_IF(std::is_pointer<T>::value ||
+ std::is_member_pointer<T>::value)>
+ value(const T&) {
+ // Formatting of arbitrary pointers is disallowed. If you want to format a
+ // pointer cast it to `void*` or `const void*`. In particular, this forbids
+ // formatting of `[const] volatile char*` printed as bool by iostreams.
+ static_assert(sizeof(T) == 0,
+ "formatting of non-void pointers is disallowed");
+ }
+
+ template <typename T, FMT_ENABLE_IF(use_format_as<T>::value)>
+ value(const T& x) : value(format_as(x)) {}
+ template <typename T, FMT_ENABLE_IF(use_format_as_member<T>::value)>
+ value(const T& x) : value(formatter<T>::format_as(x)) {}
- int index;
- basic_string_view<Char> name;
- } val;
-};
+ template <typename T, FMT_ENABLE_IF(is_named_arg<T>::value)>
+ value(const T& named_arg) : value(named_arg.value) {}
-// Format specifiers with width and precision resolved at formatting rather
-// than parsing time to allow reusing the same parsed specifiers with
-// different sets of arguments (precompilation of format strings).
-template <typename Char = char> struct dynamic_format_specs : format_specs {
- arg_ref<Char> width_ref;
- arg_ref<Char> precision_ref;
-};
+ template <typename T,
+ FMT_ENABLE_IF(use_formatter<T>::value || !FMT_BUILTIN_TYPES)>
+ FMT_CONSTEXPR20 FMT_INLINE value(T& x) : value(x, custom_tag()) {}
-// Converts a character to ASCII. Returns '\0' on conversion failure.
-template <typename Char, FMT_ENABLE_IF(std::is_integral<Char>::value)>
-constexpr auto to_ascii(Char c) -> char {
- return c <= 0xff ? static_cast<char>(c) : '\0';
-}
+ FMT_ALWAYS_INLINE value(const named_arg_info<char_type>* args, size_t size)
+ : named_args{args, size} {}
-// Returns the number of code units in a code point or 1 on error.
-template <typename Char>
-FMT_CONSTEXPR auto code_point_length(const Char* begin) -> int {
- if (const_check(sizeof(Char) != 1)) return 1;
- auto c = static_cast<unsigned char>(*begin);
- return static_cast<int>((0x3a55000000000000ull >> (2 * (c >> 3))) & 0x3) + 1;
-}
+ private:
+ template <typename T, FMT_ENABLE_IF(has_formatter<T, char_type>())>
+ FMT_CONSTEXPR value(T& x, custom_tag) {
+ using value_type = remove_const_t<T>;
+ // T may overload operator& e.g. std::vector<bool>::reference in libc++.
+ if (!is_constant_evaluated()) {
+ custom.value =
+ const_cast<char*>(&reinterpret_cast<const volatile char&>(x));
+ } else {
+ custom.value = nullptr;
+#if defined(__cpp_if_constexpr)
+ if constexpr (std::is_same<decltype(&x), remove_reference_t<T>*>::value)
+ custom.value = const_cast<value_type*>(&x);
+#endif
+ }
+ custom.format = format_custom<value_type>;
+ }
-// Return the result via the out param to workaround gcc bug 77539.
-template <bool IS_CONSTEXPR, typename T, typename Ptr = const T*>
-FMT_CONSTEXPR auto find(Ptr first, Ptr last, T value, Ptr& out) -> bool {
- for (out = first; out != last; ++out) {
- if (*out == value) return true;
+ template <typename T, FMT_ENABLE_IF(!has_formatter<T, char_type>())>
+ FMT_CONSTEXPR value(const T&, custom_tag) {
+ // Cannot format an argument; to make type T formattable provide a
+ // formatter<T> specialization: https://fmt.dev/latest/api.html#udt.
+ type_is_unformattable_for<T, char_type> _;
}
- return false;
-}
-template <>
-inline auto find<false, char>(const char* first, const char* last, char value,
- const char*& out) -> bool {
- out =
- static_cast<const char*>(memchr(first, value, to_unsigned(last - first)));
- return out != nullptr;
-}
+ // Formats an argument of a custom type, such as a user-defined class.
+ template <typename T>
+ static void format_custom(void* arg, parse_context<char_type>& parse_ctx,
+ Context& ctx) {
+ 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>;
+ // format must be const for compatibility with std::format and compilation.
+ const auto& cf = f;
+ ctx.advance_to(cf.format(*static_cast<qualified_type*>(arg), ctx));
+ }
+};
-// Parses the range [begin, end) as an unsigned integer. This function assumes
-// that the range is non-empty and the first character is a digit.
-template <typename Char>
-FMT_CONSTEXPR auto parse_nonnegative_int(const Char*& begin, const Char* end,
- int error_value) noexcept -> int {
- FMT_ASSERT(begin != end && '0' <= *begin && *begin <= '9', "");
- unsigned value = 0, prev = 0;
- auto p = begin;
- do {
- prev = value;
- value = value * 10 + unsigned(*p - '0');
- ++p;
- } while (p != end && '0' <= *p && *p <= '9');
- auto num_digits = p - begin;
- begin = p;
- int digits10 = static_cast<int>(sizeof(int) * CHAR_BIT * 3 / 10);
- if (num_digits <= digits10) return static_cast<int>(value);
- // Check for overflow.
- unsigned max = INT_MAX;
- return num_digits == digits10 + 1 &&
- prev * 10ull + unsigned(p[-1] - '0') <= max
- ? static_cast<int>(value)
- : error_value;
+enum { packed_arg_bits = 4 };
+// Maximum number of arguments with packed types.
+enum { max_packed_args = 62 / packed_arg_bits };
+enum : unsigned long long { is_unpacked_bit = 1ULL << 63 };
+enum : unsigned long long { has_named_args_bit = 1ULL << 62 };
+
+template <typename It, typename T, typename Enable = void>
+struct is_output_iterator : std::false_type {};
+
+template <> struct is_output_iterator<appender, char> : std::true_type {};
+
+template <typename It, typename T>
+struct is_output_iterator<
+ It, T,
+ enable_if_t<std::is_assignable<decltype(*std::declval<decay_t<It>&>()++),
+ T>::value>> : std::true_type {};
+
+template <typename> constexpr auto encode_types() -> unsigned long long {
+ return 0;
}
-FMT_CONSTEXPR inline auto parse_align(char c) -> align_t {
- switch (c) {
- case '<':
- return align::left;
- case '>':
- return align::right;
- case '^':
- return align::center;
- }
- return align::none;
+template <typename Context, typename First, typename... T>
+constexpr auto encode_types() -> unsigned long long {
+ return static_cast<unsigned>(stored_type_constant<First, Context>::value) |
+ (encode_types<Context, T...>() << packed_arg_bits);
}
-template <typename Char> constexpr auto is_name_start(Char c) -> bool {
- return ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || c == '_';
+template <typename Context, typename... T, size_t NUM_ARGS = sizeof...(T)>
+constexpr auto make_descriptor() -> unsigned long long {
+ return NUM_ARGS <= max_packed_args ? encode_types<Context, T...>()
+ : is_unpacked_bit | NUM_ARGS;
}
-template <typename Char, typename Handler>
-FMT_CONSTEXPR auto do_parse_arg_id(const Char* begin, const Char* end,
- Handler&& handler) -> const Char* {
- Char c = *begin;
- if (c >= '0' && c <= '9') {
- int index = 0;
- if (c != '0')
- index = parse_nonnegative_int(begin, end, INT_MAX);
- else
- ++begin;
- if (begin == end || (*begin != '}' && *begin != ':'))
- report_error("invalid format string");
- else
- handler.on_index(index);
- return begin;
+template <typename Context, int NUM_ARGS>
+using arg_t = conditional_t<NUM_ARGS <= max_packed_args, value<Context>,
+ basic_format_arg<Context>>;
+
+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[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)
+ : args{{named_args, NUM_NAMED_ARGS}, values...} {
+ int arg_index = 0, named_arg_index = 0;
+ FMT_APPLY_VARIADIC(
+ init_named_arg(named_args, arg_index, named_arg_index, values));
}
- if (!is_name_start(c)) {
- report_error("invalid format string");
- return begin;
+
+ named_arg_store(named_arg_store&& rhs) {
+ args[0] = {named_args, NUM_NAMED_ARGS};
+ for (size_t i = 1; i < sizeof(args) / sizeof(*args); ++i)
+ args[i] = rhs.args[i];
+ for (size_t i = 0; i < NUM_NAMED_ARGS; ++i)
+ named_args[i] = rhs.named_args[i];
}
- auto it = begin;
- do {
- ++it;
- } while (it != end && (is_name_start(*it) || ('0' <= *it && *it <= '9')));
- handler.on_name({begin, to_unsigned(it - begin)});
- return it;
-}
-template <typename Char, typename Handler>
-FMT_CONSTEXPR auto parse_arg_id(const Char* begin, const Char* end,
- Handler&& handler) -> const Char* {
- FMT_ASSERT(begin != end, "");
- Char c = *begin;
- if (c != '}' && c != ':') return do_parse_arg_id(begin, end, handler);
- handler.on_auto();
- return begin;
-}
+ named_arg_store(const 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; }
+};
-template <typename Char> struct dynamic_spec_id_handler {
- basic_format_parse_context<Char>& ctx;
- arg_ref<Char>& ref;
+// An array of references to arguments. It can be implicitly converted to
+// `basic_format_args` for passing into type-erased formatting functions
+// such as `vformat`. It is a plain struct to reduce binary size in debug mode.
+template <typename Context, int NUM_ARGS, int NUM_NAMED_ARGS,
+ unsigned long long DESC>
+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<size_t>(1, NUM_ARGS)],
+ named_arg_store<Context, NUM_ARGS, NUM_NAMED_ARGS, DESC>>;
+ type args;
+};
- FMT_CONSTEXPR void on_auto() {
- int id = ctx.next_arg_id();
- ref = arg_ref<Char>(id);
- ctx.check_dynamic_spec(id);
- }
- FMT_CONSTEXPR void on_index(int id) {
- ref = arg_ref<Char>(id);
- ctx.check_arg_id(id);
- ctx.check_dynamic_spec(id);
+// TYPE can be different from type_constant<T>, e.g. for __float128.
+template <typename T, typename Char, type TYPE> struct native_formatter {
+ private:
+ dynamic_format_specs<Char> specs_;
+
+ public:
+ using nonlocking = void;
+
+ FMT_CONSTEXPR auto parse(parse_context<Char>& ctx) -> const Char* {
+ if (ctx.begin() == ctx.end() || *ctx.begin() == '}') return ctx.begin();
+ auto end = parse_format_specs(ctx.begin(), ctx.end(), specs_, ctx, TYPE);
+ if (const_check(TYPE == type::char_type)) check_char_specs(specs_);
+ return end;
}
- FMT_CONSTEXPR void on_name(basic_string_view<Char> id) {
- ref = arg_ref<Char>(id);
- ctx.check_arg_id(id);
+
+ template <type U = TYPE,
+ FMT_ENABLE_IF(U == type::string_type || U == type::cstring_type ||
+ U == type::char_type)>
+ FMT_CONSTEXPR void set_debug_format(bool set = true) {
+ specs_.set_type(set ? presentation_type::debug : presentation_type::none);
}
+
+ FMT_PRAGMA_CLANG(diagnostic ignored "-Wundefined-inline")
+ template <typename FormatContext>
+ FMT_CONSTEXPR auto format(const T& val, FormatContext& ctx) const
+ -> decltype(ctx.out());
};
-// Parses [integer | "{" [arg_id] "}"].
+template <typename T, typename Enable = void>
+struct locking
+ : bool_constant<mapped_type_constant<T>::value == type::custom_type> {};
+template <typename T>
+struct locking<T, void_t<typename formatter<remove_cvref_t<T>>::nonlocking>>
+ : std::false_type {};
+
+template <typename T = int> FMT_CONSTEXPR inline auto is_locking() -> bool {
+ return locking<T>::value;
+}
+template <typename T1, typename T2, typename... Tail>
+FMT_CONSTEXPR inline auto is_locking() -> bool {
+ return locking<T1>::value || is_locking<T2, Tail...>();
+}
+
+FMT_API void vformat_to(buffer<char>& buf, string_view fmt, format_args args,
+ locale_ref loc = {});
+
+#if FMT_WIN32
+FMT_API void vprint_mojibake(FILE*, string_view, format_args, bool);
+#else // format_args is passed by reference since it is defined later.
+inline void vprint_mojibake(FILE*, string_view, const format_args&, bool) {}
+#endif
+} // namespace detail
+
+// The main public API.
+
template <typename Char>
-FMT_CONSTEXPR auto parse_dynamic_spec(const Char* begin, const Char* end,
- int& value, arg_ref<Char>& ref,
- basic_format_parse_context<Char>& ctx)
- -> const Char* {
- FMT_ASSERT(begin != end, "");
- if ('0' <= *begin && *begin <= '9') {
- int val = parse_nonnegative_int(begin, end, -1);
- if (val != -1)
- value = val;
- else
- report_error("number is too big");
- } else if (*begin == '{') {
- ++begin;
- auto handler = dynamic_spec_id_handler<Char>{ctx, ref};
- if (begin != end) begin = parse_arg_id(begin, end, handler);
- if (begin != end && *begin == '}') return ++begin;
- report_error("invalid format string");
+FMT_CONSTEXPR void parse_context<Char>::do_check_arg_id(int arg_id) {
+ // Argument id is only checked at compile time during parsing because
+ // formatting has its own validation.
+ if (detail::is_constant_evaluated() && use_constexpr_cast) {
+ auto ctx = static_cast<detail::compile_parse_context<Char>*>(this);
+ if (arg_id >= ctx->num_args()) report_error("argument not found");
}
- return begin;
}
template <typename Char>
-FMT_CONSTEXPR auto parse_precision(const Char* begin, const Char* end,
- int& value, arg_ref<Char>& ref,
- basic_format_parse_context<Char>& ctx)
- -> const Char* {
- ++begin;
- if (begin == end || *begin == '}') {
- report_error("invalid precision");
- return begin;
- }
- return parse_dynamic_spec(begin, end, value, ref, ctx);
+FMT_CONSTEXPR void parse_context<Char>::check_dynamic_spec(int arg_id) {
+ using detail::compile_parse_context;
+ if (detail::is_constant_evaluated() && use_constexpr_cast)
+ static_cast<compile_parse_context<Char>*>(this)->check_dynamic_spec(arg_id);
}
-enum class state { start, align, sign, hash, zero, width, precision, locale };
+FMT_BEGIN_EXPORT
-// Parses standard format specifiers.
-template <typename Char>
-FMT_CONSTEXPR auto parse_format_specs(const Char* begin, const Char* end,
- dynamic_format_specs<Char>& specs,
- basic_format_parse_context<Char>& ctx,
- type arg_type) -> const Char* {
- auto c = '\0';
- if (end - begin > 1) {
- auto next = to_ascii(begin[1]);
- c = parse_align(next) == align::none ? to_ascii(*begin) : '\0';
- } else {
- if (begin == end) return begin;
- c = to_ascii(*begin);
+// An output iterator that appends to a buffer. It is used instead of
+// back_insert_iterator to reduce symbol sizes and avoid <iterator> dependency.
+template <typename T> class basic_appender {
+ protected:
+ detail::buffer<T>* container;
+
+ public:
+ using container_type = detail::buffer<T>;
+
+ FMT_CONSTEXPR basic_appender(detail::buffer<T>& buf) : container(&buf) {}
+
+ FMT_CONSTEXPR20 auto operator=(T c) -> basic_appender& {
+ container->push_back(c);
+ return *this;
}
+ FMT_CONSTEXPR20 auto operator*() -> basic_appender& { return *this; }
+ FMT_CONSTEXPR20 auto operator++() -> basic_appender& { return *this; }
+ FMT_CONSTEXPR20 auto operator++(int) -> basic_appender { return *this; }
+};
- struct {
- state current_state = state::start;
- FMT_CONSTEXPR void operator()(state s, bool valid = true) {
- if (current_state >= s || !valid)
- report_error("invalid format specifier");
- current_state = s;
- }
- } enter_state;
+// A formatting argument. Context is a template parameter for the compiled API
+// where output can be unbuffered.
+template <typename Context> class basic_format_arg {
+ private:
+ detail::value<Context> value_;
+ detail::type type_;
- using pres = presentation_type;
- constexpr auto integral_set = sint_set | uint_set | bool_set | char_set;
- struct {
- const Char*& begin;
- dynamic_format_specs<Char>& specs;
- type arg_type;
+ friend class basic_format_args<Context>;
- FMT_CONSTEXPR auto operator()(pres pres_type, int set) -> const Char* {
- if (!in(arg_type, set)) {
- if (arg_type == type::none_type) return begin;
- report_error("invalid format specifier");
- }
- specs.type = pres_type;
- return begin + 1;
- }
- } parse_presentation_type{begin, specs, arg_type};
+ using char_type = typename Context::char_type;
- for (;;) {
- switch (c) {
- case '<':
- case '>':
- case '^':
- enter_state(state::align);
- specs.align = parse_align(c);
- ++begin;
- break;
- case '+':
- case '-':
- case ' ':
- if (arg_type == type::none_type) return begin;
- enter_state(state::sign, in(arg_type, sint_set | float_set));
- switch (c) {
- case '+':
- specs.sign = sign::plus;
- break;
- case '-':
- specs.sign = sign::minus;
- break;
- case ' ':
- specs.sign = sign::space;
- break;
- }
- ++begin;
- break;
- case '#':
- if (arg_type == type::none_type) return begin;
- enter_state(state::hash, is_arithmetic_type(arg_type));
- specs.alt = true;
- ++begin;
- break;
- case '0':
- enter_state(state::zero);
- if (!is_arithmetic_type(arg_type)) {
- if (arg_type == type::none_type) return begin;
- report_error("format specifier requires numeric argument");
- }
- if (specs.align == align::none) {
- // Ignore 0 if align is specified for compatibility with std::format.
- specs.align = align::numeric;
- specs.fill = '0';
- }
- ++begin;
- break;
- case '1':
- case '2':
- case '3':
- case '4':
- case '5':
- case '6':
- case '7':
- case '8':
- case '9':
- case '{':
- enter_state(state::width);
- begin = parse_dynamic_spec(begin, end, specs.width, specs.width_ref, ctx);
- break;
- case '.':
- if (arg_type == type::none_type) return begin;
- enter_state(state::precision,
- in(arg_type, float_set | string_set | cstring_set));
- begin = parse_precision(begin, end, specs.precision, specs.precision_ref,
- ctx);
- break;
- case 'L':
- if (arg_type == type::none_type) return begin;
- enter_state(state::locale, is_arithmetic_type(arg_type));
- specs.localized = true;
- ++begin;
- break;
- case 'd':
- return parse_presentation_type(pres::dec, integral_set);
- case 'X':
- specs.upper = true;
- FMT_FALLTHROUGH;
- case 'x':
- return parse_presentation_type(pres::hex, integral_set);
- case 'o':
- return parse_presentation_type(pres::oct, integral_set);
- case 'B':
- specs.upper = true;
- FMT_FALLTHROUGH;
- case 'b':
- return parse_presentation_type(pres::bin, integral_set);
- case 'E':
- specs.upper = true;
- FMT_FALLTHROUGH;
- case 'e':
- return parse_presentation_type(pres::exp, float_set);
- case 'F':
- specs.upper = true;
- FMT_FALLTHROUGH;
- case 'f':
- return parse_presentation_type(pres::fixed, float_set);
- case 'G':
- specs.upper = true;
- FMT_FALLTHROUGH;
- case 'g':
- return parse_presentation_type(pres::general, float_set);
- case 'A':
- specs.upper = true;
- FMT_FALLTHROUGH;
- case 'a':
- return parse_presentation_type(pres::hexfloat, float_set);
- case 'c':
- if (arg_type == type::bool_type) report_error("invalid format specifier");
- return parse_presentation_type(pres::chr, integral_set);
- case 's':
- return parse_presentation_type(pres::string,
- bool_set | string_set | cstring_set);
- case 'p':
- return parse_presentation_type(pres::pointer, pointer_set | cstring_set);
- case '?':
- return parse_presentation_type(pres::debug,
- char_set | string_set | cstring_set);
- case '}':
- return begin;
- default: {
- if (*begin == '}') return begin;
- // Parse fill and alignment.
- auto fill_end = begin + code_point_length(begin);
- if (end - fill_end <= 0) {
- report_error("invalid format specifier");
- return begin;
- }
- if (*begin == '{') {
- report_error("invalid fill character '{'");
- return begin;
- }
- auto align = parse_align(to_ascii(*fill_end));
- enter_state(state::align, align != align::none);
- specs.fill =
- basic_string_view<Char>(begin, to_unsigned(fill_end - begin));
- specs.align = align;
- begin = fill_end + 1;
- }
- }
- if (begin == end) return begin;
- c = to_ascii(*begin);
- }
-}
+ public:
+ class handle {
+ private:
+ detail::custom_value<Context> custom_;
+
+ public:
+ explicit handle(detail::custom_value<Context> custom) : custom_(custom) {}
-template <typename Char, typename Handler>
-FMT_CONSTEXPR auto parse_replacement_field(const Char* begin, const Char* end,
- Handler&& handler) -> const Char* {
- struct id_adapter {
- Handler& handler;
- int arg_id;
-
- FMT_CONSTEXPR void on_auto() { arg_id = handler.on_arg_id(); }
- FMT_CONSTEXPR void on_index(int id) { arg_id = handler.on_arg_id(id); }
- FMT_CONSTEXPR void on_name(basic_string_view<Char> id) {
- arg_id = handler.on_arg_id(id);
+ void format(parse_context<char_type>& parse_ctx, Context& ctx) const {
+ custom_.format(custom_.value, parse_ctx, ctx);
}
};
- ++begin;
- if (begin == end) return handler.on_error("invalid format string"), end;
- if (*begin == '}') {
- handler.on_replacement_field(handler.on_arg_id(), begin);
- } else if (*begin == '{') {
- handler.on_text(begin, begin + 1);
- } else {
- auto adapter = id_adapter{handler, 0};
- begin = parse_arg_id(begin, end, adapter);
- Char c = begin != end ? *begin : Char();
- if (c == '}') {
- handler.on_replacement_field(adapter.arg_id, begin);
- } else if (c == ':') {
- begin = handler.on_format_specs(adapter.arg_id, begin + 1, end);
- if (begin == end || *begin != '}')
- return handler.on_error("unknown format specifier"), end;
- } else {
- return handler.on_error("missing '}' in format string"), end;
- }
+ constexpr basic_format_arg() : type_(detail::type::none_type) {}
+ basic_format_arg(const detail::named_arg_info<char_type>* args, size_t size)
+ : value_(args, size) {}
+ template <typename T>
+ basic_format_arg(T&& val)
+ : value_(val), type_(detail::stored_type_constant<T, Context>::value) {}
+
+ constexpr explicit operator bool() const noexcept {
+ return type_ != detail::type::none_type;
}
- return begin + 1;
-}
+ auto type() const -> detail::type { return type_; }
-template <bool IS_CONSTEXPR, typename Char, typename Handler>
-FMT_CONSTEXPR void parse_format_string(basic_string_view<Char> format_str,
- Handler&& handler) {
- auto begin = format_str.data();
- auto end = begin + format_str.size();
- if (end - begin < 32) {
- // Use a simple loop instead of memchr for small strings.
- const Char* p = begin;
- while (p != end) {
- auto c = *p++;
- if (c == '{') {
- handler.on_text(begin, p - 1);
- begin = p = parse_replacement_field(p - 1, end, handler);
- } else if (c == '}') {
- if (p == end || *p != '}')
- return handler.on_error("unmatched '}' in format string");
- handler.on_text(begin, p);
- begin = ++p;
- }
- }
- handler.on_text(begin, end);
- return;
- }
- struct writer {
- FMT_CONSTEXPR void operator()(const Char* from, const Char* to) {
- if (from == to) return;
- for (;;) {
- const Char* p = nullptr;
- if (!find<IS_CONSTEXPR>(from, to, Char('}'), p))
- return handler_.on_text(from, to);
- ++p;
- if (p == to || *p != '}')
- return handler_.on_error("unmatched '}' in format string");
- handler_.on_text(from, p);
- from = p + 1;
- }
+ /**
+ * Visits an argument dispatching to the appropriate visit method based on
+ * the argument type. For example, if the argument type is `double` then
+ * `vis(value)` will be called with the value of type `double`.
+ */
+ template <typename Visitor>
+ FMT_CONSTEXPR FMT_INLINE auto visit(Visitor&& vis) const -> decltype(vis(0)) {
+ using detail::map;
+ switch (type_) {
+ case detail::type::none_type: break;
+ case detail::type::int_type: return vis(value_.int_value);
+ case detail::type::uint_type: return vis(value_.uint_value);
+ case detail::type::long_long_type: return vis(value_.long_long_value);
+ case detail::type::ulong_long_type: return vis(value_.ulong_long_value);
+ case detail::type::int128_type: return vis(map(value_.int128_value));
+ case detail::type::uint128_type: return vis(map(value_.uint128_value));
+ case detail::type::bool_type: return vis(value_.bool_value);
+ case detail::type::char_type: return vis(value_.char_value);
+ case detail::type::float_type: return vis(value_.float_value);
+ case detail::type::double_type: return vis(value_.double_value);
+ case detail::type::long_double_type: return vis(value_.long_double_value);
+ case detail::type::cstring_type: return vis(value_.string.data);
+ case detail::type::string_type: return vis(value_.string.str());
+ case detail::type::pointer_type: return vis(value_.pointer);
+ case detail::type::custom_type: return vis(handle(value_.custom));
}
- Handler& handler_;
- } write = {handler};
- while (begin != end) {
- // Doing two passes with memchr (one for '{' and another for '}') is up to
- // 2.5x faster than the naive one-pass implementation on big format strings.
- const Char* p = begin;
- if (*begin != '{' && !find<IS_CONSTEXPR>(begin + 1, end, Char('{'), p))
- return write(begin, end);
- write(begin, p);
- begin = parse_replacement_field(p, end, handler);
+ return vis(monostate());
}
-}
-template <typename T, bool = is_named_arg<T>::value> struct strip_named_arg {
- using type = T;
-};
-template <typename T> struct strip_named_arg<T, true> {
- using type = remove_cvref_t<decltype(T::value)>;
+ auto format_custom(const char_type* parse_begin,
+ parse_context<char_type>& parse_ctx, Context& ctx)
+ -> bool {
+ if (type_ != detail::type::custom_type) return false;
+ parse_ctx.advance_to(parse_begin);
+ value_.custom.format(value_.custom.value, parse_ctx, ctx);
+ return true;
+ }
};
-template <typename T, typename ParseContext>
-FMT_VISIBILITY("hidden") // Suppress an ld warning on macOS (#3769).
-FMT_CONSTEXPR auto parse_format_specs(ParseContext& ctx)
- -> decltype(ctx.begin()) {
- using char_type = typename ParseContext::char_type;
- using context = buffered_context<char_type>;
- using mapped_type = conditional_t<
- mapped_type_constant<T, context>::value != type::custom_type,
- decltype(arg_mapper<context>().map(std::declval<const T&>())),
- typename strip_named_arg<T>::type>;
-#if defined(__cpp_if_constexpr)
- if constexpr (std::is_default_constructible<
- formatter<mapped_type, char_type>>::value) {
- return formatter<mapped_type, char_type>().parse(ctx);
- } else {
- type_is_unformattable_for<T, char_type> _;
- return ctx.begin();
- }
-#else
- return formatter<mapped_type, char_type>().parse(ctx);
-#endif
-}
+/**
+ * A view of a collection of formatting arguments. To avoid lifetime issues it
+ * should only be used as a parameter type in type-erased functions such as
+ * `vformat`:
+ *
+ * void vlog(fmt::string_view fmt, fmt::format_args args); // OK
+ * fmt::format_args args = fmt::make_format_args(); // Dangling reference
+ */
+template <typename Context> class basic_format_args {
+ private:
+ // A descriptor that contains information about formatting arguments.
+ // If the number of arguments is less or equal to max_packed_args then
+ // argument types are passed in the descriptor. This reduces binary code size
+ // per formatting function call.
+ unsigned long long desc_;
+ union {
+ // If is_packed() returns true then argument values are stored in values_;
+ // otherwise they are stored in args_. This is done to improve cache
+ // locality and reduce compiled code size since storing larger objects
+ // may require more code (at least on x86-64) even if the same amount of
+ // data is actually copied to stack. It saves ~10% on the bloat test.
+ const detail::value<Context>* values_;
+ const basic_format_arg<Context>* args_;
+ };
-// Checks char specs and returns true iff the presentation type is char-like.
-FMT_CONSTEXPR inline auto check_char_specs(const format_specs& specs) -> bool {
- if (specs.type != presentation_type::none &&
- specs.type != presentation_type::chr &&
- specs.type != presentation_type::debug) {
- return false;
+ constexpr auto is_packed() const -> bool {
+ return (desc_ & detail::is_unpacked_bit) == 0;
+ }
+ constexpr auto has_named_args() const -> bool {
+ return (desc_ & detail::has_named_args_bit) != 0;
}
- if (specs.align == align::numeric || specs.sign != sign::none || specs.alt)
- report_error("invalid format specifier for char");
- return true;
-}
-#if FMT_USE_NONTYPE_TEMPLATE_ARGS
-template <int N, typename T, typename... Args, typename Char>
-constexpr auto get_arg_index_by_name(basic_string_view<Char> name) -> int {
- if constexpr (is_statically_named_arg<T>()) {
- if (name == T::name) return N;
+ FMT_CONSTEXPR auto type(int index) const -> detail::type {
+ int shift = index * detail::packed_arg_bits;
+ unsigned mask = (1 << detail::packed_arg_bits) - 1;
+ return static_cast<detail::type>((desc_ >> shift) & mask);
}
- if constexpr (sizeof...(Args) > 0)
- return get_arg_index_by_name<N + 1, Args...>(name);
- (void)name; // Workaround an MSVC bug about "unused" parameter.
- return -1;
-}
-#endif
-template <typename... Args, typename Char>
-FMT_CONSTEXPR auto get_arg_index_by_name(basic_string_view<Char> name) -> int {
-#if FMT_USE_NONTYPE_TEMPLATE_ARGS
- if constexpr (sizeof...(Args) > 0)
- return get_arg_index_by_name<0, Args...>(name);
-#endif
- (void)name;
- return -1;
-}
+ template <int NUM_ARGS, int NUM_NAMED_ARGS, unsigned long long DESC>
+ using store =
+ detail::format_arg_store<Context, NUM_ARGS, NUM_NAMED_ARGS, DESC>;
-template <typename Char, typename... Args> class format_string_checker {
- private:
- using parse_context_type = compile_parse_context<Char>;
- static constexpr int num_args = sizeof...(Args);
+ public:
+ using format_arg = basic_format_arg<Context>;
- // Format specifier parsing function.
- // In the future basic_format_parse_context will replace compile_parse_context
- // here and will use is_constant_evaluated and downcasting to access the data
- // needed for compile-time checks: https://godbolt.org/z/GvWzcTjh1.
- using parse_func = const Char* (*)(parse_context_type&);
+ constexpr basic_format_args() : desc_(0), args_(nullptr) {}
- type types_[num_args > 0 ? static_cast<size_t>(num_args) : 1];
- parse_context_type context_;
- parse_func parse_funcs_[num_args > 0 ? static_cast<size_t>(num_args) : 1];
+ /// Constructs a `basic_format_args` object from `format_arg_store`.
+ template <int NUM_ARGS, int NUM_NAMED_ARGS, unsigned long long DESC,
+ FMT_ENABLE_IF(NUM_ARGS <= detail::max_packed_args)>
+ constexpr FMT_ALWAYS_INLINE basic_format_args(
+ const store<NUM_ARGS, NUM_NAMED_ARGS, DESC>& s)
+ : desc_(DESC | (NUM_NAMED_ARGS != 0 ? +detail::has_named_args_bit : 0)),
+ values_(s.args) {}
- public:
- explicit FMT_CONSTEXPR format_string_checker(basic_string_view<Char> fmt)
- : types_{mapped_type_constant<Args, buffered_context<Char>>::value...},
- context_(fmt, num_args, types_),
- parse_funcs_{&parse_format_specs<Args, parse_context_type>...} {}
+ template <int NUM_ARGS, int NUM_NAMED_ARGS, unsigned long long DESC,
+ FMT_ENABLE_IF(NUM_ARGS > detail::max_packed_args)>
+ constexpr basic_format_args(const store<NUM_ARGS, NUM_NAMED_ARGS, DESC>& s)
+ : desc_(DESC | (NUM_NAMED_ARGS != 0 ? +detail::has_named_args_bit : 0)),
+ args_(s.args) {}
- FMT_CONSTEXPR void on_text(const Char*, const Char*) {}
+ /// Constructs a `basic_format_args` object from a dynamic list of arguments.
+ constexpr basic_format_args(const format_arg* args, int count,
+ bool has_named = false)
+ : desc_(detail::is_unpacked_bit | detail::to_unsigned(count) |
+ (has_named ? +detail::has_named_args_bit : 0)),
+ args_(args) {}
- FMT_CONSTEXPR auto on_arg_id() -> int { return context_.next_arg_id(); }
- FMT_CONSTEXPR auto on_arg_id(int id) -> int {
- return context_.check_arg_id(id), id;
- }
- FMT_CONSTEXPR auto on_arg_id(basic_string_view<Char> id) -> int {
-#if FMT_USE_NONTYPE_TEMPLATE_ARGS
- auto index = get_arg_index_by_name<Args...>(id);
- if (index < 0) on_error("named argument is not found");
- return index;
-#else
- (void)id;
- on_error("compile-time checks for named arguments require C++20 support");
- return 0;
-#endif
+ /// Returns the argument with the specified id.
+ FMT_CONSTEXPR auto get(int id) const -> format_arg {
+ auto arg = format_arg();
+ if (!is_packed()) {
+ if (id < max_size()) arg = args_[id];
+ return arg;
+ }
+ if (static_cast<unsigned>(id) >= detail::max_packed_args) return arg;
+ arg.type_ = type(id);
+ if (arg.type_ != detail::type::none_type) arg.value_ = values_[id];
+ return arg;
}
- FMT_CONSTEXPR void on_replacement_field(int id, const Char* begin) {
- on_format_specs(id, begin, begin); // Call parse() on empty specs.
+ template <typename Char>
+ auto get(basic_string_view<Char> name) const -> format_arg {
+ int id = get_id(name);
+ return id >= 0 ? get(id) : format_arg();
}
- FMT_CONSTEXPR auto on_format_specs(int id, const Char* begin, const Char*)
- -> const Char* {
- context_.advance_to(begin);
- // id >= 0 check is a workaround for gcc 10 bug (#2065).
- return id >= 0 && id < num_args ? parse_funcs_[id](context_) : begin;
+ template <typename Char>
+ FMT_CONSTEXPR auto get_id(basic_string_view<Char> name) const -> int {
+ if (!has_named_args()) return -1;
+ const auto& named_args =
+ (is_packed() ? values_[-1] : args_[-1].value_).named_args;
+ for (size_t i = 0; i < named_args.size; ++i) {
+ if (named_args.data[i].name == name) return named_args.data[i].id;
+ }
+ return -1;
}
- FMT_NORETURN FMT_CONSTEXPR void on_error(const char* message) {
- report_error(message);
+ auto max_size() const -> int {
+ unsigned long long max_packed = detail::max_packed_args;
+ return static_cast<int>(is_packed() ? max_packed
+ : desc_ & ~detail::is_unpacked_bit);
}
};
-// A base class for compile-time strings.
-struct compile_string {};
+// A formatting context.
+class context {
+ private:
+ appender out_;
+ format_args args_;
+ FMT_NO_UNIQUE_ADDRESS locale_ref loc_;
-template <typename S>
-using is_compile_string = std::is_base_of<compile_string, S>;
+ public:
+ using char_type = char; ///< The character type for the output.
+ using iterator = appender;
+ using format_arg = basic_format_arg<context>;
+ enum { builtin_types = FMT_BUILTIN_TYPES };
-// Reports a compile-time error if S is not a valid format string.
-template <typename..., typename S, FMT_ENABLE_IF(!is_compile_string<S>::value)>
-FMT_ALWAYS_INLINE void check_format_string(const S&) {
-#ifdef FMT_ENFORCE_COMPILE_STRING
- static_assert(is_compile_string<S>::value,
- "FMT_ENFORCE_COMPILE_STRING requires all format strings to use "
- "FMT_STRING.");
-#endif
-}
-template <typename... Args, typename S,
- FMT_ENABLE_IF(is_compile_string<S>::value)>
-void check_format_string(S format_str) {
- using char_t = typename S::char_type;
- FMT_CONSTEXPR auto s = basic_string_view<char_t>(format_str);
- using checker = format_string_checker<char_t, remove_cvref_t<Args>...>;
- FMT_CONSTEXPR bool error = (parse_format_string<true>(s, checker(s)), true);
- ignore_unused(error);
-}
+ /// 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, locale_ref loc = {})
+ : out_(out), args_(args), loc_(loc) {}
+ context(context&&) = default;
+ context(const context&) = delete;
+ void operator=(const context&) = delete;
-// Report truncation to prevent silent data loss.
-inline void report_truncation(bool truncated) {
- if (truncated) report_error("output is truncated");
-}
+ FMT_CONSTEXPR auto arg(int id) const -> format_arg { return args_.get(id); }
+ inline auto arg(string_view name) const -> format_arg {
+ return args_.get(name);
+ }
+ FMT_CONSTEXPR auto arg_id(string_view name) const -> int {
+ return args_.get_id(name);
+ }
+ auto args() const -> const format_args& { return args_; }
-// Use vformat_args and avoid type_identity to keep symbols short and workaround
-// a GCC <= 4.8 bug.
-template <typename Char = char> struct vformat_args {
- using type = basic_format_args<buffered_context<Char>>;
-};
-template <> struct vformat_args<char> {
- using type = format_args;
+ // Returns an iterator to the beginning of the output range.
+ FMT_CONSTEXPR auto out() const -> iterator { return out_; }
+
+ // Advances the begin iterator to `it`.
+ FMT_CONSTEXPR void advance_to(iterator) {}
+
+ FMT_CONSTEXPR auto locale() const -> locale_ref { return loc_; }
};
-template <typename Char>
-void vformat_to(buffer<Char>& buf, basic_string_view<Char> fmt,
- typename vformat_args<Char>::type args, locale_ref loc = {});
+template <typename Char = char> struct runtime_format_string {
+ basic_string_view<Char> str;
+};
-FMT_API void vprint_mojibake(FILE*, string_view, format_args, bool = false);
-#ifndef _WIN32
-inline void vprint_mojibake(FILE*, string_view, format_args, bool) {}
-#endif
+/**
+ * Creates a runtime format string.
+ *
+ * **Example**:
+ *
+ * // Check format string at runtime instead of compile-time.
+ * fmt::print(fmt::runtime("{:d}"), "I am not a number");
+ */
+inline auto runtime(string_view s) -> runtime_format_string<> { return {{s}}; }
-template <typename T, typename Char, type TYPE> struct native_formatter {
+/// A compile-time format string. Use `format_string` in the public API to
+/// prevent type deduction.
+template <typename... T> struct fstring {
private:
- dynamic_format_specs<Char> specs_;
+ static constexpr int num_static_named_args =
+ detail::count_static_named_args<T...>();
- public:
- using nonlocking = void;
+ using checker = detail::format_string_checker<
+ char, static_cast<int>(sizeof...(T)), num_static_named_args,
+ num_static_named_args != detail::count_named_args<T...>()>;
- template <typename ParseContext>
- FMT_CONSTEXPR auto parse(ParseContext& ctx) -> const Char* {
- if (ctx.begin() == ctx.end() || *ctx.begin() == '}') return ctx.begin();
- auto end = parse_format_specs(ctx.begin(), ctx.end(), specs_, ctx, TYPE);
- if (const_check(TYPE == type::char_type)) check_char_specs(specs_);
- return end;
- }
+ using arg_pack = detail::arg_pack<T...>;
- template <type U = TYPE,
- FMT_ENABLE_IF(U == type::string_type || U == type::cstring_type ||
- U == type::char_type)>
- FMT_CONSTEXPR void set_debug_format(bool set = true) {
- specs_.type = set ? presentation_type::debug : presentation_type::none;
+ public:
+ string_view str;
+ using t = fstring;
+
+ // Reports a compile-time error if S is not a valid format string for T.
+ template <size_t N>
+ FMT_CONSTEVAL FMT_ALWAYS_INLINE fstring(const char (&s)[N]) : str(s, N - 1) {
+ using namespace detail;
+ 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()));
+#ifdef FMT_ENFORCE_COMPILE_STRING
+ static_assert(
+ FMT_USE_CONSTEVAL && sizeof(s) != 0,
+ "FMT_ENFORCE_COMPILE_STRING requires format strings to use FMT_STRING");
+#endif
}
-
- template <typename FormatContext>
- FMT_CONSTEXPR auto format(const T& val, FormatContext& ctx) const
- -> decltype(ctx.out());
+ template <typename S,
+ FMT_ENABLE_IF(std::is_convertible<const S&, string_view>::value)>
+ FMT_CONSTEVAL FMT_ALWAYS_INLINE fstring(const S& s) : str(s) {
+ auto sv = string_view(str);
+ if (FMT_USE_CONSTEVAL)
+ detail::parse_format_string<char>(sv, checker(sv, arg_pack()));
+#ifdef FMT_ENFORCE_COMPILE_STRING
+ static_assert(
+ FMT_USE_CONSTEVAL && sizeof(s) != 0,
+ "FMT_ENFORCE_COMPILE_STRING requires format strings to use FMT_STRING");
+#endif
+ }
+ template <typename S,
+ FMT_ENABLE_IF(std::is_base_of<detail::compile_string, S>::value&&
+ std::is_same<typename S::char_type, char>::value)>
+ FMT_ALWAYS_INLINE fstring(const S&) : str(S()) {
+ FMT_CONSTEXPR auto sv = string_view(S());
+ FMT_CONSTEXPR int unused =
+ (parse_format_string(sv, checker(sv, arg_pack())), 0);
+ detail::ignore_unused(unused);
+ }
+ fstring(runtime_format_string<> fmt) : str(fmt.str) {}
+
+ // Returning by reference generates better code in debug mode.
+ FMT_ALWAYS_INLINE operator const string_view&() const { return str; }
+ auto get() const -> string_view { return str; }
};
-} // namespace detail
-FMT_BEGIN_EXPORT
+template <typename... T> using format_string = typename fstring<T...>::t;
+
+template <typename T, typename Char = char>
+using is_formattable = bool_constant<!std::is_same<
+ detail::mapped_t<conditional_t<std::is_void<T>::value, int*, T>, Char>,
+ void>::value>;
+#ifdef __cpp_concepts
+template <typename T, typename Char = char>
+concept formattable = is_formattable<remove_reference_t<T>, Char>::value;
+#endif
// A formatter specialization for natively supported types.
template <typename T, typename Char>
: detail::native_formatter<T, Char, detail::type_constant<T, Char>::value> {
};
-template <typename Char = char> struct runtime_format_string {
- basic_string_view<Char> str;
-};
-
-/// A compile-time format string.
-template <typename Char, typename... Args> class basic_format_string {
- private:
- basic_string_view<Char> str_;
-
- public:
- template <
- typename S,
- FMT_ENABLE_IF(
- std::is_convertible<const S&, basic_string_view<Char>>::value ||
- (detail::is_compile_string<S>::value &&
- std::is_constructible<basic_string_view<Char>, const S&>::value))>
- FMT_CONSTEVAL FMT_ALWAYS_INLINE basic_format_string(const S& s) : str_(s) {
- static_assert(
- detail::count<
- (std::is_base_of<detail::view, remove_reference_t<Args>>::value &&
- std::is_reference<Args>::value)...>() == 0,
- "passing views as lvalues is disallowed");
-#if FMT_USE_CONSTEVAL
- if constexpr (detail::count_named_args<Args...>() ==
- detail::count_statically_named_args<Args...>()) {
- using checker =
- detail::format_string_checker<Char, remove_cvref_t<Args>...>;
- detail::parse_format_string<true>(str_, checker(s));
- }
-#else
- detail::check_format_string<Args...>(s);
-#endif
- }
- basic_format_string(runtime_format_string<Char> fmt) : str_(fmt.str) {}
+/**
+ * Constructs an object that stores references to arguments and can be
+ * implicitly converted to `format_args`. `Context` can be omitted in which case
+ * it defaults to `context`. See `arg` for lifetime considerations.
+ */
+// Take arguments by lvalue references to avoid some lifetime issues, e.g.
+// auto args = make_format_args(std::string());
+template <typename Context = context, typename... T,
+ int NUM_ARGS = sizeof...(T),
+ int NUM_NAMED_ARGS = detail::count_named_args<T...>(),
+ unsigned long long DESC = detail::make_descriptor<Context, T...>()>
+constexpr FMT_ALWAYS_INLINE auto make_format_args(T&... args)
+ -> detail::format_arg_store<Context, NUM_ARGS, NUM_NAMED_ARGS, DESC> {
+ // Suppress warnings for pathological types convertible to detail::value.
+ FMT_PRAGMA_GCC(diagnostic ignored "-Wconversion")
+ return {{args...}};
+}
- FMT_ALWAYS_INLINE operator basic_string_view<Char>() const { return str_; }
- auto get() const -> basic_string_view<Char> { return str_; }
-};
+template <typename... T>
+using vargs =
+ detail::format_arg_store<context, sizeof...(T),
+ detail::count_named_args<T...>(),
+ detail::make_descriptor<context, T...>()>;
-#if FMT_GCC_VERSION && FMT_GCC_VERSION < 409
-// Workaround broken conversion on older gcc.
-template <typename...> using format_string = string_view;
-inline auto runtime(string_view s) -> string_view { return s; }
-#else
-template <typename... Args>
-using format_string = basic_format_string<char, type_identity_t<Args>...>;
/**
- * Creates a runtime format string.
+ * Returns a named argument to be used in a formatting function.
+ * It should only be used in a call to a formatting function.
*
* **Example**:
*
- * // Check format string at runtime instead of compile-time.
- * fmt::print(fmt::runtime("{:d}"), "I am not a number");
+ * fmt::print("The answer is {answer}.", fmt::arg("answer", 42));
*/
-inline auto runtime(string_view s) -> runtime_format_string<> { return {{s}}; }
-#endif
+template <typename Char, typename T>
+inline auto arg(const Char* name, const T& arg) -> detail::named_arg<Char, T> {
+ return {name, arg};
+}
/// Formats a string and writes the output to `out`.
template <typename OutputIt,
char>::value)>
FMT_INLINE auto format_to(OutputIt&& out, format_string<T...> fmt, T&&... args)
-> remove_cvref_t<OutputIt> {
- return vformat_to(FMT_FWD(out), fmt, fmt::make_format_args(args...));
+ return vformat_to(out, fmt.str, vargs<T...>{{args...}});
}
template <typename OutputIt> struct format_to_n_result {
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, char>::value)>
FMT_INLINE auto format_to_n(OutputIt out, size_t n, format_string<T...> fmt,
T&&... args) -> format_to_n_result<OutputIt> {
- return vformat_to_n(out, n, fmt, fmt::make_format_args(args...));
+ return vformat_to_n(out, n, fmt.str, vargs<T...>{{args...}});
}
-template <typename OutputIt, typename Sentinel = OutputIt>
struct format_to_result {
- /// Iterator pointing to just after the last successful write in the range.
- OutputIt out;
+ /// Pointer to just after the last successful write in the array.
+ char* out;
/// Specifies if the output was truncated.
bool truncated;
- FMT_CONSTEXPR operator OutputIt&() & {
- detail::report_truncation(truncated);
- return out;
- }
- FMT_CONSTEXPR operator const OutputIt&() const& {
- detail::report_truncation(truncated);
+ FMT_CONSTEXPR operator char*() const {
+ // Report truncation to prevent silent data loss.
+ if (truncated) report_error("output is truncated");
return out;
}
- FMT_CONSTEXPR operator OutputIt&&() && {
- detail::report_truncation(truncated);
- return static_cast<OutputIt&&>(out);
- }
};
template <size_t N>
auto vformat_to(char (&out)[N], string_view fmt, format_args args)
- -> format_to_result<char*> {
+ -> format_to_result {
auto result = vformat_to_n(out, N, fmt, args);
return {result.out, result.size > N};
}
template <size_t N, typename... T>
FMT_INLINE auto format_to(char (&out)[N], format_string<T...> fmt, T&&... args)
- -> format_to_result<char*> {
- auto result = fmt::format_to_n(out, N, fmt, static_cast<T&&>(args)...);
+ -> format_to_result {
+ auto result = vformat_to_n(out, N, fmt.str, vargs<T...>{{args...}});
return {result.out, result.size > N};
}
FMT_NODISCARD FMT_INLINE auto formatted_size(format_string<T...> fmt,
T&&... args) -> size_t {
auto buf = detail::counting_buffer<>();
- detail::vformat_to<char>(buf, fmt, fmt::make_format_args(args...), {});
+ detail::vformat_to(buf, fmt.str, vargs<T...>{{args...}}, {});
return buf.count();
}
FMT_API void vprint(string_view fmt, format_args args);
FMT_API void vprint(FILE* f, string_view fmt, format_args args);
-FMT_API void vprint_buffered(FILE* f, string_view fmt, format_args args);
FMT_API void vprintln(FILE* f, string_view fmt, format_args args);
+FMT_API void vprint_buffered(FILE* f, string_view fmt, format_args args);
/**
* Formats `args` according to specifications in `fmt` and writes the output
*/
template <typename... T>
FMT_INLINE void print(format_string<T...> fmt, T&&... args) {
- const auto& vargs = fmt::make_format_args(args...);
- if (!detail::use_utf8()) return detail::vprint_mojibake(stdout, fmt, vargs);
- return detail::is_locking<T...>() ? vprint_buffered(stdout, fmt, vargs)
- : vprint(fmt, vargs);
+ vargs<T...> va = {{args...}};
+ if (detail::const_check(!detail::use_utf8))
+ return detail::vprint_mojibake(stdout, fmt.str, va, false);
+ return detail::is_locking<T...>() ? vprint_buffered(stdout, fmt.str, va)
+ : vprint(fmt.str, va);
}
/**
*/
template <typename... T>
FMT_INLINE void print(FILE* f, format_string<T...> fmt, T&&... args) {
- const auto& vargs = fmt::make_format_args(args...);
- if (!detail::use_utf8()) return detail::vprint_mojibake(f, fmt, vargs);
- return detail::is_locking<T...>() ? vprint_buffered(f, fmt, vargs)
- : vprint(f, fmt, vargs);
+ vargs<T...> va = {{args...}};
+ if (detail::const_check(!detail::use_utf8))
+ return detail::vprint_mojibake(f, fmt.str, va, false);
+ return detail::is_locking<T...>() ? vprint_buffered(f, fmt.str, va)
+ : vprint(f, fmt.str, va);
}
/// Formats `args` according to specifications in `fmt` and writes the output
/// to the file `f` followed by a newline.
template <typename... T>
FMT_INLINE void println(FILE* f, format_string<T...> fmt, T&&... args) {
- const auto& vargs = fmt::make_format_args(args...);
- return detail::use_utf8() ? vprintln(f, fmt, vargs)
- : detail::vprint_mojibake(f, fmt, vargs, true);
+ vargs<T...> va = {{args...}};
+ return detail::const_check(detail::use_utf8)
+ ? vprintln(f, fmt.str, va)
+ : detail::vprint_mojibake(f, fmt.str, va, true);
}
/// Formats `args` according to specifications in `fmt` and writes the output
return fmt::println(stdout, fmt, static_cast<T&&>(args)...);
}
+FMT_PRAGMA_GCC(diagnostic pop)
+FMT_PRAGMA_CLANG(diagnostic pop)
+FMT_PRAGMA_GCC(pop_options)
FMT_END_EXPORT
-FMT_GCC_PRAGMA("GCC pop_options")
FMT_END_NAMESPACE
#ifdef FMT_HEADER_ONLY
FMT_BEGIN_NAMESPACE
-// Check if std::chrono::local_t is available.
-#ifndef FMT_USE_LOCAL_TIME
-# ifdef __cpp_lib_chrono
-# define FMT_USE_LOCAL_TIME (__cpp_lib_chrono >= 201907L)
-# else
-# define FMT_USE_LOCAL_TIME 0
-# endif
-#endif
-
-// Check if std::chrono::utc_timestamp is available.
-#ifndef FMT_USE_UTC_TIME
-# ifdef __cpp_lib_chrono
-# define FMT_USE_UTC_TIME (__cpp_lib_chrono >= 201907L)
-# else
-# define FMT_USE_UTC_TIME 0
-# endif
-#endif
-
-// Enable tzset.
-#ifndef FMT_USE_TZSET
-// UWP doesn't provide _tzset.
-# if FMT_HAS_INCLUDE("winapifamily.h")
-# include <winapifamily.h>
-# endif
-# if defined(_WIN32) && (!defined(WINAPI_FAMILY) || \
- (WINAPI_FAMILY == WINAPI_FAMILY_DESKTOP_APP))
-# define FMT_USE_TZSET 1
-# else
-# define FMT_USE_TZSET 0
-# endif
-#endif
-
// Enable safe chrono durations, unless explicitly disabled.
#ifndef FMT_SAFE_DURATION_CAST
# define FMT_SAFE_DURATION_CAST 1
// 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 ==
return from;
}
-/// Safe duration cast between integral durations
-template <typename To, typename FromRep, typename FromPeriod,
- FMT_ENABLE_IF(std::is_integral<FromRep>::value),
- FMT_ENABLE_IF(std::is_integral<typename To::rep>::value)>
-auto safe_duration_cast(std::chrono::duration<FromRep, FromPeriod> from,
- int& ec) -> To {
- using From = std::chrono::duration<FromRep, FromPeriod>;
- ec = 0;
- // the basic idea is that we need to convert from count() in the from type
- // to count() in the To type, by multiplying it with this:
- struct Factor
- : std::ratio_divide<typename From::period, typename To::period> {};
-
- static_assert(Factor::num > 0, "num must be positive");
- static_assert(Factor::den > 0, "den must be positive");
-
- // the conversion is like this: multiply from.count() with Factor::num
- // /Factor::den and convert it to To::rep, all this without
- // overflow/underflow. let's start by finding a suitable type that can hold
- // both To, From and Factor::num
- using IntermediateRep =
- typename std::common_type<typename From::rep, typename To::rep,
- decltype(Factor::num)>::type;
-
- // safe conversion to IntermediateRep
- IntermediateRep count =
- lossless_integral_conversion<IntermediateRep>(from.count(), ec);
- if (ec) return {};
- // multiply with Factor::num without overflow or underflow
- if (detail::const_check(Factor::num != 1)) {
- const auto max1 = detail::max_value<IntermediateRep>() / Factor::num;
- if (count > max1) {
- ec = 1;
- return {};
- }
- const auto min1 =
- (std::numeric_limits<IntermediateRep>::min)() / Factor::num;
- if (detail::const_check(!std::is_unsigned<IntermediateRep>::value) &&
- count < min1) {
- ec = 1;
- return {};
- }
- count *= Factor::num;
- }
-
- if (detail::const_check(Factor::den != 1)) count /= Factor::den;
- auto tocount = lossless_integral_conversion<typename To::rep>(count, ec);
- return ec ? To() : To(tocount);
-}
-
/// Safe duration_cast between floating point durations
template <typename To, typename FromRep, typename FromPeriod,
FMT_ENABLE_IF(std::is_floating_point<FromRep>::value),
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:
} // namespace safe_duration_cast
#endif
+namespace detail {
+
+// Check if std::chrono::utc_time is available.
+#ifdef FMT_USE_UTC_TIME
+// Use the provided definition.
+#elif defined(__cpp_lib_chrono)
+# define FMT_USE_UTC_TIME (__cpp_lib_chrono >= 201907L)
+#else
+# define FMT_USE_UTC_TIME 0
+#endif
+#if FMT_USE_UTC_TIME
+using utc_clock = std::chrono::utc_clock;
+#else
+struct utc_clock {
+ template <typename T> void to_sys(T);
+};
+#endif
+
+// Check if std::chrono::local_time is available.
+#ifdef FMT_USE_LOCAL_TIME
+// Use the provided definition.
+#elif defined(__cpp_lib_chrono)
+# define FMT_USE_LOCAL_TIME (__cpp_lib_chrono >= 201907L)
+#else
+# define FMT_USE_LOCAL_TIME 0
+#endif
+#if FMT_USE_LOCAL_TIME
+using local_t = std::chrono::local_t;
+#else
+struct local_t {};
+#endif
+
+} // namespace detail
+
+template <typename Duration>
+using sys_time = std::chrono::time_point<std::chrono::system_clock, Duration>;
+
+template <typename Duration>
+using utc_time = std::chrono::time_point<detail::utc_clock, Duration>;
+
+template <class Duration>
+using local_time = std::chrono::time_point<detail::local_t, Duration>;
+
+namespace detail {
+
// Prevents expansion of a preceding token as a function-style macro.
// Usage: f FMT_NOMACRO()
#define FMT_NOMACRO
-namespace detail {
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<>(); }
// It is defined here and not in ostream.h because the latter has expensive
// includes.
-template <typename Streambuf> class formatbuf : public Streambuf {
+template <typename StreamBuf> class formatbuf : public StreamBuf {
private:
- using char_type = typename Streambuf::char_type;
- using streamsize = decltype(std::declval<Streambuf>().sputn(nullptr, 0));
- using int_type = typename Streambuf::int_type;
- using traits_type = typename Streambuf::traits_type;
+ using char_type = typename StreamBuf::char_type;
+ using streamsize = decltype(std::declval<StreamBuf>().sputn(nullptr, 0));
+ using int_type = typename StreamBuf::int_type;
+ using traits_type = typename StreamBuf::traits_type;
buffer<char_type>& buffer_;
}
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;
};
template <typename CodeUnit>
-void write_codecvt(codecvt_result<CodeUnit>& out, string_view in_buf,
+void write_codecvt(codecvt_result<CodeUnit>& out, string_view in,
const std::locale& loc) {
-#if FMT_CLANG_VERSION
-# pragma clang diagnostic push
-# pragma clang diagnostic ignored "-Wdeprecated"
+ FMT_PRAGMA_CLANG(diagnostic push)
+ FMT_PRAGMA_CLANG(diagnostic ignored "-Wdeprecated")
auto& f = std::use_facet<std::codecvt<CodeUnit, char, std::mbstate_t>>(loc);
-# pragma clang diagnostic pop
-#else
- auto& f = std::use_facet<std::codecvt<CodeUnit, char, std::mbstate_t>>(loc);
-#endif
+ FMT_PRAGMA_CLANG(diagnostic pop)
auto mb = std::mbstate_t();
const char* from_next = nullptr;
- auto result = f.in(mb, in_buf.begin(), in_buf.end(), from_next,
- std::begin(out.buf), std::end(out.buf), out.end);
+ auto result = f.in(mb, in.begin(), in.end(), from_next, std::begin(out.buf),
+ std::end(out.buf), out.end);
if (result != std::codecvt_base::ok)
FMT_THROW(format_error("failed to format time"));
}
template <typename OutputIt>
auto write_encoded_tm_str(OutputIt out, string_view in, const std::locale& loc)
-> OutputIt {
- if (detail::use_utf8() && loc != get_classic_locale()) {
+ if (const_check(detail::use_utf8) && loc != get_classic_locale()) {
// char16_t and char32_t codecvts are broken in MSVC (linkage errors) and
// gcc-4.
#if FMT_MSC_VERSION != 0 || \
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"));
+}
-template <
- typename To, typename FromRep, typename FromPeriod,
- FMT_ENABLE_IF(is_same_arithmetic_type<FromRep, typename To::rep>::value)>
-auto fmt_duration_cast(std::chrono::duration<FromRep, FromPeriod> from) -> To {
+// Cast one integral duration to another with an overflow check.
+template <typename To, typename FromRep, typename FromPeriod,
+ FMT_ENABLE_IF(std::is_integral<FromRep>::value&&
+ std::is_integral<typename To::rep>::value)>
+auto duration_cast(std::chrono::duration<FromRep, FromPeriod> from) -> To {
+#if !FMT_SAFE_DURATION_CAST
+ return std::chrono::duration_cast<To>(from);
+#else
+ // The conversion factor: to.count() == factor * from.count().
+ using factor = std::ratio_divide<FromPeriod, typename To::period>;
+
+ using common_rep = typename std::common_type<FromRep, typename To::rep,
+ decltype(factor::num)>::type;
+ common_rep count = from.count(); // This conversion is lossless.
+
+ // Multiply from.count() by factor and check for overflow.
+ if (const_check(factor::num != 1)) {
+ if (count > max_value<common_rep>() / factor::num) throw_duration_error();
+ const auto min = (std::numeric_limits<common_rep>::min)() / factor::num;
+ if (const_check(!std::is_unsigned<common_rep>::value) && count < min)
+ throw_duration_error();
+ 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));
+ if (ec) throw_duration_error();
+ return to;
+#endif
+}
+
+template <typename To, typename FromRep, typename FromPeriod,
+ FMT_ENABLE_IF(std::is_floating_point<FromRep>::value&&
+ 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;
To to = safe_duration_cast::safe_duration_cast<To>(from, ec);
- if (ec) FMT_THROW(format_error("cannot format duration"));
+ if (ec) throw_duration_error();
return to;
#else
// Standard duration cast, may overflow.
#endif
}
-template <
- typename To, typename FromRep, typename FromPeriod,
- FMT_ENABLE_IF(!is_same_arithmetic_type<FromRep, typename To::rep>::value)>
-auto fmt_duration_cast(std::chrono::duration<FromRep, FromPeriod> from) -> To {
- // Mixed integer <-> float cast is not supported by safe_duration_cast.
+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.
return std::chrono::duration_cast<To>(from);
}
template <typename Duration>
-auto to_time_t(
- std::chrono::time_point<std::chrono::system_clock, Duration> time_point)
- -> std::time_t {
+auto to_time_t(sys_time<Duration> time_point) -> std::time_t {
// Cannot use std::chrono::system_clock::to_time_t since this would first
// require a cast to std::chrono::system_clock::time_point, which could
// overflow.
- return fmt_duration_cast<std::chrono::duration<std::time_t>>(
+ return detail::duration_cast<std::chrono::duration<std::time_t>>(
time_point.time_since_epoch())
.count();
}
+
} // 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_;
-
- dispatcher(std::time_t t) : time_(t) {}
-
- auto run() -> bool {
- using namespace fmt::detail;
- return handle(localtime_r(&time_, &tm_));
- }
-
- auto handle(std::tm* tm) -> bool { return tm != nullptr; }
-
- auto handle(detail::null<>) -> bool {
- using namespace fmt::detail;
- return fallback(localtime_s(&tm_, &time_));
- }
-
- auto fallback(int res) -> bool { return res == 0; }
-
-#if !FMT_MSC_VERSION
- 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>
-inline auto localtime(std::chrono::local_time<Duration> time) -> std::tm {
- return localtime(
- detail::to_time_t(std::chrono::current_zone()->to_sys(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
std::time_t time_;
std::tm tm_;
- dispatcher(std::time_t t) : time_(t) {}
+ inline dispatcher(std::time_t t) : time_(t) {}
- auto run() -> bool {
+ inline auto run() -> bool {
using namespace fmt::detail;
return handle(gmtime_r(&time_, &tm_));
}
- auto handle(std::tm* tm) -> bool { return tm != nullptr; }
+ inline auto handle(std::tm* tm) -> bool { return tm != nullptr; }
- auto handle(detail::null<>) -> bool {
+ inline auto handle(detail::null<>) -> bool {
using namespace fmt::detail;
return fallback(gmtime_s(&tm_, &time_));
}
- auto fallback(int res) -> bool { return res == 0; }
+ inline auto fallback(int res) -> bool { return res == 0; }
#if !FMT_MSC_VERSION
- auto fallback(detail::null<>) -> bool {
+ inline auto fallback(detail::null<>) -> bool {
std::tm* tm = std::gmtime(&time_);
if (tm) tm_ = *tm;
return tm != nullptr;
}
template <typename Duration>
-inline auto gmtime(
- std::chrono::time_point<std::chrono::system_clock, Duration> time_point)
- -> std::tm {
+inline auto gmtime(sys_time<Duration> time_point) -> std::tm {
return gmtime(detail::to_time_t(time_point));
}
// 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);
if (std::is_same<Period, std::femto>::value) return "fs";
if (std::is_same<Period, std::pico>::value) return "ps";
if (std::is_same<Period, std::nano>::value) return "ns";
- if (std::is_same<Period, std::micro>::value) return "µs";
+ if (std::is_same<Period, std::micro>::value)
+ return detail::use_utf8 ? "µs" : "us";
if (std::is_same<Period, std::milli>::value) return "ms";
if (std::is_same<Period, std::centi>::value) return "cs";
if (std::is_same<Period, std::deci>::value) return "ds";
if (ptr == end) FMT_THROW(format_error("invalid format"));
c = *ptr++;
switch (c) {
- case '%':
- handler.on_text(ptr - 1, ptr);
- break;
+ case '%': handler.on_text(ptr - 1, ptr); break;
case 'n': {
const Char newline[] = {'\n'};
handler.on_text(newline, newline + 1);
break;
}
// Year:
- case 'Y':
- handler.on_year(numeric_system::standard);
- break;
- case 'y':
- handler.on_short_year(numeric_system::standard);
- break;
- case 'C':
- handler.on_century(numeric_system::standard);
- break;
- case 'G':
- handler.on_iso_week_based_year();
- break;
- case 'g':
- handler.on_iso_week_based_short_year();
- break;
+ case 'Y': handler.on_year(numeric_system::standard, pad); break;
+ case 'y': handler.on_short_year(numeric_system::standard); break;
+ case 'C': handler.on_century(numeric_system::standard); break;
+ case 'G': handler.on_iso_week_based_year(); break;
+ case 'g': handler.on_iso_week_based_short_year(); break;
// Day of the week:
- case 'a':
- handler.on_abbr_weekday();
- break;
- case 'A':
- handler.on_full_weekday();
- break;
- case 'w':
- handler.on_dec0_weekday(numeric_system::standard);
- break;
- case 'u':
- handler.on_dec1_weekday(numeric_system::standard);
- break;
+ case 'a': handler.on_abbr_weekday(); break;
+ case 'A': handler.on_full_weekday(); break;
+ case 'w': handler.on_dec0_weekday(numeric_system::standard); break;
+ case 'u': handler.on_dec1_weekday(numeric_system::standard); break;
// Month:
case 'b':
- case 'h':
- handler.on_abbr_month();
- break;
- case 'B':
- handler.on_full_month();
- break;
- case 'm':
- handler.on_dec_month(numeric_system::standard);
- break;
+ case 'h': handler.on_abbr_month(); break;
+ case 'B': handler.on_full_month(); break;
+ case 'm': handler.on_dec_month(numeric_system::standard, pad); break;
// Day of the year/month:
case 'U':
handler.on_dec0_week_of_year(numeric_system::standard, pad);
case 'W':
handler.on_dec1_week_of_year(numeric_system::standard, pad);
break;
- case 'V':
- handler.on_iso_week_of_year(numeric_system::standard, pad);
- break;
- case 'j':
- handler.on_day_of_year();
- break;
- case 'd':
- handler.on_day_of_month(numeric_system::standard, pad);
- break;
+ case 'V': handler.on_iso_week_of_year(numeric_system::standard, pad); break;
+ case 'j': handler.on_day_of_year(pad); break;
+ case 'd': handler.on_day_of_month(numeric_system::standard, pad); break;
case 'e':
handler.on_day_of_month(numeric_system::standard, pad_type::space);
break;
// Hour, minute, second:
- case 'H':
- handler.on_24_hour(numeric_system::standard, pad);
- break;
- case 'I':
- handler.on_12_hour(numeric_system::standard, pad);
- break;
- case 'M':
- handler.on_minute(numeric_system::standard, pad);
- break;
- case 'S':
- handler.on_second(numeric_system::standard, pad);
- break;
+ case 'H': handler.on_24_hour(numeric_system::standard, pad); break;
+ case 'I': handler.on_12_hour(numeric_system::standard, pad); break;
+ case 'M': handler.on_minute(numeric_system::standard, pad); break;
+ case 'S': handler.on_second(numeric_system::standard, pad); break;
// Other:
- case 'c':
- handler.on_datetime(numeric_system::standard);
- break;
- case 'x':
- handler.on_loc_date(numeric_system::standard);
- break;
- case 'X':
- handler.on_loc_time(numeric_system::standard);
- break;
- case 'D':
- handler.on_us_date();
- break;
- case 'F':
- handler.on_iso_date();
- break;
- case 'r':
- handler.on_12_hour_time();
- break;
- case 'R':
- handler.on_24_hour_time();
- break;
- case 'T':
- handler.on_iso_time();
- break;
- case 'p':
- handler.on_am_pm();
- break;
- case 'Q':
- handler.on_duration_value();
- break;
- case 'q':
- handler.on_duration_unit();
- break;
- case 'z':
- handler.on_utc_offset(numeric_system::standard);
- break;
- case 'Z':
- handler.on_tz_name();
- break;
+ case 'c': handler.on_datetime(numeric_system::standard); break;
+ case 'x': handler.on_loc_date(numeric_system::standard); break;
+ case 'X': handler.on_loc_time(numeric_system::standard); break;
+ case 'D': handler.on_us_date(); break;
+ case 'F': handler.on_iso_date(); break;
+ case 'r': handler.on_12_hour_time(); break;
+ case 'R': handler.on_24_hour_time(); break;
+ case 'T': handler.on_iso_time(); break;
+ case 'p': handler.on_am_pm(); break;
+ case 'Q': handler.on_duration_value(); break;
+ case 'q': handler.on_duration_unit(); break;
+ case 'z': handler.on_utc_offset(numeric_system::standard); break;
+ case 'Z': handler.on_tz_name(); break;
// Alternative representation:
case 'E': {
if (ptr == end) FMT_THROW(format_error("invalid format"));
c = *ptr++;
switch (c) {
- case 'Y':
- handler.on_year(numeric_system::alternative);
- break;
- case 'y':
- handler.on_offset_year();
- break;
- case 'C':
- handler.on_century(numeric_system::alternative);
- break;
- case 'c':
- handler.on_datetime(numeric_system::alternative);
- break;
- case 'x':
- handler.on_loc_date(numeric_system::alternative);
- break;
- case 'X':
- handler.on_loc_time(numeric_system::alternative);
- break;
- case 'z':
- handler.on_utc_offset(numeric_system::alternative);
- break;
- default:
- FMT_THROW(format_error("invalid format"));
+ case 'Y': handler.on_year(numeric_system::alternative, pad); break;
+ case 'y': handler.on_offset_year(); break;
+ case 'C': handler.on_century(numeric_system::alternative); break;
+ case 'c': handler.on_datetime(numeric_system::alternative); break;
+ case 'x': handler.on_loc_date(numeric_system::alternative); break;
+ case 'X': handler.on_loc_time(numeric_system::alternative); break;
+ case 'z': handler.on_utc_offset(numeric_system::alternative); break;
+ default: FMT_THROW(format_error("invalid format"));
}
break;
}
if (ptr == end) FMT_THROW(format_error("invalid format"));
c = *ptr++;
switch (c) {
- case 'y':
- handler.on_short_year(numeric_system::alternative);
- break;
- case 'm':
- handler.on_dec_month(numeric_system::alternative);
- break;
+ case 'y': handler.on_short_year(numeric_system::alternative); break;
+ case 'm': handler.on_dec_month(numeric_system::alternative, pad); break;
case 'U':
handler.on_dec0_week_of_year(numeric_system::alternative, pad);
break;
case 'e':
handler.on_day_of_month(numeric_system::alternative, pad_type::space);
break;
- case 'w':
- handler.on_dec0_weekday(numeric_system::alternative);
- break;
- case 'u':
- handler.on_dec1_weekday(numeric_system::alternative);
- break;
- case 'H':
- handler.on_24_hour(numeric_system::alternative, pad);
- break;
- case 'I':
- handler.on_12_hour(numeric_system::alternative, pad);
- break;
- case 'M':
- handler.on_minute(numeric_system::alternative, pad);
- break;
- case 'S':
- handler.on_second(numeric_system::alternative, pad);
- break;
- case 'z':
- handler.on_utc_offset(numeric_system::alternative);
- break;
- default:
- FMT_THROW(format_error("invalid format"));
+ case 'w': handler.on_dec0_weekday(numeric_system::alternative); break;
+ case 'u': handler.on_dec1_weekday(numeric_system::alternative); break;
+ case 'H': handler.on_24_hour(numeric_system::alternative, pad); break;
+ case 'I': handler.on_12_hour(numeric_system::alternative, pad); break;
+ case 'M': handler.on_minute(numeric_system::alternative, pad); break;
+ case 'S': handler.on_second(numeric_system::alternative, pad); break;
+ case 'z': handler.on_utc_offset(numeric_system::alternative); break;
+ default: FMT_THROW(format_error("invalid format"));
}
break;
- default:
- FMT_THROW(format_error("invalid format"));
+ default: FMT_THROW(format_error("invalid format"));
}
begin = ptr;
}
FMT_CONSTEXPR void unsupported() {
static_cast<Derived*>(this)->unsupported();
}
- FMT_CONSTEXPR void on_year(numeric_system) { unsupported(); }
+ FMT_CONSTEXPR void on_year(numeric_system, pad_type) { unsupported(); }
FMT_CONSTEXPR void on_short_year(numeric_system) { unsupported(); }
FMT_CONSTEXPR void on_offset_year() { unsupported(); }
FMT_CONSTEXPR void on_century(numeric_system) { unsupported(); }
FMT_CONSTEXPR void on_dec1_weekday(numeric_system) { unsupported(); }
FMT_CONSTEXPR void on_abbr_month() { unsupported(); }
FMT_CONSTEXPR void on_full_month() { unsupported(); }
- FMT_CONSTEXPR void on_dec_month(numeric_system) { unsupported(); }
+ FMT_CONSTEXPR void on_dec_month(numeric_system, pad_type) { unsupported(); }
FMT_CONSTEXPR void on_dec0_week_of_year(numeric_system, pad_type) {
unsupported();
}
FMT_CONSTEXPR void on_iso_week_of_year(numeric_system, pad_type) {
unsupported();
}
- FMT_CONSTEXPR void on_day_of_year() { unsupported(); }
+ FMT_CONSTEXPR void on_day_of_year(pad_type) { unsupported(); }
FMT_CONSTEXPR void on_day_of_month(numeric_system, pad_type) {
unsupported();
}
FMT_CONSTEXPR void on_tz_name() { unsupported(); }
};
-struct tm_format_checker : null_chrono_spec_handler<tm_format_checker> {
- FMT_NORETURN void unsupported() { FMT_THROW(format_error("no format")); }
+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"));
+ }
template <typename Char>
FMT_CONSTEXPR void on_text(const Char*, const Char*) {}
- FMT_CONSTEXPR void on_year(numeric_system) {}
+ FMT_CONSTEXPR void on_year(numeric_system, pad_type) {}
FMT_CONSTEXPR void on_short_year(numeric_system) {}
FMT_CONSTEXPR void on_offset_year() {}
FMT_CONSTEXPR void on_century(numeric_system) {}
FMT_CONSTEXPR void on_dec1_weekday(numeric_system) {}
FMT_CONSTEXPR void on_abbr_month() {}
FMT_CONSTEXPR void on_full_month() {}
- FMT_CONSTEXPR void on_dec_month(numeric_system) {}
+ FMT_CONSTEXPR void on_dec_month(numeric_system, pad_type) {}
FMT_CONSTEXPR void on_dec0_week_of_year(numeric_system, pad_type) {}
FMT_CONSTEXPR void on_dec1_week_of_year(numeric_system, pad_type) {}
FMT_CONSTEXPR void on_iso_week_of_year(numeric_system, pad_type) {}
- FMT_CONSTEXPR void on_day_of_year() {}
+ FMT_CONSTEXPR void on_day_of_year(pad_type) {}
FMT_CONSTEXPR void on_day_of_month(numeric_system, pad_type) {}
FMT_CONSTEXPR void on_24_hour(numeric_system, pad_type) {}
FMT_CONSTEXPR void on_12_hour(numeric_system, pad_type) {}
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* {
}
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 {};
-#if FMT_USE_TZSET
-inline void tzset_once() {
- static bool init = []() -> bool {
- _tzset();
- return true;
- }();
- 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;
}
-#endif
// Converts value to Int and checks that it's in the range [0, upper).
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);
}
using subsecond_precision = std::chrono::duration<
typename std::common_type<typename Duration::rep,
std::chrono::seconds::rep>::type,
- std::ratio<1, detail::pow10(num_fractional_digits)>>;
+ std::ratio<1, pow10(num_fractional_digits)>>;
- const auto fractional = d - fmt_duration_cast<std::chrono::seconds>(d);
+ const auto fractional = d - detail::duration_cast<std::chrono::seconds>(d);
const auto subseconds =
std::chrono::treat_as_floating_point<
typename subsecond_precision::rep>::value
? fractional.count()
- : fmt_duration_cast<subsecond_precision>(fractional).count();
+ : detail::duration_cast<subsecond_precision>(fractional).count();
auto n = static_cast<uint32_or_64_or_128_t<long long>>(subseconds);
- const int num_digits = detail::count_digits(n);
+ const int num_digits = count_digits(n);
int leading_zeroes = (std::max)(0, num_fractional_digits - num_digits);
if (precision < 0) {
std::chrono::seconds::period>::value) {
*out++ = '.';
out = detail::fill_n(out, leading_zeroes, '0');
- out = format_decimal<Char>(out, n, num_digits).end;
+ out = format_decimal<Char>(out, n, num_digits);
}
} else if (precision > 0) {
*out++ = '.';
- leading_zeroes = (std::min)(leading_zeroes, precision);
+ leading_zeroes = min_of(leading_zeroes, precision);
int remaining = precision - leading_zeroes;
out = detail::fill_n(out, leading_zeroes, '0');
if (remaining < num_digits) {
int num_truncated_digits = num_digits - remaining;
- n /= to_unsigned(detail::pow10(to_unsigned(num_truncated_digits)));
- if (n) {
- out = format_decimal<Char>(out, n, remaining).end;
- }
+ n /= to_unsigned(pow10(to_unsigned(num_truncated_digits)));
+ if (n != 0) out = format_decimal<Char>(out, n, remaining);
return;
}
- if (n) {
- out = format_decimal<Char>(out, n, num_digits).end;
+ if (n != 0) {
+ out = format_decimal<Char>(out, n, num_digits);
remaining -= num_digits;
}
out = detail::fill_n(out, remaining, '0');
// 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) {
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_;
}
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;
}
// 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);
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;
}
}
- void write_year_extended(long long year) {
+ void write_year_extended(long long year, pad_type pad) {
// At least 4 characters.
int width = 4;
- if (year < 0) {
- *out_++ = '-';
+ bool negative = year < 0;
+ if (negative) {
year = 0 - year;
--width;
}
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)
- out_ = detail::fill_n(out_, width - num_digits, '0');
- out_ = format_decimal<Char>(out_, n, num_digits).end;
+ out_ = detail::write_padding(out_, pad, width - num_digits);
+ if (negative && pad != pad_type::zero) *out_++ = '-';
+ out_ = format_decimal<Char>(out_, n, num_digits);
}
- void write_year(long long year) {
- if (year >= 0 && year < 10000) {
- write2(static_cast<int>(year / 100));
- write2(static_cast<int>(year % 100));
- } else {
- write_year_extended(year);
- }
+ void write_year(long long year, pad_type pad) {
+ write_year_extended(year, pad);
}
- void write_utc_offset(long offset, numeric_system ns) {
+ void write_utc_offset(long long offset, numeric_system ns) {
if (offset < 0) {
*out_++ = '-';
offset = -offset;
if (ns != numeric_system::standard) *out_++ = ':';
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)
-# if FMT_USE_TZSET
- tzset_once();
-# endif
- 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(>m);
- std::tm ltm = gmtime(gt);
- std::time_t lt = std::mktime(<m);
- 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) {
*out_++ = ' ';
on_iso_time();
*out_++ = ' ';
- on_year(numeric_system::standard);
+ on_year(numeric_system::standard, pad_type::space);
} else {
format_localized('c', ns == numeric_system::standard ? '\0' : 'E');
}
char buf[10];
size_t offset = 0;
if (year >= 0 && year < 10000) {
- copy2(buf, digits2(static_cast<size_t>(year / 100)));
+ write2digits(buf, static_cast<size_t>(year / 100));
} else {
offset = 4;
- write_year_extended(year);
+ write_year_extended(year, pad_type::zero);
year = 0;
}
write_digit2_separated(buf + 2, static_cast<unsigned>(year % 100),
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) {
+ void on_year(numeric_system ns, pad_type pad) {
if (is_classic_ || ns == numeric_system::standard)
- return write_year(tm_year());
+ return write_year(tm_year(), pad);
format_localized('Y', 'E');
}
void on_short_year(numeric_system ns) {
}
}
- void on_dec_month(numeric_system ns) {
+ void on_dec_month(numeric_system ns, pad_type pad) {
if (is_classic_ || ns == numeric_system::standard)
- return write2(tm_mon() + 1);
+ return write2(tm_mon() + 1, pad);
format_localized('m', 'O');
}
format_localized('V', 'O');
}
- void on_iso_week_based_year() { write_year(tm_iso_week_year()); }
+ void on_iso_week_based_year() {
+ write_year(tm_iso_week_year(), pad_type::zero);
+ }
void on_iso_week_based_short_year() {
write2(split_year_lower(tm_iso_week_year()));
}
- void on_day_of_year() {
+ void on_day_of_year(pad_type pad) {
auto yday = tm_yday() + 1;
- write1(yday / 100);
- write2(yday % 100);
+ auto digit1 = yday / 100;
+ if (digit1 != 0)
+ write1(digit1);
+ else
+ out_ = detail::write_padding(out_, pad);
+ write2(yday % 100, pad);
}
+
void on_day_of_month(numeric_system ns, pad_type pad) {
if (is_classic_ || ns == numeric_system::standard)
return write2(tm_mday(), pad);
write_floating_seconds(buf, *subsecs_);
if (buf.size() > 1) {
// Remove the leading "0", write something like ".123".
- out_ = std::copy(buf.begin() + 1, buf.end(), out_);
+ out_ = copy<Char>(buf.begin() + 1, buf.end(), out_);
}
} else {
write_fractional_seconds<Char>(out_, *subsecs_);
struct chrono_format_checker : null_chrono_spec_handler<chrono_format_checker> {
bool has_precision_integral = false;
- FMT_NORETURN void unsupported() { FMT_THROW(format_error("no date")); }
+ FMT_NORETURN inline void unsupported() { FMT_THROW(format_error("no date")); }
template <typename Char>
FMT_CONSTEXPR void on_text(const Char*, const Char*) {}
- FMT_CONSTEXPR void on_day_of_year() {}
+ FMT_CONSTEXPR void on_day_of_year(pad_type) {}
FMT_CONSTEXPR void on_24_hour(numeric_system, pad_type) {}
FMT_CONSTEXPR void on_12_hour(numeric_system, pad_type) {}
FMT_CONSTEXPR void on_minute(numeric_system, pad_type) {}
FMT_CONSTEXPR void on_iso_time() {}
FMT_CONSTEXPR void on_am_pm() {}
FMT_CONSTEXPR void on_duration_value() const {
- if (has_precision_integral) {
+ if (has_precision_integral)
FMT_THROW(format_error("precision not allowed for this argument type"));
- }
}
FMT_CONSTEXPR void on_duration_unit() {}
};
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 = fmt_duration_cast<CommonSecondsType>(d);
- const auto d_as_whole_seconds =
- fmt_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 =
- fmt_duration_cast<std::chrono::duration<Rep, std::milli>>(diff);
+ 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.
+ 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 = fmt_duration_cast<std::chrono::seconds>(d);
- return fmt_duration_cast<std::chrono::milliseconds>(d - s);
+ auto s = detail::duration_cast<std::chrono::seconds>(d);
+ return detail::duration_cast<std::chrono::milliseconds>(d - s);
#endif
}
auto format_duration_value(OutputIt out, Rep val, int precision) -> OutputIt {
auto specs = format_specs();
specs.precision = precision;
- specs.type =
- precision >= 0 ? presentation_type::fixed : presentation_type::general;
+ specs.set_type(precision >= 0 ? presentation_type::fixed
+ : presentation_type::general);
return write<Char>(out, val, specs);
}
template <typename Char, typename OutputIt>
auto copy_unit(string_view unit, OutputIt out, Char) -> OutputIt {
- return std::copy(unit.begin(), unit.end(), out);
+ return copy<Char>(unit.begin(), unit.end(), out);
}
template <typename OutputIt>
// This works when wchar_t is UTF-32 because units only contain characters
// that have the same representation in UTF-16 and UTF-32.
utf8_to_utf16 u(unit);
- return std::copy(u.c_str(), u.c_str() + u.size(), out);
+ return copy<wchar_t>(u.c_str(), u.c_str() + u.size(), out);
}
template <typename Char, typename Period, typename OutputIt>
bool has_locale_ = false;
public:
- get_locale(bool localized, locale_ref loc) : has_locale_(localized) {
-#ifndef FMT_STATIC_THOUSANDS_SEPARATOR
- if (localized)
- ::new (&locale_) std::locale(loc.template get<std::locale>());
+ inline get_locale(bool localized, locale_ref loc) : has_locale_(localized) {
+ if (!localized) return;
+ ignore_unused(loc);
+ ::new (&locale_) std::locale(
+#if FMT_USE_LOCALE
+ loc.template get<std::locale>()
#endif
+ );
}
- ~get_locale() {
+ inline ~get_locale() {
if (has_locale_) locale_.~locale();
}
- operator const std::locale&() const {
+ inline operator const std::locale&() const {
return has_locale_ ? locale_ : get_classic_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;
// this may overflow and/or the result may not fit in the
// target type.
// might need checked conversion (rep!=Rep)
- s = fmt_duration_cast<seconds>(std::chrono::duration<rep, Period>(val));
+ s = detail::duration_cast<seconds>(std::chrono::duration<rep, Period>(val));
}
// 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;
}
}
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) {
if (width > num_digits) {
out = detail::write_padding(out, pad, width - num_digits);
}
- out = format_decimal<char_type>(out, n, num_digits).end;
+ 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) {
- std::copy(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.
void on_iso_date() {}
void on_utc_offset(numeric_system) {}
void on_tz_name() {}
- void on_year(numeric_system) {}
+ void on_year(numeric_system, pad_type) {}
void on_short_year(numeric_system) {}
void on_offset_year() {}
void on_century(numeric_system) {}
void on_iso_week_based_year() {}
void on_iso_week_based_short_year() {}
- void on_dec_month(numeric_system) {}
+ void on_dec_month(numeric_system, pad_type) {}
void on_dec0_week_of_year(numeric_system, pad_type) {}
void on_dec1_week_of_year(numeric_system, pad_type) {}
void on_iso_week_of_year(numeric_system, pad_type) {}
void on_day_of_month(numeric_system, pad_type) {}
- void on_day_of_year() {
+ void on_day_of_year(pad_type) {
if (handle_nan_inf()) return;
write(days(), 0);
}
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 = std::copy(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;
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
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:
- FMT_CONSTEXPR auto parse(basic_format_parse_context<Char>& ctx)
- -> decltype(ctx.begin()) {
+ FMT_CONSTEXPR auto parse(parse_context<Char>& ctx) -> const 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;
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();
bool use_tm_formatter_ = false;
public:
- FMT_CONSTEXPR auto parse(basic_format_parse_context<Char>& ctx)
- -> decltype(ctx.begin()) {
+ FMT_CONSTEXPR auto parse(parse_context<Char>& ctx) -> const Char* {
auto it = ctx.begin(), end = ctx.end();
use_tm_formatter_ = it != end && *it != '}';
return use_tm_formatter_ ? formatter<std::tm, Char>::parse(ctx) : it;
template <typename Char>
struct formatter<month, Char> : private formatter<std::tm, Char> {
private:
- bool localized_ = false;
bool use_tm_formatter_ = false;
public:
- FMT_CONSTEXPR auto parse(basic_format_parse_context<Char>& ctx)
- -> decltype(ctx.begin()) {
+ FMT_CONSTEXPR auto parse(parse_context<Char>& ctx) -> const 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;
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();
bool use_tm_formatter_ = false;
public:
- FMT_CONSTEXPR auto parse(basic_format_parse_context<Char>& ctx)
- -> decltype(ctx.begin()) {
+ FMT_CONSTEXPR auto parse(parse_context<Char>& ctx) -> const Char* {
auto it = ctx.begin(), end = ctx.end();
use_tm_formatter_ = it != end && *it != '}';
return use_tm_formatter_ ? formatter<std::tm, Char>::parse(ctx) : it;
if (use_tm_formatter_) return formatter<std::tm, Char>::format(time, ctx);
detail::get_locale loc(false, ctx.locale());
auto w = detail::tm_writer<decltype(ctx.out()), Char>(loc, ctx.out(), time);
- w.on_year(detail::numeric_system::standard);
+ w.on_year(detail::numeric_system::standard, detail::pad_type::zero);
return w.out();
}
};
bool use_tm_formatter_ = false;
public:
- FMT_CONSTEXPR auto parse(basic_format_parse_context<Char>& ctx)
- -> decltype(ctx.begin()) {
+ FMT_CONSTEXPR auto parse(parse_context<Char>& ctx) -> const Char* {
auto it = ctx.begin(), end = ctx.end();
use_tm_formatter_ = it != end && *it != '}';
return use_tm_formatter_ ? formatter<std::tm, Char>::parse(ctx) : it;
format_specs specs_;
detail::arg_ref<Char> width_ref_;
detail::arg_ref<Char> precision_ref_;
- bool localized_ = false;
- basic_string_view<Char> format_str_;
+ basic_string_view<Char> fmt_;
public:
- FMT_CONSTEXPR auto parse(basic_format_parse_context<Char>& ctx)
- -> decltype(ctx.begin()) {
+ FMT_CONSTEXPR auto parse(parse_context<Char>& ctx) -> const Char* {
auto it = ctx.begin(), end = ctx.end();
if (it == end || *it == '}') return it;
it = detail::parse_align(it, end, specs_);
if (it == end) return it;
- it = detail::parse_dynamic_spec(it, end, specs_.width, width_ref_, ctx);
- if (it == end) return it;
+ Char c = *it;
+ if ((c >= '0' && c <= '9') || c == '{') {
+ it = detail::parse_width(it, end, specs_, width_ref_, ctx);
+ if (it == end) return it;
+ }
auto checker = detail::chrono_format_checker();
if (*it == '.') {
checker.has_precision_integral = !std::is_floating_point<Rep>::value;
- it = detail::parse_precision(it, end, specs_.precision, precision_ref_,
- ctx);
+ 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);
- format_str_ = {it, detail::to_unsigned(end - it)};
+ fmt_ = {it, detail::to_unsigned(end - it)};
return end;
}
auto specs = specs_;
auto precision = specs.precision;
specs.precision = -1;
- auto begin = format_str_.begin(), end = format_str_.end();
+ auto begin = fmt_.begin(), end = fmt_.end();
// As a possible future optimization, we could avoid extra copying if width
// is not specified.
auto buf = basic_memory_buffer<Char>();
- auto out = std::back_inserter(buf);
- detail::handle_dynamic_spec<detail::width_checker>(specs.width, width_ref_,
- ctx);
- detail::handle_dynamic_spec<detail::precision_checker>(precision,
- precision_ref_, ctx);
+ auto out = basic_appender<Char>(buf);
+ detail::handle_dynamic_spec(specs.dynamic_width(), specs.width, width_ref_,
+ ctx);
+ detail::handle_dynamic_spec(specs.dynamic_precision(), precision,
+ precision_ref_, ctx);
if (begin == end || *begin == '}') {
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(
}
};
+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:
+ auto localized() const -> bool { return specs_.localized(); }
+ FMT_CONSTEXPR void set_localized() { specs_.set_localized(); }
+
+ 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;
+
+ it = detail::parse_align(it, end, specs_);
+ if (it == end) return it;
+
+ Char c = *it;
+ if ((c >= '0' && c <= '9') || c == '{') {
+ it = detail::parse_width(it, end, specs_, width_ref_, ctx);
+ if (it == end) return it;
+ }
+
+ 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()) {
+ return do_format<std::chrono::seconds>(tm, ctx, nullptr);
+ }
+};
+
+// DEPRECATED! Reversed order of template parameters.
template <typename Char, typename Duration>
-struct formatter<std::chrono::time_point<std::chrono::system_clock, Duration>,
- Char> : formatter<std::tm, Char> {
- FMT_CONSTEXPR formatter() {
- this->format_str_ = 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>
- auto format(std::chrono::time_point<std::chrono::system_clock, Duration> val,
- FormatContext& ctx) const -> decltype(ctx.out()) {
+ auto format(sys_time<Duration> val, FormatContext& ctx) const
+ -> decltype(ctx.out()) {
std::tm tm = gmtime(val);
using period = typename Duration::period;
if (detail::const_check(
period::num == 1 && period::den == 1 &&
!std::is_floating_point<typename Duration::rep>::value)) {
+ detail::set_tm_zone(tm, detail::utc());
return formatter<std::tm, Char>::format(tm, ctx);
}
Duration epoch = val.time_since_epoch();
- Duration subsecs = detail::fmt_duration_cast<Duration>(
- epoch - detail::fmt_duration_cast<std::chrono::seconds>(epoch));
+ Duration subsecs = detail::duration_cast<Duration>(
+ epoch - detail::duration_cast<std::chrono::seconds>(epoch));
if (subsecs.count() < 0) {
- auto second =
- detail::fmt_duration_cast<Duration>(std::chrono::seconds(1));
- if (tm.tm_sec != 0)
+ auto second = detail::duration_cast<Duration>(std::chrono::seconds(1));
+ if (tm.tm_sec != 0) {
--tm.tm_sec;
- else
+ } else {
tm = gmtime(val - second);
- subsecs += detail::fmt_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);
}
};
-#if FMT_USE_LOCAL_TIME
-template <typename Char, typename Duration>
-struct formatter<std::chrono::local_time<Duration>, Char>
- : formatter<std::tm, Char> {
- FMT_CONSTEXPR formatter() {
- this->format_str_ = detail::string_literal<Char, '%', 'F', ' ', '%', 'T'>{};
- }
-
+template <typename Duration, typename Char>
+struct formatter<utc_time<Duration>, Char>
+ : formatter<sys_time<Duration>, Char> {
template <typename FormatContext>
- auto format(std::chrono::local_time<Duration> val, FormatContext& ctx) const
+ auto format(utc_time<Duration> val, FormatContext& ctx) const
-> decltype(ctx.out()) {
- using period = typename Duration::period;
- if (period::num != 1 || period::den != 1 ||
- std::is_floating_point<typename Duration::rep>::value) {
- const auto epoch = val.time_since_epoch();
- const auto subsecs = detail::fmt_duration_cast<Duration>(
- epoch - detail::fmt_duration_cast<std::chrono::seconds>(epoch));
-
- return formatter<std::tm, Char>::do_format(localtime(val), ctx, &subsecs);
- }
-
- return formatter<std::tm, Char>::format(localtime(val), ctx);
+ return formatter<sys_time<Duration>, Char>::format(
+ detail::utc_clock::to_sys(val), ctx);
}
};
-#endif
-#if FMT_USE_UTC_TIME
-template <typename Char, typename Duration>
-struct formatter<std::chrono::time_point<std::chrono::utc_clock, Duration>,
- Char>
- : formatter<std::chrono::time_point<std::chrono::system_clock, Duration>,
- Char> {
- template <typename FormatContext>
- auto format(std::chrono::time_point<std::chrono::utc_clock, Duration> val,
- FormatContext& ctx) const -> decltype(ctx.out()) {
- return formatter<
- std::chrono::time_point<std::chrono::system_clock, Duration>,
- Char>::format(std::chrono::utc_clock::to_sys(val), ctx);
- }
-};
-#endif
-
-template <typename Char> struct formatter<std::tm, Char> {
- private:
- format_specs specs_;
- detail::arg_ref<Char> width_ref_;
-
- protected:
- basic_string_view<Char> format_str_;
-
- template <typename FormatContext, typename Duration>
- 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 = std::back_inserter(buf);
- detail::handle_dynamic_spec<detail::width_checker>(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(format_str_.begin(), format_str_.end(), w);
- return detail::write(
- ctx.out(), basic_string_view<Char>(buf.data(), buf.size()), specs);
- }
-
- public:
- FMT_CONSTEXPR auto parse(basic_format_parse_context<Char>& ctx)
- -> decltype(ctx.begin()) {
- auto it = ctx.begin(), end = ctx.end();
- if (it == end || *it == '}') return it;
-
- it = detail::parse_align(it, end, specs_);
- if (it == end) return it;
-
- it = detail::parse_dynamic_spec(it, end, specs_.width, width_ref_, ctx);
- if (it == end) return it;
-
- end = detail::parse_chrono_format(it, end, detail::tm_format_checker());
- // Replace the default format_str only if the new spec is not empty.
- if (end != it) format_str_ = {it, detail::to_unsigned(end - it)};
- return end;
+template <typename Duration, typename Char>
+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(const std::tm& tm, FormatContext& ctx) const
+ auto format(local_time<Duration> val, FormatContext& ctx) const
-> decltype(ctx.out()) {
- return do_format<FormatContext, std::chrono::seconds>(tm, ctx, nullptr);
+ 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(t, ctx);
+ }
+ auto subsecs =
+ detail::duration_cast<Duration>(time_since_epoch - seconds_since_epoch);
+ return formatter<std::tm, Char>::do_format(t, ctx, &subsecs);
}
};
// 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) {}
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;
- }
- FMT_CONSTEXPR color_type(terminal_color term_color) noexcept
- : is_rgb(), value{} {
- value.term_color = static_cast<uint8_t>(term_color);
+
+ constexpr auto value() const noexcept -> uint32_t {
+ return value_ & 0xFFFFFF;
}
- 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) {}
-
- 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;
- }
+ : style_(static_cast<uint64_t>(em) << 54) {}
- 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;
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
namespace detail {
template <typename Char> struct ansi_color_escape {
- FMT_CONSTEXPR ansi_color_escape(detail::color_type text_color,
+ FMT_CONSTEXPR ansi_color_escape(color_type text_color,
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] = {};
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 {
};
template <typename Char>
-FMT_CONSTEXPR auto make_foreground_color(detail::color_type foreground) noexcept
+FMT_CONSTEXPR auto make_foreground_color(color_type foreground) noexcept
-> ansi_color_escape<Char> {
return ansi_color_escape<Char>(foreground, "\x1b[38;2;");
}
template <typename Char>
-FMT_CONSTEXPR auto make_background_color(detail::color_type background) noexcept
+FMT_CONSTEXPR auto make_background_color(color_type background) noexcept
-> ansi_color_escape<Char> {
return ansi_color_escape<Char>(background, "\x1b[48;2;");
}
buffer.append(reset_color.begin(), reset_color.end());
}
-template <typename T> struct styled_arg : detail::view {
+template <typename T> struct styled_arg : view {
const T& value;
text_style style;
styled_arg(const T& v, text_style s) : value(v), style(s) {}
};
template <typename Char>
-void vformat_to(
- buffer<Char>& buf, const text_style& ts, basic_string_view<Char> format_str,
- basic_format_args<buffered_context<type_identity_t<Char>>> args) {
- bool has_style = false;
+void vformat_to(buffer<Char>& buf, text_style ts, basic_string_view<Char> fmt,
+ basic_format_args<buffered_context<Char>> args) {
if (ts.has_emphasis()) {
- has_style = true;
- auto emphasis = detail::make_emphasis<Char>(ts.get_emphasis());
+ auto emphasis = make_emphasis<Char>(ts.get_emphasis());
buf.append(emphasis.begin(), emphasis.end());
}
if (ts.has_foreground()) {
- has_style = true;
- auto foreground = detail::make_foreground_color<Char>(ts.get_foreground());
+ auto foreground = make_foreground_color<Char>(ts.get_foreground());
buf.append(foreground.begin(), foreground.end());
}
if (ts.has_background()) {
- has_style = true;
- auto background = detail::make_background_color<Char>(ts.get_background());
+ auto background = make_background_color<Char>(ts.get_background());
buf.append(background.begin(), background.end());
}
- detail::vformat_to(buf, format_str, args, {});
- if (has_style) detail::reset_color<Char>(buf);
+ vformat_to(buf, fmt, args);
+ 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()));
* "Elapsed time: {0:.2f} seconds", 1.23);
*/
template <typename... T>
-void print(FILE* f, const text_style& ts, format_string<T...> fmt,
- T&&... args) {
- vprint(f, ts, fmt, fmt::make_format_args(args...));
+void print(FILE* f, text_style ts, format_string<T...> fmt, T&&... args) {
+ vprint(f, ts, fmt.str, vargs<T...>{{args...}});
}
/**
* "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);
* ```
*/
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, fmt::make_format_args(args...));
+ return fmt::vformat(ts, fmt.str, vargs<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);
*/
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 {
- return vformat_to(out, ts, fmt, fmt::make_format_args(args...));
+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...}});
}
template <typename T, typename Char>
auto format(const detail::styled_arg<T>& arg, FormatContext& ctx) const
-> decltype(ctx.out()) {
const auto& ts = arg.style;
- const auto& value = arg.value;
auto out = ctx.out();
bool has_style = false;
if (ts.has_emphasis()) {
has_style = true;
auto emphasis = detail::make_emphasis<Char>(ts.get_emphasis());
- out = std::copy(emphasis.begin(), emphasis.end(), out);
+ out = detail::copy<Char>(emphasis.begin(), emphasis.end(), out);
}
if (ts.has_foreground()) {
has_style = true;
auto foreground =
detail::make_foreground_color<Char>(ts.get_foreground());
- out = std::copy(foreground.begin(), foreground.end(), out);
+ out = detail::copy<Char>(foreground.begin(), foreground.end(), out);
}
if (ts.has_background()) {
has_style = true;
auto background =
detail::make_background_color<Char>(ts.get_background());
- out = std::copy(background.begin(), background.end(), out);
+ out = detail::copy<Char>(background.begin(), background.end(), out);
}
- out = formatter<T, Char>::format(value, ctx);
+ out = formatter<T, Char>::format(arg.value, ctx);
if (has_style) {
auto reset_color = string_view("\x1b[0m");
- out = std::copy(reset_color.begin(), reset_color.end(), out);
+ out = detail::copy<Char>(reset_color.begin(), reset_color.end(), out);
}
return out;
}
#include "format.h"
FMT_BEGIN_NAMESPACE
+FMT_BEGIN_EXPORT
// A compile-time string which is compiled into fast formatting code.
-FMT_EXPORT class compiled_string {};
-
-namespace detail {
-
-template <typename T, typename InputIt>
-FMT_CONSTEXPR inline auto copy(InputIt begin, InputIt end, counting_iterator it)
- -> counting_iterator {
- return it + (end - begin);
-}
+class compiled_string {};
template <typename S>
struct is_compiled_string : std::is_base_of<compiled_string, S> {};
* std::string s = fmt::format(FMT_COMPILE("{}"), 42);
*/
#if defined(__cpp_if_constexpr) && defined(__cpp_return_type_deduction)
-# define FMT_COMPILE(s) FMT_STRING_IMPL(s, fmt::compiled_string, explicit)
+# define FMT_COMPILE(s) FMT_STRING_IMPL(s, fmt::compiled_string)
#else
# 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
-template <typename Char, size_t N,
- fmt::detail_exported::fixed_string<Char, N> Str>
-struct udl_compiled_string : compiled_string {
- using char_type = Char;
- explicit constexpr operator basic_string_view<char_type>() const {
- return {Str.data, N - 1};
- }
-};
+inline namespace literals {
+template <detail::fixed_string Str> constexpr auto operator""_cf() {
+ return FMT_COMPILE(Str.data);
+}
+} // namespace literals
#endif
+FMT_END_EXPORT
+
+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;
return detail::get<N - 1>(rest...);
}
+# if FMT_USE_NONTYPE_TEMPLATE_ARGS
+template <int N, typename T, typename... Args, typename Char>
+constexpr auto get_arg_index_by_name(basic_string_view<Char> name) -> int {
+ if constexpr (is_static_named_arg<T>()) {
+ if (name == T::name) return N;
+ }
+ if constexpr (sizeof...(Args) > 0)
+ return get_arg_index_by_name<N + 1, Args...>(name);
+ (void)name; // Workaround an MSVC bug about "unused" parameter.
+ return -1;
+}
+# endif
+
+template <typename... Args, typename Char>
+FMT_CONSTEXPR auto get_arg_index_by_name(basic_string_view<Char> name) -> int {
+# if FMT_USE_NONTYPE_TEMPLATE_ARGS
+ if constexpr (sizeof...(Args) > 0)
+ return get_arg_index_by_name<0, Args...>(name);
+# endif
+ (void)name;
+ return -1;
+}
+
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);
}
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);
}
};
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}};
}
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;
}
// 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;
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 {
+ return write<Char>(out, arg);
}
- return write<Char>(out, arg);
}
};
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);
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"));
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);
}
};
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...);
}
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;
}
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);
}
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;
}
};
template <typename Char> struct parse_arg_id_result {
+ arg_id_kind kind;
arg_ref<Char> arg_id;
const Char* arg_id_end;
};
template <int ID, typename Char>
constexpr auto parse_arg_id(const Char* begin, const Char* end) {
- auto handler = arg_id_handler<Char>{arg_ref<Char>{}};
+ auto handler = arg_id_handler<Char>{arg_id_kind::none, arg_ref<Char>{}};
auto arg_id_end = parse_arg_id(begin, end, handler);
- return parse_arg_id_result<Char>{handler.arg_id, arg_id_end};
+ return parse_arg_id_result<Char>{handler.kind, handler.arg_id, arg_id_end};
}
template <typename T, typename Enable = void> struct field_type {
constexpr char_type c =
arg_id_end_pos != str.size() ? str[arg_id_end_pos] : char_type();
static_assert(c == '}' || c == ':', "missing '}' in format string");
- if constexpr (arg_id_result.arg_id.kind == arg_id_kind::index) {
+ if constexpr (arg_id_result.kind == arg_id_kind::index) {
static_assert(
ID == manual_indexing_id || ID == 0,
"cannot switch from automatic to manual argument indexing");
- constexpr auto arg_index = arg_id_result.arg_id.val.index;
+ constexpr auto arg_index = arg_id_result.arg_id.index;
return parse_replacement_field_then_tail<get_type<arg_index, Args>,
Args, arg_id_end_pos,
arg_index, manual_indexing_id>(
fmt);
- } else if constexpr (arg_id_result.arg_id.kind == arg_id_kind::name) {
+ } else if constexpr (arg_id_result.kind == arg_id_kind::name) {
constexpr auto arg_index =
- get_arg_index_by_name(arg_id_result.arg_id.val.name, Args{});
+ get_arg_index_by_name(arg_id_result.arg_id.name, Args{});
if constexpr (arg_index >= 0) {
constexpr auto next_id =
ID != manual_indexing_id ? ID + 1 : manual_indexing_id;
arg_index, next_id>(fmt);
} else if constexpr (c == '}') {
return parse_tail<Args, arg_id_end_pos + 1, ID>(
- runtime_named_field<char_type>{arg_id_result.arg_id.val.name},
- fmt);
+ runtime_named_field<char_type>{arg_id_result.arg_id.name}, fmt);
} else if constexpr (c == ':') {
return unknown_format(); // no type info for specs parsing
}
}
template <typename... Args, typename S,
- FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
+ FMT_ENABLE_IF(is_compiled_string<S>::value)>
constexpr auto compile(S fmt) {
constexpr auto str = basic_string_view<typename S::char_type>(fmt);
if constexpr (str.size() == 0) {
#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,
- FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
-FMT_INLINE std::basic_string<typename S::char_type> format(const S&,
- Args&&... args) {
+template <typename S, typename... T,
+ FMT_ENABLE_IF(is_compiled_string<S>::value)>
+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] == '}') {
}
}
}
- 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,
- FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
-FMT_CONSTEXPR OutputIt format_to(OutputIt out, const S&, Args&&... args) {
- constexpr auto compiled = detail::compile<Args...>(S());
+template <typename OutputIt, typename S, typename... T,
+ FMT_ENABLE_IF(is_compiled_string<S>::value)>
+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,
- FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
-auto format_to_n(OutputIt out, size_t n, const S& fmt, Args&&... 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, 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,
- FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
-FMT_CONSTEXPR20 auto formatted_size(const S& fmt, const Args&... args)
- -> size_t {
- return fmt::format_to(detail::counting_iterator(), fmt, args...).count();
+template <typename S, typename... T,
+ FMT_ENABLE_IF(is_compiled_string<S>::value)>
+FMT_CONSTEXPR20 auto formatted_size(const S& fmt, T&&... args) -> size_t {
+ auto buf = detail::counting_buffer<>();
+ fmt::format_to(appender(buf), fmt, std::forward<T>(args)...);
+ return buf.count();
}
-template <typename S, typename... Args,
- FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
-void print(std::FILE* f, const S& fmt, const Args&... args) {
- memory_buffer buffer;
- fmt::format_to(std::back_inserter(buffer), fmt, args...);
- detail::print(f, {buffer.data(), buffer.size()});
+template <typename S, typename... T,
+ FMT_ENABLE_IF(is_compiled_string<S>::value)>
+void print(std::FILE* f, const S& fmt, T&&... args) {
+ auto buf = memory_buffer();
+ fmt::format_to(appender(buf), fmt, std::forward<T>(args)...);
+ detail::print(f, {buf.data(), buf.size()});
}
-template <typename S, typename... Args,
- FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
-void print(const S& fmt, const Args&... args) {
- print(stdout, fmt, args...);
+template <typename S, typename... T,
+ FMT_ENABLE_IF(is_compiled_string<S>::value)>
+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_exported::fixed_string Str> constexpr auto operator""_cf() {
- using char_t = remove_cvref_t<decltype(Str.data[0])>;
- return detail::udl_compiled_string<char_t, sizeof(Str.data) / sizeof(char_t),
- Str>();
-}
-} // 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
# include <climits>
# include <cmath>
# include <exception>
-
-# if !defined(FMT_STATIC_THOUSANDS_SEPARATOR)
-# include <locale>
-# endif
#endif
#if defined(_WIN32) && !defined(FMT_USE_WRITE_CONSOLE)
#include "format.h"
+#if FMT_USE_LOCALE && !defined(FMT_MODULE)
+# include <locale>
+#endif
+
+#ifndef FMT_FUNC
+# define FMT_FUNC
+#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
+ // writing to stderr fails.
std::fprintf(stderr, "%s:%d: assertion failed: %s", file, line, message);
- // Chosen instead of std::abort to satisfy Clang in CUDA mode during device
- // code pass.
- std::terminate();
+ abort();
+}
+#endif
+
+#if FMT_USE_LOCALE
+namespace detail {
+using std::locale;
+using std::numpunct;
+using std::use_facet;
+} // namespace detail
+#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 {
// Report error code making sure that the output fits into
FMT_ASSERT(out.size() <= inline_buffer_size, "");
}
-FMT_FUNC void report_error(format_func func, int error_code,
- const char* message) noexcept {
+FMT_FUNC void do_report_error(format_func func, int error_code,
+ const char* message) noexcept {
memory_buffer full_message;
func(full_message, error_code, message);
- // Don't use fwrite_fully because the latter may throw.
+ // Don't use fwrite_all because the latter may throw.
if (std::fwrite(full_message.data(), full_message.size(), 1, stderr) > 0)
std::fputc('\n', stderr);
}
// A wrapper around fwrite that throws on error.
-inline void fwrite_fully(const void* ptr, size_t count, FILE* stream) {
+inline void fwrite_all(const void* ptr, size_t count, FILE* stream) {
size_t written = std::fwrite(ptr, 1, count, stream);
if (written < count)
FMT_THROW(system_error(errno, FMT_STRING("cannot write to file")));
}
-#ifndef FMT_STATIC_THOUSANDS_SEPARATOR
-template <typename Locale>
-locale_ref::locale_ref(const Locale& loc) : locale_(&loc) {
- static_assert(std::is_same<Locale, std::locale>::value, "");
-}
-
-template <typename Locale> auto locale_ref::get() const -> Locale {
- static_assert(std::is_same<Locale, std::locale>::value, "");
- return locale_ ? *static_cast<const std::locale*>(locale_) : std::locale();
-}
-
template <typename Char>
FMT_FUNC auto thousands_sep_impl(locale_ref loc) -> thousands_sep_result<Char> {
- auto& facet = std::use_facet<std::numpunct<Char>>(loc.get<std::locale>());
+ auto&& facet = use_facet<numpunct<Char>>(loc.get<locale>());
auto grouping = facet.grouping();
auto thousands_sep = grouping.empty() ? Char() : facet.thousands_sep();
return {std::move(grouping), thousands_sep};
}
template <typename Char>
FMT_FUNC auto decimal_point_impl(locale_ref loc) -> Char {
- return std::use_facet<std::numpunct<Char>>(loc.get<std::locale>())
- .decimal_point();
+ return use_facet<numpunct<Char>>(loc.get<locale>()).decimal_point();
}
-#else
-template <typename Char>
-FMT_FUNC auto thousands_sep_impl(locale_ref) -> thousands_sep_result<Char> {
- return {"\03", FMT_STATIC_THOUSANDS_SEPARATOR};
-}
-template <typename Char> FMT_FUNC Char decimal_point_impl(locale_ref) {
- return '.';
-}
-#endif
+#if FMT_USE_LOCALE
FMT_FUNC auto write_loc(appender out, loc_value value,
const format_specs& specs, locale_ref loc) -> bool {
-#ifdef FMT_STATIC_THOUSANDS_SEPARATOR
- value.visit(loc_writer<>{
- out, specs, std::string(1, FMT_STATIC_THOUSANDS_SEPARATOR), "\3", "."});
- return true;
-#else
auto locale = loc.get<std::locale>();
// We cannot use the num_put<char> facet because it may produce output in
// a wrong encoding.
using facet = format_facet<std::locale>;
if (std::has_facet<facet>(locale))
- return std::use_facet<facet>(locale).put(out, value, specs);
+ return use_facet<facet>(locale).put(out, value, specs);
return facet(locale).put(out, value, specs);
-#endif
}
+#endif
} // namespace detail
FMT_FUNC void report_error(const char* message) {
+#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;
-#ifndef FMT_STATIC_THOUSANDS_SEPARATOR
template <typename Locale> format_facet<Locale>::format_facet(Locale& loc) {
- auto& numpunct = std::use_facet<std::numpunct<char>>(loc);
- grouping_ = numpunct.grouping();
- if (!grouping_.empty()) separator_ = std::string(1, numpunct.thousands_sep());
+ auto& np = detail::use_facet<detail::numpunct<char>>(loc);
+ grouping_ = np.grouping();
+ if (!grouping_.empty()) separator_ = std::string(1, np.thousands_sep());
}
+#if FMT_USE_LOCALE
template <>
FMT_API FMT_FUNC auto format_facet<std::locale>::do_put(
appender out, loc_value val, const format_specs& specs) const -> 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));
}
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}};
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,
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},
#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,
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));
}
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;
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.
// 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);
FMT_FUNC void report_system_error(int error_code,
const char* message) noexcept {
- report_error(format_system_error, error_code, message);
+ do_report_error(format_system_error, error_code, message);
}
FMT_FUNC auto vformat(string_view fmt, format_args args) -> std::string {
namespace detail {
+FMT_FUNC void vformat_to(buffer<char>& buf, string_view fmt, format_args args,
+ locale_ref loc) {
+ 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<>{parse_context<>(fmt), {out, args, loc}});
+}
+
template <typename T> struct span {
T* data;
size_t size;
};
-#ifdef _WIN32
-inline void flockfile(FILE* f) { _lock_file(f); }
-inline void funlockfile(FILE* f) { _unlock_file(f); }
-inline int getc_unlocked(FILE* f) { return _fgetc_nolock(f); }
+template <typename F> auto flockfile(F* f) -> decltype(_lock_file(f)) {
+ _lock_file(f);
+}
+template <typename F> auto funlockfile(F* f) -> decltype(_unlock_file(f)) {
+ _unlock_file(f);
+}
+
+#ifndef getc_unlocked
+template <typename F> auto getc_unlocked(F* f) -> decltype(_fgetc_nolock(f)) {
+ return _fgetc_nolock(f);
+}
#endif
+template <typename F = FILE, typename Enable = void>
+struct has_flockfile : std::false_type {};
+
+template <typename F>
+struct has_flockfile<F, void_t<decltype(flockfile(&std::declval<F&>()))>>
+ : std::true_type {};
+
// A FILE wrapper. F is FILE defined as a template parameter to make system API
// detection work.
template <typename F> class file_base {
}
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.
putc_unlocked(0, this->file_);
--this->file_->_IO_write_ptr;
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_); }
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;
}
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));
};
#ifndef FMT_USE_FALLBACK_FILE
-# define FMT_USE_FALLBACK_FILE 1
+# define FMT_USE_FALLBACK_FILE 0
#endif
template <typename F,
using file_ref = decltype(get_file(static_cast<FILE*>(nullptr), 0));
+template <typename F = FILE, typename Enable = void>
class file_print_buffer : public buffer<char> {
+ public:
+ explicit file_print_buffer(F*) : buffer(nullptr, size_t()) {}
+};
+
+template <typename F>
+class file_print_buffer<F, enable_if_t<has_flockfile<F>::value>>
+ : public buffer<char> {
private:
file_ref file_;
}
public:
- explicit file_print_buffer(FILE* f) : buffer(grow, size_t()), file_(f) {
+ explicit file_print_buffer(F* f) : buffer(grow, size_t()), file_(f) {
flockfile(f);
file_.init_buffer();
auto buf = file_.get_write_buffer();
~file_print_buffer() {
file_.advance_write_buffer(size());
bool flush = file_.needs_flush();
- funlockfile(file_);
+ F* f = file_; // Make funlockfile depend on the template parameter F
+ funlockfile(f); // for the system API detection to work.
if (flush) fflush(file_);
}
};
auto buffer = memory_buffer();
detail::vformat_to(buffer, fmt, args);
if (newline) buffer.push_back('\n');
- fwrite_fully(buffer.data(), buffer.size(), f);
+ fwrite_all(buffer.data(), buffer.size(), f);
}
#endif
if (write_console(fd, text)) return;
}
#endif
- fwrite_fully(text.data(), text.size(), f);
+ fwrite_all(text.data(), text.size(), f);
}
} // namespace detail
}
FMT_FUNC void vprint(std::FILE* f, string_view fmt, format_args args) {
- if (!detail::file_ref(f).is_buffered()) return vprint_buffered(f, fmt, args);
- auto&& buffer = detail::file_print_buffer(f);
+ if (!detail::file_ref(f).is_buffered() || !detail::has_flockfile<>())
+ return vprint_buffered(f, fmt, args);
+ auto&& buffer = detail::file_print_buffer<>(f);
return detail::vformat_to(buffer, fmt, args);
}
#include "base.h"
+// libc++ supports string_view in pre-c++17.
+#if FMT_HAS_INCLUDE(<string_view>) && \
+ (FMT_CPLUSPLUS >= 201703L || defined(_LIBCPP_VERSION))
+# define FMT_USE_STRING_VIEW
+#endif
+
#ifndef FMT_MODULE
-# include <cmath> // std::signbit
-# include <cstdint> // uint32_t
-# include <cstring> // std::memcpy
-# include <initializer_list> // std::initializer_list
-# include <limits> // std::numeric_limits
+# include <stdlib.h> // malloc, free
+
+# include <cmath> // std::signbit
+# include <cstddef> // std::byte
+# include <cstdint> // uint32_t
+# include <cstring> // std::memcpy
+# include <limits> // std::numeric_limits
+# include <new> // std::bad_alloc
# if defined(__GLIBCXX__) && !defined(_GLIBCXX_USE_DUAL_ABI)
// Workaround for pre gcc 5 libstdc++.
# include <memory> // std::allocator_traits
# include <string> // std::string
# include <system_error> // std::system_error
-// Checking FMT_CPLUSPLUS for warning suppression in MSVC.
+// Check FMT_CPLUSPLUS to avoid a warning in MSVC.
# if FMT_HAS_INCLUDE(<bit>) && FMT_CPLUSPLUS > 201703L
# include <bit> // std::bit_cast
# endif
-// libc++ supports string_view in pre-c++17.
-# if FMT_HAS_INCLUDE(<string_view>) && \
- (FMT_CPLUSPLUS >= 201703L || defined(_LIBCPP_VERSION))
+# if defined(FMT_USE_STRING_VIEW)
# include <string_view>
-# define FMT_USE_STRING_VIEW
+# endif
+
+# if FMT_MSC_VERSION
+# include <intrin.h> // _BitScanReverse[64], _umul128
# endif
#endif // FMT_MODULE
+#if defined(FMT_USE_NONTYPE_TEMPLATE_ARGS)
+// Use the provided definition.
+#elif defined(__NVCOMPILER)
+# define FMT_USE_NONTYPE_TEMPLATE_ARGS 0
+#elif FMT_GCC_VERSION >= 903 && FMT_CPLUSPLUS >= 201709L
+# define FMT_USE_NONTYPE_TEMPLATE_ARGS 1
+#elif defined(__cpp_nontype_template_args) && \
+ __cpp_nontype_template_args >= 201911L
+# define FMT_USE_NONTYPE_TEMPLATE_ARGS 1
+#elif FMT_CLANG_VERSION >= 1200 && FMT_CPLUSPLUS >= 202002L
+# define FMT_USE_NONTYPE_TEMPLATE_ARGS 1
+#else
+# define FMT_USE_NONTYPE_TEMPLATE_ARGS 0
+#endif
+
#if defined __cpp_inline_variables && __cpp_inline_variables >= 201606L
# define FMT_INLINE_VARIABLE inline
#else
# define FMT_INLINE_VARIABLE
#endif
-#ifndef FMT_NO_UNIQUE_ADDRESS
-# if FMT_CPLUSPLUS >= 202002L
-# if FMT_HAS_CPP_ATTRIBUTE(no_unique_address)
-# define FMT_NO_UNIQUE_ADDRESS [[no_unique_address]]
-// VS2019 v16.10 and later except clang-cl (https://reviews.llvm.org/D110485).
-# elif (FMT_MSC_VERSION >= 1929) && !FMT_CLANG_VERSION
-# define FMT_NO_UNIQUE_ADDRESS [[msvc::no_unique_address]]
-# endif
-# endif
-#endif
-#ifndef FMT_NO_UNIQUE_ADDRESS
-# define FMT_NO_UNIQUE_ADDRESS
+// Check if RTTI is disabled.
+#ifdef FMT_USE_RTTI
+// Use the provided definition.
+#elif defined(__GXX_RTTI) || FMT_HAS_FEATURE(cxx_rtti) || defined(_CPPRTTI) || \
+ defined(__INTEL_RTTI__) || defined(__RTTI)
+// __RTTI is for EDG compilers. _CPPRTTI is for MSVC.
+# define FMT_USE_RTTI 1
+#else
+# define FMT_USE_RTTI 0
#endif
// Visibility when compiled as a shared library/object.
# define FMT_SO_VISIBILITY(value)
#endif
-#ifdef __has_builtin
-# define FMT_HAS_BUILTIN(x) __has_builtin(x)
-#else
-# define FMT_HAS_BUILTIN(x) 0
-#endif
-
#if FMT_GCC_VERSION || FMT_CLANG_VERSION
# define FMT_NOINLINE __attribute__((noinline))
#else
# define FMT_NOINLINE
#endif
-#ifndef FMT_THROW
-# if FMT_EXCEPTIONS
-# if FMT_MSC_VERSION || defined(__NVCC__)
-FMT_BEGIN_NAMESPACE
-namespace detail {
-template <typename Exception> inline void do_throw(const Exception& x) {
- // 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) throw x;
-}
-} // namespace detail
-FMT_END_NAMESPACE
-# define FMT_THROW(x) detail::do_throw(x)
+#ifdef FMT_DEPRECATED
+// Use the provided definition.
+#elif FMT_HAS_CPP14_ATTRIBUTE(deprecated)
+# define FMT_DEPRECATED [[deprecated]]
+#else
+# define FMT_DEPRECATED /* deprecated */
+#endif
+
+// Detect constexpr std::string.
+#if !FMT_USE_CONSTEVAL
+# define FMT_USE_CONSTEXPR_STRING 0
+#elif defined(__cpp_lib_constexpr_string) && \
+ __cpp_lib_constexpr_string >= 201907L
+# if FMT_CLANG_VERSION && FMT_GLIBCXX_RELEASE
+// clang + libstdc++ are able to work only starting with gcc13.3
+// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=113294
+# if FMT_GLIBCXX_RELEASE < 13
+# define FMT_USE_CONSTEXPR_STRING 0
+# elif FMT_GLIBCXX_RELEASE == 13 && __GLIBCXX__ < 20240521
+# define FMT_USE_CONSTEXPR_STRING 0
# else
-# define FMT_THROW(x) throw x
+# define FMT_USE_CONSTEXPR_STRING 1
# endif
# else
-# define FMT_THROW(x) \
- ::fmt::detail::assert_fail(__FILE__, __LINE__, (x).what())
+# define FMT_USE_CONSTEXPR_STRING 1
# endif
+#else
+# define FMT_USE_CONSTEXPR_STRING 0
+#endif
+#if FMT_USE_CONSTEXPR_STRING
+# define FMT_CONSTEXPR_STRING constexpr
+#else
+# define FMT_CONSTEXPR_STRING
#endif
-#ifndef FMT_MAYBE_UNUSED
-# if FMT_HAS_CPP17_ATTRIBUTE(maybe_unused)
-# define FMT_MAYBE_UNUSED [[maybe_unused]]
-# else
-# define FMT_MAYBE_UNUSED
-# endif
+// GCC 4.9 doesn't support qualified names in specializations.
+namespace std {
+template <typename T> struct iterator_traits<fmt::basic_appender<T>> {
+ using iterator_category = output_iterator_tag;
+ using value_type = T;
+ using difference_type =
+ decltype(static_cast<int*>(nullptr) - static_cast<int*>(nullptr));
+ using pointer = void;
+ using reference = void;
+};
+} // namespace std
+
+#ifdef FMT_THROW
+// Use the provided definition.
+#elif FMT_USE_EXCEPTIONS
+# define FMT_THROW(x) throw x
+#else
+# define FMT_THROW(x) ::fmt::assert_fail(__FILE__, __LINE__, (x).what())
#endif
-#ifndef FMT_USE_USER_DEFINED_LITERALS
-// EDG based compilers (Intel, NVIDIA, Elbrus, etc), GCC and MSVC support UDLs.
-//
-// GCC before 4.9 requires a space in `operator"" _a` which is invalid in later
-// compiler versions.
-# if (FMT_HAS_FEATURE(cxx_user_literals) || FMT_GCC_VERSION >= 409 || \
- FMT_MSC_VERSION >= 1900) && \
- (!defined(__EDG_VERSION__) || __EDG_VERSION__ >= /* UDL feature */ 480)
-# define FMT_USE_USER_DEFINED_LITERALS 1
-# else
-# define FMT_USE_USER_DEFINED_LITERALS 0
-# endif
+#ifdef __clang_analyzer__
+# define FMT_CLANG_ANALYZER 1
+#else
+# define FMT_CLANG_ANALYZER 0
#endif
// Defining FMT_REDUCE_INT_INSTANTIATIONS to 1, will reduce the number of
# define FMT_REDUCE_INT_INSTANTIATIONS 0
#endif
-// __builtin_clz is broken in clang with Microsoft CodeGen:
+FMT_BEGIN_NAMESPACE
+
+template <typename Char, typename Traits, typename Allocator>
+struct is_contiguous<std::basic_string<Char, Traits, Allocator>>
+ : std::true_type {};
+
+namespace detail {
+
+// __builtin_clz is broken in clang with Microsoft codegen:
// https://github.com/fmtlib/fmt/issues/519.
#if !FMT_MSC_VERSION
# if FMT_HAS_BUILTIN(__builtin_clz) || FMT_GCC_VERSION || FMT_ICC_VERSION
# endif
#endif
-// __builtin_ctz is broken in Intel Compiler Classic on Windows:
-// https://github.com/fmtlib/fmt/issues/2510.
-#ifndef __ICL
-# if FMT_HAS_BUILTIN(__builtin_ctz) || FMT_GCC_VERSION || FMT_ICC_VERSION || \
- defined(__NVCOMPILER)
-# define FMT_BUILTIN_CTZ(n) __builtin_ctz(n)
-# endif
-# if FMT_HAS_BUILTIN(__builtin_ctzll) || FMT_GCC_VERSION || \
- FMT_ICC_VERSION || defined(__NVCOMPILER)
-# define FMT_BUILTIN_CTZLL(n) __builtin_ctzll(n)
-# endif
-#endif
-
-#if FMT_MSC_VERSION
-# include <intrin.h> // _BitScanReverse[64], _BitScanForward[64], _umul128
-#endif
-
-// Some compilers masquerade as both MSVC and GCC-likes or otherwise support
+// Some compilers masquerade as both MSVC and GCC but otherwise support
// __builtin_clz and __builtin_clzll, so only define FMT_BUILTIN_CLZ using the
// MSVC intrinsics if the clz and clzll builtins are not available.
-#if FMT_MSC_VERSION && !defined(FMT_BUILTIN_CLZLL) && \
- !defined(FMT_BUILTIN_CTZLL)
-FMT_BEGIN_NAMESPACE
-namespace detail {
+#if FMT_MSC_VERSION && !defined(FMT_BUILTIN_CLZLL)
// Avoid Clang with Microsoft CodeGen's -Wunknown-pragmas warning.
-# if !defined(__clang__)
-# pragma intrinsic(_BitScanForward)
+# ifndef __clang__
# pragma intrinsic(_BitScanReverse)
-# if defined(_WIN64)
-# pragma intrinsic(_BitScanForward64)
+# ifdef _WIN64
# pragma intrinsic(_BitScanReverse64)
# endif
# endif
inline auto clz(uint32_t x) -> int {
+ FMT_ASSERT(x != 0, "");
+ FMT_MSC_WARNING(suppress : 6102) // Suppress a bogus static analysis warning.
unsigned long r = 0;
_BitScanReverse(&r, x);
- FMT_ASSERT(x != 0, "");
- // Static analysis complains about using uninitialized data
- // "r", but the only way that can happen is if "x" is 0,
- // which the callers guarantee to not happen.
- FMT_MSC_WARNING(suppress : 6102)
return 31 ^ static_cast<int>(r);
}
# define FMT_BUILTIN_CLZ(n) detail::clz(n)
inline auto clzll(uint64_t x) -> int {
+ FMT_ASSERT(x != 0, "");
+ FMT_MSC_WARNING(suppress : 6102) // Suppress a bogus static analysis warning.
unsigned long r = 0;
# ifdef _WIN64
_BitScanReverse64(&r, x);
// Scan the low 32 bits.
_BitScanReverse(&r, static_cast<uint32_t>(x));
# endif
- FMT_ASSERT(x != 0, "");
- FMT_MSC_WARNING(suppress : 6102) // Suppress a bogus static analysis warning.
return 63 ^ static_cast<int>(r);
}
# define FMT_BUILTIN_CLZLL(n) detail::clzll(n)
-
-inline auto ctz(uint32_t x) -> int {
- unsigned long r = 0;
- _BitScanForward(&r, x);
- FMT_ASSERT(x != 0, "");
- FMT_MSC_WARNING(suppress : 6102) // Suppress a bogus static analysis warning.
- return static_cast<int>(r);
-}
-# define FMT_BUILTIN_CTZ(n) detail::ctz(n)
-
-inline auto ctzll(uint64_t x) -> int {
- unsigned long r = 0;
- FMT_ASSERT(x != 0, "");
- FMT_MSC_WARNING(suppress : 6102) // Suppress a bogus static analysis warning.
-# ifdef _WIN64
- _BitScanForward64(&r, x);
-# else
- // Scan the low 32 bits.
- if (_BitScanForward(&r, static_cast<uint32_t>(x))) return static_cast<int>(r);
- // Scan the high 32 bits.
- _BitScanForward(&r, static_cast<uint32_t>(x >> 32));
- r += 32;
-# endif
- return static_cast<int>(r);
-}
-# define FMT_BUILTIN_CTZLL(n) detail::ctzll(n)
-} // namespace detail
-FMT_END_NAMESPACE
-#endif
-
-FMT_BEGIN_NAMESPACE
-
-template <typename Char, typename Traits, typename Allocator>
-struct is_contiguous<std::basic_string<Char, Traits, Allocator>>
- : std::true_type {};
-
-namespace detail {
+#endif // FMT_MSC_VERSION && !defined(FMT_BUILTIN_CLZLL)
FMT_CONSTEXPR inline void abort_fuzzing_if(bool condition) {
ignore_unused(condition);
#if defined(FMT_USE_STRING_VIEW)
template <typename Char> using std_string_view = std::basic_string_view<Char>;
#else
-template <typename T> struct std_string_view {};
+template <typename Char> struct std_string_view {
+ operator basic_string_view<Char>() const;
+};
+#endif
+
+template <typename Char, Char... C> struct string_literal {
+ static constexpr Char value[sizeof...(C)] = {C...};
+ constexpr operator basic_string_view<Char>() const {
+ return {value, sizeof...(C)};
+ }
+};
+#if FMT_CPLUSPLUS < 201703L
+template <typename Char, Char... C>
+constexpr Char string_literal<Char, C...>::value[sizeof...(C)];
#endif
// Implementation of std::bit_cast for pre-C++20.
-> uint128_fallback {
return {~n.hi_, ~n.lo_};
}
- friend auto operator+(const uint128_fallback& lhs,
- const uint128_fallback& rhs) -> uint128_fallback {
+ friend FMT_CONSTEXPR auto operator+(const uint128_fallback& lhs,
+ const uint128_fallback& rhs)
+ -> uint128_fallback {
auto result = uint128_fallback(lhs);
result += rhs;
return result;
}
- friend auto operator*(const uint128_fallback& lhs, uint32_t rhs)
+ friend FMT_CONSTEXPR auto operator*(const uint128_fallback& lhs, uint32_t rhs)
-> uint128_fallback {
FMT_ASSERT(lhs.hi_ == 0, "");
uint64_t hi = (lhs.lo_ >> 32) * rhs;
uint64_t new_lo = (hi << 32) + lo;
return {(hi >> 32) + (new_lo < lo ? 1 : 0), new_lo};
}
- friend auto operator-(const uint128_fallback& lhs, uint64_t rhs)
+ friend constexpr auto operator-(const uint128_fallback& lhs, uint64_t rhs)
-> uint128_fallback {
return {lhs.hi_ - (lhs.lo_ < rhs ? 1 : 0), lhs.lo_ - rhs};
}
// and 128-bit pointers to uint128_fallback.
template <typename To, typename From, FMT_ENABLE_IF(sizeof(To) > sizeof(From))>
inline auto bit_cast(const From& from) -> To {
- constexpr auto size = static_cast<int>(sizeof(From) / sizeof(unsigned));
+ constexpr auto size = static_cast<int>(sizeof(From) / sizeof(unsigned short));
struct data_t {
- unsigned value[static_cast<unsigned>(size)];
+ unsigned short value[static_cast<unsigned>(size)];
} data = bit_cast<data_t>(from);
auto result = To();
if (const_check(is_big_endian())) {
for (int i = 0; i < size; ++i)
- result = (result << num_bits<unsigned>()) | data.value[i];
+ result = (result << num_bits<unsigned short>()) | data.value[i];
} else {
for (int i = size - 1; i >= 0; --i)
- result = (result << num_bits<unsigned>()) | data.value[i];
+ result = (result << num_bits<unsigned short>()) | data.value[i];
}
return result;
}
#endif
}
-// An approximation of iterator_t for pre-C++20 systems.
-template <typename T>
-using iterator_t = decltype(std::begin(std::declval<T&>()));
-template <typename T> using sentinel_t = decltype(std::end(std::declval<T&>()));
-
-// A workaround for std::string not having mutable data() until C++17.
-template <typename Char>
-inline auto get_data(std::basic_string<Char>& s) -> Char* {
- return &s[0];
-}
-template <typename Container>
-inline auto get_data(Container& c) -> typename Container::value_type* {
- return c.data();
-}
-
// Attempts to reserve space for n extra characters in the output range.
// Returns a pointer to the reserved range or a reference to it.
template <typename OutputIt,
#if FMT_CLANG_VERSION >= 307 && !FMT_ICC_VERSION
__attribute__((no_sanitize("undefined")))
#endif
-inline auto
+FMT_CONSTEXPR20 inline auto
reserve(OutputIt it, size_t n) -> typename OutputIt::value_type* {
auto& c = get_container(it);
size_t size = c.size();
c.resize(size + n);
- return get_data(c) + size;
+ return &c[size];
}
template <typename T>
-inline auto reserve(basic_appender<T> it, size_t n) -> basic_appender<T> {
+FMT_CONSTEXPR20 inline auto reserve(basic_appender<T> it, size_t n)
+ -> basic_appender<T> {
buffer<T>& buf = get_container(it);
buf.try_reserve(buf.size() + n);
return it;
constexpr auto to_pointer(OutputIt, size_t) -> T* {
return nullptr;
}
-template <typename T> auto to_pointer(basic_appender<T> it, size_t n) -> T* {
+template <typename T> FMT_CONSTEXPR auto to_pointer(T*& ptr, size_t n) -> T* {
+ T* begin = ptr;
+ ptr += n;
+ return begin;
+}
+template <typename T>
+FMT_CONSTEXPR20 auto to_pointer(basic_appender<T> it, size_t n) -> T* {
buffer<T>& buf = get_container(it);
+ buf.try_reserve(buf.size() + n);
auto size = buf.size();
if (buf.capacity() < size + n) return nullptr;
buf.try_resize(size + n);
}
template <typename T, typename Size>
FMT_CONSTEXPR20 auto fill_n(T* out, Size count, char value) -> T* {
- if (is_constant_evaluated()) {
- return fill_n<T*, Size, T>(out, count, value);
- }
+ if (is_constant_evaluated()) return fill_n<T*, Size, T>(out, count, value);
+ static_assert(sizeof(T) == 1,
+ "sizeof(T) must be 1 to use char for initialization");
std::memset(out, value, to_unsigned(count));
return out + count;
}
*/
FMT_CONSTEXPR inline auto utf8_decode(const char* s, uint32_t* c, int* e)
-> const char* {
- constexpr const int masks[] = {0x00, 0x7f, 0x1f, 0x0f, 0x07};
- constexpr const uint32_t mins[] = {4194304, 0, 128, 2048, 65536};
- constexpr const int shiftc[] = {0, 18, 12, 6, 0};
- constexpr const int shifte[] = {0, 6, 4, 2, 0};
+ constexpr int masks[] = {0x00, 0x7f, 0x1f, 0x0f, 0x07};
+ constexpr uint32_t mins[] = {4194304, 0, 128, 2048, 65536};
+ constexpr int shiftc[] = {0, 18, 12, 6, 0};
+ constexpr int shifte[] = {0, 6, 4, 2, 0};
int len = "\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\0\0\0\0\0\0\0\0\2\2\2\2\3\3\4"
[static_cast<unsigned char>(*s) >> 3];
string_view(ptr, error ? 1 : to_unsigned(end - buf_ptr)));
return result ? (error ? buf_ptr + 1 : end) : nullptr;
};
+
auto p = s.data();
const size_t block_size = 4; // utf8_decode always reads blocks of 4 chars.
if (s.size() >= block_size) {
if (!p) return;
}
}
- if (auto num_chars_left = s.data() + s.size() - p) {
- char buf[2 * block_size - 1] = {};
- copy<char>(p, p + num_chars_left, buf);
- const char* buf_ptr = buf;
- do {
- auto end = decode(buf_ptr, p);
- if (!end) return;
- p += end - buf_ptr;
- buf_ptr = end;
- } while (buf_ptr - buf < num_chars_left);
- }
+ auto num_chars_left = to_unsigned(s.data() + s.size() - p);
+ if (num_chars_left == 0) return;
+
+ // Suppress bogus -Wstringop-overflow.
+ if (FMT_GCC_VERSION) num_chars_left &= 3;
+ char buf[2 * block_size - 1] = {};
+ copy<char>(p, p + num_chars_left, buf);
+ const char* buf_ptr = buf;
+ do {
+ auto end = decode(buf_ptr, p);
+ if (!end) return;
+ p += end - buf_ptr;
+ buf_ptr = end;
+ } while (buf_ptr < buf + num_chars_left);
}
-template <typename Char>
-inline auto compute_width(basic_string_view<Char> s) -> size_t {
- return s.size();
-}
-
-// Computes approximate display width of a UTF-8 string.
-FMT_CONSTEXPR inline auto compute_width(string_view s) -> size_t {
- size_t num_code_points = 0;
- // It is not a lambda for compatibility with C++14.
- struct count_code_points {
- size_t* count;
- FMT_CONSTEXPR auto operator()(uint32_t cp, string_view) const -> bool {
- *count += detail::to_unsigned(
- 1 +
- (cp >= 0x1100 &&
+FMT_CONSTEXPR inline auto display_width_of(uint32_t cp) noexcept -> size_t {
+ return to_unsigned(
+ 1 + (cp >= 0x1100 &&
(cp <= 0x115f || // Hangul Jamo init. consonants
cp == 0x2329 || // LEFT-POINTING ANGLE BRACKET
cp == 0x232a || // RIGHT-POINTING ANGLE BRACKET
(cp >= 0x1f300 && cp <= 0x1f64f) ||
// Supplemental Symbols and Pictographs:
(cp >= 0x1f900 && cp <= 0x1f9ff))));
- return true;
- }
- };
- // We could avoid branches by using utf8_decode directly.
- for_each_codepoint(s, count_code_points{&num_code_points});
- return num_code_points;
-}
-
-template <typename Char>
-inline auto code_point_index(basic_string_view<Char> s, size_t n) -> size_t {
- size_t size = s.size();
- return n < size ? n : size;
-}
-
-// Calculates the index of the nth code point in a UTF-8 string.
-inline auto code_point_index(string_view s, size_t n) -> size_t {
- size_t result = s.size();
- const char* begin = s.begin();
- for_each_codepoint(s, [begin, &n, &result](uint32_t, string_view sv) {
- if (n != 0) {
- --n;
- return true;
- }
- result = to_unsigned(sv.begin() - begin);
- return false;
- });
- return result;
}
template <typename T> struct is_integral : std::is_integral<T> {};
!std::is_same<T, char>::value &&
!std::is_same<T, wchar_t>::value>;
-#ifndef FMT_USE_FLOAT
-# define FMT_USE_FLOAT 1
-#endif
-#ifndef FMT_USE_DOUBLE
-# define FMT_USE_DOUBLE 1
-#endif
-#ifndef FMT_USE_LONG_DOUBLE
-# define FMT_USE_LONG_DOUBLE 1
-#endif
-
#if defined(FMT_USE_FLOAT128)
// Use the provided definition.
-#elif FMT_CLANG_VERSION && FMT_HAS_INCLUDE(<quadmath.h>)
+#elif FMT_CLANG_VERSION >= 309 && FMT_HAS_INCLUDE(<quadmath.h>)
# define FMT_USE_FLOAT128 1
#elif FMT_GCC_VERSION && defined(_GLIBCXX_USE_FLOAT128) && \
!defined(__STRICT_ANSI__)
#if FMT_USE_FLOAT128
using float128 = __float128;
#else
-using float128 = void;
+struct float128 {};
#endif
template <typename T> using is_float128 = std::is_same<T, float128>;
-template <typename T>
-using is_floating_point =
- bool_constant<std::is_floating_point<T>::value || is_float128<T>::value>;
+template <typename T> struct is_floating_point : std::is_floating_point<T> {};
+template <> struct is_floating_point<float128> : std::true_type {};
-template <typename T, bool = std::is_floating_point<T>::value>
+template <typename T, bool = is_floating_point<T>::value>
struct is_fast_float : bool_constant<std::numeric_limits<T>::is_iec559 &&
sizeof(T) <= sizeof(double)> {};
template <typename T> struct is_fast_float<T, false> : std::false_type {};
+template <typename T>
+using fast_float_t = conditional_t<sizeof(T) == sizeof(double), double, float>;
+
template <typename T>
using is_double_double = bool_constant<std::numeric_limits<T>::digits == 106>;
# define FMT_USE_FULL_CACHE_DRAGONBOX 0
#endif
-template <typename T, typename Enable = void>
-struct is_locale : std::false_type {};
-template <typename T>
-struct is_locale<T, void_t<decltype(T::classic())>> : std::true_type {};
+// An allocator that uses malloc/free to allow removing dependency on the C++
+// standard libary runtime. std::decay is used for back_inserter to be found by
+// ADL when applied to memory_buffer.
+template <typename T> struct allocator : private std::decay<void> {
+ using value_type = T;
+
+ auto allocate(size_t n) -> T* {
+ FMT_ASSERT(n <= max_value<size_t>() / sizeof(T), "");
+ T* p = static_cast<T*>(malloc(n * sizeof(T)));
+ if (!p) FMT_THROW(std::bad_alloc());
+ return p;
+ }
+
+ void deallocate(T* p, size_t) { free(p); }
+
+ constexpr friend auto operator==(allocator, allocator) noexcept -> bool {
+ return true; // All instances of this allocator are equivalent.
+ }
+ constexpr friend auto operator!=(allocator, allocator) noexcept -> bool {
+ return false;
+ }
+};
+
+template <typename Formatter>
+FMT_CONSTEXPR auto maybe_set_debug_format(Formatter& f, bool set)
+ -> decltype(f.set_debug_format(set)) {
+ f.set_debug_format(set);
+}
+template <typename Formatter>
+FMT_CONSTEXPR void maybe_set_debug_format(Formatter&, ...) {}
+
} // namespace detail
FMT_BEGIN_EXPORT
* converted to `std::string` with `to_string(out)`.
*/
template <typename T, size_t SIZE = inline_buffer_size,
- typename Allocator = std::allocator<T>>
+ typename Allocator = detail::allocator<T>>
class basic_memory_buffer : public detail::buffer<T> {
private:
T store_[SIZE];
if (size > new_capacity)
new_capacity = size;
else if (new_capacity > max_size)
- new_capacity = size > max_size ? size : max_size;
+ new_capacity = max_of(size, max_size);
T* old_data = buf.data();
T* new_data = self.alloc_.allocate(new_capacity);
// Suppress a bogus -Wstringop-overflow in gcc 13.1 (#3481).
using value_type = T;
using const_reference = const T&;
- FMT_CONSTEXPR20 explicit basic_memory_buffer(
+ FMT_CONSTEXPR explicit basic_memory_buffer(
const Allocator& alloc = Allocator())
: detail::buffer<T>(grow), alloc_(alloc) {
this->set(store_, SIZE);
FMT_CONSTEXPR20 ~basic_memory_buffer() { deallocate(); }
private:
+ template <typename Alloc = Allocator,
+ FMT_ENABLE_IF(std::allocator_traits<Alloc>::
+ propagate_on_container_move_assignment::value)>
+ FMT_CONSTEXPR20 auto move_alloc(basic_memory_buffer& other) -> bool {
+ alloc_ = std::move(other.alloc_);
+ return true;
+ }
+ // If the allocator does not propagate then copy the data from other.
+ template <typename Alloc = Allocator,
+ FMT_ENABLE_IF(!std::allocator_traits<Alloc>::
+ propagate_on_container_move_assignment::value)>
+ FMT_CONSTEXPR20 auto move_alloc(basic_memory_buffer& other) -> bool {
+ T* data = other.data();
+ if (alloc_ == other.alloc_ || data == other.store_) return true;
+ size_t size = other.size();
+ // Perform copy operation, allocators are different.
+ this->resize(size);
+ detail::copy<T>(data, data + size, this->data());
+ return false;
+ }
+
// Move data from other to this buffer.
FMT_CONSTEXPR20 void move(basic_memory_buffer& other) {
- alloc_ = std::move(other.alloc_);
T* data = other.data();
size_t size = other.size(), capacity = other.capacity();
+ if (!move_alloc(other)) return;
if (data == other.store_) {
this->set(store_, capacity);
detail::copy<T>(other.store_, other.store_ + size, store_);
/// Resizes the buffer to contain `count` elements. If T is a POD type new
/// elements may not be initialized.
- FMT_CONSTEXPR20 void resize(size_t count) { this->try_resize(count); }
+ FMT_CONSTEXPR void resize(size_t count) { this->try_resize(count); }
/// Increases the buffer capacity to `new_capacity`.
void reserve(size_t new_capacity) { this->try_reserve(new_capacity); }
using detail::buffer<T>::append;
template <typename ContiguousRange>
- void append(const ContiguousRange& range) {
+ FMT_CONSTEXPR20 void append(const ContiguousRange& range) {
append(range.data(), range.data() + range.size());
}
};
using memory_buffer = basic_memory_buffer<char>;
-template <typename T, size_t SIZE, typename Allocator>
-struct is_contiguous<basic_memory_buffer<T, SIZE, Allocator>> : std::true_type {
+template <size_t SIZE>
+FMT_NODISCARD auto to_string(const basic_memory_buffer<char, SIZE>& buf)
+ -> std::string {
+ auto size = buf.size();
+ detail::assume(size < std::string().max_size());
+ return {buf.data(), size};
+}
+
+// A writer to a buffered stream. It doesn't own the underlying stream.
+class writer {
+ private:
+ detail::buffer<char>* buf_;
+
+ // We cannot create a file buffer in advance because any write to a FILE may
+ // invalidate it.
+ FILE* file_;
+
+ public:
+ inline writer(FILE* f) : buf_(nullptr), file_(f) {}
+ inline writer(detail::buffer<char>& buf) : buf_(&buf) {}
+
+ /// Formats `args` according to specifications in `fmt` and writes the
+ /// output to the file.
+ template <typename... T> void print(format_string<T...> fmt, T&&... args) {
+ if (buf_)
+ fmt::format_to(appender(*buf_), fmt, std::forward<T>(args)...);
+ else
+ fmt::print(file_, fmt, std::forward<T>(args)...);
+ }
};
-FMT_END_EXPORT
-namespace detail {
-FMT_API auto write_console(int fd, string_view text) -> bool;
-FMT_API void print(std::FILE*, string_view);
-} // namespace detail
+class string_buffer {
+ private:
+ std::string str_;
+ detail::container_buffer<std::string> buf_;
-FMT_BEGIN_EXPORT
+ public:
+ inline string_buffer() : buf_(str_) {}
+
+ inline operator writer() { return buf_; }
+ inline auto str() -> std::string& { return str_; }
+};
+
+template <typename T, size_t SIZE, typename Allocator>
+struct is_contiguous<basic_memory_buffer<T, SIZE, Allocator>> : std::true_type {
+};
// Suppress a misleading warning in older versions of clang.
-#if FMT_CLANG_VERSION
-# pragma clang diagnostic ignored "-Wweak-vtables"
-#endif
+FMT_PRAGMA_CLANG(diagnostic ignored "-Wweak-vtables")
/// An error reported from a formatting function.
class FMT_SO_VISIBILITY("default") format_error : public std::runtime_error {
using std::runtime_error::runtime_error;
};
-namespace detail_exported {
-#if FMT_USE_NONTYPE_TEMPLATE_ARGS
+class loc_value;
+
+FMT_END_EXPORT
+namespace detail {
+FMT_API auto write_console(int fd, string_view text) -> bool;
+FMT_API void print(FILE*, string_view);
+} // namespace detail
+
+namespace detail {
template <typename Char, size_t N> struct fixed_string {
- constexpr fixed_string(const Char (&str)[N]) {
- detail::copy<Char, const Char*, Char*>(static_cast<const Char*>(str),
- str + N, data);
+ FMT_CONSTEXPR20 fixed_string(const Char (&s)[N]) {
+ detail::copy<Char, const Char*, Char*>(static_cast<const Char*>(s), s + N,
+ data);
}
Char data[N] = {};
};
-#endif
// Converts a compile-time string to basic_string_view.
-template <typename Char, size_t N>
+FMT_EXPORT template <typename Char, size_t N>
constexpr auto compile_string_to_view(const Char (&s)[N])
-> basic_string_view<Char> {
// Remove trailing NUL character if needed. Won't be present if this is used
// with a raw character array (i.e. not defined as a string).
return {s, N - (std::char_traits<Char>::to_int_type(s[N - 1]) == 0 ? 1 : 0)};
}
-template <typename Char>
+FMT_EXPORT template <typename Char>
constexpr auto compile_string_to_view(basic_string_view<Char> s)
-> basic_string_view<Char> {
return s;
}
-} // namespace detail_exported
-// A generic formatting context with custom output iterator and character
-// (code unit) support. Char is the format string code unit type which can be
-// different from OutputIt::value_type.
-template <typename OutputIt, typename Char> class generic_context {
- private:
- OutputIt out_;
- basic_format_args<generic_context> args_;
- detail::locale_ref loc_;
+// Returns true if value is negative, false otherwise.
+// Same as `value < 0` but doesn't produce warnings if T is an unsigned type.
+template <typename T, FMT_ENABLE_IF(is_signed<T>::value)>
+constexpr auto is_negative(T value) -> bool {
+ return value < 0;
+}
+template <typename T, FMT_ENABLE_IF(!is_signed<T>::value)>
+constexpr auto is_negative(T) -> bool {
+ return false;
+}
- public:
- using char_type = Char;
- using iterator = OutputIt;
- using parse_context_type = basic_format_parse_context<Char>;
- template <typename T> using formatter_type = formatter<T, Char>;
+// Smallest of uint32_t, uint64_t, uint128_t that is large enough to
+// represent all values of an integral type T.
+template <typename T>
+using uint32_or_64_or_128_t =
+ conditional_t<num_bits<T>() <= 32 && !FMT_REDUCE_INT_INSTANTIATIONS,
+ uint32_t,
+ conditional_t<num_bits<T>() <= 64, uint64_t, uint128_t>>;
+template <typename T>
+using uint64_or_128_t = conditional_t<num_bits<T>() <= 64, uint64_t, uint128_t>;
- constexpr generic_context(OutputIt out,
- basic_format_args<generic_context> ctx_args,
- detail::locale_ref loc = {})
- : out_(out), args_(ctx_args), loc_(loc) {}
- generic_context(generic_context&&) = default;
- generic_context(const generic_context&) = delete;
- void operator=(const generic_context&) = delete;
+#define FMT_POWERS_OF_10(factor) \
+ factor * 10, (factor) * 100, (factor) * 1000, (factor) * 10000, \
+ (factor) * 100000, (factor) * 1000000, (factor) * 10000000, \
+ (factor) * 100000000, (factor) * 1000000000
- constexpr auto arg(int id) const -> basic_format_arg<generic_context> {
- return args_.get(id);
- }
- auto arg(basic_string_view<Char> name) -> basic_format_arg<generic_context> {
- return args_.get(name);
- }
- FMT_CONSTEXPR auto arg_id(basic_string_view<Char> name) -> int {
- return args_.get_id(name);
- }
- auto args() const -> const basic_format_args<generic_context>& {
- return args_;
- }
+// Converts value in the range [0, 100) to a string.
+// GCC generates slightly better code when value is pointer-size.
+inline auto digits2(size_t value) -> const char* {
+ // Align data since unaligned access may be slower when crossing a
+ // hardware-specific boundary.
+ alignas(2) static const char data[] =
+ "0001020304050607080910111213141516171819"
+ "2021222324252627282930313233343536373839"
+ "4041424344454647484950515253545556575859"
+ "6061626364656667686970717273747576777879"
+ "8081828384858687888990919293949596979899";
+ return &data[value * 2];
+}
- FMT_CONSTEXPR auto out() -> iterator { return out_; }
+template <typename Char> constexpr auto getsign(sign s) -> Char {
+ return static_cast<char>(((' ' << 24) | ('+' << 16) | ('-' << 8)) >>
+ (static_cast<int>(s) * 8));
+}
- void advance_to(iterator it) {
- if (!detail::is_back_insert_iterator<iterator>()) out_ = it;
- }
-
- FMT_CONSTEXPR auto locale() -> detail::locale_ref { return loc_; }
-};
-
-class loc_value {
- private:
- basic_format_arg<format_context> value_;
-
- public:
- template <typename T, FMT_ENABLE_IF(!detail::is_float128<T>::value)>
- loc_value(T value) : value_(detail::make_arg<format_context>(value)) {}
-
- template <typename T, FMT_ENABLE_IF(detail::is_float128<T>::value)>
- loc_value(T) {}
-
- template <typename Visitor> auto visit(Visitor&& vis) -> decltype(vis(0)) {
- return value_.visit(vis);
- }
-};
-
-// A locale facet that formats values in UTF-8.
-// It is parameterized on the locale to avoid the heavy <locale> include.
-template <typename Locale> class format_facet : public Locale::facet {
- private:
- std::string separator_;
- std::string grouping_;
- std::string decimal_point_;
-
- protected:
- virtual auto do_put(appender out, loc_value val,
- const format_specs& specs) const -> bool;
-
- public:
- static FMT_API typename Locale::id id;
-
- explicit format_facet(Locale& loc);
- explicit format_facet(string_view sep = "",
- std::initializer_list<unsigned char> g = {3},
- std::string decimal_point = ".")
- : separator_(sep.data(), sep.size()),
- grouping_(g.begin(), g.end()),
- decimal_point_(decimal_point) {}
-
- auto put(appender out, loc_value val, const format_specs& specs) const
- -> bool {
- return do_put(out, val, specs);
- }
-};
-
-FMT_END_EXPORT
-
-namespace detail {
-
-// Returns true if value is negative, false otherwise.
-// Same as `value < 0` but doesn't produce warnings if T is an unsigned type.
-template <typename T, FMT_ENABLE_IF(is_signed<T>::value)>
-constexpr auto is_negative(T value) -> bool {
- return value < 0;
-}
-template <typename T, FMT_ENABLE_IF(!is_signed<T>::value)>
-constexpr auto is_negative(T) -> bool {
- return false;
-}
-
-template <typename T>
-FMT_CONSTEXPR auto is_supported_floating_point(T) -> bool {
- if (std::is_same<T, float>()) return FMT_USE_FLOAT;
- if (std::is_same<T, double>()) return FMT_USE_DOUBLE;
- if (std::is_same<T, long double>()) return FMT_USE_LONG_DOUBLE;
- return true;
-}
-
-// Smallest of uint32_t, uint64_t, uint128_t that is large enough to
-// represent all values of an integral type T.
-template <typename T>
-using uint32_or_64_or_128_t =
- conditional_t<num_bits<T>() <= 32 && !FMT_REDUCE_INT_INSTANTIATIONS,
- uint32_t,
- conditional_t<num_bits<T>() <= 64, uint64_t, uint128_t>>;
-template <typename T>
-using uint64_or_128_t = conditional_t<num_bits<T>() <= 64, uint64_t, uint128_t>;
-
-#define FMT_POWERS_OF_10(factor) \
- factor * 10, (factor) * 100, (factor) * 1000, (factor) * 10000, \
- (factor) * 100000, (factor) * 1000000, (factor) * 10000000, \
- (factor) * 100000000, (factor) * 1000000000
-
-// Converts value in the range [0, 100) to a string.
-constexpr auto digits2(size_t value) -> const char* {
- // GCC generates slightly better code when value is pointer-size.
- return &"0001020304050607080910111213141516171819"
- "2021222324252627282930313233343536373839"
- "4041424344454647484950515253545556575859"
- "6061626364656667686970717273747576777879"
- "8081828384858687888990919293949596979899"[value * 2];
-}
-
-// Sign is a template parameter to workaround a bug in gcc 4.8.
-template <typename Char, typename Sign> constexpr auto sign(Sign s) -> Char {
-#if !FMT_GCC_VERSION || FMT_GCC_VERSION >= 604
- static_assert(std::is_same<Sign, sign_t>::value, "");
-#endif
- return static_cast<Char>("\0-+ "[s]);
-}
-
-template <typename T> FMT_CONSTEXPR auto count_digits_fallback(T n) -> int {
- int count = 1;
- for (;;) {
- // Integer division is slow so do it for a group of four digits instead
- // of for every digit. The idea comes from the talk by Alexandrescu
- // "Three Optimization Tips for C++". See speed-test for a comparison.
- if (n < 10) return count;
- if (n < 100) return count + 1;
- if (n < 1000) return count + 2;
- if (n < 10000) return count + 3;
- n /= 10000u;
- count += 4;
+template <typename T> FMT_CONSTEXPR auto count_digits_fallback(T n) -> int {
+ int count = 1;
+ for (;;) {
+ // Integer division is slow so do it for a group of four digits instead
+ // of for every digit. The idea comes from the talk by Alexandrescu
+ // "Three Optimization Tips for C++". See speed-test for a comparison.
+ if (n < 10) return count;
+ if (n < 100) return count + 1;
+ if (n < 1000) return count + 2;
+ if (n < 10000) return count + 3;
+ n /= 10000u;
+ count += 4;
}
}
#if FMT_USE_INT128
10, 11, 11, 11, 12, 12, 12, 13, 13, 13, 13, 14, 14, 14, 15, 15,
15, 16, 16, 16, 16, 17, 17, 17, 18, 18, 18, 19, 19, 19, 19, 20};
auto t = bsr2log10[FMT_BUILTIN_CLZLL(n | 1) ^ 63];
- static constexpr const uint64_t zero_or_powers_of_10[] = {
+ static constexpr uint64_t zero_or_powers_of_10[] = {
0, 0, FMT_POWERS_OF_10(1U), FMT_POWERS_OF_10(1000000000ULL),
10000000000000000000ULL};
return t - (n < zero_or_powers_of_10[t]);
// except for n == 0 in which case count_digits returns 1.
FMT_CONSTEXPR20 inline auto count_digits(uint64_t n) -> int {
#ifdef FMT_BUILTIN_CLZLL
- if (!is_constant_evaluated()) return do_count_digits(n);
+ if (!is_constant_evaluated() && !FMT_OPTIMIZE_SIZE) return do_count_digits(n);
#endif
return count_digits_fallback(n);
}
// Optional version of count_digits for better performance on 32-bit platforms.
FMT_CONSTEXPR20 inline auto count_digits(uint32_t n) -> int {
#ifdef FMT_BUILTIN_CLZ
- if (!is_constant_evaluated()) {
- return do_count_digits(n);
- }
+ if (!is_constant_evaluated() && !FMT_OPTIMIZE_SIZE) return do_count_digits(n);
#endif
return count_digits_fallback(n);
}
return decimal_point_impl<wchar_t>(loc);
}
+#ifndef FMT_HEADER_ONLY
+FMT_BEGIN_EXPORT
+extern template FMT_API auto thousands_sep_impl<char>(locale_ref)
+ -> thousands_sep_result<char>;
+extern template FMT_API auto thousands_sep_impl<wchar_t>(locale_ref)
+ -> thousands_sep_result<wchar_t>;
+extern template FMT_API auto decimal_point_impl(locale_ref) -> char;
+extern template FMT_API auto decimal_point_impl(locale_ref) -> wchar_t;
+FMT_END_EXPORT
+#endif // FMT_HEADER_ONLY
+
// Compares two characters for equality.
template <typename Char> auto equal2(const Char* lhs, const char* rhs) -> bool {
return lhs[0] == Char(rhs[0]) && lhs[1] == Char(rhs[1]);
return memcmp(lhs, rhs, 2) == 0;
}
-// Copies two characters from src to dst.
+// Writes a two-digit value to out.
template <typename Char>
-FMT_CONSTEXPR20 FMT_INLINE void copy2(Char* dst, const char* src) {
- if (!is_constant_evaluated() && sizeof(Char) == sizeof(char)) {
- memcpy(dst, src, 2);
+FMT_CONSTEXPR20 FMT_INLINE void write2digits(Char* out, size_t value) {
+ if (!is_constant_evaluated() && std::is_same<Char, char>::value &&
+ !FMT_OPTIMIZE_SIZE) {
+ memcpy(out, digits2(value), 2);
return;
}
- *dst++ = static_cast<Char>(*src++);
- *dst = static_cast<Char>(*src);
+ *out++ = static_cast<Char>('0' + value / 10);
+ *out = static_cast<Char>('0' + value % 10);
}
-template <typename Iterator> struct format_decimal_result {
- Iterator begin;
- Iterator end;
-};
-
-// Formats a decimal unsigned integer value writing into out pointing to a
-// buffer of specified size. The caller must ensure that the buffer is large
-// enough.
+// Formats a decimal unsigned integer value writing to out pointing to a buffer
+// of specified size. The caller must ensure that the buffer is large enough.
template <typename Char, typename UInt>
-FMT_CONSTEXPR20 auto format_decimal(Char* out, UInt value, int size)
- -> format_decimal_result<Char*> {
+FMT_CONSTEXPR20 auto do_format_decimal(Char* out, UInt value, int size)
+ -> Char* {
FMT_ASSERT(size >= count_digits(value), "invalid digit count");
- out += size;
- Char* end = out;
+ unsigned n = to_unsigned(size);
while (value >= 100) {
// Integer division is slow so do it for a group of two digits instead
// of for every digit. The idea comes from the talk by Alexandrescu
// "Three Optimization Tips for C++". See speed-test for a comparison.
- out -= 2;
- copy2(out, digits2(static_cast<size_t>(value % 100)));
+ n -= 2;
+ write2digits(out + n, static_cast<unsigned>(value % 100));
value /= 100;
}
- if (value < 10) {
- *--out = static_cast<Char>('0' + value);
- return {out, end};
+ if (value >= 10) {
+ n -= 2;
+ write2digits(out + n, static_cast<unsigned>(value));
+ } else {
+ out[--n] = static_cast<Char>('0' + value);
}
- out -= 2;
- copy2(out, digits2(static_cast<size_t>(value)));
- return {out, end};
+ return out + n;
+}
+
+template <typename Char, typename UInt>
+FMT_CONSTEXPR FMT_INLINE auto format_decimal(Char* out, UInt value,
+ int num_digits) -> Char* {
+ do_format_decimal(out, value, num_digits);
+ return out + num_digits;
}
-template <typename Char, typename UInt, typename Iterator,
- FMT_ENABLE_IF(!std::is_pointer<remove_cvref_t<Iterator>>::value)>
-FMT_CONSTEXPR inline auto format_decimal(Iterator out, UInt value, int size)
- -> format_decimal_result<Iterator> {
+template <typename Char, typename UInt, typename OutputIt,
+ FMT_ENABLE_IF(!std::is_pointer<remove_cvref_t<OutputIt>>::value)>
+FMT_CONSTEXPR auto format_decimal(OutputIt out, UInt value, int num_digits)
+ -> OutputIt {
+ if (auto ptr = to_pointer<Char>(out, to_unsigned(num_digits))) {
+ do_format_decimal(ptr, value, num_digits);
+ return out;
+ }
// Buffer is large enough to hold all digits (digits10 + 1).
- Char buffer[digits10<UInt>() + 1] = {};
- auto end = format_decimal(buffer, value, size).end;
- return {out, detail::copy_noinline<Char>(buffer, end, out)};
+ char buffer[digits10<UInt>() + 1];
+ if (is_constant_evaluated()) fill_n(buffer, sizeof(buffer), '\0');
+ do_format_decimal(buffer, value, num_digits);
+ return copy_noinline<Char>(buffer, buffer + num_digits, out);
}
-template <unsigned BASE_BITS, typename Char, typename UInt>
-FMT_CONSTEXPR auto format_uint(Char* buffer, UInt value, int num_digits,
- bool upper = false) -> Char* {
- buffer += num_digits;
- Char* end = buffer;
+template <typename Char, typename UInt>
+FMT_CONSTEXPR auto do_format_base2e(int base_bits, Char* out, UInt value,
+ int size, bool upper = false) -> Char* {
+ out += size;
do {
const char* digits = upper ? "0123456789ABCDEF" : "0123456789abcdef";
- unsigned digit = static_cast<unsigned>(value & ((1 << BASE_BITS) - 1));
- *--buffer = static_cast<Char>(BASE_BITS < 4 ? static_cast<char>('0' + digit)
- : digits[digit]);
- } while ((value >>= BASE_BITS) != 0);
- return end;
+ unsigned digit = static_cast<unsigned>(value & ((1u << base_bits) - 1));
+ *--out = static_cast<Char>(base_bits < 4 ? static_cast<char>('0' + digit)
+ : digits[digit]);
+ } while ((value >>= base_bits) != 0);
+ return out;
+}
+
+// Formats an unsigned integer in the power of two base (binary, octal, hex).
+template <typename Char, typename UInt>
+FMT_CONSTEXPR auto format_base2e(int base_bits, Char* out, UInt value,
+ int num_digits, bool upper = false) -> Char* {
+ do_format_base2e(base_bits, out, value, num_digits, upper);
+ return out + num_digits;
}
-template <unsigned BASE_BITS, typename Char, typename It, typename UInt>
-FMT_CONSTEXPR inline auto format_uint(It out, UInt value, int num_digits,
- bool upper = false) -> It {
+template <typename Char, typename OutputIt, typename UInt,
+ FMT_ENABLE_IF(is_back_insert_iterator<OutputIt>::value)>
+FMT_CONSTEXPR inline auto format_base2e(int base_bits, OutputIt out, UInt value,
+ int num_digits, bool upper = false)
+ -> OutputIt {
if (auto ptr = to_pointer<Char>(out, to_unsigned(num_digits))) {
- format_uint<BASE_BITS>(ptr, value, num_digits, upper);
+ format_base2e(base_bits, ptr, value, num_digits, upper);
return out;
}
- // Buffer should be large enough to hold all digits (digits / BASE_BITS + 1).
- char buffer[num_bits<UInt>() / BASE_BITS + 1] = {};
- format_uint<BASE_BITS>(buffer, value, num_digits, upper);
+ // Make buffer large enough for any base.
+ char buffer[num_bits<UInt>()];
+ if (is_constant_evaluated()) fill_n(buffer, sizeof(buffer), '\0');
+ format_base2e(base_bits, buffer, value, num_digits, upper);
return detail::copy_noinline<Char>(buffer, buffer + num_digits, out);
}
public:
FMT_API explicit utf8_to_utf16(string_view s);
- operator basic_string_view<wchar_t>() const { return {&buffer_[0], size()}; }
- auto size() const -> size_t { return buffer_.size() - 1; }
- auto c_str() const -> const wchar_t* { return &buffer_[0]; }
- auto str() const -> std::wstring { return {&buffer_[0], size()}; }
+ inline operator basic_string_view<wchar_t>() const {
+ return {&buffer_[0], size()};
+ }
+ inline auto size() const -> size_t { return buffer_.size() - 1; }
+ inline auto c_str() const -> const wchar_t* { return &buffer_[0]; }
+ inline auto str() const -> std::wstring { return {&buffer_[0], size()}; }
};
enum class to_utf8_error_policy { abort, replace };
explicit to_utf8(basic_string_view<WChar> s,
to_utf8_error_policy policy = to_utf8_error_policy::abort) {
static_assert(sizeof(WChar) == 2 || sizeof(WChar) == 4,
- "Expect utf16 or utf32");
- if (!convert(s, policy))
+ "expected utf16 or utf32");
+ if (!convert(s, policy)) {
FMT_THROW(std::runtime_error(sizeof(WChar) == 2 ? "invalid utf16"
: "invalid utf32"));
+ }
}
operator string_view() const { return string_view(&buffer_[0], size()); }
auto size() const -> size_t { return buffer_.size() - 1; }
if (policy == to_utf8_error_policy::abort) return false;
buf.append(string_view("\xEF\xBF\xBD"));
--p;
- } else {
- c = (c << 10) + static_cast<uint32_t>(*p) - 0x35fdc00;
+ continue;
}
- } else if (c < 0x80) {
+ c = (c << 10) + static_cast<uint32_t>(*p) - 0x35fdc00;
+ }
+ if (c < 0x80) {
buf.push_back(static_cast<char>(c));
} else if (c < 0x800) {
buf.push_back(static_cast<char>(0xc0 | (c >> 6)));
};
// Computes 128-bit result of multiplication of two 64-bit unsigned integers.
-inline auto umul128(uint64_t x, uint64_t y) noexcept -> uint128_fallback {
+FMT_INLINE auto umul128(uint64_t x, uint64_t y) noexcept -> uint128_fallback {
#if FMT_USE_INT128
auto p = static_cast<uint128_opt>(x) * static_cast<uint128_opt>(y);
return {static_cast<uint64_t>(p >> 64), static_cast<uint64_t>(p)};
: std::numeric_limits<Float>::max_exponent - 1;
}
+FMT_CONSTEXPR inline auto compute_exp_size(int exp) -> int {
+ auto prefix_size = 2; // sign + 'e'
+ auto abs_exp = exp >= 0 ? exp : -exp;
+ if (abs_exp < 100) return prefix_size + 2;
+ return prefix_size + (abs_exp >= 1000 ? 4 : 3);
+}
+
// Writes the exponent exp in the form "[+-]d{2,3}" to buffer.
-template <typename Char, typename It>
-FMT_CONSTEXPR auto write_exponent(int exp, It it) -> It {
+template <typename Char, typename OutputIt>
+FMT_CONSTEXPR auto write_exponent(int exp, OutputIt out) -> OutputIt {
FMT_ASSERT(-10000 < exp && exp < 10000, "exponent out of range");
if (exp < 0) {
- *it++ = static_cast<Char>('-');
+ *out++ = static_cast<Char>('-');
exp = -exp;
} else {
- *it++ = static_cast<Char>('+');
- }
- if (exp >= 100) {
- const char* top = digits2(to_unsigned(exp / 100));
- if (exp >= 1000) *it++ = static_cast<Char>(top[0]);
- *it++ = static_cast<Char>(top[1]);
- exp %= 100;
+ *out++ = static_cast<Char>('+');
}
- const char* d = digits2(to_unsigned(exp));
- *it++ = static_cast<Char>(d[0]);
- *it++ = static_cast<Char>(d[1]);
- return it;
+ auto uexp = static_cast<uint32_t>(exp);
+ if (is_constant_evaluated()) {
+ if (uexp < 10) *out++ = '0';
+ return format_decimal<Char>(out, uexp, count_digits(uexp));
+ }
+ if (uexp >= 100u) {
+ const char* top = digits2(uexp / 100);
+ if (uexp >= 1000u) *out++ = static_cast<Char>(top[0]);
+ *out++ = static_cast<Char>(top[1]);
+ uexp %= 100;
+ }
+ const char* d = digits2(uexp);
+ *out++ = static_cast<Char>(d[0]);
+ *out++ = static_cast<Char>(d[1]);
+ return out;
}
// A floating-point number f * pow(2, e) where F is an unsigned type.
F f;
int e;
- static constexpr const int num_significand_bits =
+ static constexpr int num_significand_bits =
static_cast<int>(sizeof(F) * num_bits<unsigned char>());
constexpr basic_fp() : f(0), e(0) {}
return static_cast<convert_float_result<T>>(value);
}
+template <bool C, typename T, typename F, FMT_ENABLE_IF(C)>
+auto select(T true_value, F) -> T {
+ return true_value;
+}
+template <bool C, typename T, typename F, FMT_ENABLE_IF(!C)>
+auto select(T, F false_value) -> F {
+ return false_value;
+}
+
template <typename Char, typename OutputIt>
-FMT_NOINLINE FMT_CONSTEXPR auto fill(OutputIt it, size_t n, const fill_t& fill)
- -> OutputIt {
- auto fill_size = fill.size();
- if (fill_size == 1) return detail::fill_n(it, n, fill.template get<Char>());
- if (const Char* data = fill.template data<Char>()) {
+FMT_CONSTEXPR FMT_NOINLINE auto fill(OutputIt it, size_t n,
+ const basic_specs& specs) -> OutputIt {
+ auto fill_size = specs.fill_size();
+ if (fill_size == 1) return detail::fill_n(it, n, specs.fill_unit<Char>());
+ if (const Char* data = specs.fill<Char>()) {
for (size_t i = 0; i < n; ++i) it = copy<Char>(data, data + fill_size, it);
}
return it;
// Writes the output of f, padded according to format specifications in specs.
// size: output size in code units.
// width: output display width in (terminal) column positions.
-template <typename Char, align::type align = align::left, typename OutputIt,
+template <typename Char, align default_align = align::left, typename OutputIt,
typename F>
FMT_CONSTEXPR auto write_padded(OutputIt out, const format_specs& specs,
size_t size, size_t width, F&& f) -> OutputIt {
- static_assert(align == align::left || align == align::right, "");
+ static_assert(default_align == align::left || default_align == align::right,
+ "");
unsigned spec_width = to_unsigned(specs.width);
size_t padding = spec_width > width ? spec_width - width : 0;
// Shifts are encoded as string literals because static constexpr is not
// supported in constexpr functions.
- auto* shifts = align == align::left ? "\x1f\x1f\x00\x01" : "\x00\x1f\x00\x01";
- size_t left_padding = padding >> shifts[specs.align];
+ auto* shifts =
+ default_align == align::left ? "\x1f\x1f\x00\x01" : "\x00\x1f\x00\x01";
+ size_t left_padding = padding >> shifts[static_cast<int>(specs.align())];
size_t right_padding = padding - left_padding;
- auto it = reserve(out, size + padding * specs.fill.size());
- if (left_padding != 0) it = fill<Char>(it, left_padding, specs.fill);
+ auto it = reserve(out, size + padding * specs.fill_size());
+ if (left_padding != 0) it = fill<Char>(it, left_padding, specs);
it = f(it);
- if (right_padding != 0) it = fill<Char>(it, right_padding, specs.fill);
+ if (right_padding != 0) it = fill<Char>(it, right_padding, specs);
return base_iterator(out, it);
}
-template <typename Char, align::type align = align::left, typename OutputIt,
+template <typename Char, align default_align = align::left, typename OutputIt,
typename F>
constexpr auto write_padded(OutputIt out, const format_specs& specs,
size_t size, F&& f) -> OutputIt {
- return write_padded<Char, align>(out, specs, size, size, f);
+ return write_padded<Char, default_align>(out, specs, size, size, f);
}
-template <typename Char, align::type align = align::left, typename OutputIt>
+template <typename Char, align default_align = align::left, typename OutputIt>
FMT_CONSTEXPR auto write_bytes(OutputIt out, string_view bytes,
const format_specs& specs = {}) -> OutputIt {
- return write_padded<Char, align>(
+ return write_padded<Char, default_align>(
out, specs, bytes.size(), [bytes](reserve_iterator<OutputIt> it) {
const char* data = bytes.data();
return copy<Char>(data, data + bytes.size(), it);
auto write = [=](reserve_iterator<OutputIt> it) {
*it++ = static_cast<Char>('0');
*it++ = static_cast<Char>('x');
- return format_uint<4, Char>(it, value, num_digits);
+ return format_base2e<Char>(4, it, value, num_digits);
};
return specs ? write_padded<Char, align::right>(out, *specs, size, write)
: base_iterator(out, write(reserve(out, size)));
FMT_API auto is_printable(uint32_t cp) -> bool;
inline auto needs_escape(uint32_t cp) -> bool {
- return cp < 0x20 || cp == 0x7f || cp == '"' || cp == '\\' ||
- !is_printable(cp);
+ if (cp < 0x20 || cp == 0x7f || cp == '"' || cp == '\\') return true;
+ if (const_check(FMT_OPTIMIZE_SIZE > 1)) return false;
+ return !is_printable(cp);
}
template <typename Char> struct find_escape_result {
inline auto find_escape(const char* begin, const char* end)
-> find_escape_result<char> {
- if (!use_utf8()) return find_escape<char>(begin, end);
+ if (const_check(!use_utf8)) return find_escape<char>(begin, end);
auto result = find_escape_result<char>{end, nullptr, 0};
for_each_codepoint(string_view(begin, to_unsigned(end - begin)),
[&](uint32_t cp, string_view sv) {
return result;
}
-#define FMT_STRING_IMPL(s, base, explicit) \
- [] { \
- /* Use the hidden visibility as a workaround for a GCC bug (#1973). */ \
- /* Use a macro-like name to avoid shadowing warnings. */ \
- struct FMT_VISIBILITY("hidden") FMT_COMPILE_STRING : base { \
- using char_type FMT_MAYBE_UNUSED = fmt::remove_cvref_t<decltype(s[0])>; \
- FMT_MAYBE_UNUSED FMT_CONSTEXPR explicit \
- operator fmt::basic_string_view<char_type>() const { \
- return fmt::detail_exported::compile_string_to_view<char_type>(s); \
- } \
- }; \
- return FMT_COMPILE_STRING(); \
- }()
-
-/**
- * Constructs a compile-time format string from a string literal `s`.
- *
- * **Example**:
- *
- * // A compile-time error because 'd' is an invalid specifier for strings.
- * std::string s = fmt::format(FMT_STRING("{:d}"), "foo");
- */
-#define FMT_STRING(s) FMT_STRING_IMPL(s, fmt::detail::compile_string, )
-
template <size_t width, typename Char, typename OutputIt>
auto write_codepoint(OutputIt out, char prefix, uint32_t cp) -> OutputIt {
*out++ = static_cast<Char>('\\');
*out++ = static_cast<Char>(prefix);
Char buf[width];
fill_n(buf, width, static_cast<Char>('0'));
- format_uint<4>(buf, cp, width);
+ format_base2e(4, buf, cp, width);
return copy<Char>(buf, buf + width, out);
}
*out++ = static_cast<Char>('\\');
c = static_cast<Char>('t');
break;
- case '"':
- FMT_FALLTHROUGH;
- case '\'':
- FMT_FALLTHROUGH;
- case '\\':
- *out++ = static_cast<Char>('\\');
- break;
+ case '"': FMT_FALLTHROUGH;
+ case '\'': FMT_FALLTHROUGH;
+ case '\\': *out++ = static_cast<Char>('\\'); break;
default:
if (escape.cp < 0x100) return write_codepoint<2, Char>(out, 'x', escape.cp);
if (escape.cp < 0x10000)
template <typename Char, typename OutputIt>
FMT_CONSTEXPR auto write_char(OutputIt out, Char value,
const format_specs& specs) -> OutputIt {
- bool is_debug = specs.type == presentation_type::debug;
+ bool is_debug = specs.type() == presentation_type::debug;
return write_padded<Char>(out, specs, 1, [=](reserve_iterator<OutputIt> it) {
if (is_debug) return write_escaped_char(it, value);
*it++ = value;
return it;
});
}
-template <typename Char, typename OutputIt>
-FMT_CONSTEXPR auto write(OutputIt out, Char value, const format_specs& specs,
- locale_ref loc = {}) -> OutputIt {
- // char is formatted as unsigned char for consistency across platforms.
- using unsigned_type =
- conditional_t<std::is_same<Char, char>::value, unsigned char, unsigned>;
- return check_char_specs(specs)
- ? write_char<Char>(out, value, specs)
- : write<Char>(out, static_cast<unsigned_type>(value), specs, loc);
-}
-
-// Data for write_int that doesn't depend on output iterator type. It is used to
-// avoid template code bloat.
-template <typename Char> struct write_int_data {
- size_t size;
- size_t padding;
-
- FMT_CONSTEXPR write_int_data(int num_digits, unsigned prefix,
- const format_specs& specs)
- : size((prefix >> 24) + to_unsigned(num_digits)), padding(0) {
- if (specs.align == align::numeric) {
- auto width = to_unsigned(specs.width);
- if (width > size) {
- padding = width - size;
- size = width;
- }
- } else if (specs.precision > num_digits) {
- size = (prefix >> 24) + to_unsigned(specs.precision);
- padding = to_unsigned(specs.precision - num_digits);
- }
- }
-};
-
-// Writes an integer in the format
-// <left-padding><prefix><numeric-padding><digits><right-padding>
-// where <digits> are written by write_digits(it).
-// prefix contains chars in three lower bytes and the size in the fourth byte.
-template <typename Char, typename OutputIt, typename W>
-FMT_CONSTEXPR FMT_INLINE auto write_int(OutputIt out, int num_digits,
- unsigned prefix,
- const format_specs& specs,
- W write_digits) -> OutputIt {
- // Slightly faster check for specs.width == 0 && specs.precision == -1.
- if ((specs.width | (specs.precision + 1)) == 0) {
- auto it = reserve(out, to_unsigned(num_digits) + (prefix >> 24));
- if (prefix != 0) {
- for (unsigned p = prefix & 0xffffff; p != 0; p >>= 8)
- *it++ = static_cast<Char>(p & 0xff);
- }
- return base_iterator(out, write_digits(it));
- }
- auto data = write_int_data<Char>(num_digits, prefix, specs);
- return write_padded<Char, align::right>(
- out, specs, data.size, [=](reserve_iterator<OutputIt> it) {
- for (unsigned p = prefix & 0xffffff; p != 0; p >>= 8)
- *it++ = static_cast<Char>(p & 0xff);
- it = detail::fill_n(it, data.padding, static_cast<Char>('0'));
- return write_digits(it);
- });
-}
template <typename Char> class digit_grouping {
private:
return count;
}
- // Applies grouping to digits and write the output to out.
+ // Applies grouping to digits and writes the output to out.
template <typename Out, typename C>
auto apply(Out out, basic_string_view<C> digits) const -> Out {
auto num_digits = static_cast<int>(digits.size());
static_assert(std::is_same<uint64_or_128_t<UInt>, UInt>::value, "");
int num_digits = 0;
auto buffer = memory_buffer();
- switch (specs.type) {
- default:
- FMT_ASSERT(false, "");
- FMT_FALLTHROUGH;
+ switch (specs.type()) {
+ default: FMT_ASSERT(false, ""); FMT_FALLTHROUGH;
case presentation_type::none:
case presentation_type::dec:
num_digits = count_digits(value);
format_decimal<char>(appender(buffer), value, num_digits);
break;
case presentation_type::hex:
- if (specs.alt)
- prefix_append(prefix, unsigned(specs.upper ? 'X' : 'x') << 8 | '0');
+ if (specs.alt())
+ prefix_append(prefix, unsigned(specs.upper() ? 'X' : 'x') << 8 | '0');
num_digits = count_digits<4>(value);
- format_uint<4, char>(appender(buffer), value, num_digits, specs.upper);
+ format_base2e<char>(4, appender(buffer), value, num_digits, specs.upper());
break;
case presentation_type::oct:
num_digits = count_digits<3>(value);
// Octal prefix '0' is counted as a digit, so only add it if precision
// is not greater than the number of digits.
- if (specs.alt && specs.precision <= num_digits && value != 0)
+ if (specs.alt() && specs.precision <= num_digits && value != 0)
prefix_append(prefix, '0');
- format_uint<3, char>(appender(buffer), value, num_digits);
+ format_base2e<char>(3, appender(buffer), value, num_digits);
break;
case presentation_type::bin:
- if (specs.alt)
- prefix_append(prefix, unsigned(specs.upper ? 'B' : 'b') << 8 | '0');
+ if (specs.alt())
+ prefix_append(prefix, unsigned(specs.upper() ? 'B' : 'b') << 8 | '0');
num_digits = count_digits<1>(value);
- format_uint<1, char>(appender(buffer), value, num_digits);
+ format_base2e<char>(1, appender(buffer), value, num_digits);
break;
case presentation_type::chr:
return write_char<Char>(out, static_cast<Char>(value), specs);
});
}
+#if FMT_USE_LOCALE
// Writes a localized value.
FMT_API auto write_loc(appender out, loc_value value, const format_specs& specs,
locale_ref loc) -> bool;
+auto write_loc(basic_appender<wchar_t> out, loc_value value,
+ const format_specs& specs, locale_ref loc) -> bool;
+#endif
template <typename OutputIt>
-inline auto write_loc(OutputIt, loc_value, const format_specs&, locale_ref)
- -> bool {
+inline auto write_loc(OutputIt, const loc_value&, const format_specs&,
+ locale_ref) -> bool {
return false;
}
};
template <typename T>
-FMT_CONSTEXPR auto make_write_int_arg(T value, sign_t sign)
+FMT_CONSTEXPR auto make_write_int_arg(T value, sign s)
-> write_int_arg<uint32_or_64_or_128_t<T>> {
auto prefix = 0u;
auto abs_value = static_cast<uint32_or_64_or_128_t<T>>(value);
prefix = 0x01000000 | '-';
abs_value = 0 - abs_value;
} else {
- constexpr const unsigned prefixes[4] = {0, 0, 0x1000000u | '+',
- 0x1000000u | ' '};
- prefix = prefixes[sign];
+ constexpr unsigned prefixes[4] = {0, 0, 0x1000000u | '+', 0x1000000u | ' '};
+ prefix = prefixes[static_cast<int>(s)];
}
return {abs_value, prefix};
}
template <typename T, FMT_ENABLE_IF(is_integer<T>::value)>
auto operator()(T value) -> bool {
- auto arg = make_write_int_arg(value, specs.sign);
+ auto arg = make_write_int_arg(value, specs.sign());
write_int(out, static_cast<uint64_or_128_t<T>>(arg.abs_value), arg.prefix,
specs, digit_grouping<Char>(grouping, sep));
return true;
}
};
+// Size and padding computation separate from write_int to avoid template bloat.
+struct size_padding {
+ unsigned size;
+ unsigned padding;
+
+ FMT_CONSTEXPR size_padding(int num_digits, unsigned prefix,
+ const format_specs& specs)
+ : size((prefix >> 24) + to_unsigned(num_digits)), padding(0) {
+ if (specs.align() == align::numeric) {
+ auto width = to_unsigned(specs.width);
+ if (width > size) {
+ padding = width - size;
+ size = width;
+ }
+ } else if (specs.precision > num_digits) {
+ size = (prefix >> 24) + to_unsigned(specs.precision);
+ padding = to_unsigned(specs.precision - num_digits);
+ }
+ }
+};
+
template <typename Char, typename OutputIt, typename T>
FMT_CONSTEXPR FMT_INLINE auto write_int(OutputIt out, write_int_arg<T> arg,
- const format_specs& specs, locale_ref)
- -> OutputIt {
+ const format_specs& specs) -> OutputIt {
static_assert(std::is_same<T, uint32_or_64_or_128_t<T>>::value, "");
+
+ constexpr size_t buffer_size = num_bits<T>();
+ char buffer[buffer_size];
+ if (is_constant_evaluated()) fill_n(buffer, buffer_size, '\0');
+ const char* begin = nullptr;
+ const char* end = buffer + buffer_size;
+
auto abs_value = arg.abs_value;
auto prefix = arg.prefix;
- switch (specs.type) {
- default:
- FMT_ASSERT(false, "");
- FMT_FALLTHROUGH;
+ switch (specs.type()) {
+ default: FMT_ASSERT(false, ""); FMT_FALLTHROUGH;
case presentation_type::none:
- case presentation_type::dec: {
- int num_digits = count_digits(abs_value);
- return write_int<Char>(
- out, num_digits, prefix, specs, [=](reserve_iterator<OutputIt> it) {
- return format_decimal<Char>(it, abs_value, num_digits).end;
- });
- }
- case presentation_type::hex: {
- if (specs.alt)
- prefix_append(prefix, unsigned(specs.upper ? 'X' : 'x') << 8 | '0');
- int num_digits = count_digits<4>(abs_value);
- return write_int<Char>(
- out, num_digits, prefix, specs, [=](reserve_iterator<OutputIt> it) {
- return format_uint<4, Char>(it, abs_value, num_digits, specs.upper);
- });
- }
+ case presentation_type::dec:
+ begin = do_format_decimal(buffer, abs_value, buffer_size);
+ break;
+ case presentation_type::hex:
+ begin = do_format_base2e(4, buffer, abs_value, buffer_size, specs.upper());
+ if (specs.alt())
+ prefix_append(prefix, unsigned(specs.upper() ? 'X' : 'x') << 8 | '0');
+ break;
case presentation_type::oct: {
- int num_digits = count_digits<3>(abs_value);
+ begin = do_format_base2e(3, buffer, abs_value, buffer_size);
// Octal prefix '0' is counted as a digit, so only add it if precision
// is not greater than the number of digits.
- if (specs.alt && specs.precision <= num_digits && abs_value != 0)
+ auto num_digits = end - begin;
+ if (specs.alt() && specs.precision <= num_digits && abs_value != 0)
prefix_append(prefix, '0');
- return write_int<Char>(
- out, num_digits, prefix, specs, [=](reserve_iterator<OutputIt> it) {
- return format_uint<3, Char>(it, abs_value, num_digits);
- });
- }
- case presentation_type::bin: {
- if (specs.alt)
- prefix_append(prefix, unsigned(specs.upper ? 'B' : 'b') << 8 | '0');
- int num_digits = count_digits<1>(abs_value);
- return write_int<Char>(
- out, num_digits, prefix, specs, [=](reserve_iterator<OutputIt> it) {
- return format_uint<1, Char>(it, abs_value, num_digits);
- });
+ break;
}
+ case presentation_type::bin:
+ begin = do_format_base2e(1, buffer, abs_value, buffer_size);
+ if (specs.alt())
+ prefix_append(prefix, unsigned(specs.upper() ? 'B' : 'b') << 8 | '0');
+ break;
case presentation_type::chr:
return write_char<Char>(out, static_cast<Char>(abs_value), specs);
}
+
+ // Write an integer in the format
+ // <left-padding><prefix><numeric-padding><digits><right-padding>
+ // prefix contains chars in three lower bytes and the size in the fourth byte.
+ int num_digits = static_cast<int>(end - begin);
+ // Slightly faster check for specs.width == 0 && specs.precision == -1.
+ if ((specs.width | (specs.precision + 1)) == 0) {
+ auto it = reserve(out, to_unsigned(num_digits) + (prefix >> 24));
+ for (unsigned p = prefix & 0xffffff; p != 0; p >>= 8)
+ *it++ = static_cast<Char>(p & 0xff);
+ return base_iterator(out, copy<Char>(begin, end, it));
+ }
+ auto sp = size_padding(num_digits, prefix, specs);
+ unsigned padding = sp.padding;
+ return write_padded<Char, align::right>(
+ out, specs, sp.size, [=](reserve_iterator<OutputIt> it) {
+ for (unsigned p = prefix & 0xffffff; p != 0; p >>= 8)
+ *it++ = static_cast<Char>(p & 0xff);
+ it = detail::fill_n(it, padding, static_cast<Char>('0'));
+ return copy<Char>(begin, end, it);
+ });
}
+
template <typename Char, typename OutputIt, typename T>
FMT_CONSTEXPR FMT_NOINLINE auto write_int_noinline(OutputIt out,
write_int_arg<T> arg,
- const format_specs& specs,
- locale_ref loc) -> OutputIt {
- return write_int<Char>(out, arg, specs, loc);
+ const format_specs& specs)
+ -> OutputIt {
+ return write_int<Char>(out, arg, specs);
}
+
template <typename Char, typename T,
FMT_ENABLE_IF(is_integral<T>::value &&
!std::is_same<T, bool>::value &&
FMT_CONSTEXPR FMT_INLINE auto write(basic_appender<Char> out, T value,
const format_specs& specs, locale_ref loc)
-> basic_appender<Char> {
- if (specs.localized && write_loc(out, value, specs, loc)) return out;
- return write_int_noinline<Char>(out, make_write_int_arg(value, specs.sign),
- specs, loc);
+ if (specs.localized() && write_loc(out, value, specs, loc)) return out;
+ return write_int_noinline<Char>(out, make_write_int_arg(value, specs.sign()),
+ specs);
}
+
// An inlined version of write used in format string compilation.
template <typename Char, typename OutputIt, typename T,
FMT_ENABLE_IF(is_integral<T>::value &&
FMT_CONSTEXPR FMT_INLINE auto write(OutputIt out, T value,
const format_specs& specs, locale_ref loc)
-> OutputIt {
- if (specs.localized && write_loc(out, value, specs, loc)) return out;
- return write_int<Char>(out, make_write_int_arg(value, specs.sign), specs,
- loc);
+ if (specs.localized() && write_loc(out, value, specs, loc)) return out;
+ return write_int<Char>(out, make_write_int_arg(value, specs.sign()), specs);
}
-// An output iterator that counts the number of objects written to it and
-// discards them.
-class counting_iterator {
- private:
- size_t count_;
+template <typename Char, typename OutputIt>
+FMT_CONSTEXPR auto write(OutputIt out, Char value, const format_specs& specs,
+ locale_ref loc = {}) -> OutputIt {
+ // char is formatted as unsigned char for consistency across platforms.
+ using unsigned_type =
+ conditional_t<std::is_same<Char, char>::value, unsigned char, unsigned>;
+ return check_char_specs(specs)
+ ? write_char<Char>(out, value, specs)
+ : write<Char>(out, static_cast<unsigned_type>(value), specs, loc);
+}
- public:
- using iterator_category = std::output_iterator_tag;
- using difference_type = std::ptrdiff_t;
- using pointer = void;
- using reference = void;
- FMT_UNCHECKED_ITERATOR(counting_iterator);
+template <typename Char, typename OutputIt,
+ FMT_ENABLE_IF(std::is_same<Char, char>::value)>
+FMT_CONSTEXPR auto write(OutputIt out, basic_string_view<Char> s,
+ const format_specs& specs) -> OutputIt {
+ bool is_debug = specs.type() == presentation_type::debug;
+ if (specs.precision < 0 && specs.width == 0) {
+ auto&& it = reserve(out, s.size());
+ return is_debug ? write_escaped_string(it, s) : copy<char>(s, it);
+ }
+
+ size_t display_width_limit =
+ specs.precision < 0 ? SIZE_MAX : to_unsigned(specs.precision);
+ size_t display_width =
+ !is_debug || specs.precision == 0 ? 0 : 1; // Account for opening '"'.
+ size_t size = !is_debug || specs.precision == 0 ? 0 : 1;
+ for_each_codepoint(s, [&](uint32_t cp, string_view sv) {
+ if (is_debug && needs_escape(cp)) {
+ counting_buffer<char> buf;
+ write_escaped_cp(basic_appender<char>(buf),
+ find_escape_result<char>{sv.begin(), sv.end(), cp});
+ // We're reinterpreting bytes as display width. That's okay
+ // because write_escaped_cp() only writes ASCII characters.
+ size_t cp_width = buf.count();
+ if (display_width + cp_width <= display_width_limit) {
+ display_width += cp_width;
+ size += cp_width;
+ // If this is the end of the string, account for closing '"'.
+ if (display_width < display_width_limit && sv.end() == s.end()) {
+ ++display_width;
+ ++size;
+ }
+ return true;
+ }
- struct value_type {
- template <typename T> FMT_CONSTEXPR void operator=(const T&) {}
- };
+ size += display_width_limit - display_width;
+ display_width = display_width_limit;
+ return false;
+ }
- FMT_CONSTEXPR counting_iterator() : count_(0) {}
+ size_t cp_width = display_width_of(cp);
+ if (cp_width + display_width <= display_width_limit) {
+ display_width += cp_width;
+ size += sv.size();
+ // If this is the end of the string, account for closing '"'.
+ if (is_debug && display_width < display_width_limit &&
+ sv.end() == s.end()) {
+ ++display_width;
+ ++size;
+ }
+ return true;
+ }
- FMT_CONSTEXPR auto count() const -> size_t { return count_; }
+ return false;
+ });
- FMT_CONSTEXPR auto operator++() -> counting_iterator& {
- ++count_;
- return *this;
- }
- FMT_CONSTEXPR auto operator++(int) -> counting_iterator {
- auto it = *this;
- ++*this;
- return it;
- }
+ struct bounded_output_iterator {
+ reserve_iterator<OutputIt> underlying_iterator;
+ size_t bound;
- FMT_CONSTEXPR friend auto operator+(counting_iterator it, difference_type n)
- -> counting_iterator {
- it.count_ += static_cast<size_t>(n);
- return it;
- }
+ FMT_CONSTEXPR auto operator*() -> bounded_output_iterator& { return *this; }
+ FMT_CONSTEXPR auto operator++() -> bounded_output_iterator& {
+ return *this;
+ }
+ FMT_CONSTEXPR auto operator++(int) -> bounded_output_iterator& {
+ return *this;
+ }
+ FMT_CONSTEXPR auto operator=(char c) -> bounded_output_iterator& {
+ if (bound > 0) {
+ *underlying_iterator++ = c;
+ --bound;
+ }
+ return *this;
+ }
+ };
- FMT_CONSTEXPR auto operator*() const -> value_type { return {}; }
-};
+ return write_padded<char>(
+ out, specs, size, display_width, [=](reserve_iterator<OutputIt> it) {
+ return is_debug
+ ? write_escaped_string(bounded_output_iterator{it, size}, s)
+ .underlying_iterator
+ : copy<char>(s.data(), s.data() + size, it);
+ });
+}
-template <typename Char, typename OutputIt>
+template <typename Char, typename OutputIt,
+ FMT_ENABLE_IF(!std::is_same<Char, char>::value)>
FMT_CONSTEXPR auto write(OutputIt out, basic_string_view<Char> s,
const format_specs& specs) -> OutputIt {
auto data = s.data();
auto size = s.size();
if (specs.precision >= 0 && to_unsigned(specs.precision) < size)
- size = code_point_index(s, to_unsigned(specs.precision));
- bool is_debug = specs.type == presentation_type::debug;
- size_t width = 0;
-
- if (is_debug) size = write_escaped_string(counting_iterator{}, s).count();
+ size = to_unsigned(specs.precision);
- if (specs.width != 0) {
- if (is_debug)
- width = size;
- else
- width = compute_width(basic_string_view<Char>(data, size));
+ bool is_debug = specs.type() == presentation_type::debug;
+ if (is_debug) {
+ auto buf = counting_buffer<Char>();
+ write_escaped_string(basic_appender<Char>(buf), s);
+ size = buf.count();
}
- return write_padded<Char>(out, specs, size, width,
- [=](reserve_iterator<OutputIt> it) {
- if (is_debug) return write_escaped_string(it, s);
- return copy<Char>(data, data + size, it);
- });
+
+ return write_padded<Char>(
+ out, specs, size, [=](reserve_iterator<OutputIt> it) {
+ return is_debug ? write_escaped_string(it, s)
+ : copy<Char>(data, data + size, it);
+ });
}
+
template <typename Char, typename OutputIt>
-FMT_CONSTEXPR auto write(OutputIt out,
- basic_string_view<type_identity_t<Char>> s,
+FMT_CONSTEXPR auto write(OutputIt out, basic_string_view<Char> s,
const format_specs& specs, locale_ref) -> OutputIt {
return write<Char>(out, s, specs);
}
+
template <typename Char, typename OutputIt>
FMT_CONSTEXPR auto write(OutputIt out, const Char* s, const format_specs& specs,
locale_ref) -> OutputIt {
- if (specs.type == presentation_type::pointer)
+ if (specs.type() == presentation_type::pointer)
return write_ptr<Char>(out, bit_cast<uintptr_t>(s), &specs);
if (!s) report_error("string pointer is null");
return write<Char>(out, basic_string_view<Char>(s), specs, {});
if (negative) abs_value = ~abs_value + 1;
int num_digits = count_digits(abs_value);
auto size = (negative ? 1 : 0) + static_cast<size_t>(num_digits);
- auto it = reserve(out, size);
- if (auto ptr = to_pointer<Char>(it, size)) {
+ if (auto ptr = to_pointer<Char>(out, size)) {
if (negative) *ptr++ = static_cast<Char>('-');
format_decimal<Char>(ptr, abs_value, num_digits);
return out;
}
- if (negative) *it++ = static_cast<Char>('-');
- it = format_decimal<Char>(it, abs_value, num_digits).end;
- return base_iterator(out, it);
+ if (negative) *out++ = static_cast<Char>('-');
+ return format_decimal<Char>(out, abs_value, num_digits);
}
-// DEPRECATED!
template <typename Char>
FMT_CONSTEXPR auto parse_align(const Char* begin, const Char* end,
format_specs& specs) -> const Char* {
FMT_ASSERT(begin != end, "");
- auto align = align::none;
+ auto alignment = align::none;
auto p = begin + code_point_length(begin);
if (end - p <= 0) p = begin;
for (;;) {
switch (to_ascii(*p)) {
- case '<':
- align = align::left;
- break;
- case '>':
- align = align::right;
- break;
- case '^':
- align = align::center;
- break;
+ case '<': alignment = align::left; break;
+ case '>': alignment = align::right; break;
+ case '^': alignment = align::center; break;
}
- if (align != align::none) {
+ if (alignment != align::none) {
if (p != begin) {
auto c = *begin;
if (c == '}') return begin;
report_error("invalid fill character '{'");
return begin;
}
- specs.fill = basic_string_view<Char>(begin, to_unsigned(p - begin));
+ specs.set_fill(basic_string_view<Char>(begin, to_unsigned(p - begin)));
begin = p + 1;
} else {
++begin;
}
p = begin;
}
- specs.align = align;
+ specs.set_align(alignment);
return begin;
}
-// A floating-point presentation format.
-enum class float_format : unsigned char {
- general, // General: exponent notation or fixed point based on magnitude.
- exp, // Exponent notation with the default precision of 6, e.g. 1.2e-3.
- fixed // Fixed point with the default precision of 6, e.g. 0.0012.
-};
-
-struct float_specs {
- int precision;
- float_format format : 8;
- sign_t sign : 8;
- bool locale : 1;
- bool binary32 : 1;
- bool showpoint : 1;
-};
-
-// DEPRECATED!
-FMT_CONSTEXPR inline auto parse_float_type_spec(const format_specs& specs)
- -> float_specs {
- auto result = float_specs();
- result.showpoint = specs.alt;
- result.locale = specs.localized;
- switch (specs.type) {
- default:
- FMT_FALLTHROUGH;
- case presentation_type::none:
- result.format = float_format::general;
- break;
- case presentation_type::exp:
- result.format = float_format::exp;
- result.showpoint |= specs.precision != 0;
- break;
- case presentation_type::fixed:
- result.format = float_format::fixed;
- result.showpoint |= specs.precision != 0;
- break;
- case presentation_type::general:
- result.format = float_format::general;
- break;
- }
- return result;
-}
-
template <typename Char, typename OutputIt>
FMT_CONSTEXPR20 auto write_nonfinite(OutputIt out, bool isnan,
- format_specs specs, sign_t sign)
- -> OutputIt {
+ format_specs specs, sign s) -> OutputIt {
auto str =
- isnan ? (specs.upper ? "NAN" : "nan") : (specs.upper ? "INF" : "inf");
+ isnan ? (specs.upper() ? "NAN" : "nan") : (specs.upper() ? "INF" : "inf");
constexpr size_t str_size = 3;
- auto size = str_size + (sign ? 1 : 0);
+ auto size = str_size + (s != sign::none ? 1 : 0);
// Replace '0'-padding with space for non-finite values.
const bool is_zero_fill =
- specs.fill.size() == 1 && specs.fill.template get<Char>() == '0';
- if (is_zero_fill) specs.fill = ' ';
+ specs.fill_size() == 1 && specs.fill_unit<Char>() == '0';
+ if (is_zero_fill) specs.set_fill(' ');
return write_padded<Char>(out, specs, size,
[=](reserve_iterator<OutputIt> it) {
- if (sign) *it++ = detail::sign<Char>(sign);
+ if (s != sign::none)
+ *it++ = detail::getsign<Char>(s);
return copy<Char>(str, str + str_size, it);
});
}
template <typename Char, typename OutputIt, typename UInt>
inline auto write_significand(OutputIt out, UInt significand,
int significand_size) -> OutputIt {
- return format_decimal<Char>(out, significand, significand_size).end;
+ return format_decimal<Char>(out, significand, significand_size);
}
template <typename Char, typename OutputIt, typename T, typename Grouping>
FMT_CONSTEXPR20 auto write_significand(OutputIt out, T significand,
FMT_ENABLE_IF(std::is_integral<UInt>::value)>
inline auto write_significand(Char* out, UInt significand, int significand_size,
int integral_size, Char decimal_point) -> Char* {
- if (!decimal_point)
- return format_decimal(out, significand, significand_size).end;
+ if (!decimal_point) return format_decimal(out, significand, significand_size);
out += significand_size + 1;
Char* end = out;
int floating_size = significand_size - integral_size;
for (int i = floating_size / 2; i > 0; --i) {
out -= 2;
- copy2(out, digits2(static_cast<std::size_t>(significand % 100)));
+ write2digits(out, static_cast<size_t>(significand % 100));
significand /= 100;
}
if (floating_size % 2 != 0) {
buffer.end(), out);
}
-template <typename Char, typename OutputIt, typename DecimalFP,
- typename Grouping = digit_grouping<Char>>
-FMT_CONSTEXPR20 auto do_write_float(OutputIt out, const DecimalFP& f,
- const format_specs& specs,
- float_specs fspecs, locale_ref loc)
- -> OutputIt {
- auto significand = f.significand;
- int significand_size = get_significand_size(f);
- const Char zero = static_cast<Char>('0');
- auto sign = fspecs.sign;
- size_t size = to_unsigned(significand_size) + (sign ? 1 : 0);
- using iterator = reserve_iterator<OutputIt>;
-
- Char decimal_point =
- fspecs.locale ? detail::decimal_point<Char>(loc) : static_cast<Char>('.');
-
- int output_exp = f.exponent + significand_size - 1;
- auto use_exp_format = [=]() {
- if (fspecs.format == float_format::exp) return true;
- if (fspecs.format != float_format::general) return false;
- // Use the fixed notation if the exponent is in [exp_lower, exp_upper),
- // e.g. 0.0001 instead of 1e-04. Otherwise use the exponent notation.
- const int exp_lower = -4, exp_upper = 16;
- return output_exp < exp_lower ||
- output_exp >= (fspecs.precision > 0 ? fspecs.precision : exp_upper);
- };
- if (use_exp_format()) {
- int num_zeros = 0;
- if (fspecs.showpoint) {
- num_zeros = fspecs.precision - significand_size;
- if (num_zeros < 0) num_zeros = 0;
- size += to_unsigned(num_zeros);
- } else if (significand_size == 1) {
- decimal_point = Char();
- }
- auto abs_output_exp = output_exp >= 0 ? output_exp : -output_exp;
- int exp_digits = 2;
- if (abs_output_exp >= 100) exp_digits = abs_output_exp >= 1000 ? 4 : 3;
-
- size += to_unsigned((decimal_point ? 1 : 0) + 2 + exp_digits);
- char exp_char = specs.upper ? 'E' : 'e';
- auto write = [=](iterator it) {
- if (sign) *it++ = detail::sign<Char>(sign);
- // Insert a decimal point after the first digit and add an exponent.
- it = write_significand(it, significand, significand_size, 1,
- decimal_point);
- if (num_zeros > 0) it = detail::fill_n(it, num_zeros, zero);
- *it++ = static_cast<Char>(exp_char);
- return write_exponent<Char>(output_exp, it);
- };
- return specs.width > 0
- ? write_padded<Char, align::right>(out, specs, size, write)
- : base_iterator(out, write(reserve(out, size)));
- }
+// Numbers with exponents greater or equal to the returned value will use
+// the exponential notation.
+template <typename T> FMT_CONSTEVAL auto exp_upper() -> int {
+ return std::numeric_limits<T>::digits10 != 0
+ ? min_of(16, std::numeric_limits<T>::digits10 + 1)
+ : 16;
+}
- int exp = f.exponent + significand_size;
- if (f.exponent >= 0) {
- // 1234e5 -> 123400000[.0+]
- size += to_unsigned(f.exponent);
- int num_zeros = fspecs.precision - exp;
- abort_fuzzing_if(num_zeros > 5000);
- if (fspecs.showpoint) {
- ++size;
- if (num_zeros <= 0 && fspecs.format != float_format::fixed) num_zeros = 0;
- if (num_zeros > 0) size += to_unsigned(num_zeros);
- }
- auto grouping = Grouping(loc, fspecs.locale);
- size += to_unsigned(grouping.count_separators(exp));
- return write_padded<Char, align::right>(out, specs, size, [&](iterator it) {
- if (sign) *it++ = detail::sign<Char>(sign);
- it = write_significand<Char>(it, significand, significand_size,
- f.exponent, grouping);
- if (!fspecs.showpoint) return it;
- *it++ = decimal_point;
- return num_zeros > 0 ? detail::fill_n(it, num_zeros, zero) : it;
- });
- } else if (exp > 0) {
- // 1234e-2 -> 12.34[0+]
- int num_zeros = fspecs.showpoint ? fspecs.precision - significand_size : 0;
- size += 1 + to_unsigned(num_zeros > 0 ? num_zeros : 0);
- auto grouping = Grouping(loc, fspecs.locale);
- size += to_unsigned(grouping.count_separators(exp));
- return write_padded<Char, align::right>(out, specs, size, [&](iterator it) {
- if (sign) *it++ = detail::sign<Char>(sign);
- it = write_significand(it, significand, significand_size, exp,
- decimal_point, grouping);
- return num_zeros > 0 ? detail::fill_n(it, num_zeros, zero) : it;
- });
- }
- // 1234e-6 -> 0.001234
- int num_zeros = -exp;
- if (significand_size == 0 && fspecs.precision >= 0 &&
- fspecs.precision < num_zeros) {
- num_zeros = fspecs.precision;
- }
- bool pointy = num_zeros != 0 || significand_size != 0 || fspecs.showpoint;
- size += 1 + (pointy ? 1 : 0) + to_unsigned(num_zeros);
- return write_padded<Char, align::right>(out, specs, size, [&](iterator it) {
- if (sign) *it++ = detail::sign<Char>(sign);
- *it++ = zero;
- if (!pointy) return it;
- *it++ = decimal_point;
- it = detail::fill_n(it, num_zeros, zero);
- return write_significand<Char>(it, significand, significand_size);
- });
+// Use the fixed notation if the exponent is in [-4, exp_upper),
+// e.g. 0.0001 instead of 1e-04. Otherwise use the exponent notation.
+constexpr auto use_fixed(int exp, int exp_upper) -> bool {
+ return exp >= -4 && exp < exp_upper;
}
template <typename Char> class fallback_digit_grouping {
}
};
+template <typename Char, typename Grouping, typename OutputIt,
+ typename DecimalFP>
+FMT_CONSTEXPR20 auto write_fixed(OutputIt out, const DecimalFP& f,
+ int significand_size, Char decimal_point,
+ const format_specs& specs, sign s,
+ locale_ref loc = {}) -> OutputIt {
+ using iterator = reserve_iterator<OutputIt>;
+
+ int exp = f.exponent + significand_size;
+ long long size = significand_size + (s != sign::none ? 1 : 0);
+ if (f.exponent >= 0) {
+ // 1234e5 -> 123400000[.0+]
+ size += f.exponent;
+ int num_zeros = specs.precision - exp;
+ abort_fuzzing_if(num_zeros > 5000);
+ if (specs.alt()) {
+ ++size;
+ if (num_zeros <= 0 && specs.type() != presentation_type::fixed)
+ num_zeros = 0;
+ if (num_zeros > 0) size += num_zeros;
+ }
+ auto grouping = Grouping(loc, specs.localized());
+ size += grouping.count_separators(exp);
+ return write_padded<Char, align::right>(
+ out, specs, static_cast<size_t>(size), [&](iterator it) {
+ if (s != sign::none) *it++ = detail::getsign<Char>(s);
+ it = write_significand<Char>(it, f.significand, significand_size,
+ f.exponent, grouping);
+ if (!specs.alt()) return it;
+ *it++ = decimal_point;
+ return num_zeros > 0 ? detail::fill_n(it, num_zeros, Char('0')) : it;
+ });
+ }
+ if (exp > 0) {
+ // 1234e-2 -> 12.34[0+]
+ int num_zeros = specs.alt() ? specs.precision - significand_size : 0;
+ size += 1 + max_of(num_zeros, 0);
+ auto grouping = Grouping(loc, specs.localized());
+ size += grouping.count_separators(exp);
+ return write_padded<Char, align::right>(
+ out, specs, to_unsigned(size), [&](iterator it) {
+ if (s != sign::none) *it++ = detail::getsign<Char>(s);
+ it = write_significand(it, f.significand, significand_size, exp,
+ decimal_point, grouping);
+ return num_zeros > 0 ? detail::fill_n(it, num_zeros, Char('0')) : it;
+ });
+ }
+ // 1234e-6 -> 0.001234
+ int num_zeros = -exp;
+ if (significand_size == 0 && specs.precision >= 0 &&
+ specs.precision < num_zeros) {
+ num_zeros = specs.precision;
+ }
+ bool pointy = num_zeros != 0 || significand_size != 0 || specs.alt();
+ size += 1 + (pointy ? 1 : 0) + num_zeros;
+ return write_padded<Char, align::right>(
+ out, specs, to_unsigned(size), [&](iterator it) {
+ if (s != sign::none) *it++ = detail::getsign<Char>(s);
+ *it++ = Char('0');
+ if (!pointy) return it;
+ *it++ = decimal_point;
+ it = detail::fill_n(it, num_zeros, Char('0'));
+ return write_significand<Char>(it, f.significand, significand_size);
+ });
+}
+
+template <typename Char, typename Grouping, typename OutputIt,
+ typename DecimalFP>
+FMT_CONSTEXPR20 auto do_write_float(OutputIt out, const DecimalFP& f,
+ const format_specs& specs, sign s,
+ int exp_upper, locale_ref loc) -> OutputIt {
+ Char point = specs.localized() ? detail::decimal_point<Char>(loc) : Char('.');
+ int significand_size = get_significand_size(f);
+ int exp = f.exponent + significand_size - 1;
+ if (specs.type() == presentation_type::fixed ||
+ (specs.type() != presentation_type::exp &&
+ use_fixed(exp, specs.precision > 0 ? specs.precision : exp_upper))) {
+ return write_fixed<Char, Grouping>(out, f, significand_size, point, specs,
+ s, loc);
+ }
+
+ // Write value in the exponential format.
+ int num_zeros = 0;
+ long long size = significand_size + (s != sign::none ? 1 : 0);
+ if (specs.alt()) {
+ num_zeros = max_of(specs.precision - significand_size, 0);
+ size += num_zeros;
+ } else if (significand_size == 1) {
+ point = Char();
+ }
+ size += (point ? 1 : 0) + compute_exp_size(exp);
+ char exp_char = specs.upper() ? 'E' : 'e';
+ auto write = [=](reserve_iterator<OutputIt> it) {
+ if (s != sign::none) *it++ = detail::getsign<Char>(s);
+ // Insert a decimal point after the first digit and add an exponent.
+ it = write_significand(it, f.significand, significand_size, 1, point);
+ if (num_zeros > 0) it = detail::fill_n(it, num_zeros, Char('0'));
+ *it++ = Char(exp_char);
+ return write_exponent<Char>(exp, it);
+ };
+ auto usize = to_unsigned(size);
+ return specs.width > 0
+ ? write_padded<Char, align::right>(out, specs, usize, write)
+ : base_iterator(out, write(reserve(out, usize)));
+}
+
template <typename Char, typename OutputIt, typename DecimalFP>
FMT_CONSTEXPR20 auto write_float(OutputIt out, const DecimalFP& f,
- const format_specs& specs, float_specs fspecs,
- locale_ref loc) -> OutputIt {
+ const format_specs& specs, sign s,
+ int exp_upper, locale_ref loc) -> OutputIt {
if (is_constant_evaluated()) {
- return do_write_float<Char, OutputIt, DecimalFP,
- fallback_digit_grouping<Char>>(out, f, specs, fspecs,
- loc);
+ return do_write_float<Char, fallback_digit_grouping<Char>>(out, f, specs, s,
+ exp_upper, loc);
} else {
- return do_write_float<Char>(out, f, specs, fspecs, loc);
+ return do_write_float<Char, digit_grouping<Char>>(out, f, specs, s,
+ exp_upper, loc);
}
}
struct has_isfinite<T, enable_if_t<sizeof(std::isfinite(T())) != 0>>
: std::true_type {};
-template <typename T, FMT_ENABLE_IF(std::is_floating_point<T>::value&&
- has_isfinite<T>::value)>
+template <typename T,
+ FMT_ENABLE_IF(is_floating_point<T>::value&& has_isfinite<T>::value)>
FMT_CONSTEXPR20 auto isfinite(T value) -> bool {
constexpr T inf = T(std::numeric_limits<double>::infinity());
if (is_constant_evaluated())
}
template <typename T, FMT_ENABLE_IF(is_floating_point<T>::value)>
-FMT_INLINE FMT_CONSTEXPR bool signbit(T value) {
+FMT_INLINE FMT_CONSTEXPR auto signbit(T value) -> bool {
if (is_constant_evaluated()) {
#ifdef __cpp_if_constexpr
if constexpr (std::numeric_limits<double>::is_iec559) {
class bigint {
private:
- // A bigint is stored as an array of bigits (big digits), with bigit at index
- // 0 being the least significant one.
- using bigit = uint32_t;
+ // A bigint is a number in the form bigit_[N - 1] ... bigit_[0] * 32^exp_.
+ using bigit = uint32_t; // A big digit.
using double_bigit = uint64_t;
+ enum { bigit_bits = num_bits<bigit>() };
enum { bigits_capacity = 32 };
basic_memory_buffer<bigit, bigits_capacity> bigits_;
int exp_;
- FMT_CONSTEXPR20 auto operator[](int index) const -> bigit {
- return bigits_[to_unsigned(index)];
- }
- FMT_CONSTEXPR20 auto operator[](int index) -> bigit& {
- return bigits_[to_unsigned(index)];
- }
-
- static constexpr const int bigit_bits = num_bits<bigit>();
-
friend struct formatter<bigint>;
- FMT_CONSTEXPR20 void subtract_bigits(int index, bigit other, bigit& borrow) {
- auto result = static_cast<double_bigit>((*this)[index]) - other - borrow;
- (*this)[index] = static_cast<bigit>(result);
+ FMT_CONSTEXPR auto get_bigit(int i) const -> bigit {
+ return i >= exp_ && i < num_bigits() ? bigits_[i - exp_] : 0;
+ }
+
+ FMT_CONSTEXPR void subtract_bigits(int index, bigit other, bigit& borrow) {
+ auto result = double_bigit(bigits_[index]) - other - borrow;
+ bigits_[index] = static_cast<bigit>(result);
borrow = static_cast<bigit>(result >> (bigit_bits * 2 - 1));
}
- FMT_CONSTEXPR20 void remove_leading_zeros() {
+ FMT_CONSTEXPR void remove_leading_zeros() {
int num_bigits = static_cast<int>(bigits_.size()) - 1;
- while (num_bigits > 0 && (*this)[num_bigits] == 0) --num_bigits;
+ while (num_bigits > 0 && bigits_[num_bigits] == 0) --num_bigits;
bigits_.resize(to_unsigned(num_bigits + 1));
}
// Computes *this -= other assuming aligned bigints and *this >= other.
- FMT_CONSTEXPR20 void subtract_aligned(const bigint& other) {
+ FMT_CONSTEXPR void subtract_aligned(const bigint& other) {
FMT_ASSERT(other.exp_ >= exp_, "unaligned bigints");
FMT_ASSERT(compare(*this, other) >= 0, "");
bigit borrow = 0;
int i = other.exp_ - exp_;
for (size_t j = 0, n = other.bigits_.size(); j != n; ++i, ++j)
subtract_bigits(i, other.bigits_[j], borrow);
- while (borrow > 0) subtract_bigits(i, 0, borrow);
+ if (borrow != 0) subtract_bigits(i, 0, borrow);
+ FMT_ASSERT(borrow == 0, "");
remove_leading_zeros();
}
- FMT_CONSTEXPR20 void multiply(uint32_t value) {
- const double_bigit wide_value = value;
+ FMT_CONSTEXPR void multiply(uint32_t value) {
bigit carry = 0;
+ const double_bigit wide_value = value;
for (size_t i = 0, n = bigits_.size(); i < n; ++i) {
double_bigit result = bigits_[i] * wide_value + carry;
bigits_[i] = static_cast<bigit>(result);
template <typename UInt, FMT_ENABLE_IF(std::is_same<UInt, uint64_t>::value ||
std::is_same<UInt, uint128_t>::value)>
- FMT_CONSTEXPR20 void multiply(UInt value) {
+ FMT_CONSTEXPR void multiply(UInt value) {
using half_uint =
conditional_t<std::is_same<UInt, uint128_t>::value, uint64_t, uint32_t>;
const int shift = num_bits<half_uint>() - bigit_bits;
template <typename UInt, FMT_ENABLE_IF(std::is_same<UInt, uint64_t>::value ||
std::is_same<UInt, uint128_t>::value)>
- FMT_CONSTEXPR20 void assign(UInt n) {
+ FMT_CONSTEXPR void assign(UInt n) {
size_t num_bigits = 0;
do {
bigits_[num_bigits++] = static_cast<bigit>(n);
}
public:
- FMT_CONSTEXPR20 bigint() : exp_(0) {}
+ FMT_CONSTEXPR bigint() : exp_(0) {}
explicit bigint(uint64_t n) { assign(n); }
bigint(const bigint&) = delete;
void operator=(const bigint&) = delete;
- FMT_CONSTEXPR20 void assign(const bigint& other) {
+ FMT_CONSTEXPR void assign(const bigint& other) {
auto size = other.bigits_.size();
bigits_.resize(size);
auto data = other.bigits_.data();
exp_ = other.exp_;
}
- template <typename Int> FMT_CONSTEXPR20 void operator=(Int n) {
+ template <typename Int> FMT_CONSTEXPR void operator=(Int n) {
FMT_ASSERT(n > 0, "");
assign(uint64_or_128_t<Int>(n));
}
- FMT_CONSTEXPR20 auto num_bigits() const -> int {
+ FMT_CONSTEXPR auto num_bigits() const -> int {
return static_cast<int>(bigits_.size()) + exp_;
}
- FMT_NOINLINE FMT_CONSTEXPR20 auto operator<<=(int shift) -> bigint& {
+ FMT_CONSTEXPR auto operator<<=(int shift) -> bigint& {
FMT_ASSERT(shift >= 0, "");
exp_ += shift / bigit_bits;
shift %= bigit_bits;
return *this;
}
- template <typename Int>
- FMT_CONSTEXPR20 auto operator*=(Int value) -> bigint& {
+ template <typename Int> FMT_CONSTEXPR auto operator*=(Int value) -> bigint& {
FMT_ASSERT(value > 0, "");
multiply(uint32_or_64_or_128_t<Int>(value));
return *this;
}
- friend FMT_CONSTEXPR20 auto compare(const bigint& lhs, const bigint& rhs)
- -> int {
- int num_lhs_bigits = lhs.num_bigits(), num_rhs_bigits = rhs.num_bigits();
- if (num_lhs_bigits != num_rhs_bigits)
- return num_lhs_bigits > num_rhs_bigits ? 1 : -1;
- int i = static_cast<int>(lhs.bigits_.size()) - 1;
- int j = static_cast<int>(rhs.bigits_.size()) - 1;
+ friend FMT_CONSTEXPR auto compare(const bigint& b1, const bigint& b2) -> int {
+ int num_bigits1 = b1.num_bigits(), num_bigits2 = b2.num_bigits();
+ if (num_bigits1 != num_bigits2) return num_bigits1 > num_bigits2 ? 1 : -1;
+ int i = static_cast<int>(b1.bigits_.size()) - 1;
+ int j = static_cast<int>(b2.bigits_.size()) - 1;
int end = i - j;
if (end < 0) end = 0;
for (; i >= end; --i, --j) {
- bigit lhs_bigit = lhs[i], rhs_bigit = rhs[j];
- if (lhs_bigit != rhs_bigit) return lhs_bigit > rhs_bigit ? 1 : -1;
+ bigit b1_bigit = b1.bigits_[i], b2_bigit = b2.bigits_[j];
+ if (b1_bigit != b2_bigit) return b1_bigit > b2_bigit ? 1 : -1;
}
if (i != j) return i > j ? 1 : -1;
return 0;
}
// Returns compare(lhs1 + lhs2, rhs).
- friend FMT_CONSTEXPR20 auto add_compare(const bigint& lhs1,
- const bigint& lhs2, const bigint& rhs)
- -> int {
- auto minimum = [](int a, int b) { return a < b ? a : b; };
- auto maximum = [](int a, int b) { return a > b ? a : b; };
- int max_lhs_bigits = maximum(lhs1.num_bigits(), lhs2.num_bigits());
+ friend FMT_CONSTEXPR auto add_compare(const bigint& lhs1, const bigint& lhs2,
+ const bigint& rhs) -> int {
+ int max_lhs_bigits = max_of(lhs1.num_bigits(), lhs2.num_bigits());
int num_rhs_bigits = rhs.num_bigits();
if (max_lhs_bigits + 1 < num_rhs_bigits) return -1;
if (max_lhs_bigits > num_rhs_bigits) return 1;
- auto get_bigit = [](const bigint& n, int i) -> bigit {
- return i >= n.exp_ && i < n.num_bigits() ? n[i - n.exp_] : 0;
- };
double_bigit borrow = 0;
- int min_exp = minimum(minimum(lhs1.exp_, lhs2.exp_), rhs.exp_);
+ int min_exp = min_of(min_of(lhs1.exp_, lhs2.exp_), rhs.exp_);
for (int i = num_rhs_bigits - 1; i >= min_exp; --i) {
- double_bigit sum =
- static_cast<double_bigit>(get_bigit(lhs1, i)) + get_bigit(lhs2, i);
- bigit rhs_bigit = get_bigit(rhs, i);
+ double_bigit sum = double_bigit(lhs1.get_bigit(i)) + lhs2.get_bigit(i);
+ bigit rhs_bigit = rhs.get_bigit(i);
if (sum > rhs_bigit + borrow) return 1;
borrow = rhs_bigit + borrow - sum;
if (borrow > 1) return -1;
FMT_CONSTEXPR20 void assign_pow10(int exp) {
FMT_ASSERT(exp >= 0, "");
if (exp == 0) return *this = 1;
- // Find the top bit.
- int bitmask = 1;
- while (exp >= bitmask) bitmask <<= 1;
- bitmask >>= 1;
+ int bitmask = 1 << (num_bits<unsigned>() -
+ countl_zero(static_cast<uint32_t>(exp)) - 1);
// pow(10, exp) = pow(5, exp) * pow(2, exp). First compute pow(5, exp) by
// repeated squaring and multiplication.
*this = 5;
// cross-product terms n[i] * n[j] such that i + j == bigit_index.
for (int i = 0, j = bigit_index; j >= 0; ++i, --j) {
// Most terms are multiplied twice which can be optimized in the future.
- sum += static_cast<double_bigit>(n[i]) * n[j];
+ sum += double_bigit(n[i]) * n[j];
}
- (*this)[bigit_index] = static_cast<bigit>(sum);
+ bigits_[bigit_index] = static_cast<bigit>(sum);
sum >>= num_bits<bigit>(); // Compute the carry.
}
// Do the same for the top half.
for (int bigit_index = num_bigits; bigit_index < num_result_bigits;
++bigit_index) {
for (int j = num_bigits - 1, i = bigit_index - j; i < num_bigits;)
- sum += static_cast<double_bigit>(n[i++]) * n[j--];
- (*this)[bigit_index] = static_cast<bigit>(sum);
+ sum += double_bigit(n[i++]) * n[j--];
+ bigits_[bigit_index] = static_cast<bigit>(sum);
sum >>= num_bits<bigit>();
}
remove_leading_zeros();
// If this bigint has a bigger exponent than other, adds trailing zero to make
// exponents equal. This simplifies some operations such as subtraction.
- FMT_CONSTEXPR20 void align(const bigint& other) {
+ FMT_CONSTEXPR void align(const bigint& other) {
int exp_difference = exp_ - other.exp_;
if (exp_difference <= 0) return;
int num_bigits = static_cast<int>(bigits_.size());
bigits_.resize(to_unsigned(num_bigits + exp_difference));
for (int i = num_bigits - 1, j = i + exp_difference; i >= 0; --i, --j)
bigits_[j] = bigits_[i];
- memset(bigits_.data(), 0, to_unsigned(exp_difference) * sizeof(bigit));
+ fill_n(bigits_.data(), to_unsigned(exp_difference), 0U);
exp_ -= exp_difference;
}
// Divides this bignum by divisor, assigning the remainder to this and
// returning the quotient.
- FMT_CONSTEXPR20 auto divmod_assign(const bigint& divisor) -> int {
+ FMT_CONSTEXPR auto divmod_assign(const bigint& divisor) -> int {
FMT_ASSERT(this != &divisor, "");
if (compare(*this, divisor) < 0) return 0;
FMT_ASSERT(divisor.bigits_[divisor.bigits_.size() - 1u] != 0, "");
// Assume Float is in the format [sign][exponent][significand].
using carrier_uint = typename info::carrier_uint;
- constexpr auto num_float_significand_bits =
- detail::num_significand_bits<Float>();
+ const auto num_float_significand_bits = detail::num_significand_bits<Float>();
basic_fp<carrier_uint> f(value);
f.e += num_float_significand_bits;
if (!has_implicit_bit<Float>()) --f.e;
- constexpr auto num_fraction_bits =
+ const auto num_fraction_bits =
num_float_significand_bits + (has_implicit_bit<Float>() ? 1 : 0);
- constexpr auto num_xdigits = (num_fraction_bits + 3) / 4;
+ const auto num_xdigits = (num_fraction_bits + 3) / 4;
- constexpr auto leading_shift = ((num_xdigits - 1) * 4);
+ const auto leading_shift = ((num_xdigits - 1) * 4);
const auto leading_mask = carrier_uint(0xF) << leading_shift;
const auto leading_xdigit =
static_cast<uint32_t>((f.f & leading_mask) >> leading_shift);
char xdigits[num_bits<carrier_uint>() / 4];
detail::fill_n(xdigits, sizeof(xdigits), '0');
- format_uint<4>(xdigits, f.f, num_xdigits, specs.upper);
+ format_base2e(4, xdigits, f.f, num_xdigits, specs.upper());
// Remove zero tail
while (print_xdigits > 0 && xdigits[print_xdigits] == '0') --print_xdigits;
buf.push_back('0');
- buf.push_back(specs.upper ? 'X' : 'x');
+ buf.push_back(specs.upper() ? 'X' : 'x');
buf.push_back(xdigits[0]);
- if (specs.alt || print_xdigits > 0 || print_xdigits < specs.precision)
+ if (specs.alt() || print_xdigits > 0 || print_xdigits < specs.precision)
buf.push_back('.');
buf.append(xdigits + 1, xdigits + 1 + print_xdigits);
for (; print_xdigits < specs.precision; ++print_xdigits) buf.push_back('0');
- buf.push_back(specs.upper ? 'P' : 'p');
+ buf.push_back(specs.upper() ? 'P' : 'p');
uint32_t abs_e;
if (f.e < 0) {
}
template <typename Float>
-FMT_CONSTEXPR20 auto format_float(Float value, int precision, float_specs specs,
+FMT_CONSTEXPR20 auto format_float(Float value, int precision,
+ const format_specs& specs, bool binary32,
buffer<char>& buf) -> int {
// float is passed as double to reduce the number of instantiations.
static_assert(!std::is_same<Float, float>::value, "");
- FMT_ASSERT(value >= 0, "value is negative");
auto converted_value = convert_float(value);
- const bool fixed = specs.format == float_format::fixed;
- if (value <= 0) { // <= instead of == to silence a warning.
+ const bool fixed = specs.type() == presentation_type::fixed;
+ if (value == 0) {
if (precision <= 0 || !fixed) {
buf.push_back('0');
return 0;
exp = static_cast<int>(e);
if (e > exp) ++exp; // Compute ceil.
dragon_flags = dragon::fixup;
- } else if (precision < 0) {
- // Use Dragonbox for the shortest format.
- if (specs.binary32) {
- auto dec = dragonbox::to_decimal(static_cast<float>(value));
- write<char>(appender(buf), dec.significand);
- return dec.exponent;
- }
- auto dec = dragonbox::to_decimal(static_cast<double>(value));
- write<char>(appender(buf), dec.significand);
- return dec.exponent;
} else {
// Extract significand bits and exponent bits.
using info = dragonbox::float_info<double>;
uint64_t prod;
uint32_t digits;
bool should_round_up;
- int number_of_digits_to_print = precision > 9 ? 9 : precision;
+ int number_of_digits_to_print = min_of(precision, 9);
// Print a 9-digits subsegment, either the first or the second.
auto print_subsegment = [&](uint32_t subsegment, char* buffer) {
// for details.
prod = ((subsegment * static_cast<uint64_t>(450359963)) >> 20) + 1;
digits = static_cast<uint32_t>(prod >> 32);
- copy2(buffer, digits2(digits));
+ write2digits(buffer, digits);
number_of_digits_printed += 2;
}
while (number_of_digits_printed < number_of_digits_to_print) {
prod = static_cast<uint32_t>(prod) * static_cast<uint64_t>(100);
digits = static_cast<uint32_t>(prod >> 32);
- copy2(buffer + number_of_digits_printed, digits2(digits));
+ write2digits(buffer + number_of_digits_printed, digits);
number_of_digits_printed += 2;
}
};
}
if (use_dragon) {
auto f = basic_fp<uint128_t>();
- bool is_predecessor_closer = specs.binary32
- ? f.assign(static_cast<float>(value))
- : f.assign(converted_value);
+ bool is_predecessor_closer = binary32 ? f.assign(static_cast<float>(value))
+ : f.assign(converted_value);
if (is_predecessor_closer) dragon_flags |= dragon::predecessor_closer;
if (fixed) dragon_flags |= dragon::fixed;
// Limit precision to the maximum possible number of significant digits in
if (precision > max_double_digits) precision = max_double_digits;
format_dragon(f, dragon_flags, precision, buf, exp);
}
- if (!fixed && !specs.showpoint) {
+ if (!fixed && !specs.alt()) {
// Remove trailing zeros.
auto num_digits = buf.size();
while (num_digits > 0 && buf[num_digits - 1] == '0') {
return exp;
}
-template <typename Char, typename OutputIt, typename T>
-FMT_CONSTEXPR20 auto write_float(OutputIt out, T value, format_specs specs,
- locale_ref loc) -> OutputIt {
- sign_t sign = specs.sign;
- if (detail::signbit(value)) { // value < 0 is false for NaN so use signbit.
- sign = sign::minus;
- value = -value;
- } else if (sign == sign::minus) {
- sign = sign::none;
- }
+template <typename Char, typename OutputIt, typename T,
+ FMT_ENABLE_IF(is_floating_point<T>::value)>
+FMT_CONSTEXPR20 auto write(OutputIt out, T value, format_specs specs,
+ locale_ref loc = {}) -> OutputIt {
+ if (specs.localized() && write_loc(out, value, specs, loc)) return out;
+
+ // Use signbit because value < 0 is false for NaN.
+ sign s = detail::signbit(value) ? sign::minus : specs.sign();
if (!detail::isfinite(value))
- return write_nonfinite<Char>(out, detail::isnan(value), specs, sign);
+ return write_nonfinite<Char>(out, detail::isnan(value), specs, s);
- if (specs.align == align::numeric && sign) {
- auto it = reserve(out, 1);
- *it++ = detail::sign<Char>(sign);
- out = base_iterator(out, it);
- sign = sign::none;
+ if (specs.align() == align::numeric && s != sign::none) {
+ *out++ = detail::getsign<Char>(s);
+ s = sign::none;
if (specs.width != 0) --specs.width;
}
+ const int exp_upper = detail::exp_upper<T>();
+ int precision = specs.precision;
+ if (precision < 0) {
+ if (specs.type() != presentation_type::none) {
+ precision = 6;
+ } else if (is_fast_float<T>::value && !is_constant_evaluated()) {
+ // Use Dragonbox for the shortest format.
+ auto dec = dragonbox::to_decimal(static_cast<fast_float_t<T>>(value));
+ return write_float<Char>(out, dec, specs, s, exp_upper, loc);
+ }
+ }
+
memory_buffer buffer;
- if (specs.type == presentation_type::hexfloat) {
- if (sign) buffer.push_back(detail::sign<char>(sign));
+ if (specs.type() == presentation_type::hexfloat) {
+ if (s != sign::none) buffer.push_back(detail::getsign<char>(s));
format_hexfloat(convert_float(value), specs, buffer);
return write_bytes<Char, align::right>(out, {buffer.data(), buffer.size()},
specs);
}
- int precision = specs.precision >= 0 || specs.type == presentation_type::none
- ? specs.precision
- : 6;
- if (specs.type == presentation_type::exp) {
+ if (specs.type() == presentation_type::exp) {
if (precision == max_value<int>())
report_error("number is too big");
else
++precision;
- } else if (specs.type != presentation_type::fixed && precision == 0) {
+ if (specs.precision != 0) specs.set_alt();
+ } else if (specs.type() == presentation_type::fixed) {
+ if (specs.precision != 0) specs.set_alt();
+ } else if (precision == 0) {
precision = 1;
}
- float_specs fspecs = parse_float_type_spec(specs);
- fspecs.sign = sign;
- if (const_check(std::is_same<T, float>())) fspecs.binary32 = true;
- int exp = format_float(convert_float(value), precision, fspecs, buffer);
- fspecs.precision = precision;
- auto f = big_decimal_fp{buffer.data(), static_cast<int>(buffer.size()), exp};
- return write_float<Char>(out, f, specs, fspecs, loc);
-}
+ int exp = format_float(convert_float(value), precision, specs,
+ std::is_same<T, float>(), buffer);
-template <typename Char, typename OutputIt, typename T,
- FMT_ENABLE_IF(is_floating_point<T>::value)>
-FMT_CONSTEXPR20 auto write(OutputIt out, T value, format_specs specs,
- locale_ref loc = {}) -> OutputIt {
- if (const_check(!is_supported_floating_point(value))) return out;
- return specs.localized && write_loc(out, value, specs, loc)
- ? out
- : write_float<Char>(out, value, specs, loc);
+ specs.precision = precision;
+ auto f = big_decimal_fp{buffer.data(), static_cast<int>(buffer.size()), exp};
+ return write_float<Char>(out, f, specs, s, exp_upper, loc);
}
template <typename Char, typename OutputIt, typename T,
FMT_ENABLE_IF(is_fast_float<T>::value)>
FMT_CONSTEXPR20 auto write(OutputIt out, T value) -> OutputIt {
if (is_constant_evaluated()) return write<Char>(out, value, format_specs());
- if (const_check(!is_supported_floating_point(value))) return out;
- auto sign = sign_t::none;
- if (detail::signbit(value)) {
- sign = sign::minus;
- value = -value;
+ auto s = detail::signbit(value) ? sign::minus : sign::none;
+ auto mask = exponent_mask<fast_float_t<T>>();
+ if ((bit_cast<decltype(mask)>(value) & mask) == mask)
+ return write_nonfinite<Char>(out, std::isnan(value), {}, s);
+
+ auto dec = dragonbox::to_decimal(static_cast<fast_float_t<T>>(value));
+ auto significand = dec.significand;
+ int significand_size = count_digits(significand);
+ int exponent = dec.exponent + significand_size - 1;
+ if (use_fixed(exponent, detail::exp_upper<T>())) {
+ return write_fixed<Char, fallback_digit_grouping<Char>>(
+ out, dec, significand_size, Char('.'), {}, s);
+ }
+
+ // Write value in the exponential format.
+ const char* prefix = "e+";
+ int abs_exponent = exponent;
+ if (exponent < 0) {
+ abs_exponent = -exponent;
+ prefix = "e-";
+ }
+ auto has_decimal_point = significand_size != 1;
+ size_t size = std::is_pointer<OutputIt>::value
+ ? 0u
+ : to_unsigned((s != sign::none ? 1 : 0) + significand_size +
+ (has_decimal_point ? 1 : 0) +
+ (abs_exponent >= 100 ? 5 : 4));
+ if (auto ptr = to_pointer<Char>(out, size)) {
+ if (s != sign::none) *ptr++ = Char('-');
+ if (has_decimal_point) {
+ auto begin = ptr;
+ ptr = format_decimal<Char>(ptr, significand, significand_size + 1);
+ *begin = begin[1];
+ begin[1] = '.';
+ } else {
+ *ptr++ = static_cast<Char>('0' + significand);
+ }
+ if (std::is_same<Char, char>::value) {
+ memcpy(ptr, prefix, 2);
+ ptr += 2;
+ } else {
+ *ptr++ = prefix[0];
+ *ptr++ = prefix[1];
+ }
+ if (abs_exponent >= 100) {
+ *ptr++ = static_cast<Char>('0' + abs_exponent / 100);
+ abs_exponent %= 100;
+ }
+ write2digits(ptr, static_cast<unsigned>(abs_exponent));
+ return select<std::is_pointer<OutputIt>::value>(ptr + 2, out);
}
-
- constexpr auto specs = format_specs();
- using floaty = conditional_t<std::is_same<T, long double>::value, double, T>;
- using floaty_uint = typename dragonbox::float_info<floaty>::carrier_uint;
- floaty_uint mask = exponent_mask<floaty>();
- if ((bit_cast<floaty_uint>(value) & mask) == mask)
- return write_nonfinite<Char>(out, std::isnan(value), specs, sign);
-
- auto fspecs = float_specs();
- fspecs.sign = sign;
- auto dec = dragonbox::to_decimal(static_cast<floaty>(value));
- return write_float<Char>(out, dec, specs, fspecs, {});
+ auto it = reserve(out, size);
+ if (s != sign::none) *it++ = Char('-');
+ // Insert a decimal point after the first digit and add an exponent.
+ it = write_significand(it, significand, significand_size, 1,
+ has_decimal_point ? Char('.') : Char());
+ *it++ = Char('e');
+ it = write_exponent<Char>(exponent, it);
+ return base_iterator(out, it);
}
template <typename Char, typename OutputIt, typename T,
FMT_ENABLE_IF(is_floating_point<T>::value &&
!is_fast_float<T>::value)>
inline auto write(OutputIt out, T value) -> OutputIt {
- return write<Char>(out, value, format_specs());
+ return write<Char>(out, value, {});
}
template <typename Char, typename OutputIt>
template <typename Char, typename OutputIt>
FMT_CONSTEXPR auto write(OutputIt out, basic_string_view<Char> value)
-> OutputIt {
- auto it = reserve(out, value.size());
- it = copy_noinline<Char>(value.begin(), value.end(), it);
- return base_iterator(out, it);
+ return copy_noinline<Char>(value.begin(), value.end(), out);
}
template <typename Char, typename OutputIt, typename T,
// FMT_ENABLE_IF() condition separated to workaround an MSVC bug.
template <
typename Char, typename OutputIt, typename T,
- bool check =
- std::is_enum<T>::value && !std::is_same<T, Char>::value &&
- mapped_type_constant<T, basic_format_context<OutputIt, Char>>::value !=
- type::custom_type,
+ bool check = std::is_enum<T>::value && !std::is_same<T, Char>::value &&
+ mapped_type_constant<T, Char>::value != type::custom_type,
FMT_ENABLE_IF(check)>
FMT_CONSTEXPR auto write(OutputIt out, T value) -> OutputIt {
return write<Char>(out, static_cast<underlying_t<T>>(value));
FMT_ENABLE_IF(std::is_same<T, bool>::value)>
FMT_CONSTEXPR auto write(OutputIt out, T value, const format_specs& specs = {},
locale_ref = {}) -> OutputIt {
- return specs.type != presentation_type::none &&
- specs.type != presentation_type::string
+ return specs.type() != presentation_type::none &&
+ specs.type() != presentation_type::string
? write<Char>(out, value ? 1 : 0, specs, {})
: write_bytes<Char>(out, value ? "true" : "false", specs);
}
return write_ptr<Char>(out, bit_cast<uintptr_t>(value), &specs);
}
-// A write overload that handles implicit conversions.
template <typename Char, typename OutputIt, typename T,
- typename Context = basic_format_context<OutputIt, Char>>
-FMT_CONSTEXPR auto write(OutputIt out, const T& value) -> enable_if_t<
- std::is_class<T>::value && !has_to_string_view<T>::value &&
- !is_floating_point<T>::value && !std::is_same<T, Char>::value &&
- !std::is_same<T, remove_cvref_t<decltype(arg_mapper<Context>().map(
- value))>>::value,
- OutputIt> {
- return write<Char>(out, arg_mapper<Context>().map(value));
+ FMT_ENABLE_IF(mapped_type_constant<T, Char>::value ==
+ type::custom_type &&
+ !std::is_fundamental<T>::value)>
+FMT_CONSTEXPR auto write(OutputIt out, const T& value) -> OutputIt {
+ auto f = formatter<T, Char>();
+ auto parse_ctx = parse_context<Char>({});
+ f.parse(parse_ctx);
+ auto ctx = basic_format_context<OutputIt, Char>(out, {}, {});
+ return f.format(value, ctx);
}
-template <typename Char, typename OutputIt, typename T,
- typename Context = basic_format_context<OutputIt, Char>>
-FMT_CONSTEXPR auto write(OutputIt out, const T& value)
- -> enable_if_t<mapped_type_constant<T, Context>::value ==
- type::custom_type &&
- !std::is_fundamental<T>::value,
- OutputIt> {
- auto formatter = typename Context::template formatter_type<T>();
- auto parse_ctx = typename Context::parse_context_type({});
- formatter.parse(parse_ctx);
- auto ctx = Context(out, {}, {});
- return formatter.format(value, ctx);
-}
+template <typename T>
+using is_builtin =
+ bool_constant<std::is_same<T, int>::value || FMT_BUILTIN_TYPES>;
// An argument visitor that formats the argument and writes it via the output
// iterator. It's a class and not a generic lambda for compatibility with C++11.
template <typename Char> struct default_arg_formatter {
- using iterator = basic_appender<Char>;
using context = buffered_context<Char>;
- iterator out;
- basic_format_args<context> args;
- locale_ref loc;
+ basic_appender<Char> out;
+
+ void operator()(monostate) { report_error("argument not found"); }
+
+ template <typename T, FMT_ENABLE_IF(is_builtin<T>::value)>
+ void operator()(T value) {
+ write<Char>(out, value);
+ }
- template <typename T> auto operator()(T value) -> iterator {
- return write<Char>(out, value);
+ template <typename T, FMT_ENABLE_IF(!is_builtin<T>::value)>
+ void operator()(T) {
+ FMT_ASSERT(false, "");
}
- auto operator()(typename basic_format_arg<context>::handle h) -> iterator {
- basic_format_parse_context<Char> parse_ctx({});
- context format_ctx(out, args, loc);
+
+ void operator()(typename basic_format_arg<context>::handle h) {
+ // Use a null locale since the default format must be unlocalized.
+ auto parse_ctx = parse_context<Char>({});
+ auto format_ctx = context(out, {}, {});
h.format(parse_ctx, format_ctx);
- return format_ctx.out();
}
};
template <typename Char> struct arg_formatter {
- using iterator = basic_appender<Char>;
- using context = buffered_context<Char>;
-
- iterator out;
+ basic_appender<Char> out;
const format_specs& specs;
- locale_ref locale;
+ FMT_NO_UNIQUE_ADDRESS locale_ref locale;
- template <typename T>
- FMT_CONSTEXPR FMT_INLINE auto operator()(T value) -> iterator {
- return detail::write<Char>(out, value, specs, locale);
+ template <typename T, FMT_ENABLE_IF(is_builtin<T>::value)>
+ FMT_CONSTEXPR FMT_INLINE void operator()(T value) {
+ detail::write<Char>(out, value, specs, locale);
}
- auto operator()(typename basic_format_arg<context>::handle) -> iterator {
- // User-defined types are handled separately because they require access
- // to the parse context.
- return out;
- }
-};
-struct width_checker {
- template <typename T, FMT_ENABLE_IF(is_integer<T>::value)>
- FMT_CONSTEXPR auto operator()(T value) -> unsigned long long {
- if (is_negative(value)) report_error("negative width");
- return static_cast<unsigned long long>(value);
+ template <typename T, FMT_ENABLE_IF(!is_builtin<T>::value)>
+ void operator()(T) {
+ FMT_ASSERT(false, "");
}
- template <typename T, FMT_ENABLE_IF(!is_integer<T>::value)>
- FMT_CONSTEXPR auto operator()(T) -> unsigned long long {
- report_error("width is not integer");
- return 0;
+ void operator()(typename basic_format_arg<buffered_context<Char>>::handle) {
+ // User-defined types are handled separately because they require access
+ // to the parse context.
}
};
-struct precision_checker {
+struct dynamic_spec_getter {
template <typename T, FMT_ENABLE_IF(is_integer<T>::value)>
FMT_CONSTEXPR auto operator()(T value) -> unsigned long long {
- if (is_negative(value)) report_error("negative precision");
- return static_cast<unsigned long long>(value);
+ return is_negative(value) ? ~0ull : static_cast<unsigned long long>(value);
}
template <typename T, FMT_ENABLE_IF(!is_integer<T>::value)>
FMT_CONSTEXPR auto operator()(T) -> unsigned long long {
- report_error("precision is not integer");
+ report_error("width/precision is not integer");
return 0;
}
};
-template <typename Handler, typename FormatArg>
-FMT_CONSTEXPR auto get_dynamic_spec(FormatArg arg) -> int {
- unsigned long long value = arg.visit(Handler());
- if (value > to_unsigned(max_value<int>())) report_error("number is too big");
- return static_cast<int>(value);
-}
-
-template <typename Context, typename ID>
-FMT_CONSTEXPR auto get_arg(Context& ctx, ID id) -> decltype(ctx.arg(id)) {
- auto arg = ctx.arg(id);
+template <typename Context>
+FMT_CONSTEXPR void handle_dynamic_spec(
+ arg_id_kind kind, int& value,
+ const arg_ref<typename Context::char_type>& ref, Context& ctx) {
+ if (kind == arg_id_kind::none) return;
+ auto arg =
+ kind == arg_id_kind::index ? ctx.arg(ref.index) : ctx.arg(ref.name);
if (!arg) report_error("argument not found");
- return arg;
-}
-
-template <typename Handler, typename Context>
-FMT_CONSTEXPR void handle_dynamic_spec(int& value,
- arg_ref<typename Context::char_type> ref,
- Context& ctx) {
- switch (ref.kind) {
- case arg_id_kind::none:
- break;
- case arg_id_kind::index:
- value = detail::get_dynamic_spec<Handler>(get_arg(ctx, ref.val.index));
- break;
- case arg_id_kind::name:
- value = detail::get_dynamic_spec<Handler>(get_arg(ctx, ref.val.name));
- break;
- }
+ unsigned long long result = arg.visit(dynamic_spec_getter());
+ if (result > to_unsigned(max_value<int>()))
+ report_error("width/precision is out of range");
+ value = static_cast<int>(result);
}
-#if FMT_USE_USER_DEFINED_LITERALS
-# if FMT_USE_NONTYPE_TEMPLATE_ARGS
+#if FMT_USE_NONTYPE_TEMPLATE_ARGS
template <typename T, typename Char, size_t N,
- fmt::detail_exported::fixed_string<Char, N> Str>
-struct statically_named_arg : view {
+ fmt::detail::fixed_string<Char, N> Str>
+struct static_named_arg : view {
static constexpr auto name = Str.data;
const T& value;
- statically_named_arg(const T& v) : value(v) {}
+ static_named_arg(const T& v) : value(v) {}
};
template <typename T, typename Char, size_t N,
- fmt::detail_exported::fixed_string<Char, N> Str>
-struct is_named_arg<statically_named_arg<T, Char, N, Str>> : std::true_type {};
+ fmt::detail::fixed_string<Char, N> Str>
+struct is_named_arg<static_named_arg<T, Char, N, Str>> : std::true_type {};
template <typename T, typename Char, size_t N,
- fmt::detail_exported::fixed_string<Char, N> Str>
-struct is_statically_named_arg<statically_named_arg<T, Char, N, Str>>
- : std::true_type {};
+ fmt::detail::fixed_string<Char, N> Str>
+struct is_static_named_arg<static_named_arg<T, Char, N, Str>> : std::true_type {
+};
-template <typename Char, size_t N,
- fmt::detail_exported::fixed_string<Char, N> Str>
+template <typename Char, size_t N, fmt::detail::fixed_string<Char, N> Str>
struct udl_arg {
template <typename T> auto operator=(T&& value) const {
- return statically_named_arg<T, Char, N, Str>(std::forward<T>(value));
+ return static_named_arg<T, Char, N, Str>(std::forward<T>(value));
}
};
-# else
+#else
template <typename Char> struct udl_arg {
const Char* str;
return {str, std::forward<T>(value)};
}
};
-# endif
-#endif // FMT_USE_USER_DEFINED_LITERALS
-
-template <typename Locale, typename Char>
-auto vformat(const Locale& loc, basic_string_view<Char> fmt,
- typename detail::vformat_args<Char>::type args)
- -> std::basic_string<Char> {
- auto buf = basic_memory_buffer<Char>();
- detail::vformat_to(buf, fmt, args, detail::locale_ref(loc));
- return {buf.data(), buf.size()};
-}
+#endif // FMT_USE_NONTYPE_TEMPLATE_ARGS
+
+template <typename Char = char> struct format_handler {
+ parse_context<Char> parse_ctx;
+ buffered_context<Char> ctx;
+
+ void on_text(const Char* begin, const Char* end) {
+ copy_noinline<Char>(begin, end, ctx.out());
+ }
+
+ FMT_CONSTEXPR auto on_arg_id() -> int { return parse_ctx.next_arg_id(); }
+ FMT_CONSTEXPR auto on_arg_id(int id) -> int {
+ parse_ctx.check_arg_id(id);
+ return id;
+ }
+ FMT_CONSTEXPR auto on_arg_id(basic_string_view<Char> id) -> int {
+ parse_ctx.check_arg_id(id);
+ int arg_id = ctx.arg_id(id);
+ if (arg_id < 0) report_error("argument not found");
+ return arg_id;
+ }
+
+ FMT_INLINE void on_replacement_field(int id, const Char*) {
+ ctx.arg(id).visit(default_arg_formatter<Char>{ctx.out()});
+ }
+
+ auto on_format_specs(int id, const Char* begin, const Char* end)
+ -> const Char* {
+ auto arg = ctx.arg(id);
+ if (!arg) report_error("argument not found");
+ // Not using a visitor for custom types gives better codegen.
+ if (arg.format_custom(begin, parse_ctx, ctx)) return parse_ctx.begin();
+
+ auto specs = dynamic_format_specs<Char>();
+ begin = parse_format_specs(begin, end, specs, parse_ctx, arg.type());
+ if (specs.dynamic()) {
+ handle_dynamic_spec(specs.dynamic_width(), specs.width, specs.width_ref,
+ ctx);
+ handle_dynamic_spec(specs.dynamic_precision(), specs.precision,
+ specs.precision_ref, ctx);
+ }
+ arg.visit(arg_formatter<Char>{ctx.out(), specs, ctx.locale()});
+ return begin;
+ }
+
+ FMT_NORETURN void on_error(const char* message) { report_error(message); }
+};
+
+// It is used in format-inl.h and os.cc.
using format_func = void (*)(detail::buffer<char>&, int, const char*);
+FMT_API void do_report_error(format_func func, int error_code,
+ const char* message) noexcept;
FMT_API void format_error_code(buffer<char>& out, int error_code,
string_view message) noexcept;
-using fmt::report_error;
-FMT_API void report_error(format_func func, int error_code,
- const char* message) noexcept;
+template <typename T, typename Char, type TYPE>
+template <typename FormatContext>
+FMT_CONSTEXPR auto native_formatter<T, Char, TYPE>::format(
+ const T& val, FormatContext& ctx) const -> decltype(ctx.out()) {
+ if (!specs_.dynamic())
+ return write<Char>(ctx.out(), val, specs_, ctx.locale());
+ auto specs = format_specs(specs_);
+ handle_dynamic_spec(specs.dynamic_width(), specs.width, specs_.width_ref,
+ ctx);
+ handle_dynamic_spec(specs.dynamic_precision(), specs.precision,
+ specs_.precision_ref, ctx);
+ return write<Char>(ctx.out(), val, specs, ctx.locale());
+}
} // namespace detail
FMT_BEGIN_EXPORT
-FMT_API auto vsystem_error(int error_code, string_view format_str,
- format_args args) -> std::system_error;
-/**
- * Constructs `std::system_error` with a message formatted with
- * `fmt::format(fmt, args...)`.
- * `error_code` is a system error code as given by `errno`.
- *
- * **Example**:
- *
- * // This throws std::system_error with the description
- * // cannot open file 'madeup': No such file or directory
- * // or similar (system message may vary).
- * const char* filename = "madeup";
- * std::FILE* file = std::fopen(filename, "r");
- * if (!file)
- * throw fmt::system_error(errno, "cannot open file '{}'", filename);
- */
-template <typename... T>
-auto system_error(int error_code, format_string<T...> fmt, T&&... args)
- -> std::system_error {
- return vsystem_error(error_code, fmt, fmt::make_format_args(args...));
-}
+// A generic formatting context with custom output iterator and character
+// (code unit) support. Char is the format string code unit type which can be
+// different from OutputIt::value_type.
+template <typename OutputIt, typename Char> class generic_context {
+ private:
+ OutputIt out_;
+ basic_format_args<generic_context> args_;
+ locale_ref loc_;
-/**
- * Formats an error message for an error returned by an operating system or a
- * language runtime, for example a file opening error, and writes it to `out`.
- * The format is the same as the one used by `std::system_error(ec, message)`
- * where `ec` is `std::error_code(error_code, std::generic_category())`.
- * It is implementation-defined but normally looks like:
- *
- * <message>: <system-message>
- *
- * where `<message>` is the passed message and `<system-message>` is the system
- * message corresponding to the error code.
- * `error_code` is a system error code as given by `errno`.
- */
-FMT_API void format_system_error(detail::buffer<char>& out, int error_code,
- const char* message) noexcept;
+ public:
+ using char_type = Char;
+ using iterator = OutputIt;
+ enum { builtin_types = FMT_BUILTIN_TYPES };
-// Reports a system error without throwing an exception.
-// Can be used to report errors from destructors.
-FMT_API void report_system_error(int error_code, const char* message) noexcept;
+ constexpr generic_context(OutputIt out,
+ basic_format_args<generic_context> args,
+ locale_ref loc = {})
+ : out_(out), args_(args), loc_(loc) {}
+ generic_context(generic_context&&) = default;
+ generic_context(const generic_context&) = delete;
+ void operator=(const generic_context&) = delete;
-/// A fast integer formatter.
-class format_int {
- private:
- // Buffer should be large enough to hold all digits (digits10 + 1),
- // a sign and a null character.
- enum { buffer_size = std::numeric_limits<unsigned long long>::digits10 + 3 };
- mutable char buffer_[buffer_size];
- char* str_;
-
- template <typename UInt>
- FMT_CONSTEXPR20 auto format_unsigned(UInt value) -> char* {
- auto n = static_cast<detail::uint32_or_64_or_128_t<UInt>>(value);
- return detail::format_decimal(buffer_, n, buffer_size - 1).begin;
+ constexpr auto arg(int id) const -> basic_format_arg<generic_context> {
+ return args_.get(id);
+ }
+ auto arg(basic_string_view<Char> name) const
+ -> basic_format_arg<generic_context> {
+ return args_.get(name);
+ }
+ constexpr auto arg_id(basic_string_view<Char> name) const -> int {
+ return args_.get_id(name);
}
- template <typename Int>
- FMT_CONSTEXPR20 auto format_signed(Int value) -> char* {
- auto abs_value = static_cast<detail::uint32_or_64_or_128_t<Int>>(value);
- bool negative = value < 0;
- if (negative) abs_value = 0 - abs_value;
- auto begin = format_unsigned(abs_value);
- if (negative) *--begin = '-';
- return begin;
+ constexpr auto out() const -> iterator { return out_; }
+
+ void advance_to(iterator it) {
+ if (!detail::is_back_insert_iterator<iterator>()) out_ = it;
}
+ constexpr auto locale() const -> locale_ref { return loc_; }
+};
+
+class loc_value {
+ private:
+ basic_format_arg<context> value_;
+
public:
- explicit FMT_CONSTEXPR20 format_int(int value) : str_(format_signed(value)) {}
- explicit FMT_CONSTEXPR20 format_int(long value)
- : str_(format_signed(value)) {}
- explicit FMT_CONSTEXPR20 format_int(long long value)
- : str_(format_signed(value)) {}
- explicit FMT_CONSTEXPR20 format_int(unsigned value)
- : str_(format_unsigned(value)) {}
- explicit FMT_CONSTEXPR20 format_int(unsigned long value)
- : str_(format_unsigned(value)) {}
- explicit FMT_CONSTEXPR20 format_int(unsigned long long value)
- : str_(format_unsigned(value)) {}
+ template <typename T, FMT_ENABLE_IF(!detail::is_float128<T>::value)>
+ loc_value(T value) : value_(value) {}
- /// Returns the number of characters written to the output buffer.
- FMT_CONSTEXPR20 auto size() const -> size_t {
- return detail::to_unsigned(buffer_ - str_ + buffer_size - 1);
+ template <typename T, FMT_ENABLE_IF(detail::is_float128<T>::value)>
+ loc_value(T) {}
+
+ template <typename Visitor> auto visit(Visitor&& vis) -> decltype(vis(0)) {
+ return value_.visit(vis);
}
+};
- /// Returns a pointer to the output buffer content. No terminating null
- /// character is appended.
- FMT_CONSTEXPR20 auto data() const -> const char* { return str_; }
+// A locale facet that formats values in UTF-8.
+// It is parameterized on the locale to avoid the heavy <locale> include.
+template <typename Locale> class format_facet : public Locale::facet {
+ private:
+ std::string separator_;
+ std::string grouping_;
+ std::string decimal_point_;
- /// Returns a pointer to the output buffer content with terminating null
- /// character appended.
- FMT_CONSTEXPR20 auto c_str() const -> const char* {
- buffer_[buffer_size - 1] = '\0';
- return str_;
- }
+ protected:
+ virtual auto do_put(appender out, loc_value val,
+ const format_specs& specs) const -> bool;
- /// Returns the content of the output buffer as an `std::string`.
- auto str() const -> std::string { return std::string(str_, size()); }
-};
+ public:
+ static FMT_API typename Locale::id id;
-template <typename T, typename Char>
-struct formatter<T, Char, enable_if_t<detail::has_format_as<T>::value>>
- : formatter<detail::format_as_t<T>, Char> {
- template <typename FormatContext>
- auto format(const T& value, FormatContext& ctx) const -> decltype(ctx.out()) {
- using base = formatter<detail::format_as_t<T>, Char>;
- auto&& val = format_as(value); // Make an lvalue reference for format.
- return base::format(val, ctx);
+ explicit format_facet(Locale& loc);
+ explicit format_facet(string_view sep = "", std::string grouping = "\3",
+ std::string decimal_point = ".")
+ : separator_(sep.data(), sep.size()),
+ grouping_(grouping),
+ decimal_point_(decimal_point) {}
+
+ auto put(appender out, loc_value val, const format_specs& specs) const
+ -> bool {
+ return do_put(out, val, specs);
}
};
-#define FMT_FORMAT_AS(Type, Base) \
- template <typename Char> \
- struct formatter<Type, Char> : formatter<Base, Char> {}
+#define FMT_FORMAT_AS(Type, Base) \
+ template <typename Char> \
+ struct formatter<Type, Char> : formatter<Base, Char> { \
+ template <typename FormatContext> \
+ FMT_CONSTEXPR auto format(Type value, FormatContext& ctx) const \
+ -> decltype(ctx.out()) { \
+ return formatter<Base, Char>::format(value, ctx); \
+ } \
+ }
FMT_FORMAT_AS(signed char, int);
FMT_FORMAT_AS(unsigned char, unsigned);
FMT_FORMAT_AS(long, detail::long_type);
FMT_FORMAT_AS(unsigned long, detail::ulong_type);
FMT_FORMAT_AS(Char*, const Char*);
-FMT_FORMAT_AS(std::nullptr_t, const void*);
FMT_FORMAT_AS(detail::std_string_view<Char>, basic_string_view<Char>);
+FMT_FORMAT_AS(std::nullptr_t, const void*);
FMT_FORMAT_AS(void*, const void*);
+template <typename Char, size_t N>
+struct formatter<Char[N], Char> : formatter<basic_string_view<Char>, Char> {};
+
template <typename Char, typename Traits, typename Allocator>
class formatter<std::basic_string<Char, Traits, Allocator>, Char>
: public formatter<basic_string_view<Char>, Char> {};
-template <typename Char, size_t N>
-struct formatter<Char[N], Char> : formatter<basic_string_view<Char>, Char> {};
+template <int N, typename Char>
+struct formatter<detail::bitint<N>, Char> : formatter<long long, Char> {};
+template <int N, typename Char>
+struct formatter<detail::ubitint<N>, Char>
+ : formatter<unsigned long long, Char> {};
+
+template <typename Char>
+struct formatter<detail::float128, Char>
+ : detail::native_formatter<detail::float128, Char,
+ detail::type::float_type> {};
+
+template <typename T, typename Char>
+struct formatter<T, Char, void_t<detail::format_as_result<T>>>
+ : formatter<detail::format_as_result<T>, Char> {
+ template <typename FormatContext>
+ FMT_CONSTEXPR auto format(const T& value, FormatContext& ctx) const
+ -> decltype(ctx.out()) {
+ auto&& val = format_as(value); // Make an lvalue reference for format.
+ return formatter<detail::format_as_result<T>, Char>::format(val, ctx);
+ }
+};
/**
* Converts `p` to `const void*` for pointer formatting.
* auto s = fmt::format("{}", fmt::ptr(p));
*/
template <typename T> auto ptr(T p) -> const void* {
- static_assert(std::is_pointer<T>::value, "");
+ static_assert(std::is_pointer<T>::value, "fmt::ptr used with non-pointer");
return detail::bit_cast<const void*>(p);
}
* **Example**:
*
* enum class color { red, green, blue };
- * auto s = fmt::format("{}", fmt::underlying(color::red));
+ * auto s = fmt::format("{}", fmt::underlying(color::red)); // s == "0"
*/
template <typename Enum>
constexpr auto underlying(Enum e) noexcept -> underlying_t<Enum> {
}
} // namespace enums
-class bytes {
- private:
- string_view data_;
- friend struct formatter<bytes>;
+#ifdef __cpp_lib_byte
+template <typename Char>
+struct formatter<std::byte, Char> : formatter<unsigned, Char> {
+ static auto format_as(std::byte b) -> unsigned char {
+ return static_cast<unsigned char>(b);
+ }
+ template <typename Context>
+ auto format(std::byte b, Context& ctx) const -> decltype(ctx.out()) {
+ return formatter<unsigned, Char>::format(format_as(b), ctx);
+ }
+};
+#endif
- public:
- explicit bytes(string_view data) : data_(data) {}
+struct bytes {
+ string_view data;
+
+ inline explicit bytes(string_view s) : data(s) {}
};
template <> struct formatter<bytes> {
detail::dynamic_format_specs<> specs_;
public:
- template <typename ParseContext>
- FMT_CONSTEXPR auto parse(ParseContext& ctx) -> const char* {
+ FMT_CONSTEXPR auto parse(parse_context<>& ctx) -> const char* {
return parse_format_specs(ctx.begin(), ctx.end(), specs_, ctx,
detail::type::string_type);
}
template <typename FormatContext>
auto format(bytes b, FormatContext& ctx) const -> decltype(ctx.out()) {
auto specs = specs_;
- detail::handle_dynamic_spec<detail::width_checker>(specs.width,
- specs.width_ref, ctx);
- detail::handle_dynamic_spec<detail::precision_checker>(
- specs.precision, specs.precision_ref, ctx);
- return detail::write_bytes<char>(ctx.out(), b.data_, specs);
+ detail::handle_dynamic_spec(specs.dynamic_width(), specs.width,
+ specs.width_ref, ctx);
+ detail::handle_dynamic_spec(specs.dynamic_precision(), specs.precision,
+ specs.precision_ref, ctx);
+ return detail::write_bytes<char>(ctx.out(), b.data, specs);
}
};
detail::dynamic_format_specs<> specs_;
public:
- template <typename ParseContext>
- FMT_CONSTEXPR auto parse(ParseContext& ctx) -> const char* {
+ FMT_CONSTEXPR auto parse(parse_context<>& ctx) -> const char* {
return parse_format_specs(ctx.begin(), ctx.end(), specs_, ctx,
detail::type::int_type);
}
template <typename FormatContext>
- auto format(group_digits_view<T> t, FormatContext& ctx) const
+ auto format(group_digits_view<T> view, FormatContext& ctx) const
-> decltype(ctx.out()) {
auto specs = specs_;
- detail::handle_dynamic_spec<detail::width_checker>(specs.width,
- specs.width_ref, ctx);
- detail::handle_dynamic_spec<detail::precision_checker>(
- specs.precision, specs.precision_ref, ctx);
- auto arg = detail::make_write_int_arg(t.value, specs.sign);
+ detail::handle_dynamic_spec(specs.dynamic_width(), specs.width,
+ specs.width_ref, ctx);
+ detail::handle_dynamic_spec(specs.dynamic_precision(), specs.precision,
+ specs.precision_ref, ctx);
+ auto arg = detail::make_write_int_arg(view.value, specs.sign());
return detail::write_int(
ctx.out(), static_cast<detail::uint64_or_128_t<T>>(arg.abs_value),
arg.prefix, specs, detail::digit_grouping<char>("\3", ","));
template <typename T, typename Char>
struct formatter<nested_view<T, Char>, Char> {
- template <typename ParseContext>
- FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
+ FMT_CONSTEXPR auto parse(parse_context<Char>& ctx) -> const Char* {
return ctx.begin();
}
template <typename FormatContext>
template <typename T, typename Char = char> struct nested_formatter {
private:
+ basic_specs specs_;
int width_;
- detail::fill_t fill_;
- align_t align_ : 4;
formatter<T, Char> formatter_;
public:
- constexpr nested_formatter() : width_(0), align_(align_t::none) {}
-
- FMT_CONSTEXPR auto parse(basic_format_parse_context<Char>& ctx)
- -> decltype(ctx.begin()) {
- auto specs = detail::dynamic_format_specs<Char>();
- auto it = parse_format_specs(ctx.begin(), ctx.end(), specs, ctx,
- detail::type::none_type);
- width_ = specs.width;
- fill_ = specs.fill;
- align_ = specs.align;
+ constexpr nested_formatter() : width_(0) {}
+
+ FMT_CONSTEXPR auto parse(parse_context<Char>& ctx) -> const Char* {
+ auto it = ctx.begin(), end = ctx.end();
+ if (it == end) return it;
+ auto specs = format_specs();
+ it = detail::parse_align(it, end, specs);
+ specs_ = specs;
+ Char c = *it;
+ auto width_ref = detail::arg_ref<Char>();
+ if ((c >= '0' && c <= '9') || c == '{') {
+ it = detail::parse_width(it, end, specs, width_ref, ctx);
+ width_ = specs.width;
+ }
ctx.advance_to(it);
return formatter_.parse(ctx);
}
write(basic_appender<Char>(buf));
auto specs = format_specs();
specs.width = width_;
- specs.fill = fill_;
- specs.align = align_;
+ specs.copy_fill_from(specs_);
+ specs.set_align(specs_.align());
return detail::write<Char>(
ctx.out(), basic_string_view<Char>(buf.data(), buf.size()), specs);
}
}
};
+inline namespace literals {
+#if FMT_USE_NONTYPE_TEMPLATE_ARGS
+template <detail::fixed_string S> constexpr auto operator""_a() {
+ using char_t = remove_cvref_t<decltype(*S.data)>;
+ return detail::udl_arg<char_t, sizeof(S.data) / sizeof(char_t), S>();
+}
+#else
/**
- * Converts `value` to `std::string` using the default format for type `T`.
+ * User-defined literal equivalent of `fmt::arg`.
*
* **Example**:
*
- * std::string answer = fmt::to_string(42);
+ * using namespace fmt::literals;
+ * fmt::print("The answer is {answer}.", "answer"_a=42);
*/
-template <typename T, FMT_ENABLE_IF(!std::is_integral<T>::value &&
- !detail::has_format_as<T>::value)>
-inline auto to_string(const T& value) -> std::string {
- auto buffer = memory_buffer();
- detail::write<char>(appender(buffer), value);
- return {buffer.data(), buffer.size()};
-}
-
-template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
-FMT_NODISCARD inline auto to_string(T value) -> std::string {
- // The buffer should be large enough to store the number including the sign
- // or "false" for bool.
- constexpr int max_size = detail::digits10<T>() + 2;
- char buffer[max_size > 5 ? static_cast<unsigned>(max_size) : 5];
- char* begin = buffer;
- return std::string(begin, detail::write<char>(begin, value));
+constexpr auto operator""_a(const char* s, size_t) -> detail::udl_arg<char> {
+ return {s};
}
+#endif // FMT_USE_NONTYPE_TEMPLATE_ARGS
+} // namespace literals
-template <typename Char, size_t SIZE>
-FMT_NODISCARD auto to_string(const basic_memory_buffer<Char, SIZE>& buf)
- -> std::basic_string<Char> {
- auto size = buf.size();
- detail::assume(size < std::basic_string<Char>().max_size());
- return std::basic_string<Char>(buf.data(), size);
-}
+/// A fast integer formatter.
+class format_int {
+ private:
+ // Buffer should be large enough to hold all digits (digits10 + 1),
+ // a sign and a null character.
+ enum { buffer_size = std::numeric_limits<unsigned long long>::digits10 + 3 };
+ mutable char buffer_[buffer_size];
+ char* str_;
-template <typename T, FMT_ENABLE_IF(!std::is_integral<T>::value &&
- detail::has_format_as<T>::value)>
-inline auto to_string(const T& value) -> std::string {
- return to_string(format_as(value));
-}
+ template <typename UInt>
+ FMT_CONSTEXPR20 auto format_unsigned(UInt value) -> char* {
+ auto n = static_cast<detail::uint32_or_64_or_128_t<UInt>>(value);
+ return detail::do_format_decimal(buffer_, n, buffer_size - 1);
+ }
-FMT_END_EXPORT
+ template <typename Int>
+ FMT_CONSTEXPR20 auto format_signed(Int value) -> char* {
+ auto abs_value = static_cast<detail::uint32_or_64_or_128_t<Int>>(value);
+ bool negative = value < 0;
+ if (negative) abs_value = 0 - abs_value;
+ auto begin = format_unsigned(abs_value);
+ if (negative) *--begin = '-';
+ return begin;
+ }
-namespace detail {
+ public:
+ FMT_CONSTEXPR20 explicit format_int(int value) : str_(format_signed(value)) {}
+ FMT_CONSTEXPR20 explicit format_int(long value)
+ : str_(format_signed(value)) {}
+ FMT_CONSTEXPR20 explicit format_int(long long value)
+ : str_(format_signed(value)) {}
+ FMT_CONSTEXPR20 explicit format_int(unsigned value)
+ : str_(format_unsigned(value)) {}
+ FMT_CONSTEXPR20 explicit format_int(unsigned long value)
+ : str_(format_unsigned(value)) {}
+ FMT_CONSTEXPR20 explicit format_int(unsigned long long value)
+ : str_(format_unsigned(value)) {}
-template <typename Char>
-void vformat_to(buffer<Char>& buf, basic_string_view<Char> fmt,
- typename vformat_args<Char>::type args, locale_ref loc) {
- auto out = basic_appender<Char>(buf);
- if (fmt.size() == 2 && equal2(fmt.data(), "{}")) {
- auto arg = args.get(0);
- if (!arg) report_error("argument not found");
- arg.visit(default_arg_formatter<Char>{out, args, loc});
- return;
+ /// Returns the number of characters written to the output buffer.
+ FMT_CONSTEXPR20 auto size() const -> size_t {
+ return detail::to_unsigned(buffer_ - str_ + buffer_size - 1);
}
- struct format_handler {
- basic_format_parse_context<Char> parse_context;
- buffered_context<Char> context;
+ /// Returns a pointer to the output buffer content. No terminating null
+ /// character is appended.
+ FMT_CONSTEXPR20 auto data() const -> const char* { return str_; }
- format_handler(basic_appender<Char> p_out, basic_string_view<Char> str,
- basic_format_args<buffered_context<Char>> p_args,
- locale_ref p_loc)
- : parse_context(str), context(p_out, p_args, p_loc) {}
+ /// Returns a pointer to the output buffer content with terminating null
+ /// character appended.
+ FMT_CONSTEXPR20 auto c_str() const -> const char* {
+ buffer_[buffer_size - 1] = '\0';
+ return str_;
+ }
- void on_text(const Char* begin, const Char* end) {
- auto text = basic_string_view<Char>(begin, to_unsigned(end - begin));
- context.advance_to(write<Char>(context.out(), text));
- }
+ /// Returns the content of the output buffer as an `std::string`.
+ inline auto str() const -> std::string { return {str_, size()}; }
+};
- FMT_CONSTEXPR auto on_arg_id() -> int {
- return parse_context.next_arg_id();
- }
- FMT_CONSTEXPR auto on_arg_id(int id) -> int {
- parse_context.check_arg_id(id);
- return id;
- }
- FMT_CONSTEXPR auto on_arg_id(basic_string_view<Char> id) -> int {
- parse_context.check_arg_id(id);
- int arg_id = context.arg_id(id);
- if (arg_id < 0) report_error("argument not found");
- return arg_id;
- }
+#if FMT_CLANG_ANALYZER
+# define FMT_STRING_IMPL(s, base) s
+#else
+# define FMT_STRING_IMPL(s, base) \
+ [] { \
+ /* Use the hidden visibility as a workaround for a GCC bug (#1973). */ \
+ /* Use a macro-like name to avoid shadowing warnings. */ \
+ struct FMT_VISIBILITY("hidden") FMT_COMPILE_STRING : base { \
+ using char_type = fmt::remove_cvref_t<decltype(s[0])>; \
+ constexpr explicit operator fmt::basic_string_view<char_type>() \
+ const { \
+ return fmt::detail::compile_string_to_view<char_type>(s); \
+ } \
+ }; \
+ using FMT_STRING_VIEW = \
+ fmt::basic_string_view<typename FMT_COMPILE_STRING::char_type>; \
+ fmt::detail::ignore_unused(FMT_STRING_VIEW(FMT_COMPILE_STRING())); \
+ return FMT_COMPILE_STRING(); \
+ }()
+#endif // FMT_CLANG_ANALYZER
- FMT_INLINE void on_replacement_field(int id, const Char*) {
- auto arg = get_arg(context, id);
- context.advance_to(arg.visit(default_arg_formatter<Char>{
- context.out(), context.args(), context.locale()}));
- }
+/**
+ * Constructs a legacy compile-time format string from a string literal `s`.
+ *
+ * **Example**:
+ *
+ * // A compile-time error because 'd' is an invalid specifier for strings.
+ * std::string s = fmt::format(FMT_STRING("{:d}"), "foo");
+ */
+#define FMT_STRING(s) FMT_STRING_IMPL(s, fmt::detail::compile_string)
- auto on_format_specs(int id, const Char* begin, const Char* end)
- -> const Char* {
- auto arg = get_arg(context, id);
- // Not using a visitor for custom types gives better codegen.
- if (arg.format_custom(begin, parse_context, context))
- return parse_context.begin();
- auto specs = detail::dynamic_format_specs<Char>();
- begin = parse_format_specs(begin, end, specs, parse_context, arg.type());
- detail::handle_dynamic_spec<detail::width_checker>(
- specs.width, specs.width_ref, context);
- detail::handle_dynamic_spec<detail::precision_checker>(
- specs.precision, specs.precision_ref, context);
- if (begin == end || *begin != '}')
- report_error("missing '}' in format string");
- context.advance_to(arg.visit(
- arg_formatter<Char>{context.out(), specs, context.locale()}));
- return begin;
- }
+FMT_API auto vsystem_error(int error_code, string_view fmt, format_args args)
+ -> std::system_error;
- FMT_NORETURN void on_error(const char* message) { report_error(message); }
- };
- detail::parse_format_string<false>(fmt, format_handler(out, fmt, args, loc));
+/**
+ * Constructs `std::system_error` with a message formatted with
+ * `fmt::format(fmt, args...)`.
+ * `error_code` is a system error code as given by `errno`.
+ *
+ * **Example**:
+ *
+ * // This throws std::system_error with the description
+ * // cannot open file 'madeup': No such file or directory
+ * // or similar (system message may vary).
+ * const char* filename = "madeup";
+ * FILE* file = fopen(filename, "r");
+ * if (!file)
+ * throw fmt::system_error(errno, "cannot open file '{}'", filename);
+ */
+template <typename... T>
+auto system_error(int error_code, format_string<T...> fmt, T&&... args)
+ -> std::system_error {
+ return vsystem_error(error_code, fmt.str, vargs<T...>{{args...}});
}
-FMT_BEGIN_EXPORT
-
-#ifndef FMT_HEADER_ONLY
-extern template FMT_API void vformat_to(buffer<char>&, string_view,
- typename vformat_args<>::type,
- locale_ref);
-extern template FMT_API auto thousands_sep_impl<char>(locale_ref)
- -> thousands_sep_result<char>;
-extern template FMT_API auto thousands_sep_impl<wchar_t>(locale_ref)
- -> thousands_sep_result<wchar_t>;
-extern template FMT_API auto decimal_point_impl(locale_ref) -> char;
-extern template FMT_API auto decimal_point_impl(locale_ref) -> wchar_t;
-#endif // FMT_HEADER_ONLY
+/**
+ * Formats an error message for an error returned by an operating system or a
+ * language runtime, for example a file opening error, and writes it to `out`.
+ * The format is the same as the one used by `std::system_error(ec, message)`
+ * where `ec` is `std::error_code(error_code, std::generic_category())`.
+ * It is implementation-defined but normally looks like:
+ *
+ * <message>: <system-message>
+ *
+ * where `<message>` is the passed message and `<system-message>` is the system
+ * message corresponding to the error code.
+ * `error_code` is a system error code as given by `errno`.
+ */
+FMT_API void format_system_error(detail::buffer<char>& out, int error_code,
+ const char* message) noexcept;
-FMT_END_EXPORT
+// Reports a system error without throwing an exception.
+// Can be used to report errors from destructors.
+FMT_API void report_system_error(int error_code, const char* message) noexcept;
-template <typename T, typename Char, type TYPE>
-template <typename FormatContext>
-FMT_CONSTEXPR FMT_INLINE auto native_formatter<T, Char, TYPE>::format(
- const T& val, FormatContext& ctx) const -> decltype(ctx.out()) {
- if (specs_.width_ref.kind == arg_id_kind::none &&
- specs_.precision_ref.kind == arg_id_kind::none) {
- return write<Char>(ctx.out(), val, specs_, ctx.locale());
- }
- auto specs = specs_;
- handle_dynamic_spec<width_checker>(specs.width, specs.width_ref, ctx);
- handle_dynamic_spec<precision_checker>(specs.precision, specs.precision_ref,
- ctx);
- return write<Char>(ctx.out(), val, specs, ctx.locale());
+inline auto vformat(locale_ref loc, string_view fmt, format_args args)
+ -> std::string {
+ auto buf = memory_buffer();
+ detail::vformat_to(buf, fmt, args, loc);
+ return {buf.data(), buf.size()};
}
-} // namespace detail
-
-FMT_BEGIN_EXPORT
+template <typename... T>
+FMT_INLINE auto format(locale_ref loc, format_string<T...> fmt, T&&... args)
+ -> std::string {
+ return vformat(loc, fmt.str, vargs<T...>{{args...}});
+}
-template <typename Char>
-struct formatter<detail::float128, Char>
- : detail::native_formatter<detail::float128, Char,
- detail::type::float_type> {};
+template <typename OutputIt,
+ FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, char>::value)>
+auto vformat_to(OutputIt out, locale_ref loc, string_view fmt, format_args args)
+ -> OutputIt {
+ auto&& buf = detail::get_buffer<char>(out);
+ detail::vformat_to(buf, fmt, args, loc);
+ return detail::get_iterator(buf, out);
+}
-#if FMT_USE_USER_DEFINED_LITERALS
-inline namespace literals {
-/**
- * User-defined literal equivalent of `fmt::arg`.
- *
- * **Example**:
- *
- * using namespace fmt::literals;
- * fmt::print("The answer is {answer}.", "answer"_a=42);
- */
-# if FMT_USE_NONTYPE_TEMPLATE_ARGS
-template <detail_exported::fixed_string Str> constexpr auto operator""_a() {
- using char_t = remove_cvref_t<decltype(Str.data[0])>;
- return detail::udl_arg<char_t, sizeof(Str.data) / sizeof(char_t), Str>();
+template <typename OutputIt, typename... T,
+ FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, char>::value)>
+FMT_INLINE auto format_to(OutputIt out, locale_ref loc, format_string<T...> fmt,
+ T&&... args) -> OutputIt {
+ return fmt::vformat_to(out, loc, fmt.str, vargs<T...>{{args...}});
}
-# else
-constexpr auto operator""_a(const char* s, size_t) -> detail::udl_arg<char> {
- return {s};
+
+template <typename... T>
+FMT_NODISCARD FMT_INLINE auto formatted_size(locale_ref loc,
+ format_string<T...> fmt,
+ T&&... args) -> size_t {
+ auto buf = detail::counting_buffer<>();
+ detail::vformat_to(buf, fmt.str, vargs<T...>{{args...}}, loc);
+ return buf.count();
}
-# endif
-} // namespace literals
-#endif // FMT_USE_USER_DEFINED_LITERALS
FMT_API auto vformat(string_view fmt, format_args args) -> std::string;
template <typename... T>
FMT_NODISCARD FMT_INLINE auto format(format_string<T...> fmt, T&&... args)
-> std::string {
- return vformat(fmt, fmt::make_format_args(args...));
+ return vformat(fmt.str, vargs<T...>{{args...}});
}
-template <typename Locale, FMT_ENABLE_IF(detail::is_locale<Locale>::value)>
-inline auto vformat(const Locale& loc, string_view fmt, format_args args)
- -> std::string {
- return detail::vformat(loc, fmt, args);
+/**
+ * Converts `value` to `std::string` using the default format for type `T`.
+ *
+ * **Example**:
+ *
+ * std::string answer = fmt::to_string(42);
+ */
+template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
+FMT_NODISCARD FMT_CONSTEXPR_STRING auto to_string(T value) -> std::string {
+ // The buffer should be large enough to store the number including the sign
+ // or "false" for bool.
+ char buffer[max_of(detail::digits10<T>() + 2, 5)];
+ return {buffer, detail::write<char>(buffer, value)};
}
-template <typename Locale, typename... T,
- FMT_ENABLE_IF(detail::is_locale<Locale>::value)>
-inline auto format(const Locale& loc, format_string<T...> fmt, T&&... args)
+template <typename T, FMT_ENABLE_IF(detail::use_format_as<T>::value)>
+FMT_NODISCARD FMT_CONSTEXPR_STRING auto to_string(const T& value)
-> std::string {
- return fmt::vformat(loc, string_view(fmt), fmt::make_format_args(args...));
-}
-
-template <typename OutputIt, typename Locale,
- FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, char>::value&&
- detail::is_locale<Locale>::value)>
-auto vformat_to(OutputIt out, const Locale& loc, string_view fmt,
- format_args args) -> OutputIt {
- using detail::get_buffer;
- auto&& buf = get_buffer<char>(out);
- detail::vformat_to(buf, fmt, args, detail::locale_ref(loc));
- return detail::get_iterator(buf, out);
-}
-
-template <typename OutputIt, typename Locale, typename... T,
- FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, char>::value&&
- detail::is_locale<Locale>::value)>
-FMT_INLINE auto format_to(OutputIt out, const Locale& loc,
- format_string<T...> fmt, T&&... args) -> OutputIt {
- return vformat_to(out, loc, fmt, fmt::make_format_args(args...));
+ return to_string(format_as(value));
}
-template <typename Locale, typename... T,
- FMT_ENABLE_IF(detail::is_locale<Locale>::value)>
-FMT_NODISCARD FMT_INLINE auto formatted_size(const Locale& loc,
- format_string<T...> fmt,
- T&&... args) -> size_t {
- auto buf = detail::counting_buffer<>();
- detail::vformat_to<char>(buf, fmt, fmt::make_format_args(args...),
- detail::locale_ref(loc));
- return buf.count();
+template <typename T, FMT_ENABLE_IF(!std::is_integral<T>::value &&
+ !detail::use_format_as<T>::value)>
+FMT_NODISCARD FMT_CONSTEXPR_STRING auto to_string(const T& value)
+ -> std::string {
+ auto buffer = memory_buffer();
+ detail::write<char>(appender(buffer), value);
+ return {buffer.data(), buffer.size()};
}
FMT_END_EXPORT
-
FMT_END_NAMESPACE
#ifdef FMT_HEADER_ONLY
# define FMT_FUNC inline
# include "format-inl.h"
-#else
-# define FMT_FUNC
#endif
// Restore _LIBCPP_REMOVE_TRANSITIVE_INCLUDES.
+++ /dev/null
-#include "xchar.h"
-#warning fmt/locale.h is deprecated, include fmt/format.h or fmt/xchar.h instead
# 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
const char* message) noexcept;
}
-FMT_API std::system_error vwindows_error(int error_code, string_view format_str,
+FMT_API std::system_error vwindows_error(int error_code, string_view fmt,
format_args args);
/**
* **Example**:
*
* // This throws a system_error with the description
- * // cannot open file 'madeup': The system cannot find the file
- * specified.
- * // or similar (system message may vary).
- * const char *filename = "madeup";
+ * // cannot open file 'foo': The system cannot find the file specified.
+ * // or similar (system message may vary) if the file doesn't exist.
+ * const char *filename = "foo";
* LPOFSTRUCT of = LPOFSTRUCT();
* HFILE file = OpenFile(filename, &of, OF_READ);
* if (file == HFILE_ERROR) {
* "cannot open file '{}'", filename);
* }
*/
-template <typename... Args>
-std::system_error windows_error(int error_code, string_view message,
- const Args&... args) {
- return vwindows_error(error_code, message, fmt::make_format_args(args...));
+template <typename... T>
+auto windows_error(int error_code, string_view message, const T&... args)
+ -> std::system_error {
+ return vwindows_error(error_code, message, vargs<T...>{{args...}});
}
// Reports a Windows error without throwing an exception.
// std::system is not available on some platforms such as iOS (#2248).
#ifdef __OSX__
template <typename S, typename... Args, typename Char = char_t<S>>
-void say(const S& format_str, Args&&... args) {
- std::system(format("say \"{}\"", format(format_str, args...)).c_str());
+void say(const S& fmt, Args&&... args) {
+ std::system(format("say \"{}\"", format(fmt, args...)).c_str());
}
#endif
friend class file;
- explicit buffered_file(FILE* f) : file_(f) {}
+ inline explicit buffered_file(FILE* f) : file_(f) {}
public:
buffered_file(const buffered_file&) = delete;
void operator=(const buffered_file&) = delete;
// Constructs a buffered_file object which doesn't represent any file.
- buffered_file() noexcept : file_(nullptr) {}
+ inline buffered_file() noexcept : file_(nullptr) {}
// Destroys the object closing the file it represents if any.
FMT_API ~buffered_file() noexcept;
public:
- buffered_file(buffered_file&& other) noexcept : file_(other.file_) {
+ inline buffered_file(buffered_file&& other) noexcept : file_(other.file_) {
other.file_ = nullptr;
}
- auto operator=(buffered_file&& other) -> buffered_file& {
+ inline auto operator=(buffered_file&& other) -> buffered_file& {
close();
file_ = other.file_;
other.file_ = nullptr;
FMT_API void close();
// Returns the pointer to a FILE object representing this file.
- auto get() const noexcept -> FILE* { return file_; }
+ inline auto get() const noexcept -> FILE* { return file_; }
FMT_API auto descriptor() const -> int;
template <typename... T>
inline void print(string_view fmt, const T&... args) {
- const auto& vargs = fmt::make_format_args(args...);
+ fmt::vargs<T...> vargs = {{args...}};
detail::is_locking<T...>() ? fmt::vprint_buffered(file_, fmt, vargs)
: fmt::vprint(file_, fmt, vargs);
}
};
// Constructs a file object which doesn't represent any file.
- file() noexcept : fd_(-1) {}
+ inline file() noexcept : fd_(-1) {}
// Opens a file and constructs a file object representing this file.
file(cstring_view path, int oflag);
file(const file&) = delete;
void operator=(const file&) = delete;
- file(file&& other) noexcept : fd_(other.fd_) { other.fd_ = -1; }
+ inline file(file&& other) noexcept : fd_(other.fd_) { other.fd_ = -1; }
// Move assignment is not noexcept because close may throw.
- auto operator=(file&& other) -> file& {
+ inline auto operator=(file&& other) -> file& {
close();
fd_ = other.fd_;
other.fd_ = -1;
~file() noexcept;
// Returns the file descriptor.
- auto descriptor() const noexcept -> int { return fd_; }
+ inline auto descriptor() const noexcept -> int { return fd_; }
// Closes the file.
void close();
namespace detail {
struct buffer_size {
- buffer_size() = default;
+ constexpr buffer_size() = default;
size_t value = 0;
- auto operator=(size_t val) const -> buffer_size {
+ FMT_CONSTEXPR auto operator=(size_t val) const -> buffer_size {
auto bs = buffer_size();
bs.value = val;
return bs;
int oflag = file::WRONLY | file::CREATE | file::TRUNC;
size_t buffer_size = BUFSIZ > 32768 ? BUFSIZ : 32768;
- ostream_params() {}
+ constexpr ostream_params() {}
template <typename... T>
ostream_params(T... params, int new_oflag) : ostream_params(params...) {
# endif
};
-class file_buffer final : public buffer<char> {
+} // namespace detail
+
+FMT_INLINE_VARIABLE constexpr auto buffer_size = detail::buffer_size();
+
+/// A fast buffered output stream for writing from a single thread. Writing from
+/// multiple threads without external synchronization may result in a data race.
+class ostream : private detail::buffer<char> {
private:
file file_;
+ FMT_API ostream(cstring_view path, const detail::ostream_params& params);
+
FMT_API static void grow(buffer<char>& buf, size_t);
public:
- FMT_API file_buffer(cstring_view path, const ostream_params& params);
- FMT_API file_buffer(file_buffer&& other) noexcept;
- FMT_API ~file_buffer();
+ FMT_API ostream(ostream&& other) noexcept;
+ FMT_API ~ostream();
+
+ operator writer() {
+ detail::buffer<char>& buf = *this;
+ return buf;
+ }
- void flush() {
+ inline void flush() {
if (size() == 0) return;
file_.write(data(), size() * sizeof(data()[0]));
clear();
}
- void close() {
- flush();
- file_.close();
- }
-};
-
-} // namespace detail
-
-constexpr auto buffer_size = detail::buffer_size();
-
-/// A fast output stream for writing from a single thread. Writing from
-/// multiple threads without external synchronization may result in a data race.
-class FMT_API ostream {
- private:
- FMT_MSC_WARNING(suppress : 4251)
- detail::file_buffer buffer_;
-
- ostream(cstring_view path, const detail::ostream_params& params)
- : buffer_(path, params) {}
-
- public:
- ostream(ostream&& other) : buffer_(std::move(other.buffer_)) {}
-
- ~ostream();
-
- void flush() { buffer_.flush(); }
-
template <typename... T>
friend auto output_file(cstring_view path, T... params) -> ostream;
- void close() { buffer_.close(); }
+ inline void close() {
+ flush();
+ file_.close();
+ }
/// Formats `args` according to specifications in `fmt` and writes the
/// output to the file.
template <typename... T> void print(format_string<T...> fmt, T&&... args) {
- vformat_to(appender(buffer_), fmt, fmt::make_format_args(args...));
+ vformat_to(appender(*this), fmt.str, vargs<T...>{{args...}});
}
};
#include "chrono.h" // formatbuf
+#ifdef _MSVC_STL_UPDATE
+# define FMT_MSVC_STL_UPDATE _MSVC_STL_UPDATE
+#elif defined(_MSC_VER) && _MSC_VER < 1912 // VS 15.5
+# define FMT_MSVC_STL_UPDATE _MSVC_LANG
+#else
+# define FMT_MSVC_STL_UPDATE 0
+#endif
+
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
friend auto get_file(BufType& obj) -> FILE* { return obj.*FileMemberPtr; }
};
-#if FMT_MSC_VERSION
+#if FMT_MSVC_STL_UPDATE
template class file_access<file_access_tag, std::filebuf,
&std::filebuf::_Myfile>;
auto get_file(std::filebuf&) -> FILE*;
#endif
-inline auto write_ostream_unicode(std::ostream& os, fmt::string_view data)
- -> bool {
- FILE* f = nullptr;
-#if FMT_MSC_VERSION && FMT_USE_RTTI
- if (auto* buf = dynamic_cast<std::filebuf*>(os.rdbuf()))
- f = get_file(*buf);
- else
- return false;
-#elif defined(_WIN32) && defined(__GLIBCXX__) && FMT_USE_RTTI
- auto* rdbuf = os.rdbuf();
- if (auto* sfbuf = dynamic_cast<__gnu_cxx::stdio_sync_filebuf<char>*>(rdbuf))
- f = sfbuf->file();
- else if (auto* fbuf = dynamic_cast<__gnu_cxx::stdio_filebuf<char>*>(rdbuf))
- f = fbuf->file();
- else
- return false;
-#else
- ignore_unused(os, data, f);
-#endif
-#ifdef _WIN32
- if (f) {
- int fd = _fileno(f);
- if (_isatty(fd)) {
- os.flush();
- return write_console(fd, data);
- }
- }
-#endif
- return false;
-}
-inline auto write_ostream_unicode(std::wostream&,
- fmt::basic_string_view<wchar_t>) -> bool {
- return false;
-}
-
// Write the content of buf to os.
// It is a separate function rather than a part of vprint to simplify testing.
template <typename Char>
void write_buffer(std::basic_ostream<Char>& os, buffer<Char>& buf) {
const Char* buf_data = buf.data();
- using unsigned_streamsize = std::make_unsigned<std::streamsize>::type;
+ using unsigned_streamsize = make_unsigned_t<std::streamsize>;
unsigned_streamsize size = buf.size();
unsigned_streamsize max_size = to_unsigned(max_value<std::streamsize>());
do {
} while (size != 0);
}
-template <typename Char, typename T>
-void format_value(buffer<Char>& buf, const T& value) {
- auto&& format_buf = formatbuf<std::basic_streambuf<Char>>(buf);
- auto&& output = std::basic_ostream<Char>(&format_buf);
-#if !defined(FMT_STATIC_THOUSANDS_SEPARATOR)
- output.imbue(std::locale::classic()); // The default is always unlocalized.
-#endif
- output << value;
- output.exceptions(std::ios_base::failbit | std::ios_base::badbit);
-}
-
template <typename T> struct streamed_view {
const T& value;
};
-
} // namespace detail
// Formats an object of type T that has an overloaded ostream operator<<.
template <typename T, typename Context>
auto format(const T& value, Context& ctx) const -> decltype(ctx.out()) {
auto buffer = basic_memory_buffer<Char>();
- detail::format_value(buffer, value);
+ auto&& formatbuf = detail::formatbuf<std::basic_streambuf<Char>>(buffer);
+ auto&& output = std::basic_ostream<Char>(&formatbuf);
+ output.imbue(std::locale::classic()); // The default is always unlocalized.
+ output << value;
+ output.exceptions(std::ios_base::failbit | std::ios_base::badbit);
return formatter<basic_string_view<Char>, Char>::format(
{buffer.data(), buffer.size()}, ctx);
}
return {value};
}
-namespace detail {
-
-inline void vprint_directly(std::ostream& os, string_view format_str,
- format_args args) {
+inline void vprint(std::ostream& os, string_view fmt, format_args args) {
auto buffer = memory_buffer();
- detail::vformat_to(buffer, format_str, args);
- detail::write_buffer(os, buffer);
-}
-
-} // namespace detail
-
-FMT_EXPORT template <typename Char>
-void vprint(std::basic_ostream<Char>& os,
- basic_string_view<type_identity_t<Char>> format_str,
- typename detail::vformat_args<Char>::type args) {
- auto buffer = basic_memory_buffer<Char>();
- detail::vformat_to(buffer, format_str, args);
- if (detail::write_ostream_unicode(os, {buffer.data(), buffer.size()})) return;
+ detail::vformat_to(buffer, fmt, args);
+ FILE* f = nullptr;
+#if FMT_MSVC_STL_UPDATE && FMT_USE_RTTI
+ if (auto* buf = dynamic_cast<std::filebuf*>(os.rdbuf()))
+ f = detail::get_file(*buf);
+#elif defined(_WIN32) && defined(__GLIBCXX__) && FMT_USE_RTTI
+ auto* rdbuf = os.rdbuf();
+ if (auto* sfbuf = dynamic_cast<__gnu_cxx::stdio_sync_filebuf<char>*>(rdbuf))
+ f = sfbuf->file();
+ else if (auto* fbuf = dynamic_cast<__gnu_cxx::stdio_filebuf<char>*>(rdbuf))
+ f = fbuf->file();
+#endif
+#ifdef _WIN32
+ if (f) {
+ int fd = _fileno(f);
+ if (_isatty(fd)) {
+ os.flush();
+ if (detail::write_console(fd, {buffer.data(), buffer.size()})) return;
+ }
+ }
+#endif
+ detail::ignore_unused(f);
detail::write_buffer(os, buffer);
}
*/
FMT_EXPORT template <typename... T>
void print(std::ostream& os, format_string<T...> fmt, T&&... args) {
- const auto& vargs = fmt::make_format_args(args...);
- if (detail::use_utf8())
- vprint(os, fmt, vargs);
- else
- detail::vprint_directly(os, fmt, vargs);
-}
-
-FMT_EXPORT
-template <typename... Args>
-void print(std::wostream& os,
- basic_format_string<wchar_t, type_identity_t<Args>...> fmt,
- Args&&... args) {
- vprint(os, fmt, fmt::make_format_args<buffered_context<wchar_t>>(args...));
+ fmt::vargs<T...> vargs = {{args...}};
+ if (detail::const_check(detail::use_utf8)) return vprint(os, fmt.str, vargs);
+ auto buffer = memory_buffer();
+ detail::vformat_to(buffer, fmt.str, vargs);
+ detail::write_buffer(os, buffer);
}
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_EXPORT
-template <typename... Args>
-void println(std::wostream& os,
- basic_format_string<wchar_t, type_identity_t<Args>...> fmt,
- Args&&... args) {
- print(os, L"{}\n", fmt::format(fmt, std::forward<Args>(args)...));
+ fmt::print(os, FMT_STRING("{}\n"),
+ fmt::format(fmt, std::forward<T>(args)...));
}
FMT_END_NAMESPACE
+++ /dev/null
-#include "os.h"
-#warning "fmt/posix.h is deprecated; use fmt/os.h instead"
#define FMT_PRINTF_H_
#ifndef FMT_MODULE
-# include <algorithm> // std::max
+# include <algorithm> // std::find
# include <limits> // std::numeric_limits
#endif
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_;
public:
using char_type = Char;
- using parse_context_type = basic_format_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
/// stored in the context object so make sure they have appropriate lifetimes.
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);
namespace detail {
+// Return the result via the out param to workaround gcc bug 77539.
+template <bool IS_CONSTEXPR, typename T, typename Ptr = const T*>
+FMT_CONSTEXPR auto find(Ptr first, Ptr last, T value, Ptr& out) -> bool {
+ for (out = first; out != last; ++out) {
+ if (*out == value) return true;
+ }
+ return false;
+}
+
+template <>
+inline auto find<false, char>(const char* first, const char* last, char value,
+ const char*& out) -> bool {
+ out =
+ static_cast<const char*>(memchr(first, value, to_unsigned(last - first)));
+ return out != nullptr;
+}
+
// 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>());
}
- static auto fits_in_int(bool) -> bool { return true; }
+ inline static auto fits_in_int(bool) -> bool { return true; }
};
template <> struct int_checker<true> {
return value >= (std::numeric_limits<int>::min)() &&
value <= max_value<int>();
}
- static auto fits_in_int(int) -> bool { return true; }
+ inline static auto fits_in_int(int) -> bool { return true; }
};
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)>
using target_type = conditional_t<std::is_same<T, void>::value, U, T>;
if (const_check(sizeof(target_type) <= sizeof(int))) {
// Extra casts are used to silence warnings.
- if (is_signed) {
- auto n = static_cast<int>(static_cast<target_type>(value));
- arg_ = detail::make_arg<Context>(n);
- } else {
- using unsigned_type = typename make_unsigned_or_bool<target_type>::type;
- auto n = static_cast<unsigned>(static_cast<unsigned_type>(value));
- arg_ = detail::make_arg<Context>(n);
- }
+ using unsigned_type = typename make_unsigned_or_bool<target_type>::type;
+ if (is_signed)
+ arg_ = static_cast<int>(static_cast<target_type>(value));
+ else
+ arg_ = static_cast<unsigned>(static_cast<unsigned_type>(value));
} else {
- if (is_signed) {
- // glibc's printf doesn't sign extend arguments of smaller types:
- // std::printf("%lld", -42); // prints "4294967254"
- // but we don't have to do the same because it's a UB.
- auto n = static_cast<long long>(value);
- arg_ = detail::make_arg<Context>(n);
- } else {
- auto n = static_cast<typename make_unsigned_or_bool<U>::type>(value);
- arg_ = detail::make_arg<Context>(n);
- }
+ // glibc's printf doesn't sign extend arguments of smaller types:
+ // std::printf("%lld", -42); // prints "4294967254"
+ // but we don't have to do the same because it's a UB.
+ if (is_signed)
+ arg_ = static_cast<long long>(value);
+ else
+ arg_ = static_cast<typename make_unsigned_or_bool<U>::type>(value);
}
}
template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
void operator()(T value) {
- auto c = static_cast<typename Context::char_type>(value);
- arg_ = detail::make_arg<Context>(c);
+ arg_ = static_cast<typename Context::char_type>(value);
}
template <typename T, FMT_ENABLE_IF(!std::is_integral<T>::value)>
format_specs& specs_;
public:
- explicit printf_width_handler(format_specs& specs) : specs_(specs) {}
+ inline explicit printf_width_handler(format_specs& specs) : specs_(specs) {}
template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
auto operator()(T value) -> unsigned {
auto width = static_cast<uint32_or_64_or_128_t<T>>(value);
if (detail::is_negative(value)) {
- specs_.align = align::left;
+ specs_.set_align(align::left);
width = 0 - width;
}
unsigned int_max = to_unsigned(max_value<int>());
void write_null_pointer(bool is_string = false) {
auto s = this->specs;
- s.type = presentation_type::none;
+ s.set_type(presentation_type::none);
write_bytes<Char>(this->out, is_string ? "(null)" : "(nil)", s);
}
+ template <typename T> void write(T value) {
+ detail::write<Char>(this->out, value, this->specs, this->locale);
+ }
+
public:
printf_arg_formatter(basic_appender<Char> iter, format_specs& s,
context_type& ctx)
: base(make_arg_formatter(iter, s)), context_(ctx) {}
- void operator()(monostate value) { base::operator()(value); }
+ void operator()(monostate value) { write(value); }
template <typename T, FMT_ENABLE_IF(detail::is_integral<T>::value)>
void operator()(T value) {
// MSVC2013 fails to compile separate overloads for bool and Char so use
// std::is_same instead.
if (!std::is_same<T, Char>::value) {
- base::operator()(value);
+ write(value);
return;
}
format_specs s = this->specs;
- if (s.type != presentation_type::none && s.type != presentation_type::chr) {
+ if (s.type() != presentation_type::none &&
+ s.type() != presentation_type::chr) {
return (*this)(static_cast<int>(value));
}
- s.sign = sign::none;
- s.alt = false;
- s.fill = ' '; // Ignore '0' flag for char types.
+ s.set_sign(sign::none);
+ s.clear_alt();
+ s.set_fill(' '); // Ignore '0' flag for char types.
// align::numeric needs to be overwritten here since the '0' flag is
// ignored for non-numeric types
- if (s.align == align::none || s.align == align::numeric)
- s.align = align::right;
- write<Char>(this->out, static_cast<Char>(value), s);
+ if (s.align() == align::none || s.align() == align::numeric)
+ s.set_align(align::right);
+ detail::write<Char>(this->out, static_cast<Char>(value), s);
}
template <typename T, FMT_ENABLE_IF(std::is_floating_point<T>::value)>
void operator()(T value) {
- base::operator()(value);
+ write(value);
}
void operator()(const char* value) {
if (value)
- base::operator()(value);
+ write(value);
else
- write_null_pointer(this->specs.type != presentation_type::pointer);
+ write_null_pointer(this->specs.type() != presentation_type::pointer);
}
void operator()(const wchar_t* value) {
if (value)
- base::operator()(value);
+ write(value);
else
- write_null_pointer(this->specs.type != presentation_type::pointer);
+ write_null_pointer(this->specs.type() != presentation_type::pointer);
}
- void operator()(basic_string_view<Char> value) { base::operator()(value); }
+ void operator()(basic_string_view<Char> value) { write(value); }
void operator()(const void* value) {
if (value)
- base::operator()(value);
+ write(value);
else
write_null_pointer();
}
void operator()(typename basic_format_arg<context_type>::handle handle) {
- auto parse_ctx = basic_format_parse_context<Char>({});
+ auto parse_ctx = parse_context<Char>({});
handle.format(parse_ctx, context_);
}
};
void parse_flags(format_specs& specs, const Char*& it, const Char* end) {
for (; it != end; ++it) {
switch (*it) {
- case '-':
- specs.align = align::left;
- break;
- case '+':
- specs.sign = sign::plus;
- break;
- case '0':
- specs.fill = '0';
- break;
+ case '-': specs.set_align(align::left); break;
+ case '+': specs.set_sign(sign::plus); break;
+ case '0': specs.set_fill('0'); break;
case ' ':
- if (specs.sign != sign::plus) specs.sign = sign::space;
+ if (specs.sign() != sign::plus) specs.set_sign(sign::space);
break;
- case '#':
- specs.alt = true;
- break;
- default:
- return;
+ case '#': specs.set_alt(); break;
+ default: return;
}
}
}
++it;
arg_index = value != -1 ? value : max_value<int>();
} else {
- if (c == '0') specs.fill = '0';
+ if (c == '0') specs.set_fill('0');
if (value != 0) {
// Nonzero value means that we parsed width and don't need to
// parse it or flags again, so return now.
using pt = presentation_type;
constexpr auto integral_set = sint_set | uint_set | bool_set | char_set;
switch (c) {
- case 'd':
- return in(t, integral_set) ? pt::dec : pt::none;
- case 'o':
- return in(t, integral_set) ? pt::oct : pt::none;
- case 'X':
- upper = true;
- FMT_FALLTHROUGH;
- case 'x':
- return in(t, integral_set) ? pt::hex : pt::none;
- case 'E':
- upper = true;
- FMT_FALLTHROUGH;
- case 'e':
- return in(t, float_set) ? pt::exp : pt::none;
- case 'F':
- upper = true;
- FMT_FALLTHROUGH;
- case 'f':
- return in(t, float_set) ? pt::fixed : pt::none;
- case 'G':
- upper = true;
- FMT_FALLTHROUGH;
- case 'g':
- return in(t, float_set) ? pt::general : pt::none;
- case 'A':
- upper = true;
- FMT_FALLTHROUGH;
- case 'a':
- return in(t, float_set) ? pt::hexfloat : pt::none;
- case 'c':
- return in(t, integral_set) ? pt::chr : pt::none;
- case 's':
- return in(t, string_set | cstring_set) ? pt::string : pt::none;
- case 'p':
- return in(t, pointer_set | cstring_set) ? pt::pointer : pt::none;
- default:
- return pt::none;
+ case 'd': return in(t, integral_set) ? pt::dec : pt::none;
+ case 'o': return in(t, integral_set) ? pt::oct : pt::none;
+ case 'X': upper = true; FMT_FALLTHROUGH;
+ case 'x': return in(t, integral_set) ? pt::hex : pt::none;
+ case 'E': upper = true; FMT_FALLTHROUGH;
+ case 'e': return in(t, float_set) ? pt::exp : pt::none;
+ case 'F': upper = true; FMT_FALLTHROUGH;
+ case 'f': return in(t, float_set) ? pt::fixed : pt::none;
+ case 'G': upper = true; FMT_FALLTHROUGH;
+ case 'g': return in(t, float_set) ? pt::general : pt::none;
+ case 'A': upper = true; FMT_FALLTHROUGH;
+ case 'a': return in(t, float_set) ? pt::hexfloat : pt::none;
+ case 'c': return in(t, integral_set) ? pt::chr : pt::none;
+ case 's': return in(t, string_set | cstring_set) ? pt::string : pt::none;
+ case 'p': return in(t, pointer_set | cstring_set) ? pt::pointer : pt::none;
+ default: return pt::none;
}
}
using iterator = basic_appender<Char>;
auto out = iterator(buf);
auto context = basic_printf_context<Char>(out, args);
- auto parse_ctx = basic_format_parse_context<Char>(format);
+ auto parse_ctx = parse_context<Char>(format);
// Returns the argument with specified index or, if arg_index is -1, the next
// argument.
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();
write(out, basic_string_view<Char>(start, to_unsigned(it - 1 - start)));
auto specs = format_specs();
- specs.align = align::right;
+ specs.set_align(align::right);
// Parse argument index, flags and width.
int arg_index = parse_header(it, end, specs, get_arg);
auto arg = get_arg(arg_index);
// For d, i, o, u, x, and X conversion specifiers, if a precision is
// specified, the '0' flag is ignored
- if (specs.precision >= 0 && arg.is_integral()) {
+ if (specs.precision >= 0 && is_integral_type(arg.type())) {
// Ignore '0' for non-numeric types or if '-' present.
- specs.fill = ' ';
+ specs.set_fill(' ');
}
if (specs.precision >= 0 && arg.type() == type::cstring_type) {
auto str = arg.visit(get_cstring<Char>());
auto nul = std::find(str, str_end, Char());
auto sv = basic_string_view<Char>(
str, to_unsigned(nul != str_end ? nul - str : specs.precision));
- arg = make_arg<basic_printf_context<Char>>(sv);
+ arg = sv;
}
- if (specs.alt && arg.visit(is_zero_int())) specs.alt = false;
- if (specs.fill.template get<Char>() == '0') {
- if (arg.is_arithmetic() && specs.align != align::left)
- specs.align = align::numeric;
- else
- specs.fill = ' '; // Ignore '0' flag for non-numeric types or if '-'
- // flag is also present.
+ if (specs.alt() && arg.visit(is_zero_int())) specs.clear_alt();
+ if (specs.fill_unit<Char>() == '0') {
+ if (is_arithmetic_type(arg.type()) && specs.align() != align::left) {
+ specs.set_align(align::numeric);
+ } else {
+ // Ignore '0' flag for non-numeric types or if '-' flag is also present.
+ specs.set_fill(' ');
+ }
}
// Parse length and convert the argument to the required type.
convert_arg<long>(arg, t);
}
break;
- case 'j':
- convert_arg<intmax_t>(arg, t);
- break;
- case 'z':
- convert_arg<size_t>(arg, t);
- break;
- case 't':
- convert_arg<std::ptrdiff_t>(arg, t);
- break;
+ case 'j': convert_arg<intmax_t>(arg, t); break;
+ case 'z': convert_arg<size_t>(arg, t); break;
+ case 't': convert_arg<std::ptrdiff_t>(arg, t); break;
case 'L':
// printf produces garbage when 'L' is omitted for long double, no
// need to do the same.
break;
- default:
- --it;
- convert_arg<void>(arg, c);
+ default: --it; convert_arg<void>(arg, c);
}
// Parse type.
if (it == end) report_error("invalid format string");
char type = static_cast<char>(*it++);
- if (arg.is_integral()) {
+ if (is_integral_type(arg.type())) {
// Normalize type.
switch (type) {
case 'i':
- case 'u':
- type = 'd';
- break;
+ case 'u': type = 'd'; break;
case 'c':
arg.visit(char_converter<basic_printf_context<Char>>(arg));
break;
}
}
bool upper = false;
- specs.type = parse_printf_presentation_type(type, arg.type(), upper);
- if (specs.type == presentation_type::none)
+ specs.set_type(parse_printf_presentation_type(type, arg.type(), upper));
+ if (specs.type() == presentation_type::none)
report_error("invalid format specifier");
- specs.upper = upper;
+ if (upper) specs.set_upper();
start = it;
/// arguments and can be implicitly converted to `printf_args`.
template <typename Char = char, typename... T>
inline auto make_printf_args(T&... args)
- -> decltype(make_format_args<basic_printf_context<Char>>(args...)) {
- return make_format_args<basic_printf_context<Char>>(args...);
+ -> decltype(fmt::make_format_args<basic_printf_context<Char>>(args...)) {
+ return fmt::make_format_args<basic_printf_context<Char>>(args...);
}
template <typename Char> struct vprintf_args {
-> std::basic_string<Char> {
auto buf = basic_memory_buffer<Char>();
detail::vprintf(buf, fmt, args);
- return to_string(buf);
+ return {buf.data(), buf.size()};
}
/**
*
* std::string message = fmt::sprintf("The answer is %d", 42);
*/
-template <typename S, typename... T, typename Char = 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();
*
* fmt::fprintf(stderr, "Don't %s!", "panic");
*/
-template <typename S, typename... T, typename Char = 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...));
}
/**
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
#ifndef FMT_MODULE
# include <initializer_list>
# include <iterator>
-# include <string>
# include <tuple>
# include <type_traits>
# include <utility>
#include "format.h"
+#if FMT_HAS_CPP_ATTRIBUTE(clang::lifetimebound)
+# define FMT_LIFETIMEBOUND [[clang::lifetimebound]]
+#else
+# define FMT_LIFETIMEBOUND
+#endif
+FMT_PRAGMA_CLANG(diagnostic error "-Wreturn-stack-address")
+
FMT_BEGIN_NAMESPACE
FMT_EXPORT
template <typename> static void check(...);
public:
- static constexpr const bool value =
+ static constexpr bool value =
!std::is_void<decltype(check<T>(nullptr))>::value;
};
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;
};
-template <typename... Ts> struct conditional_helper {};
-
-template <typename T, typename _ = void> struct is_range_ : std::false_type {};
-
-#if !FMT_MSC_VERSION || FMT_MSC_VERSION > 1800
-
-# define FMT_DECLTYPE_RETURN(val) \
- ->decltype(val) { return val; } \
- static_assert( \
- true, "") // This makes it so that a semicolon is required after the
- // macro, which helps clang-format handle the formatting.
-
// 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;
}
// Member function overloads.
template <typename T>
-auto range_begin(T&& rng) FMT_DECLTYPE_RETURN(static_cast<T&&>(rng).begin());
+auto range_begin(T&& rng) -> decltype(static_cast<T&&>(rng).begin()) {
+ return static_cast<T&&>(rng).begin();
+}
template <typename T>
-auto range_end(T&& rng) FMT_DECLTYPE_RETURN(static_cast<T&&>(rng).end());
+auto range_end(T&& rng) -> decltype(static_cast<T&&>(rng).end()) {
+ return static_cast<T&&>(rng).end();
+}
// ADL overloads. Only participate in overload resolution if member functions
// are not found.
// SFINAE properly unless there are distinct types
int>> : std::true_type {};
+template <typename T, typename _ = void> struct is_range_ : std::false_type {};
template <typename T>
struct is_range_<T, void>
: std::integral_constant<bool, (has_const_begin_end<T>::value ||
has_mutable_begin_end<T>::value)> {};
-# undef FMT_DECLTYPE_RETURN
-#endif
// tuple_size and tuple_element check.
template <typename T> class is_tuple_like_ {
- template <typename U>
- static auto check(U* p) -> decltype(std::tuple_size<U>::value, int());
+ template <typename U, typename V = typename std::remove_cv<U>::type>
+ static auto check(U* p) -> decltype(std::tuple_size<V>::value, 0);
template <typename> static void check(...);
public:
- static constexpr const bool value =
+ static constexpr bool value =
!std::is_void<decltype(check<T>(nullptr))>::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>
C>::value)...>{}));
public:
- static constexpr const bool value =
+ static constexpr bool value =
decltype(check(tuple_index_sequence<T>{}))::value;
};
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
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&;
};
template <typename Range>
using uncvref_type = remove_cvref_t<range_reference_type<Range>>;
-template <typename Formatter>
-FMT_CONSTEXPR auto maybe_set_debug_format(Formatter& f, bool set)
- -> decltype(f.set_debug_format(set)) {
- f.set_debug_format(set);
-}
-template <typename Formatter>
-FMT_CONSTEXPR void maybe_set_debug_format(Formatter&, ...) {}
-
template <typename T>
struct range_format_kind_
: std::integral_constant<range_format,
using range_format_constant = std::integral_constant<range_format, K>;
// These are not generic lambdas for compatibility with C++11.
-template <typename ParseContext> struct parse_empty_specs {
+template <typename Char> struct parse_empty_specs {
template <typename Formatter> FMT_CONSTEXPR void operator()(Formatter& f) {
f.parse(ctx);
detail::maybe_set_debug_format(f, true);
}
- ParseContext& ctx;
+ parse_context<Char>& ctx;
};
template <typename FormatContext> struct format_tuple_element {
using char_type = typename FormatContext::char_type;
} // 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>
closing_bracket_ = close;
}
- template <typename ParseContext>
- FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
+ FMT_CONSTEXPR auto parse(parse_context<Char>& ctx) -> const Char* {
auto it = ctx.begin();
- if (it != ctx.end() && *it != '}') report_error("invalid format specifier");
- detail::for_each(formatters_, detail::parse_empty_specs<ParseContext>{ctx});
+ auto end = ctx.end();
+ if (it != end && detail::to_ascii(*it) == 'n') {
+ ++it;
+ set_brackets({}, {});
+ set_separator({});
+ }
+ if (it != end && *it != '}') report_error("invalid format specifier");
+ ctx.advance_to(it);
+ detail::for_each(formatters_, detail::parse_empty_specs<Char>{ctx});
return it;
}
}
};
+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;
};
namespace detail {
-template <typename Context> struct range_mapper {
- using mapper = arg_mapper<Context>;
-
- template <typename T,
- FMT_ENABLE_IF(has_formatter<remove_cvref_t<T>, Context>::value)>
- static auto map(T&& value) -> T&& {
- return static_cast<T&&>(value);
- }
- template <typename T,
- FMT_ENABLE_IF(!has_formatter<remove_cvref_t<T>, Context>::value)>
- static auto map(T&& value)
- -> decltype(mapper().map(static_cast<T&&>(value))) {
- return mapper().map(static_cast<T&&>(value));
- }
-};
template <typename Char, typename Element>
-using range_formatter_type =
- formatter<remove_cvref_t<decltype(range_mapper<buffered_context<Char>>{}
- .map(std::declval<Element>()))>,
- Char>;
+using range_formatter_type = formatter<remove_cvref_t<Element>, Char>;
template <typename R>
using maybe_const_range =
conditional_t<has_const_begin_end<R>::value, const R, R>;
-// Workaround a bug in MSVC 2015 and earlier.
-#if !FMT_MSC_VERSION || FMT_MSC_VERSION >= 1910
template <typename R, typename Char>
struct is_formattable_delayed
: is_formattable<uncvref_type<maybe_const_range<R>>, Char> {};
-#endif
} // namespace detail
template <typename...> struct conjunction : std::true_type {};
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;
auto buf = basic_memory_buffer<Char>();
for (; it != end; ++it) buf.push_back(*it);
auto specs = format_specs();
- specs.type = presentation_type::debug;
+ specs.set_type(presentation_type::debug);
return detail::write<Char>(
out, basic_string_view<Char>(buf.data(), buf.size()), specs);
}
closing_bracket_ = close;
}
- template <typename ParseContext>
- FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
+ FMT_CONSTEXPR auto parse(parse_context<Char>& ctx) -> const Char* {
auto it = ctx.begin();
auto end = ctx.end();
detail::maybe_set_debug_format(underlying_, true);
template <typename R, typename FormatContext>
auto format(R&& range, FormatContext& ctx) const -> decltype(ctx.out()) {
- auto mapper = detail::range_mapper<buffered_context<Char>>();
auto out = ctx.out();
auto it = detail::range_begin(range);
auto end = detail::range_end(range);
- if (is_debug) return write_debug_string(out, it, end);
+ if (is_debug) return write_debug_string(out, std::move(it), end);
out = detail::copy<Char>(opening_bracket_, out);
int i = 0;
if (i > 0) out = detail::copy<Char>(separator_, out);
ctx.advance_to(out);
auto&& item = *it; // Need an lvalue
- out = underlying_.format(mapper.map(item), ctx);
+ out = underlying_.format(item, ctx);
++i;
}
out = detail::copy<Char>(closing_bracket_, out);
range_format_kind<R, Char>::value != range_format::disabled &&
range_format_kind<R, Char>::value != range_format::map &&
range_format_kind<R, Char>::value != range_format::string &&
- range_format_kind<R, Char>::value != range_format::debug_string>
-// Workaround a bug in MSVC 2015 and earlier.
-#if !FMT_MSC_VERSION || FMT_MSC_VERSION >= 1910
- ,
- detail::is_formattable_delayed<R, Char>
-#endif
- >::value>> {
+ range_format_kind<R, Char>::value != range_format::debug_string>,
+ detail::is_formattable_delayed<R, Char>>::value>> {
private:
using range_type = detail::maybe_const_range<R>;
range_formatter<detail::uncvref_type<range_type>, Char> range_formatter_;
detail::string_literal<Char, '}'>{});
}
- template <typename ParseContext>
- FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
+ FMT_CONSTEXPR auto parse(parse_context<Char>& ctx) -> const Char* {
return range_formatter_.parse(ctx);
}
template <typename R, typename Char>
struct formatter<
R, Char,
- enable_if_t<range_format_kind<R, Char>::value == range_format::map>> {
+ enable_if_t<conjunction<
+ bool_constant<range_format_kind<R, Char>::value == range_format::map>,
+ detail::is_formattable_delayed<R, Char>>::value>> {
private:
using map_type = detail::maybe_const_range<R>;
using element_type = detail::uncvref_type<map_type>;
public:
FMT_CONSTEXPR formatter() {}
- template <typename ParseContext>
- FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
+ FMT_CONSTEXPR auto parse(parse_context<Char>& ctx) -> const Char* {
auto it = ctx.begin();
auto end = ctx.end();
if (it != end) {
}
ctx.advance_to(it);
}
- detail::for_each(formatters_, detail::parse_empty_specs<ParseContext>{ctx});
+ detail::for_each(formatters_, detail::parse_empty_specs<Char>{ctx});
return it;
}
basic_string_view<Char> open = detail::string_literal<Char, '{'>{};
if (!no_delimiters_) out = detail::copy<Char>(open, out);
int i = 0;
- auto mapper = detail::range_mapper<buffered_context<Char>>();
basic_string_view<Char> sep = detail::string_literal<Char, ',', ' '>{};
for (auto&& value : map) {
if (i > 0) out = detail::copy<Char>(sep, out);
ctx.advance_to(out);
- detail::for_each2(formatters_, mapper.map(value),
+ detail::for_each2(formatters_, value,
detail::format_tuple_element<FormatContext>{
0, ctx, detail::string_literal<Char, ':', ' '>{}});
++i;
formatter<string_type, Char> underlying_;
public:
- template <typename ParseContext>
- FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
+ FMT_CONSTEXPR auto parse(parse_context<Char>& ctx) -> const Char* {
return underlying_.parse(ctx);
}
#endif
formatter<remove_cvref_t<value_type>, Char> value_formatter_;
- using view_ref = conditional_t<std::is_copy_constructible<It>::value,
- const join_view<It, Sentinel, Char>&,
- join_view<It, Sentinel, Char>&&>;
+ using view = conditional_t<std::is_copy_constructible<It>::value,
+ const join_view<It, Sentinel, Char>,
+ join_view<It, Sentinel, Char>>;
public:
using nonlocking = void;
- template <typename ParseContext>
- FMT_CONSTEXPR auto parse(ParseContext& ctx) -> const Char* {
+ FMT_CONSTEXPR auto parse(parse_context<Char>& ctx) -> const Char* {
return value_formatter_.parse(ctx);
}
template <typename FormatContext>
- auto format(view_ref& value, FormatContext& ctx) const
- -> decltype(ctx.out()) {
- auto it = std::forward<view_ref>(value).begin;
+ auto format(view& value, FormatContext& ctx) const -> decltype(ctx.out()) {
+ using iter =
+ conditional_t<std::is_copy_constructible<view>::value, It, It&>;
+ iter it = value.begin;
auto out = ctx.out();
if (it == value.end) return out;
out = value_formatter_.format(*it, ctx);
}
};
-/// Returns a view that formats the iterator range `[begin, end)` with elements
-/// separated by `sep`.
-template <typename It, typename Sentinel>
-auto join(It begin, Sentinel end, string_view sep) -> join_view<It, Sentinel> {
- return {std::move(begin), end, sep};
-}
-
-/**
- * Returns a view that formats `range` with elements separated by `sep`.
- *
- * **Example**:
- *
- * auto v = std::vector<int>{1, 2, 3};
- * fmt::print("{}", fmt::join(v, ", "));
- * // Output: 1, 2, 3
- *
- * `fmt::join` applies passed format specifiers to the range elements:
- *
- * fmt::print("{:02}", fmt::join(v, ", "));
- * // Output: 01, 02, 03
- */
-template <typename Range>
-auto join(Range&& r, string_view sep)
- -> join_view<decltype(detail::range_begin(r)),
- decltype(detail::range_end(r))> {
- return {detail::range_begin(r), detail::range_end(r), sep};
-}
-
-template <typename Char, typename... T> struct tuple_join_view : detail::view {
- const std::tuple<T...>& tuple;
+FMT_EXPORT
+template <typename Tuple, typename Char> struct tuple_join_view : detail::view {
+ const Tuple& tuple;
basic_string_view<Char> sep;
- tuple_join_view(const std::tuple<T...>& t, basic_string_view<Char> s)
+ tuple_join_view(const Tuple& t, basic_string_view<Char> s)
: tuple(t), sep{s} {}
};
# define FMT_TUPLE_JOIN_SPECIFIERS 0
#endif
-template <typename Char, typename... T>
-struct formatter<tuple_join_view<Char, T...>, Char> {
- template <typename ParseContext>
- FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
- return do_parse(ctx, std::integral_constant<size_t, sizeof...(T)>());
+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, T...>& value,
+ auto format(const tuple_join_view<Tuple, Char>& value,
FormatContext& ctx) const -> typename FormatContext::iterator {
- return do_format(value, ctx,
- std::integral_constant<size_t, sizeof...(T)>());
+ return do_format(value, ctx, std::tuple_size<Tuple>());
}
private:
- std::tuple<formatter<typename std::decay<T>::type, Char>...> formatters_;
+ decltype(detail::tuple::get_formatters<Tuple, Char>(
+ detail::tuple_index_sequence<Tuple>())) formatters_;
- template <typename ParseContext>
- FMT_CONSTEXPR auto do_parse(ParseContext& ctx,
+ FMT_CONSTEXPR auto do_parse(parse_context<Char>& ctx,
std::integral_constant<size_t, 0>)
- -> decltype(ctx.begin()) {
+ -> const Char* {
return ctx.begin();
}
- template <typename ParseContext, size_t N>
- FMT_CONSTEXPR auto do_parse(ParseContext& ctx,
+ template <size_t N>
+ FMT_CONSTEXPR auto do_parse(parse_context<Char>& ctx,
std::integral_constant<size_t, N>)
- -> decltype(ctx.begin()) {
+ -> const Char* {
auto end = ctx.begin();
#if FMT_TUPLE_JOIN_SPECIFIERS
- end = std::get<sizeof...(T) - N>(formatters_).parse(ctx);
+ end = std::get<std::tuple_size<Tuple>::value - N>(formatters_).parse(ctx);
if (N > 1) {
auto end1 = do_parse(ctx, std::integral_constant<size_t, N - 1>());
if (end != end1)
}
template <typename FormatContext>
- auto do_format(const tuple_join_view<Char, T...>&, 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, T...>& 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 {
- auto out = std::get<sizeof...(T) - N>(formatters_)
- .format(std::get<sizeof...(T) - N>(value.tuple), ctx);
+ using std::get;
+ auto out =
+ std::get<std::tuple_size<Tuple>::value - N>(formatters_)
+ .format(get<std::tuple_size<Tuple>::value - N>(value.tuple), ctx);
if (N <= 1) return out;
out = detail::copy<Char>(value.sep, out);
ctx.advance_to(out);
template <typename> static void check(...);
public:
- static constexpr const bool value =
+ static constexpr bool value =
!std::is_void<decltype(check<T>(nullptr))>::value;
};
: 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);
}
};
FMT_BEGIN_EXPORT
+/// Returns a view that formats the iterator range `[begin, end)` with elements
+/// separated by `sep`.
+template <typename It, typename Sentinel>
+auto join(It begin, Sentinel end, string_view sep) -> join_view<It, Sentinel> {
+ return {std::move(begin), end, sep};
+}
+
+/**
+ * Returns a view that formats `range` with elements separated by `sep`.
+ *
+ * **Example**:
+ *
+ * auto v = std::vector<int>{1, 2, 3};
+ * fmt::print("{}", fmt::join(v, ", "));
+ * // Output: 1, 2, 3
+ *
+ * `fmt::join` applies passed format specifiers to the range elements:
+ *
+ * fmt::print("{:02}", fmt::join(v, ", "));
+ * // Output: 01, 02, 03
+ */
+template <typename Range, FMT_ENABLE_IF(!is_tuple_like<Range>::value)>
+auto join(Range&& r, string_view sep)
+ -> join_view<decltype(detail::range_begin(r)),
+ decltype(detail::range_end(r))> {
+ return {detail::range_begin(r), detail::range_end(r), sep};
+}
+
/**
* Returns an object that formats `std::tuple` with elements separated by `sep`.
*
* **Example**:
*
- * auto t = std::tuple<int, char>{1, 'a'};
+ * auto t = std::tuple<int, char>(1, 'a');
* fmt::print("{}", fmt::join(t, ", "));
* // Output: 1, a
*/
-template <typename... T>
-FMT_CONSTEXPR auto join(const std::tuple<T...>& tuple, string_view sep)
- -> tuple_join_view<char, T...> {
+template <typename Tuple, FMT_ENABLE_IF(is_tuple_like<Tuple>::value)>
+FMT_CONSTEXPR auto join(const Tuple& tuple FMT_LIFETIMEBOUND, string_view sep)
+ -> tuple_join_view<Tuple, char> {
return {tuple, sep};
}
# include <atomic>
# include <bitset>
# include <complex>
-# include <cstdlib>
# include <exception>
+# 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
-# if FMT_HAS_INCLUDE(<filesystem>)
+# if FMT_HAS_INCLUDE(<filesystem>) && \
+ (!defined(FMT_CPP_LIB_FILESYSTEM) || FMT_CPP_LIB_FILESYSTEM != 0)
# include <filesystem>
# endif
# if FMT_HAS_INCLUDE(<variant>)
# 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
-#if FMT_CPP_LIB_FILESYSTEM
FMT_BEGIN_NAMESPACE
-
namespace detail {
+#if FMT_CPP_LIB_FILESYSTEM
+
template <typename Char, typename PathChar>
auto get_path_string(const std::filesystem::path& p,
const std::basic_string<PathChar>& native) {
}
}
+#endif // FMT_CPP_LIB_FILESYSTEM
+
+#if defined(__cpp_lib_expected) || FMT_CPP_LIB_VARIANT
+
+template <typename Char, typename OutputIt, typename T, typename FormatContext>
+auto write_escaped_alternative(OutputIt out, const T& v, FormatContext& ctx)
+ -> OutputIt {
+ 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);
+
+ formatter<std::remove_cv_t<T>, Char> underlying;
+ maybe_set_debug_format(underlying, true);
+ return underlying.format(v, ctx);
+}
+#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
+inline auto normalize_libcxx_inline_namespaces(string_view demangled_name_view,
+ char* begin) -> string_view {
+ // 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* to = begin + 5; // std::
+ for (const 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] == '_') {
+ const 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)};
+ }
+ return demangled_name_view;
+}
+
+template <class OutputIt>
+auto normalize_msvc_abi_name(string_view abi_name_view, OutputIt out)
+ -> OutputIt {
+ const string_view demangled_name(abi_name_view);
+ 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;
+}
+
+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), &free);
+
+ string_view demangled_name_view;
+ if (demangled_name_ptr) {
+ demangled_name_view = normalize_libcxx_inline_namespaces(
+ demangled_name_ptr.get(), demangled_name_ptr.get());
+ } else {
+ demangled_name_view = string_view(ti.name());
+ }
+ return detail::write_bytes<char>(out, demangled_name_view);
+# elif FMT_MSC_VERSION && defined(_MSVC_STL_UPDATE)
+ return normalize_msvc_abi_name(ti.name(), out);
+# elif FMT_MSC_VERSION && defined(_LIBCPP_VERSION)
+ const string_view demangled_name = ti.name();
+ std::string name_copy(demangled_name.size(), '\0');
+ // normalize_msvc_abi_name removes class, struct, union etc that MSVC has in
+ // front of types
+ name_copy.erase(normalize_msvc_abi_name(demangled_name, name_copy.begin()),
+ name_copy.end());
+ // normalize_libcxx_inline_namespaces removes the inline __1, __2, etc
+ // namespaces libc++ uses for ABI versioning On MSVC ABI + libc++
+ // environments, we need to eliminate both of them.
+ const string_view normalized_name =
+ normalize_libcxx_inline_namespaces(name_copy, name_copy.data());
+ return detail::write_bytes<char>(out, normalized_name);
+# 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
+
template <typename Char> struct formatter<std::filesystem::path, Char> {
private:
format_specs specs_;
public:
FMT_CONSTEXPR void set_debug_format(bool set = true) { debug_ = set; }
- template <typename ParseContext> FMT_CONSTEXPR auto parse(ParseContext& ctx) {
+ FMT_CONSTEXPR auto parse(parse_context<Char>& ctx) {
auto it = ctx.begin(), end = ctx.end();
if (it == end) return it;
it = detail::parse_align(it, end, specs_);
if (it == end) return it;
- it = detail::parse_dynamic_spec(it, end, specs_.width, width_ref_, ctx);
+ Char c = *it;
+ if ((c >= '0' && c <= '9') || c == '{')
+ it = detail::parse_width(it, end, specs_, width_ref_, ctx);
if (it != end && *it == '?') {
debug_ = true;
++it;
!path_type_ ? p.native()
: p.generic_string<std::filesystem::path::value_type>();
- detail::handle_dynamic_spec<detail::width_checker>(specs.width, width_ref_,
- ctx);
+ detail::handle_dynamic_spec(specs.dynamic_width(), specs.width, width_ref_,
+ ctx);
if (!debug_) {
auto s = detail::get_path_string<Char>(p, path_string);
return detail::write(ctx.out(), basic_string_view<Char>(s), specs);
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>
-struct formatter<std::bitset<N>, Char> : nested_formatter<string_view> {
+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;
}
};
template <typename FormatContext>
auto format(const std::bitset<N>& bs, FormatContext& ctx) const
-> decltype(ctx.out()) {
- return write_padded(ctx, writer{bs});
+ return this->write_padded(ctx, writer{bs});
}
};
-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>> {
private:
- formatter<T, Char> underlying_;
+ formatter<std::remove_cv_t<T>, Char> underlying_;
static constexpr basic_string_view<Char> optional =
detail::string_literal<Char, 'o', 'p', 't', 'i', 'o', 'n', 'a', 'l',
'('>{};
static constexpr basic_string_view<Char> none =
detail::string_literal<Char, 'n', 'o', 'n', 'e'>{};
- template <class U>
- FMT_CONSTEXPR static auto maybe_set_debug_format(U& u, bool set)
- -> decltype(u.set_debug_format(set)) {
- u.set_debug_format(set);
- }
-
- template <class U>
- FMT_CONSTEXPR static void maybe_set_debug_format(U&, ...) {}
-
public:
- template <typename ParseContext> FMT_CONSTEXPR auto parse(ParseContext& ctx) {
- maybe_set_debug_format(underlying_, true);
+ FMT_CONSTEXPR auto parse(parse_context<Char>& ctx) {
+ detail::maybe_set_debug_format(underlying_, true);
return underlying_.parse(ctx);
}
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<is_formattable<T, Char>::value &&
+ std::enable_if_t<(std::is_void<T>::value ||
+ is_formattable<T, Char>::value) &&
is_formattable<E, Char>::value>> {
- template <typename ParseContext>
- FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
+ FMT_CONSTEXPR auto parse(parse_context<Char>& ctx) -> const Char* {
return ctx.begin();
}
if (value.has_value()) {
out = detail::write<Char>(out, "expected(");
- out = detail::write_escaped_alternative<Char>(out, value.value());
+ if constexpr (!std::is_void<T>::value)
+ out = detail::write_escaped_alternative<Char>(out, *value, ctx);
} else {
out = detail::write<Char>(out, "unexpected(");
- out = detail::write_escaped_alternative<Char>(out, value.error());
+ out = detail::write_escaped_alternative<Char>(out, value.error(), ctx);
}
*out++ = ')';
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> {
- template <typename ParseContext> FMT_CONSTEXPR auto parse(ParseContext& ctx) {
- return ctx.begin();
- }
+ FMT_CONSTEXPR auto parse(parse_context<>& ctx) { return ctx.begin(); }
template <typename FormatContext>
auto format(const std::source_location& loc, FormatContext& ctx) const
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> {
- template <typename ParseContext>
- FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
+ FMT_CONSTEXPR auto parse(parse_context<Char>& ctx) -> const Char* {
return ctx.begin();
}
}
};
-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>>>> {
- template <typename ParseContext>
- FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
+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();
}
FMT_TRY {
std::visit(
[&](const auto& v) {
- out = detail::write_escaped_alternative<Char>(out, v);
+ out = detail::write_escaped_alternative<Char>(out, v, ctx);
},
value);
}
return out;
}
};
-FMT_END_NAMESPACE
+
#endif // FMT_CPP_LIB_VARIANT
-FMT_BEGIN_NAMESPACE
-FMT_EXPORT
-template <typename Char> struct formatter<std::error_code, Char> {
- template <typename ParseContext>
- FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
- return ctx.begin();
- }
+template <> struct formatter<std::error_code> {
+ private:
+ format_specs specs_;
+ detail::arg_ref<char> width_ref_;
+ bool debug_ = false;
- template <typename FormatContext>
- FMT_CONSTEXPR auto format(const std::error_code& ec, FormatContext& ctx) const
- -> decltype(ctx.out()) {
- auto out = ctx.out();
- out = detail::write_bytes<Char>(out, ec.category().name(), format_specs());
- out = detail::write<Char>(out, Char(':'));
- out = detail::write<Char>(out, ec.value());
- return out;
- }
-};
+ public:
+ FMT_CONSTEXPR void set_debug_format(bool set = true) { debug_ = set; }
-#if FMT_USE_RTTI
-namespace detail {
+ FMT_CONSTEXPR auto parse(parse_context<>& ctx) -> const char* {
+ auto it = ctx.begin(), end = ctx.end();
+ if (it == end) return it;
-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);
+ it = detail::parse_align(it, end, specs_);
- 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)};
+ char c = *it;
+ 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;
}
- } else {
- demangled_name_view = string_view(ti.name());
+ if (it != end && *it == 's') {
+ specs_.set_type(presentation_type::string);
+ ++it;
+ }
+ return it;
}
- 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;
+
+ template <typename FormatContext>
+ FMT_CONSTEXPR20 auto format(const std::error_code& ec,
+ FormatContext& ctx) const -> decltype(ctx.out()) {
+ auto specs = specs_;
+ detail::handle_dynamic_spec(specs.dynamic_width(), specs.width, width_ref_,
+ ctx);
+ 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());
}
- if (sub.starts_with("class ") || sub.starts_with("union ")) {
- i += 5;
- continue;
+ 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());
}
- if (sub.starts_with("struct ")) {
- i += 6;
- continue;
- }
- if (*sub.begin() != ' ') *out++ = *sub.begin();
+ return detail::write<char>(ctx.out(), str, specs);
}
- 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.
- > {
+#if FMT_USE_RTTI
+template <> struct formatter<std::type_info> {
public:
- FMT_CONSTEXPR auto parse(basic_format_parse_context<Char>& ctx)
- -> decltype(ctx.begin()) {
+ 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(basic_format_parse_context<Char>& ctx)
- -> decltype(ctx.begin()) {
+ FMT_CONSTEXPR auto parse(parse_context<>& ctx) -> const char* {
auto it = ctx.begin();
auto end = ctx.end();
if (it == end || *it == '}') return it;
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>>
}
};
-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>>
};
#ifdef __cpp_lib_atomic_flag_test
-FMT_EXPORT
template <typename Char>
struct formatter<std::atomic_flag, Char> : formatter<bool, Char> {
template <typename FormatContext>
};
#endif // __cpp_lib_atomic_flag_test
-FMT_EXPORT
-template <typename F, typename Char>
-struct formatter<std::complex<F>, Char> : nested_formatter<F, Char> {
+template <typename T, typename Char> struct formatter<std::complex<T>, Char> {
private:
- // Functor because C++11 doesn't support generic lambdas.
- struct writer {
- const formatter<std::complex<F>, Char>* f;
- const std::complex<F>& c;
-
- template <typename OutputIt>
- FMT_CONSTEXPR auto operator()(OutputIt out) -> OutputIt {
- if (c.real() != 0) {
- auto format_full = detail::string_literal<Char, '(', '{', '}', '+', '{',
- '}', 'i', ')'>{};
- return fmt::format_to(out, basic_string_view<Char>(format_full),
- f->nested(c.real()), f->nested(c.imag()));
- }
- auto format_imag = detail::string_literal<Char, '{', '}', 'i'>{};
- return fmt::format_to(out, basic_string_view<Char>(format_imag),
- f->nested(c.imag()));
+ detail::dynamic_format_specs<Char> specs_;
+
+ template <typename FormatContext, typename OutputIt>
+ FMT_CONSTEXPR auto do_format(const std::complex<T>& c,
+ detail::dynamic_format_specs<Char>& specs,
+ FormatContext& ctx, OutputIt out) const
+ -> OutputIt {
+ if (c.real() != 0) {
+ *out++ = Char('(');
+ out = detail::write<Char>(out, c.real(), specs, ctx.locale());
+ specs.set_sign(sign::plus);
+ out = detail::write<Char>(out, c.imag(), specs, ctx.locale());
+ if (!detail::isfinite(c.imag())) *out++ = Char(' ');
+ *out++ = Char('i');
+ *out++ = Char(')');
+ return out;
}
- };
+ out = detail::write<Char>(out, c.imag(), specs, ctx.locale());
+ if (!detail::isfinite(c.imag())) *out++ = Char(' ');
+ *out++ = Char('i');
+ return out;
+ }
public:
+ FMT_CONSTEXPR auto parse(parse_context<Char>& ctx) -> const Char* {
+ if (ctx.begin() == ctx.end() || *ctx.begin() == '}') return ctx.begin();
+ return parse_format_specs(ctx.begin(), ctx.end(), specs_, ctx,
+ detail::type_constant<T, Char>::value);
+ }
+
template <typename FormatContext>
- auto format(const std::complex<F>& c, FormatContext& ctx) const
+ auto format(const std::complex<T>& c, FormatContext& ctx) const
-> decltype(ctx.out()) {
- return this->write_padded(ctx, writer{this, c});
+ auto specs = specs_;
+ if (specs.dynamic()) {
+ detail::handle_dynamic_spec(specs.dynamic_width(), specs.width,
+ specs.width_ref, ctx);
+ detail::handle_dynamic_spec(specs.dynamic_precision(), specs.precision,
+ specs.precision_ref, ctx);
+ }
+
+ if (specs.width == 0) return do_format(c, specs, ctx, ctx.out());
+ auto buf = basic_memory_buffer<Char>();
+
+ auto outer_specs = format_specs();
+ outer_specs.width = specs.width;
+ outer_specs.copy_fill_from(specs);
+ outer_specs.set_align(specs.align());
+
+ specs.width = 0;
+ specs.set_fill({});
+ specs.set_align(align::none);
+
+ do_format(c, specs, ctx, basic_appender<Char>(buf));
+ return detail::write<Char>(ctx.out(),
+ basic_string_view<Char>(buf.data(), buf.size()),
+ outer_specs);
+ }
+};
+
+template <typename T, typename Char>
+struct formatter<std::reference_wrapper<T>, Char,
+ // 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
+ -> decltype(ctx.out()) {
+ return formatter<remove_cvref_t<T>, Char>::format(ref.get(), ctx);
}
};
FMT_END_NAMESPACE
+
#endif // FMT_STD_H_
#include "color.h"
#include "format.h"
+#include "ostream.h"
#include "ranges.h"
#ifndef FMT_MODULE
# include <cwchar>
-# if !defined(FMT_STATIC_THOUSANDS_SEPARATOR)
+# if FMT_USE_LOCALE
# include <locale>
# endif
#endif
};
template <typename S>
-struct format_string_char<S, enable_if_t<is_compile_string<S>::value>> {
+struct format_string_char<
+ S, enable_if_t<std::is_base_of<detail::compile_string, S>::value>> {
using type = typename S::char_type;
};
inline auto write_loc(basic_appender<wchar_t> out, loc_value value,
const format_specs& specs, locale_ref loc) -> bool {
-#ifndef FMT_STATIC_THOUSANDS_SEPARATOR
+#if FMT_USE_LOCALE
auto& numpunct =
std::use_facet<std::numpunct<wchar_t>>(loc.get<std::locale>());
auto separator = std::wstring();
#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
using wstring_view = basic_string_view<wchar_t>;
-using wformat_parse_context = basic_format_parse_context<wchar_t>;
+using wformat_parse_context = parse_context<wchar_t>;
using wformat_context = buffered_context<wchar_t>;
using wformat_args = basic_format_args<wformat_context>;
using wmemory_buffer = basic_memory_buffer<wchar_t>;
-#if FMT_GCC_VERSION && FMT_GCC_VERSION < 409
-// Workaround broken conversion on older gcc.
-template <typename... Args> using wformat_string = wstring_view;
-inline auto runtime(wstring_view s) -> wstring_view { return s; }
-#else
-template <typename... Args>
-using wformat_string = basic_format_string<wchar_t, type_identity_t<Args>...>;
+template <typename Char, typename... T> struct basic_fstring {
+ private:
+ basic_string_view<Char> str_;
+
+ static constexpr int num_static_named_args =
+ detail::count_static_named_args<T...>();
+
+ using checker = detail::format_string_checker<
+ Char, static_cast<int>(sizeof...(T)), num_static_named_args,
+ num_static_named_args != detail::count_named_args<T...>()>;
+
+ using arg_pack = detail::arg_pack<T...>;
+
+ public:
+ using t = basic_fstring;
+
+ template <typename S,
+ FMT_ENABLE_IF(
+ std::is_convertible<const S&, basic_string_view<Char>>::value)>
+ FMT_CONSTEVAL FMT_ALWAYS_INLINE basic_fstring(const S& s) : str_(s) {
+ if (FMT_USE_CONSTEVAL)
+ detail::parse_format_string<Char>(s, checker(s, arg_pack()));
+ }
+ template <typename S,
+ FMT_ENABLE_IF(std::is_base_of<detail::compile_string, S>::value&&
+ std::is_same<typename S::char_type, Char>::value)>
+ FMT_ALWAYS_INLINE basic_fstring(const S&) : str_(S()) {
+ FMT_CONSTEXPR auto sv = basic_string_view<Char>(S());
+ FMT_CONSTEXPR int ignore =
+ (parse_format_string(sv, checker(sv, arg_pack())), 0);
+ detail::ignore_unused(ignore);
+ }
+ basic_fstring(runtime_format_string<Char> fmt) : str_(fmt.str) {}
+
+ operator basic_string_view<Char>() const { return str_; }
+ auto get() const -> basic_string_view<Char> { return str_; }
+};
+
+template <typename Char, typename... T>
+using basic_format_string = basic_fstring<Char, T...>;
+
+template <typename... T>
+using wformat_string = typename basic_format_string<wchar_t, T...>::t;
inline auto runtime(wstring_view s) -> runtime_format_string<wchar_t> {
return {{s}};
}
-#endif
-
-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)
return fmt::make_format_args<wformat_context>(args...);
}
+#if !FMT_USE_NONTYPE_TEMPLATE_ARGS
inline namespace literals {
-#if FMT_USE_USER_DEFINED_LITERALS && !FMT_USE_NONTYPE_TEMPLATE_ARGS
-constexpr auto operator""_a(const wchar_t* s, size_t)
- -> detail::udl_arg<wchar_t> {
+inline auto operator""_a(const wchar_t* s, size_t) -> detail::udl_arg<wchar_t> {
return {s};
}
-#endif
} // namespace literals
+#endif
template <typename It, typename Sentinel>
auto join(It begin, Sentinel end, wstring_view sep)
return {begin, end, sep};
}
-template <typename Range>
+template <typename Range, FMT_ENABLE_IF(!is_tuple_like<Range>::value)>
auto join(Range&& range, wstring_view sep)
- -> join_view<detail::iterator_t<Range>, detail::sentinel_t<Range>,
+ -> join_view<decltype(std::begin(range)), decltype(std::end(range)),
wchar_t> {
return join(std::begin(range), std::end(range), sep);
}
return join(std::begin(list), std::end(list), sep);
}
-template <typename... T>
-auto join(const std::tuple<T...>& tuple, basic_string_view<wchar_t> sep)
- -> tuple_join_view<wchar_t, T...> {
+template <typename Tuple, FMT_ENABLE_IF(is_tuple_like<Tuple>::value)>
+auto join(const Tuple& tuple, basic_string_view<wchar_t> sep)
+ -> tuple_join_view<Tuple, wchar_t> {
return {tuple, sep};
}
template <typename Char, FMT_ENABLE_IF(!std::is_same<Char, char>::value)>
-auto vformat(basic_string_view<Char> format_str,
- typename detail::vformat_args<Char>::type args)
+auto vformat(basic_string_view<Char> fmt,
+ basic_format_args<buffered_context<Char>> args)
-> std::basic_string<Char> {
auto buf = basic_memory_buffer<Char>();
- detail::vformat_to(buf, format_str, args);
- return to_string(buf);
+ detail::vformat_to(buf, fmt, args);
+ return {buf.data(), buf.size()};
}
template <typename... T>
typename Char = detail::format_string_char_t<S>,
FMT_ENABLE_IF(!std::is_same<Char, char>::value &&
!std::is_same<Char, wchar_t>::value)>
-auto format(const S& format_str, T&&... args) -> std::basic_string<Char> {
- return vformat(detail::to_string_view(format_str),
+auto format(const S& fmt, T&&... args) -> std::basic_string<Char> {
+ return vformat(detail::to_string_view(fmt),
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& format_str,
- 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> {
- return detail::vformat(loc, detail::to_string_view(format_str), args);
+ auto buf = basic_memory_buffer<Char>();
+ 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& format_str, 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 detail::vformat(
- loc, detail::to_string_view(format_str),
- fmt::make_format_args<buffered_context<Char>>(args...));
+ return vformat(loc, detail::to_string_view(fmt),
+ fmt::make_format_args<buffered_context<Char>>(args...));
}
template <typename OutputIt, typename S,
typename Char = detail::format_string_char_t<S>,
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value&&
detail::is_exotic_char<Char>::value)>
-auto vformat_to(OutputIt out, const S& format_str,
- typename detail::vformat_args<Char>::type args) -> OutputIt {
+auto vformat_to(OutputIt out, const S& fmt,
+ basic_format_args<buffered_context<Char>> args) -> OutputIt {
auto&& buf = detail::get_buffer<Char>(out);
- detail::vformat_to(buf, detail::to_string_view(format_str), args);
+ detail::vformat_to(buf, detail::to_string_view(fmt), args);
return detail::get_iterator(buf, out);
}
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& format_str,
- 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(format_str), args,
- detail::locale_ref(loc));
+ vformat_to(buf, detail::to_string_view(fmt), args, loc);
return detail::get_iterator(buf, out);
}
-template <typename OutputIt, typename Locale, 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& format_str,
- T&&... args) ->
- typename std::enable_if<enable, OutputIt>::type {
- return vformat_to(out, loc, detail::to_string_view(format_str),
+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...));
}
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> format_str,
- typename detail::vformat_args<Char>::type args)
+inline auto vformat_to_n(OutputIt out, size_t n, basic_string_view<Char> fmt,
+ 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);
- detail::vformat_to(buf, format_str, args);
+ detail::vformat_to(buf, fmt, args);
return {buf.out(), buf.count()};
}
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);
- return fmt::to_string(buf);
+ return {buf.data(), buf.size()};
}
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...));
}
+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);
+ detail::write_buffer(os, buffer);
+}
+
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...));
+void print(std::wostream& os, wformat_string<T...> fmt, T&&... args) {
+ vprint(os, fmt, fmt::make_format_args<buffered_context<wchar_t>>(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...);
+void println(std::wostream& os, wformat_string<T...> fmt, T&&... args) {
+ print(os, L"{}\n", fmt::format(fmt, std::forward<T>(args)...));
}
/// Converts `value` to `std::wstring` using the default format for type `T`.