|
|
|
@ -10,12 +10,15 @@
|
|
|
|
|
|
|
|
|
|
#include <cstdio> // std::FILE
|
|
|
|
|
#include <cstring>
|
|
|
|
|
#include <functional>
|
|
|
|
|
#include <iterator>
|
|
|
|
|
#include <memory>
|
|
|
|
|
#include <string>
|
|
|
|
|
#include <type_traits>
|
|
|
|
|
#include <vector>
|
|
|
|
|
|
|
|
|
|
// The fmt library version in the form major * 10000 + minor * 100 + patch.
|
|
|
|
|
#define FMT_VERSION 60102
|
|
|
|
|
#define FMT_VERSION 60200
|
|
|
|
|
|
|
|
|
|
#ifdef __has_feature
|
|
|
|
|
# define FMT_HAS_FEATURE(x) __has_feature(x)
|
|
|
|
@ -36,6 +39,18 @@
|
|
|
|
|
# define FMT_HAS_CPP_ATTRIBUTE(x) 0
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#define FMT_HAS_CPP14_ATTRIBUTE(attribute) \
|
|
|
|
|
(__cplusplus >= 201402L && FMT_HAS_CPP_ATTRIBUTE(attribute))
|
|
|
|
|
|
|
|
|
|
#define FMT_HAS_CPP17_ATTRIBUTE(attribute) \
|
|
|
|
|
(__cplusplus >= 201703L && FMT_HAS_CPP_ATTRIBUTE(attribute))
|
|
|
|
|
|
|
|
|
|
#ifdef __clang__
|
|
|
|
|
# define FMT_CLANG_VERSION (__clang_major__ * 100 + __clang_minor__)
|
|
|
|
|
#else
|
|
|
|
|
# define FMT_CLANG_VERSION 0
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#if defined(__GNUC__) && !defined(__clang__)
|
|
|
|
|
# define FMT_GCC_VERSION (__GNUC__ * 100 + __GNUC_MINOR__)
|
|
|
|
|
#else
|
|
|
|
@ -117,16 +132,25 @@
|
|
|
|
|
# endif
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
// [[noreturn]] is disabled on MSVC because of bogus unreachable code warnings.
|
|
|
|
|
#if FMT_EXCEPTIONS && FMT_HAS_CPP_ATTRIBUTE(noreturn) && !FMT_MSC_VER
|
|
|
|
|
// [[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
|
|
|
|
|
# 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
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#ifndef FMT_DEPRECATED
|
|
|
|
|
# if (FMT_HAS_CPP_ATTRIBUTE(deprecated) && __cplusplus >= 201402L) || \
|
|
|
|
|
FMT_MSC_VER >= 1900
|
|
|
|
|
# if FMT_HAS_CPP14_ATTRIBUTE(deprecated) || FMT_MSC_VER >= 1900
|
|
|
|
|
# define FMT_DEPRECATED [[deprecated]]
|
|
|
|
|
# else
|
|
|
|
|
# if defined(__GNUC__) || defined(__clang__)
|
|
|
|
@ -139,8 +163,8 @@
|
|
|
|
|
# endif
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
// Workaround broken [[deprecated]] in the Intel compiler and NVCC.
|
|
|
|
|
#if defined(__INTEL_COMPILER) || FMT_NVCC
|
|
|
|
|
// Workaround broken [[deprecated]] in the Intel, PGI and NVCC compilers.
|
|
|
|
|
#if defined(__INTEL_COMPILER) || defined(__PGI) || FMT_NVCC
|
|
|
|
|
# define FMT_DEPRECATED_ALIAS
|
|
|
|
|
#else
|
|
|
|
|
# define FMT_DEPRECATED_ALIAS FMT_DEPRECATED
|
|
|
|
@ -166,6 +190,12 @@
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#if !defined(FMT_HEADER_ONLY) && defined(_WIN32)
|
|
|
|
|
# if FMT_MSC_VER
|
|
|
|
|
# define FMT_NO_W4275 __pragma(warning(suppress : 4275))
|
|
|
|
|
# else
|
|
|
|
|
# define FMT_NO_W4275
|
|
|
|
|
# endif
|
|
|
|
|
# define FMT_CLASS_API FMT_NO_W4275
|
|
|
|
|
# ifdef FMT_EXPORT
|
|
|
|
|
# define FMT_API __declspec(dllexport)
|
|
|
|
|
# elif defined(FMT_SHARED)
|
|
|
|
@ -173,12 +203,24 @@
|
|
|
|
|
# define FMT_EXTERN_TEMPLATE_API FMT_API
|
|
|
|
|
# endif
|
|
|
|
|
#endif
|
|
|
|
|
#ifndef FMT_CLASS_API
|
|
|
|
|
# define FMT_CLASS_API
|
|
|
|
|
#endif
|
|
|
|
|
#ifndef FMT_API
|
|
|
|
|
# define FMT_API
|
|
|
|
|
# if FMT_GCC_VERSION || FMT_CLANG_VERSION
|
|
|
|
|
# define FMT_API __attribute__((visibility("default")))
|
|
|
|
|
# define FMT_EXTERN_TEMPLATE_API FMT_API
|
|
|
|
|
# define FMT_INSTANTIATION_DEF_API
|
|
|
|
|
# else
|
|
|
|
|
# define FMT_API
|
|
|
|
|
# endif
|
|
|
|
|
#endif
|
|
|
|
|
#ifndef FMT_EXTERN_TEMPLATE_API
|
|
|
|
|
# define FMT_EXTERN_TEMPLATE_API
|
|
|
|
|
#endif
|
|
|
|
|
#ifndef FMT_INSTANTIATION_DEF_API
|
|
|
|
|
# define FMT_INSTANTIATION_DEF_API FMT_API
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#ifndef FMT_HEADER_ONLY
|
|
|
|
|
# define FMT_EXTERN extern
|
|
|
|
@ -197,9 +239,16 @@
|
|
|
|
|
# define FMT_USE_EXPERIMENTAL_STRING_VIEW
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#ifndef FMT_UNICODE
|
|
|
|
|
# define FMT_UNICODE !FMT_MSC_VER
|
|
|
|
|
#endif
|
|
|
|
|
#if FMT_UNICODE && FMT_MSC_VER
|
|
|
|
|
# pragma execution_character_set("utf-8")
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
FMT_BEGIN_NAMESPACE
|
|
|
|
|
|
|
|
|
|
// Implementations of enable_if_t and other types for pre-C++14 systems.
|
|
|
|
|
// Implementations of enable_if_t and other metafunctions for older systems.
|
|
|
|
|
template <bool B, class T = void>
|
|
|
|
|
using enable_if_t = typename std::enable_if<B, T>::type;
|
|
|
|
|
template <bool B, class T, class F>
|
|
|
|
@ -211,6 +260,8 @@ 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;
|
|
|
|
|
|
|
|
|
|
struct monostate {};
|
|
|
|
|
|
|
|
|
@ -221,19 +272,25 @@ struct monostate {};
|
|
|
|
|
|
|
|
|
|
namespace internal {
|
|
|
|
|
|
|
|
|
|
// A helper function to suppress bogus "conditional expression is constant"
|
|
|
|
|
// warnings.
|
|
|
|
|
template <typename T> FMT_CONSTEXPR T const_check(T value) { return value; }
|
|
|
|
|
|
|
|
|
|
// 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; };
|
|
|
|
|
|
|
|
|
|
FMT_API void assert_fail(const char* file, int line, const char* message);
|
|
|
|
|
FMT_NORETURN FMT_API void assert_fail(const char* file, int line,
|
|
|
|
|
const char* message);
|
|
|
|
|
|
|
|
|
|
#ifndef FMT_ASSERT
|
|
|
|
|
# ifdef NDEBUG
|
|
|
|
|
# define FMT_ASSERT(condition, message)
|
|
|
|
|
// FMT_ASSERT is not empty to avoid -Werror=empty-body.
|
|
|
|
|
# define FMT_ASSERT(condition, message) ((void)0)
|
|
|
|
|
# else
|
|
|
|
|
# define FMT_ASSERT(condition, message) \
|
|
|
|
|
((condition) \
|
|
|
|
|
? void() \
|
|
|
|
|
: fmt::internal::assert_fail(__FILE__, __LINE__, (message)))
|
|
|
|
|
# define FMT_ASSERT(condition, message) \
|
|
|
|
|
((condition) /* void() fails with -Winvalid-constexpr on clang 4.0.1 */ \
|
|
|
|
|
? (void)0 \
|
|
|
|
|
: ::fmt::internal::assert_fail(__FILE__, __LINE__, (message)))
|
|
|
|
|
# endif
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
@ -248,7 +305,7 @@ template <typename T> struct std_string_view {};
|
|
|
|
|
|
|
|
|
|
#ifdef FMT_USE_INT128
|
|
|
|
|
// Do nothing.
|
|
|
|
|
#elif defined(__SIZEOF_INT128__)
|
|
|
|
|
#elif defined(__SIZEOF_INT128__) && !FMT_NVCC
|
|
|
|
|
# define FMT_USE_INT128 1
|
|
|
|
|
using int128_t = __int128_t;
|
|
|
|
|
using uint128_t = __uint128_t;
|
|
|
|
@ -266,6 +323,19 @@ FMT_CONSTEXPR typename std::make_unsigned<Int>::type to_unsigned(Int value) {
|
|
|
|
|
FMT_ASSERT(value >= 0, "negative value");
|
|
|
|
|
return static_cast<typename std::make_unsigned<Int>::type>(value);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
constexpr unsigned char micro[] = "\u00B5";
|
|
|
|
|
|
|
|
|
|
template <typename Char> constexpr bool is_unicode() {
|
|
|
|
|
return FMT_UNICODE || sizeof(Char) != 1 ||
|
|
|
|
|
(sizeof(micro) == 3 && micro[0] == 0xC2 && micro[1] == 0xB5);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#ifdef __cpp_char8_t
|
|
|
|
|
using char8_type = char8_t;
|
|
|
|
|
#else
|
|
|
|
|
enum char8_type : unsigned char {};
|
|
|
|
|
#endif
|
|
|
|
|
} // namespace internal
|
|
|
|
|
|
|
|
|
|
template <typename... Ts>
|
|
|
|
@ -284,7 +354,8 @@ template <typename Char> class basic_string_view {
|
|
|
|
|
size_t size_;
|
|
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
using char_type = Char;
|
|
|
|
|
using char_type FMT_DEPRECATED_ALIAS = Char;
|
|
|
|
|
using value_type = Char;
|
|
|
|
|
using iterator = const Char*;
|
|
|
|
|
|
|
|
|
|
FMT_CONSTEXPR basic_string_view() FMT_NOEXCEPT : data_(nullptr), size_(0) {}
|
|
|
|
@ -300,6 +371,9 @@ template <typename Char> class basic_string_view {
|
|
|
|
|
the size with ``std::char_traits<Char>::length``.
|
|
|
|
|
\endrst
|
|
|
|
|
*/
|
|
|
|
|
#if __cplusplus >= 201703L // C++17's char_traits::length() is constexpr.
|
|
|
|
|
FMT_CONSTEXPR
|
|
|
|
|
#endif
|
|
|
|
|
basic_string_view(const Char* s)
|
|
|
|
|
: data_(s), size_(std::char_traits<Char>::length(s)) {}
|
|
|
|
|
|
|
|
|
@ -365,15 +439,15 @@ using string_view = basic_string_view<char>;
|
|
|
|
|
using wstring_view = basic_string_view<wchar_t>;
|
|
|
|
|
|
|
|
|
|
#ifndef __cpp_char8_t
|
|
|
|
|
// A UTF-8 code unit type.
|
|
|
|
|
enum char8_t : unsigned char {};
|
|
|
|
|
// char8_t is deprecated; use char instead.
|
|
|
|
|
using char8_t FMT_DEPRECATED_ALIAS = internal::char8_type;
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
/** Specifies if ``T`` is a character type. Can be specialized by users. */
|
|
|
|
|
template <typename T> struct is_char : std::false_type {};
|
|
|
|
|
template <> struct is_char<char> : std::true_type {};
|
|
|
|
|
template <> struct is_char<wchar_t> : std::true_type {};
|
|
|
|
|
template <> struct is_char<char8_t> : std::true_type {};
|
|
|
|
|
template <> struct is_char<internal::char8_type> : std::true_type {};
|
|
|
|
|
template <> struct is_char<char16_t> : std::true_type {};
|
|
|
|
|
template <> struct is_char<char32_t> : std::true_type {};
|
|
|
|
|
|
|
|
|
@ -442,7 +516,7 @@ struct is_string : std::is_class<decltype(to_string_view(std::declval<S>()))> {
|
|
|
|
|
template <typename S, typename = void> struct char_t_impl {};
|
|
|
|
|
template <typename S> struct char_t_impl<S, enable_if_t<is_string<S>::value>> {
|
|
|
|
|
using result = decltype(to_string_view(std::declval<S>()));
|
|
|
|
|
using type = typename result::char_type;
|
|
|
|
|
using type = typename result::value_type;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
struct error_handler {
|
|
|
|
@ -603,6 +677,9 @@ template <typename T> class buffer {
|
|
|
|
|
T* begin() FMT_NOEXCEPT { return ptr_; }
|
|
|
|
|
T* end() FMT_NOEXCEPT { return ptr_ + size_; }
|
|
|
|
|
|
|
|
|
|
const T* begin() const FMT_NOEXCEPT { return ptr_; }
|
|
|
|
|
const T* end() const FMT_NOEXCEPT { return ptr_ + size_; }
|
|
|
|
|
|
|
|
|
|
/** Returns the size of this buffer. */
|
|
|
|
|
std::size_t size() const FMT_NOEXCEPT { return size_; }
|
|
|
|
|
|
|
|
|
@ -639,8 +716,10 @@ template <typename T> class buffer {
|
|
|
|
|
/** Appends data to the end of the buffer. */
|
|
|
|
|
template <typename U> void append(const U* begin, const U* end);
|
|
|
|
|
|
|
|
|
|
T& operator[](std::size_t index) { return ptr_[index]; }
|
|
|
|
|
const T& operator[](std::size_t index) const { return ptr_[index]; }
|
|
|
|
|
template <typename I> T& operator[](I index) { return ptr_[index]; }
|
|
|
|
|
template <typename I> const T& operator[](I index) const {
|
|
|
|
|
return ptr_[index];
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// A container-backed buffer.
|
|
|
|
@ -684,7 +763,7 @@ using has_fallback_formatter =
|
|
|
|
|
template <typename Char> struct named_arg_base;
|
|
|
|
|
template <typename T, typename Char> struct named_arg;
|
|
|
|
|
|
|
|
|
|
enum type {
|
|
|
|
|
enum class type {
|
|
|
|
|
none_type,
|
|
|
|
|
named_arg_type,
|
|
|
|
|
// Integer types should go first,
|
|
|
|
@ -710,11 +789,12 @@ enum type {
|
|
|
|
|
|
|
|
|
|
// Maps core type T to the corresponding type enum constant.
|
|
|
|
|
template <typename T, typename Char>
|
|
|
|
|
struct type_constant : std::integral_constant<type, custom_type> {};
|
|
|
|
|
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, constant> {}
|
|
|
|
|
struct type_constant<Type, Char> \
|
|
|
|
|
: std::integral_constant<type, type::constant> {}
|
|
|
|
|
|
|
|
|
|
FMT_TYPE_CONSTANT(const named_arg_base<Char>&, named_arg_type);
|
|
|
|
|
FMT_TYPE_CONSTANT(int, int_type);
|
|
|
|
@ -733,13 +813,13 @@ FMT_TYPE_CONSTANT(basic_string_view<Char>, string_type);
|
|
|
|
|
FMT_TYPE_CONSTANT(const void*, pointer_type);
|
|
|
|
|
|
|
|
|
|
FMT_CONSTEXPR bool is_integral_type(type t) {
|
|
|
|
|
FMT_ASSERT(t != named_arg_type, "invalid argument type");
|
|
|
|
|
return t > none_type && t <= last_integer_type;
|
|
|
|
|
FMT_ASSERT(t != type::named_arg_type, "invalid argument type");
|
|
|
|
|
return t > type::none_type && t <= type::last_integer_type;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
FMT_CONSTEXPR bool is_arithmetic_type(type t) {
|
|
|
|
|
FMT_ASSERT(t != named_arg_type, "invalid argument type");
|
|
|
|
|
return t > none_type && t <= last_numeric_type;
|
|
|
|
|
FMT_ASSERT(t != type::named_arg_type, "invalid argument type");
|
|
|
|
|
return t > type::none_type && t <= type::last_numeric_type;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
template <typename Char> struct string_value {
|
|
|
|
@ -869,7 +949,8 @@ template <typename Context> struct arg_mapper {
|
|
|
|
|
template <typename T,
|
|
|
|
|
FMT_ENABLE_IF(
|
|
|
|
|
std::is_constructible<basic_string_view<char_type>, T>::value &&
|
|
|
|
|
!is_string<T>::value)>
|
|
|
|
|
!is_string<T>::value && !has_formatter<T, Context>::value &&
|
|
|
|
|
!has_fallback_formatter<T, Context>::value)>
|
|
|
|
|
FMT_CONSTEXPR basic_string_view<char_type> map(const T& val) {
|
|
|
|
|
return basic_string_view<char_type>(val);
|
|
|
|
|
}
|
|
|
|
@ -878,7 +959,8 @@ template <typename Context> struct arg_mapper {
|
|
|
|
|
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)>
|
|
|
|
|
!is_string<T>::value && !has_formatter<T, Context>::value &&
|
|
|
|
|
!has_fallback_formatter<T, Context>::value)>
|
|
|
|
|
FMT_CONSTEXPR basic_string_view<char_type> map(const T& val) {
|
|
|
|
|
return std_string_view<char_type>(val);
|
|
|
|
|
}
|
|
|
|
@ -907,18 +989,15 @@ template <typename Context> struct arg_mapper {
|
|
|
|
|
FMT_ENABLE_IF(std::is_enum<T>::value &&
|
|
|
|
|
!has_formatter<T, Context>::value &&
|
|
|
|
|
!has_fallback_formatter<T, Context>::value)>
|
|
|
|
|
FMT_CONSTEXPR auto map(const T& val) -> decltype(
|
|
|
|
|
map(static_cast<typename std::underlying_type<T>::type>(val))) {
|
|
|
|
|
FMT_CONSTEXPR 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));
|
|
|
|
|
}
|
|
|
|
|
template <
|
|
|
|
|
typename T,
|
|
|
|
|
FMT_ENABLE_IF(
|
|
|
|
|
!is_string<T>::value && !is_char<T>::value &&
|
|
|
|
|
!std::is_constructible<basic_string_view<char_type>, T>::value &&
|
|
|
|
|
(has_formatter<T, Context>::value ||
|
|
|
|
|
(has_fallback_formatter<T, Context>::value &&
|
|
|
|
|
!std::is_constructible<std_string_view<char_type>, T>::value)))>
|
|
|
|
|
template <typename T,
|
|
|
|
|
FMT_ENABLE_IF(!is_string<T>::value && !is_char<T>::value &&
|
|
|
|
|
(has_formatter<T, Context>::value ||
|
|
|
|
|
has_fallback_formatter<T, Context>::value))>
|
|
|
|
|
FMT_CONSTEXPR const T& map(const T& val) {
|
|
|
|
|
return val;
|
|
|
|
|
}
|
|
|
|
@ -930,6 +1009,16 @@ template <typename Context> struct arg_mapper {
|
|
|
|
|
std::memcpy(val.data, &arg, sizeof(arg));
|
|
|
|
|
return val;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int map(...) {
|
|
|
|
|
constexpr bool formattable = sizeof(Context) == 0;
|
|
|
|
|
static_assert(
|
|
|
|
|
formattable,
|
|
|
|
|
"Cannot format argument. To make type T formattable provide a "
|
|
|
|
|
"formatter<T> specialization: "
|
|
|
|
|
"https://fmt.dev/latest/api.html#formatting-user-defined-types");
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// A type constant after applying arg_mapper<Context>.
|
|
|
|
@ -981,10 +1070,10 @@ template <typename Context> class basic_format_arg {
|
|
|
|
|
internal::custom_value<Context> custom_;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
FMT_CONSTEXPR basic_format_arg() : type_(internal::none_type) {}
|
|
|
|
|
FMT_CONSTEXPR basic_format_arg() : type_(internal::type::none_type) {}
|
|
|
|
|
|
|
|
|
|
FMT_CONSTEXPR explicit operator bool() const FMT_NOEXCEPT {
|
|
|
|
|
return type_ != internal::none_type;
|
|
|
|
|
return type_ != internal::type::none_type;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
internal::type type() const { return type_; }
|
|
|
|
@ -1006,47 +1095,47 @@ FMT_CONSTEXPR auto visit_format_arg(Visitor&& vis,
|
|
|
|
|
-> decltype(vis(0)) {
|
|
|
|
|
using char_type = typename Context::char_type;
|
|
|
|
|
switch (arg.type_) {
|
|
|
|
|
case internal::none_type:
|
|
|
|
|
case internal::type::none_type:
|
|
|
|
|
break;
|
|
|
|
|
case internal::named_arg_type:
|
|
|
|
|
case internal::type::named_arg_type:
|
|
|
|
|
FMT_ASSERT(false, "invalid argument type");
|
|
|
|
|
break;
|
|
|
|
|
case internal::int_type:
|
|
|
|
|
case internal::type::int_type:
|
|
|
|
|
return vis(arg.value_.int_value);
|
|
|
|
|
case internal::uint_type:
|
|
|
|
|
case internal::type::uint_type:
|
|
|
|
|
return vis(arg.value_.uint_value);
|
|
|
|
|
case internal::long_long_type:
|
|
|
|
|
case internal::type::long_long_type:
|
|
|
|
|
return vis(arg.value_.long_long_value);
|
|
|
|
|
case internal::ulong_long_type:
|
|
|
|
|
case internal::type::ulong_long_type:
|
|
|
|
|
return vis(arg.value_.ulong_long_value);
|
|
|
|
|
#if FMT_USE_INT128
|
|
|
|
|
case internal::int128_type:
|
|
|
|
|
case internal::type::int128_type:
|
|
|
|
|
return vis(arg.value_.int128_value);
|
|
|
|
|
case internal::uint128_type:
|
|
|
|
|
case internal::type::uint128_type:
|
|
|
|
|
return vis(arg.value_.uint128_value);
|
|
|
|
|
#else
|
|
|
|
|
case internal::int128_type:
|
|
|
|
|
case internal::uint128_type:
|
|
|
|
|
case internal::type::int128_type:
|
|
|
|
|
case internal::type::uint128_type:
|
|
|
|
|
break;
|
|
|
|
|
#endif
|
|
|
|
|
case internal::bool_type:
|
|
|
|
|
case internal::type::bool_type:
|
|
|
|
|
return vis(arg.value_.bool_value);
|
|
|
|
|
case internal::char_type:
|
|
|
|
|
case internal::type::char_type:
|
|
|
|
|
return vis(arg.value_.char_value);
|
|
|
|
|
case internal::float_type:
|
|
|
|
|
case internal::type::float_type:
|
|
|
|
|
return vis(arg.value_.float_value);
|
|
|
|
|
case internal::double_type:
|
|
|
|
|
case internal::type::double_type:
|
|
|
|
|
return vis(arg.value_.double_value);
|
|
|
|
|
case internal::long_double_type:
|
|
|
|
|
case internal::type::long_double_type:
|
|
|
|
|
return vis(arg.value_.long_double_value);
|
|
|
|
|
case internal::cstring_type:
|
|
|
|
|
case internal::type::cstring_type:
|
|
|
|
|
return vis(arg.value_.string.data);
|
|
|
|
|
case internal::string_type:
|
|
|
|
|
case internal::type::string_type:
|
|
|
|
|
return vis(basic_string_view<char_type>(arg.value_.string.data,
|
|
|
|
|
arg.value_.string.size));
|
|
|
|
|
case internal::pointer_type:
|
|
|
|
|
case internal::type::pointer_type:
|
|
|
|
|
return vis(arg.value_.pointer);
|
|
|
|
|
case internal::custom_type:
|
|
|
|
|
case internal::type::custom_type:
|
|
|
|
|
return vis(typename basic_format_arg<Context>::handle(arg.value_.custom));
|
|
|
|
|
}
|
|
|
|
|
return vis(monostate());
|
|
|
|
@ -1106,7 +1195,7 @@ template <typename> constexpr unsigned long long encode_types() { return 0; }
|
|
|
|
|
|
|
|
|
|
template <typename Context, typename Arg, typename... Args>
|
|
|
|
|
constexpr unsigned long long encode_types() {
|
|
|
|
|
return mapped_type_constant<Arg, Context>::value |
|
|
|
|
|
return static_cast<unsigned>(mapped_type_constant<Arg, Context>::value) |
|
|
|
|
|
(encode_types<Context, Args...>() << packed_arg_bits);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -1129,6 +1218,43 @@ template <bool IS_PACKED, typename Context, typename T,
|
|
|
|
|
inline basic_format_arg<Context> make_arg(const T& value) {
|
|
|
|
|
return make_arg<Context>(value);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
template <typename T> struct is_reference_wrapper : std::false_type {};
|
|
|
|
|
|
|
|
|
|
template <typename T>
|
|
|
|
|
struct is_reference_wrapper<std::reference_wrapper<T>> : std::true_type {};
|
|
|
|
|
|
|
|
|
|
class dynamic_arg_list {
|
|
|
|
|
// Workaround for clang's -Wweak-vtables. Unlike for regular classes, for
|
|
|
|
|
// templates it doesn't complain about inability to deduce single translation
|
|
|
|
|
// unit for placing vtable. So storage_node_base is made a fake template.
|
|
|
|
|
template <typename = void> struct node {
|
|
|
|
|
virtual ~node() = default;
|
|
|
|
|
std::unique_ptr<node<>> next;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
template <typename T> struct typed_node : node<> {
|
|
|
|
|
T value;
|
|
|
|
|
|
|
|
|
|
template <typename Arg>
|
|
|
|
|
FMT_CONSTEXPR typed_node(const Arg& arg) : value(arg) {}
|
|
|
|
|
|
|
|
|
|
template <typename Char>
|
|
|
|
|
FMT_CONSTEXPR typed_node(const basic_string_view<Char>& arg)
|
|
|
|
|
: value(arg.data(), arg.size()) {}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
std::unique_ptr<node<>> head_;
|
|
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
template <typename T, typename Arg> const T& push(const Arg& arg) {
|
|
|
|
|
auto node = std::unique_ptr<typed_node<T>>(new typed_node<T>(arg));
|
|
|
|
|
auto& value = node->value;
|
|
|
|
|
node->next = std::move(head_);
|
|
|
|
|
head_ = std::move(node);
|
|
|
|
|
return value;
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
} // namespace internal
|
|
|
|
|
|
|
|
|
|
// Formatting context.
|
|
|
|
@ -1191,7 +1317,13 @@ using wformat_context = buffer_context<wchar_t>;
|
|
|
|
|
such as `~fmt::vformat`.
|
|
|
|
|
\endrst
|
|
|
|
|
*/
|
|
|
|
|
template <typename Context, typename... Args> class format_arg_store {
|
|
|
|
|
template <typename Context, typename... Args>
|
|
|
|
|
class format_arg_store
|
|
|
|
|
#if FMT_GCC_VERSION && FMT_GCC_VERSION < 409
|
|
|
|
|
// Workaround a GCC template argument substitution bug.
|
|
|
|
|
: public basic_format_args<Context>
|
|
|
|
|
#endif
|
|
|
|
|
{
|
|
|
|
|
private:
|
|
|
|
|
static const size_t num_args = sizeof...(Args);
|
|
|
|
|
static const bool is_packed = num_args < internal::max_packed_args;
|
|
|
|
@ -1210,7 +1342,12 @@ template <typename Context, typename... Args> class format_arg_store {
|
|
|
|
|
: internal::is_unpacked_bit | num_args;
|
|
|
|
|
|
|
|
|
|
format_arg_store(const Args&... args)
|
|
|
|
|
: data_{internal::make_arg<is_packed, Context>(args)...} {}
|
|
|
|
|
:
|
|
|
|
|
#if FMT_GCC_VERSION && FMT_GCC_VERSION < 409
|
|
|
|
|
basic_format_args<Context>(*this),
|
|
|
|
|
#endif
|
|
|
|
|
data_{internal::make_arg<is_packed, Context>(args)...} {
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
@ -1227,7 +1364,112 @@ inline format_arg_store<Context, Args...> make_format_args(
|
|
|
|
|
return {args...};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** Formatting arguments. */
|
|
|
|
|
/**
|
|
|
|
|
\rst
|
|
|
|
|
A dynamic version of `fmt::format_arg_store<>`.
|
|
|
|
|
It's equipped with a storage to potentially temporary objects which lifetime
|
|
|
|
|
could be shorter than the format arguments object.
|
|
|
|
|
|
|
|
|
|
It can be implicitly converted into `~fmt::basic_format_args` for passing
|
|
|
|
|
into type-erased formatting functions such as `~fmt::vformat`.
|
|
|
|
|
\endrst
|
|
|
|
|
*/
|
|
|
|
|
template <typename Context>
|
|
|
|
|
class dynamic_format_arg_store
|
|
|
|
|
#if FMT_GCC_VERSION && FMT_GCC_VERSION < 409
|
|
|
|
|
// Workaround a GCC template argument substitution bug.
|
|
|
|
|
: public basic_format_args<Context>
|
|
|
|
|
#endif
|
|
|
|
|
{
|
|
|
|
|
private:
|
|
|
|
|
using char_type = typename Context::char_type;
|
|
|
|
|
|
|
|
|
|
template <typename T> struct need_copy {
|
|
|
|
|
static constexpr internal::type mapped_type =
|
|
|
|
|
internal::mapped_type_constant<T, Context>::value;
|
|
|
|
|
|
|
|
|
|
enum {
|
|
|
|
|
value = !(internal::is_reference_wrapper<T>::value ||
|
|
|
|
|
std::is_same<T, basic_string_view<char_type>>::value ||
|
|
|
|
|
std::is_same<T, internal::std_string_view<char_type>>::value ||
|
|
|
|
|
(mapped_type != internal::type::cstring_type &&
|
|
|
|
|
mapped_type != internal::type::string_type &&
|
|
|
|
|
mapped_type != internal::type::custom_type &&
|
|
|
|
|
mapped_type != internal::type::named_arg_type))
|
|
|
|
|
};
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
template <typename T>
|
|
|
|
|
using stored_type = conditional_t<internal::is_string<T>::value,
|
|
|
|
|
std::basic_string<char_type>, T>;
|
|
|
|
|
|
|
|
|
|
// Storage of basic_format_arg must be contiguous.
|
|
|
|
|
std::vector<basic_format_arg<Context>> data_;
|
|
|
|
|
|
|
|
|
|
// Storage of arguments not fitting into basic_format_arg must grow
|
|
|
|
|
// without relocation because items in data_ refer to it.
|
|
|
|
|
internal::dynamic_arg_list dynamic_args_;
|
|
|
|
|
|
|
|
|
|
friend class basic_format_args<Context>;
|
|
|
|
|
|
|
|
|
|
unsigned long long get_types() const {
|
|
|
|
|
return internal::is_unpacked_bit | data_.size();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
template <typename T> void emplace_arg(const T& arg) {
|
|
|
|
|
data_.emplace_back(internal::make_arg<Context>(arg));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
/**
|
|
|
|
|
\rst
|
|
|
|
|
Adds an argument into the dynamic store for later passing to a formating
|
|
|
|
|
function.
|
|
|
|
|
|
|
|
|
|
Note that custom types and string types (but not string views!) are copied
|
|
|
|
|
into the store with dynamic memory (in addition to resizing vector).
|
|
|
|
|
|
|
|
|
|
**Example**::
|
|
|
|
|
|
|
|
|
|
fmt::dynamic_format_arg_store<fmt::format_context> store;
|
|
|
|
|
store.push_back(42);
|
|
|
|
|
store.push_back("abc");
|
|
|
|
|
store.push_back(1.5f);
|
|
|
|
|
std::string result = fmt::vformat("{} and {} and {}", store);
|
|
|
|
|
\endrst
|
|
|
|
|
*/
|
|
|
|
|
template <typename T> void push_back(const T& arg) {
|
|
|
|
|
static_assert(
|
|
|
|
|
!std::is_base_of<internal::named_arg_base<char_type>, T>::value,
|
|
|
|
|
"named arguments are not supported yet");
|
|
|
|
|
if (internal::const_check(need_copy<T>::value))
|
|
|
|
|
emplace_arg(dynamic_args_.push<stored_type<T>>(arg));
|
|
|
|
|
else
|
|
|
|
|
emplace_arg(arg);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
Adds a reference to the argument into the dynamic store for later passing to
|
|
|
|
|
a formating function.
|
|
|
|
|
*/
|
|
|
|
|
template <typename T> void push_back(std::reference_wrapper<T> arg) {
|
|
|
|
|
static_assert(
|
|
|
|
|
need_copy<T>::value,
|
|
|
|
|
"objects of built-in types and string views are always copied");
|
|
|
|
|
emplace_arg(arg.get());
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
\rst
|
|
|
|
|
A view of a collection of formatting arguments. To avoid lifetime issues it
|
|
|
|
|
should only be used as a parameter type in type-erased functions such as
|
|
|
|
|
``vformat``::
|
|
|
|
|
|
|
|
|
|
void vlog(string_view format_str, format_args args); // OK
|
|
|
|
|
format_args args = make_format_args(42); // Error: dangling reference
|
|
|
|
|
\endrst
|
|
|
|
|
*/
|
|
|
|
|
template <typename Context> class basic_format_args {
|
|
|
|
|
public:
|
|
|
|
|
using size_type = int;
|
|
|
|
@ -1269,7 +1511,7 @@ template <typename Context> class basic_format_args {
|
|
|
|
|
}
|
|
|
|
|
if (index > internal::max_packed_args) return arg;
|
|
|
|
|
arg.type_ = type(index);
|
|
|
|
|
if (arg.type_ == internal::none_type) return arg;
|
|
|
|
|
if (arg.type_ == internal::type::none_type) return arg;
|
|
|
|
|
internal::value<Context>& val = arg.value_;
|
|
|
|
|
val = values_[index];
|
|
|
|
|
return arg;
|
|
|
|
@ -1289,6 +1531,17 @@ template <typename Context> class basic_format_args {
|
|
|
|
|
set_data(store.data_);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
\rst
|
|
|
|
|
Constructs a `basic_format_args` object from
|
|
|
|
|
`~fmt::dynamic_format_arg_store`.
|
|
|
|
|
\endrst
|
|
|
|
|
*/
|
|
|
|
|
basic_format_args(const dynamic_format_arg_store<Context>& store)
|
|
|
|
|
: types_(store.get_types()) {
|
|
|
|
|
set_data(store.data_.data());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
\rst
|
|
|
|
|
Constructs a `basic_format_args` object from a dynamic set of arguments.
|
|
|
|
@ -1302,7 +1555,7 @@ template <typename Context> class basic_format_args {
|
|
|
|
|
/** Returns the argument at specified index. */
|
|
|
|
|
format_arg get(int index) const {
|
|
|
|
|
format_arg arg = do_get(index);
|
|
|
|
|
if (arg.type_ == internal::named_arg_type)
|
|
|
|
|
if (arg.type_ == internal::type::named_arg_type)
|
|
|
|
|
arg = arg.value_.named_arg->template deserialize<Context>();
|
|
|
|
|
return arg;
|
|
|
|
|
}
|
|
|
|
@ -1319,12 +1572,12 @@ template <typename Context> class basic_format_args {
|
|
|
|
|
struct format_args : basic_format_args<format_context> {
|
|
|
|
|
template <typename... Args>
|
|
|
|
|
format_args(Args&&... args)
|
|
|
|
|
: basic_format_args<format_context>(std::forward<Args>(args)...) {}
|
|
|
|
|
: basic_format_args<format_context>(static_cast<Args&&>(args)...) {}
|
|
|
|
|
};
|
|
|
|
|
struct wformat_args : basic_format_args<wformat_context> {
|
|
|
|
|
template <typename... Args>
|
|
|
|
|
wformat_args(Args&&... args)
|
|
|
|
|
: basic_format_args<wformat_context>(std::forward<Args>(args)...) {}
|
|
|
|
|
: basic_format_args<wformat_context>(static_cast<Args&&>(args)...) {}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
template <typename Container> struct is_contiguous : std::false_type {};
|
|
|
|
@ -1358,7 +1611,10 @@ template <typename Char> struct named_arg_base {
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
template <typename T, typename Char> struct named_arg : named_arg_base<Char> {
|
|
|
|
|
struct view {};
|
|
|
|
|
|
|
|
|
|
template <typename T, typename Char>
|
|
|
|
|
struct named_arg : view, named_arg_base<Char> {
|
|
|
|
|
const T& value;
|
|
|
|
|
|
|
|
|
|
named_arg(basic_string_view<Char> name, const T& val)
|
|
|
|
@ -1376,7 +1632,6 @@ inline void check_format_string(const S&) {
|
|
|
|
|
template <typename..., typename S, FMT_ENABLE_IF(is_compile_string<S>::value)>
|
|
|
|
|
void check_format_string(S);
|
|
|
|
|
|
|
|
|
|
struct view {};
|
|
|
|
|
template <bool...> struct bool_pack;
|
|
|
|
|
template <bool... Args>
|
|
|
|
|
using all_true =
|
|
|
|
@ -1386,32 +1641,38 @@ template <typename... Args, typename S, typename Char = char_t<S>>
|
|
|
|
|
inline format_arg_store<buffer_context<Char>, remove_reference_t<Args>...>
|
|
|
|
|
make_args_checked(const S& format_str,
|
|
|
|
|
const remove_reference_t<Args>&... args) {
|
|
|
|
|
static_assert(all_true<(!std::is_base_of<view, remove_reference_t<Args>>() ||
|
|
|
|
|
!std::is_reference<Args>())...>::value,
|
|
|
|
|
"passing views as lvalues is disallowed");
|
|
|
|
|
check_format_string<remove_const_t<remove_reference_t<Args>>...>(format_str);
|
|
|
|
|
static_assert(
|
|
|
|
|
all_true<(!std::is_base_of<view, remove_reference_t<Args>>::value ||
|
|
|
|
|
!std::is_reference<Args>::value)...>::value,
|
|
|
|
|
"passing views as lvalues is disallowed");
|
|
|
|
|
check_format_string<Args...>(format_str);
|
|
|
|
|
return {args...};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
template <typename Char>
|
|
|
|
|
std::basic_string<Char> vformat(basic_string_view<Char> format_str,
|
|
|
|
|
basic_format_args<buffer_context<Char>> args);
|
|
|
|
|
std::basic_string<Char> vformat(
|
|
|
|
|
basic_string_view<Char> format_str,
|
|
|
|
|
basic_format_args<buffer_context<type_identity_t<Char>>> args);
|
|
|
|
|
|
|
|
|
|
template <typename Char>
|
|
|
|
|
typename buffer_context<Char>::iterator vformat_to(
|
|
|
|
|
buffer<Char>& buf, basic_string_view<Char> format_str,
|
|
|
|
|
basic_format_args<buffer_context<Char>> args);
|
|
|
|
|
basic_format_args<buffer_context<type_identity_t<Char>>> args);
|
|
|
|
|
|
|
|
|
|
template <typename Char, typename Args,
|
|
|
|
|
FMT_ENABLE_IF(!std::is_same<Char, char>::value)>
|
|
|
|
|
inline void vprint_mojibake(std::FILE*, basic_string_view<Char>, const Args&) {}
|
|
|
|
|
|
|
|
|
|
FMT_API void vprint_mojibake(std::FILE*, string_view, format_args);
|
|
|
|
|
#ifndef _WIN32
|
|
|
|
|
inline void vprint_mojibake(std::FILE*, string_view, format_args) {}
|
|
|
|
|
#endif
|
|
|
|
|
} // namespace internal
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
\rst
|
|
|
|
|
Returns a named argument to be used in a formatting function.
|
|
|
|
|
|
|
|
|
|
The named argument holds a reference and does not extend the lifetime
|
|
|
|
|
of its arguments.
|
|
|
|
|
Consequently, a dangling reference can accidentally be created.
|
|
|
|
|
The user should take care to only pass this function temporaries when
|
|
|
|
|
the named argument is itself a temporary, as per the following example.
|
|
|
|
|
Returns a named argument to be used in a formatting function. It should only
|
|
|
|
|
be used in a call to a formatting function.
|
|
|
|
|
|
|
|
|
|
**Example**::
|
|
|
|
|
|
|
|
|
@ -1434,8 +1695,9 @@ void arg(S, internal::named_arg<T, Char>) = delete;
|
|
|
|
|
template <typename OutputIt, typename S, typename Char = char_t<S>,
|
|
|
|
|
FMT_ENABLE_IF(
|
|
|
|
|
internal::is_contiguous_back_insert_iterator<OutputIt>::value)>
|
|
|
|
|
OutputIt vformat_to(OutputIt out, const S& format_str,
|
|
|
|
|
basic_format_args<buffer_context<Char>> args) {
|
|
|
|
|
OutputIt vformat_to(
|
|
|
|
|
OutputIt out, const S& format_str,
|
|
|
|
|
basic_format_args<buffer_context<type_identity_t<Char>>> args) {
|
|
|
|
|
using container = remove_reference_t<decltype(internal::get_container(out))>;
|
|
|
|
|
internal::container_buffer<container> buf((internal::get_container(out)));
|
|
|
|
|
internal::vformat_to(buf, to_string_view(format_str), args);
|
|
|
|
@ -1448,14 +1710,14 @@ template <typename Container, typename S, typename... Args,
|
|
|
|
|
inline std::back_insert_iterator<Container> format_to(
|
|
|
|
|
std::back_insert_iterator<Container> out, const S& format_str,
|
|
|
|
|
Args&&... args) {
|
|
|
|
|
return vformat_to(
|
|
|
|
|
out, to_string_view(format_str),
|
|
|
|
|
{internal::make_args_checked<Args...>(format_str, args...)});
|
|
|
|
|
return vformat_to(out, to_string_view(format_str),
|
|
|
|
|
internal::make_args_checked<Args...>(format_str, args...));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
template <typename S, typename Char = char_t<S>>
|
|
|
|
|
inline std::basic_string<Char> vformat(
|
|
|
|
|
const S& format_str, basic_format_args<buffer_context<Char>> args) {
|
|
|
|
|
const S& format_str,
|
|
|
|
|
basic_format_args<buffer_context<type_identity_t<Char>>> args) {
|
|
|
|
|
return internal::vformat(to_string_view(format_str), args);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -1475,44 +1737,52 @@ template <typename S, typename... Args, typename Char = char_t<S>>
|
|
|
|
|
inline std::basic_string<Char> format(const S& format_str, Args&&... args) {
|
|
|
|
|
return internal::vformat(
|
|
|
|
|
to_string_view(format_str),
|
|
|
|
|
{internal::make_args_checked<Args...>(format_str, args...)});
|
|
|
|
|
internal::make_args_checked<Args...>(format_str, args...));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
FMT_API void vprint(std::FILE* f, string_view format_str, format_args args);
|
|
|
|
|
FMT_API void vprint(string_view format_str, format_args args);
|
|
|
|
|
FMT_API void vprint(string_view, format_args);
|
|
|
|
|
FMT_API void vprint(std::FILE*, string_view, format_args);
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
\rst
|
|
|
|
|
Prints formatted data to the file *f*. For wide format strings,
|
|
|
|
|
*f* should be in wide-oriented mode set via ``fwide(f, 1)`` or
|
|
|
|
|
``_setmode(_fileno(f), _O_U8TEXT)`` on Windows.
|
|
|
|
|
Formats ``args`` according to specifications in ``format_str`` and writes the
|
|
|
|
|
output to the file ``f``. Strings are assumed to be Unicode-encoded unless the
|
|
|
|
|
``FMT_UNICODE`` macro is set to 0.
|
|
|
|
|
|
|
|
|
|
**Example**::
|
|
|
|
|
|
|
|
|
|
fmt::print(stderr, "Don't {}!", "panic");
|
|
|
|
|
\endrst
|
|
|
|
|
*/
|
|
|
|
|
template <typename S, typename... Args,
|
|
|
|
|
FMT_ENABLE_IF(internal::is_string<S>::value)>
|
|
|
|
|
template <typename S, typename... Args, typename Char = char_t<S>>
|
|
|
|
|
inline void print(std::FILE* f, const S& format_str, Args&&... args) {
|
|
|
|
|
vprint(f, to_string_view(format_str),
|
|
|
|
|
internal::make_args_checked<Args...>(format_str, args...));
|
|
|
|
|
return internal::is_unicode<Char>()
|
|
|
|
|
? vprint(f, to_string_view(format_str),
|
|
|
|
|
internal::make_args_checked<Args...>(format_str, args...))
|
|
|
|
|
: internal::vprint_mojibake(
|
|
|
|
|
f, to_string_view(format_str),
|
|
|
|
|
internal::make_args_checked<Args...>(format_str, args...));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
\rst
|
|
|
|
|
Prints formatted data to ``stdout``.
|
|
|
|
|
Formats ``args`` according to specifications in ``format_str`` and writes
|
|
|
|
|
the output to ``stdout``. Strings are assumed to be Unicode-encoded unless
|
|
|
|
|
the ``FMT_UNICODE`` macro is set to 0.
|
|
|
|
|
|
|
|
|
|
**Example**::
|
|
|
|
|
|
|
|
|
|
fmt::print("Elapsed time: {0:.2f} seconds", 1.23);
|
|
|
|
|
\endrst
|
|
|
|
|
*/
|
|
|
|
|
template <typename S, typename... Args,
|
|
|
|
|
FMT_ENABLE_IF(internal::is_string<S>::value)>
|
|
|
|
|
template <typename S, typename... Args, typename Char = char_t<S>>
|
|
|
|
|
inline void print(const S& format_str, Args&&... args) {
|
|
|
|
|
vprint(to_string_view(format_str),
|
|
|
|
|
internal::make_args_checked<Args...>(format_str, args...));
|
|
|
|
|
return internal::is_unicode<Char>()
|
|
|
|
|
? vprint(to_string_view(format_str),
|
|
|
|
|
internal::make_args_checked<Args...>(format_str, args...))
|
|
|
|
|
: internal::vprint_mojibake(
|
|
|
|
|
stdout, to_string_view(format_str),
|
|
|
|
|
internal::make_args_checked<Args...>(format_str, args...));
|
|
|
|
|
}
|
|
|
|
|
FMT_END_NAMESPACE
|
|
|
|
|
|
|
|
|
|