#include <vector>
// The fmt library version in the form major * 10000 + minor * 100 + patch.
-#define FMT_VERSION 60200
-
-#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__) && \
- !(defined(__INTEL_COMPILER) && __INTEL_COMPILER < 1600)
-# define FMT_HAS_INCLUDE(x) __has_include(x)
-#else
-# define FMT_HAS_INCLUDE(x) 0
-#endif
-
-#ifdef __has_cpp_attribute
-# define FMT_HAS_CPP_ATTRIBUTE(x) __has_cpp_attribute(x)
-#else
-# 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))
+#define FMT_VERSION 70002
#ifdef __clang__
# define FMT_CLANG_VERSION (__clang_major__ * 100 + __clang_minor__)
# define FMT_GCC_VERSION 0
#endif
+#if defined(__INTEL_COMPILER)
+# define FMT_ICC_VERSION __INTEL_COMPILER
+#else
+# define FMT_ICC_VERSION 0
+#endif
+
#if __cplusplus >= 201103L || defined(__GXX_EXPERIMENTAL_CXX0X__)
# define FMT_HAS_GXX_CXX11 FMT_GCC_VERSION
#else
#ifdef _MSC_VER
# define FMT_MSC_VER _MSC_VER
+# define FMT_SUPPRESS_MSC_WARNING(n) __pragma(warning(suppress : n))
#else
# define FMT_MSC_VER 0
+# define FMT_SUPPRESS_MSC_WARNING(n)
+#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)
+# define FMT_HAS_INCLUDE(x) __has_include(x)
+#else
+# define FMT_HAS_INCLUDE(x) 0
+#endif
+
+#ifdef __has_cpp_attribute
+# define FMT_HAS_CPP_ATTRIBUTE(x) __has_cpp_attribute(x)
+#else
+# 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))
+
// 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_NVCC && !FMT_ICC_VERSION
#endif
#if FMT_USE_CONSTEXPR
# define FMT_CONSTEXPR constexpr
# 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_CPP14_ATTRIBUTE(deprecated) || FMT_MSC_VER >= 1900
# define FMT_DEPRECATED [[deprecated]]
#endif
// Workaround broken [[deprecated]] in the Intel, PGI and NVCC compilers.
-#if defined(__INTEL_COMPILER) || defined(__PGI) || FMT_NVCC
+#if FMT_ICC_VERSION || defined(__PGI) || FMT_NVCC
# define FMT_DEPRECATED_ALIAS
#else
# define FMT_DEPRECATED_ALIAS FMT_DEPRECATED
#endif
+#ifndef FMT_INLINE
+# if FMT_GCC_VERSION || FMT_CLANG_VERSION
+# define FMT_INLINE inline __attribute__((always_inline))
+# else
+# define FMT_INLINE inline
+# endif
+#endif
+
#ifndef FMT_BEGIN_NAMESPACE
# if FMT_HAS_FEATURE(cxx_inline_namespaces) || FMT_GCC_VERSION >= 404 || \
FMT_MSC_VER >= 1900
# define FMT_INLINE_NAMESPACE namespace
# define FMT_END_NAMESPACE \
} \
- using namespace v6; \
+ using namespace v7; \
}
# endif
# define FMT_BEGIN_NAMESPACE \
namespace fmt { \
- FMT_INLINE_NAMESPACE v6 {
+ FMT_INLINE_NAMESPACE v7 {
#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
+# define FMT_CLASS_API FMT_SUPPRESS_MSC_WARNING(4275)
# ifdef FMT_EXPORT
# define FMT_API __declspec(dllexport)
+# define FMT_EXTERN_TEMPLATE_API FMT_API
+# define FMT_EXPORTED
# elif defined(FMT_SHARED)
# define FMT_API __declspec(dllimport)
# define FMT_EXTERN_TEMPLATE_API FMT_API
# endif
-#endif
-#ifndef FMT_CLASS_API
+#else
# define FMT_CLASS_API
#endif
#ifndef 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
+# define FMT_API
#endif
#ifndef FMT_EXTERN_TEMPLATE_API
# define FMT_EXTERN_TEMPLATE_API
// to workaround a bug in MSVC 2019 (see #1140 and #1186).
#define FMT_ENABLE_IF(...) enable_if_t<(__VA_ARGS__), int> = 0
-namespace internal {
+namespace detail {
// 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; };
+template <typename T> constexpr T const_check(T value) { return value; }
FMT_NORETURN FMT_API void assert_fail(const char* file, int line,
const char* message);
# define FMT_ASSERT(condition, message) \
((condition) /* void() fails with -Winvalid-constexpr on clang 4.0.1 */ \
? (void)0 \
- : ::fmt::internal::assert_fail(__FILE__, __LINE__, (message)))
+ : ::fmt::detail::assert_fail(__FILE__, __LINE__, (message)))
# endif
#endif
return static_cast<typename std::make_unsigned<Int>::type>(value);
}
-constexpr unsigned char micro[] = "\u00B5";
+FMT_SUPPRESS_MSC_WARNING(4566) constexpr unsigned char micro[] = "\u00B5";
template <typename Char> constexpr bool is_unicode() {
return FMT_UNICODE || sizeof(Char) != 1 ||
#else
enum char8_type : unsigned char {};
#endif
-} // namespace internal
+} // namespace detail
-template <typename... Ts>
-using void_t = typename internal::void_t_impl<Ts...>::type;
+#ifdef FMT_USE_INTERNAL
+namespace internal = detail; // DEPRECATED
+#endif
/**
An implementation of ``std::basic_string_view`` for pre-C++17. It provides a
size_t size_;
public:
- 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) {}
+ constexpr basic_string_view() FMT_NOEXCEPT : data_(nullptr), size_(0) {}
/** Constructs a string reference object from a C string and a size. */
- FMT_CONSTEXPR basic_string_view(const Char* s, size_t count) FMT_NOEXCEPT
+ constexpr basic_string_view(const Char* s, size_t count) FMT_NOEXCEPT
: data_(s),
size_(count) {}
: data_(s.data()),
size_(s.size()) {}
- template <
- typename S,
- FMT_ENABLE_IF(std::is_same<S, internal::std_string_view<Char>>::value)>
+ 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()) {}
/** Returns a pointer to the string data. */
- FMT_CONSTEXPR const Char* data() const { return data_; }
+ constexpr const Char* data() const { return data_; }
/** Returns the string size. */
- FMT_CONSTEXPR size_t size() const { return size_; }
+ constexpr size_t size() const { return size_; }
- FMT_CONSTEXPR iterator begin() const { return data_; }
- FMT_CONSTEXPR iterator end() const { return data_ + size_; }
+ constexpr iterator begin() const { return data_; }
+ constexpr iterator end() const { return data_ + size_; }
- FMT_CONSTEXPR const Char& operator[](size_t pos) const { return data_[pos]; }
+ constexpr const Char& operator[](size_t pos) const { return data_[pos]; }
FMT_CONSTEXPR void remove_prefix(size_t n) {
data_ += n;
using string_view = basic_string_view<char>;
using wstring_view = basic_string_view<wchar_t>;
-#ifndef __cpp_char8_t
-// 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<internal::char8_type> : std::true_type {};
+template <> struct is_char<detail::char8_type> : std::true_type {};
template <> struct is_char<char16_t> : std::true_type {};
template <> struct is_char<char32_t> : std::true_type {};
}
template <typename Char,
- FMT_ENABLE_IF(!std::is_empty<internal::std_string_view<Char>>::value)>
-inline basic_string_view<Char> to_string_view(
- internal::std_string_view<Char> s) {
+ FMT_ENABLE_IF(!std::is_empty<detail::std_string_view<Char>>::value)>
+inline basic_string_view<Char> to_string_view(detail::std_string_view<Char> s) {
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("{}"), 42).
+// make formatting functions visible via ADL, e.g. format(FMT_STRING("{}"), 42).
struct compile_string {};
template <typename S>
return s;
}
-namespace internal {
+namespace detail {
void to_string_view(...);
-using fmt::v6::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
};
struct error_handler {
- FMT_CONSTEXPR error_handler() = default;
- FMT_CONSTEXPR error_handler(const error_handler&) = default;
+ 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);
};
-} // namespace internal
+} // namespace detail
/** String's character type. */
-template <typename S> using char_t = typename internal::char_t_impl<S>::type;
+template <typename S> using char_t = typename detail::char_t_impl<S>::type;
/**
\rst
+-----------------------+-------------------------------------+
\endrst
*/
-template <typename Char, typename ErrorHandler = internal::error_handler>
+template <typename Char, typename ErrorHandler = detail::error_handler>
class basic_format_parse_context : private ErrorHandler {
private:
basic_string_view<Char> format_str_;
using char_type = Char;
using iterator = typename basic_string_view<Char>::iterator;
- explicit FMT_CONSTEXPR basic_format_parse_context(
- basic_string_view<Char> format_str, ErrorHandler eh = ErrorHandler())
+ explicit constexpr basic_format_parse_context(
+ basic_string_view<Char> format_str, ErrorHandler eh = {})
: ErrorHandler(eh), format_str_(format_str), next_arg_id_(0) {}
/**
Returns an iterator to the beginning of the format string range being
parsed.
*/
- FMT_CONSTEXPR iterator begin() const FMT_NOEXCEPT {
- return format_str_.begin();
- }
+ constexpr iterator begin() const FMT_NOEXCEPT { return format_str_.begin(); }
/**
Returns an iterator past the end of the format string range being parsed.
*/
- FMT_CONSTEXPR iterator end() const FMT_NOEXCEPT { return format_str_.end(); }
+ constexpr iterator end() const FMT_NOEXCEPT { return format_str_.end(); }
/** Advances the begin iterator to ``it``. */
FMT_CONSTEXPR void advance_to(iterator it) {
- format_str_.remove_prefix(internal::to_unsigned(it - begin()));
+ format_str_.remove_prefix(detail::to_unsigned(it - begin()));
}
/**
the next argument index and switches to the automatic indexing.
*/
FMT_CONSTEXPR int next_arg_id() {
+ // 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;
ErrorHandler::on_error(message);
}
- FMT_CONSTEXPR ErrorHandler error_handler() const { return *this; }
+ constexpr ErrorHandler error_handler() const { return *this; }
};
using format_parse_context = basic_format_parse_context<char>;
using wformat_parse_context = basic_format_parse_context<wchar_t>;
-template <typename Char, typename ErrorHandler = internal::error_handler>
-using basic_parse_context FMT_DEPRECATED_ALIAS =
- basic_format_parse_context<Char, ErrorHandler>;
-using parse_context FMT_DEPRECATED_ALIAS = basic_format_parse_context<char>;
-using wparse_context FMT_DEPRECATED_ALIAS = basic_format_parse_context<wchar_t>;
-
template <typename Context> class basic_format_arg;
template <typename Context> class basic_format_args;
+template <typename Context> class dynamic_format_arg_store;
// A formatter for objects of type T.
template <typename T, typename Char = char, typename Enable = void>
formatter() = delete;
};
-template <typename T, typename Char, typename Enable = void>
-struct FMT_DEPRECATED convert_to_int
- : bool_constant<!std::is_arithmetic<T>::value &&
- std::is_convertible<T, int>::value> {};
-
// Specifies if T has an enabled formatter specialization. A type can be
// formattable even if it doesn't have a formatter e.g. via a conversion.
template <typename T, typename Context>
using has_formatter =
std::is_constructible<typename Context::template formatter_type<T>>;
-namespace internal {
+namespace detail {
-/** A contiguous memory buffer with an optional growing ability. */
+/**
+ \rst
+ A contiguous memory buffer with an optional growing ability. It is an internal
+ class and shouldn't be used directly, only via `~fmt::basic_memory_buffer`.
+ \endrst
+ */
template <typename T> class buffer {
private:
T* ptr_;
- std::size_t size_;
- std::size_t capacity_;
+ size_t size_;
+ size_t capacity_;
protected:
// Don't initialize ptr_ since it is not accessed to save a few cycles.
- buffer(std::size_t sz) FMT_NOEXCEPT : size_(sz), capacity_(sz) {}
+ FMT_SUPPRESS_MSC_WARNING(26495)
+ buffer(size_t sz) FMT_NOEXCEPT : size_(sz), capacity_(sz) {}
- buffer(T* p = nullptr, std::size_t sz = 0, std::size_t cap = 0) FMT_NOEXCEPT
+ buffer(T* p = nullptr, size_t sz = 0, size_t cap = 0) FMT_NOEXCEPT
: ptr_(p),
size_(sz),
capacity_(cap) {}
/** Sets the buffer data and capacity. */
- void set(T* buf_data, std::size_t buf_capacity) FMT_NOEXCEPT {
+ void set(T* buf_data, size_t buf_capacity) FMT_NOEXCEPT {
ptr_ = buf_data;
capacity_ = buf_capacity;
}
/** Increases the buffer capacity to hold at least *capacity* elements. */
- virtual void grow(std::size_t capacity) = 0;
+ virtual void grow(size_t capacity) = 0;
public:
using value_type = T;
const T* end() const FMT_NOEXCEPT { return ptr_ + size_; }
/** Returns the size of this buffer. */
- std::size_t size() const FMT_NOEXCEPT { return size_; }
+ size_t size() const FMT_NOEXCEPT { return size_; }
/** Returns the capacity of this buffer. */
- std::size_t capacity() const FMT_NOEXCEPT { return capacity_; }
+ size_t capacity() const FMT_NOEXCEPT { return capacity_; }
/** Returns a pointer to the buffer data. */
T* data() FMT_NOEXCEPT { return ptr_; }
/**
Resizes the buffer. If T is a POD type new elements may not be initialized.
*/
- void resize(std::size_t new_size) {
+ void resize(size_t new_size) {
reserve(new_size);
size_ = new_size;
}
void clear() { size_ = 0; }
/** Reserves space to store at least *capacity* elements. */
- void reserve(std::size_t new_capacity) {
+ void reserve(size_t new_capacity) {
if (new_capacity > capacity_) grow(new_capacity);
}
Container& container_;
protected:
- void grow(std::size_t capacity) FMT_OVERRIDE {
+ void grow(size_t capacity) FMT_OVERRIDE {
container_.resize(capacity);
this->set(&container_[0], capacity);
}
using has_fallback_formatter =
std::is_constructible<fallback_formatter<T, typename Context::char_type>>;
-template <typename Char> struct named_arg_base;
-template <typename T, typename Char> struct named_arg;
+struct view {};
+
+template <typename Char, typename T> struct named_arg : view {
+ const Char* name;
+ const T& value;
+ named_arg(const Char* n, const T& v) : name(n), value(v) {}
+};
+
+template <typename Char> struct named_arg_info {
+ const Char* name;
+ int id;
+};
+
+template <typename T, typename Char, size_t NUM_ARGS, size_t NUM_NAMED_ARGS>
+struct arg_data {
+ // args_[0].named_args points to named_args_ to avoid bloating format_args.
+ T args_[1 + (NUM_ARGS != 0 ? NUM_ARGS : 1)];
+ named_arg_info<Char> named_args_[NUM_NAMED_ARGS];
+
+ template <typename... U>
+ arg_data(const U&... init) : args_{T(named_args_, NUM_NAMED_ARGS), init...} {}
+ arg_data(const arg_data& other) = delete;
+ const T* args() const { return args_ + 1; }
+ named_arg_info<Char>* named_args() { return named_args_; }
+};
+
+template <typename T, typename Char, size_t NUM_ARGS>
+struct arg_data<T, Char, NUM_ARGS, 0> {
+ T args_[NUM_ARGS != 0 ? NUM_ARGS : 1];
+
+ template <typename... U>
+ FMT_INLINE arg_data(const U&... init) : args_{init...} {}
+ FMT_INLINE const T* args() const { return args_; }
+ FMT_INLINE std::nullptr_t named_args() { return nullptr; }
+};
+
+template <typename Char>
+inline void init_named_args(named_arg_info<Char>*, int, int) {}
+
+template <typename Char, typename T, typename... Tail>
+void init_named_args(named_arg_info<Char>* named_args, int arg_count,
+ int named_arg_count, const T&, const Tail&... args) {
+ init_named_args(named_args, arg_count + 1, named_arg_count, args...);
+}
+
+template <typename Char, typename T, typename... Tail>
+void init_named_args(named_arg_info<Char>* named_args, int arg_count,
+ int named_arg_count, const named_arg<Char, T>& arg,
+ const Tail&... args) {
+ named_args[named_arg_count++] = {arg.name, arg_count};
+ init_named_args(named_args, arg_count + 1, named_arg_count, args...);
+}
+
+template <typename... Args>
+FMT_INLINE void init_named_args(std::nullptr_t, int, int, const Args&...) {}
+
+template <typename T> struct is_named_arg : std::false_type {};
+
+template <typename T, typename Char>
+struct is_named_arg<named_arg<Char, T>> : std::true_type {};
+
+template <bool B = false> constexpr size_t count() { return B ? 1 : 0; }
+template <bool B1, bool B2, bool... Tail> constexpr size_t count() {
+ return (B1 ? 1 : 0) + count<B2, Tail...>();
+}
+
+template <typename... Args> constexpr size_t count_named_args() {
+ return count<is_named_arg<Args>::value...>();
+}
enum class type {
none_type,
- named_arg_type,
// Integer types should go first,
int_type,
uint_type,
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);
FMT_TYPE_CONSTANT(unsigned, uint_type);
FMT_TYPE_CONSTANT(long long, long_long_type);
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 != type::named_arg_type, "invalid argument type");
+constexpr bool is_integral_type(type t) {
return t > type::none_type && t <= type::last_integer_type;
}
-FMT_CONSTEXPR bool is_arithmetic_type(type t) {
- FMT_ASSERT(t != type::named_arg_type, "invalid argument type");
+constexpr bool is_arithmetic_type(type t) {
return t > type::none_type && t <= type::last_numeric_type;
}
template <typename Char> struct string_value {
const Char* data;
- std::size_t size;
+ size_t size;
+};
+
+template <typename Char> struct named_arg_value {
+ const named_arg_info<Char>* data;
+ size_t size;
};
template <typename Context> struct custom_value {
- using parse_context = basic_format_parse_context<typename Context::char_type>;
+ using parse_context = typename Context::parse_context_type;
const void* value;
void (*format)(const void* arg, parse_context& parse_ctx, Context& ctx);
};
const void* pointer;
string_value<char_type> string;
custom_value<Context> custom;
- const named_arg_base<char_type>* named_arg;
+ named_arg_value<char_type> named_args;
};
- FMT_CONSTEXPR value(int val = 0) : int_value(val) {}
- FMT_CONSTEXPR value(unsigned val) : uint_value(val) {}
- value(long long val) : long_long_value(val) {}
- value(unsigned long long val) : ulong_long_value(val) {}
- value(int128_t val) : int128_value(val) {}
- value(uint128_t val) : uint128_value(val) {}
- value(float val) : float_value(val) {}
- value(double val) : double_value(val) {}
- value(long double val) : long_double_value(val) {}
- value(bool val) : bool_value(val) {}
- value(char_type val) : char_value(val) {}
- value(const char_type* val) { string.data = val; }
- value(basic_string_view<char_type> val) {
+ constexpr FMT_INLINE value(int val = 0) : int_value(val) {}
+ constexpr FMT_INLINE value(unsigned val) : uint_value(val) {}
+ FMT_INLINE value(long long val) : long_long_value(val) {}
+ 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(long double val) : long_double_value(val) {}
+ FMT_INLINE value(bool val) : bool_value(val) {}
+ FMT_INLINE value(char_type val) : char_value(val) {}
+ FMT_INLINE value(const char_type* val) { string.data = val; }
+ FMT_INLINE value(basic_string_view<char_type> val) {
string.data = val.data();
string.size = val.size();
}
- value(const void* val) : pointer(val) {}
+ FMT_INLINE value(const void* val) : pointer(val) {}
+ FMT_INLINE value(const named_arg_info<char_type>* args, size_t size)
+ : named_args{args, size} {}
- template <typename T> value(const T& val) {
+ template <typename T> FMT_INLINE value(const T& val) {
custom.value = &val;
// Get the formatter type through the context to allow different contexts
// have different extension points, e.g. `formatter<T>` for `format` and
fallback_formatter<T, char_type>>>;
}
- value(const named_arg_base<char_type>& val) { named_arg = &val; }
-
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, basic_format_parse_context<char_type>& parse_ctx,
- Context& ctx) {
+ static void format_custom_arg(const void* arg,
+ typename Context::parse_context_type& parse_ctx,
+ Context& ctx) {
Formatter f;
parse_ctx.advance_to(f.parse(parse_ctx));
ctx.advance_to(f.format(*static_cast<const T*>(arg), ctx));
static_assert(std::is_same<char_type, char>::value, "invalid string type");
return reinterpret_cast<const char*>(val);
}
+ FMT_CONSTEXPR const char* map(signed char* val) {
+ const auto* const_val = val;
+ return map(const_val);
+ }
+ FMT_CONSTEXPR const char* map(unsigned char* val) {
+ const auto* const_val = val;
+ return map(const_val);
+ }
FMT_CONSTEXPR const void* map(void* val) { return val; }
FMT_CONSTEXPR const void* map(const void* val) { return val; }
}
template <typename T>
- FMT_CONSTEXPR const named_arg_base<char_type>& map(
- const named_arg<T, char_type>& val) {
- auto arg = make_arg<Context>(val.value);
- std::memcpy(val.data, &arg, sizeof(arg));
- return val;
+ FMT_CONSTEXPR auto map(const named_arg<char_type, T>& val)
+ -> decltype(std::declval<arg_mapper>().map(val.value)) {
+ return map(val.value);
}
int map(...) {
type_constant<decltype(arg_mapper<Context>().map(std::declval<const T&>())),
typename Context::char_type>;
-enum { packed_arg_bits = 5 };
+enum { packed_arg_bits = 4 };
// Maximum number of arguments with packed types.
-enum { max_packed_args = 63 / packed_arg_bits };
+enum { max_packed_args = 62 / packed_arg_bits };
enum : unsigned long long { is_unpacked_bit = 1ULL << 63 };
-
-template <typename Context> class arg_map;
-} // namespace internal
+enum : unsigned long long { has_named_args_bit = 1ULL << 62 };
+} // namespace detail
// A formatting argument. It is a trivially copyable/constructible type to
// allow storage in basic_memory_buffer.
template <typename Context> class basic_format_arg {
private:
- internal::value<Context> value_;
- internal::type type_;
+ detail::value<Context> value_;
+ detail::type type_;
template <typename ContextType, typename T>
- friend FMT_CONSTEXPR basic_format_arg<ContextType> internal::make_arg(
+ friend FMT_CONSTEXPR basic_format_arg<ContextType> detail::make_arg(
const T& value);
template <typename Visitor, typename Ctx>
-> decltype(vis(0));
friend class basic_format_args<Context>;
- friend class internal::arg_map<Context>;
+ friend class dynamic_format_arg_store<Context>;
using char_type = typename Context::char_type;
+ template <typename T, typename Char, size_t NUM_ARGS, size_t NUM_NAMED_ARGS>
+ friend struct detail::arg_data;
+
+ basic_format_arg(const detail::named_arg_info<char_type>* args, size_t size)
+ : value_(args, size) {}
+
public:
class handle {
public:
- explicit handle(internal::custom_value<Context> custom) : custom_(custom) {}
+ explicit handle(detail::custom_value<Context> custom) : custom_(custom) {}
- void format(basic_format_parse_context<char_type>& parse_ctx,
+ void format(typename Context::parse_context_type& parse_ctx,
Context& ctx) const {
custom_.format(custom_.value, parse_ctx, ctx);
}
private:
- internal::custom_value<Context> custom_;
+ detail::custom_value<Context> custom_;
};
- FMT_CONSTEXPR basic_format_arg() : type_(internal::type::none_type) {}
+ constexpr basic_format_arg() : type_(detail::type::none_type) {}
- FMT_CONSTEXPR explicit operator bool() const FMT_NOEXCEPT {
- return type_ != internal::type::none_type;
+ constexpr explicit operator bool() const FMT_NOEXCEPT {
+ return type_ != detail::type::none_type;
}
- internal::type type() const { return type_; }
+ detail::type type() const { return type_; }
- bool is_integral() const { return internal::is_integral_type(type_); }
- bool is_arithmetic() const { return internal::is_arithmetic_type(type_); }
+ bool is_integral() const { return detail::is_integral_type(type_); }
+ bool is_arithmetic() const { return detail::is_arithmetic_type(type_); }
};
/**
\endrst
*/
template <typename Visitor, typename Context>
-FMT_CONSTEXPR auto visit_format_arg(Visitor&& vis,
- const basic_format_arg<Context>& arg)
- -> decltype(vis(0)) {
+FMT_CONSTEXPR_DECL FMT_INLINE auto visit_format_arg(
+ Visitor&& vis, const basic_format_arg<Context>& arg) -> decltype(vis(0)) {
using char_type = typename Context::char_type;
switch (arg.type_) {
- case internal::type::none_type:
+ case detail::type::none_type:
break;
- case internal::type::named_arg_type:
- FMT_ASSERT(false, "invalid argument type");
- break;
- case internal::type::int_type:
+ case detail::type::int_type:
return vis(arg.value_.int_value);
- case internal::type::uint_type:
+ case detail::type::uint_type:
return vis(arg.value_.uint_value);
- case internal::type::long_long_type:
+ case detail::type::long_long_type:
return vis(arg.value_.long_long_value);
- case internal::type::ulong_long_type:
+ case detail::type::ulong_long_type:
return vis(arg.value_.ulong_long_value);
#if FMT_USE_INT128
- case internal::type::int128_type:
+ case detail::type::int128_type:
return vis(arg.value_.int128_value);
- case internal::type::uint128_type:
+ case detail::type::uint128_type:
return vis(arg.value_.uint128_value);
#else
- case internal::type::int128_type:
- case internal::type::uint128_type:
+ case detail::type::int128_type:
+ case detail::type::uint128_type:
break;
#endif
- case internal::type::bool_type:
+ case detail::type::bool_type:
return vis(arg.value_.bool_value);
- case internal::type::char_type:
+ case detail::type::char_type:
return vis(arg.value_.char_value);
- case internal::type::float_type:
+ case detail::type::float_type:
return vis(arg.value_.float_value);
- case internal::type::double_type:
+ case detail::type::double_type:
return vis(arg.value_.double_value);
- case internal::type::long_double_type:
+ case detail::type::long_double_type:
return vis(arg.value_.long_double_value);
- case internal::type::cstring_type:
+ case detail::type::cstring_type:
return vis(arg.value_.string.data);
- case internal::type::string_type:
+ case detail::type::string_type:
return vis(basic_string_view<char_type>(arg.value_.string.data,
arg.value_.string.size));
- case internal::type::pointer_type:
+ case detail::type::pointer_type:
return vis(arg.value_.pointer);
- case internal::type::custom_type:
+ case detail::type::custom_type:
return vis(typename basic_format_arg<Context>::handle(arg.value_.custom));
}
return vis(monostate());
}
-namespace internal {
-// A map from argument names to their values for named arguments.
-template <typename Context> class arg_map {
- private:
- using char_type = typename Context::char_type;
-
- struct entry {
- basic_string_view<char_type> name;
- basic_format_arg<Context> arg;
- };
+// Checks whether T is a container with contiguous storage.
+template <typename T> struct is_contiguous : std::false_type {};
+template <typename Char>
+struct is_contiguous<std::basic_string<Char>> : std::true_type {};
+template <typename Char>
+struct is_contiguous<detail::buffer<Char>> : std::true_type {};
- entry* map_;
- unsigned size_;
+namespace detail {
- void push_back(value<Context> val) {
- const auto& named = *val.named_arg;
- map_[size_] = {named.name, named.template deserialize<Context>()};
- ++size_;
- }
+template <typename OutputIt>
+struct is_back_insert_iterator : std::false_type {};
+template <typename Container>
+struct is_back_insert_iterator<std::back_insert_iterator<Container>>
+ : std::true_type {};
- public:
- arg_map(const arg_map&) = delete;
- void operator=(const arg_map&) = delete;
- arg_map() : map_(nullptr), size_(0) {}
- void init(const basic_format_args<Context>& args);
- ~arg_map() { delete[] map_; }
-
- basic_format_arg<Context> find(basic_string_view<char_type> name) const {
- // The list is unsorted, so just return the first matching name.
- for (entry *it = map_, *end = map_ + size_; it != end; ++it) {
- if (it->name == name) return it->arg;
- }
- return {};
- }
-};
+template <typename OutputIt>
+struct is_contiguous_back_insert_iterator : std::false_type {};
+template <typename Container>
+struct is_contiguous_back_insert_iterator<std::back_insert_iterator<Container>>
+ : is_contiguous<Container> {};
// A type-erased reference to an std::locale to avoid heavy <locale> include.
class locale_ref {
return arg;
}
-template <bool IS_PACKED, typename Context, typename T,
+// The type template parameter is there to avoid an ODR violation when using
+// a fallback formatter in one translation unit and an implicit conversion in
+// another (not recommended).
+template <bool IS_PACKED, typename Context, type, typename T,
FMT_ENABLE_IF(IS_PACKED)>
inline value<Context> make_arg(const T& val) {
return arg_mapper<Context>().map(val);
}
-template <bool IS_PACKED, typename Context, typename T,
+template <bool IS_PACKED, typename Context, type, typename T,
FMT_ENABLE_IF(!IS_PACKED)>
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 {};
+template <typename T> const T& unwrap(const T& v) { return v; }
+template <typename T> const T& unwrap(const std::reference_wrapper<T>& v) {
+ return static_cast<const T&>(v);
+}
+
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
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);
+ auto new_node = std::unique_ptr<typed_node<T>>(new typed_node<T>(arg));
+ auto& value = new_node->value;
+ new_node->next = std::move(head_);
+ head_ = std::move(new_node);
return value;
}
};
-} // namespace internal
+} // namespace detail
// Formatting context.
template <typename OutputIt, typename Char> class basic_format_context {
private:
OutputIt out_;
basic_format_args<basic_format_context> args_;
- internal::arg_map<basic_format_context> map_;
- internal::locale_ref loc_;
+ detail::locale_ref loc_;
public:
using iterator = OutputIt;
using format_arg = basic_format_arg<basic_format_context>;
+ using parse_context_type = basic_format_parse_context<Char>;
template <typename T> using formatter_type = formatter<T, char_type>;
basic_format_context(const basic_format_context&) = delete;
*/
basic_format_context(OutputIt out,
basic_format_args<basic_format_context> ctx_args,
- internal::locale_ref loc = internal::locale_ref())
+ detail::locale_ref loc = detail::locale_ref())
: out_(out), args_(ctx_args), loc_(loc) {}
format_arg arg(int id) const { return args_.get(id); }
+ format_arg arg(basic_string_view<char_type> name) { return args_.get(name); }
+ int arg_id(basic_string_view<char_type> name) { return args_.get_id(name); }
+ const basic_format_args<basic_format_context>& args() const { return args_; }
- // Checks if manual indexing is used and returns the argument with the
- // specified name.
- format_arg arg(basic_string_view<char_type> name);
-
- internal::error_handler error_handler() { return {}; }
+ detail::error_handler error_handler() { return {}; }
void on_error(const char* message) { error_handler().on_error(message); }
// Returns an iterator to the beginning of the output range.
iterator out() { return out_; }
// Advances the begin iterator to ``it``.
- void advance_to(iterator it) { out_ = it; }
+ void advance_to(iterator it) {
+ if (!detail::is_back_insert_iterator<iterator>()) out_ = it;
+ }
- internal::locale_ref locale() { return loc_; }
+ detail::locale_ref locale() { return loc_; }
};
template <typename Char>
using buffer_context =
- basic_format_context<std::back_insert_iterator<internal::buffer<Char>>,
- Char>;
+ basic_format_context<std::back_insert_iterator<detail::buffer<Char>>, Char>;
using format_context = buffer_context<char>;
using wformat_context = buffer_context<wchar_t>;
+// Workaround a bug in gcc: https://stackoverflow.com/q/62767544/471164.
+#define FMT_BUFFER_CONTEXT(Char) \
+ basic_format_context<std::back_insert_iterator<detail::buffer<Char>>, Char>
+
/**
\rst
An array of references to arguments. It can be implicitly converted into
{
private:
static const size_t num_args = sizeof...(Args);
- static const bool is_packed = num_args < internal::max_packed_args;
+ static const size_t num_named_args = detail::count_named_args<Args...>();
+ static const bool is_packed = num_args <= detail::max_packed_args;
- using value_type = conditional_t<is_packed, internal::value<Context>,
+ using value_type = conditional_t<is_packed, detail::value<Context>,
basic_format_arg<Context>>;
- // If the arguments are not packed, add one more element to mark the end.
- value_type data_[num_args + (num_args == 0 ? 1 : 0)];
+ detail::arg_data<value_type, typename Context::char_type, num_args,
+ num_named_args>
+ data_;
friend class basic_format_args<Context>;
- public:
- static constexpr unsigned long long types =
- is_packed ? internal::encode_types<Context, Args...>()
- : internal::is_unpacked_bit | num_args;
+ static constexpr unsigned long long desc =
+ (is_packed ? detail::encode_types<Context, Args...>()
+ : detail::is_unpacked_bit | num_args) |
+ (num_named_args != 0
+ ? static_cast<unsigned long long>(detail::has_named_args_bit)
+ : 0);
+ public:
format_arg_store(const Args&... args)
:
#if FMT_GCC_VERSION && FMT_GCC_VERSION < 409
basic_format_args<Context>(*this),
#endif
- data_{internal::make_arg<is_packed, Context>(args)...} {
+ data_{detail::make_arg<
+ is_packed, Context,
+ detail::mapped_type_constant<Args, Context>::value>(args)...} {
+ detail::init_named_args(data_.named_args(), 0, 0, args...);
}
};
/**
\rst
- A dynamic version of `fmt::format_arg_store<>`.
- It's equipped with a storage to potentially temporary objects which lifetime
+ Returns a named argument to be used in a formatting function. It should only
+ be used in a call to a formatting function.
+
+ **Example**::
+
+ fmt::print("Elapsed time: {s:.2f} seconds", fmt::arg("s", 1.23));
+ \endrst
+ */
+template <typename Char, typename T>
+inline detail::named_arg<Char, T> arg(const Char* name, const T& arg) {
+ static_assert(!detail::is_named_arg<T>(), "nested named arguments");
+ return {name, arg};
+}
+
+/**
+ \rst
+ A dynamic version of `fmt::format_arg_store`.
+ It's equipped with a storage to potentially temporary objects which lifetimes
could be shorter than the format arguments object.
It can be implicitly converted into `~fmt::basic_format_args` for passing
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;
+ static constexpr detail::type mapped_type =
+ detail::mapped_type_constant<T, Context>::value;
enum {
- value = !(internal::is_reference_wrapper<T>::value ||
+ value = !(detail::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))
+ std::is_same<T, detail::std_string_view<char_type>>::value ||
+ (mapped_type != detail::type::cstring_type &&
+ mapped_type != detail::type::string_type &&
+ mapped_type != detail::type::custom_type))
};
};
template <typename T>
- using stored_type = conditional_t<internal::is_string<T>::value,
+ using stored_type = conditional_t<detail::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_;
+ std::vector<detail::named_arg_info<char_type>> named_info_;
// 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_;
+ detail::dynamic_arg_list dynamic_args_;
friend class basic_format_args<Context>;
unsigned long long get_types() const {
- return internal::is_unpacked_bit | data_.size();
+ return detail::is_unpacked_bit | data_.size() |
+ (named_info_.empty()
+ ? 0ULL
+ : static_cast<unsigned long long>(detail::has_named_args_bit));
+ }
+
+ const basic_format_arg<Context>* data() const {
+ return named_info_.empty() ? data_.data() : data_.data() + 1;
}
template <typename T> void emplace_arg(const T& arg) {
- data_.emplace_back(internal::make_arg<Context>(arg));
+ data_.emplace_back(detail::make_arg<Context>(arg));
+ }
+
+ template <typename T>
+ void emplace_arg(const detail::named_arg<char_type, T>& arg) {
+ if (named_info_.empty()) {
+ constexpr const detail::named_arg_info<char_type>* zero_ptr{nullptr};
+ data_.insert(data_.begin(), {zero_ptr, 0});
+ }
+ data_.emplace_back(detail::make_arg<Context>(detail::unwrap(arg.value)));
+ auto pop_one = [](std::vector<basic_format_arg<Context>>* data) {
+ data->pop_back();
+ };
+ std::unique_ptr<std::vector<basic_format_arg<Context>>, decltype(pop_one)>
+ guard{&data_, pop_one};
+ named_info_.push_back({arg.name, static_cast<int>(data_.size() - 2u)});
+ data_[0].value_.named_args = {named_info_.data(), named_info_.size()};
+ guard.release();
}
public:
/**
\rst
- Adds an argument into the dynamic store for later passing to a formating
+ Adds an argument into the dynamic store for later passing to a formatting
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).
+ Note that custom types and string types (but not string views) are copied
+ into the store dynamically allocating memory if necessary.
**Example**::
\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))
+ if (detail::const_check(need_copy<T>::value))
emplace_arg(dynamic_args_.push<stored_type<T>>(arg));
else
- emplace_arg(arg);
+ emplace_arg(detail::unwrap(arg));
}
/**
+ \rst
Adds a reference to the argument into the dynamic store for later passing to
- a formating function.
+ a formatting function. Supports named arguments wrapped in
+ ``std::reference_wrapper`` via ``std::ref()``/``std::cref()``.
+
+ **Example**::
+
+ fmt::dynamic_format_arg_store<fmt::format_context> store;
+ char str[] = "1234567890";
+ store.push_back(std::cref(str));
+ int a1_val{42};
+ auto a1 = fmt::arg("a1_", a1_val);
+ store.push_back(std::cref(a1));
+
+ // Changing str affects the output but only for string and custom types.
+ str[0] = 'X';
+
+ std::string result = fmt::vformat("{} and {a1_}");
+ assert(result == "X234567890 and 42");
+ \endrst
*/
template <typename T> void push_back(std::reference_wrapper<T> arg) {
static_assert(
- need_copy<T>::value,
+ detail::is_named_arg<typename std::remove_cv<T>::type>::value ||
+ need_copy<T>::value,
"objects of built-in types and string views are always copied");
emplace_arg(arg.get());
}
+
+ /**
+ Adds named argument into the dynamic store for later passing to a formatting
+ function. ``std::reference_wrapper`` is supported to avoid copying of the
+ argument.
+ */
+ template <typename T>
+ void push_back(const detail::named_arg<char_type, T>& arg) {
+ const char_type* arg_name =
+ dynamic_args_.push<std::basic_string<char_type>>(arg.name).c_str();
+ if (detail::const_check(need_copy<T>::value)) {
+ emplace_arg(
+ fmt::arg(arg_name, dynamic_args_.push<stored_type<T>>(arg.value)));
+ } else {
+ emplace_arg(fmt::arg(arg_name, arg.value));
+ }
+ }
+
+ /** Erase all elements from the store */
+ void clear() {
+ data_.clear();
+ named_info_.clear();
+ dynamic_args_ = detail::dynamic_arg_list();
+ }
+
+ /**
+ \rst
+ Reserves space to store at least *new_cap* arguments including
+ *new_cap_named* named arguments.
+ \endrst
+ */
+ void reserve(size_t new_cap, size_t new_cap_named) {
+ FMT_ASSERT(new_cap >= new_cap_named,
+ "Set of arguments includes set of named arguments");
+ data_.reserve(new_cap);
+ named_info_.reserve(new_cap_named);
+ }
};
/**
using format_arg = basic_format_arg<Context>;
private:
- // To reduce compiled code size per formatting function call, types of first
- // max_packed_args arguments are passed in the types_ field.
- unsigned long long types_;
+ // A descriptor that contains information about formatting arguments.
+ // If the number of arguments is less or equal to max_packed_args then
+ // argument types are passed in the descriptor. This reduces binary code size
+ // per formatting function call.
+ unsigned long long desc_;
union {
- // If the number of arguments is less than max_packed_args, the argument
- // values are stored in values_, otherwise they are stored in args_.
- // This is done to reduce compiled code size as storing larger objects
+ // If is_packed() returns true then argument values are stored in values_;
+ // otherwise they are stored in args_. This is done to improve cache
+ // locality and reduce compiled code size since storing larger objects
// may require more code (at least on x86-64) even if the same amount of
// data is actually copied to stack. It saves ~10% on the bloat test.
- const internal::value<Context>* values_;
+ const detail::value<Context>* values_;
const format_arg* args_;
};
- bool is_packed() const { return (types_ & internal::is_unpacked_bit) == 0; }
-
- internal::type type(int index) const {
- int shift = index * internal::packed_arg_bits;
- unsigned int mask = (1 << internal::packed_arg_bits) - 1;
- return static_cast<internal::type>((types_ >> shift) & mask);
+ bool is_packed() const { return (desc_ & detail::is_unpacked_bit) == 0; }
+ bool has_named_args() const {
+ return (desc_ & detail::has_named_args_bit) != 0;
}
- friend class internal::arg_map<Context>;
-
- void set_data(const internal::value<Context>* values) { values_ = values; }
- void set_data(const format_arg* args) { args_ = args; }
-
- format_arg do_get(int index) const {
- format_arg arg;
- if (!is_packed()) {
- auto num_args = max_size();
- if (index < num_args) arg = args_[index];
- return arg;
- }
- if (index > internal::max_packed_args) return arg;
- arg.type_ = type(index);
- if (arg.type_ == internal::type::none_type) return arg;
- internal::value<Context>& val = arg.value_;
- val = values_[index];
- return arg;
+ detail::type type(int index) const {
+ int shift = index * detail::packed_arg_bits;
+ unsigned int mask = (1 << detail::packed_arg_bits) - 1;
+ return static_cast<detail::type>((desc_ >> shift) & mask);
}
+ basic_format_args(unsigned long long desc,
+ const detail::value<Context>* values)
+ : desc_(desc), values_(values) {}
+ basic_format_args(unsigned long long desc, const format_arg* args)
+ : desc_(desc), args_(args) {}
+
public:
- basic_format_args() : types_(0) {}
+ basic_format_args() : desc_(0) {}
/**
\rst
\endrst
*/
template <typename... Args>
- basic_format_args(const format_arg_store<Context, Args...>& store)
- : types_(store.types) {
- set_data(store.data_);
- }
+ FMT_INLINE basic_format_args(const format_arg_store<Context, Args...>& store)
+ : basic_format_args(store.desc, store.data_.args()) {}
/**
\rst
`~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());
- }
+ FMT_INLINE basic_format_args(const dynamic_format_arg_store<Context>& store)
+ : basic_format_args(store.get_types(), store.data()) {}
/**
\rst
\endrst
*/
basic_format_args(const format_arg* args, int count)
- : types_(internal::is_unpacked_bit | internal::to_unsigned(count)) {
- set_data(args);
- }
+ : basic_format_args(detail::is_unpacked_bit | detail::to_unsigned(count),
+ args) {}
- /** Returns the argument at specified index. */
- format_arg get(int index) const {
- format_arg arg = do_get(index);
- if (arg.type_ == internal::type::named_arg_type)
- arg = arg.value_.named_arg->template deserialize<Context>();
+ /** Returns the argument with the specified id. */
+ format_arg get(int id) const {
+ format_arg arg;
+ if (!is_packed()) {
+ if (id < max_size()) arg = args_[id];
+ return arg;
+ }
+ if (id >= detail::max_packed_args) return arg;
+ arg.type_ = type(id);
+ if (arg.type_ == detail::type::none_type) return arg;
+ arg.value_ = values_[id];
return arg;
}
+ template <typename Char> format_arg get(basic_string_view<Char> name) const {
+ int id = get_id(name);
+ return id >= 0 ? get(id) : format_arg();
+ }
+
+ template <typename Char> int get_id(basic_string_view<Char> name) const {
+ if (!has_named_args()) return {};
+ const auto& named_args =
+ (is_packed() ? values_[-1] : args_[-1].value_).named_args;
+ for (size_t i = 0; i < named_args.size; ++i) {
+ if (named_args.data[i].name == name) return named_args.data[i].id;
+ }
+ return -1;
+ }
+
int max_size() const {
- unsigned long long max_packed = internal::max_packed_args;
+ unsigned long long max_packed = detail::max_packed_args;
return static_cast<int>(is_packed() ? max_packed
- : types_ & ~internal::is_unpacked_bit);
+ : desc_ & ~detail::is_unpacked_bit);
}
};
// It is a separate type rather than an alias to make symbols readable.
struct format_args : basic_format_args<format_context> {
template <typename... Args>
- format_args(Args&&... args)
- : basic_format_args<format_context>(static_cast<Args&&>(args)...) {}
+ FMT_INLINE format_args(const Args&... args) : basic_format_args(args...) {}
};
struct wformat_args : basic_format_args<wformat_context> {
- template <typename... Args>
- wformat_args(Args&&... args)
- : basic_format_args<wformat_context>(static_cast<Args&&>(args)...) {}
+ using basic_format_args::basic_format_args;
};
-template <typename Container> struct is_contiguous : std::false_type {};
-
-template <typename Char>
-struct is_contiguous<std::basic_string<Char>> : std::true_type {};
-
-template <typename Char>
-struct is_contiguous<internal::buffer<Char>> : std::true_type {};
-
-namespace internal {
-
-template <typename OutputIt>
-struct is_contiguous_back_insert_iterator : std::false_type {};
-template <typename Container>
-struct is_contiguous_back_insert_iterator<std::back_insert_iterator<Container>>
- : is_contiguous<Container> {};
-
-template <typename Char> struct named_arg_base {
- basic_string_view<Char> name;
-
- // Serialized value<context>.
- mutable char data[sizeof(basic_format_arg<buffer_context<Char>>)];
-
- named_arg_base(basic_string_view<Char> nm) : name(nm) {}
-
- template <typename Context> basic_format_arg<Context> deserialize() const {
- basic_format_arg<Context> arg;
- std::memcpy(&arg, data, sizeof(basic_format_arg<Context>));
- return arg;
- }
-};
-
-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)
- : named_arg_base<Char>(name), value(val) {}
-};
+namespace detail {
+// 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)>
-inline void check_format_string(const S&) {
-#if defined(FMT_ENFORCE_COMPILE_STRING)
+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 "
- "utilize FMT_STRING() or fmt().");
+ "FMT_ENFORCE_COMPILE_STRING requires all format strings to use "
+ "FMT_STRING.");
#endif
}
template <typename..., typename S, FMT_ENABLE_IF(is_compile_string<S>::value)>
void check_format_string(S);
-template <bool...> struct bool_pack;
-template <bool... Args>
-using all_true =
- std::is_same<bool_pack<Args..., true>, bool_pack<true, Args...>>;
-
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>>::value ||
- !std::is_reference<Args>::value)...>::value,
- "passing views as lvalues is disallowed");
+ static_assert(count<(std::is_base_of<view, remove_reference_t<Args>>::value &&
+ std::is_reference<Args>::value)...>() == 0,
+ "passing views as lvalues is disallowed");
check_format_string<Args...>(format_str);
return {args...};
}
-template <typename Char>
+template <typename Char, FMT_ENABLE_IF(!std::is_same<Char, char>::value)>
std::basic_string<Char> vformat(
basic_string_view<Char> format_str,
basic_format_args<buffer_context<type_identity_t<Char>>> args);
+FMT_API std::string vformat(string_view format_str, format_args args);
+
template <typename Char>
-typename buffer_context<Char>::iterator vformat_to(
+typename FMT_BUFFER_CONTEXT(Char)::iterator vformat_to(
buffer<Char>& buf, basic_string_view<Char> format_str,
- basic_format_args<buffer_context<type_identity_t<Char>>> args);
+ basic_format_args<FMT_BUFFER_CONTEXT(type_identity_t<Char>)> args);
template <typename Char, typename Args,
FMT_ENABLE_IF(!std::is_same<Char, char>::value)>
#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. It should only
- be used in a call to a formatting function.
-
- **Example**::
-
- fmt::print("Elapsed time: {s:.2f} seconds", fmt::arg("s", 1.23));
- \endrst
- */
-template <typename S, typename T, typename Char = char_t<S>>
-inline internal::named_arg<T, Char> arg(const S& name, const T& arg) {
- static_assert(internal::is_string<S>::value, "");
- return {name, arg};
-}
-
-// Disable nested named arguments, e.g. ``arg("a", arg("b", 42))``.
-template <typename S, typename T, typename Char>
-void arg(S, internal::named_arg<T, Char>) = delete;
+} // namespace detail
/** Formats a string and writes the output to ``out``. */
// GCC 8 and earlier cannot handle std::back_insert_iterator<Container> with
// vformat_to<ArgFormatter>(...) overload, so SFINAE on iterator type instead.
-template <typename OutputIt, typename S, typename Char = char_t<S>,
- FMT_ENABLE_IF(
- internal::is_contiguous_back_insert_iterator<OutputIt>::value)>
+template <
+ typename OutputIt, typename S, typename Char = char_t<S>,
+ FMT_ENABLE_IF(detail::is_contiguous_back_insert_iterator<OutputIt>::value)>
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);
+ auto& c = detail::get_container(out);
+ detail::container_buffer<remove_reference_t<decltype(c)>> buf(c);
+ detail::vformat_to(buf, to_string_view(format_str), args);
return out;
}
template <typename Container, typename S, typename... Args,
FMT_ENABLE_IF(
- is_contiguous<Container>::value&& internal::is_string<S>::value)>
+ is_contiguous<Container>::value&& detail::is_string<S>::value)>
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...));
+ detail::make_args_checked<Args...>(format_str, args...));
}
template <typename S, typename Char = char_t<S>>
-inline std::basic_string<Char> vformat(
+FMT_INLINE std::basic_string<Char> vformat(
const S& format_str,
basic_format_args<buffer_context<type_identity_t<Char>>> args) {
- return internal::vformat(to_string_view(format_str), args);
+ return detail::vformat(to_string_view(format_str), args);
}
/**
// Pass char_t as a default template parameter instead of using
// std::basic_string<char_t<S>> to reduce the symbol size.
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...));
+FMT_INLINE std::basic_string<Char> format(const S& format_str, Args&&... args) {
+ const auto& vargs = detail::make_args_checked<Args...>(format_str, args...);
+ return detail::vformat(to_string_view(format_str), vargs);
}
FMT_API void vprint(string_view, format_args);
*/
template <typename S, typename... Args, typename Char = char_t<S>>
inline void print(std::FILE* f, const S& format_str, Args&&... 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...));
+ const auto& vargs = detail::make_args_checked<Args...>(format_str, args...);
+ return detail::is_unicode<Char>()
+ ? vprint(f, to_string_view(format_str), vargs)
+ : detail::vprint_mojibake(f, to_string_view(format_str), vargs);
}
/**
*/
template <typename S, typename... Args, typename Char = char_t<S>>
inline void print(const S& format_str, Args&&... 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...));
+ const auto& vargs = detail::make_args_checked<Args...>(format_str, args...);
+ return detail::is_unicode<Char>()
+ ? vprint(to_string_view(format_str), vargs)
+ : detail::vprint_mojibake(stdout, to_string_view(format_str),
+ vargs);
}
FMT_END_NAMESPACE
#include "core.h"
-#ifdef FMT_DEPRECATED_INCLUDE_OS
-# include "os.h"
-#endif
-
#ifdef __INTEL_COMPILER
# define FMT_ICC_VERSION __INTEL_COMPILER
#elif defined(__ICL)
#if __cplusplus == 201103L || __cplusplus == 201402L
# if defined(__clang__)
# define FMT_FALLTHROUGH [[clang::fallthrough]]
-# elif FMT_GCC_VERSION >= 700 && !defined(__PGI)
+# elif FMT_GCC_VERSION >= 700 && !defined(__PGI) && \
+ (!defined(__EDG_VERSION__) || __EDG_VERSION__ >= 520)
# define FMT_FALLTHROUGH [[gnu::fallthrough]]
# else
# define FMT_FALLTHROUGH
# define FMT_FALLTHROUGH
#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_THROW
# if FMT_EXCEPTIONS
# if FMT_MSC_VER || FMT_NVCC
FMT_BEGIN_NAMESPACE
-namespace internal {
+namespace detail {
template <typename Exception> inline void do_throw(const Exception& x) {
// Silence unreachable code warnings in MSVC and NVCC because these
// are nearly impossible to fix in a generic code.
volatile bool b = true;
if (b) throw x;
}
-} // namespace internal
+} // namespace detail
FMT_END_NAMESPACE
-# define FMT_THROW(x) internal::do_throw(x)
+# define FMT_THROW(x) detail::do_throw(x)
# else
# define FMT_THROW(x) throw x
# endif
#endif
#ifndef FMT_USE_USER_DEFINED_LITERALS
-// For Intel and NVIDIA compilers both they and the system gcc/msc support UDLs.
-# if (FMT_HAS_FEATURE(cxx_user_literals) || FMT_GCC_VERSION >= 407 || \
- FMT_MSC_VER >= 1900) && \
- (!(FMT_ICC_VERSION || FMT_CUDA_VERSION) || FMT_ICC_VERSION >= 1500 || \
- FMT_CUDA_VERSION >= 700)
+// EDG based compilers (Intel, NVIDIA, Elbrus, etc), GCC and MSVC support UDLs.
+# if (FMT_HAS_FEATURE(cxx_user_literals) || FMT_GCC_VERSION >= 407 || \
+ FMT_MSC_VER >= 1900) && \
+ (!defined(__EDG_VERSION__) || __EDG_VERSION__ >= /* UDL feature */ 480)
# define FMT_USE_USER_DEFINED_LITERALS 1
# else
# define FMT_USE_USER_DEFINED_LITERALS 0
#endif
#ifndef FMT_USE_UDL_TEMPLATE
-// EDG front end based compilers (icc, nvcc) and GCC < 6.4 do not propertly
+// EDG frontend based compilers (icc, nvcc, etc) and GCC < 6.4 do not properly
// support UDL templates and GCC >= 9 warns about them.
-# if FMT_USE_USER_DEFINED_LITERALS && FMT_ICC_VERSION == 0 && \
- FMT_CUDA_VERSION == 0 && \
- ((FMT_GCC_VERSION >= 604 && FMT_GCC_VERSION <= 900 && \
- __cplusplus >= 201402L) || \
+# if FMT_USE_USER_DEFINED_LITERALS && \
+ (!defined(__EDG_VERSION__) || __EDG_VERSION__ >= 501) && \
+ ((FMT_GCC_VERSION >= 604 && __cplusplus >= 201402L) || \
FMT_CLANG_VERSION >= 304)
# define FMT_USE_UDL_TEMPLATE 1
# else
# include <intrin.h> // _BitScanReverse, _BitScanReverse64
FMT_BEGIN_NAMESPACE
-namespace internal {
+namespace detail {
// Avoid Clang with Microsoft CodeGen's -Wunknown-pragmas warning.
# ifndef __clang__
# pragma intrinsic(_BitScanReverse)
// Static analysis complains about using uninitialized data
// "r", but the only way that can happen is if "x" is 0,
// which the callers guarantee to not happen.
-# pragma warning(suppress : 6102)
+ FMT_SUPPRESS_MSC_WARNING(6102)
return 31 - r;
}
-# define FMT_BUILTIN_CLZ(n) internal::clz(n)
+# define FMT_BUILTIN_CLZ(n) detail::clz(n)
# if defined(_WIN64) && !defined(__clang__)
# pragma intrinsic(_BitScanReverse64)
// Static analysis complains about using uninitialized data
// "r", but the only way that can happen is if "x" is 0,
// which the callers guarantee to not happen.
-# pragma warning(suppress : 6102)
+ FMT_SUPPRESS_MSC_WARNING(6102)
return 63 - r;
}
-# define FMT_BUILTIN_CLZLL(n) internal::clzll(n)
-} // namespace internal
+# define FMT_BUILTIN_CLZLL(n) detail::clzll(n)
+} // namespace detail
FMT_END_NAMESPACE
#endif
// Enable the deprecated numeric alignment.
-#ifndef FMT_NUMERIC_ALIGN
-# define FMT_NUMERIC_ALIGN 1
-#endif
-
-// Enable the deprecated percent specifier.
-#ifndef FMT_DEPRECATED_PERCENT
-# define FMT_DEPRECATED_PERCENT 0
+#ifndef FMT_DEPRECATED_NUMERIC_ALIGN
+# define FMT_DEPRECATED_NUMERIC_ALIGN 0
#endif
FMT_BEGIN_NAMESPACE
-namespace internal {
+namespace detail {
// An equivalent of `*reinterpret_cast<Dest*>(&source)` that doesn't have
// undefined behavior (e.g. due to type aliasing).
template <typename T> constexpr int num_bits() {
return std::numeric_limits<T>::digits;
}
+// std::numeric_limits<T>::digits may return 0 for 128-bit ints.
+template <> constexpr int num_bits<int128_t>() { return 128; }
+template <> constexpr int num_bits<uint128_t>() { return 128; }
template <> constexpr int num_bits<fallback_uintptr>() {
return static_cast<int>(sizeof(void*) *
std::numeric_limits<unsigned char>::digits);
}
+FMT_INLINE void assume(bool condition) {
+ (void)condition;
+#if FMT_HAS_BUILTIN(__builtin_assume)
+ __builtin_assume(condition);
+#endif
+}
+
+// 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 <typename... Ts>
+using void_t = typename detail::void_t_impl<Ts...>::type;
+
// An approximation of iterator_t for pre-C++20 systems.
template <typename T>
using iterator_t = decltype(std::begin(std::declval<T&>()));
+template <typename T> using sentinel_t = decltype(std::end(std::declval<T&>()));
// Detect the iterator category of *any* given type in a SFINAE-friendly way.
// Unfortunately, older implementations of std::iterator_traits are not safe
#if defined(_SECURE_SCL) && _SECURE_SCL
// Make a checked iterator to avoid MSVC warnings.
template <typename T> using checked_ptr = stdext::checked_array_iterator<T*>;
-template <typename T> checked_ptr<T> make_checked(T* p, std::size_t size) {
+template <typename T> checked_ptr<T> make_checked(T* p, size_t size) {
return {p, size};
}
#else
template <typename T> using checked_ptr = T*;
-template <typename T> inline T* make_checked(T* p, std::size_t) { return p; }
+template <typename T> inline T* make_checked(T* p, size_t) { return p; }
#endif
template <typename Container, FMT_ENABLE_IF(is_contiguous<Container>::value)>
-inline checked_ptr<typename Container::value_type> reserve(
- std::back_insert_iterator<Container>& it, std::size_t n) {
+#if FMT_CLANG_VERSION
+__attribute__((no_sanitize("undefined")))
+#endif
+inline checked_ptr<typename Container::value_type>
+reserve(std::back_insert_iterator<Container> it, size_t n) {
Container& c = get_container(it);
- std::size_t size = c.size();
+ size_t size = c.size();
c.resize(size + n);
return make_checked(get_data(c) + size, n);
}
+template <typename Iterator> inline Iterator& reserve(Iterator& it, size_t) {
+ return it;
+}
+
+template <typename Container, FMT_ENABLE_IF(is_contiguous<Container>::value)>
+inline std::back_insert_iterator<Container> base_iterator(
+ std::back_insert_iterator<Container>& it,
+ checked_ptr<typename Container::value_type>) {
+ return it;
+}
+
template <typename Iterator>
-inline Iterator& reserve(Iterator& it, std::size_t) {
+inline Iterator base_iterator(Iterator, Iterator it) {
return it;
}
// discards them.
class counting_iterator {
private:
- std::size_t count_;
+ size_t count_;
public:
using iterator_category = std::output_iterator_tag;
counting_iterator() : count_(0) {}
- std::size_t count() const { return count_; }
+ size_t count() const { return count_; }
counting_iterator& operator++() {
++count_;
template <typename OutputIt> class truncating_iterator_base {
protected:
OutputIt out_;
- std::size_t limit_;
- std::size_t count_;
+ size_t limit_;
+ size_t count_;
- truncating_iterator_base(OutputIt out, std::size_t limit)
+ truncating_iterator_base(OutputIt out, size_t limit)
: out_(out), limit_(limit), count_(0) {}
public:
truncating_iterator_base; // Mark iterator as checked.
OutputIt base() const { return out_; }
- std::size_t count() const { return count_; }
+ size_t count() const { return count_; }
};
// An output iterator that truncates the output and counts the number of objects
public:
using value_type = typename truncating_iterator_base<OutputIt>::value_type;
- truncating_iterator(OutputIt out, std::size_t limit)
+ truncating_iterator(OutputIt out, size_t limit)
: truncating_iterator_base<OutputIt>(out, limit) {}
truncating_iterator& operator++() {
class truncating_iterator<OutputIt, std::true_type>
: public truncating_iterator_base<OutputIt> {
public:
- truncating_iterator(OutputIt out, std::size_t limit)
+ truncating_iterator(OutputIt out, size_t limit)
: truncating_iterator_base<OutputIt>(out, limit) {}
template <typename T> truncating_iterator& operator=(T val) {
truncating_iterator& operator*() { return *this; }
};
-// A range with the specified output iterator and value type.
-template <typename OutputIt, typename T = typename OutputIt::value_type>
-class output_range {
- private:
- OutputIt it_;
-
- public:
- using value_type = T;
- using iterator = OutputIt;
- struct sentinel {};
-
- explicit output_range(OutputIt it) : it_(it) {}
- OutputIt begin() const { return it_; }
- sentinel end() const { return {}; } // Sentinel is not used yet.
-};
-
template <typename Char>
inline size_t count_code_points(basic_string_view<Char> s) {
return s.size();
return s.size();
}
-inline char8_type to_char8_t(char c) { return static_cast<char8_type>(c); }
-
template <typename InputIt, typename OutChar>
using needs_conversion = bool_constant<
std::is_same<typename std::iterator_traits<InputIt>::value_type,
template <typename OutChar, typename InputIt, typename OutputIt,
FMT_ENABLE_IF(needs_conversion<InputIt, OutChar>::value)>
OutputIt copy_str(InputIt begin, InputIt end, OutputIt it) {
- return std::transform(begin, end, it, to_char8_t);
+ return std::transform(begin, end, it,
+ [](char c) { return static_cast<char8_type>(c); });
}
#ifndef FMT_USE_GRISU
template <typename T>
template <typename U>
void buffer<T>::append(const U* begin, const U* end) {
- std::size_t new_size = size_ + to_unsigned(end - begin);
+ size_t new_size = size_ + to_unsigned(end - begin);
reserve(new_size);
- std::uninitialized_copy(begin, end, make_checked(ptr_, capacity_) + size_);
+ std::uninitialized_copy(begin, end,
+ make_checked(ptr_ + size_, capacity_ - size_));
size_ = new_size;
}
-} // namespace internal
-
-// A range with an iterator appending to a buffer.
-template <typename T>
-class buffer_range : public internal::output_range<
- std::back_insert_iterator<internal::buffer<T>>, T> {
- public:
- using iterator = std::back_insert_iterator<internal::buffer<T>>;
- using internal::output_range<iterator, T>::output_range;
- buffer_range(internal::buffer<T>& buf)
- : internal::output_range<iterator, T>(std::back_inserter(buf)) {}
-};
-
-class FMT_DEPRECATED u8string_view
- : public basic_string_view<internal::char8_type> {
- public:
- u8string_view(const char* s)
- : basic_string_view<internal::char8_type>(
- reinterpret_cast<const internal::char8_type*>(s)) {}
- u8string_view(const char* s, size_t count) FMT_NOEXCEPT
- : basic_string_view<internal::char8_type>(
- reinterpret_cast<const internal::char8_type*>(s), count) {}
-};
-
-#if FMT_USE_USER_DEFINED_LITERALS
-inline namespace literals {
-FMT_DEPRECATED inline basic_string_view<internal::char8_type> operator"" _u(
- const char* s, std::size_t n) {
- return {reinterpret_cast<const internal::char8_type*>(s), n};
-}
-} // namespace literals
-#endif
+} // namespace detail
// The number of characters to store in the basic_memory_buffer object itself
// to avoid dynamic memory allocation.
The output can be converted to an ``std::string`` with ``to_string(out)``.
\endrst
*/
-template <typename T, std::size_t SIZE = inline_buffer_size,
+template <typename T, size_t SIZE = inline_buffer_size,
typename Allocator = std::allocator<T>>
-class basic_memory_buffer : private Allocator, public internal::buffer<T> {
+class basic_memory_buffer : public detail::buffer<T> {
private:
T store_[SIZE];
+ // Don't inherit from Allocator avoid generating type_info for it.
+ Allocator alloc_;
+
// Deallocate memory allocated by the buffer.
void deallocate() {
T* data = this->data();
- if (data != store_) Allocator::deallocate(data, this->capacity());
+ if (data != store_) alloc_.deallocate(data, this->capacity());
}
protected:
- void grow(std::size_t size) FMT_OVERRIDE;
+ void grow(size_t size) FMT_OVERRIDE;
public:
using value_type = T;
using const_reference = const T&;
explicit basic_memory_buffer(const Allocator& alloc = Allocator())
- : Allocator(alloc) {
+ : alloc_(alloc) {
this->set(store_, SIZE);
}
~basic_memory_buffer() FMT_OVERRIDE { deallocate(); }
private:
// Move data from other to this buffer.
void move(basic_memory_buffer& other) {
- Allocator &this_alloc = *this, &other_alloc = other;
- this_alloc = std::move(other_alloc);
+ alloc_ = std::move(other.alloc_);
T* data = other.data();
- std::size_t size = other.size(), capacity = other.capacity();
+ size_t size = other.size(), capacity = other.capacity();
if (data == other.store_) {
this->set(store_, capacity);
std::uninitialized_copy(other.store_, other.store_ + size,
- internal::make_checked(store_, capacity));
+ detail::make_checked(store_, capacity));
} else {
this->set(data, capacity);
// Set pointer to the inline array so that delete is not called
}
// Returns a copy of the allocator associated with this buffer.
- Allocator get_allocator() const { return *this; }
+ Allocator get_allocator() const { return alloc_; }
};
-template <typename T, std::size_t SIZE, typename Allocator>
-void basic_memory_buffer<T, SIZE, Allocator>::grow(std::size_t size) {
-#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
- if (size > 1000) throw std::runtime_error("fuzz mode - won't grow that much");
+template <typename T, size_t SIZE, typename Allocator>
+void basic_memory_buffer<T, SIZE, Allocator>::grow(size_t size) {
+#ifdef FMT_FUZZ
+ if (size > 5000) throw std::runtime_error("fuzz mode - won't grow that much");
#endif
- std::size_t old_capacity = this->capacity();
- std::size_t new_capacity = old_capacity + old_capacity / 2;
+ size_t old_capacity = this->capacity();
+ size_t new_capacity = old_capacity + old_capacity / 2;
if (size > new_capacity) new_capacity = size;
T* old_data = this->data();
- T* new_data = std::allocator_traits<Allocator>::allocate(*this, new_capacity);
+ T* new_data =
+ std::allocator_traits<Allocator>::allocate(alloc_, new_capacity);
// The following code doesn't throw, so the raw pointer above doesn't leak.
std::uninitialized_copy(old_data, old_data + this->size(),
- internal::make_checked(new_data, new_capacity));
+ detail::make_checked(new_data, new_capacity));
this->set(new_data, new_capacity);
// deallocate must not throw according to the standard, but even if it does,
// the buffer already uses the new storage and will deallocate it in
// destructor.
- if (old_data != store_) Allocator::deallocate(old_data, old_capacity);
+ if (old_data != store_) alloc_.deallocate(old_data, old_capacity);
}
using memory_buffer = basic_memory_buffer<char>;
using wmemory_buffer = basic_memory_buffer<wchar_t>;
+template <typename T, size_t SIZE, typename Allocator>
+struct is_contiguous<basic_memory_buffer<T, SIZE, Allocator>> : std::true_type {
+};
+
/** A formatting error such as invalid format string. */
FMT_CLASS_API
class FMT_API format_error : public std::runtime_error {
~format_error() FMT_NOEXCEPT FMT_OVERRIDE;
};
-namespace internal {
+namespace detail {
// Returns true if value is negative, false otherwise.
// Same as `value < 0` but doesn't produce warnings if T is an unsigned type.
// represent all values of T.
template <typename T>
using uint32_or_64_or_128_t = conditional_t<
- std::numeric_limits<T>::digits <= 32, uint32_t,
- conditional_t<std::numeric_limits<T>::digits <= 64, uint64_t, uint128_t>>;
+ num_bits<T>() <= 32, uint32_t,
+ conditional_t<num_bits<T>() <= 64, uint64_t, uint128_t>>;
// Static data is placed in this class template for the header-only config.
template <typename T = void> struct FMT_EXTERN_TEMPLATE_API basic_data {
static const uint64_t zero_or_powers_of_10_64[];
static const uint64_t pow10_significands[];
static const int16_t pow10_exponents[];
- static const char digits[];
+ // GCC generates slightly better code for pairs than chars.
+ using digit_pair = char[2];
+ static const digit_pair digits[];
static const char hex_digits[];
static const char foreground_color[];
static const char background_color[];
static const char reset_color[5];
static const wchar_t wreset_color[5];
static const char signs[];
+ static const char left_padding_shifts[5];
+ static const char right_padding_shifts[5];
};
+#ifndef FMT_EXPORTED
FMT_EXTERN template struct basic_data<void>;
+#endif
// This is a struct rather than an alias to avoid shadowing warnings in gcc.
struct data : basic_data<> {};
return num_digits;
}
-template <> int count_digits<4>(internal::fallback_uintptr n);
+template <> int count_digits<4>(detail::fallback_uintptr n);
#if FMT_GCC_VERSION || FMT_CLANG_VERSION
# define FMT_ALWAYS_INLINE inline __attribute__((always_inline))
}
#endif
+template <typename Int> constexpr int digits10() FMT_NOEXCEPT {
+ return std::numeric_limits<Int>::digits10;
+}
+template <> constexpr int digits10<int128_t>() FMT_NOEXCEPT { return 38; }
+template <> constexpr int digits10<uint128_t>() FMT_NOEXCEPT { return 38; }
+
template <typename Char> FMT_API std::string grouping_impl(locale_ref loc);
template <typename Char> inline std::string grouping(locale_ref loc) {
return grouping_impl<char>(loc);
return decimal_point_impl<wchar_t>(loc);
}
-// Formats a decimal unsigned integer value writing into buffer.
-// add_thousands_sep is called after writing each char to add a thousands
-// separator if necessary.
-template <typename UInt, typename Char, typename F>
-inline Char* format_decimal(Char* buffer, UInt value, int num_digits,
- F add_thousands_sep) {
- FMT_ASSERT(num_digits >= 0, "invalid digit count");
- buffer += num_digits;
- Char* end = buffer;
+// Compares two characters for equality.
+template <typename Char> bool equal2(const Char* lhs, const char* rhs) {
+ return lhs[0] == rhs[0] && lhs[1] == rhs[1];
+}
+inline bool equal2(const char* lhs, const char* rhs) {
+ return memcmp(lhs, rhs, 2) == 0;
+}
+
+// Copies two characters from src to dst.
+template <typename Char> void copy2(Char* dst, const char* src) {
+ *dst++ = static_cast<Char>(*src++);
+ *dst = static_cast<Char>(*src);
+}
+inline void copy2(char* dst, const char* src) { memcpy(dst, src, 2); }
+
+template <typename Iterator> struct format_decimal_result {
+ Iterator begin;
+ Iterator end;
+};
+
+// Formats a decimal unsigned integer value writing into out pointing to a
+// buffer of specified size. The caller must ensure that the buffer is large
+// enough.
+template <typename Char, typename UInt>
+inline format_decimal_result<Char*> format_decimal(Char* out, UInt value,
+ int size) {
+ FMT_ASSERT(size >= count_digits(value), "invalid digit count");
+ out += size;
+ Char* end = out;
while (value >= 100) {
// Integer division is slow so do it for a group of two digits instead
// of for every digit. The idea comes from the talk by Alexandrescu
// "Three Optimization Tips for C++". See speed-test for a comparison.
- auto index = static_cast<unsigned>((value % 100) * 2);
+ out -= 2;
+ copy2(out, data::digits[value % 100]);
value /= 100;
- *--buffer = static_cast<Char>(data::digits[index + 1]);
- add_thousands_sep(buffer);
- *--buffer = static_cast<Char>(data::digits[index]);
- add_thousands_sep(buffer);
}
if (value < 10) {
- *--buffer = static_cast<Char>('0' + value);
- return end;
+ *--out = static_cast<Char>('0' + value);
+ return {out, end};
}
- auto index = static_cast<unsigned>(value * 2);
- *--buffer = static_cast<Char>(data::digits[index + 1]);
- add_thousands_sep(buffer);
- *--buffer = static_cast<Char>(data::digits[index]);
- return end;
+ out -= 2;
+ copy2(out, data::digits[value]);
+ return {out, end};
}
-template <typename Int> constexpr int digits10() FMT_NOEXCEPT {
- return std::numeric_limits<Int>::digits10;
-}
-template <> constexpr int digits10<int128_t>() FMT_NOEXCEPT { return 38; }
-template <> constexpr int digits10<uint128_t>() FMT_NOEXCEPT { return 38; }
-
-template <typename Char, typename UInt, typename Iterator, typename F>
-inline Iterator format_decimal(Iterator out, UInt value, int num_digits,
- F add_thousands_sep) {
- FMT_ASSERT(num_digits >= 0, "invalid digit count");
+template <typename Char, typename UInt, typename Iterator,
+ FMT_ENABLE_IF(!std::is_pointer<remove_cvref_t<Iterator>>::value)>
+inline format_decimal_result<Iterator> format_decimal(Iterator out, UInt value,
+ int num_digits) {
// Buffer should be large enough to hold all digits (<= digits10 + 1).
enum { max_size = digits10<UInt>() + 1 };
Char buffer[2 * max_size];
- auto end = format_decimal(buffer, value, num_digits, add_thousands_sep);
- return internal::copy_str<Char>(buffer, end, out);
-}
-
-template <typename Char, typename It, typename UInt>
-inline It format_decimal(It out, UInt value, int num_digits) {
- return format_decimal<Char>(out, value, num_digits, [](Char*) {});
+ auto end = format_decimal(buffer, value, num_digits).end;
+ return {out, detail::copy_str<Char>(buffer, end, out)};
}
template <unsigned BASE_BITS, typename Char, typename UInt>
}
template <unsigned BASE_BITS, typename Char>
-Char* format_uint(Char* buffer, internal::fallback_uintptr n, int num_digits,
+Char* format_uint(Char* buffer, detail::fallback_uintptr n, int num_digits,
bool = false) {
auto char_digits = std::numeric_limits<unsigned char>::digits / 4;
int start = (num_digits + char_digits - 1) / char_digits - 1;
// Buffer should be large enough to hold all digits (digits / BASE_BITS + 1).
char buffer[num_bits<UInt>() / BASE_BITS + 1];
format_uint<BASE_BITS>(buffer, value, num_digits, upper);
- return internal::copy_str<Char>(buffer, buffer + num_digits, out);
+ return detail::copy_str<Char>(buffer, buffer + num_digits, out);
}
// A converter from UTF-8 to UTF-16.
return fill;
}
};
-} // namespace internal
+} // namespace detail
// We cannot use enum classes as bit fields because of a gcc bug
// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=61414.
align_t align : 4;
sign_t sign : 3;
bool alt : 1; // Alternate form ('#').
- internal::fill_t<Char> fill;
+ detail::fill_t<Char> fill;
constexpr basic_format_specs()
: width(0),
align(align::none),
sign(sign::none),
alt(false),
- fill(internal::fill_t<Char>::make()) {}
+ fill(detail::fill_t<Char>::make()) {}
};
using format_specs = basic_format_specs<char>;
-namespace internal {
+namespace detail {
// A floating-point presentation format.
enum class float_format : unsigned char {
sign_t sign : 8;
bool upper : 1;
bool locale : 1;
- bool percent : 1;
bool binary32 : 1;
bool use_grisu : 1;
bool showpoint : 1;
*it++ = static_cast<Char>('+');
}
if (exp >= 100) {
- const char* top = data::digits + (exp / 100) * 2;
+ const char* top = data::digits[exp / 100];
if (exp >= 1000) *it++ = static_cast<Char>(top[0]);
*it++ = static_cast<Char>(top[1]);
exp %= 100;
}
- const char* d = data::digits + exp * 2;
+ const char* d = data::digits[exp];
*it++ = static_cast<Char>(d[0]);
*it++ = static_cast<Char>(d[1]);
return it;
*it++ = static_cast<Char>('0');
return it;
}
-#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
- if (num_zeros > 1000)
+#ifdef FMT_FUZZ
+ if (num_zeros > 5000)
throw std::runtime_error("fuzz mode - avoiding excessive cpu use");
#endif
it = std::fill_n(it, num_zeros, static_cast<Char>('0'));
}
size_t size() const { return size_; }
- size_t width() const { return size(); }
- template <typename It> void operator()(It&& it) {
+ template <typename It> It operator()(It it) const {
if (specs_.sign) *it++ = static_cast<Char>(data::signs[specs_.sign]);
- it = prettify(it);
+ return prettify(it);
}
};
case 'o':
handler.on_oct();
break;
+#ifdef FMT_DEPRECATED_N_SPECIFIER
case 'n':
+#endif
case 'L':
handler.on_num();
break;
+ case 'c':
+ handler.on_chr();
+ break;
default:
handler.on_error();
}
result.format = float_format::fixed;
result.showpoint |= specs.precision != 0;
break;
-#if FMT_DEPRECATED_PERCENT
- case '%':
- result.format = float_format::fixed;
- result.percent = true;
- break;
-#endif
case 'A':
result.upper = true;
FMT_FALLTHROUGH;
case 'a':
result.format = float_format::hex;
break;
+#ifdef FMT_DEPRECATED_N_SPECIFIER
case 'n':
+#endif
+ case 'L':
result.locale = true;
break;
default:
FMT_CONSTEXPR void on_bin() {}
FMT_CONSTEXPR void on_oct() {}
FMT_CONSTEXPR void on_num() {}
+ FMT_CONSTEXPR void on_chr() {}
FMT_CONSTEXPR void on_error() {
ErrorHandler::on_error("invalid type specifier");
FMT_CONSTEXPR void on_pointer() {}
};
-template <typename Context>
-void arg_map<Context>::init(const basic_format_args<Context>& args) {
- if (map_) return;
- map_ = new entry[internal::to_unsigned(args.max_size())];
- if (args.is_packed()) {
- for (int i = 0;; ++i) {
- internal::type arg_type = args.type(i);
- if (arg_type == internal::type::none_type) return;
- if (arg_type == internal::type::named_arg_type)
- push_back(args.values_[i]);
- }
- }
- for (int i = 0, n = args.max_size(); i < n; ++i) {
- auto type = args.args_[i].type_;
- if (type == internal::type::named_arg_type) push_back(args.args_[i].value_);
- }
-}
-
-template <typename Char> struct nonfinite_writer {
- sign_t sign;
- const char* str;
- static constexpr size_t str_size = 3;
-
- size_t size() const { return str_size + (sign ? 1 : 0); }
- size_t width() const { return size(); }
-
- template <typename It> void operator()(It&& it) const {
- if (sign) *it++ = static_cast<Char>(data::signs[sign]);
- it = copy_str<Char>(str, str + str_size, it);
- }
-};
-
template <typename OutputIt, typename Char>
FMT_NOINLINE OutputIt fill(OutputIt it, size_t n, const fill_t<Char>& fill) {
auto fill_size = fill.size();
return it;
}
-// This template provides operations for formatting and writing data into a
-// character range.
-template <typename Range> class basic_writer {
- public:
- using char_type = typename Range::value_type;
- using iterator = typename Range::iterator;
- using format_specs = basic_format_specs<char_type>;
-
- private:
- iterator out_; // Output iterator.
- locale_ref locale_;
-
- // Attempts to reserve space for n extra characters in the output range.
- // Returns a pointer to the reserved range or a reference to out_.
- auto reserve(std::size_t n) -> decltype(internal::reserve(out_, n)) {
- return internal::reserve(out_, n);
- }
-
- template <typename F> struct padded_int_writer {
- size_t size_;
- string_view prefix;
- char_type fill;
- std::size_t padding;
- F f;
-
- size_t size() const { return size_; }
- size_t width() const { return size_; }
-
- template <typename It> void operator()(It&& it) const {
- if (prefix.size() != 0)
- it = copy_str<char_type>(prefix.begin(), prefix.end(), it);
- it = std::fill_n(it, padding, fill);
- f(it);
- }
- };
-
- // Writes an integer in the format
- // <left-padding><prefix><numeric-padding><digits><right-padding>
- // where <digits> are written by f(it).
- template <typename F>
- void write_int(int num_digits, string_view prefix, format_specs specs, F f) {
- std::size_t size = prefix.size() + to_unsigned(num_digits);
- char_type fill = specs.fill[0];
- std::size_t padding = 0;
+// Writes the output of f, padded according to format specifications in specs.
+// size: output size in code units.
+// width: output display width in (terminal) column positions.
+template <align::type align = align::left, typename OutputIt, typename Char,
+ typename F>
+inline OutputIt write_padded(OutputIt out,
+ const basic_format_specs<Char>& specs, size_t size,
+ size_t width, const F& f) {
+ static_assert(align == align::left || align == align::right, "");
+ unsigned spec_width = to_unsigned(specs.width);
+ size_t padding = spec_width > width ? spec_width - width : 0;
+ auto* shifts = align == align::left ? data::left_padding_shifts
+ : data::right_padding_shifts;
+ size_t left_padding = padding >> shifts[specs.align];
+ auto it = reserve(out, size + padding * specs.fill.size());
+ it = fill(it, left_padding, specs.fill);
+ it = f(it);
+ it = fill(it, padding - left_padding, specs.fill);
+ return base_iterator(out, it);
+}
+
+template <align::type align = align::left, typename OutputIt, typename Char,
+ typename F>
+inline OutputIt write_padded(OutputIt out,
+ const basic_format_specs<Char>& specs, size_t size,
+ const F& f) {
+ return write_padded<align>(out, specs, size, size, f);
+}
+
+template <typename Char, typename OutputIt>
+OutputIt write_bytes(OutputIt out, string_view bytes,
+ const basic_format_specs<Char>& specs) {
+ using iterator = remove_reference_t<decltype(reserve(out, 0))>;
+ return write_padded(out, specs, bytes.size(), [bytes](iterator it) {
+ const char* data = bytes.data();
+ return copy_str<Char>(data, data + bytes.size(), it);
+ });
+}
+
+// Data for write_int that doesn't depend on output iterator type. It is used to
+// avoid template code bloat.
+template <typename Char> struct write_int_data {
+ size_t size;
+ size_t padding;
+
+ write_int_data(int num_digits, string_view prefix,
+ const basic_format_specs<Char>& specs)
+ : size(prefix.size() + to_unsigned(num_digits)), padding(0) {
if (specs.align == align::numeric) {
- auto unsiged_width = to_unsigned(specs.width);
- if (unsiged_width > size) {
- padding = unsiged_width - size;
- size = unsiged_width;
+ auto width = to_unsigned(specs.width);
+ if (width > size) {
+ padding = width - size;
+ size = width;
}
} else if (specs.precision > num_digits) {
size = prefix.size() + to_unsigned(specs.precision);
padding = to_unsigned(specs.precision - num_digits);
- fill = static_cast<char_type>('0');
}
- if (specs.align == align::none) specs.align = align::right;
- write_padded(specs, padded_int_writer<F>{size, prefix, fill, padding, f});
}
+};
- // Writes a decimal integer.
- template <typename Int> void write_decimal(Int value) {
- auto abs_value = static_cast<uint32_or_64_or_128_t<Int>>(value);
- bool negative = is_negative(value);
- // Don't do -abs_value since it trips unsigned-integer-overflow sanitizer.
- if (negative) abs_value = ~abs_value + 1;
- int num_digits = count_digits(abs_value);
- auto&& it = reserve((negative ? 1 : 0) + static_cast<size_t>(num_digits));
- if (negative) *it++ = static_cast<char_type>('-');
- it = format_decimal<char_type>(it, abs_value, num_digits);
- }
-
- // The handle_int_type_spec handler that writes an integer.
- template <typename Int, typename Specs> struct int_writer {
- using unsigned_type = uint32_or_64_or_128_t<Int>;
-
- basic_writer<Range>& writer;
- const Specs& specs;
- unsigned_type abs_value;
- char prefix[4];
- unsigned prefix_size;
-
- string_view get_prefix() const { return string_view(prefix, prefix_size); }
-
- int_writer(basic_writer<Range>& w, Int value, const Specs& s)
- : writer(w),
- specs(s),
- abs_value(static_cast<unsigned_type>(value)),
- prefix_size(0) {
- if (is_negative(value)) {
- prefix[0] = '-';
- ++prefix_size;
- abs_value = 0 - abs_value;
- } else if (specs.sign != sign::none && specs.sign != sign::minus) {
- prefix[0] = specs.sign == sign::plus ? '+' : ' ';
- ++prefix_size;
- }
+// Writes an integer in the format
+// <left-padding><prefix><numeric-padding><digits><right-padding>
+// where <digits> are written by f(it).
+template <typename OutputIt, typename Char, typename F>
+OutputIt write_int(OutputIt out, int num_digits, string_view prefix,
+ const basic_format_specs<Char>& specs, F f) {
+ auto data = write_int_data<Char>(num_digits, prefix, specs);
+ using iterator = remove_reference_t<decltype(reserve(out, 0))>;
+ return write_padded<align::right>(out, specs, data.size, [=](iterator it) {
+ if (prefix.size() != 0)
+ it = copy_str<Char>(prefix.begin(), prefix.end(), it);
+ it = std::fill_n(it, data.padding, static_cast<Char>('0'));
+ return f(it);
+ });
+}
+
+template <typename StrChar, typename Char, typename OutputIt>
+OutputIt write(OutputIt out, basic_string_view<StrChar> s,
+ const basic_format_specs<Char>& specs) {
+ auto data = s.data();
+ auto size = s.size();
+ if (specs.precision >= 0 && to_unsigned(specs.precision) < size)
+ size = code_point_index(s, to_unsigned(specs.precision));
+ auto width = specs.width != 0
+ ? count_code_points(basic_string_view<StrChar>(data, size))
+ : 0;
+ using iterator = remove_reference_t<decltype(reserve(out, 0))>;
+ return write_padded(out, specs, size, width, [=](iterator it) {
+ return copy_str<Char>(data, data + size, it);
+ });
+}
+
+// The handle_int_type_spec handler that writes an integer.
+template <typename OutputIt, typename Char, typename UInt> struct int_writer {
+ OutputIt out;
+ locale_ref locale;
+ const basic_format_specs<Char>& specs;
+ UInt abs_value;
+ char prefix[4];
+ unsigned prefix_size;
+
+ using iterator =
+ remove_reference_t<decltype(reserve(std::declval<OutputIt&>(), 0))>;
+
+ string_view get_prefix() const { return string_view(prefix, prefix_size); }
+
+ template <typename Int>
+ int_writer(OutputIt output, locale_ref loc, Int value,
+ const basic_format_specs<Char>& s)
+ : out(output),
+ locale(loc),
+ specs(s),
+ abs_value(static_cast<UInt>(value)),
+ prefix_size(0) {
+ static_assert(std::is_same<uint32_or_64_or_128_t<Int>, UInt>::value, "");
+ if (is_negative(value)) {
+ prefix[0] = '-';
+ ++prefix_size;
+ abs_value = 0 - abs_value;
+ } else if (specs.sign != sign::none && specs.sign != sign::minus) {
+ prefix[0] = specs.sign == sign::plus ? '+' : ' ';
+ ++prefix_size;
}
+ }
- struct dec_writer {
- unsigned_type abs_value;
- int num_digits;
-
- template <typename It> void operator()(It&& it) const {
- it = internal::format_decimal<char_type>(it, abs_value, num_digits);
- }
- };
+ void on_dec() {
+ auto num_digits = count_digits(abs_value);
+ out = write_int(
+ out, num_digits, get_prefix(), specs, [this, num_digits](iterator it) {
+ return format_decimal<Char>(it, abs_value, num_digits).end;
+ });
+ }
- void on_dec() {
- int num_digits = count_digits(abs_value);
- writer.write_int(num_digits, get_prefix(), specs,
- dec_writer{abs_value, num_digits});
+ void on_hex() {
+ if (specs.alt) {
+ prefix[prefix_size++] = '0';
+ prefix[prefix_size++] = specs.type;
}
-
- struct hex_writer {
- int_writer& self;
- int num_digits;
-
- template <typename It> void operator()(It&& it) const {
- it = format_uint<4, char_type>(it, self.abs_value, num_digits,
- self.specs.type != 'x');
- }
- };
-
- void on_hex() {
- if (specs.alt) {
- prefix[prefix_size++] = '0';
- prefix[prefix_size++] = specs.type;
- }
- int num_digits = count_digits<4>(abs_value);
- writer.write_int(num_digits, get_prefix(), specs,
- hex_writer{*this, num_digits});
+ int num_digits = count_digits<4>(abs_value);
+ out = write_int(out, num_digits, get_prefix(), specs,
+ [this, num_digits](iterator it) {
+ return format_uint<4, Char>(it, abs_value, num_digits,
+ specs.type != 'x');
+ });
+ }
+
+ void on_bin() {
+ if (specs.alt) {
+ prefix[prefix_size++] = '0';
+ prefix[prefix_size++] = static_cast<char>(specs.type);
}
-
- template <int BITS> struct bin_writer {
- unsigned_type abs_value;
- int num_digits;
-
- template <typename It> void operator()(It&& it) const {
- it = format_uint<BITS, char_type>(it, abs_value, num_digits);
- }
- };
-
- void on_bin() {
- if (specs.alt) {
- prefix[prefix_size++] = '0';
- prefix[prefix_size++] = static_cast<char>(specs.type);
- }
- int num_digits = count_digits<1>(abs_value);
- writer.write_int(num_digits, get_prefix(), specs,
- bin_writer<1>{abs_value, num_digits});
+ int num_digits = count_digits<1>(abs_value);
+ out = write_int(out, num_digits, get_prefix(), specs,
+ [this, num_digits](iterator it) {
+ return format_uint<1, Char>(it, abs_value, num_digits);
+ });
+ }
+
+ void on_oct() {
+ int num_digits = count_digits<3>(abs_value);
+ if (specs.alt && specs.precision <= num_digits && abs_value != 0) {
+ // Octal prefix '0' is counted as a digit, so only add it if precision
+ // is not greater than the number of digits.
+ prefix[prefix_size++] = '0';
}
+ out = write_int(out, num_digits, get_prefix(), specs,
+ [this, num_digits](iterator it) {
+ return format_uint<3, Char>(it, abs_value, num_digits);
+ });
+ }
- void on_oct() {
- int num_digits = count_digits<3>(abs_value);
- if (specs.alt && specs.precision <= num_digits && abs_value != 0) {
- // Octal prefix '0' is counted as a digit, so only add it if precision
- // is not greater than the number of digits.
- prefix[prefix_size++] = '0';
- }
- writer.write_int(num_digits, get_prefix(), specs,
- bin_writer<3>{abs_value, num_digits});
- }
+ enum { sep_size = 1 };
- enum { sep_size = 1 };
-
- struct num_writer {
- unsigned_type abs_value;
- int size;
- const std::string& groups;
- char_type sep;
-
- template <typename It> void operator()(It&& it) const {
- basic_string_view<char_type> s(&sep, sep_size);
- // Index of a decimal digit with the least significant digit having
- // index 0.
- int digit_index = 0;
- std::string::const_iterator group = groups.cbegin();
- it = format_decimal<char_type>(
- it, abs_value, size,
- [this, s, &group, &digit_index](char_type*& buffer) {
- if (*group <= 0 || ++digit_index % *group != 0 ||
- *group == max_value<char>())
- return;
- if (group + 1 != groups.cend()) {
- digit_index = 0;
- ++group;
- }
- buffer -= s.size();
- std::uninitialized_copy(s.data(), s.data() + s.size(),
- make_checked(buffer, s.size()));
- });
- }
- };
-
- void on_num() {
- std::string groups = grouping<char_type>(writer.locale_);
- if (groups.empty()) return on_dec();
- auto sep = thousands_sep<char_type>(writer.locale_);
- if (!sep) return on_dec();
- int num_digits = count_digits(abs_value);
- int size = num_digits;
- std::string::const_iterator group = groups.cbegin();
- while (group != groups.cend() && num_digits > *group && *group > 0 &&
- *group != max_value<char>()) {
- size += sep_size;
- num_digits -= *group;
+ void on_num() {
+ std::string groups = grouping<Char>(locale);
+ if (groups.empty()) return on_dec();
+ auto sep = thousands_sep<Char>(locale);
+ if (!sep) return on_dec();
+ int num_digits = count_digits(abs_value);
+ int size = num_digits, n = num_digits;
+ std::string::const_iterator group = groups.cbegin();
+ while (group != groups.cend() && n > *group && *group > 0 &&
+ *group != max_value<char>()) {
+ size += sep_size;
+ n -= *group;
+ ++group;
+ }
+ if (group == groups.cend()) size += sep_size * ((n - 1) / groups.back());
+ char digits[40];
+ format_decimal(digits, abs_value, num_digits);
+ basic_memory_buffer<Char> buffer;
+ size += prefix_size;
+ buffer.resize(size);
+ basic_string_view<Char> s(&sep, sep_size);
+ // Index of a decimal digit with the least significant digit having index 0.
+ int digit_index = 0;
+ group = groups.cbegin();
+ auto p = buffer.data() + size;
+ for (int i = num_digits - 1; i >= 0; --i) {
+ *--p = static_cast<Char>(digits[i]);
+ if (*group <= 0 || ++digit_index % *group != 0 ||
+ *group == max_value<char>())
+ continue;
+ if (group + 1 != groups.cend()) {
+ digit_index = 0;
++group;
}
- if (group == groups.cend())
- size += sep_size * ((num_digits - 1) / groups.back());
- writer.write_int(size, get_prefix(), specs,
- num_writer{abs_value, size, groups, sep});
- }
-
- FMT_NORETURN void on_error() {
- FMT_THROW(format_error("invalid type specifier"));
+ p -= s.size();
+ std::uninitialized_copy(s.data(), s.data() + s.size(),
+ make_checked(p, s.size()));
}
- };
+ if (prefix_size != 0) p[-1] = static_cast<Char>('-');
+ write(out, basic_string_view<Char>(buffer.data(), buffer.size()), specs);
+ }
- template <typename Char> struct str_writer {
- const Char* s;
- size_t size_;
+ void on_chr() { *out++ = static_cast<Char>(abs_value); }
- size_t size() const { return size_; }
- size_t width() const {
- return count_code_points(basic_string_view<Char>(s, size_));
- }
+ FMT_NORETURN void on_error() {
+ FMT_THROW(format_error("invalid type specifier"));
+ }
+};
- template <typename It> void operator()(It&& it) const {
- it = copy_str<char_type>(s, s + size_, it);
- }
+template <typename Char, typename OutputIt>
+OutputIt write_nonfinite(OutputIt out, bool isinf,
+ const basic_format_specs<Char>& specs,
+ const float_specs& fspecs) {
+ auto str =
+ isinf ? (fspecs.upper ? "INF" : "inf") : (fspecs.upper ? "NAN" : "nan");
+ constexpr size_t str_size = 3;
+ auto sign = fspecs.sign;
+ auto size = str_size + (sign ? 1 : 0);
+ using iterator = remove_reference_t<decltype(reserve(out, 0))>;
+ return write_padded(out, specs, size, [=](iterator it) {
+ if (sign) *it++ = static_cast<Char>(data::signs[sign]);
+ return copy_str<Char>(str, str + str_size, it);
+ });
+}
+
+template <typename Char, typename OutputIt, typename T,
+ FMT_ENABLE_IF(std::is_floating_point<T>::value)>
+OutputIt write(OutputIt out, T value, basic_format_specs<Char> specs,
+ locale_ref loc = {}) {
+ if (const_check(!is_supported_floating_point(value))) return out;
+ float_specs fspecs = parse_float_type_spec(specs);
+ fspecs.sign = specs.sign;
+ if (std::signbit(value)) { // value < 0 is false for NaN so use signbit.
+ fspecs.sign = sign::minus;
+ value = -value;
+ } else if (fspecs.sign == sign::minus) {
+ fspecs.sign = sign::none;
+ }
+
+ if (!std::isfinite(value))
+ return write_nonfinite(out, std::isinf(value), specs, fspecs);
+
+ if (specs.align == align::numeric && fspecs.sign) {
+ auto it = reserve(out, 1);
+ *it++ = static_cast<Char>(data::signs[fspecs.sign]);
+ out = base_iterator(out, it);
+ fspecs.sign = sign::none;
+ if (specs.width != 0) --specs.width;
+ }
+
+ memory_buffer buffer;
+ if (fspecs.format == float_format::hex) {
+ if (fspecs.sign) buffer.push_back(data::signs[fspecs.sign]);
+ snprintf_float(promote_float(value), specs.precision, fspecs, buffer);
+ return write_bytes(out, {buffer.data(), buffer.size()}, specs);
+ }
+ int precision = specs.precision >= 0 || !specs.type ? specs.precision : 6;
+ if (fspecs.format == float_format::exp) {
+ if (precision == max_value<int>())
+ FMT_THROW(format_error("number is too big"));
+ else
+ ++precision;
+ }
+ if (const_check(std::is_same<T, float>())) fspecs.binary32 = true;
+ fspecs.use_grisu = use_grisu<T>();
+ int exp = format_float(promote_float(value), precision, fspecs, buffer);
+ fspecs.precision = precision;
+ Char point =
+ fspecs.locale ? decimal_point<Char>(loc) : static_cast<Char>('.');
+ float_writer<Char> w(buffer.data(), static_cast<int>(buffer.size()), exp,
+ fspecs, point);
+ return write_padded<align::right>(out, specs, w.size(), w);
+}
+
+template <typename Char, typename OutputIt, typename T,
+ FMT_ENABLE_IF(std::is_floating_point<T>::value)>
+OutputIt write(OutputIt out, T value) {
+ if (const_check(!is_supported_floating_point(value))) return out;
+ auto fspecs = float_specs();
+ if (std::signbit(value)) { // value < 0 is false for NaN so use signbit.
+ fspecs.sign = sign::minus;
+ value = -value;
+ }
+
+ auto specs = basic_format_specs<Char>();
+ if (!std::isfinite(value))
+ return write_nonfinite(out, std::isinf(value), specs, fspecs);
+
+ memory_buffer buffer;
+ int precision = -1;
+ if (const_check(std::is_same<T, float>())) fspecs.binary32 = true;
+ fspecs.use_grisu = use_grisu<T>();
+ int exp = format_float(promote_float(value), precision, fspecs, buffer);
+ fspecs.precision = precision;
+ float_writer<Char> w(buffer.data(), static_cast<int>(buffer.size()), exp,
+ fspecs, static_cast<Char>('.'));
+ return base_iterator(out, w(reserve(out, w.size())));
+}
+
+template <typename Char, typename OutputIt>
+OutputIt write_char(OutputIt out, Char value,
+ const basic_format_specs<Char>& specs) {
+ using iterator = remove_reference_t<decltype(reserve(out, 0))>;
+ return write_padded(out, specs, 1, [=](iterator it) {
+ *it++ = value;
+ return it;
+ });
+}
+
+template <typename Char, typename OutputIt, typename UIntPtr>
+OutputIt write_ptr(OutputIt out, UIntPtr value,
+ const basic_format_specs<Char>* specs) {
+ int num_digits = count_digits<4>(value);
+ auto size = to_unsigned(num_digits) + size_t(2);
+ using iterator = remove_reference_t<decltype(reserve(out, 0))>;
+ auto write = [=](iterator it) {
+ *it++ = static_cast<Char>('0');
+ *it++ = static_cast<Char>('x');
+ return format_uint<4, Char>(it, value, num_digits);
};
+ return specs ? write_padded<align::right>(out, *specs, size, write)
+ : base_iterator(out, write(reserve(out, size)));
+}
- struct bytes_writer {
- string_view bytes;
+template <typename T> struct is_integral : std::is_integral<T> {};
+template <> struct is_integral<int128_t> : std::true_type {};
+template <> struct is_integral<uint128_t> : std::true_type {};
- size_t size() const { return bytes.size(); }
- size_t width() const { return bytes.size(); }
+template <typename Char, typename OutputIt>
+OutputIt write(OutputIt out, monostate) {
+ FMT_ASSERT(false, "");
+ return out;
+}
- template <typename It> void operator()(It&& it) const {
- const char* data = bytes.data();
- it = copy_str<char>(data, data + size(), it);
- }
- };
+template <typename Char, typename OutputIt,
+ FMT_ENABLE_IF(!std::is_same<Char, char>::value)>
+OutputIt write(OutputIt out, string_view value) {
+ auto it = reserve(out, value.size());
+ it = copy_str<Char>(value.begin(), value.end(), it);
+ return base_iterator(out, it);
+}
- template <typename UIntPtr> struct pointer_writer {
- UIntPtr value;
- int num_digits;
+template <typename Char, typename OutputIt>
+OutputIt write(OutputIt out, basic_string_view<Char> value) {
+ auto it = reserve(out, value.size());
+ it = std::copy(value.begin(), value.end(), it);
+ return base_iterator(out, it);
+}
- size_t size() const { return to_unsigned(num_digits) + 2; }
- size_t width() const { return size(); }
+template <typename Char, typename OutputIt, typename T,
+ FMT_ENABLE_IF(is_integral<T>::value &&
+ !std::is_same<T, bool>::value &&
+ !std::is_same<T, Char>::value)>
+OutputIt write(OutputIt out, T value) {
+ auto abs_value = static_cast<uint32_or_64_or_128_t<T>>(value);
+ bool negative = is_negative(value);
+ // Don't do -abs_value since it trips unsigned-integer-overflow sanitizer.
+ if (negative) abs_value = ~abs_value + 1;
+ int num_digits = count_digits(abs_value);
+ auto it = reserve(out, (negative ? 1 : 0) + static_cast<size_t>(num_digits));
+ if (negative) *it++ = static_cast<Char>('-');
+ it = format_decimal<Char>(it, abs_value, num_digits).end;
+ return base_iterator(out, it);
+}
- template <typename It> void operator()(It&& it) const {
- *it++ = static_cast<char_type>('0');
- *it++ = static_cast<char_type>('x');
- it = format_uint<4, char_type>(it, value, num_digits);
- }
- };
+template <typename Char, typename OutputIt>
+OutputIt write(OutputIt out, bool value) {
+ return write<Char>(out, string_view(value ? "true" : "false"));
+}
- public:
- explicit basic_writer(Range out, locale_ref loc = locale_ref())
- : out_(out.begin()), locale_(loc) {}
-
- iterator out() const { return out_; }
-
- // Writes a value in the format
- // <left-padding><value><right-padding>
- // where <value> is written by f(it).
- template <typename F> void write_padded(const format_specs& specs, F&& f) {
- // User-perceived width (in code points).
- unsigned width = to_unsigned(specs.width);
- size_t size = f.size(); // The number of code units.
- size_t num_code_points = width != 0 ? f.width() : size;
- if (width <= num_code_points) return f(reserve(size));
- size_t padding = width - num_code_points;
- size_t fill_size = specs.fill.size();
- auto&& it = reserve(size + padding * fill_size);
- if (specs.align == align::right) {
- it = fill(it, padding, specs.fill);
- f(it);
- } else if (specs.align == align::center) {
- std::size_t left_padding = padding / 2;
- it = fill(it, left_padding, specs.fill);
- f(it);
- it = fill(it, padding - left_padding, specs.fill);
- } else {
- f(it);
- it = fill(it, padding, specs.fill);
- }
+template <typename Char, typename OutputIt>
+OutputIt write(OutputIt out, Char value) {
+ auto it = reserve(out, 1);
+ *it++ = value;
+ return base_iterator(out, it);
+}
+
+template <typename Char, typename OutputIt>
+OutputIt write(OutputIt out, const Char* value) {
+ if (!value) {
+ FMT_THROW(format_error("string pointer is null"));
+ } else {
+ auto length = std::char_traits<Char>::length(value);
+ out = write(out, basic_string_view<Char>(value, length));
}
+ return out;
+}
+
+template <typename Char, typename OutputIt>
+OutputIt write(OutputIt out, const void* value) {
+ return write_ptr<Char>(out, to_uintptr(value), nullptr);
+}
- void write(int value) { write_decimal(value); }
- void write(long value) { write_decimal(value); }
- void write(long long value) { write_decimal(value); }
+template <typename Char, typename OutputIt, typename T>
+auto write(OutputIt out, const T& value) -> typename std::enable_if<
+ mapped_type_constant<T, basic_format_context<OutputIt, Char>>::value ==
+ type::custom_type,
+ OutputIt>::type {
+ basic_format_context<OutputIt, Char> ctx(out, {}, {});
+ return formatter<T>().format(value, ctx);
+}
- void write(unsigned value) { write_decimal(value); }
- void write(unsigned long value) { write_decimal(value); }
- void write(unsigned long long value) { write_decimal(value); }
+// An argument visitor that formats the argument and writes it via the output
+// iterator. It's a class and not a generic lambda for compatibility with C++11.
+template <typename OutputIt, typename Char> struct default_arg_formatter {
+ using context = basic_format_context<OutputIt, Char>;
-#if FMT_USE_INT128
- void write(int128_t value) { write_decimal(value); }
- void write(uint128_t value) { write_decimal(value); }
-#endif
+ OutputIt out;
+ basic_format_args<context> args;
+ locale_ref loc;
- template <typename T, typename Spec>
- void write_int(T value, const Spec& spec) {
- handle_int_type_spec(spec.type, int_writer<T, Spec>(*this, value, spec));
+ template <typename T> OutputIt operator()(T value) {
+ return write<Char>(out, value);
}
- template <typename T, FMT_ENABLE_IF(std::is_floating_point<T>::value)>
- void write(T value, format_specs specs = {}) {
- if (const_check(!is_supported_floating_point(value))) {
- return;
- }
- float_specs fspecs = parse_float_type_spec(specs);
- fspecs.sign = specs.sign;
- if (std::signbit(value)) { // value < 0 is false for NaN so use signbit.
- fspecs.sign = sign::minus;
- value = -value;
- } else if (fspecs.sign == sign::minus) {
- fspecs.sign = sign::none;
- }
+ OutputIt operator()(typename basic_format_arg<context>::handle handle) {
+ basic_format_parse_context<Char> parse_ctx({});
+ basic_format_context<OutputIt, Char> format_ctx(out, args, loc);
+ handle.format(parse_ctx, format_ctx);
+ return format_ctx.out();
+ }
+};
- if (!std::isfinite(value)) {
- auto str = std::isinf(value) ? (fspecs.upper ? "INF" : "inf")
- : (fspecs.upper ? "NAN" : "nan");
- return write_padded(specs, nonfinite_writer<char_type>{fspecs.sign, str});
- }
+template <typename OutputIt, typename Char,
+ typename ErrorHandler = error_handler>
+class arg_formatter_base {
+ public:
+ using iterator = OutputIt;
+ using char_type = Char;
+ using format_specs = basic_format_specs<Char>;
- if (specs.align == align::none) {
- specs.align = align::right;
- } else if (specs.align == align::numeric) {
- if (fspecs.sign) {
- auto&& it = reserve(1);
- *it++ = static_cast<char_type>(data::signs[fspecs.sign]);
- fspecs.sign = sign::none;
- if (specs.width != 0) --specs.width;
- }
- specs.align = align::right;
- }
+ private:
+ iterator out_;
+ locale_ref locale_;
+ format_specs* specs_;
- memory_buffer buffer;
- if (fspecs.format == float_format::hex) {
- if (fspecs.sign) buffer.push_back(data::signs[fspecs.sign]);
- snprintf_float(promote_float(value), specs.precision, fspecs, buffer);
- write_padded(specs, str_writer<char>{buffer.data(), buffer.size()});
- return;
- }
- int precision = specs.precision >= 0 || !specs.type ? specs.precision : 6;
- if (fspecs.format == float_format::exp) {
- if (precision == max_value<int>())
- FMT_THROW(format_error("number is too big"));
- else
- ++precision;
- }
- if (const_check(std::is_same<T, float>())) fspecs.binary32 = true;
- fspecs.use_grisu = use_grisu<T>();
- if (const_check(FMT_DEPRECATED_PERCENT) && fspecs.percent) value *= 100;
- int exp = format_float(promote_float(value), precision, fspecs, buffer);
- if (const_check(FMT_DEPRECATED_PERCENT) && fspecs.percent) {
- buffer.push_back('%');
- --exp; // Adjust decimal place position.
- }
- fspecs.precision = precision;
- char_type point = fspecs.locale ? decimal_point<char_type>(locale_)
- : static_cast<char_type>('.');
- write_padded(specs, float_writer<char_type>(buffer.data(),
- static_cast<int>(buffer.size()),
- exp, fspecs, point));
+ // Attempts to reserve space for n extra characters in the output range.
+ // Returns a pointer to the reserved range or a reference to out_.
+ auto reserve(size_t n) -> decltype(detail::reserve(out_, n)) {
+ return detail::reserve(out_, n);
+ }
+
+ using reserve_iterator = remove_reference_t<decltype(
+ detail::reserve(std::declval<iterator&>(), 0))>;
+
+ template <typename T> void write_int(T value, const format_specs& spec) {
+ using uint_type = uint32_or_64_or_128_t<T>;
+ int_writer<iterator, Char, uint_type> w(out_, locale_, value, spec);
+ handle_int_type_spec(spec.type, w);
+ out_ = w.out;
}
void write(char value) {
*it++ = value;
}
- template <typename Char, FMT_ENABLE_IF(std::is_same<Char, char_type>::value)>
- void write(Char value) {
- auto&& it = reserve(1);
- *it++ = value;
+ template <typename Ch, FMT_ENABLE_IF(std::is_same<Ch, Char>::value)>
+ void write(Ch value) {
+ out_ = detail::write<Char>(out_, value);
}
void write(string_view value) {
auto&& it = reserve(value.size());
- it = copy_str<char_type>(value.begin(), value.end(), it);
+ it = copy_str<Char>(value.begin(), value.end(), it);
}
void write(wstring_view value) {
- static_assert(std::is_same<char_type, wchar_t>::value, "");
+ static_assert(std::is_same<Char, wchar_t>::value, "");
auto&& it = reserve(value.size());
it = std::copy(value.begin(), value.end(), it);
}
- template <typename Char>
- void write(const Char* s, std::size_t size, const format_specs& specs) {
- write_padded(specs, str_writer<Char>{s, size});
+ template <typename Ch>
+ void write(const Ch* s, size_t size, const format_specs& specs) {
+ auto width = specs.width != 0
+ ? count_code_points(basic_string_view<Ch>(s, size))
+ : 0;
+ out_ = write_padded(out_, specs, size, width, [=](reserve_iterator it) {
+ return copy_str<Char>(s, s + size, it);
+ });
}
- template <typename Char>
- void write(basic_string_view<Char> s, const format_specs& specs = {}) {
- const Char* data = s.data();
- std::size_t size = s.size();
- if (specs.precision >= 0 && to_unsigned(specs.precision) < size)
- size = code_point_index(s, to_unsigned(specs.precision));
- write(data, size, specs);
+ template <typename Ch>
+ void write(basic_string_view<Ch> s, const format_specs& specs = {}) {
+ out_ = detail::write(out_, s, specs);
}
- void write_bytes(string_view bytes, const format_specs& specs) {
- write_padded(specs, bytes_writer{bytes});
- }
-
- template <typename UIntPtr>
- void write_pointer(UIntPtr value, const format_specs* specs) {
- int num_digits = count_digits<4>(value);
- auto pw = pointer_writer<UIntPtr>{value, num_digits};
- if (!specs) return pw(reserve(to_unsigned(num_digits) + 2));
- format_specs specs_copy = *specs;
- if (specs_copy.align == align::none) specs_copy.align = align::right;
- write_padded(specs_copy, pw);
+ void write_pointer(const void* p) {
+ out_ = write_ptr<char_type>(out_, to_uintptr(p), specs_);
}
-};
-using writer = basic_writer<buffer_range<char>>;
-
-template <typename T> struct is_integral : std::is_integral<T> {};
-template <> struct is_integral<int128_t> : std::true_type {};
-template <> struct is_integral<uint128_t> : std::true_type {};
+ struct char_spec_handler : ErrorHandler {
+ arg_formatter_base& formatter;
+ Char value;
-template <typename Range, typename ErrorHandler = internal::error_handler>
-class arg_formatter_base {
- public:
- using char_type = typename Range::value_type;
- using iterator = typename Range::iterator;
- using format_specs = basic_format_specs<char_type>;
+ char_spec_handler(arg_formatter_base& f, Char val)
+ : formatter(f), value(val) {}
- private:
- using writer_type = basic_writer<Range>;
- writer_type writer_;
- format_specs* specs_;
+ void on_int() {
+ // char is only formatted as int if there are specs.
+ formatter.write_int(static_cast<int>(value), *formatter.specs_);
+ }
+ void on_char() {
+ if (formatter.specs_)
+ formatter.out_ = write_char(formatter.out_, value, *formatter.specs_);
+ else
+ formatter.write(value);
+ }
+ };
- struct char_writer {
- char_type value;
+ struct cstring_spec_handler : error_handler {
+ arg_formatter_base& formatter;
+ const Char* value;
- size_t size() const { return 1; }
- size_t width() const { return 1; }
+ cstring_spec_handler(arg_formatter_base& f, const Char* val)
+ : formatter(f), value(val) {}
- template <typename It> void operator()(It&& it) const { *it++ = value; }
+ void on_string() { formatter.write(value); }
+ void on_pointer() { formatter.write_pointer(value); }
};
- void write_char(char_type value) {
- if (specs_)
- writer_.write_padded(*specs_, char_writer{value});
- else
- writer_.write(value);
- }
-
- void write_pointer(const void* p) {
- writer_.write_pointer(internal::to_uintptr(p), specs_);
- }
-
protected:
- writer_type& writer() { return writer_; }
- FMT_DEPRECATED format_specs* spec() { return specs_; }
+ iterator out() { return out_; }
format_specs* specs() { return specs_; }
- iterator out() { return writer_.out(); }
void write(bool value) {
- string_view sv(value ? "true" : "false");
- specs_ ? writer_.write(sv, *specs_) : writer_.write(sv);
+ if (specs_)
+ write(string_view(value ? "true" : "false"), *specs_);
+ else
+ out_ = detail::write<Char>(out_, value);
}
- void write(const char_type* value) {
+ void write(const Char* value) {
if (!value) {
FMT_THROW(format_error("string pointer is null"));
} else {
auto length = std::char_traits<char_type>::length(value);
basic_string_view<char_type> sv(value, length);
- specs_ ? writer_.write(sv, *specs_) : writer_.write(sv);
+ specs_ ? write(sv, *specs_) : write(sv);
}
}
public:
- arg_formatter_base(Range r, format_specs* s, locale_ref loc)
- : writer_(r, loc), specs_(s) {}
+ arg_formatter_base(OutputIt out, format_specs* s, locale_ref loc)
+ : out_(out), locale_(loc), specs_(s) {}
iterator operator()(monostate) {
FMT_ASSERT(false, "invalid argument type");
- return out();
+ return out_;
}
template <typename T, FMT_ENABLE_IF(is_integral<T>::value)>
- iterator operator()(T value) {
+ FMT_INLINE iterator operator()(T value) {
if (specs_)
- writer_.write_int(value, *specs_);
+ write_int(value, *specs_);
else
- writer_.write(value);
- return out();
+ out_ = detail::write<Char>(out_, value);
+ return out_;
}
- iterator operator()(char_type value) {
- internal::handle_char_specs(
- specs_, char_spec_handler(*this, static_cast<char_type>(value)));
- return out();
+ iterator operator()(Char value) {
+ handle_char_specs(specs_,
+ char_spec_handler(*this, static_cast<Char>(value)));
+ return out_;
}
iterator operator()(bool value) {
if (specs_ && specs_->type) return (*this)(value ? 1 : 0);
write(value != 0);
- return out();
+ return out_;
}
template <typename T, FMT_ENABLE_IF(std::is_floating_point<T>::value)>
iterator operator()(T value) {
+ auto specs = specs_ ? *specs_ : format_specs();
if (const_check(is_supported_floating_point(value)))
- writer_.write(value, specs_ ? *specs_ : format_specs());
+ out_ = detail::write(out_, value, specs, locale_);
else
FMT_ASSERT(false, "unsupported float argument type");
- return out();
+ return out_;
}
- struct char_spec_handler : ErrorHandler {
- arg_formatter_base& formatter;
- char_type value;
-
- char_spec_handler(arg_formatter_base& f, char_type val)
- : formatter(f), value(val) {}
-
- void on_int() {
- if (formatter.specs_)
- formatter.writer_.write_int(value, *formatter.specs_);
- else
- formatter.writer_.write(value);
- }
- void on_char() { formatter.write_char(value); }
- };
-
- struct cstring_spec_handler : internal::error_handler {
- arg_formatter_base& formatter;
- const char_type* value;
-
- cstring_spec_handler(arg_formatter_base& f, const char_type* val)
- : formatter(f), value(val) {}
-
- void on_string() { formatter.write(value); }
- void on_pointer() { formatter.write_pointer(value); }
- };
-
- iterator operator()(const char_type* value) {
- if (!specs_) return write(value), out();
- internal::handle_cstring_type_spec(specs_->type,
- cstring_spec_handler(*this, value));
- return out();
+ iterator operator()(const Char* value) {
+ if (!specs_) return write(value), out_;
+ handle_cstring_type_spec(specs_->type, cstring_spec_handler(*this, value));
+ return out_;
}
- iterator operator()(basic_string_view<char_type> value) {
+ iterator operator()(basic_string_view<Char> value) {
if (specs_) {
- internal::check_string_type_spec(specs_->type, internal::error_handler());
- writer_.write(value, *specs_);
+ check_string_type_spec(specs_->type, error_handler());
+ write(value, *specs_);
} else {
- writer_.write(value);
+ write(value);
}
- return out();
+ return out_;
}
iterator operator()(const void* value) {
- if (specs_)
- check_pointer_type_spec(specs_->type, internal::error_handler());
+ if (specs_) check_pointer_type_spec(specs_->type, error_handler());
write_pointer(value);
- return out();
+ return out_;
}
};
template <typename ErrorHandler> class numeric_specs_checker {
public:
- FMT_CONSTEXPR numeric_specs_checker(ErrorHandler& eh, internal::type arg_type)
+ FMT_CONSTEXPR numeric_specs_checker(ErrorHandler& eh, detail::type arg_type)
: error_handler_(eh), arg_type_(arg_type) {}
FMT_CONSTEXPR void require_numeric_argument() {
private:
ErrorHandler& error_handler_;
- internal::type arg_type_;
+ detail::type arg_type_;
};
// A format specifier handler that checks if specifiers are consistent with the
// argument type.
template <typename Handler> class specs_checker : public Handler {
+ private:
+ numeric_specs_checker<Handler> checker_;
+
+ // Suppress an MSVC warning about using this in initializer list.
+ FMT_CONSTEXPR Handler& error_handler() { return *this; }
+
public:
- FMT_CONSTEXPR specs_checker(const Handler& handler, internal::type arg_type)
- : Handler(handler), checker_(*this, arg_type) {}
+ FMT_CONSTEXPR specs_checker(const Handler& handler, detail::type arg_type)
+ : Handler(handler), checker_(error_handler(), arg_type) {}
FMT_CONSTEXPR specs_checker(const specs_checker& other)
- : Handler(other), checker_(*this, other.arg_type_) {}
+ : Handler(other), checker_(error_handler(), other.arg_type_) {}
FMT_CONSTEXPR void on_align(align_t align) {
if (align == align::numeric) checker_.require_numeric_argument();
}
FMT_CONSTEXPR void end_precision() { checker_.check_precision(); }
-
- private:
- numeric_specs_checker<Handler> checker_;
};
template <template <typename> class Handler, typename FormatArg,
struct auto_id {};
-template <typename Context>
-FMT_CONSTEXPR typename Context::format_arg get_arg(Context& ctx, int id) {
+template <typename Context, typename ID>
+FMT_CONSTEXPR typename Context::format_arg get_arg(Context& ctx, ID id) {
auto arg = ctx.arg(id);
- if (!arg) ctx.on_error("argument index out of range");
+ if (!arg) ctx.on_error("argument not found");
return arg;
}
using format_arg = typename Context::format_arg;
FMT_CONSTEXPR format_arg get_arg(auto_id) {
- return internal::get_arg(context_, parse_context_.next_arg_id());
+ return detail::get_arg(context_, parse_context_.next_arg_id());
}
FMT_CONSTEXPR format_arg get_arg(int arg_id) {
parse_context_.check_arg_id(arg_id);
- return internal::get_arg(context_, arg_id);
+ return detail::get_arg(context_, arg_id);
}
FMT_CONSTEXPR format_arg get_arg(basic_string_view<char_type> arg_id) {
parse_context_.check_arg_id(arg_id);
- return context_.arg(arg_id);
+ return detail::get_arg(context_, arg_id);
}
ParseContext& parse_context_;
case '>':
align = align::right;
break;
-#if FMT_NUMERIC_ALIGN
+#if FMT_DEPRECATED_NUMERIC_ALIGN
case '=':
align = align::numeric;
break;
inline bool find<false, char>(const char* first, const char* last, char value,
const char*& out) {
out = static_cast<const char*>(
- std::memchr(first, value, internal::to_unsigned(last - first)));
+ std::memchr(first, value, detail::to_unsigned(last - first)));
return out != nullptr;
}
template <typename Handler, typename Char> struct id_adapter {
- FMT_CONSTEXPR void operator()() { handler.on_arg_id(); }
- FMT_CONSTEXPR void operator()(int id) { handler.on_arg_id(id); }
+ Handler& handler;
+ int arg_id;
+
+ FMT_CONSTEXPR void operator()() { arg_id = handler.on_arg_id(); }
+ FMT_CONSTEXPR void operator()(int id) { arg_id = handler.on_arg_id(id); }
FMT_CONSTEXPR void operator()(basic_string_view<Char> id) {
- handler.on_arg_id(id);
+ arg_id = handler.on_arg_id(id);
}
FMT_CONSTEXPR void on_error(const char* message) {
handler.on_error(message);
}
- Handler& handler;
};
+template <typename Char, typename Handler>
+FMT_CONSTEXPR const Char* parse_replacement_field(const Char* begin,
+ const Char* end,
+ Handler&& handler) {
+ ++begin;
+ if (begin == end) return handler.on_error("invalid format string"), end;
+ if (static_cast<char>(*begin) == '}') {
+ handler.on_replacement_field(handler.on_arg_id(), begin);
+ } else if (*begin == '{') {
+ handler.on_text(begin, begin + 1);
+ } else {
+ auto adapter = id_adapter<Handler, Char>{handler, 0};
+ begin = parse_arg_id(begin, end, adapter);
+ Char c = begin != end ? *begin : Char();
+ if (c == '}') {
+ handler.on_replacement_field(adapter.arg_id, begin);
+ } else if (c == ':') {
+ begin = handler.on_format_specs(adapter.arg_id, begin + 1, end);
+ if (begin == end || *begin != '}')
+ return handler.on_error("unknown format specifier"), end;
+ } else {
+ return handler.on_error("missing '}' in format string"), end;
+ }
+ }
+ return begin + 1;
+}
+
template <bool IS_CONSTEXPR, typename Char, typename Handler>
-FMT_CONSTEXPR void parse_format_string(basic_string_view<Char> format_str,
- Handler&& handler) {
- struct pfs_writer {
+FMT_CONSTEXPR_DECL FMT_INLINE void parse_format_string(
+ basic_string_view<Char> format_str, Handler&& handler) {
+ auto begin = format_str.data();
+ auto end = begin + format_str.size();
+ if (end - begin < 32) {
+ // Use a simple loop instead of memchr for small strings.
+ const Char* p = begin;
+ while (p != end) {
+ auto c = *p++;
+ if (c == '{') {
+ handler.on_text(begin, p - 1);
+ begin = p = parse_replacement_field(p - 1, end, handler);
+ } else if (c == '}') {
+ if (p == end || *p != '}')
+ return handler.on_error("unmatched '}' in format string");
+ handler.on_text(begin, p);
+ begin = ++p;
+ }
+ }
+ handler.on_text(begin, end);
+ return;
+ }
+ struct writer {
FMT_CONSTEXPR void operator()(const Char* begin, const Char* end) {
if (begin == end) return;
for (;;) {
}
Handler& handler_;
} write{handler};
- auto begin = format_str.data();
- auto end = begin + format_str.size();
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.
if (*begin != '{' && !find<IS_CONSTEXPR>(begin + 1, end, '{', p))
return write(begin, end);
write(begin, p);
- ++p;
- if (p == end) return handler.on_error("invalid format string");
- if (static_cast<char>(*p) == '}') {
- handler.on_arg_id();
- handler.on_replacement_field(p);
- } else if (*p == '{') {
- handler.on_text(p, p + 1);
- } else {
- p = parse_arg_id(p, end, id_adapter<Handler, Char>{handler});
- Char c = p != end ? *p : Char();
- if (c == '}') {
- handler.on_replacement_field(p);
- } else if (c == ':') {
- p = handler.on_format_specs(p + 1, end);
- if (p == end || *p != '}')
- return handler.on_error("unknown format specifier");
- } else {
- return handler.on_error("missing '}' in format string");
- }
- }
- begin = p + 1;
+ begin = parse_replacement_field(p, end, handler);
}
}
using char_type = typename ParseContext::char_type;
using context = buffer_context<char_type>;
using mapped_type =
- conditional_t<internal::mapped_type_constant<T, context>::value !=
+ conditional_t<detail::mapped_type_constant<T, context>::value !=
type::custom_type,
decltype(arg_mapper<context>().map(std::declval<T>())), T>;
auto f = conditional_t<has_formatter<mapped_type, context>::value,
formatter<mapped_type, char_type>,
- internal::fallback_formatter<T, char_type>>();
+ detail::fallback_formatter<T, char_type>>();
return f.parse(ctx);
}
+template <typename ArgFormatter, typename Char, typename Context>
+struct format_handler : detail::error_handler {
+ basic_format_parse_context<Char> parse_context;
+ Context context;
+
+ format_handler(typename ArgFormatter::iterator out,
+ basic_string_view<Char> str,
+ basic_format_args<Context> format_args, detail::locale_ref loc)
+ : parse_context(str), context(out, format_args, loc) {}
+
+ void on_text(const Char* begin, const Char* end) {
+ auto size = to_unsigned(end - begin);
+ auto out = context.out();
+ auto&& it = reserve(out, size);
+ it = std::copy_n(begin, size, it);
+ context.advance_to(out);
+ }
+
+ int on_arg_id() { return parse_context.next_arg_id(); }
+ int on_arg_id(int id) { return parse_context.check_arg_id(id), id; }
+ int on_arg_id(basic_string_view<Char> id) {
+ int arg_id = context.arg_id(id);
+ if (arg_id < 0) on_error("argument not found");
+ return arg_id;
+ }
+
+ FMT_INLINE void on_replacement_field(int id, const Char*) {
+ auto arg = get_arg(context, id);
+ context.advance_to(visit_format_arg(
+ default_arg_formatter<typename ArgFormatter::iterator, Char>{
+ context.out(), context.args(), context.locale()},
+ arg));
+ }
+
+ const Char* on_format_specs(int id, const Char* begin, const Char* end) {
+ advance_to(parse_context, begin);
+ auto arg = get_arg(context, id);
+ custom_formatter<Context> f(parse_context, context);
+ if (visit_format_arg(f, arg)) return parse_context.begin();
+ basic_format_specs<Char> specs;
+ using parse_context_t = basic_format_parse_context<Char>;
+ specs_checker<specs_handler<parse_context_t, Context>> handler(
+ specs_handler<parse_context_t, Context>(specs, parse_context, context),
+ arg.type());
+ begin = parse_format_specs(begin, end, handler);
+ if (begin == end || *begin != '}') on_error("missing '}' in format string");
+ advance_to(parse_context, begin);
+ context.advance_to(
+ visit_format_arg(ArgFormatter(context, &parse_context, &specs), arg));
+ return begin;
+ }
+};
+
+// 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 = max_value<int>(),
+ ErrorHandler eh = {})
+ : base(format_str, eh), num_args_(num_args) {}
+
+ FMT_CONSTEXPR int next_arg_id() {
+ 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 Char, typename ErrorHandler, typename... Args>
class format_string_checker {
public:
explicit FMT_CONSTEXPR format_string_checker(
basic_string_view<Char> format_str, ErrorHandler eh)
- : arg_id_(-1),
- context_(format_str, eh),
+ : context_(format_str, num_args, eh),
parse_funcs_{&parse_format_specs<Args, parse_context_type>...} {}
FMT_CONSTEXPR void on_text(const Char*, const Char*) {}
- FMT_CONSTEXPR void on_arg_id() {
- arg_id_ = context_.next_arg_id();
- check_arg_id();
- }
- FMT_CONSTEXPR void on_arg_id(int id) {
- arg_id_ = id;
- context_.check_arg_id(id);
- check_arg_id();
- }
- FMT_CONSTEXPR void on_arg_id(basic_string_view<Char>) {
+ FMT_CONSTEXPR int on_arg_id() { return context_.next_arg_id(); }
+ FMT_CONSTEXPR int on_arg_id(int id) { return context_.check_arg_id(id), id; }
+ FMT_CONSTEXPR int on_arg_id(basic_string_view<Char>) {
on_error("compile-time checks don't support named arguments");
+ return 0;
}
- FMT_CONSTEXPR void on_replacement_field(const Char*) {}
+ FMT_CONSTEXPR void on_replacement_field(int, const Char*) {}
- FMT_CONSTEXPR const Char* on_format_specs(const Char* begin, const Char*) {
+ FMT_CONSTEXPR const Char* on_format_specs(int id, const Char* begin,
+ const Char*) {
advance_to(context_, begin);
- return arg_id_ < num_args ? parse_funcs_[arg_id_](context_) : begin;
+ return id < num_args ? parse_funcs_[id](context_) : begin;
}
FMT_CONSTEXPR void on_error(const char* message) {
}
private:
- using parse_context_type = basic_format_parse_context<Char, ErrorHandler>;
+ using parse_context_type = compile_parse_context<Char, ErrorHandler>;
enum { num_args = sizeof...(Args) };
- FMT_CONSTEXPR void check_arg_id() {
- if (arg_id_ >= num_args) context_.on_error("argument index out of range");
- }
-
// Format specifier parsing function.
using parse_func = const Char* (*)(parse_context_type&);
- int arg_id_;
parse_context_type context_;
parse_func parse_funcs_[num_args > 0 ? num_args : 1];
};
-template <typename Char, typename ErrorHandler, typename... Args>
-FMT_CONSTEXPR bool do_check_format_string(basic_string_view<Char> s,
- ErrorHandler eh = ErrorHandler()) {
- format_string_checker<Char, ErrorHandler, Args...> checker(s, eh);
- parse_format_string<true>(s, checker);
- return true;
+// Converts string literals to basic_string_view.
+template <typename Char, size_t N>
+FMT_CONSTEXPR basic_string_view<Char> compile_string_to_view(
+ const Char (&s)[N]) {
+ // Remove trailing null character if needed. Won't be present if this is used
+ // with raw character array (i.e. not defined as a string).
+ return {s,
+ N - ((std::char_traits<Char>::to_int_type(s[N - 1]) == 0) ? 1 : 0)};
}
+// Converts string_view to basic_string_view.
+template <typename Char>
+FMT_CONSTEXPR basic_string_view<Char> compile_string_to_view(
+ const std_string_view<Char>& s) {
+ return {s.data(), s.size()};
+}
+
+#define FMT_STRING_IMPL(s, base) \
+ [] { \
+ /* Use a macro-like name to avoid shadowing warnings. */ \
+ struct FMT_COMPILE_STRING : base { \
+ using char_type = fmt::remove_cvref_t<decltype(s[0])>; \
+ FMT_MAYBE_UNUSED FMT_CONSTEXPR \
+ operator fmt::basic_string_view<char_type>() const { \
+ return fmt::detail::compile_string_to_view<char_type>(s); \
+ } \
+ }; \
+ return FMT_COMPILE_STRING(); \
+ }()
+
+/**
+ \rst
+ Constructs a compile-time format string from a string literal *s*.
+
+ **Example**::
+
+ // A compile-time error because 'd' is an invalid specifier for strings.
+ std::string s = fmt::format(FMT_STRING("{:d}"), "foo");
+ \endrst
+ */
+#define FMT_STRING(s) FMT_STRING_IMPL(s, fmt::compile_string)
+
template <typename... Args, typename S,
enable_if_t<(is_compile_string<S>::value), int>>
void check_format_string(S format_str) {
- FMT_CONSTEXPR_DECL bool invalid_format = internal::do_check_format_string<
- typename S::char_type, internal::error_handler,
- remove_const_t<remove_reference_t<Args>>...>(to_string_view(format_str));
+ FMT_CONSTEXPR_DECL auto s = to_string_view(format_str);
+ using checker = format_string_checker<typename S::char_type, error_handler,
+ remove_cvref_t<Args>...>;
+ FMT_CONSTEXPR_DECL bool invalid_format =
+ (parse_format_string<true>(s, checker(s, {})), true);
(void)invalid_format;
}
case arg_id_kind::none:
break;
case arg_id_kind::index:
- value = internal::get_dynamic_spec<Handler>(ctx.arg(ref.val.index),
- ctx.error_handler());
+ value = detail::get_dynamic_spec<Handler>(ctx.arg(ref.val.index),
+ ctx.error_handler());
break;
case arg_id_kind::name:
- value = internal::get_dynamic_spec<Handler>(ctx.arg(ref.val.name),
- ctx.error_handler());
+ value = detail::get_dynamic_spec<Handler>(ctx.arg(ref.val.name),
+ ctx.error_handler());
break;
}
}
-using format_func = void (*)(internal::buffer<char>&, int, string_view);
+using format_func = void (*)(detail::buffer<char>&, int, string_view);
FMT_API void format_error_code(buffer<char>& out, int error_code,
string_view message) FMT_NOEXCEPT;
FMT_API void report_error(format_func func, int error_code,
string_view message) FMT_NOEXCEPT;
-} // namespace internal
-
-template <typename Range>
-using basic_writer FMT_DEPRECATED_ALIAS = internal::basic_writer<Range>;
-using writer FMT_DEPRECATED_ALIAS = internal::writer;
-using wwriter FMT_DEPRECATED_ALIAS =
- internal::basic_writer<buffer_range<wchar_t>>;
/** The default argument formatter. */
-template <typename Range>
-class arg_formatter : public internal::arg_formatter_base<Range> {
+template <typename OutputIt, typename Char>
+class arg_formatter : public arg_formatter_base<OutputIt, Char> {
private:
- using char_type = typename Range::value_type;
- using base = internal::arg_formatter_base<Range>;
- using context_type = basic_format_context<typename base::iterator, char_type>;
+ using char_type = Char;
+ using base = arg_formatter_base<OutputIt, Char>;
+ using context_type = basic_format_context<OutputIt, Char>;
context_type& ctx_;
basic_format_parse_context<char_type>* parse_ctx_;
+ const Char* ptr_;
public:
- using range = Range;
using iterator = typename base::iterator;
using format_specs = typename base::format_specs;
explicit arg_formatter(
context_type& ctx,
basic_format_parse_context<char_type>* parse_ctx = nullptr,
- format_specs* specs = nullptr)
- : base(Range(ctx.out()), specs, ctx.locale()),
+ format_specs* specs = nullptr, const Char* ptr = nullptr)
+ : base(ctx.out(), specs, ctx.locale()),
ctx_(ctx),
- parse_ctx_(parse_ctx) {}
+ parse_ctx_(parse_ctx),
+ ptr_(ptr) {}
using base::operator();
/** Formats an argument of a user-defined type. */
iterator operator()(typename basic_format_arg<context_type>::handle handle) {
+ if (ptr_) advance_to(*parse_ctx_, ptr_);
handle.format(*parse_ctx_, ctx_);
return ctx_.out();
}
};
+} // namespace detail
+
+template <typename OutputIt, typename Char>
+using arg_formatter FMT_DEPRECATED_ALIAS =
+ detail::arg_formatter<OutputIt, Char>;
/**
An error returned by an operating system or a language runtime,
may look like "Unknown error -1" and is platform-dependent.
\endrst
*/
-FMT_API void format_system_error(internal::buffer<char>& out, int error_code,
+FMT_API void format_system_error(detail::buffer<char>& out, int error_code,
string_view message) FMT_NOEXCEPT;
// Reports a system error without throwing an exception.
mutable char buffer_[buffer_size];
char* str_;
- // Formats value in reverse and returns a pointer to the beginning.
- char* format_decimal(unsigned long long value) {
- char* ptr = buffer_ + (buffer_size - 1); // Parens to workaround MSVC bug.
- while (value >= 100) {
- // Integer division is slow so do it for a group of two digits instead
- // of for every digit. The idea comes from the talk by Alexandrescu
- // "Three Optimization Tips for C++". See speed-test for a comparison.
- auto index = static_cast<unsigned>((value % 100) * 2);
- value /= 100;
- *--ptr = internal::data::digits[index + 1];
- *--ptr = internal::data::digits[index];
- }
- if (value < 10) {
- *--ptr = static_cast<char>('0' + value);
- return ptr;
- }
- auto index = static_cast<unsigned>(value * 2);
- *--ptr = internal::data::digits[index + 1];
- *--ptr = internal::data::digits[index];
- return ptr;
+ template <typename UInt> char* format_unsigned(UInt value) {
+ auto n = static_cast<detail::uint32_or_64_or_128_t<UInt>>(value);
+ return detail::format_decimal(buffer_, n, buffer_size - 1).begin;
}
- void format_signed(long long value) {
- auto abs_value = static_cast<unsigned long long>(value);
+ template <typename Int> char* format_signed(Int value) {
+ auto abs_value = static_cast<detail::uint32_or_64_or_128_t<Int>>(value);
bool negative = value < 0;
if (negative) abs_value = 0 - abs_value;
- str_ = format_decimal(abs_value);
- if (negative) *--str_ = '-';
+ auto begin = format_unsigned(abs_value);
+ if (negative) *--begin = '-';
+ return begin;
}
public:
- explicit format_int(int value) { format_signed(value); }
- explicit format_int(long value) { format_signed(value); }
- explicit format_int(long long value) { format_signed(value); }
- explicit format_int(unsigned value) : str_(format_decimal(value)) {}
- explicit format_int(unsigned long value) : str_(format_decimal(value)) {}
- explicit format_int(unsigned long long value) : str_(format_decimal(value)) {}
+ explicit format_int(int value) : str_(format_signed(value)) {}
+ explicit format_int(long value) : str_(format_signed(value)) {}
+ explicit format_int(long long value) : str_(format_signed(value)) {}
+ explicit format_int(unsigned value) : str_(format_unsigned(value)) {}
+ explicit format_int(unsigned long value) : str_(format_unsigned(value)) {}
+ explicit format_int(unsigned long long value)
+ : str_(format_unsigned(value)) {}
/** Returns the number of characters written to the output buffer. */
- std::size_t size() const {
- return internal::to_unsigned(buffer_ - str_ + buffer_size - 1);
+ size_t size() const {
+ return detail::to_unsigned(buffer_ - str_ + buffer_size - 1);
}
/**
std::string str() const { return std::string(str_, size()); }
};
-// A formatter specialization for the core types corresponding to internal::type
+// A formatter specialization for the core types corresponding to detail::type
// constants.
template <typename T, typename Char>
struct formatter<T, Char,
- enable_if_t<internal::type_constant<T, Char>::value !=
- internal::type::custom_type>> {
+ enable_if_t<detail::type_constant<T, Char>::value !=
+ detail::type::custom_type>> {
FMT_CONSTEXPR formatter() = default;
// Parses format specifiers stopping either at the end of the range or at the
// terminating '}'.
template <typename ParseContext>
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
- using handler_type = internal::dynamic_specs_handler<ParseContext>;
- auto type = internal::type_constant<T, Char>::value;
- internal::specs_checker<handler_type> handler(handler_type(specs_, ctx),
- type);
+ using handler_type = detail::dynamic_specs_handler<ParseContext>;
+ auto type = detail::type_constant<T, Char>::value;
+ detail::specs_checker<handler_type> handler(handler_type(specs_, ctx),
+ type);
auto it = parse_format_specs(ctx.begin(), ctx.end(), handler);
auto eh = ctx.error_handler();
switch (type) {
- case internal::type::none_type:
- case internal::type::named_arg_type:
+ case detail::type::none_type:
FMT_ASSERT(false, "invalid argument type");
break;
- case internal::type::int_type:
- case internal::type::uint_type:
- case internal::type::long_long_type:
- case internal::type::ulong_long_type:
- case internal::type::int128_type:
- case internal::type::uint128_type:
- case internal::type::bool_type:
+ case detail::type::int_type:
+ case detail::type::uint_type:
+ case detail::type::long_long_type:
+ case detail::type::ulong_long_type:
+ case detail::type::int128_type:
+ case detail::type::uint128_type:
+ case detail::type::bool_type:
handle_int_type_spec(specs_.type,
- internal::int_type_checker<decltype(eh)>(eh));
+ detail::int_type_checker<decltype(eh)>(eh));
break;
- case internal::type::char_type:
+ case detail::type::char_type:
handle_char_specs(
- &specs_, internal::char_specs_checker<decltype(eh)>(specs_.type, eh));
+ &specs_, detail::char_specs_checker<decltype(eh)>(specs_.type, eh));
break;
- case internal::type::float_type:
- if (internal::const_check(FMT_USE_FLOAT)) {
- internal::parse_float_type_spec(specs_, eh);
- } else {
+ case detail::type::float_type:
+ if (detail::const_check(FMT_USE_FLOAT))
+ detail::parse_float_type_spec(specs_, eh);
+ else
FMT_ASSERT(false, "float support disabled");
- }
break;
- case internal::type::double_type:
- if (internal::const_check(FMT_USE_DOUBLE)) {
- internal::parse_float_type_spec(specs_, eh);
- } else {
+ case detail::type::double_type:
+ if (detail::const_check(FMT_USE_DOUBLE))
+ detail::parse_float_type_spec(specs_, eh);
+ else
FMT_ASSERT(false, "double support disabled");
- }
break;
- case internal::type::long_double_type:
- if (internal::const_check(FMT_USE_LONG_DOUBLE)) {
- internal::parse_float_type_spec(specs_, eh);
- } else {
+ case detail::type::long_double_type:
+ if (detail::const_check(FMT_USE_LONG_DOUBLE))
+ detail::parse_float_type_spec(specs_, eh);
+ else
FMT_ASSERT(false, "long double support disabled");
- }
break;
- case internal::type::cstring_type:
- internal::handle_cstring_type_spec(
- specs_.type, internal::cstring_type_checker<decltype(eh)>(eh));
+ case detail::type::cstring_type:
+ detail::handle_cstring_type_spec(
+ specs_.type, detail::cstring_type_checker<decltype(eh)>(eh));
break;
- case internal::type::string_type:
- internal::check_string_type_spec(specs_.type, eh);
+ case detail::type::string_type:
+ detail::check_string_type_spec(specs_.type, eh);
break;
- case internal::type::pointer_type:
- internal::check_pointer_type_spec(specs_.type, eh);
+ case detail::type::pointer_type:
+ detail::check_pointer_type_spec(specs_.type, eh);
break;
- case internal::type::custom_type:
+ case detail::type::custom_type:
// Custom format specifiers should be checked in parse functions of
// formatter specializations.
break;
template <typename FormatContext>
auto format(const T& val, FormatContext& ctx) -> decltype(ctx.out()) {
- internal::handle_dynamic_spec<internal::width_checker>(
- specs_.width, specs_.width_ref, ctx);
- internal::handle_dynamic_spec<internal::precision_checker>(
+ detail::handle_dynamic_spec<detail::width_checker>(specs_.width,
+ specs_.width_ref, ctx);
+ detail::handle_dynamic_spec<detail::precision_checker>(
specs_.precision, specs_.precision_ref, ctx);
- using range_type =
- internal::output_range<typename FormatContext::iterator,
- typename FormatContext::char_type>;
- return visit_format_arg(arg_formatter<range_type>(ctx, nullptr, &specs_),
- internal::make_arg<FormatContext>(val));
+ using af = detail::arg_formatter<typename FormatContext::iterator,
+ typename FormatContext::char_type>;
+ return visit_format_arg(af(ctx, nullptr, &specs_),
+ detail::make_arg<FormatContext>(val));
}
private:
- internal::dynamic_format_specs<Char> specs_;
+ detail::dynamic_format_specs<Char> specs_;
};
#define FMT_FORMAT_AS(Type, Base) \
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(internal::std_string_view<Char>, basic_string_view<Char>);
+FMT_FORMAT_AS(detail::std_string_view<Char>, basic_string_view<Char>);
template <typename Char>
struct formatter<void*, Char> : formatter<const void*, Char> {
// };
template <typename Char = char> class dynamic_formatter {
private:
- struct null_handler : internal::error_handler {
+ struct null_handler : detail::error_handler {
void on_align(align_t) {}
void on_plus() {}
void on_minus() {}
auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
format_str_ = ctx.begin();
// Checks are deferred to formatting time when the argument type is known.
- internal::dynamic_specs_handler<ParseContext> handler(specs_, ctx);
+ detail::dynamic_specs_handler<ParseContext> handler(specs_, ctx);
return parse_format_specs(ctx.begin(), ctx.end(), handler);
}
template <typename T, typename FormatContext>
auto format(const T& val, FormatContext& ctx) -> decltype(ctx.out()) {
handle_specs(ctx);
- internal::specs_checker<null_handler> checker(
- null_handler(),
- internal::mapped_type_constant<T, FormatContext>::value);
+ detail::specs_checker<null_handler> checker(
+ null_handler(), detail::mapped_type_constant<T, FormatContext>::value);
checker.on_align(specs_.align);
switch (specs_.sign) {
case sign::none:
}
if (specs_.alt) checker.on_hash();
if (specs_.precision >= 0) checker.end_precision();
- using range = internal::output_range<typename FormatContext::iterator,
- typename FormatContext::char_type>;
- visit_format_arg(arg_formatter<range>(ctx, nullptr, &specs_),
- internal::make_arg<FormatContext>(val));
+ using af = detail::arg_formatter<typename FormatContext::iterator,
+ typename FormatContext::char_type>;
+ visit_format_arg(af(ctx, nullptr, &specs_),
+ detail::make_arg<FormatContext>(val));
return ctx.out();
}
private:
template <typename Context> void handle_specs(Context& ctx) {
- internal::handle_dynamic_spec<internal::width_checker>(
- specs_.width, specs_.width_ref, ctx);
- internal::handle_dynamic_spec<internal::precision_checker>(
+ detail::handle_dynamic_spec<detail::width_checker>(specs_.width,
+ specs_.width_ref, ctx);
+ detail::handle_dynamic_spec<detail::precision_checker>(
specs_.precision, specs_.precision_ref, ctx);
}
- internal::dynamic_format_specs<Char> specs_;
+ detail::dynamic_format_specs<Char> specs_;
const Char* format_str_;
};
-template <typename Range, typename Char>
-typename basic_format_context<Range, Char>::format_arg
-basic_format_context<Range, Char>::arg(basic_string_view<char_type> name) {
- map_.init(args_);
- format_arg arg = map_.find(name);
- if (arg.type() == internal::type::none_type)
- this->on_error("argument not found");
- return arg;
-}
-
template <typename Char, typename ErrorHandler>
FMT_CONSTEXPR void advance_to(
basic_format_parse_context<Char, ErrorHandler>& ctx, const Char* p) {
ctx.advance_to(ctx.begin() + (p - &*ctx.begin()));
}
-template <typename ArgFormatter, typename Char, typename Context>
-struct format_handler : internal::error_handler {
- using range = typename ArgFormatter::range;
-
- format_handler(range r, basic_string_view<Char> str,
- basic_format_args<Context> format_args,
- internal::locale_ref loc)
- : parse_context(str), context(r.begin(), format_args, loc) {}
-
- void on_text(const Char* begin, const Char* end) {
- auto size = internal::to_unsigned(end - begin);
- auto out = context.out();
- auto&& it = internal::reserve(out, size);
- it = std::copy_n(begin, size, it);
- context.advance_to(out);
- }
-
- void get_arg(int id) { arg = internal::get_arg(context, id); }
-
- void on_arg_id() { get_arg(parse_context.next_arg_id()); }
- void on_arg_id(int id) {
- parse_context.check_arg_id(id);
- get_arg(id);
- }
- void on_arg_id(basic_string_view<Char> id) { arg = context.arg(id); }
-
- void on_replacement_field(const Char* p) {
- advance_to(parse_context, p);
- context.advance_to(
- visit_format_arg(ArgFormatter(context, &parse_context), arg));
- }
-
- const Char* on_format_specs(const Char* begin, const Char* end) {
- advance_to(parse_context, begin);
- internal::custom_formatter<Context> f(parse_context, context);
- if (visit_format_arg(f, arg)) return parse_context.begin();
- basic_format_specs<Char> specs;
- using internal::specs_handler;
- using parse_context_t = basic_format_parse_context<Char>;
- internal::specs_checker<specs_handler<parse_context_t, Context>> handler(
- specs_handler<parse_context_t, Context>(specs, parse_context, context),
- arg.type());
- begin = parse_format_specs(begin, end, handler);
- if (begin == end || *begin != '}') on_error("missing '}' in format string");
- advance_to(parse_context, begin);
- context.advance_to(
- visit_format_arg(ArgFormatter(context, &parse_context, &specs), arg));
- return begin;
- }
-
- basic_format_parse_context<Char> parse_context;
- Context context;
- basic_format_arg<Context> arg;
-};
-
/** Formats arguments and writes the output to the range. */
template <typename ArgFormatter, typename Char, typename Context>
typename Context::iterator vformat_to(
- typename ArgFormatter::range out, basic_string_view<Char> format_str,
+ typename ArgFormatter::iterator out, basic_string_view<Char> format_str,
basic_format_args<Context> args,
- internal::locale_ref loc = internal::locale_ref()) {
- format_handler<ArgFormatter, Char, Context> h(out, format_str, args, loc);
- internal::parse_format_string<false>(format_str, h);
+ detail::locale_ref loc = detail::locale_ref()) {
+ if (format_str.size() == 2 && detail::equal2(format_str.data(), "{}")) {
+ auto arg = args.get(0);
+ if (!arg) detail::error_handler().on_error("argument not found");
+ using iterator = typename ArgFormatter::iterator;
+ return visit_format_arg(
+ detail::default_arg_formatter<iterator, Char>{out, args, loc}, arg);
+ }
+ detail::format_handler<ArgFormatter, Char, Context> h(out, format_str, args,
+ loc);
+ detail::parse_format_string<false>(format_str, h);
return h.context.out();
}
template <> struct formatter<bytes> {
template <typename ParseContext>
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
- using handler_type = internal::dynamic_specs_handler<ParseContext>;
- internal::specs_checker<handler_type> handler(handler_type(specs_, ctx),
- internal::type::string_type);
+ using handler_type = detail::dynamic_specs_handler<ParseContext>;
+ detail::specs_checker<handler_type> handler(handler_type(specs_, ctx),
+ detail::type::string_type);
auto it = parse_format_specs(ctx.begin(), ctx.end(), handler);
- internal::check_string_type_spec(specs_.type, ctx.error_handler());
+ detail::check_string_type_spec(specs_.type, ctx.error_handler());
return it;
}
template <typename FormatContext>
auto format(bytes b, FormatContext& ctx) -> decltype(ctx.out()) {
- internal::handle_dynamic_spec<internal::width_checker>(
- specs_.width, specs_.width_ref, ctx);
- internal::handle_dynamic_spec<internal::precision_checker>(
+ detail::handle_dynamic_spec<detail::width_checker>(specs_.width,
+ specs_.width_ref, ctx);
+ detail::handle_dynamic_spec<detail::precision_checker>(
specs_.precision, specs_.precision_ref, ctx);
- using range_type =
- internal::output_range<typename FormatContext::iterator, char>;
- internal::basic_writer<range_type> writer(range_type(ctx.out()));
- writer.write_bytes(b.data_, specs_);
- return writer.out();
+ return detail::write_bytes(ctx.out(), b.data_, specs_);
}
private:
- internal::dynamic_format_specs<char> specs_;
+ detail::dynamic_format_specs<char> specs_;
};
-template <typename It, typename Char> struct arg_join : internal::view {
+template <typename It, typename Sentinel, typename Char>
+struct arg_join : detail::view {
It begin;
- It end;
+ Sentinel end;
basic_string_view<Char> sep;
- arg_join(It b, It e, basic_string_view<Char> s) : begin(b), end(e), sep(s) {}
+ arg_join(It b, Sentinel e, basic_string_view<Char> s)
+ : begin(b), end(e), sep(s) {}
};
-template <typename It, typename Char>
-struct formatter<arg_join<It, Char>, Char>
+template <typename It, typename Sentinel, typename Char>
+struct formatter<arg_join<It, Sentinel, Char>, Char>
: formatter<typename std::iterator_traits<It>::value_type, Char> {
template <typename FormatContext>
- auto format(const arg_join<It, Char>& value, FormatContext& ctx)
+ auto format(const arg_join<It, Sentinel, Char>& value, FormatContext& ctx)
-> decltype(ctx.out()) {
using base = formatter<typename std::iterator_traits<It>::value_type, Char>;
auto it = value.begin;
Returns an object that formats the iterator range `[begin, end)` with elements
separated by `sep`.
*/
-template <typename It>
-arg_join<It, char> join(It begin, It end, string_view sep) {
+template <typename It, typename Sentinel>
+arg_join<It, Sentinel, char> join(It begin, Sentinel end, string_view sep) {
return {begin, end, sep};
}
-template <typename It>
-arg_join<It, wchar_t> join(It begin, It end, wstring_view sep) {
+template <typename It, typename Sentinel>
+arg_join<It, Sentinel, wchar_t> join(It begin, Sentinel end, wstring_view sep) {
return {begin, end, sep};
}
\endrst
*/
template <typename Range>
-arg_join<internal::iterator_t<const Range>, char> join(const Range& range,
- string_view sep) {
+arg_join<detail::iterator_t<const Range>, detail::sentinel_t<const Range>, char>
+join(const Range& range, string_view sep) {
return join(std::begin(range), std::end(range), sep);
}
template <typename Range>
-arg_join<internal::iterator_t<const Range>, wchar_t> join(const Range& range,
- wstring_view sep) {
+arg_join<detail::iterator_t<const Range>, detail::sentinel_t<const Range>,
+ wchar_t>
+join(const Range& range, wstring_view sep) {
return join(std::begin(range), std::end(range), sep);
}
std::string answer = fmt::to_string(42);
\endrst
*/
-template <typename T> inline std::string to_string(const T& value) {
- return format("{}", value);
+template <typename T, FMT_ENABLE_IF(!std::is_integral<T>::value)>
+inline std::string to_string(const T& value) {
+ std::string result;
+ detail::write<char>(std::back_inserter(result), value);
+ return result;
+}
+
+template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
+inline std::string to_string(T value) {
+ // The buffer should be large enough to store the number including the sign or
+ // "false" for bool.
+ constexpr int max_size = detail::digits10<T>() + 2;
+ char buffer[max_size > 5 ? max_size : 5];
+ char* begin = buffer;
+ return std::string(begin, detail::write<char>(begin, value));
}
/**
return format(L"{}", value);
}
-template <typename Char, std::size_t SIZE>
+template <typename Char, size_t SIZE>
std::basic_string<Char> to_string(const basic_memory_buffer<Char, SIZE>& buf) {
- return std::basic_string<Char>(buf.data(), buf.size());
+ auto size = buf.size();
+ detail::assume(size < std::basic_string<Char>().max_size());
+ return std::basic_string<Char>(buf.data(), size);
}
template <typename Char>
-typename buffer_context<Char>::iterator internal::vformat_to(
- internal::buffer<Char>& buf, basic_string_view<Char> format_str,
+typename buffer_context<Char>::iterator detail::vformat_to(
+ detail::buffer<Char>& buf, basic_string_view<Char> format_str,
basic_format_args<buffer_context<type_identity_t<Char>>> args) {
- using range = buffer_range<Char>;
- return vformat_to<arg_formatter<range>>(buf, to_string_view(format_str),
- args);
-}
+ using af = arg_formatter<typename buffer_context<Char>::iterator, Char>;
+ return vformat_to<af>(std::back_inserter(buf), to_string_view(format_str),
+ args);
+}
+
+#ifndef FMT_HEADER_ONLY
+extern template format_context::iterator detail::vformat_to(
+ detail::buffer<char>&, string_view, basic_format_args<format_context>);
+namespace detail {
+extern template FMT_API std::string grouping_impl<char>(locale_ref loc);
+extern template FMT_API std::string grouping_impl<wchar_t>(locale_ref loc);
+extern template FMT_API char thousands_sep_impl<char>(locale_ref loc);
+extern template FMT_API wchar_t thousands_sep_impl<wchar_t>(locale_ref loc);
+extern template FMT_API char decimal_point_impl(locale_ref loc);
+extern template FMT_API wchar_t decimal_point_impl(locale_ref loc);
+extern template int format_float<double>(double value, int precision,
+ float_specs specs, buffer<char>& buf);
+extern template int format_float<long double>(long double value, int precision,
+ float_specs specs,
+ buffer<char>& buf);
+int snprintf_float(float value, int precision, float_specs specs,
+ buffer<char>& buf) = delete;
+extern template int snprintf_float<double>(double value, int precision,
+ float_specs specs,
+ buffer<char>& buf);
+extern template int snprintf_float<long double>(long double value,
+ int precision,
+ float_specs specs,
+ buffer<char>& buf);
+} // namespace detail
+#endif
template <typename S, typename Char = char_t<S>,
- FMT_ENABLE_IF(internal::is_string<S>::value)>
-inline typename buffer_context<Char>::iterator vformat_to(
- internal::buffer<Char>& buf, const S& format_str,
- basic_format_args<buffer_context<type_identity_t<Char>>> args) {
- return internal::vformat_to(buf, to_string_view(format_str), args);
+ FMT_ENABLE_IF(detail::is_string<S>::value)>
+inline typename FMT_BUFFER_CONTEXT(Char)::iterator vformat_to(
+ detail::buffer<Char>& buf, const S& format_str,
+ basic_format_args<FMT_BUFFER_CONTEXT(type_identity_t<Char>)> args) {
+ return detail::vformat_to(buf, to_string_view(format_str), args);
}
-template <typename S, typename... Args, std::size_t SIZE = inline_buffer_size,
- typename Char = enable_if_t<internal::is_string<S>::value, char_t<S>>>
+template <typename S, typename... Args, size_t SIZE = inline_buffer_size,
+ typename Char = enable_if_t<detail::is_string<S>::value, char_t<S>>>
inline typename buffer_context<Char>::iterator format_to(
basic_memory_buffer<Char, SIZE>& buf, const S& format_str, Args&&... args) {
- internal::check_format_string<Args...>(format_str);
+ detail::check_format_string<Args...>(format_str);
using context = buffer_context<Char>;
- return internal::vformat_to(buf, to_string_view(format_str),
- make_format_args<context>(args...));
+ return detail::vformat_to(buf, to_string_view(format_str),
+ make_format_args<context>(args...));
}
template <typename OutputIt, typename Char = char>
template <typename OutputIt, typename Char = char>
using format_args_t = basic_format_args<format_context_t<OutputIt, Char>>;
-template <typename S, typename OutputIt, typename... Args,
- FMT_ENABLE_IF(
- internal::is_output_iterator<OutputIt>::value &&
- !internal::is_contiguous_back_insert_iterator<OutputIt>::value)>
+template <
+ typename S, typename OutputIt, typename... Args,
+ FMT_ENABLE_IF(detail::is_output_iterator<OutputIt>::value &&
+ !detail::is_contiguous_back_insert_iterator<OutputIt>::value)>
inline OutputIt vformat_to(
OutputIt out, const S& format_str,
format_args_t<type_identity_t<OutputIt>, char_t<S>> args) {
- using range = internal::output_range<OutputIt, char_t<S>>;
- return vformat_to<arg_formatter<range>>(range(out),
- to_string_view(format_str), args);
+ using af = detail::arg_formatter<OutputIt, char_t<S>>;
+ return vformat_to<af>(out, to_string_view(format_str), args);
}
/**
*/
template <typename OutputIt, typename S, typename... Args,
FMT_ENABLE_IF(
- internal::is_output_iterator<OutputIt>::value &&
- !internal::is_contiguous_back_insert_iterator<OutputIt>::value &&
- internal::is_string<S>::value)>
+ detail::is_output_iterator<OutputIt>::value &&
+ !detail::is_contiguous_back_insert_iterator<OutputIt>::value &&
+ detail::is_string<S>::value)>
inline OutputIt format_to(OutputIt out, const S& format_str, Args&&... args) {
- internal::check_format_string<Args...>(format_str);
+ detail::check_format_string<Args...>(format_str);
using context = format_context_t<OutputIt, char_t<S>>;
return vformat_to(out, to_string_view(format_str),
make_format_args<context>(args...));
/** Iterator past the end of the output range. */
OutputIt out;
/** Total (not truncated) output size. */
- std::size_t size;
+ size_t size;
};
template <typename OutputIt, typename Char = typename OutputIt::value_type>
using format_to_n_context =
- format_context_t<internal::truncating_iterator<OutputIt>, Char>;
+ format_context_t<detail::truncating_iterator<OutputIt>, Char>;
template <typename OutputIt, typename Char = typename OutputIt::value_type>
using format_to_n_args = basic_format_args<format_to_n_context<OutputIt, Char>>;
}
template <typename OutputIt, typename Char, typename... Args,
- FMT_ENABLE_IF(internal::is_output_iterator<OutputIt>::value)>
+ FMT_ENABLE_IF(detail::is_output_iterator<OutputIt>::value)>
inline format_to_n_result<OutputIt> vformat_to_n(
- OutputIt out, std::size_t n, basic_string_view<Char> format_str,
+ OutputIt out, size_t n, basic_string_view<Char> format_str,
format_to_n_args<type_identity_t<OutputIt>, type_identity_t<Char>> args) {
- auto it = vformat_to(internal::truncating_iterator<OutputIt>(out, n),
+ auto it = vformat_to(detail::truncating_iterator<OutputIt>(out, n),
format_str, args);
return {it.base(), it.count()};
}
\endrst
*/
template <typename OutputIt, typename S, typename... Args,
- FMT_ENABLE_IF(internal::is_string<S>::value&&
- internal::is_output_iterator<OutputIt>::value)>
-inline format_to_n_result<OutputIt> format_to_n(OutputIt out, std::size_t n,
+ FMT_ENABLE_IF(detail::is_string<S>::value&&
+ detail::is_output_iterator<OutputIt>::value)>
+inline format_to_n_result<OutputIt> format_to_n(OutputIt out, size_t n,
const S& format_str,
const Args&... args) {
- internal::check_format_string<Args...>(format_str);
+ detail::check_format_string<Args...>(format_str);
using context = format_to_n_context<OutputIt, char_t<S>>;
return vformat_to_n(out, n, to_string_view(format_str),
make_format_args<context>(args...));
}
-template <typename Char>
-inline std::basic_string<Char> internal::vformat(
+template <typename Char, enable_if_t<(!std::is_same<Char, char>::value), int>>
+std::basic_string<Char> detail::vformat(
basic_string_view<Char> format_str,
basic_format_args<buffer_context<type_identity_t<Char>>> args) {
basic_memory_buffer<Char> buffer;
- internal::vformat_to(buffer, format_str, args);
+ detail::vformat_to(buffer, format_str, args);
return to_string(buffer);
}
``format(format_str, args...)``.
*/
template <typename... Args>
-inline std::size_t formatted_size(string_view format_str, const Args&... args) {
- return format_to(internal::counting_iterator(), format_str, args...).count();
+inline size_t formatted_size(string_view format_str, const Args&... args) {
+ return format_to(detail::counting_iterator(), format_str, args...).count();
}
template <typename Char, FMT_ENABLE_IF(std::is_same<Char, wchar_t>::value)>
void vprint(std::FILE* f, basic_string_view<Char> format_str,
wformat_args args) {
wmemory_buffer buffer;
- internal::vformat_to(buffer, format_str, args);
+ detail::vformat_to(buffer, format_str, args);
buffer.push_back(L'\0');
if (std::fputws(buffer.data(), f) == -1)
FMT_THROW(system_error(errno, "cannot write to file"));
}
#if FMT_USE_USER_DEFINED_LITERALS
-namespace internal {
+namespace detail {
# if FMT_USE_UDL_TEMPLATE
template <typename Char, Char... CHARS> class udl_formatter {
public:
template <typename... Args>
std::basic_string<Char> operator()(Args&&... args) const {
- FMT_CONSTEXPR_DECL Char s[] = {CHARS..., '\0'};
- FMT_CONSTEXPR_DECL bool invalid_format =
- do_check_format_string<Char, error_handler, remove_cvref_t<Args>...>(
- basic_string_view<Char>(s, sizeof...(CHARS)));
- (void)invalid_format;
+ static FMT_CONSTEXPR_DECL Char s[] = {CHARS..., '\0'};
+ check_format_string<remove_cvref_t<Args>...>(FMT_STRING(s));
return format(s, std::forward<Args>(args)...);
}
};
# endif // FMT_USE_UDL_TEMPLATE
template <typename Char> struct udl_arg {
- basic_string_view<Char> str;
+ const Char* str;
- template <typename T> named_arg<T, Char> operator=(T&& value) const {
+ template <typename T> named_arg<Char, T> operator=(T&& value) const {
return {str, std::forward<T>(value)};
}
};
-
-// Converts string literals to basic_string_view.
-template <typename Char, size_t N>
-FMT_CONSTEXPR basic_string_view<Char> compile_string_to_view(
- const Char (&s)[N]) {
- // Remove trailing null character if needed. Won't be present if this is used
- // with raw character array (i.e. not defined as a string).
- return {s,
- N - ((std::char_traits<Char>::to_int_type(s[N - 1]) == 0) ? 1 : 0)};
-}
-
-// Converts string_view to basic_string_view.
-template <typename Char>
-FMT_CONSTEXPR basic_string_view<Char> compile_string_to_view(
- const std_string_view<Char>& s) {
- return {s.data(), s.size()};
-}
-} // namespace internal
+} // namespace detail
inline namespace literals {
# if FMT_USE_UDL_TEMPLATE
# pragma GCC diagnostic push
+# pragma GCC diagnostic ignored "-Wpedantic"
# if FMT_CLANG_VERSION
# pragma GCC diagnostic ignored "-Wgnu-string-literal-operator-template"
# endif
template <typename Char, Char... CHARS>
-FMT_CONSTEXPR internal::udl_formatter<Char, CHARS...> operator""_format() {
+FMT_CONSTEXPR detail::udl_formatter<Char, CHARS...> operator""_format() {
return {};
}
# pragma GCC diagnostic pop
std::string message = "The answer is {}"_format(42);
\endrst
*/
-FMT_CONSTEXPR internal::udl_formatter<char> operator"" _format(const char* s,
- std::size_t n) {
+FMT_CONSTEXPR detail::udl_formatter<char> operator"" _format(const char* s,
+ size_t n) {
return {{s, n}};
}
-FMT_CONSTEXPR internal::udl_formatter<wchar_t> operator"" _format(
- const wchar_t* s, std::size_t n) {
+FMT_CONSTEXPR detail::udl_formatter<wchar_t> operator"" _format(
+ const wchar_t* s, size_t n) {
return {{s, n}};
}
# endif // FMT_USE_UDL_TEMPLATE
fmt::print("Elapsed time: {s:.2f} seconds", "s"_a=1.23);
\endrst
*/
-FMT_CONSTEXPR internal::udl_arg<char> operator"" _a(const char* s,
- std::size_t n) {
- return {{s, n}};
+FMT_CONSTEXPR detail::udl_arg<char> operator"" _a(const char* s, size_t) {
+ return {s};
}
-FMT_CONSTEXPR internal::udl_arg<wchar_t> operator"" _a(const wchar_t* s,
- std::size_t n) {
- return {{s, n}};
+FMT_CONSTEXPR detail::udl_arg<wchar_t> operator"" _a(const wchar_t* s, size_t) {
+ return {s};
}
} // namespace literals
#endif // FMT_USE_USER_DEFINED_LITERALS
FMT_END_NAMESPACE
-#define FMT_STRING_IMPL(s, ...) \
- [] { \
- /* Use a macro-like name to avoid shadowing warnings. */ \
- struct FMT_COMPILE_STRING : fmt::compile_string { \
- using char_type = fmt::remove_cvref_t<decltype(s[0])>; \
- FMT_MAYBE_UNUSED __VA_ARGS__ FMT_CONSTEXPR \
- operator fmt::basic_string_view<char_type>() const { \
- return fmt::internal::compile_string_to_view<char_type>(s); \
- } \
- }; \
- return FMT_COMPILE_STRING(); \
- }()
-
-/**
- \rst
- Constructs a compile-time format string from a string literal *s*.
-
- **Example**::
-
- // A compile-time error because 'd' is an invalid specifier for strings.
- std::string s = format(FMT_STRING("{:d}"), "foo");
- \endrst
- */
-#define FMT_STRING(s) FMT_STRING_IMPL(s, )
-
-#if defined(FMT_STRING_ALIAS) && FMT_STRING_ALIAS
-# define fmt(s) FMT_STRING_IMPL(s, [[deprecated]])
-#endif
-
#ifdef FMT_HEADER_ONLY
# define FMT_FUNC inline
# include "format-inl.h"