#endif
// The fmt library version in the form major * 10000 + minor * 100 + patch.
-#define FMT_VERSION 110104
+#define FMT_VERSION 120000
// Detect compiler versions.
#if defined(__clang__) && !defined(__ibmxl__)
# define FMT_NODISCARD
#endif
-#ifdef FMT_DEPRECATED
-// Use the provided definition.
-#elif FMT_HAS_CPP14_ATTRIBUTE(deprecated)
-# define FMT_DEPRECATED [[deprecated]]
-#else
-# define FMT_DEPRECATED /* deprecated */
-#endif
-
-#ifdef FMT_ALWAYS_INLINE
-// Use the provided definition.
-#elif FMT_GCC_VERSION || FMT_CLANG_VERSION
-# define FMT_ALWAYS_INLINE inline __attribute__((always_inline))
-#else
-# define FMT_ALWAYS_INLINE inline
-#endif
-// A version of FMT_ALWAYS_INLINE to prevent code bloat in debug mode.
-#ifdef NDEBUG
-# define FMT_INLINE FMT_ALWAYS_INLINE
-#else
-# define FMT_INLINE inline
-#endif
-
#if FMT_GCC_VERSION || FMT_CLANG_VERSION
# define FMT_VISIBILITY(value) __attribute__((visibility(value)))
#else
# define FMT_MSC_WARNING(...)
#endif
+// Enable minimal optimizations for more compact code in debug mode.
+FMT_PRAGMA_GCC(push_options)
+#if !defined(__OPTIMIZE__) && !defined(__CUDACC__) && !defined(FMT_MODULE)
+FMT_PRAGMA_GCC(optimize("Og"))
+# define FMT_GCC_OPTIMIZED
+#endif
+FMT_PRAGMA_CLANG(diagnostic push)
+
+#ifdef FMT_ALWAYS_INLINE
+// Use the provided definition.
+#elif FMT_GCC_VERSION || FMT_CLANG_VERSION
+# define FMT_ALWAYS_INLINE inline __attribute__((always_inline))
+#else
+# define FMT_ALWAYS_INLINE inline
+#endif
+// A version of FMT_ALWAYS_INLINE to prevent code bloat in debug mode.
+#if defined(NDEBUG) || defined(FMT_GCC_OPTIMIZED)
+# define FMT_INLINE FMT_ALWAYS_INLINE
+#else
+# define FMT_INLINE inline
+#endif
+
#ifndef FMT_BEGIN_NAMESPACE
# define FMT_BEGIN_NAMESPACE \
namespace fmt { \
- inline namespace v11 {
+ inline namespace v12 {
# define FMT_END_NAMESPACE \
} \
}
using unused = int[]; \
(void)unused { 0, (expr, 0)... }
-// Enable minimal optimizations for more compact code in debug mode.
-FMT_PRAGMA_GCC(push_options)
-#if !defined(__OPTIMIZE__) && !defined(__CUDACC__) && !defined(FMT_MODULE)
-FMT_PRAGMA_GCC(optimize("Og"))
-#endif
-FMT_PRAGMA_CLANG(diagnostic push)
-
FMT_BEGIN_NAMESPACE
// Implementations of enable_if_t and other metafunctions for older systems.
template <typename T> using decay_t = typename std::decay<T>::type;
using nullptr_t = decltype(nullptr);
-#if FMT_GCC_VERSION && FMT_GCC_VERSION < 500
-// A workaround for gcc 4.9 to make void_t work in a SFINAE context.
+#if (FMT_GCC_VERSION && FMT_GCC_VERSION < 500) || FMT_MSC_VERSION
+// A workaround for gcc 4.9 & MSVC v141 to make void_t work in a SFINAE context.
template <typename...> struct void_t_impl {
using type = void;
};
return a > b ? a : b;
}
+FMT_NORETURN FMT_API void assert_fail(const char* file, int line,
+ const char* message);
+
namespace detail {
// Suppresses "unused variable" warnings with the method described in
// https://herbsutter.com/2009/10/18/mailbag-shutting-up-compiler-warnings/.
# 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
static_assert(!FMT_UNICODE || use_utf8,
"Unicode support requires compiling with /utf-8");
-template <typename T> constexpr const char* narrow(const T*) { return nullptr; }
-constexpr FMT_ALWAYS_INLINE const char* narrow(const char* s) { return s; }
+template <typename T> constexpr auto narrow(T*) -> char* { return nullptr; }
+constexpr FMT_ALWAYS_INLINE auto narrow(const char* s) -> const char* {
+ return s;
+}
template <typename Char>
-FMT_CONSTEXPR auto compare(const Char* s1, const Char* s2, std::size_t n)
- -> int {
+FMT_CONSTEXPR auto compare(const Char* s1, const Char* s2, size_t n) -> int {
if (!is_constant_evaluated() && sizeof(Char) == 1) return memcmp(s1, s2, n);
for (; n != 0; ++s1, ++s2, --n) {
if (*s1 < *s2) return -1;
constexpr basic_string_view() noexcept : data_(nullptr), size_(0) {}
- /// Constructs a string reference object from a C string and a size.
+ /// Constructs a string view object from a C string and a size.
constexpr basic_string_view(const Char* s, size_t count) noexcept
: data_(s), size_(count) {}
constexpr basic_string_view(nullptr_t) = delete;
- /// Constructs a string reference object from a C string.
+ /// Constructs a string view object from a C string.
#if FMT_GCC_VERSION
FMT_ALWAYS_INLINE
#endif
FMT_CONSTEXPR20 basic_string_view(const Char* s) : data_(s) {
#if FMT_HAS_BUILTIN(__builtin_strlen) || FMT_GCC_VERSION || FMT_CLANG_VERSION
- if (std::is_same<Char, char>::value) {
- size_ = __builtin_strlen(detail::narrow(s));
+ if (std::is_same<Char, char>::value && !detail::is_constant_evaluated()) {
+ size_ = __builtin_strlen(detail::narrow(s)); // strlen is not constexpr.
return;
}
#endif
size_ = len;
}
- /// Constructs a string reference from a `std::basic_string` or a
+ /// Constructs a string view from a `std::basic_string` or a
/// `std::basic_string_view` object.
template <typename S,
FMT_ENABLE_IF(detail::is_std_string_like<S>::value&& std::is_same<
return starts_with(basic_string_view<Char>(s));
}
- // Lexicographically compare this string reference to other.
FMT_CONSTEXPR auto compare(basic_string_view other) const -> int {
int result =
detail::compare(data_, other.data_, min_of(size_, other.size_));
using string_view = basic_string_view<char>;
-/// Specifies if `T` is an extended character type. Can be specialized by users.
-template <typename T> struct is_xchar : std::false_type {};
-template <> struct is_xchar<wchar_t> : std::true_type {};
-template <> struct is_xchar<char16_t> : std::true_type {};
-template <> struct is_xchar<char32_t> : std::true_type {};
-#ifdef __cpp_char8_t
-template <> struct is_xchar<char8_t> : std::true_type {};
-#endif
-
-// DEPRECATED! Will be replaced with an alias to prevent specializations.
-template <typename T> struct is_char : is_xchar<T> {};
-template <> struct is_char<char> : std::true_type {};
-
template <typename T> class basic_appender;
using appender = basic_appender<char>;
(static_cast<unsigned>(p) << precision_shift);
}
- constexpr bool dynamic() const {
+ constexpr auto dynamic() const -> bool {
return (data_ & (width_mask | precision_mask)) != 0;
}
FMT_CONSTEXPR void check_dynamic_spec(int arg_id);
};
+#ifndef FMT_USE_LOCALE
+# define FMT_USE_LOCALE (FMT_OPTIMIZE_SIZE <= 1)
+#endif
+
+// A type-erased reference to std::locale to avoid the heavy <locale> include.
+class locale_ref {
+#if FMT_USE_LOCALE
+ private:
+ const void* locale_; // A type-erased pointer to std::locale.
+
+ public:
+ constexpr locale_ref() : locale_(nullptr) {}
+
+ template <typename Locale, FMT_ENABLE_IF(sizeof(Locale::collate) != 0)>
+ locale_ref(const Locale& loc);
+
+ inline explicit operator bool() const noexcept { return locale_ != nullptr; }
+#endif // FMT_USE_LOCALE
+
+ public:
+ template <typename Locale> auto get() const -> Locale;
+};
+
FMT_END_EXPORT
namespace detail {
+// Specifies if `T` is a code unit type.
+template <typename T> struct is_code_unit : std::false_type {};
+template <> struct is_code_unit<char> : std::true_type {};
+template <> struct is_code_unit<wchar_t> : std::true_type {};
+template <> struct is_code_unit<char16_t> : std::true_type {};
+template <> struct is_code_unit<char32_t> : std::true_type {};
+#ifdef __cpp_char8_t
+template <> struct is_code_unit<char8_t> : bool_constant<is_utf8_enabled> {};
+#endif
+
// Constructs fmt::basic_string_view<Char> from types implicitly convertible
// to it, deducing Char. Explicitly convertible types such as the ones returned
// from FMT_STRING are intentionally excluded.
-template <typename Char, FMT_ENABLE_IF(is_char<Char>::value)>
+template <typename Char, FMT_ENABLE_IF(is_code_unit<Char>::value)>
constexpr auto to_string_view(const Char* s) -> basic_string_view<Char> {
return s;
}
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 {};
return (B1 ? 1 : 0) + count<B2, Tail...>();
}
-template <typename... Args> constexpr auto count_named_args() -> int {
- return count<is_named_arg<Args>::value...>();
+template <typename... T> constexpr auto count_named_args() -> int {
+ return count<is_named_arg<T>::value...>();
}
-template <typename... Args> constexpr auto count_static_named_args() -> int {
- return count<is_static_named_arg<Args>::value...>();
+template <typename... T> constexpr auto count_static_named_args() -> int {
+ return count<is_static_named_arg<T>::value...>();
}
template <typename Char> struct named_arg_info {
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++};
}
FMT_ENABLE_IF(is_static_named_arg<T>::value)>
FMT_CONSTEXPR void init_static_named_arg(named_arg_info<Char>* named_args,
int& arg_index, int& named_arg_index) {
+ check_for_duplicate<Char>(named_args, named_arg_index, T::name);
named_args[named_arg_index++] = {T::name, arg_index++};
}
// To minimize the number of types we need to deal with, long is translated
// either to int or to long long depending on its size.
-enum { long_short = sizeof(long) == sizeof(int) };
+enum { long_short = sizeof(long) == sizeof(int) && FMT_BUILTIN_TYPES };
using long_type = conditional_t<long_short, int, long long>;
using ulong_type = conditional_t<long_short, unsigned, unsigned long long>;
static auto map(ubitint<N>)
-> conditional_t<N <= 64, unsigned long long, void>;
- template <typename T, FMT_ENABLE_IF(is_char<T>::value)>
+ template <typename T, FMT_ENABLE_IF(is_code_unit<T>::value)>
static auto map(T) -> conditional_t<
std::is_same<T, char>::value || std::is_same<T, Char>::value, Char, void>;
template <typename Char, int NUM_ARGS, int NUM_NAMED_ARGS, bool DYNAMIC_NAMES>
class format_string_checker {
private:
- type types_[max_of(1, NUM_ARGS)];
- named_arg_info<Char> named_args_[max_of(1, NUM_NAMED_ARGS)];
+ type types_[max_of<size_t>(1, NUM_ARGS)];
+ named_arg_info<Char> named_args_[max_of<size_t>(1, NUM_NAMED_ARGS)];
compile_parse_context<Char> context_;
using parse_func = auto (*)(parse_context<Char>&) -> const Char*;
- parse_func parse_funcs_[max_of(1, NUM_ARGS)];
+ parse_func parse_funcs_[max_of<size_t>(1, NUM_ARGS)];
public:
template <typename... T>
-> const Char* {
context_.advance_to(begin);
if (id >= 0 && id < NUM_ARGS) return parse_funcs_[id](context_);
- while (begin != end && *begin != '}') ++begin;
+
+ // If id is out of range, it means we do not know the type and cannot parse
+ // the format at compile time. Instead, skip over content until we finish
+ // the format spec, accounting for any nested replacements.
+ for (int bracket_count = 0;
+ begin != end && (bracket_count > 0 || *begin != '}'); ++begin) {
+ if (*begin == '{')
+ ++bracket_count;
+ else if (*begin == '}')
+ --bracket_count;
+ }
return begin;
}
.append(std::declval<InputIt>(),
std::declval<InputIt>()))>> : std::true_type {};
+template <typename OutputIt, typename InputIt, typename = void>
+struct has_back_insert_iterator_container_insert_at_end : std::false_type {};
+
+template <typename OutputIt, typename InputIt>
+struct has_back_insert_iterator_container_insert_at_end<
+ OutputIt, InputIt,
+ void_t<decltype(get_container(std::declval<OutputIt>())
+ .insert(get_container(std::declval<OutputIt>()).end(),
+ std::declval<InputIt>(),
+ std::declval<InputIt>()))>> : std::true_type {};
+
// An optimized version of std::copy with the output value type (T).
template <typename T, typename InputIt, typename OutputIt,
FMT_ENABLE_IF(is_back_insert_iterator<OutputIt>::value&&
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 {
}
template <typename T, typename InputIt, typename OutputIt,
- FMT_ENABLE_IF(!is_back_insert_iterator<OutputIt>::value)>
+ FMT_ENABLE_IF(!(is_back_insert_iterator<OutputIt>::value &&
+ (has_back_insert_iterator_container_append<
+ OutputIt, InputIt>::value ||
+ has_back_insert_iterator_container_insert_at_end<
+ OutputIt, InputIt>::value)))>
FMT_CONSTEXPR auto copy(InputIt begin, InputIt end, OutputIt out) -> OutputIt {
while (begin != end) *out++ = static_cast<T>(*begin++);
return out;
static_assert(N <= 64, "unsupported _BitInt");
}
- template <typename T, FMT_ENABLE_IF(is_char<T>::value)>
+ template <typename T, FMT_ENABLE_IF(is_code_unit<T>::value)>
constexpr FMT_INLINE value(T x FMT_BUILTIN) : char_value(x) {
static_assert(
std::is_same<T, char>::value || std::is_same<T, char_type>::value,
custom.value = const_cast<value_type*>(&x);
#endif
}
- custom.format = format_custom<value_type, formatter<value_type, char_type>>;
+ custom.format = format_custom<value_type>;
}
template <typename T, FMT_ENABLE_IF(!has_formatter<T, char_type>())>
}
// Formats an argument of a custom type, such as a user-defined class.
- template <typename T, typename Formatter>
+ template <typename T>
static void format_custom(void* arg, parse_context<char_type>& parse_ctx,
Context& ctx) {
- auto f = Formatter();
+ auto f = formatter<T, char_type>();
parse_ctx.advance_to(f.parse(parse_ctx));
using qualified_type =
conditional_t<has_formatter<const T, char_type>(), const T, T>;
enable_if_t<std::is_assignable<decltype(*std::declval<decay_t<It>&>()++),
T>::value>> : std::true_type {};
-#ifndef FMT_USE_LOCALE
-# define FMT_USE_LOCALE (FMT_OPTIMIZE_SIZE <= 1)
-#endif
-
-// A type-erased reference to an std::locale to avoid a heavy <locale> include.
-class locale_ref {
-#if FMT_USE_LOCALE
- private:
- const void* locale_; // A type-erased pointer to std::locale.
-
- public:
- constexpr locale_ref() : locale_(nullptr) {}
- template <typename Locale> locale_ref(const Locale& loc);
-
- inline explicit operator bool() const noexcept { return locale_ != nullptr; }
-#endif // FMT_USE_LOCALE
-
- public:
- template <typename Locale> auto get() const -> Locale;
-};
-
template <typename> constexpr auto encode_types() -> unsigned long long {
return 0;
}
-template <typename Context, typename Arg, typename... Args>
+template <typename Context, typename First, typename... T>
constexpr auto encode_types() -> unsigned long long {
- return static_cast<unsigned>(stored_type_constant<Arg, Context>::value) |
- (encode_types<Context, Args...>() << packed_arg_bits);
+ return static_cast<unsigned>(stored_type_constant<First, Context>::value) |
+ (encode_types<Context, T...>() << packed_arg_bits);
}
template <typename Context, typename... T, size_t NUM_ARGS = sizeof...(T)>
unsigned long long DESC>
struct named_arg_store {
// args_[0].named_args points to named_args to avoid bloating format_args.
- arg_t<Context, NUM_ARGS> args[1 + NUM_ARGS];
- named_arg_info<typename Context::char_type> named_args[NUM_NAMED_ARGS];
+ arg_t<Context, NUM_ARGS> args[1u + NUM_ARGS];
+ named_arg_info<typename Context::char_type>
+ named_args[static_cast<size_t>(NUM_NAMED_ARGS)];
template <typename... T>
FMT_CONSTEXPR FMT_ALWAYS_INLINE named_arg_store(T&... values)
}
named_arg_store(const named_arg_store& rhs) = delete;
- named_arg_store& operator=(const named_arg_store& rhs) = delete;
- named_arg_store& operator=(named_arg_store&& rhs) = delete;
+ auto operator=(const named_arg_store& rhs) -> named_arg_store& = delete;
+ auto operator=(named_arg_store&& rhs) -> named_arg_store& = delete;
operator const arg_t<Context, NUM_ARGS>*() const { return args + 1; }
};
// +1 to workaround a bug in gcc 7.5 that causes duplicated-branches warning.
using type =
conditional_t<NUM_NAMED_ARGS == 0,
- arg_t<Context, NUM_ARGS>[max_of(1, NUM_ARGS)],
+ arg_t<Context, NUM_ARGS>[max_of<size_t>(1, NUM_ARGS)],
named_arg_store<Context, NUM_ARGS, NUM_NAMED_ARGS, DESC>>;
type args;
};
private:
appender out_;
format_args args_;
- FMT_NO_UNIQUE_ADDRESS detail::locale_ref loc_;
+ FMT_NO_UNIQUE_ADDRESS locale_ref loc_;
public:
- /// The character type for the output.
- using char_type = char;
-
+ using char_type = char; ///< The character type for the output.
using iterator = appender;
using format_arg = basic_format_arg<context>;
- using parse_context_type FMT_DEPRECATED = parse_context<>;
- template <typename T> using formatter_type FMT_DEPRECATED = formatter<T>;
enum { builtin_types = FMT_BUILTIN_TYPES };
/// Constructs a `context` object. References to the arguments are stored
/// in the object so make sure they have appropriate lifetimes.
- FMT_CONSTEXPR context(iterator out, format_args args,
- detail::locale_ref loc = {})
+ FMT_CONSTEXPR context(iterator out, format_args args, locale_ref loc = {})
: out_(out), args_(args), loc_(loc) {}
context(context&&) = default;
context(const context&) = delete;
// Advances the begin iterator to `it`.
FMT_CONSTEXPR void advance_to(iterator) {}
- FMT_CONSTEXPR auto locale() const -> detail::locale_ref { return loc_; }
+ FMT_CONSTEXPR auto locale() const -> locale_ref { return loc_; }
};
template <typename Char = char> struct runtime_format_string {
template <size_t N>
FMT_CONSTEVAL FMT_ALWAYS_INLINE fstring(const char (&s)[N]) : str(s, N - 1) {
using namespace detail;
- static_assert(count<(std::is_base_of<view, remove_reference_t<T>>::value &&
+ static_assert(count<(is_view<remove_cvref_t<T>>::value &&
std::is_reference<T>::value)...>() == 0,
"passing views as lvalues is disallowed");
if (FMT_USE_CONSTEVAL) parse_format_string<char>(s, checker(s, arg_pack()));
concept formattable = is_formattable<remove_reference_t<T>, Char>::value;
#endif
-template <typename T, typename Char>
-using has_formatter FMT_DEPRECATED = std::is_constructible<formatter<T, Char>>;
-
// A formatter specialization for natively supported types.
template <typename T, typename Char>
struct formatter<T, Char,
return fmt::println(stdout, fmt, static_cast<T&&>(args)...);
}
-FMT_END_EXPORT
FMT_PRAGMA_CLANG(diagnostic pop)
FMT_PRAGMA_GCC(pop_options)
+FMT_END_EXPORT
FMT_END_NAMESPACE
#ifdef FMT_HEADER_ONLY
# include <cmath> // std::signbit
# include <cstddef> // std::byte
# include <cstdint> // uint32_t
+# include <cstdlib> // std::malloc, std::free
# include <cstring> // std::memcpy
# include <limits> // std::numeric_limits
# include <new> // std::bad_alloc
# define FMT_NOINLINE
#endif
+#ifdef FMT_DEPRECATED
+// Use the provided definition.
+#elif FMT_HAS_CPP14_ATTRIBUTE(deprecated)
+# define FMT_DEPRECATED [[deprecated]]
+#else
+# define FMT_DEPRECATED /* deprecated */
+#endif
+
+// Detect constexpr std::string.
+#if !FMT_USE_CONSTEVAL
+# define FMT_USE_CONSTEXPR_STRING 0
+#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_USE_CONSTEXPR_STRING 1
+# endif
+# else
+# 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
+
+// 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;
};
} // namespace std
-#ifndef FMT_THROW
-# if FMT_USE_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)
-# else
-# define FMT_THROW(x) throw x
-# endif
-# else
-# define FMT_THROW(x) \
- ::fmt::detail::assert_fail(__FILE__, __LINE__, (x).what())
-# endif // FMT_USE_EXCEPTIONS
-#endif // FMT_THROW
+#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
+
+#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
// integer formatter template instantiations to just one by only using the
constexpr auto to_pointer(OutputIt, size_t) -> T* {
return nullptr;
}
+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);
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);
+ 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];
} 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 += 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 {
- return min_of(n, s.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> {};
#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__)
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>;
#endif
// An allocator that uses malloc/free to allow removing dependency on the C++
-// standard libary runtime.
-template <typename T> struct allocator {
+// 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;
- T* allocate(size_t n) {
+ auto allocate(size_t n) -> T* {
FMT_ASSERT(n <= max_value<size_t>() / sizeof(T), "");
- T* p = static_cast<T*>(malloc(n * sizeof(T)));
+ T* p = static_cast<T*>(std::malloc(n * sizeof(T)));
if (!p) FMT_THROW(std::bad_alloc());
return p;
}
- void deallocate(T* p, size_t) { free(p); }
+ void deallocate(T* p, size_t) { std::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;
+ }
};
} // namespace detail
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_);
inline string_buffer() : buf_(str_) {}
inline operator writer() { return buf_; }
- inline std::string& str() { return str_; }
+ inline auto str() -> std::string& { return str_; }
};
template <typename T, size_t SIZE, typename Allocator>
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]);
out += size;
do {
const char* digits = upper ? "0123456789ABCDEF" : "0123456789abcdef";
- unsigned digit = static_cast<unsigned>(value & ((1 << base_bits) - 1));
+ 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);
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; }
buf.append(string_view("\xEF\xBF\xBD"));
--p;
continue;
- } else {
- c = (c << 10) + static_cast<uint32_t>(*p) - 0x35fdc00;
}
+ c = (c << 10) + static_cast<uint32_t>(*p) - 0x35fdc00;
}
if (c < 0x80) {
buf.push_back(static_cast<char>(c));
};
// 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 OutputIt>
FMT_CONSTEXPR auto write_exponent(int exp, OutputIt out) -> OutputIt {
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,
+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>());
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);
-}
template <typename Char> class digit_grouping {
private:
}
public:
- template <typename Locale,
- FMT_ENABLE_IF(std::is_same<Locale, locale_ref>::value)>
- explicit digit_grouping(Locale loc, bool localized = true) {
+ explicit digit_grouping(locale_ref loc, bool localized = true) {
if (!localized) return;
auto sep = thousands_sep<Char>(loc);
grouping_ = sep.grouping;
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());
// 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, const loc_value&, const format_specs&,
prefix = 0x01000000 | '-';
abs_value = 0 - abs_value;
} else {
- constexpr const unsigned prefixes[4] = {0, 0, 0x1000000u | '+',
- 0x1000000u | ' '};
+ constexpr unsigned prefixes[4] = {0, 0, 0x1000000u | '+', 0x1000000u | ' '};
prefix = prefixes[static_cast<int>(s)];
}
return {abs_value, prefix};
const format_specs& specs) -> OutputIt {
static_assert(std::is_same<T, uint32_or_64_or_128_t<T>>::value, "");
- constexpr int buffer_size = num_bits<T>();
+ 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;
}
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);
+}
+
+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;
+ }
+
+ size += display_width_limit - display_width;
+ display_width = display_width_limit;
+ return false;
+ }
+
+ 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;
+ }
+
+ return false;
+ });
+
+ struct bounded_output_iterator {
+ reserve_iterator<OutputIt> underlying_iterator;
+ size_t bound;
+
+ 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;
+ }
+ };
+
+ 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,
+ 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));
+ size = to_unsigned(specs.precision);
bool is_debug = specs.type() == presentation_type::debug;
if (is_debug) {
size = buf.count();
}
- size_t width = 0;
- if (specs.width != 0) {
- width =
- is_debug ? size : compute_width(basic_string_view<Char>(data, size));
- }
return write_padded<Char>(
- out, specs, size, width, [=](reserve_iterator<OutputIt> it) {
+ 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<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 {
int floating_size = significand_size - integral_size;
for (int i = floating_size / 2; i > 0; --i) {
out -= 2;
- write2digits(out, 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, sign s,
- int exp_upper, locale_ref loc) -> OutputIt {
- auto significand = f.significand;
- int significand_size = get_significand_size(f);
- const Char zero = static_cast<Char>('0');
- size_t size = to_unsigned(significand_size) + (s != sign::none ? 1 : 0);
- using iterator = reserve_iterator<OutputIt>;
+// 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;
+}
- Char decimal_point = specs.localized() ? detail::decimal_point<Char>(loc)
- : static_cast<Char>('.');
-
- int output_exp = f.exponent + significand_size - 1;
- auto use_exp_format = [=]() {
- if (specs.type() == presentation_type::exp) return true;
- if (specs.type() == presentation_type::fixed) 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;
- return output_exp < exp_lower ||
- output_exp >= (specs.precision > 0 ? specs.precision : exp_upper);
- };
- if (use_exp_format()) {
- int num_zeros = 0;
- if (specs.alt()) {
- num_zeros = specs.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 (s != sign::none) *it++ = detail::getsign<Char>(s);
- // 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)));
+// 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 {
+ public:
+ constexpr fallback_digit_grouping(locale_ref, bool) {}
+
+ constexpr auto has_separator() const -> bool { return false; }
+
+ constexpr auto count_separators(int) const -> int { return 0; }
+
+ template <typename Out, typename C>
+ constexpr auto apply(Out out, basic_string_view<C>) const -> Out {
+ return out;
}
+};
+
+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 += to_unsigned(f.exponent);
+ 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 += to_unsigned(num_zeros);
+ if (num_zeros > 0) size += num_zeros;
}
auto grouping = Grouping(loc, specs.localized());
- size += to_unsigned(grouping.count_separators(exp));
- return write_padded<Char, align::right>(out, specs, size, [&](iterator it) {
- if (s != sign::none) *it++ = detail::getsign<Char>(s);
- it = write_significand<Char>(it, significand, significand_size,
- f.exponent, grouping);
- if (!specs.alt()) return it;
- *it++ = decimal_point;
- return num_zeros > 0 ? detail::fill_n(it, num_zeros, zero) : it;
- });
- } else if (exp > 0) {
+ 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<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 + static_cast<unsigned>(max_of(num_zeros, 0));
+ size += 1 + max_of(num_zeros, 0);
auto grouping = Grouping(loc, specs.localized());
- size += to_unsigned(grouping.count_separators(exp));
- return write_padded<Char, align::right>(out, specs, size, [&](iterator it) {
- if (s != sign::none) *it++ = detail::getsign<Char>(s);
- it = write_significand(it, significand, significand_size, exp,
- decimal_point, grouping);
- return num_zeros > 0 ? detail::fill_n(it, num_zeros, zero) : it;
- });
+ 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;
num_zeros = specs.precision;
}
bool pointy = num_zeros != 0 || significand_size != 0 || specs.alt();
- size += 1 + (pointy ? 1 : 0) + to_unsigned(num_zeros);
- return write_padded<Char, align::right>(out, specs, size, [&](iterator it) {
- if (s != sign::none) *it++ = detail::getsign<Char>(s);
- *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);
- });
+ 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> class fallback_digit_grouping {
- public:
- constexpr fallback_digit_grouping(locale_ref, bool) {}
-
- constexpr auto has_separator() const -> bool { return false; }
-
- constexpr auto count_separators(int) const -> int { return 0; }
-
- template <typename Out, typename C>
- constexpr auto apply(Out out, basic_string_view<C>) const -> Out {
- return out;
- }
-};
+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, 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, s,
- exp_upper, 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, s, exp_upper, 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) {
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;
}
return exp;
}
-// Numbers with exponents greater or equal to the returned value will use
-// the exponential notation.
-template <typename T> constexpr auto exp_upper() -> int {
- return std::numeric_limits<T>::digits10 != 0
- ? min_of(16, std::numeric_limits<T>::digits10 + 1)
- : 16;
-}
+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;
-template <typename Char, typename OutputIt, typename T>
-FMT_CONSTEXPR20 auto write_float(OutputIt out, T value, format_specs specs,
- locale_ref loc) -> OutputIt {
// Use signbit because value < 0 is false for NaN.
sign s = detail::signbit(value) ? sign::minus : specs.sign();
if (specs.width != 0) --specs.width;
}
- constexpr int exp_upper = detail::exp_upper<T>();
+ 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.
- using floaty = conditional_t<sizeof(T) >= sizeof(double), double, float>;
- auto dec = dragonbox::to_decimal(static_cast<floaty>(value));
+ auto dec = dragonbox::to_decimal(static_cast<fast_float_t<T>>(value));
return write_float<Char>(out, dec, specs, s, exp_upper, loc);
}
}
return write_float<Char>(out, f, specs, s, exp_upper, loc);
}
-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 {
- return specs.localized() && write_loc(out, value, specs, loc)
- ? out
- : write_float<Char>(out, value, specs, 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());
auto s = detail::signbit(value) ? sign::minus : sign::none;
-
- constexpr auto specs = format_specs();
- using floaty = conditional_t<sizeof(T) >= sizeof(double), double, float>;
- 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, s);
-
- auto dec = dragonbox::to_decimal(static_cast<floaty>(value));
- return write_float<Char>(out, dec, specs, s, exp_upper<T>(), {});
+ 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);
+ }
+ 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 Context, typename ID>
-FMT_CONSTEXPR auto get_arg(Context& ctx, ID id) -> basic_format_arg<Context> {
- auto arg = ctx.arg(id);
- if (!arg) report_error("argument not found");
- return arg;
-}
-
template <typename Context>
-FMT_CONSTEXPR int get_dynamic_spec(
- arg_id_kind kind, const arg_ref<typename Context::char_type>& ref,
- Context& ctx) {
- FMT_ASSERT(kind != arg_id_kind::none, "");
+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");
- unsigned long long value = arg.visit(dynamic_spec_getter());
- if (value > to_unsigned(max_value<int>()))
+ unsigned long long result = arg.visit(dynamic_spec_getter());
+ if (result > to_unsigned(max_value<int>()))
report_error("width/precision is out of range");
- return static_cast<int>(value);
-}
-
-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) value = get_dynamic_spec(kind, ref, ctx);
+ value = static_cast<int>(result);
}
#if FMT_USE_NONTYPE_TEMPLATE_ARGS
};
#endif // FMT_USE_NONTYPE_TEMPLATE_ARGS
-template <typename Char> struct format_handler {
+template <typename Char = char> struct format_handler {
parse_context<Char> parse_ctx;
buffered_context<Char> ctx;
auto on_format_specs(int id, const Char* begin, const Char* end)
-> const Char* {
- auto arg = get_arg(ctx, id);
+ 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();
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;
specs_.precision_ref, ctx);
return write<Char>(ctx.out(), val, specs, ctx.locale());
}
-
-// DEPRECATED! https://github.com/fmtlib/fmt/issues/4292.
-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 {};
-
-// DEPRECATED!
-template <typename Char = char> struct vformat_args {
- using type = basic_format_args<buffered_context<Char>>;
-};
-template <> struct vformat_args<char> {
- using type = format_args;
-};
-
-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);
- parse_format_string(
- fmt, format_handler<Char>{parse_context<Char>(fmt), {out, args, loc}});
-}
} // namespace detail
FMT_BEGIN_EXPORT
private:
OutputIt out_;
basic_format_args<generic_context> args_;
- detail::locale_ref loc_;
+ locale_ref loc_;
public:
using char_type = Char;
using iterator = OutputIt;
- using parse_context_type FMT_DEPRECATED = parse_context<Char>;
- template <typename T>
- using formatter_type FMT_DEPRECATED = formatter<T, Char>;
enum { builtin_types = FMT_BUILTIN_TYPES };
constexpr generic_context(OutputIt out,
basic_format_args<generic_context> args,
- detail::locale_ref loc = {})
+ locale_ref loc = {})
: out_(out), args_(args), loc_(loc) {}
generic_context(generic_context&&) = default;
generic_context(const generic_context&) = delete;
if (!detail::is_back_insert_iterator<iterator>()) out_ = it;
}
- constexpr auto locale() const -> detail::locale_ref { return loc_; }
+ constexpr auto locale() const -> locale_ref { return loc_; }
};
class loc_value {
* 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);
}
} // namespace enums
#ifdef __cpp_lib_byte
-template <> struct formatter<std::byte> : formatter<unsigned> {
+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>::format(format_as(b), ctx);
+ return formatter<unsigned, Char>::format(format_as(b), ctx);
}
};
#endif
inline auto str() const -> std::string { return {str_, size()}; }
};
-#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(); \
- }()
+#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
/**
* Constructs a legacy compile-time format string from a string literal `s`.
// Can be used to report errors from destructors.
FMT_API void report_system_error(int error_code, const char* message) noexcept;
-template <typename Locale, FMT_ENABLE_IF(detail::is_locale<Locale>::value)>
-inline auto vformat(const Locale& loc, string_view fmt, format_args args)
+inline auto vformat(locale_ref loc, string_view fmt, format_args args)
-> std::string {
auto buf = memory_buffer();
- detail::vformat_to(buf, fmt, args, detail::locale_ref(loc));
+ detail::vformat_to(buf, fmt, args, loc);
return {buf.data(), buf.size()};
}
-template <typename Locale, typename... T,
- FMT_ENABLE_IF(detail::is_locale<Locale>::value)>
-FMT_INLINE auto format(const Locale& loc, format_string<T...> fmt, T&&... args)
+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 OutputIt, typename Locale,
+template <typename OutputIt,
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, char>::value)>
-auto vformat_to(OutputIt out, const Locale& loc, string_view fmt,
- format_args args) -> OutputIt {
+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, detail::locale_ref(loc));
+ detail::vformat_to(buf, fmt, args, 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 {
+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...}});
}
-template <typename Locale, typename... T,
- FMT_ENABLE_IF(detail::is_locale<Locale>::value)>
-FMT_NODISCARD FMT_INLINE auto formatted_size(const Locale& loc,
+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...}},
- detail::locale_ref(loc));
+ detail::vformat_to(buf, fmt.str, vargs<T...>{{args...}}, loc);
return buf.count();
}
* std::string answer = fmt::to_string(42);
*/
template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
-FMT_NODISCARD auto to_string(T value) -> std::string {
+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)];
}
template <typename T, FMT_ENABLE_IF(detail::use_format_as<T>::value)>
-FMT_NODISCARD auto to_string(const T& value) -> std::string {
+FMT_NODISCARD FMT_CONSTEXPR_STRING auto to_string(const T& value)
+ -> std::string {
return to_string(format_as(value));
}
template <typename T, FMT_ENABLE_IF(!std::is_integral<T>::value &&
!detail::use_format_as<T>::value)>
-FMT_NODISCARD auto to_string(const T& value) -> std::string {
+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()};
# include <atomic>
# include <bitset>
# include <complex>
-# include <cstdlib>
# include <exception>
-# include <functional>
+# include <functional> // std::reference_wrapper
# include <memory>
# include <thread>
# include <type_traits>
-# include <typeinfo>
-# include <utility>
-# include <vector>
+# include <typeinfo> // std::type_info
+# include <utility> // std::make_index_sequence
// Check FMT_CPLUSPLUS to suppress a bogus warning in MSVC.
# if FMT_CPLUSPLUS >= 201703L
# 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>
+auto write_escaped_alternative(OutputIt out, const T& v) -> OutputIt {
+ if constexpr (has_to_string_view<T>::value)
+ return write_escaped_string<Char>(out, detail::to_string_view(v));
+ if constexpr (std::is_same_v<T, Char>) return write_escaped_char(out, v);
+ return write<Char>(out, v);
+}
+#endif
+
+#if FMT_CPP_LIB_VARIANT
+
+template <typename> struct is_variant_like_ : std::false_type {};
+template <typename... Types>
+struct is_variant_like_<std::variant<Types...>> : std::true_type {};
+
+template <typename Variant, typename Char> class is_variant_formattable {
+ template <size_t... Is>
+ static auto check(std::index_sequence<Is...>) -> std::conjunction<
+ is_formattable<std::variant_alternative_t<Is, Variant>, Char>...>;
+
+ public:
+ static constexpr bool value = decltype(check(
+ std::make_index_sequence<std::variant_size<Variant>::value>()))::value;
+};
+
+#endif // FMT_CPP_LIB_VARIANT
+
+#if FMT_USE_RTTI
+
+template <typename OutputIt>
+auto write_demangled_name(OutputIt out, const std::type_info& ti) -> OutputIt {
+# ifdef FMT_HAS_ABI_CXA_DEMANGLE
+ int status = 0;
+ size_t size = 0;
+ std::unique_ptr<char, void (*)(void*)> demangled_name_ptr(
+ abi::__cxa_demangle(ti.name(), nullptr, &size, &status), &std::free);
+
+ string_view demangled_name_view;
+ if (demangled_name_ptr) {
+ demangled_name_view = demangled_name_ptr.get();
+
+ // Normalization of stdlib inline namespace names.
+ // libc++ inline namespaces.
+ // std::__1::* -> std::*
+ // std::__1::__fs::* -> std::*
+ // libstdc++ inline namespaces.
+ // std::__cxx11::* -> std::*
+ // std::filesystem::__cxx11::* -> std::filesystem::*
+ if (demangled_name_view.starts_with("std::")) {
+ char* begin = demangled_name_ptr.get();
+ char* to = begin + 5; // std::
+ for (char *from = to, *end = begin + demangled_name_view.size();
+ from < end;) {
+ // This is safe, because demangled_name is NUL-terminated.
+ if (from[0] == '_' && from[1] == '_') {
+ char* next = from + 1;
+ while (next < end && *next != ':') next++;
+ if (next[0] == ':' && next[1] == ':') {
+ from = next + 2;
+ continue;
+ }
+ }
+ *to++ = *from++;
+ }
+ demangled_name_view = {begin, detail::to_unsigned(to - begin)};
+ }
+ } else {
+ demangled_name_view = string_view(ti.name());
+ }
+ return detail::write_bytes<char>(out, demangled_name_view);
+# elif FMT_MSC_VERSION
+ const string_view demangled_name(ti.name());
+ for (size_t i = 0; i < demangled_name.size(); ++i) {
+ auto sub = demangled_name;
+ sub.remove_prefix(i);
+ if (sub.starts_with("enum ")) {
+ i += 4;
+ continue;
+ }
+ if (sub.starts_with("class ") || sub.starts_with("union ")) {
+ i += 5;
+ continue;
+ }
+ if (sub.starts_with("struct ")) {
+ i += 6;
+ continue;
+ }
+ if (*sub.begin() != ' ') *out++ = *sub.begin();
+ }
+ return out;
+# else
+ return detail::write_bytes<char>(out, string_view(ti.name()));
+# endif
+}
+
+#endif // FMT_USE_RTTI
+
+template <typename T, typename Enable = void>
+struct has_flip : std::false_type {};
+
+template <typename T>
+struct has_flip<T, void_t<decltype(std::declval<T>().flip())>>
+ : std::true_type {};
+
+template <typename T> struct is_bit_reference_like {
+ static constexpr bool value = std::is_convertible<T, bool>::value &&
+ std::is_nothrow_assignable<T, bool>::value &&
+ has_flip<T>::value;
+};
+
+// Workaround for libc++ incompatibility with C++ standard.
+// According to the Standard, `bitset::operator[] const` returns bool.
+#if defined(_LIBCPP_VERSION) && !defined(FMT_IMPORT_STD)
+template <typename C>
+struct is_bit_reference_like<std::__bit_const_reference<C>> {
+ static constexpr bool value = true;
+};
+#endif
+
+template <typename T, typename Enable = void>
+struct has_format_as : std::false_type {};
+template <typename T>
+struct has_format_as<T, void_t<decltype(format_as(std::declval<const T&>()))>>
+ : std::true_type {};
+
+template <typename T, typename Enable = void>
+struct has_format_as_member : std::false_type {};
+template <typename T>
+struct has_format_as_member<
+ T, void_t<decltype(formatter<T>::format_as(std::declval<const T&>()))>>
+ : std::true_type {};
+
} // namespace detail
-FMT_EXPORT
+template <typename T, typename Deleter>
+auto ptr(const std::unique_ptr<T, Deleter>& p) -> const void* {
+ return p.get();
+}
+template <typename T> auto ptr(const std::shared_ptr<T>& p) -> const void* {
+ return p.get();
+}
+
+#if FMT_CPP_LIB_FILESYSTEM
+
+class path : public std::filesystem::path {
+ public:
+ auto display_string() const -> std::string {
+ const std::filesystem::path& base = *this;
+ return fmt::format(FMT_STRING("{}"), base);
+ }
+ auto system_string() const -> std::string { return string(); }
+
+ auto generic_display_string() const -> std::string {
+ const std::filesystem::path& base = *this;
+ return fmt::format(FMT_STRING("{:g}"), base);
+ }
+ auto generic_system_string() const -> std::string { return generic_string(); }
+};
+
template <typename Char> struct formatter<std::filesystem::path, Char> {
private:
format_specs specs_;
}
};
-class path : public std::filesystem::path {
- public:
- auto display_string() const -> std::string {
- const std::filesystem::path& base = *this;
- return fmt::format(FMT_STRING("{}"), base);
- }
- auto system_string() const -> std::string { return string(); }
-
- auto generic_display_string() const -> std::string {
- const std::filesystem::path& base = *this;
- return fmt::format(FMT_STRING("{:g}"), base);
- }
- auto generic_system_string() const -> std::string { return generic_string(); }
-};
-
-FMT_END_NAMESPACE
#endif // FMT_CPP_LIB_FILESYSTEM
-FMT_BEGIN_NAMESPACE
-FMT_EXPORT
-template <std::size_t N, typename Char>
+template <size_t N, typename Char>
struct formatter<std::bitset<N>, Char>
: nested_formatter<basic_string_view<Char>, Char> {
private:
- // Functor because C++11 doesn't support generic lambdas.
+ // This is a functor because C++11 doesn't support generic lambdas.
struct writer {
const std::bitset<N>& bs;
template <typename OutputIt>
FMT_CONSTEXPR auto operator()(OutputIt out) -> OutputIt {
- for (auto pos = N; pos > 0; --pos) {
+ for (auto pos = N; pos > 0; --pos)
out = detail::write<Char>(out, bs[pos - 1] ? Char('1') : Char('0'));
- }
-
return out;
}
};
}
};
-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>> {
return detail::write(out, ')');
}
};
-FMT_END_NAMESPACE
#endif // __cpp_lib_optional
-#if defined(__cpp_lib_expected) || FMT_CPP_LIB_VARIANT
-
-FMT_BEGIN_NAMESPACE
-namespace detail {
-
-template <typename Char, typename OutputIt, typename T>
-auto write_escaped_alternative(OutputIt out, const T& v) -> OutputIt {
- if constexpr (has_to_string_view<T>::value)
- return write_escaped_string<Char>(out, detail::to_string_view(v));
- if constexpr (std::is_same_v<T, Char>) return write_escaped_char(out, v);
- return write<Char>(out, v);
-}
-
-} // namespace detail
-
-FMT_END_NAMESPACE
-#endif
-
#ifdef __cpp_lib_expected
-FMT_BEGIN_NAMESPACE
-
-FMT_EXPORT
template <typename T, typename E, typename Char>
struct formatter<std::expected<T, E>, Char,
std::enable_if_t<(std::is_void<T>::value ||
return out;
}
};
-FMT_END_NAMESPACE
#endif // __cpp_lib_expected
#ifdef __cpp_lib_source_location
-FMT_BEGIN_NAMESPACE
-FMT_EXPORT
template <> struct formatter<std::source_location> {
FMT_CONSTEXPR auto parse(parse_context<>& ctx) { return ctx.begin(); }
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;
-};
-
-template <typename T, typename C> struct is_variant_formattable {
- static constexpr const bool value =
- detail::is_variant_formattable_<T, C>::value;
+ static constexpr bool value = detail::is_variant_like_<T>::value;
};
-FMT_EXPORT
template <typename Char> struct formatter<std::monostate, Char> {
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>>>> {
+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();
}
return out;
}
};
-FMT_END_NAMESPACE
+
#endif // FMT_CPP_LIB_VARIANT
-FMT_BEGIN_NAMESPACE
-FMT_EXPORT
template <> struct formatter<std::error_code> {
private:
format_specs specs_;
detail::arg_ref<char> width_ref_;
+ bool debug_ = false;
public:
FMT_CONSTEXPR auto parse(parse_context<>& ctx) -> const char* {
if (it == end) return it;
it = detail::parse_align(it, end, specs_);
- if (it == end) return it;
char c = *it;
- if ((c >= '0' && c <= '9') || c == '{')
+ if (it != end && ((c >= '0' && c <= '9') || c == '{'))
it = detail::parse_width(it, end, specs_, width_ref_, ctx);
+
+ if (it != end && *it == '?') {
+ debug_ = true;
+ ++it;
+ }
+ if (it != end && *it == 's') {
+ specs_.set_type(presentation_type::string);
+ ++it;
+ }
return it;
}
auto specs = specs_;
detail::handle_dynamic_spec(specs.dynamic_width(), specs.width, width_ref_,
ctx);
- memory_buffer buf;
- buf.append(string_view(ec.category().name()));
- buf.push_back(':');
- detail::write<char>(appender(buf), ec.value());
- return detail::write<char>(ctx.out(), string_view(buf.data(), buf.size()),
- specs);
- }
-};
-
-#if FMT_USE_RTTI
-namespace detail {
-
-template <typename Char, typename OutputIt>
-auto write_demangled_name(OutputIt out, const std::type_info& ti) -> OutputIt {
-# ifdef FMT_HAS_ABI_CXA_DEMANGLE
- int status = 0;
- std::size_t size = 0;
- std::unique_ptr<char, void (*)(void*)> demangled_name_ptr(
- abi::__cxa_demangle(ti.name(), nullptr, &size, &status), &std::free);
-
- string_view demangled_name_view;
- if (demangled_name_ptr) {
- demangled_name_view = demangled_name_ptr.get();
-
- // Normalization of stdlib inline namespace names.
- // libc++ inline namespaces.
- // std::__1::* -> std::*
- // std::__1::__fs::* -> std::*
- // libstdc++ inline namespaces.
- // std::__cxx11::* -> std::*
- // std::filesystem::__cxx11::* -> std::filesystem::*
- if (demangled_name_view.starts_with("std::")) {
- char* begin = demangled_name_ptr.get();
- char* to = begin + 5; // std::
- for (char *from = to, *end = begin + demangled_name_view.size();
- from < end;) {
- // This is safe, because demangled_name is NUL-terminated.
- if (from[0] == '_' && from[1] == '_') {
- char* next = from + 1;
- while (next < end && *next != ':') next++;
- if (next[0] == ':' && next[1] == ':') {
- from = next + 2;
- continue;
- }
- }
- *to++ = *from++;
- }
- demangled_name_view = {begin, detail::to_unsigned(to - begin)};
- }
- } else {
- demangled_name_view = string_view(ti.name());
- }
- return detail::write_bytes<Char>(out, demangled_name_view);
-# elif FMT_MSC_VERSION
- const string_view demangled_name(ti.name());
- for (std::size_t i = 0; i < demangled_name.size(); ++i) {
- auto sub = demangled_name;
- sub.remove_prefix(i);
- if (sub.starts_with("enum ")) {
- i += 4;
- continue;
- }
- if (sub.starts_with("class ") || sub.starts_with("union ")) {
- i += 5;
- continue;
+ 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("struct ")) {
- i += 6;
- 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.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(parse_context<Char>& ctx) -> const Char* {
+ FMT_CONSTEXPR auto parse(parse_context<>& ctx) -> const char* {
return ctx.begin();
}
template <typename Context>
auto format(const std::type_info& ti, Context& ctx) const
-> decltype(ctx.out()) {
- return detail::write_demangled_name<Char>(ctx.out(), ti);
+ return detail::write_demangled_name(ctx.out(), ti);
}
};
-#endif
+#endif // FMT_USE_RTTI
-FMT_EXPORT
-template <typename T, typename Char>
+template <typename T>
struct formatter<
- T, Char, // DEPRECATED! Mixing code unit types.
+ T, char,
typename std::enable_if<std::is_base_of<std::exception, T>::value>::type> {
private:
bool with_typename_ = false;
public:
- FMT_CONSTEXPR auto parse(parse_context<Char>& ctx) -> const Char* {
+ FMT_CONSTEXPR auto parse(parse_context<>& ctx) -> const char* {
auto it = ctx.begin();
auto end = ctx.end();
if (it == end || *it == '}') return it;
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 T, typename Char> struct formatter<std::complex<T>, Char> {
private:
detail::dynamic_format_specs<Char> specs_;
}
};
-FMT_EXPORT
template <typename T, typename Char>
struct formatter<std::reference_wrapper<T>, Char,
- enable_if_t<is_formattable<remove_cvref_t<T>, Char>::value>>
+ // Guard against format_as because reference_wrapper is
+ // implicitly convertible to T&.
+ enable_if_t<is_formattable<remove_cvref_t<T>, Char>::value &&
+ !detail::has_format_as<T>::value &&
+ !detail::has_format_as_member<T>::value>>
: formatter<remove_cvref_t<T>, Char> {
template <typename FormatContext>
auto format(std::reference_wrapper<T> ref, FormatContext& ctx) const
};
FMT_END_NAMESPACE
+
#endif // FMT_STD_H_