#include <cstddef> // std::byte
#include <cstdio> // std::FILE
-#include <cstring>
+#include <cstring> // std::strlen
#include <iterator>
#include <limits>
#include <string>
#include <type_traits>
// The fmt library version in the form major * 10000 + minor * 100 + patch.
-#define FMT_VERSION 80101
+#define FMT_VERSION 90100
#if defined(__clang__) && !defined(__ibmxl__)
# define FMT_CLANG_VERSION (__clang_major__ * 100 + __clang_minor__)
# define FMT_ICC_VERSION 0
#endif
-#ifdef __NVCC__
-# define FMT_NVCC __NVCC__
-#else
-# define FMT_NVCC 0
-#endif
-
#ifdef _MSC_VER
-# define FMT_MSC_VER _MSC_VER
+# define FMT_MSC_VERSION _MSC_VER
# define FMT_MSC_WARNING(...) __pragma(warning(__VA_ARGS__))
#else
-# define FMT_MSC_VER 0
+# define FMT_MSC_VERSION 0
# define FMT_MSC_WARNING(...)
#endif
+#ifdef _MSVC_LANG
+# define FMT_CPLUSPLUS _MSVC_LANG
+#else
+# define FMT_CPLUSPLUS __cplusplus
+#endif
+
#ifdef __has_feature
# define FMT_HAS_FEATURE(x) __has_feature(x)
#else
# define FMT_HAS_FEATURE(x) 0
#endif
-#if defined(__has_include) && \
- (!defined(__INTELLISENSE__) || FMT_MSC_VER > 1900) && \
- (!FMT_ICC_VERSION || FMT_ICC_VERSION >= 1600)
+#if (defined(__has_include) || FMT_ICC_VERSION >= 1600 || \
+ FMT_MSC_VERSION > 1900) && \
+ !defined(__INTELLISENSE__)
# define FMT_HAS_INCLUDE(x) __has_include(x)
#else
# define FMT_HAS_INCLUDE(x) 0
# define FMT_HAS_CPP_ATTRIBUTE(x) 0
#endif
-#ifdef _MSVC_LANG
-# define FMT_CPLUSPLUS _MSVC_LANG
-#else
-# define FMT_CPLUSPLUS __cplusplus
-#endif
-
#define FMT_HAS_CPP14_ATTRIBUTE(attribute) \
(FMT_CPLUSPLUS >= 201402L && FMT_HAS_CPP_ATTRIBUTE(attribute))
// Check if relaxed C++14 constexpr is supported.
// GCC doesn't allow throw in constexpr until version 6 (bug 67371).
#ifndef FMT_USE_CONSTEXPR
-# define FMT_USE_CONSTEXPR \
- (FMT_HAS_FEATURE(cxx_relaxed_constexpr) || FMT_MSC_VER >= 1912 || \
- (FMT_GCC_VERSION >= 600 && __cplusplus >= 201402L)) && \
- !FMT_NVCC && !FMT_ICC_VERSION
+# if (FMT_HAS_FEATURE(cxx_relaxed_constexpr) || FMT_MSC_VERSION >= 1912 || \
+ (FMT_GCC_VERSION >= 600 && FMT_CPLUSPLUS >= 201402L)) && \
+ !FMT_ICC_VERSION && !defined(__NVCC__)
+# define FMT_USE_CONSTEXPR 1
+# else
+# define FMT_USE_CONSTEXPR 0
+# endif
#endif
#if FMT_USE_CONSTEXPR
# define FMT_CONSTEXPR constexpr
-# define FMT_CONSTEXPR_DECL constexpr
#else
# define FMT_CONSTEXPR
-# define FMT_CONSTEXPR_DECL
#endif
-#if ((__cplusplus >= 202002L) && \
+#if ((FMT_CPLUSPLUS >= 202002L) && \
(!defined(_GLIBCXX_RELEASE) || _GLIBCXX_RELEASE > 9)) || \
- (__cplusplus >= 201709L && FMT_GCC_VERSION >= 1002)
+ (FMT_CPLUSPLUS >= 201709L && FMT_GCC_VERSION >= 1002)
# define FMT_CONSTEXPR20 constexpr
#else
# define FMT_CONSTEXPR20
#endif
-// Check if constexpr std::char_traits<>::compare,length is supported.
+// Check if constexpr std::char_traits<>::{compare,length} are supported.
#if defined(__GLIBCXX__)
-# if __cplusplus >= 201703L && defined(_GLIBCXX_RELEASE) && \
+# if FMT_CPLUSPLUS >= 201703L && defined(_GLIBCXX_RELEASE) && \
_GLIBCXX_RELEASE >= 7 // GCC 7+ libstdc++ has _GLIBCXX_RELEASE.
# define FMT_CONSTEXPR_CHAR_TRAITS constexpr
# endif
-#elif defined(_LIBCPP_VERSION) && __cplusplus >= 201703L && \
+#elif defined(_LIBCPP_VERSION) && FMT_CPLUSPLUS >= 201703L && \
_LIBCPP_VERSION >= 4000
# define FMT_CONSTEXPR_CHAR_TRAITS constexpr
-#elif FMT_MSC_VER >= 1914 && _MSVC_LANG >= 201703L
+#elif FMT_MSC_VERSION >= 1914 && FMT_CPLUSPLUS >= 201703L
# define FMT_CONSTEXPR_CHAR_TRAITS constexpr
#endif
#ifndef FMT_CONSTEXPR_CHAR_TRAITS
// Check if exceptions are disabled.
#ifndef FMT_EXCEPTIONS
# if (defined(__GNUC__) && !defined(__EXCEPTIONS)) || \
- FMT_MSC_VER && !_HAS_EXCEPTIONS
+ (FMT_MSC_VERSION && !_HAS_EXCEPTIONS)
# define FMT_EXCEPTIONS 0
# else
# define FMT_EXCEPTIONS 1
# endif
#endif
-// Define FMT_USE_NOEXCEPT to make fmt use noexcept (C++11 feature).
-#ifndef FMT_USE_NOEXCEPT
-# define FMT_USE_NOEXCEPT 0
-#endif
-
-#if FMT_USE_NOEXCEPT || FMT_HAS_FEATURE(cxx_noexcept) || \
- FMT_GCC_VERSION >= 408 || FMT_MSC_VER >= 1900
-# define FMT_DETECTED_NOEXCEPT noexcept
-# define FMT_HAS_CXX11_NOEXCEPT 1
-#else
-# define FMT_DETECTED_NOEXCEPT throw()
-# define FMT_HAS_CXX11_NOEXCEPT 0
-#endif
-
-#ifndef FMT_NOEXCEPT
-# if FMT_EXCEPTIONS || FMT_HAS_CXX11_NOEXCEPT
-# define FMT_NOEXCEPT FMT_DETECTED_NOEXCEPT
+#ifndef FMT_DEPRECATED
+# if FMT_HAS_CPP14_ATTRIBUTE(deprecated) || FMT_MSC_VERSION >= 1900
+# define FMT_DEPRECATED [[deprecated]]
# else
-# define FMT_NOEXCEPT
+# if (defined(__GNUC__) && !defined(__LCC__)) || defined(__clang__)
+# define FMT_DEPRECATED __attribute__((deprecated))
+# elif FMT_MSC_VERSION
+# define FMT_DEPRECATED __declspec(deprecated)
+# else
+# define FMT_DEPRECATED /* deprecated */
+# endif
# endif
#endif
// [[noreturn]] is disabled on MSVC and NVCC because of bogus unreachable code
// warnings.
-#if FMT_EXCEPTIONS && FMT_HAS_CPP_ATTRIBUTE(noreturn) && !FMT_MSC_VER && \
- !FMT_NVCC
+#if FMT_EXCEPTIONS && FMT_HAS_CPP_ATTRIBUTE(noreturn) && !FMT_MSC_VERSION && \
+ !defined(__NVCC__)
# define FMT_NORETURN [[noreturn]]
#else
# define FMT_NORETURN
#endif
-#if __cplusplus == 201103L || __cplusplus == 201402L
-# if defined(__INTEL_COMPILER) || defined(__PGI)
-# define FMT_FALLTHROUGH
-# elif defined(__clang__)
-# define FMT_FALLTHROUGH [[clang::fallthrough]]
-# elif FMT_GCC_VERSION >= 700 && \
- (!defined(__EDG_VERSION__) || __EDG_VERSION__ >= 520)
-# define FMT_FALLTHROUGH [[gnu::fallthrough]]
-# else
-# define FMT_FALLTHROUGH
-# endif
-#elif FMT_HAS_CPP17_ATTRIBUTE(fallthrough)
+#if FMT_HAS_CPP17_ATTRIBUTE(fallthrough)
# define FMT_FALLTHROUGH [[fallthrough]]
+#elif defined(__clang__)
+# define FMT_FALLTHROUGH [[clang::fallthrough]]
+#elif FMT_GCC_VERSION >= 700 && \
+ (!defined(__EDG_VERSION__) || __EDG_VERSION__ >= 520)
+# define FMT_FALLTHROUGH [[gnu::fallthrough]]
#else
# define FMT_FALLTHROUGH
#endif
# endif
#endif
-#ifndef FMT_DEPRECATED
-# if FMT_HAS_CPP14_ATTRIBUTE(deprecated) || FMT_MSC_VER >= 1900
-# define FMT_DEPRECATED [[deprecated]]
-# else
-# if (defined(__GNUC__) && !defined(__LCC__)) || defined(__clang__)
-# define FMT_DEPRECATED __attribute__((deprecated))
-# elif FMT_MSC_VER
-# define FMT_DEPRECATED __declspec(deprecated)
-# else
-# define FMT_DEPRECATED /* deprecated */
-# endif
-# endif
+// An inline std::forward replacement.
+#define FMT_FORWARD(...) static_cast<decltype(__VA_ARGS__)&&>(__VA_ARGS__)
+
+#ifdef _MSC_VER
+# define FMT_UNCHECKED_ITERATOR(It) \
+ using _Unchecked_type = It // Mark iterator as checked.
+#else
+# define FMT_UNCHECKED_ITERATOR(It) using unchecked_type = It
#endif
#ifndef FMT_BEGIN_NAMESPACE
# define FMT_BEGIN_NAMESPACE \
namespace fmt { \
- inline namespace v8 {
+ inline namespace v9 {
# define FMT_END_NAMESPACE \
} \
}
#endif
// libc++ supports string_view in pre-c++17.
-#if (FMT_HAS_INCLUDE(<string_view>) && \
- (__cplusplus > 201402L || defined(_LIBCPP_VERSION))) || \
- (defined(_MSVC_LANG) && _MSVC_LANG > 201402L && _MSC_VER >= 1910)
+#if FMT_HAS_INCLUDE(<string_view>) && \
+ (FMT_CPLUSPLUS >= 201703L || defined(_LIBCPP_VERSION))
# include <string_view>
# define FMT_USE_STRING_VIEW
-#elif FMT_HAS_INCLUDE("experimental/string_view") && __cplusplus >= 201402L
+#elif FMT_HAS_INCLUDE("experimental/string_view") && FMT_CPLUSPLUS >= 201402L
# include <experimental/string_view>
# define FMT_USE_EXPERIMENTAL_STRING_VIEW
#endif
#ifndef FMT_UNICODE
-# define FMT_UNICODE !FMT_MSC_VER
+# define FMT_UNICODE !FMT_MSC_VERSION
#endif
#ifndef FMT_CONSTEVAL
-# if ((FMT_GCC_VERSION >= 1000 || FMT_CLANG_VERSION >= 1101) && \
- __cplusplus > 201703L && !defined(__apple_build_version__)) || \
- (defined(__cpp_consteval) && \
- (!FMT_MSC_VER || _MSC_FULL_VER >= 193030704))
+# if ((FMT_GCC_VERSION >= 1000 || FMT_CLANG_VERSION >= 1101) && \
+ FMT_CPLUSPLUS >= 202002L && !defined(__apple_build_version__)) || \
+ (defined(__cpp_consteval) && \
+ (!FMT_MSC_VERSION || _MSC_FULL_VER >= 193030704))
// consteval is broken in MSVC before VS2022 and Apple clang 13.
# define FMT_CONSTEVAL consteval
# define FMT_HAS_CONSTEVAL
# endif
#endif
-#ifndef FMT_USE_NONTYPE_TEMPLATE_PARAMETERS
-# if defined(__cpp_nontype_template_args) && \
- ((FMT_GCC_VERSION >= 903 && __cplusplus >= 201709L) || \
- __cpp_nontype_template_args >= 201911L)
-# define FMT_USE_NONTYPE_TEMPLATE_PARAMETERS 1
+#ifndef FMT_USE_NONTYPE_TEMPLATE_ARGS
+# if defined(__cpp_nontype_template_args) && \
+ ((FMT_GCC_VERSION >= 903 && FMT_CPLUSPLUS >= 201709L) || \
+ __cpp_nontype_template_args >= 201911L) && \
+ !defined(__NVCOMPILER)
+# define FMT_USE_NONTYPE_TEMPLATE_ARGS 1
# else
-# define FMT_USE_NONTYPE_TEMPLATE_PARAMETERS 0
+# define FMT_USE_NONTYPE_TEMPLATE_ARGS 0
# endif
#endif
// Enable minimal optimizations for more compact code in debug mode.
FMT_GCC_PRAGMA("GCC push_options")
-#ifndef __OPTIMIZE__
+#if !defined(__OPTIMIZE__) && !defined(__NVCOMPILER)
FMT_GCC_PRAGMA("GCC optimize(\"Og\")")
#endif
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 underlying_t = typename std::underlying_type<T>::type;
+
+template <typename...> struct disjunction : std::false_type {};
+template <typename P> struct disjunction<P> : P {};
+template <typename P1, typename... Pn>
+struct disjunction<P1, Pn...>
+ : conditional_t<bool(P1::value), P1, disjunction<Pn...>> {};
+
+template <typename...> struct conjunction : std::true_type {};
+template <typename P> struct conjunction<P> : P {};
+template <typename P1, typename... Pn>
+struct conjunction<P1, Pn...>
+ : conditional_t<bool(P1::value), conjunction<Pn...>, P1> {};
struct monostate {
constexpr monostate() {}
FMT_BEGIN_DETAIL_NAMESPACE
-// Suppress "unused variable" warnings with the method described in
+// Suppresses "unused variable" warnings with the method described in
// https://herbsutter.com/2009/10/18/mailbag-shutting-up-compiler-warnings/.
// (void)var does not work on many Intel compilers.
template <typename... T> FMT_CONSTEXPR void ignore_unused(const T&...) {}
-constexpr FMT_INLINE auto is_constant_evaluated(bool default_value = false)
- FMT_NOEXCEPT -> bool {
+constexpr FMT_INLINE auto is_constant_evaluated(
+ bool default_value = false) noexcept -> bool {
#ifdef __cpp_lib_is_constant_evaluated
ignore_unused(default_value);
return std::is_constant_evaluated();
#endif
}
-// A function to suppress "conditional expression is constant" warnings.
+// Suppresses "conditional expression is constant" warnings.
template <typename T> constexpr FMT_INLINE auto const_check(T value) -> T {
return value;
}
#ifndef FMT_ASSERT
# ifdef NDEBUG
-// FMT_ASSERT is not empty to avoid -Werror=empty-body.
+// FMT_ASSERT is not empty to avoid -Wempty-body.
# define FMT_ASSERT(condition, message) \
::fmt::detail::ignore_unused((condition), (message))
# else
# endif
#endif
-#ifdef __cpp_lib_byte
-using byte = std::byte;
-#else
-enum class byte : unsigned char {};
-#endif
-
#if defined(FMT_USE_STRING_VIEW)
template <typename Char> using std_string_view = std::basic_string_view<Char>;
#elif defined(FMT_USE_EXPERIMENTAL_STRING_VIEW)
#ifdef FMT_USE_INT128
// Do nothing.
-#elif defined(__SIZEOF_INT128__) && !FMT_NVCC && \
- !(FMT_CLANG_VERSION && FMT_MSC_VER)
+#elif defined(__SIZEOF_INT128__) && !defined(__NVCC__) && \
+ !(FMT_CLANG_VERSION && FMT_MSC_VERSION)
# define FMT_USE_INT128 1
-using int128_t = __int128_t;
-using uint128_t = __uint128_t;
+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;
}
# define FMT_USE_INT128 0
#endif
#if !FMT_USE_INT128
-enum class int128_t {};
-enum class uint128_t {};
+enum class int128_opt {};
+enum class uint128_opt {};
// Reduce template instantiations.
-template <typename T> inline auto convert_for_visit(T) -> monostate {
- return {};
-}
+template <typename T> auto convert_for_visit(T) -> monostate { return {}; }
#endif
// Casts a nonnegative integer to unsigned.
template <typename Int>
FMT_CONSTEXPR auto to_unsigned(Int value) ->
typename std::make_unsigned<Int>::type {
- FMT_ASSERT(value >= 0, "negative value");
+ FMT_ASSERT(std::is_unsigned<Int>::value || value >= 0, "negative value");
return static_cast<typename std::make_unsigned<Int>::type>(value);
}
FMT_MSC_WARNING(suppress : 4566) constexpr unsigned char micro[] = "\u00B5";
constexpr auto is_utf8() -> bool {
- // Avoid buggy sign extensions in MSVC's constant evaluation mode.
- // https://developercommunity.visualstudio.com/t/C-difference-in-behavior-for-unsigned/1233612
+ // Avoid buggy sign extensions in MSVC's constant evaluation mode (#2297).
using uchar = unsigned char;
return FMT_UNICODE || (sizeof(micro) == 3 && uchar(micro[0]) == 0xC2 &&
uchar(micro[1]) == 0xB5);
using value_type = Char;
using iterator = const Char*;
- constexpr basic_string_view() FMT_NOEXCEPT : data_(nullptr), size_(0) {}
+ constexpr basic_string_view() noexcept : data_(nullptr), size_(0) {}
/** Constructs a string reference object from a C string and a size. */
- constexpr basic_string_view(const Char* s, size_t count) FMT_NOEXCEPT
- : data_(s),
- size_(count) {}
+ constexpr basic_string_view(const Char* s, size_t count) noexcept
+ : data_(s), size_(count) {}
/**
\rst
/** Constructs a string reference from a ``std::basic_string`` object. */
template <typename Traits, typename Alloc>
FMT_CONSTEXPR basic_string_view(
- const std::basic_string<Char, Traits, Alloc>& s) FMT_NOEXCEPT
- : data_(s.data()),
- size_(s.size()) {}
+ const std::basic_string<Char, Traits, Alloc>& s) noexcept
+ : data_(s.data()), size_(s.size()) {}
template <typename S, FMT_ENABLE_IF(std::is_same<
S, detail::std_string_view<Char>>::value)>
- FMT_CONSTEXPR basic_string_view(S s) FMT_NOEXCEPT : data_(s.data()),
- size_(s.size()) {}
+ FMT_CONSTEXPR basic_string_view(S s) noexcept
+ : data_(s.data()), size_(s.size()) {}
/** Returns a pointer to the string data. */
- constexpr auto data() const FMT_NOEXCEPT -> const Char* { return data_; }
+ constexpr auto data() const noexcept -> const Char* { return data_; }
/** Returns the string size. */
- constexpr auto size() const FMT_NOEXCEPT -> size_t { return size_; }
+ constexpr auto size() const noexcept -> size_t { return size_; }
- constexpr auto begin() const FMT_NOEXCEPT -> iterator { return data_; }
- constexpr auto end() const FMT_NOEXCEPT -> iterator { return data_ + size_; }
+ constexpr auto begin() const noexcept -> iterator { return data_; }
+ constexpr auto end() const noexcept -> iterator { return data_ + size_; }
- constexpr auto operator[](size_t pos) const FMT_NOEXCEPT -> const Char& {
+ constexpr auto operator[](size_t pos) const noexcept -> const Char& {
return data_[pos];
}
- FMT_CONSTEXPR void remove_prefix(size_t n) FMT_NOEXCEPT {
+ FMT_CONSTEXPR void remove_prefix(size_t n) noexcept {
data_ += n;
size_ -= n;
}
template <typename T> struct is_char : std::false_type {};
template <> struct is_char<char> : std::true_type {};
+FMT_BEGIN_DETAIL_NAMESPACE
+
+// A base class for compile-time strings.
+struct compile_string {};
+
+template <typename S>
+struct is_compile_string : std::is_base_of<compile_string, S> {};
+
// Returns a string view of `s`.
template <typename Char, FMT_ENABLE_IF(is_char<Char>::value)>
FMT_INLINE auto to_string_view(const Char* s) -> basic_string_view<Char> {
return s;
}
template <typename Char,
- FMT_ENABLE_IF(!std::is_empty<detail::std_string_view<Char>>::value)>
-inline auto to_string_view(detail::std_string_view<Char> s)
- -> basic_string_view<Char> {
+ FMT_ENABLE_IF(!std::is_empty<std_string_view<Char>>::value)>
+inline auto to_string_view(std_string_view<Char> s) -> basic_string_view<Char> {
return s;
}
-
-// A base class for compile-time strings. It is defined in the fmt namespace to
-// make formatting functions visible via ADL, e.g. format(FMT_STRING("{}"), 42).
-struct compile_string {};
-
-template <typename S>
-struct is_compile_string : std::is_base_of<compile_string, S> {};
-
template <typename S, FMT_ENABLE_IF(is_compile_string<S>::value)>
constexpr auto to_string_view(const S& s)
-> basic_string_view<typename S::char_type> {
return basic_string_view<typename S::char_type>(s);
}
-
-FMT_BEGIN_DETAIL_NAMESPACE
-
void to_string_view(...);
-using fmt::to_string_view;
// Specifies whether S is a string type convertible to fmt::basic_string_view.
// It should be a constexpr function but MSVC 2017 fails to compile it in
// enable_if and MSVC 2015 fails to compile it as an alias template.
+// ADL invocation of to_string_view is DEPRECATED!
template <typename S>
struct is_string : std::is_class<decltype(to_string_view(std::declval<S>()))> {
};
using type = typename result::value_type;
};
-// 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_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
+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 bool is_integral_type(type t) {
+ return t > type::none_type && t <= type::last_integer_type;
+}
+
+constexpr bool is_arithmetic_type(type t) {
+ return t > type::none_type && t <= type::last_numeric_type;
}
-template <typename..., typename S, FMT_ENABLE_IF(is_compile_string<S>::value)>
-void check_format_string(S);
FMT_NORETURN FMT_API void throw_format_error(const char* message);
constexpr error_handler(const error_handler&) = default;
// This function is intentionally not constexpr to give a compile-time error.
- FMT_NORETURN FMT_API void on_error(const char* message);
+ FMT_NORETURN void on_error(const char* message) {
+ throw_format_error(message);
+ }
};
FMT_END_DETAIL_NAMESPACE
basic_string_view<Char> format_str_;
int next_arg_id_;
+ FMT_CONSTEXPR void do_check_arg_id(int id);
+
public:
using char_type = Char;
using iterator = typename basic_string_view<Char>::iterator;
Returns an iterator to the beginning of the format string range being
parsed.
*/
- constexpr auto begin() const FMT_NOEXCEPT -> iterator {
+ constexpr auto begin() const noexcept -> iterator {
return format_str_.begin();
}
/**
Returns an iterator past the end of the format string range being parsed.
*/
- constexpr auto end() const FMT_NOEXCEPT -> iterator {
- return format_str_.end();
- }
+ constexpr auto end() const noexcept -> iterator { return format_str_.end(); }
/** Advances the begin iterator to ``it``. */
FMT_CONSTEXPR void advance_to(iterator it) {
the next argument index and switches to the automatic indexing.
*/
FMT_CONSTEXPR auto next_arg_id() -> int {
- // Don't check if the argument id is valid to avoid overhead and because it
- // will be checked during formatting anyway.
- if (next_arg_id_ >= 0) return next_arg_id_++;
- on_error("cannot switch from manual to automatic argument indexing");
- return 0;
+ if (next_arg_id_ < 0) {
+ on_error("cannot switch from manual to automatic argument indexing");
+ return 0;
+ }
+ int id = next_arg_id_++;
+ do_check_arg_id(id);
+ return id;
}
/**
Reports an error if using the automatic argument indexing; otherwise
switches to the manual indexing.
*/
- FMT_CONSTEXPR void check_arg_id(int) {
- if (next_arg_id_ > 0)
+ FMT_CONSTEXPR void check_arg_id(int id) {
+ if (next_arg_id_ > 0) {
on_error("cannot switch from automatic to manual argument indexing");
- else
- next_arg_id_ = -1;
+ return;
+ }
+ next_arg_id_ = -1;
+ do_check_arg_id(id);
}
-
FMT_CONSTEXPR void check_arg_id(basic_string_view<Char>) {}
+ FMT_CONSTEXPR void check_dynamic_spec(int arg_id);
FMT_CONSTEXPR void on_error(const char* message) {
ErrorHandler::on_error(message);
using format_parse_context = basic_format_parse_context<char>;
+FMT_BEGIN_DETAIL_NAMESPACE
+// A parse context with extra data used only in compile-time checks.
+template <typename Char, typename ErrorHandler = detail::error_handler>
+class compile_parse_context
+ : public basic_format_parse_context<Char, ErrorHandler> {
+ private:
+ int num_args_;
+ const type* types_;
+ using base = basic_format_parse_context<Char, ErrorHandler>;
+
+ public:
+ explicit FMT_CONSTEXPR compile_parse_context(
+ basic_string_view<Char> format_str, int num_args, const type* types,
+ ErrorHandler eh = {}, int next_arg_id = 0)
+ : base(format_str, eh, 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]; }
+
+ FMT_CONSTEXPR auto next_arg_id() -> int {
+ int id = base::next_arg_id();
+ if (id >= num_args_) this->on_error("argument not found");
+ return id;
+ }
+
+ FMT_CONSTEXPR void check_arg_id(int id) {
+ base::check_arg_id(id);
+ if (id >= num_args_) this->on_error("argument not found");
+ }
+ using base::check_arg_id;
+
+ FMT_CONSTEXPR void check_dynamic_spec(int arg_id) {
+ if (arg_id < num_args_ && types_ && !is_integral_type(types_[arg_id]))
+ this->on_error("width/precision is not integer");
+ }
+};
+FMT_END_DETAIL_NAMESPACE
+
+template <typename Char, typename ErrorHandler>
+FMT_CONSTEXPR void
+basic_format_parse_context<Char, ErrorHandler>::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 >= 1200) {
+ using context = detail::compile_parse_context<Char, ErrorHandler>;
+ if (id >= static_cast<context*>(this)->num_args())
+ on_error("argument not found");
+ }
+}
+
+template <typename Char, typename ErrorHandler>
+FMT_CONSTEXPR void
+basic_format_parse_context<Char, ErrorHandler>::check_dynamic_spec(int arg_id) {
+ if (detail::is_constant_evaluated()) {
+ using context = detail::compile_parse_context<Char, ErrorHandler>;
+ static_cast<context*>(this)->check_dynamic_spec(arg_id);
+ }
+}
+
template <typename Context> class basic_format_arg;
template <typename Context> class basic_format_args;
template <typename Context> class dynamic_format_arg_store;
template <typename Container>
inline auto get_container(std::back_insert_iterator<Container> it)
-> Container& {
- using bi_iterator = std::back_insert_iterator<Container>;
- struct accessor : bi_iterator {
- accessor(bi_iterator iter) : bi_iterator(iter) {}
- using bi_iterator::container;
+ using base = std::back_insert_iterator<Container>;
+ struct accessor : base {
+ accessor(base b) : base(b) {}
+ using base::container;
};
return *accessor(it).container;
}
protected:
// Don't initialize ptr_ since it is not accessed to save a few cycles.
FMT_MSC_WARNING(suppress : 26495)
- buffer(size_t sz) FMT_NOEXCEPT : size_(sz), capacity_(sz) {}
+ buffer(size_t sz) noexcept : size_(sz), capacity_(sz) {}
- FMT_CONSTEXPR20 buffer(T* p = nullptr, size_t sz = 0,
- size_t cap = 0) FMT_NOEXCEPT : ptr_(p),
- size_(sz),
- capacity_(cap) {}
+ FMT_CONSTEXPR20 buffer(T* p = nullptr, size_t sz = 0, size_t cap = 0) noexcept
+ : ptr_(p), size_(sz), capacity_(cap) {}
FMT_CONSTEXPR20 ~buffer() = default;
buffer(buffer&&) = default;
/** Sets the buffer data and capacity. */
- FMT_CONSTEXPR void set(T* buf_data, size_t buf_capacity) FMT_NOEXCEPT {
+ FMT_CONSTEXPR void set(T* buf_data, size_t buf_capacity) noexcept {
ptr_ = buf_data;
capacity_ = buf_capacity;
}
buffer(const buffer&) = delete;
void operator=(const buffer&) = delete;
- auto begin() FMT_NOEXCEPT -> T* { return ptr_; }
- auto end() FMT_NOEXCEPT -> T* { return ptr_ + size_; }
+ auto begin() noexcept -> T* { return ptr_; }
+ auto end() noexcept -> T* { return ptr_ + size_; }
- auto begin() const FMT_NOEXCEPT -> const T* { return ptr_; }
- auto end() const FMT_NOEXCEPT -> const 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 FMT_NOEXCEPT -> size_t { return size_; }
+ constexpr auto size() const noexcept -> size_t { return size_; }
/** Returns the capacity of this buffer. */
- constexpr auto capacity() const FMT_NOEXCEPT -> size_t { return capacity_; }
+ constexpr auto capacity() const noexcept -> size_t { return capacity_; }
/** Returns a pointer to the buffer data. */
- FMT_CONSTEXPR auto data() FMT_NOEXCEPT -> T* { return ptr_; }
+ FMT_CONSTEXPR auto data() noexcept -> T* { return ptr_; }
/** Returns a pointer to the buffer data. */
- FMT_CONSTEXPR auto data() const FMT_NOEXCEPT -> const T* { return ptr_; }
+ FMT_CONSTEXPR auto data() const noexcept -> const T* { return ptr_; }
/** Clears this buffer. */
void clear() { size_ = 0; }
/** Appends data to the end of the buffer. */
template <typename U> void append(const U* begin, const U* end);
- template <typename I> FMT_CONSTEXPR auto operator[](I index) -> T& {
+ template <typename Idx> FMT_CONSTEXPR auto operator[](Idx index) -> T& {
return ptr_[index];
}
- template <typename I>
- FMT_CONSTEXPR auto operator[](I index) const -> const T& {
+ template <typename Idx>
+ FMT_CONSTEXPR auto operator[](Idx index) const -> const T& {
return ptr_[index];
}
};
: buffer<typename Container::value_type>(c.size()), container_(c) {}
explicit iterator_buffer(std::back_insert_iterator<Container> out, size_t = 0)
: iterator_buffer(get_container(out)) {}
+
auto out() -> std::back_insert_iterator<Container> {
return std::back_inserter(container_);
}
// Specifies if T has an enabled fallback_formatter specialization.
template <typename T, typename Char>
using has_fallback_formatter =
+#ifdef FMT_DEPRECATED_OSTREAM
std::is_constructible<fallback_formatter<T, Char>>;
+#else
+ std::false_type;
+#endif
struct view {};
return count<is_statically_named_arg<Args>::value...>();
}
-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_t, int128_type);
-FMT_TYPE_CONSTANT(uint128_t, 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 bool is_integral_type(type t) {
- return t > type::none_type && t <= type::last_integer_type;
-}
-
-constexpr bool is_arithmetic_type(type t) {
- return t > type::none_type && t <= type::last_numeric_type;
-}
-
struct unformattable {};
struct unformattable_char : unformattable {};
struct unformattable_const : unformattable {};
unsigned uint_value;
long long long_long_value;
unsigned long long ulong_long_value;
- int128_t int128_value;
- uint128_t uint128_value;
+ int128_opt int128_value;
+ uint128_opt uint128_value;
bool bool_value;
char_type char_value;
float float_value;
constexpr FMT_INLINE value(unsigned val) : uint_value(val) {}
constexpr FMT_INLINE value(long long val) : long_long_value(val) {}
constexpr FMT_INLINE value(unsigned long long val) : ulong_long_value(val) {}
- FMT_INLINE value(int128_t val) : int128_value(val) {}
- FMT_INLINE value(uint128_t val) : uint128_value(val) {}
+ FMT_INLINE value(int128_opt val) : int128_value(val) {}
+ FMT_INLINE value(uint128_opt val) : uint128_value(val) {}
constexpr FMT_INLINE value(float val) : float_value(val) {}
constexpr FMT_INLINE value(double val) : double_value(val) {}
FMT_INLINE value(long double val) : long_double_value(val) {}
};
template <typename Context, typename T>
-FMT_CONSTEXPR auto make_arg(const T& value) -> basic_format_arg<Context>;
+FMT_CONSTEXPR auto make_arg(T&& value) -> basic_format_arg<Context>;
// 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.
using long_type = conditional_t<long_short, int, long long>;
using ulong_type = conditional_t<long_short, unsigned, unsigned long long>;
+#ifdef __cpp_lib_byte
+inline auto format_as(std::byte b) -> unsigned char {
+ return static_cast<unsigned char>(b);
+}
+#endif
+
+template <typename T> struct has_format_as {
+ template <typename U, typename V = decltype(format_as(U())),
+ FMT_ENABLE_IF(std::is_enum<U>::value&& std::is_integral<V>::value)>
+ static auto check(U*) -> std::true_type;
+ static auto check(...) -> std::false_type;
+
+ enum { value = decltype(check(static_cast<T*>(nullptr)))::value };
+};
+
// 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.
-> unsigned long long {
return val;
}
- FMT_CONSTEXPR FMT_INLINE auto map(int128_t val) -> int128_t { return val; }
- FMT_CONSTEXPR FMT_INLINE auto map(uint128_t val) -> uint128_t { return val; }
+ FMT_CONSTEXPR FMT_INLINE auto map(int128_opt val) -> int128_opt {
+ return val;
+ }
+ FMT_CONSTEXPR FMT_INLINE auto map(uint128_opt val) -> uint128_opt {
+ return val;
+ }
FMT_CONSTEXPR FMT_INLINE auto map(bool val) -> bool { return val; }
template <typename T, FMT_ENABLE_IF(std::is_same<T, char>::value ||
}
template <typename T,
FMT_ENABLE_IF(
- std::is_constructible<basic_string_view<char_type>, T>::value &&
+ std::is_convertible<T, basic_string_view<char_type>>::value &&
!is_string<T>::value && !has_formatter<T, Context>::value &&
!has_fallback_formatter<T, char_type>::value)>
FMT_CONSTEXPR FMT_INLINE auto map(const T& val)
-> basic_string_view<char_type> {
return basic_string_view<char_type>(val);
}
- template <
- typename T,
- FMT_ENABLE_IF(
- std::is_constructible<std_string_view<char_type>, T>::value &&
- !std::is_constructible<basic_string_view<char_type>, T>::value &&
- !is_string<T>::value && !has_formatter<T, Context>::value &&
- !has_fallback_formatter<T, char_type>::value)>
+ template <typename T,
+ FMT_ENABLE_IF(
+ std::is_convertible<T, std_string_view<char_type>>::value &&
+ !std::is_convertible<T, basic_string_view<char_type>>::value &&
+ !is_string<T>::value && !has_formatter<T, Context>::value &&
+ !has_fallback_formatter<T, char_type>::value)>
FMT_CONSTEXPR FMT_INLINE auto map(const T& val)
-> basic_string_view<char_type> {
return std_string_view<char_type>(val);
}
- using cstring_result = conditional_t<std::is_same<char_type, char>::value,
- const char*, unformattable_pointer>;
-
- FMT_DEPRECATED FMT_CONSTEXPR FMT_INLINE auto map(const signed char* val)
- -> cstring_result {
- return map(reinterpret_cast<const char*>(val));
- }
- FMT_DEPRECATED FMT_CONSTEXPR FMT_INLINE auto map(const unsigned char* val)
- -> cstring_result {
- return map(reinterpret_cast<const char*>(val));
- }
- FMT_DEPRECATED FMT_CONSTEXPR FMT_INLINE auto map(signed char* val)
- -> cstring_result {
- return map(reinterpret_cast<const char*>(val));
- }
- FMT_DEPRECATED FMT_CONSTEXPR FMT_INLINE auto map(unsigned char* val)
- -> cstring_result {
- return map(reinterpret_cast<const char*>(val));
- }
-
FMT_CONSTEXPR FMT_INLINE auto map(void* val) -> const void* { return val; }
FMT_CONSTEXPR FMT_INLINE auto map(const void* val) -> const void* {
return val;
template <
typename T,
FMT_ENABLE_IF(
- std::is_member_pointer<T>::value ||
+ std::is_pointer<T>::value || std::is_member_pointer<T>::value ||
std::is_function<typename std::remove_pointer<T>::type>::value ||
(std::is_convertible<const T&, const void*>::value &&
- !std::is_convertible<const T&, const char_type*>::value))>
+ !std::is_convertible<const T&, const char_type*>::value &&
+ !has_formatter<T, Context>::value))>
FMT_CONSTEXPR auto map(const T&) -> unformattable_pointer {
return {};
}
template <typename T,
FMT_ENABLE_IF(
std::is_enum<T>::value&& std::is_convertible<T, int>::value &&
- !has_formatter<T, Context>::value &&
+ !has_format_as<T>::value && !has_formatter<T, Context>::value &&
!has_fallback_formatter<T, char_type>::value)>
- FMT_CONSTEXPR FMT_INLINE auto map(const T& val)
+ FMT_DEPRECATED FMT_CONSTEXPR FMT_INLINE auto map(const T& val)
-> decltype(std::declval<arg_mapper>().map(
- static_cast<typename std::underlying_type<T>::type>(val))) {
- return map(static_cast<typename std::underlying_type<T>::type>(val));
+ static_cast<underlying_t<T>>(val))) {
+ return map(static_cast<underlying_t<T>>(val));
}
- FMT_CONSTEXPR FMT_INLINE auto map(detail::byte val) -> unsigned {
- return map(static_cast<unsigned char>(val));
+ template <typename T, FMT_ENABLE_IF(has_format_as<T>::value &&
+ !has_formatter<T, Context>::value)>
+ FMT_CONSTEXPR FMT_INLINE auto map(const T& val)
+ -> decltype(std::declval<arg_mapper>().map(format_as(T()))) {
+ return map(format_as(val));
}
template <typename T, typename U = remove_cvref_t<T>>
!std::is_const<remove_reference_t<T>>::value ||
has_fallback_formatter<U, char_type>::value> {};
-#if FMT_MSC_VER != 0 && FMT_MSC_VER < 1910
- // Workaround a bug in MSVC.
+#if (FMT_MSC_VERSION != 0 && FMT_MSC_VERSION < 1910) || \
+ FMT_ICC_VERSION != 0 || defined(__NVCC__)
+ // Workaround a bug in MSVC and Intel (Issue 2746).
template <typename T> FMT_CONSTEXPR FMT_INLINE auto do_map(T&& val) -> T& {
return val;
}
template <typename T, typename U = remove_cvref_t<T>,
FMT_ENABLE_IF(!is_string<U>::value && !is_char<U>::value &&
!std::is_array<U>::value &&
+ !std::is_pointer<U>::value &&
+ !has_format_as<U>::value &&
(has_formatter<U, Context>::value ||
has_fallback_formatter<U, char_type>::value))>
FMT_CONSTEXPR FMT_INLINE auto map(T&& val)
public:
using std::back_insert_iterator<detail::buffer<char>>::back_insert_iterator;
- appender(base it) FMT_NOEXCEPT : base(it) {}
- using _Unchecked_type = appender; // Mark iterator as checked.
+ appender(base it) noexcept : base(it) {}
+ FMT_UNCHECKED_ITERATOR(appender);
- auto operator++() FMT_NOEXCEPT -> appender& { return *this; }
-
- auto operator++(int) FMT_NOEXCEPT -> appender { return *this; }
+ auto operator++() noexcept -> appender& { return *this; }
+ auto operator++(int) noexcept -> appender { return *this; }
};
// A formatting argument. It is a trivially copyable/constructible type to
detail::type type_;
template <typename ContextType, typename T>
- friend FMT_CONSTEXPR auto detail::make_arg(const T& value)
+ friend FMT_CONSTEXPR auto detail::make_arg(T&& value)
-> basic_format_arg<ContextType>;
template <typename Visitor, typename Ctx>
constexpr basic_format_arg() : type_(detail::type::none_type) {}
- constexpr explicit operator bool() const FMT_NOEXCEPT {
+ constexpr explicit operator bool() const noexcept {
return type_ != detail::type::none_type;
}
return out;
}
+template <typename Char, typename R, typename OutputIt>
+FMT_CONSTEXPR auto copy_str(R&& rng, OutputIt out) -> OutputIt {
+ return detail::copy_str<Char>(rng.begin(), rng.end(), out);
+}
+
#if FMT_GCC_VERSION && FMT_GCC_VERSION < 500
// A workaround for gcc 4.8 to make void_t work in a SFINAE context.
template <typename... Ts> struct void_t_impl { using type = void; };
template <>
struct is_contiguous_back_insert_iterator<appender> : std::true_type {};
-// A type-erased reference to an std::locale to avoid heavy <locale> include.
+// 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.
constexpr locale_ref() : locale_(nullptr) {}
template <typename Locale> explicit locale_ref(const Locale& loc);
- explicit operator bool() const FMT_NOEXCEPT { return locale_ != nullptr; }
+ explicit operator bool() const noexcept { return locale_ != nullptr; }
template <typename Locale> auto get() const -> Locale;
};
}
template <typename Context, typename T>
-FMT_CONSTEXPR auto make_arg(const T& value) -> basic_format_arg<Context> {
- basic_format_arg<Context> arg;
- arg.type_ = mapped_type_constant<T, Context>::value;
- arg.value_ = arg_mapper<Context>().map(value);
- return arg;
-}
-
-// The type template parameter is there to avoid an ODR violation when using
-// a fallback formatter in one translation unit and an implicit conversion in
-// another (not recommended).
-template <bool IS_PACKED, typename Context, type, typename T,
- FMT_ENABLE_IF(IS_PACKED)>
-FMT_CONSTEXPR FMT_INLINE auto make_arg(T&& val) -> value<Context> {
- const auto& arg = arg_mapper<Context>().map(std::forward<T>(val));
+FMT_CONSTEXPR FMT_INLINE auto make_value(T&& val) -> value<Context> {
+ const auto& arg = arg_mapper<Context>().map(FMT_FORWARD(val));
constexpr bool formattable_char =
!std::is_same<decltype(arg), const unformattable_char&>::value;
return {arg};
}
+template <typename Context, typename T>
+FMT_CONSTEXPR auto make_arg(T&& value) -> basic_format_arg<Context> {
+ basic_format_arg<Context> arg;
+ arg.type_ = mapped_type_constant<T, Context>::value;
+ arg.value_ = make_value<Context>(value);
+ return arg;
+}
+
+// The type template parameter is there to avoid an ODR violation when using
+// a fallback formatter in one translation unit and an implicit conversion in
+// another (not recommended).
+template <bool IS_PACKED, typename Context, type, typename T,
+ FMT_ENABLE_IF(IS_PACKED)>
+FMT_CONSTEXPR FMT_INLINE auto make_arg(T&& val) -> value<Context> {
+ return make_value<Context>(val);
+}
+
template <bool IS_PACKED, typename Context, type, typename T,
FMT_ENABLE_IF(!IS_PACKED)>
-inline auto make_arg(const T& value) -> basic_format_arg<Context> {
+FMT_CONSTEXPR inline auto make_arg(T&& value) -> basic_format_arg<Context> {
return make_arg<Context>(value);
}
FMT_END_DETAIL_NAMESPACE
data_{detail::make_arg<
is_packed, Context,
detail::mapped_type_constant<remove_cvref_t<T>, Context>::value>(
- std::forward<T>(args))...} {
+ FMT_FORWARD(args))...} {
detail::init_named_args(data_.named_args(), 0, 0, args...);
}
};
template <typename Context = format_context, typename... Args>
constexpr auto make_format_args(Args&&... args)
-> format_arg_store<Context, remove_cvref_t<Args>...> {
- return {std::forward<Args>(args)...};
+ return {FMT_FORWARD(args)...};
}
/**
// between clang and gcc on ARM (#1919).
using format_args = basic_format_args<format_context>;
-// We cannot use enum classes as bit fields because of a gcc bug
-// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=61414.
+// 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 { none, left, right, center, numeric };
+enum type FMT_ENUM_UNDERLYING_TYPE(unsigned char){none, left, right, center,
+ numeric};
}
using align_t = align::type;
namespace sign {
-enum type { none, minus, plus, space };
+enum type FMT_ENUM_UNDERLYING_TYPE(unsigned char){none, minus, plus, space};
}
using sign_t = sign::type;
general_upper, // 'G'
chr, // 'c'
string, // 's'
- pointer // 'p'
+ pointer, // 'p'
+ debug // '?'
};
// Format specifiers for built-in and string types.
FMT_CONSTEXPR auto make_arg_ref(int arg_id) -> arg_ref_type {
context_.check_arg_id(arg_id);
+ context_.check_dynamic_spec(arg_id);
return arg_ref_type(arg_id);
}
FMT_CONSTEXPR auto make_arg_ref(auto_id) -> arg_ref_type {
- return arg_ref_type(context_.next_arg_id());
+ int arg_id = context_.next_arg_id();
+ context_.check_dynamic_spec(arg_id);
+ return arg_ref_type(arg_id);
}
FMT_CONSTEXPR auto make_arg_ref(basic_string_view<char_type> arg_id)
// Converts a character to ASCII. Returns a number > 127 on conversion failure.
template <typename Char, FMT_ENABLE_IF(std::is_integral<Char>::value)>
-constexpr auto to_ascii(Char value) -> Char {
- return value;
+constexpr auto to_ascii(Char c) -> Char {
+ return c;
}
template <typename Char, FMT_ENABLE_IF(std::is_enum<Char>::value)>
-constexpr auto to_ascii(Char value) ->
- typename std::underlying_type<Char>::type {
- return value;
+constexpr auto to_ascii(Char c) -> underlying_t<Char> {
+ return c;
+}
+
+FMT_CONSTEXPR inline auto code_point_length_impl(char c) -> int {
+ return "\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>(c) >> 3];
}
template <typename Char>
FMT_CONSTEXPR auto code_point_length(const Char* begin) -> int {
if (const_check(sizeof(Char) != 1)) return 1;
- auto lengths =
- "\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";
- int len = lengths[static_cast<unsigned char>(*begin) >> 3];
+ int len = code_point_length_impl(static_cast<char>(*begin));
// Compute the pointer to the next character early so that the next
// iteration can start working on the next character. Neither Clang
FMT_ASSERT(begin != end, "");
auto align = align::none;
auto p = begin + code_point_length(begin);
- if (p >= end) p = begin;
+ if (end - p <= 0) p = begin;
for (;;) {
switch (to_ascii(*p)) {
case '<':
return presentation_type::string;
case 'p':
return presentation_type::pointer;
+ case '?':
+ return presentation_type::debug;
default:
return presentation_type::none;
}
handler.on_text(begin, end);
return;
}
- struct writer {
- FMT_CONSTEXPR void operator()(const Char* pbegin, const Char* pend) {
- if (pbegin == pend) return;
+ struct pfs_writer {
+ FMT_CONSTEXPR void operator()(const Char* from, const Char* to) {
+ if (from == to) return;
for (;;) {
const Char* p = nullptr;
- if (!find<IS_CONSTEXPR>(pbegin, pend, Char('}'), p))
- return handler_.on_text(pbegin, pend);
+ if (!find<IS_CONSTEXPR>(from, to, Char('}'), p))
+ return handler_.on_text(from, to);
++p;
- if (p == pend || *p != '}')
+ if (p == to || *p != '}')
return handler_.on_error("unmatched '}' in format string");
- handler_.on_text(pbegin, p);
- pbegin = p + 1;
+ handler_.on_text(from, p);
+ from = p + 1;
}
}
Handler& handler_;
- } write{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.
}
}
+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)>;
+};
+
template <typename T, typename ParseContext>
FMT_CONSTEXPR auto parse_format_specs(ParseContext& ctx)
-> decltype(ctx.begin()) {
using char_type = typename ParseContext::char_type;
using context = buffer_context<char_type>;
+ using stripped_type = typename strip_named_arg<T>::type;
using mapped_type = conditional_t<
mapped_type_constant<T, context>::value != type::custom_type,
- decltype(arg_mapper<context>().map(std::declval<const T&>())), T>;
+ decltype(arg_mapper<context>().map(std::declval<const T&>())),
+ stripped_type>;
auto f = conditional_t<has_formatter<mapped_type, context>::value,
formatter<mapped_type, char_type>,
- fallback_formatter<T, char_type>>();
+ fallback_formatter<stripped_type, char_type>>();
return f.parse(ctx);
}
-// A parse context with extra argument id checks. It is only used at compile
-// time because adding checks at runtime would introduce substantial overhead
-// and would be redundant since argument ids are checked when arguments are
-// retrieved anyway.
-template <typename Char, typename ErrorHandler = error_handler>
-class compile_parse_context
- : public basic_format_parse_context<Char, ErrorHandler> {
- private:
- int num_args_;
- using base = basic_format_parse_context<Char, ErrorHandler>;
-
- public:
- explicit FMT_CONSTEXPR compile_parse_context(
- basic_string_view<Char> format_str,
- int num_args = (std::numeric_limits<int>::max)(), ErrorHandler eh = {})
- : base(format_str, eh), num_args_(num_args) {}
-
- FMT_CONSTEXPR auto next_arg_id() -> int {
- int id = base::next_arg_id();
- if (id >= num_args_) this->on_error("argument not found");
- return id;
- }
-
- FMT_CONSTEXPR void check_arg_id(int id) {
- base::check_arg_id(id);
- if (id >= num_args_) this->on_error("argument not found");
- }
- using base::check_arg_id;
-};
-
template <typename ErrorHandler>
FMT_CONSTEXPR void check_int_type_spec(presentation_type type,
ErrorHandler&& eh) {
FMT_CONSTEXPR auto check_char_specs(const basic_format_specs<Char>& specs,
ErrorHandler&& eh = {}) -> bool {
if (specs.type != presentation_type::none &&
- specs.type != presentation_type::chr) {
+ specs.type != presentation_type::chr &&
+ specs.type != presentation_type::debug) {
check_int_type_spec(specs.type, eh);
return false;
}
bool upper : 1;
bool locale : 1;
bool binary32 : 1;
- bool fallback : 1;
bool showpoint : 1;
};
template <typename ErrorHandler = error_handler>
FMT_CONSTEXPR auto check_cstring_type_spec(presentation_type type,
ErrorHandler&& eh = {}) -> bool {
- if (type == presentation_type::none || type == presentation_type::string)
+ if (type == presentation_type::none || type == presentation_type::string ||
+ type == presentation_type::debug)
return true;
if (type != presentation_type::pointer) eh.on_error("invalid type specifier");
return false;
template <typename ErrorHandler = error_handler>
FMT_CONSTEXPR void check_string_type_spec(presentation_type type,
ErrorHandler&& eh = {}) {
- if (type != presentation_type::none && type != presentation_type::string)
+ if (type != presentation_type::none && type != presentation_type::string &&
+ type != presentation_type::debug)
eh.on_error("invalid type specifier");
}
FMT_CONSTEXPR void on_sign(sign_t s) {
require_numeric_argument();
if (is_integral_type(arg_type_) && arg_type_ != type::int_type &&
- arg_type_ != type::long_long_type && arg_type_ != type::char_type) {
+ arg_type_ != type::long_long_type && arg_type_ != type::int128_type &&
+ arg_type_ != type::char_type) {
this->on_error("format specifier requires signed argument");
}
Handler::on_sign(s);
constexpr int invalid_arg_index = -1;
-#if FMT_USE_NONTYPE_TEMPLATE_PARAMETERS
+#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 (detail::is_statically_named_arg<T>()) {
template <typename... Args, typename Char>
FMT_CONSTEXPR auto get_arg_index_by_name(basic_string_view<Char> name) -> int {
-#if FMT_USE_NONTYPE_TEMPLATE_PARAMETERS
+#if FMT_USE_NONTYPE_TEMPLATE_ARGS
if constexpr (sizeof...(Args) > 0)
return get_arg_index_by_name<0, Args...>(name);
#endif
template <typename Char, typename ErrorHandler, typename... Args>
class format_string_checker {
private:
+ // 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_context_type = compile_parse_context<Char, ErrorHandler>;
- enum { num_args = sizeof...(Args) };
+ static constexpr int num_args = sizeof...(Args);
// Format specifier parsing function.
using parse_func = const Char* (*)(parse_context_type&);
parse_context_type context_;
- parse_func parse_funcs_[num_args > 0 ? num_args : 1];
+ parse_func parse_funcs_[num_args > 0 ? static_cast<size_t>(num_args) : 1];
+ type types_[num_args > 0 ? static_cast<size_t>(num_args) : 1];
public:
explicit FMT_CONSTEXPR format_string_checker(
basic_string_view<Char> format_str, ErrorHandler eh)
- : context_(format_str, num_args, eh),
- parse_funcs_{&parse_format_specs<Args, parse_context_type>...} {}
+ : context_(format_str, num_args, types_, eh),
+ parse_funcs_{&parse_format_specs<Args, parse_context_type>...},
+ types_{
+ mapped_type_constant<Args,
+ basic_format_context<Char*, Char>>::value...} {
+ }
FMT_CONSTEXPR void on_text(const Char*, const Char*) {}
return context_.check_arg_id(id), id;
}
FMT_CONSTEXPR auto on_arg_id(basic_string_view<Char> id) -> int {
-#if FMT_USE_NONTYPE_TEMPLATE_PARAMETERS
+#if FMT_USE_NONTYPE_TEMPLATE_ARGS
auto index = get_arg_index_by_name<Args...>(id);
if (index == invalid_arg_index) on_error("named argument is not found");
return context_.check_arg_id(index), index;
}
};
+// 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_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,
- enable_if_t<(is_compile_string<S>::value), int>>
+ FMT_ENABLE_IF(is_compile_string<S>::value)>
void check_format_string(S format_str) {
- FMT_CONSTEXPR auto s = to_string_view(format_str);
+ FMT_CONSTEXPR auto s = basic_string_view<typename S::char_type>(format_str);
using checker = format_string_checker<typename S::char_type, error_handler,
remove_cvref_t<Args>...>;
FMT_CONSTEXPR bool invalid_format =
return it;
}
+ template <detail::type U = detail::type_constant<T, Char>::value,
+ enable_if_t<(U == detail::type::string_type ||
+ U == detail::type::cstring_type ||
+ U == detail::type::char_type),
+ int> = 0>
+ FMT_CONSTEXPR void set_debug_format() {
+ specs_.type = presentation_type::debug;
+ }
+
template <typename FormatContext>
FMT_CONSTEXPR auto format(const T& val, FormatContext& ctx) const
-> decltype(ctx.out());
};
+#define FMT_FORMAT_AS(Type, Base) \
+ template <typename Char> \
+ struct formatter<Type, Char> : formatter<Base, Char> { \
+ template <typename FormatContext> \
+ auto format(Type const& val, FormatContext& ctx) const \
+ -> decltype(ctx.out()) { \
+ return formatter<Base, Char>::format(static_cast<Base>(val), ctx); \
+ } \
+ }
+
+FMT_FORMAT_AS(signed char, int);
+FMT_FORMAT_AS(unsigned char, unsigned);
+FMT_FORMAT_AS(short, int);
+FMT_FORMAT_AS(unsigned short, unsigned);
+FMT_FORMAT_AS(long, long long);
+FMT_FORMAT_AS(unsigned long, unsigned long long);
+FMT_FORMAT_AS(Char*, const Char*);
+FMT_FORMAT_AS(std::basic_string<Char>, basic_string_view<Char>);
+FMT_FORMAT_AS(std::nullptr_t, const void*);
+FMT_FORMAT_AS(detail::std_string_view<Char>, basic_string_view<Char>);
+
template <typename Char> struct basic_runtime { basic_string_view<Char> str; };
/** A compile-time format string. */
#if FMT_GCC_VERSION && FMT_GCC_VERSION < 409
// Workaround broken conversion on older gcc.
-template <typename... Args> using format_string = string_view;
-template <typename S> auto runtime(const S& s) -> basic_string_view<char_t<S>> {
- return s;
-}
+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>...>;
fmt::print(fmt::runtime("{:d}"), "I am not a number");
\endrst
*/
-template <typename S> auto runtime(const S& s) -> basic_runtime<char_t<S>> {
- return {{s}};
-}
+inline auto runtime(string_view s) -> basic_runtime<char> { return {{s}}; }
#endif
FMT_API auto vformat(string_view fmt, format_args args) -> std::string;