#ifndef FMT_CORE_H_
#define FMT_CORE_H_
-#include <cstdio> // std::FILE
-#include <cstring>
+#include <cstddef> // std::byte
+#include <cstdio> // std::FILE
+#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 70104
+#define FMT_VERSION 90100
-#ifdef __clang__
+#if defined(__clang__) && !defined(__ibmxl__)
# define FMT_CLANG_VERSION (__clang_major__ * 100 + __clang_minor__)
#else
# define FMT_CLANG_VERSION 0
#endif
-#if defined(__GNUC__) && !defined(__clang__)
+#if defined(__GNUC__) && !defined(__clang__) && !defined(__INTEL_COMPILER) && \
+ !defined(__NVCOMPILER)
# define FMT_GCC_VERSION (__GNUC__ * 100 + __GNUC_MINOR__)
-# define FMT_GCC_PRAGMA(arg) _Pragma(arg)
#else
# define FMT_GCC_VERSION 0
-# define FMT_GCC_PRAGMA(arg)
#endif
-#if __cplusplus >= 201103L || defined(__GXX_EXPERIMENTAL_CXX0X__)
-# define FMT_HAS_GXX_CXX11 FMT_GCC_VERSION
-#else
-# define FMT_HAS_GXX_CXX11 0
+#ifndef FMT_GCC_PRAGMA
+// Workaround _Pragma bug https://gcc.gnu.org/bugzilla/show_bug.cgi?id=59884.
+# if FMT_GCC_VERSION >= 504
+# define FMT_GCC_PRAGMA(arg) _Pragma(arg)
+# else
+# define FMT_GCC_PRAGMA(arg)
+# endif
#endif
-#if defined(__INTEL_COMPILER)
+#ifdef __ICL
+# define FMT_ICC_VERSION __ICL
+#elif defined(__INTEL_COMPILER)
# define FMT_ICC_VERSION __INTEL_COMPILER
#else
# 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_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
#endif
#define FMT_HAS_CPP14_ATTRIBUTE(attribute) \
- (__cplusplus >= 201402L && FMT_HAS_CPP_ATTRIBUTE(attribute))
+ (FMT_CPLUSPLUS >= 201402L && FMT_HAS_CPP_ATTRIBUTE(attribute))
#define FMT_HAS_CPP17_ATTRIBUTE(attribute) \
- (__cplusplus >= 201703L && FMT_HAS_CPP_ATTRIBUTE(attribute))
+ (FMT_CPLUSPLUS >= 201703L && 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 >= 1910 || \
- (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
-// Check if constexpr std::char_traits<>::compare,length is supported.
+#if ((FMT_CPLUSPLUS >= 202002L) && \
+ (!defined(_GLIBCXX_RELEASE) || _GLIBCXX_RELEASE > 9)) || \
+ (FMT_CPLUSPLUS >= 201709L && FMT_GCC_VERSION >= 1002)
+# define FMT_CONSTEXPR20 constexpr
+#else
+# define FMT_CONSTEXPR20
+#endif
+
+// 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
# define FMT_CONSTEXPR_CHAR_TRAITS
#endif
-#ifndef FMT_OVERRIDE
-# if FMT_HAS_FEATURE(cxx_override_control) || \
- (FMT_GCC_VERSION >= 408 && FMT_HAS_GXX_CXX11) || FMT_MSC_VER >= 1900
-# define FMT_OVERRIDE override
-# else
-# define FMT_OVERRIDE
-# endif
-#endif
-
// 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_HAS_GXX_CXX11) || 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
-#ifndef FMT_MAYBE_UNUSED
-# if FMT_HAS_CPP17_ATTRIBUTE(maybe_unused)
-# define FMT_MAYBE_UNUSED [[maybe_unused]]
-# else
-# define FMT_MAYBE_UNUSED
-# endif
+#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
-#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]]
+#ifndef FMT_NODISCARD
+# if FMT_HAS_CPP17_ATTRIBUTE(nodiscard)
+# define FMT_NODISCARD [[nodiscard]]
# else
-# define FMT_FALLTHROUGH
+# define FMT_NODISCARD
# endif
-#elif FMT_HAS_CPP17_ATTRIBUTE(fallthrough) || \
- (defined(_MSVC_LANG) && _MSVC_LANG >= 201703L)
-# define FMT_FALLTHROUGH [[fallthrough]]
-#else
-# define FMT_FALLTHROUGH
#endif
#ifndef FMT_USE_FLOAT
# endif
#endif
-#ifndef FMT_USE_INLINE_NAMESPACES
-# if FMT_HAS_FEATURE(cxx_inline_namespaces) || FMT_GCC_VERSION >= 404 || \
- (FMT_MSC_VER >= 1900 && (!defined(_MANAGED) || !_MANAGED))
-# define FMT_USE_INLINE_NAMESPACES 1
-# else
-# define FMT_USE_INLINE_NAMESPACES 0
-# 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
-# if FMT_USE_INLINE_NAMESPACES
-# define FMT_INLINE_NAMESPACE inline namespace
-# define FMT_END_NAMESPACE \
- } \
- }
-# else
-# define FMT_INLINE_NAMESPACE namespace
-# define FMT_END_NAMESPACE \
- } \
- using namespace v7; \
- }
-# endif
# define FMT_BEGIN_NAMESPACE \
namespace fmt { \
- FMT_INLINE_NAMESPACE v7 {
+ inline namespace v9 {
+# define FMT_END_NAMESPACE \
+ } \
+ }
#endif
#ifndef FMT_MODULE_EXPORT
# define FMT_API
#endif
-#if FMT_GCC_VERSION
-# define FMT_GCC_VISIBILITY_HIDDEN __attribute__((visibility("hidden")))
-#else
-# define FMT_GCC_VISIBILITY_HIDDEN
-#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(__cpp_consteval) && \
- !FMT_MSC_VER) // consteval is broken in MSVC.
+# 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
# else
# 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
FMT_MODULE_EXPORT_BEGIN
// Implementations of enable_if_t and other metafunctions for older systems.
-template <bool B, class T = void>
+template <bool B, typename T = void>
using enable_if_t = typename std::enable_if<B, T>::type;
-template <bool B, class T, class F>
+template <bool B, typename T, typename F>
using conditional_t = typename std::conditional<B, T, F>::type;
template <bool B> using bool_constant = std::integral_constant<bool, B>;
template <typename T>
using remove_reference_t = typename std::remove_reference<T>::type;
template <typename T>
+using remove_const_t = typename std::remove_const<T>::type;
+template <typename T>
using remove_cvref_t = typename std::remove_cv<remove_reference_t<T>>::type;
template <typename T> struct type_identity { using type = T; };
template <typename T> using type_identity_t = typename type_identity<T>::type;
+template <typename T>
+using 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
-constexpr FMT_INLINE auto is_constant_evaluated() FMT_NOEXCEPT -> bool {
+// 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) noexcept -> bool {
#ifdef __cpp_lib_is_constant_evaluated
+ ignore_unused(default_value);
return std::is_constant_evaluated();
#else
- return false;
+ return default_value;
#endif
}
-// A function to suppress "conditional expression is constant" warnings.
-template <typename T> constexpr auto const_check(T value) -> T { return value; }
+// Suppresses "conditional expression is constant" warnings.
+template <typename T> constexpr FMT_INLINE auto const_check(T value) -> T {
+ return value;
+}
FMT_NORETURN FMT_API void assert_fail(const char* file, int line,
const char* message);
#ifndef FMT_ASSERT
# ifdef NDEBUG
-// FMT_ASSERT is not empty to avoid -Werror=empty-body.
-# define FMT_ASSERT(condition, message) ((void)0)
+// FMT_ASSERT is not empty to avoid -Wempty-body.
+# define FMT_ASSERT(condition, message) \
+ ::fmt::detail::ignore_unused((condition), (message))
# else
# define FMT_ASSERT(condition, message) \
((condition) /* void() fails with -Winvalid-constexpr on clang 4.0.1 */ \
#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
*/
FMT_CONSTEXPR_CHAR_TRAITS
FMT_INLINE
- basic_string_view(const Char* s) : data_(s) {
- if (detail::const_check(std::is_same<Char, char>::value &&
- !detail::is_constant_evaluated()))
- size_ = std::strlen(reinterpret_cast<const char*>(s));
- else
- size_ = std::char_traits<Char>::length(s);
- }
+ basic_string_view(const Char* s)
+ : data_(s),
+ size_(detail::const_check(std::is_same<Char, char>::value &&
+ !detail::is_constant_evaluated(true))
+ ? std::strlen(reinterpret_cast<const char*>(s))
+ : std::char_traits<Char>::length(s)) {}
/** 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 -> const Char* { return data_; }
+ constexpr auto data() const noexcept -> const Char* { return data_; }
/** Returns the string size. */
- constexpr auto size() const -> size_t { return size_; }
+ constexpr auto size() const noexcept -> size_t { return size_; }
- constexpr auto begin() const -> iterator { return data_; }
- constexpr auto end() const -> 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 -> const Char& {
+ constexpr auto operator[](size_t pos) const noexcept -> const Char& {
return data_[pos];
}
- FMT_CONSTEXPR void remove_prefix(size_t n) {
+ 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 {};
-/**
- \rst
- Returns a string view of `s`. In order to add custom string type support to
- {fmt} provide an overload of `to_string_view` for it in the same namespace as
- the type for the argument-dependent lookup to work.
+FMT_BEGIN_DETAIL_NAMESPACE
- **Example**::
+// A base class for compile-time strings.
+struct compile_string {};
- namespace my_ns {
- inline string_view to_string_view(const my_string& s) {
- return {s.data(), s.length()};
- }
- }
- std::string message = fmt::format(my_string("The answer is {}"), 42);
- \endrst
- */
+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, typename Traits, typename Alloc>
inline auto to_string_view(const std::basic_string<Char, Traits, Alloc>& s)
-> basic_string_view<Char> {
return s;
}
-
template <typename Char>
constexpr auto to_string_view(basic_string_view<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::v7::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;
}
-template <typename..., typename S, FMT_ENABLE_IF(is_compile_string<S>::value)>
-void check_format_string(S);
+
+constexpr bool is_arithmetic_type(type t) {
+ return t > type::none_type && t <= type::last_numeric_type;
+}
+
+FMT_NORETURN FMT_API void throw_format_error(const char* message);
struct error_handler {
constexpr error_handler() = default;
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
\rst
Parsing context consisting of a format string range being parsed and an
argument counter for automatic indexing.
- You can use the ```format_parse_context`` type alias for ``char`` instead.
+ You can use the ``format_parse_context`` type alias for ``char`` instead.
\endrst
*/
template <typename Char, typename ErrorHandler = detail::error_handler>
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;
FMT_BEGIN_DETAIL_NAMESPACE
+template <typename Context, typename T>
+constexpr auto has_const_formatter_impl(T*)
+ -> decltype(typename Context::template formatter_type<T>().format(
+ std::declval<const T&>(), std::declval<Context&>()),
+ true) {
+ return true;
+}
+template <typename Context>
+constexpr auto has_const_formatter_impl(...) -> bool {
+ return false;
+}
+template <typename T, typename Context>
+constexpr auto has_const_formatter() -> bool {
+ return has_const_formatter_impl<Context>(static_cast<T*>(nullptr));
+}
+
// Extracts a reference to the container from back_insert_iterator.
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;
}
return out;
}
-template <typename Char, FMT_ENABLE_IF(std::is_same<Char, char>::value)>
-FMT_CONSTEXPR auto copy_str(const Char* begin, const Char* end, Char* out)
- -> Char* {
- if (is_constant_evaluated())
- return copy_str<Char, const Char*, Char*>(begin, end, out);
+template <typename Char, typename T, typename U,
+ FMT_ENABLE_IF(
+ std::is_same<remove_const_t<T>, U>::value&& is_char<U>::value)>
+FMT_CONSTEXPR auto copy_str(T* begin, T* end, U* out) -> U* {
+ if (is_constant_evaluated()) return copy_str<Char, T*, U*>(begin, end, out);
auto size = to_unsigned(end - begin);
- memcpy(out, begin, size);
+ memcpy(out, begin, size * sizeof(U));
return out + size;
}
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) {}
- 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) {}
- ~buffer() = default;
+ FMT_CONSTEXPR20 ~buffer() = default;
buffer(buffer&&) = default;
/** Sets the buffer data and capacity. */
- 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;
}
/** Increases the buffer capacity to hold at least *capacity* elements. */
- virtual void grow(size_t capacity) = 0;
+ virtual FMT_CONSTEXPR20 void grow(size_t capacity) = 0;
public:
using value_type = T;
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. */
- auto size() const FMT_NOEXCEPT -> size_t { return size_; }
+ constexpr auto size() const noexcept -> size_t { return size_; }
/** Returns the capacity of this buffer. */
- 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. */
- auto data() FMT_NOEXCEPT -> T* { return ptr_; }
+ FMT_CONSTEXPR auto data() noexcept -> T* { return ptr_; }
/** Returns a pointer to the buffer data. */
- 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; }
// Tries resizing the buffer to contain *count* elements. If T is a POD type
// the new elements may not be initialized.
- void try_resize(size_t count) {
+ FMT_CONSTEXPR20 void try_resize(size_t count) {
try_reserve(count);
size_ = count <= capacity_ ? count : capacity_;
}
// capacity by a smaller amount than requested but guarantees there is space
// for at least one additional element either by increasing the capacity or by
// flushing the buffer if it is full.
- void try_reserve(size_t new_capacity) {
+ FMT_CONSTEXPR20 void try_reserve(size_t new_capacity) {
if (new_capacity > capacity_) grow(new_capacity);
}
- void push_back(const T& value) {
+ FMT_CONSTEXPR20 void push_back(const T& value) {
try_reserve(size_ + 1);
ptr_[size_++] = value;
}
/** Appends data to the end of the buffer. */
template <typename U> void append(const U* begin, const U* end);
- template <typename I> auto operator[](I index) -> T& { return ptr_[index]; }
- template <typename I> auto operator[](I index) const -> const T& {
+ template <typename Idx> FMT_CONSTEXPR auto operator[](Idx index) -> T& {
+ return ptr_[index];
+ }
+ template <typename Idx>
+ FMT_CONSTEXPR auto operator[](Idx index) const -> const T& {
return ptr_[index];
}
};
T data_[buffer_size];
protected:
- void grow(size_t) final FMT_OVERRIDE {
+ FMT_CONSTEXPR20 void grow(size_t) override {
if (this->size() == buffer_size) flush();
}
auto count() const -> size_t { return Traits::count() + this->size(); }
};
+template <typename T>
+class iterator_buffer<T*, T, fixed_buffer_traits> final
+ : public fixed_buffer_traits,
+ public buffer<T> {
+ private:
+ T* out_;
+ enum { buffer_size = 256 };
+ T data_[buffer_size];
+
+ protected:
+ FMT_CONSTEXPR20 void grow(size_t) override {
+ if (this->size() == this->capacity()) flush();
+ }
+
+ void flush() {
+ size_t n = this->limit(this->size());
+ if (this->data() == out_) {
+ out_ += n;
+ this->set(data_, buffer_size);
+ }
+ this->clear();
+ }
+
+ public:
+ explicit iterator_buffer(T* out, size_t n = buffer_size)
+ : fixed_buffer_traits(n), buffer<T>(out, 0, n), out_(out) {}
+ iterator_buffer(iterator_buffer&& other)
+ : fixed_buffer_traits(other),
+ buffer<T>(std::move(other)),
+ out_(other.out_) {
+ if (this->data() != out_) {
+ this->set(data_, buffer_size);
+ this->clear();
+ }
+ }
+ ~iterator_buffer() { flush(); }
+
+ auto out() -> T* {
+ flush();
+ return out_;
+ }
+ auto count() const -> size_t {
+ return fixed_buffer_traits::count() + this->size();
+ }
+};
+
template <typename T> class iterator_buffer<T*, T> final : public buffer<T> {
protected:
- void grow(size_t) final FMT_OVERRIDE {}
+ FMT_CONSTEXPR20 void grow(size_t) override {}
public:
explicit iterator_buffer(T* out, size_t = 0) : buffer<T>(out, 0, ~size_t()) {}
Container& container_;
protected:
- void grow(size_t capacity) final FMT_OVERRIDE {
+ FMT_CONSTEXPR20 void grow(size_t capacity) override {
container_.resize(capacity);
this->set(&container_[0], capacity);
}
: 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_);
}
size_t count_ = 0;
protected:
- void grow(size_t) final FMT_OVERRIDE {
+ FMT_CONSTEXPR20 void grow(size_t) override {
if (this->size() != buffer_size) return;
count_ += this->size();
this->clear();
// 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_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;
+template <typename... Args>
+constexpr auto count_statically_named_args() -> size_t {
+ return count<is_statically_named_arg<Args>::value...>();
}
-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 {};
+struct unformattable_pointer : unformattable {};
template <typename Char> struct string_value {
const Char* data;
template <typename Context> struct custom_value {
using parse_context = typename Context::parse_context_type;
- const void* value;
- void (*format)(const void* arg, parse_context& parse_ctx, Context& ctx);
+ void* value;
+ void (*format)(void* arg, parse_context& parse_ctx, Context& ctx);
};
// A formatting argument value.
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(float val) : float_value(val) {}
- FMT_INLINE value(double val) : double_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) {}
constexpr FMT_INLINE value(bool val) : bool_value(val) {}
constexpr FMT_INLINE value(char_type val) : char_value(val) {}
FMT_INLINE value(const named_arg_info<char_type>* args, size_t size)
: named_args{args, size} {}
- template <typename T> FMT_CONSTEXPR FMT_INLINE value(const T& val) {
- custom.value = &val;
+ template <typename T> FMT_CONSTEXPR FMT_INLINE value(T& val) {
+ using value_type = remove_cvref_t<T>;
+ custom.value = const_cast<value_type*>(&val);
// Get the formatter type through the context to allow different contexts
// have different extension points, e.g. `formatter<T>` for `format` and
// `printf_formatter<T>` for `printf`.
custom.format = format_custom_arg<
- T, conditional_t<has_formatter<T, Context>::value,
- typename Context::template formatter_type<T>,
- fallback_formatter<T, char_type>>>;
+ value_type,
+ conditional_t<has_formatter<value_type, Context>::value,
+ typename Context::template formatter_type<value_type>,
+ fallback_formatter<value_type, char_type>>>;
}
+ value(unformattable);
+ value(unformattable_char);
+ value(unformattable_const);
+ value(unformattable_pointer);
private:
// Formats an argument of a custom type, such as a user-defined class.
template <typename T, typename Formatter>
- static void format_custom_arg(const void* arg,
+ static void format_custom_arg(void* arg,
typename Context::parse_context_type& parse_ctx,
Context& ctx) {
- Formatter f;
+ auto f = Formatter();
parse_ctx.advance_to(f.parse(parse_ctx));
- ctx.advance_to(f.format(*static_cast<const T*>(arg), ctx));
+ using qualified_type =
+ conditional_t<has_const_formatter<T, Context>(), const T, T>;
+ ctx.advance_to(f.format(*static_cast<qualified_type*>(arg), ctx));
}
};
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>;
-struct unformattable {};
+#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.
template <typename Context> struct arg_mapper {
using char_type = typename Context::char_type;
-> 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(is_char<T>::value)>
+ template <typename T, FMT_ENABLE_IF(std::is_same<T, char>::value ||
+ std::is_same<T, char_type>::value)>
FMT_CONSTEXPR FMT_INLINE auto map(T val) -> char_type {
- static_assert(
- std::is_same<T, char>::value || std::is_same<T, char_type>::value,
- "mixing character types is disallowed");
return val;
}
+ template <typename T, enable_if_t<(std::is_same<T, wchar_t>::value ||
+#ifdef __cpp_char8_t
+ std::is_same<T, char8_t>::value ||
+#endif
+ std::is_same<T, char16_t>::value ||
+ std::is_same<T, char32_t>::value) &&
+ !std::is_same<T, char_type>::value,
+ int> = 0>
+ FMT_CONSTEXPR FMT_INLINE auto map(T) -> unformattable_char {
+ return {};
+ }
FMT_CONSTEXPR FMT_INLINE auto map(float val) -> float { return val; }
FMT_CONSTEXPR FMT_INLINE auto map(double val) -> double { return val; }
FMT_CONSTEXPR FMT_INLINE auto map(const char_type* val) -> const char_type* {
return val;
}
- template <typename T, FMT_ENABLE_IF(is_string<T>::value)>
+ template <typename T,
+ FMT_ENABLE_IF(is_string<T>::value && !std::is_pointer<T>::value &&
+ std::is_same<char_type, char_t<T>>::value)>
FMT_CONSTEXPR FMT_INLINE auto map(const T& val)
-> basic_string_view<char_type> {
- static_assert(std::is_same<char_type, char_t<T>>::value,
- "mixing character types is disallowed");
return to_string_view(val);
}
+ template <typename T,
+ FMT_ENABLE_IF(is_string<T>::value && !std::is_pointer<T>::value &&
+ !std::is_same<char_type, char_t<T>>::value)>
+ FMT_CONSTEXPR FMT_INLINE auto map(const T&) -> unformattable_char {
+ return {};
+ }
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);
}
- FMT_CONSTEXPR FMT_INLINE auto map(const signed char* val) -> const char* {
- static_assert(std::is_same<char_type, char>::value, "invalid string type");
- return reinterpret_cast<const char*>(val);
- }
- FMT_CONSTEXPR FMT_INLINE auto map(const unsigned char* val) -> const char* {
- static_assert(std::is_same<char_type, char>::value, "invalid string type");
- return reinterpret_cast<const char*>(val);
- }
- FMT_CONSTEXPR FMT_INLINE auto map(signed char* val) -> const char* {
- const auto* const_val = val;
- return map(const_val);
- }
- FMT_CONSTEXPR FMT_INLINE auto map(unsigned char* val) -> const char* {
- const auto* const_val = val;
- return map(const_val);
- }
FMT_CONSTEXPR FMT_INLINE auto map(void* val) -> const void* { return val; }
FMT_CONSTEXPR FMT_INLINE auto map(const void* val) -> const void* {
// We use SFINAE instead of a const T* parameter to avoid conflicting with
// the C array overload.
- template <typename T>
- FMT_CONSTEXPR auto map(T) -> enable_if_t<std::is_pointer<T>::value, int> {
- // Formatting of arbitrary pointers is disallowed. If you want to output
- // a pointer cast it to "void *" or "const void *". In particular, this
- // forbids formatting of "[const] volatile char *" which is printed as bool
- // by iostreams.
- static_assert(!sizeof(T), "formatting of non-void pointers is disallowed");
- return 0;
+ template <
+ typename T,
+ FMT_ENABLE_IF(
+ std::is_pointer<T>::value || std::is_member_pointer<T>::value ||
+ std::is_function<typename std::remove_pointer<T>::type>::value ||
+ (std::is_convertible<const T&, const void*>::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, std::size_t N>
+ template <typename T, std::size_t N,
+ FMT_ENABLE_IF(!std::is_same<T, wchar_t>::value)>
FMT_CONSTEXPR FMT_INLINE auto map(const T (&values)[N]) -> const T (&)[N] {
return values;
}
template <typename T,
- FMT_ENABLE_IF(std::is_enum<T>::value &&
- !has_formatter<T, Context>::value &&
- !has_fallback_formatter<T, char_type>::value)>
- FMT_CONSTEXPR FMT_INLINE auto map(const T& val)
+ FMT_ENABLE_IF(
+ std::is_enum<T>::value&& std::is_convertible<T, int>::value &&
+ !has_format_as<T>::value && !has_formatter<T, Context>::value &&
+ !has_fallback_formatter<T, char_type>::value)>
+ 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));
}
- template <typename T,
- FMT_ENABLE_IF(!is_string<T>::value && !is_char<T>::value &&
- (has_formatter<T, Context>::value ||
- has_fallback_formatter<T, char_type>::value))>
- FMT_CONSTEXPR FMT_INLINE auto map(const T& val) -> const T& {
+
+ 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>>
+ struct formattable
+ : bool_constant<has_const_formatter<U, Context>() ||
+ !std::is_const<remove_reference_t<T>>::value ||
+ has_fallback_formatter<U, char_type>::value> {};
+
+#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;
}
+#else
+ template <typename T, FMT_ENABLE_IF(formattable<T>::value)>
+ FMT_CONSTEXPR FMT_INLINE auto do_map(T&& val) -> T& {
+ return val;
+ }
+ template <typename T, FMT_ENABLE_IF(!formattable<T>::value)>
+ FMT_CONSTEXPR FMT_INLINE auto do_map(T&&) -> unformattable_const {
+ return {};
+ }
+#endif
+
+ 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)
+ -> decltype(this->do_map(std::forward<T>(val))) {
+ return do_map(std::forward<T>(val));
+ }
template <typename T, FMT_ENABLE_IF(is_named_arg<T>::value)>
FMT_CONSTEXPR FMT_INLINE auto map(const T& named_arg)
public:
using std::back_insert_iterator<detail::buffer<char>>::back_insert_iterator;
- appender(base it) : base(it) {}
- using _Unchecked_type = appender; // Mark iterator as checked.
+ appender(base it) noexcept : base(it) {}
+ FMT_UNCHECKED_ITERATOR(appender);
- auto operator++() -> appender& {
- base::operator++();
- return *this;
- }
-
- auto operator++(int) -> appender {
- auto tmp = *this;
- ++*this;
- return tmp;
- }
+ 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> {
+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;
+ static_assert(formattable_char, "Mixing character types is disallowed.");
+
+ constexpr bool formattable_const =
+ !std::is_same<decltype(arg), const unformattable_const&>::value;
+ static_assert(formattable_const, "Cannot format a const argument.");
+
+ // Formatting of arbitrary pointers is disallowed. If you want to output
+ // a pointer cast it to "void *" or "const void *". In particular, this
+ // forbids formatting of "[const] volatile char *" which is printed as bool
+ // by iostreams.
+ constexpr bool formattable_pointer =
+ !std::is_same<decltype(arg), const unformattable_pointer&>::value;
+ static_assert(formattable_pointer,
+ "Formatting of non-void pointers is disallowed.");
+
+ constexpr bool formattable =
+ !std::is_same<decltype(arg), const unformattable&>::value;
+ static_assert(
+ formattable,
+ "Cannot format an argument. To make type T formattable provide a "
+ "formatter<T> specialization: https://fmt.dev/latest/api.html#udt");
+ return {arg};
+}
+
+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_ = arg_mapper<Context>().map(value);
+ arg.value_ = make_value<Context>(value);
return arg;
}
// another (not recommended).
template <bool IS_PACKED, typename Context, type, typename T,
FMT_ENABLE_IF(IS_PACKED)>
-FMT_CONSTEXPR FMT_INLINE auto make_arg(const T& val) -> value<Context> {
- const auto& arg = arg_mapper<Context>().map(val);
- static_assert(
- !std::is_same<decltype(arg), const unformattable&>::value,
- "Cannot format an argument. To make type T formattable provide a "
- "formatter<T> specialization: https://fmt.dev/latest/api.html#udt");
- return {arg};
+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
template <typename T, typename Char = char>
using is_formattable = bool_constant<
- !std::is_same<decltype(detail::arg_mapper<buffer_context<Char>>().map(
- std::declval<T>())),
- detail::unformattable>::value &&
+ !std::is_base_of<detail::unformattable,
+ decltype(detail::arg_mapper<buffer_context<Char>>().map(
+ std::declval<T>()))>::value &&
!detail::has_fallback_formatter<T, Char>::value>;
/**
: 0);
public:
- FMT_CONSTEXPR FMT_INLINE format_arg_store(const Args&... args)
+ template <typename... T>
+ FMT_CONSTEXPR FMT_INLINE format_arg_store(T&&... args)
:
#if FMT_GCC_VERSION && FMT_GCC_VERSION < 409
basic_format_args<Context>(*this),
#endif
data_{detail::make_arg<
is_packed, Context,
- detail::mapped_type_constant<Args, Context>::value>(args)...} {
+ detail::mapped_type_constant<remove_cvref_t<T>, Context>::value>(
+ FMT_FORWARD(args))...} {
detail::init_named_args(data_.named_args(), 0, 0, args...);
}
};
\endrst
*/
template <typename Context = format_context, typename... Args>
-constexpr auto make_format_args(const Args&... args)
- -> format_arg_store<Context, Args...> {
- return {args...};
+constexpr auto make_format_args(Args&&... args)
+ -> format_arg_store<Context, remove_cvref_t<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;
FMT_BEGIN_DETAIL_NAMESPACE
-void throw_format_error(const char* message);
-
// Workaround an array initialization issue in gcc 4.8.
template <typename Char> struct fill_t {
private:
};
FMT_END_DETAIL_NAMESPACE
+enum class presentation_type : unsigned char {
+ none,
+ // Integer types should go first,
+ dec, // 'd'
+ oct, // 'o'
+ hex_lower, // 'x'
+ hex_upper, // 'X'
+ bin_lower, // 'b'
+ bin_upper, // 'B'
+ hexfloat_lower, // 'a'
+ hexfloat_upper, // 'A'
+ exp_lower, // 'e'
+ exp_upper, // 'E'
+ fixed_lower, // 'f'
+ fixed_upper, // 'F'
+ general_lower, // 'g'
+ general_upper, // 'G'
+ chr, // 'c'
+ string, // 's'
+ pointer, // 'p'
+ debug // '?'
+};
+
// Format specifiers for built-in and string types.
template <typename Char> struct basic_format_specs {
int width;
int precision;
- char type;
+ presentation_type type;
align_t align : 4;
sign_t sign : 3;
bool alt : 1; // Alternate form ('#').
constexpr basic_format_specs()
: width(0),
precision(-1),
- type(0),
+ type(presentation_type::none),
align(align::none),
sign(sign::none),
alt(false),
}
FMT_CONSTEXPR void end_precision() {}
- FMT_CONSTEXPR void on_type(Char type) {
- specs_.type = static_cast<char>(type);
- }
+ FMT_CONSTEXPR void on_type(presentation_type type) { specs_.type = type; }
};
// Format spec handler that saves references to arguments representing dynamic
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;
- constexpr char 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, 0};
- 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 begin;
}
+template <typename Char>
+FMT_CONSTEXPR auto parse_presentation_type(Char type) -> presentation_type {
+ switch (to_ascii(type)) {
+ case 'd':
+ return presentation_type::dec;
+ case 'o':
+ return presentation_type::oct;
+ case 'x':
+ return presentation_type::hex_lower;
+ case 'X':
+ return presentation_type::hex_upper;
+ case 'b':
+ return presentation_type::bin_lower;
+ case 'B':
+ return presentation_type::bin_upper;
+ case 'a':
+ return presentation_type::hexfloat_lower;
+ case 'A':
+ return presentation_type::hexfloat_upper;
+ case 'e':
+ return presentation_type::exp_lower;
+ case 'E':
+ return presentation_type::exp_upper;
+ case 'f':
+ return presentation_type::fixed_lower;
+ case 'F':
+ return presentation_type::fixed_upper;
+ case 'g':
+ return presentation_type::general_lower;
+ case 'G':
+ return presentation_type::general_upper;
+ case 'c':
+ return presentation_type::chr;
+ case 's':
+ return presentation_type::string;
+ case 'p':
+ return presentation_type::pointer;
+ case '?':
+ return presentation_type::debug;
+ default:
+ return presentation_type::none;
+ }
+}
+
// Parses standard format specifiers and sends notifications about parsed
// components to handler.
template <typename Char, typename SpecHandler>
const Char* end,
SpecHandler&& handler)
-> const Char* {
- if (begin + 1 < end && begin[1] == '}' && is_ascii_letter(*begin) &&
+ if (1 < end - begin && begin[1] == '}' && is_ascii_letter(*begin) &&
*begin != 'L') {
- handler.on_type(*begin++);
+ presentation_type type = parse_presentation_type(*begin++);
+ if (type == presentation_type::none)
+ handler.on_error("invalid type specifier");
+ handler.on_type(type);
return begin;
}
}
// Parse type.
- if (begin != end && *begin != '}') handler.on_type(*begin++);
+ if (begin != end && *begin != '}') {
+ presentation_type type = parse_presentation_type(*begin++);
+ if (type == presentation_type::none)
+ handler.on_error("invalid type specifier");
+ handler.on_type(type);
+ }
return begin;
}
template <bool IS_CONSTEXPR, typename Char, typename Handler>
FMT_CONSTEXPR FMT_INLINE void parse_format_string(
basic_string_view<Char> format_str, Handler&& handler) {
- // this is most likely a name-lookup defect in msvc's modules implementation
+ // Workaround a name-lookup bug in MSVC's modules implementation.
using detail::find;
auto begin = format_str.data();
return;
}
struct writer {
- FMT_CONSTEXPR void operator()(const Char* pbegin, const Char* pend) {
- if (pbegin == pend) return;
+ 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, '}', 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.
const Char* p = begin;
- if (*begin != '{' && !find<IS_CONSTEXPR>(begin + 1, end, '{', p))
+ if (*begin != '{' && !find<IS_CONSTEXPR>(begin + 1, end, Char('{'), p))
return write(begin, end);
write(begin, p);
begin = parse_replacement_field(p, end, handler);
}
}
+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(char spec, ErrorHandler&& eh) {
- switch (spec) {
- case 0:
- case 'd':
- case 'x':
- case 'X':
- case 'b':
- case 'B':
- case 'o':
- case 'c':
- break;
- default:
+FMT_CONSTEXPR void check_int_type_spec(presentation_type type,
+ ErrorHandler&& eh) {
+ if (type > presentation_type::bin_upper && type != presentation_type::chr)
eh.on_error("invalid type specifier");
- break;
- }
}
// Checks char specs and returns true if the type spec is char (and not int).
template <typename Char, typename ErrorHandler = error_handler>
FMT_CONSTEXPR auto check_char_specs(const basic_format_specs<Char>& specs,
ErrorHandler&& eh = {}) -> bool {
- if (specs.type && specs.type != 'c') {
+ if (specs.type != presentation_type::none &&
+ 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 use_grisu : 1;
bool showpoint : 1;
};
result.showpoint = specs.alt;
result.locale = specs.localized;
switch (specs.type) {
- case 0:
+ case presentation_type::none:
result.format = float_format::general;
break;
- case 'G':
+ case presentation_type::general_upper:
result.upper = true;
FMT_FALLTHROUGH;
- case 'g':
+ case presentation_type::general_lower:
result.format = float_format::general;
break;
- case 'E':
+ case presentation_type::exp_upper:
result.upper = true;
FMT_FALLTHROUGH;
- case 'e':
+ case presentation_type::exp_lower:
result.format = float_format::exp;
result.showpoint |= specs.precision != 0;
break;
- case 'F':
+ case presentation_type::fixed_upper:
result.upper = true;
FMT_FALLTHROUGH;
- case 'f':
+ case presentation_type::fixed_lower:
result.format = float_format::fixed;
result.showpoint |= specs.precision != 0;
break;
- case 'A':
+ case presentation_type::hexfloat_upper:
result.upper = true;
FMT_FALLTHROUGH;
- case 'a':
+ case presentation_type::hexfloat_lower:
result.format = float_format::hex;
break;
default:
return result;
}
-template <typename Char, typename ErrorHandler = error_handler>
-FMT_CONSTEXPR auto check_cstring_type_spec(Char spec, ErrorHandler&& eh = {})
- -> bool {
- if (spec == 0 || spec == 's') return true;
- if (spec != 'p') eh.on_error("invalid type specifier");
+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 ||
+ type == presentation_type::debug)
+ return true;
+ if (type != presentation_type::pointer) eh.on_error("invalid type specifier");
return false;
}
-template <typename Char, typename ErrorHandler>
-FMT_CONSTEXPR void check_string_type_spec(Char spec, ErrorHandler&& eh) {
- if (spec != 0 && spec != 's') eh.on_error("invalid type specifier");
+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 &&
+ type != presentation_type::debug)
+ eh.on_error("invalid type specifier");
}
-template <typename Char, typename ErrorHandler>
-FMT_CONSTEXPR void check_pointer_type_spec(Char spec, ErrorHandler&& eh) {
- if (spec != 0 && spec != 'p') eh.on_error("invalid type specifier");
+template <typename ErrorHandler>
+FMT_CONSTEXPR void check_pointer_type_spec(presentation_type type,
+ ErrorHandler&& eh) {
+ if (type != presentation_type::none && type != presentation_type::pointer)
+ eh.on_error("invalid type specifier");
}
// A parse_format_specs handler that checks if specifiers are consistent with
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 =
(parse_format_string<true>(s, checker(s, {})), true);
- (void)invalid_format;
+ ignore_unused(invalid_format);
}
template <typename Char>
void vformat_to(
buffer<Char>& buf, basic_string_view<Char> fmt,
basic_format_args<FMT_BUFFER_CONTEXT(type_identity_t<Char>)> args,
- detail::locale_ref loc = {});
+ locale_ref loc = {});
FMT_API void vprint_mojibake(std::FILE*, string_view, format_args);
#ifndef _WIN32
FMT_ASSERT(false, "invalid argument type");
break;
case detail::type::bool_type:
- if (!specs_.type || specs_.type == 's') break;
+ if (specs_.type == presentation_type::none ||
+ specs_.type == presentation_type::string) {
+ break;
+ }
FMT_FALLTHROUGH;
case detail::type::int_type:
case detail::type::uint_type:
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. */
template <typename Char, typename... Args> class basic_format_string {
private:
basic_string_view<Char> str_;
template <typename S,
FMT_ENABLE_IF(
std::is_convertible<const S&, basic_string_view<Char>>::value)>
- FMT_CONSTEVAL basic_format_string(const S& s) : str_(s) {
+ FMT_CONSTEVAL FMT_INLINE basic_format_string(const S& s) : str_(s) {
static_assert(
detail::count<
(std::is_base_of<detail::view, remove_reference_t<Args>>::value &&
std::is_reference<Args>::value)...>() == 0,
"passing views as lvalues is disallowed");
#ifdef FMT_HAS_CONSTEVAL
- if constexpr (detail::count_named_args<Args...>() == 0) {
+ if constexpr (detail::count_named_args<Args...>() ==
+ detail::count_statically_named_args<Args...>()) {
using checker = detail::format_string_checker<Char, detail::error_handler,
remove_cvref_t<Args>...>;
detail::parse_format_string<true>(str_, checker(s, {}));
#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>...>;
-// Creates a runtime format string.
-template <typename S> auto runtime(const S& s) -> basic_runtime<char_t<S>> {
- return {{s}};
-}
+/**
+ \rst
+ Creates a runtime format string.
+
+ **Example**::
+
+ // Check format string at runtime instead of compile-time.
+ fmt::print(fmt::runtime("{:d}"), "I am not a number");
+ \endrst
+ */
+inline auto runtime(string_view s) -> basic_runtime<char> { return {{s}}; }
#endif
FMT_API auto vformat(string_view fmt, format_args args) -> std::string;
**Example**::
#include <fmt/core.h>
- std::string message = fmt::format("The answer is {}", 42);
+ std::string message = fmt::format("The answer is {}.", 42);
\endrst
*/
template <typename... T>
-FMT_INLINE auto format(format_string<T...> fmt, T&&... args) -> std::string {
+FMT_NODISCARD FMT_INLINE auto format(format_string<T...> fmt, T&&... args)
+ -> std::string {
return vformat(fmt, fmt::make_format_args(args...));
}
auto vformat_to(OutputIt out, string_view fmt, format_args args) -> OutputIt {
using detail::get_buffer;
auto&& buf = get_buffer<char>(out);
- detail::vformat_to(buf, string_view(fmt), args);
+ detail::vformat_to(buf, fmt, args, {});
return detail::get_iterator(buf);
}
\rst
Formats ``args`` according to specifications in ``fmt``, writes the result to
the output iterator ``out`` and returns the iterator past the end of the output
- range.
+ range. `format_to` does not append a terminating null character.
**Example**::
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, char>::value)>
auto vformat_to_n(OutputIt out, size_t n, string_view fmt, format_args args)
-> format_to_n_result<OutputIt> {
- using buffer =
- detail::iterator_buffer<OutputIt, char, detail::fixed_buffer_traits>;
- auto buf = buffer(out, n);
- detail::vformat_to(buf, fmt, args);
+ using traits = detail::fixed_buffer_traits;
+ auto buf = detail::iterator_buffer<OutputIt, char, traits>(out, n);
+ detail::vformat_to(buf, fmt, args, {});
return {buf.out(), buf.count()};
}
Formats ``args`` according to specifications in ``fmt``, writes up to ``n``
characters of the result to the output iterator ``out`` and returns the total
(not truncated) output size and the iterator past the end of the output range.
+ `format_to_n` does not append a terminating null character.
\endrst
*/
template <typename OutputIt, typename... T,
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, char>::value)>
FMT_INLINE auto format_to_n(OutputIt out, size_t n, format_string<T...> fmt,
- const T&... args) -> format_to_n_result<OutputIt> {
+ T&&... args) -> format_to_n_result<OutputIt> {
return vformat_to_n(out, n, fmt, fmt::make_format_args(args...));
}
/** Returns the number of chars in the output of ``format(fmt, args...)``. */
template <typename... T>
-FMT_INLINE auto formatted_size(format_string<T...> fmt, T&&... args) -> size_t {
+FMT_NODISCARD FMT_INLINE auto formatted_size(format_string<T...> fmt,
+ T&&... args) -> size_t {
auto buf = detail::counting_buffer<>();
- detail::vformat_to(buf, string_view(fmt), fmt::make_format_args(args...));
+ detail::vformat_to(buf, string_view(fmt), fmt::make_format_args(args...), {});
return buf.count();
}