src/third_party/fmt/*.h and src/third_party/format.cpp
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-This is a subset of https://fmt.dev[fmt] 6.1.2 with the following license:
+This is a subset of https://fmt.dev[fmt] 6.2.0 with the following license:
-------------------------------------------------------------------------------
Formatting library for C++
#include <cstdio> // std::FILE
#include <cstring>
+#include <functional>
#include <iterator>
+#include <memory>
#include <string>
#include <type_traits>
+#include <vector>
// The fmt library version in the form major * 10000 + minor * 100 + patch.
-#define FMT_VERSION 60102
+#define FMT_VERSION 60200
#ifdef __has_feature
# define FMT_HAS_FEATURE(x) __has_feature(x)
# define FMT_HAS_CPP_ATTRIBUTE(x) 0
#endif
+#define FMT_HAS_CPP14_ATTRIBUTE(attribute) \
+ (__cplusplus >= 201402L && FMT_HAS_CPP_ATTRIBUTE(attribute))
+
+#define FMT_HAS_CPP17_ATTRIBUTE(attribute) \
+ (__cplusplus >= 201703L && FMT_HAS_CPP_ATTRIBUTE(attribute))
+
+#ifdef __clang__
+# define FMT_CLANG_VERSION (__clang_major__ * 100 + __clang_minor__)
+#else
+# define FMT_CLANG_VERSION 0
+#endif
+
#if defined(__GNUC__) && !defined(__clang__)
# define FMT_GCC_VERSION (__GNUC__ * 100 + __GNUC_MINOR__)
#else
# endif
#endif
-// [[noreturn]] is disabled on MSVC because of bogus unreachable code warnings.
-#if FMT_EXCEPTIONS && FMT_HAS_CPP_ATTRIBUTE(noreturn) && !FMT_MSC_VER
+// [[noreturn]] is disabled on MSVC and NVCC because of bogus unreachable code
+// warnings.
+#if FMT_EXCEPTIONS && FMT_HAS_CPP_ATTRIBUTE(noreturn) && !FMT_MSC_VER && \
+ !FMT_NVCC
# define FMT_NORETURN [[noreturn]]
#else
# define FMT_NORETURN
#endif
+#ifndef FMT_MAYBE_UNUSED
+# if FMT_HAS_CPP17_ATTRIBUTE(maybe_unused)
+# define FMT_MAYBE_UNUSED [[maybe_unused]]
+# else
+# define FMT_MAYBE_UNUSED
+# endif
+#endif
+
#ifndef FMT_DEPRECATED
-# if (FMT_HAS_CPP_ATTRIBUTE(deprecated) && __cplusplus >= 201402L) || \
- FMT_MSC_VER >= 1900
+# if FMT_HAS_CPP14_ATTRIBUTE(deprecated) || FMT_MSC_VER >= 1900
# define FMT_DEPRECATED [[deprecated]]
# else
# if defined(__GNUC__) || defined(__clang__)
# endif
#endif
-// Workaround broken [[deprecated]] in the Intel compiler and NVCC.
-#if defined(__INTEL_COMPILER) || FMT_NVCC
+// Workaround broken [[deprecated]] in the Intel, PGI and NVCC compilers.
+#if defined(__INTEL_COMPILER) || defined(__PGI) || FMT_NVCC
# define FMT_DEPRECATED_ALIAS
#else
# define FMT_DEPRECATED_ALIAS FMT_DEPRECATED
#endif
#if !defined(FMT_HEADER_ONLY) && defined(_WIN32)
+# if FMT_MSC_VER
+# define FMT_NO_W4275 __pragma(warning(suppress : 4275))
+# else
+# define FMT_NO_W4275
+# endif
+# define FMT_CLASS_API FMT_NO_W4275
# ifdef FMT_EXPORT
# define FMT_API __declspec(dllexport)
# elif defined(FMT_SHARED)
# define FMT_EXTERN_TEMPLATE_API FMT_API
# endif
#endif
+#ifndef FMT_CLASS_API
+# define FMT_CLASS_API
+#endif
#ifndef FMT_API
-# define FMT_API
+# if FMT_GCC_VERSION || FMT_CLANG_VERSION
+# define FMT_API __attribute__((visibility("default")))
+# define FMT_EXTERN_TEMPLATE_API FMT_API
+# define FMT_INSTANTIATION_DEF_API
+# else
+# define FMT_API
+# endif
#endif
#ifndef FMT_EXTERN_TEMPLATE_API
# define FMT_EXTERN_TEMPLATE_API
#endif
+#ifndef FMT_INSTANTIATION_DEF_API
+# define FMT_INSTANTIATION_DEF_API FMT_API
+#endif
#ifndef FMT_HEADER_ONLY
# define FMT_EXTERN extern
# define FMT_USE_EXPERIMENTAL_STRING_VIEW
#endif
+#ifndef FMT_UNICODE
+# define FMT_UNICODE !FMT_MSC_VER
+#endif
+#if FMT_UNICODE && FMT_MSC_VER
+# pragma execution_character_set("utf-8")
+#endif
+
FMT_BEGIN_NAMESPACE
-// Implementations of enable_if_t and other types for pre-C++14 systems.
+// Implementations of enable_if_t and other metafunctions for older systems.
template <bool B, class T = void>
using enable_if_t = typename std::enable_if<B, T>::type;
template <bool B, class T, class F>
using remove_const_t = typename std::remove_const<T>::type;
template <typename T>
using remove_cvref_t = typename std::remove_cv<remove_reference_t<T>>::type;
+template <typename T> struct type_identity { using type = T; };
+template <typename T> using type_identity_t = typename type_identity<T>::type;
struct monostate {};
namespace internal {
+// A helper function to suppress bogus "conditional expression is constant"
+// warnings.
+template <typename T> FMT_CONSTEXPR T const_check(T value) { return value; }
+
// A workaround for gcc 4.8 to make void_t work in a SFINAE context.
template <typename... Ts> struct void_t_impl { using type = void; };
-FMT_API void assert_fail(const char* file, int line, const char* message);
+FMT_NORETURN FMT_API void assert_fail(const char* file, int line,
+ const char* message);
#ifndef FMT_ASSERT
# ifdef NDEBUG
-# define FMT_ASSERT(condition, message)
+// FMT_ASSERT is not empty to avoid -Werror=empty-body.
+# define FMT_ASSERT(condition, message) ((void)0)
# else
-# define FMT_ASSERT(condition, message) \
- ((condition) \
- ? void() \
- : fmt::internal::assert_fail(__FILE__, __LINE__, (message)))
+# define FMT_ASSERT(condition, message) \
+ ((condition) /* void() fails with -Winvalid-constexpr on clang 4.0.1 */ \
+ ? (void)0 \
+ : ::fmt::internal::assert_fail(__FILE__, __LINE__, (message)))
# endif
#endif
#ifdef FMT_USE_INT128
// Do nothing.
-#elif defined(__SIZEOF_INT128__)
+#elif defined(__SIZEOF_INT128__) && !FMT_NVCC
# define FMT_USE_INT128 1
using int128_t = __int128_t;
using uint128_t = __uint128_t;
FMT_ASSERT(value >= 0, "negative value");
return static_cast<typename std::make_unsigned<Int>::type>(value);
}
+
+constexpr unsigned char micro[] = "\u00B5";
+
+template <typename Char> constexpr bool is_unicode() {
+ return FMT_UNICODE || sizeof(Char) != 1 ||
+ (sizeof(micro) == 3 && micro[0] == 0xC2 && micro[1] == 0xB5);
+}
+
+#ifdef __cpp_char8_t
+using char8_type = char8_t;
+#else
+enum char8_type : unsigned char {};
+#endif
} // namespace internal
template <typename... Ts>
size_t size_;
public:
- using char_type = Char;
+ using char_type FMT_DEPRECATED_ALIAS = Char;
+ using value_type = Char;
using iterator = const Char*;
FMT_CONSTEXPR basic_string_view() FMT_NOEXCEPT : data_(nullptr), size_(0) {}
the size with ``std::char_traits<Char>::length``.
\endrst
*/
+#if __cplusplus >= 201703L // C++17's char_traits::length() is constexpr.
+ FMT_CONSTEXPR
+#endif
basic_string_view(const Char* s)
: data_(s), size_(std::char_traits<Char>::length(s)) {}
using wstring_view = basic_string_view<wchar_t>;
#ifndef __cpp_char8_t
-// A UTF-8 code unit type.
-enum char8_t : unsigned char {};
+// char8_t is deprecated; use char instead.
+using char8_t FMT_DEPRECATED_ALIAS = internal::char8_type;
#endif
/** Specifies if ``T`` is a character type. Can be specialized by users. */
template <typename T> struct is_char : std::false_type {};
template <> struct is_char<char> : std::true_type {};
template <> struct is_char<wchar_t> : std::true_type {};
-template <> struct is_char<char8_t> : std::true_type {};
+template <> struct is_char<internal::char8_type> : std::true_type {};
template <> struct is_char<char16_t> : std::true_type {};
template <> struct is_char<char32_t> : std::true_type {};
template <typename S, typename = void> struct char_t_impl {};
template <typename S> struct char_t_impl<S, enable_if_t<is_string<S>::value>> {
using result = decltype(to_string_view(std::declval<S>()));
- using type = typename result::char_type;
+ using type = typename result::value_type;
};
struct error_handler {
T* begin() FMT_NOEXCEPT { return ptr_; }
T* end() FMT_NOEXCEPT { return ptr_ + size_; }
+ const T* begin() const FMT_NOEXCEPT { return ptr_; }
+ const T* end() const FMT_NOEXCEPT { return ptr_ + size_; }
+
/** Returns the size of this buffer. */
std::size_t size() const FMT_NOEXCEPT { return size_; }
/** Appends data to the end of the buffer. */
template <typename U> void append(const U* begin, const U* end);
- T& operator[](std::size_t index) { return ptr_[index]; }
- const T& operator[](std::size_t index) const { return ptr_[index]; }
+ template <typename I> T& operator[](I index) { return ptr_[index]; }
+ template <typename I> const T& operator[](I index) const {
+ return ptr_[index];
+ }
};
// A container-backed buffer.
template <typename Char> struct named_arg_base;
template <typename T, typename Char> struct named_arg;
-enum type {
+enum class type {
none_type,
named_arg_type,
// Integer types should go first,
// Maps core type T to the corresponding type enum constant.
template <typename T, typename Char>
-struct type_constant : std::integral_constant<type, custom_type> {};
+struct type_constant : std::integral_constant<type, type::custom_type> {};
#define FMT_TYPE_CONSTANT(Type, constant) \
template <typename Char> \
- struct type_constant<Type, Char> : std::integral_constant<type, constant> {}
+ struct type_constant<Type, Char> \
+ : std::integral_constant<type, type::constant> {}
FMT_TYPE_CONSTANT(const named_arg_base<Char>&, named_arg_type);
FMT_TYPE_CONSTANT(int, int_type);
FMT_TYPE_CONSTANT(const void*, pointer_type);
FMT_CONSTEXPR bool is_integral_type(type t) {
- FMT_ASSERT(t != named_arg_type, "invalid argument type");
- return t > none_type && t <= last_integer_type;
+ FMT_ASSERT(t != type::named_arg_type, "invalid argument type");
+ return t > type::none_type && t <= type::last_integer_type;
}
FMT_CONSTEXPR bool is_arithmetic_type(type t) {
- FMT_ASSERT(t != named_arg_type, "invalid argument type");
- return t > none_type && t <= last_numeric_type;
+ FMT_ASSERT(t != type::named_arg_type, "invalid argument type");
+ return t > type::none_type && t <= type::last_numeric_type;
}
template <typename Char> struct string_value {
template <typename T,
FMT_ENABLE_IF(
std::is_constructible<basic_string_view<char_type>, T>::value &&
- !is_string<T>::value)>
+ !is_string<T>::value && !has_formatter<T, Context>::value &&
+ !has_fallback_formatter<T, Context>::value)>
FMT_CONSTEXPR basic_string_view<char_type> map(const T& val) {
return basic_string_view<char_type>(val);
}
FMT_ENABLE_IF(
std::is_constructible<std_string_view<char_type>, T>::value &&
!std::is_constructible<basic_string_view<char_type>, T>::value &&
- !is_string<T>::value && !has_formatter<T, Context>::value)>
+ !is_string<T>::value && !has_formatter<T, Context>::value &&
+ !has_fallback_formatter<T, Context>::value)>
FMT_CONSTEXPR basic_string_view<char_type> map(const T& val) {
return std_string_view<char_type>(val);
}
FMT_ENABLE_IF(std::is_enum<T>::value &&
!has_formatter<T, Context>::value &&
!has_fallback_formatter<T, Context>::value)>
- FMT_CONSTEXPR auto map(const T& val) -> decltype(
- map(static_cast<typename std::underlying_type<T>::type>(val))) {
+ FMT_CONSTEXPR auto map(const T& val)
+ -> decltype(std::declval<arg_mapper>().map(
+ static_cast<typename std::underlying_type<T>::type>(val))) {
return map(static_cast<typename std::underlying_type<T>::type>(val));
}
- template <
- typename T,
- FMT_ENABLE_IF(
- !is_string<T>::value && !is_char<T>::value &&
- !std::is_constructible<basic_string_view<char_type>, T>::value &&
- (has_formatter<T, Context>::value ||
- (has_fallback_formatter<T, Context>::value &&
- !std::is_constructible<std_string_view<char_type>, T>::value)))>
+ template <typename T,
+ FMT_ENABLE_IF(!is_string<T>::value && !is_char<T>::value &&
+ (has_formatter<T, Context>::value ||
+ has_fallback_formatter<T, Context>::value))>
FMT_CONSTEXPR const T& map(const T& val) {
return val;
}
std::memcpy(val.data, &arg, sizeof(arg));
return val;
}
+
+ int map(...) {
+ constexpr bool formattable = sizeof(Context) == 0;
+ static_assert(
+ formattable,
+ "Cannot format argument. To make type T formattable provide a "
+ "formatter<T> specialization: "
+ "https://fmt.dev/latest/api.html#formatting-user-defined-types");
+ return 0;
+ }
};
// A type constant after applying arg_mapper<Context>.
internal::custom_value<Context> custom_;
};
- FMT_CONSTEXPR basic_format_arg() : type_(internal::none_type) {}
+ FMT_CONSTEXPR basic_format_arg() : type_(internal::type::none_type) {}
FMT_CONSTEXPR explicit operator bool() const FMT_NOEXCEPT {
- return type_ != internal::none_type;
+ return type_ != internal::type::none_type;
}
internal::type type() const { return type_; }
-> decltype(vis(0)) {
using char_type = typename Context::char_type;
switch (arg.type_) {
- case internal::none_type:
+ case internal::type::none_type:
break;
- case internal::named_arg_type:
+ case internal::type::named_arg_type:
FMT_ASSERT(false, "invalid argument type");
break;
- case internal::int_type:
+ case internal::type::int_type:
return vis(arg.value_.int_value);
- case internal::uint_type:
+ case internal::type::uint_type:
return vis(arg.value_.uint_value);
- case internal::long_long_type:
+ case internal::type::long_long_type:
return vis(arg.value_.long_long_value);
- case internal::ulong_long_type:
+ case internal::type::ulong_long_type:
return vis(arg.value_.ulong_long_value);
#if FMT_USE_INT128
- case internal::int128_type:
+ case internal::type::int128_type:
return vis(arg.value_.int128_value);
- case internal::uint128_type:
+ case internal::type::uint128_type:
return vis(arg.value_.uint128_value);
#else
- case internal::int128_type:
- case internal::uint128_type:
+ case internal::type::int128_type:
+ case internal::type::uint128_type:
break;
#endif
- case internal::bool_type:
+ case internal::type::bool_type:
return vis(arg.value_.bool_value);
- case internal::char_type:
+ case internal::type::char_type:
return vis(arg.value_.char_value);
- case internal::float_type:
+ case internal::type::float_type:
return vis(arg.value_.float_value);
- case internal::double_type:
+ case internal::type::double_type:
return vis(arg.value_.double_value);
- case internal::long_double_type:
+ case internal::type::long_double_type:
return vis(arg.value_.long_double_value);
- case internal::cstring_type:
+ case internal::type::cstring_type:
return vis(arg.value_.string.data);
- case internal::string_type:
+ case internal::type::string_type:
return vis(basic_string_view<char_type>(arg.value_.string.data,
arg.value_.string.size));
- case internal::pointer_type:
+ case internal::type::pointer_type:
return vis(arg.value_.pointer);
- case internal::custom_type:
+ case internal::type::custom_type:
return vis(typename basic_format_arg<Context>::handle(arg.value_.custom));
}
return vis(monostate());
template <typename Context, typename Arg, typename... Args>
constexpr unsigned long long encode_types() {
- return mapped_type_constant<Arg, Context>::value |
+ return static_cast<unsigned>(mapped_type_constant<Arg, Context>::value) |
(encode_types<Context, Args...>() << packed_arg_bits);
}
inline basic_format_arg<Context> make_arg(const T& value) {
return make_arg<Context>(value);
}
+
+template <typename T> struct is_reference_wrapper : std::false_type {};
+
+template <typename T>
+struct is_reference_wrapper<std::reference_wrapper<T>> : std::true_type {};
+
+class dynamic_arg_list {
+ // Workaround for clang's -Wweak-vtables. Unlike for regular classes, for
+ // templates it doesn't complain about inability to deduce single translation
+ // unit for placing vtable. So storage_node_base is made a fake template.
+ template <typename = void> struct node {
+ virtual ~node() = default;
+ std::unique_ptr<node<>> next;
+ };
+
+ template <typename T> struct typed_node : node<> {
+ T value;
+
+ template <typename Arg>
+ FMT_CONSTEXPR typed_node(const Arg& arg) : value(arg) {}
+
+ template <typename Char>
+ FMT_CONSTEXPR typed_node(const basic_string_view<Char>& arg)
+ : value(arg.data(), arg.size()) {}
+ };
+
+ std::unique_ptr<node<>> head_;
+
+ public:
+ template <typename T, typename Arg> const T& push(const Arg& arg) {
+ auto node = std::unique_ptr<typed_node<T>>(new typed_node<T>(arg));
+ auto& value = node->value;
+ node->next = std::move(head_);
+ head_ = std::move(node);
+ return value;
+ }
+};
} // namespace internal
// Formatting context.
such as `~fmt::vformat`.
\endrst
*/
-template <typename Context, typename... Args> class format_arg_store {
+template <typename Context, typename... Args>
+class format_arg_store
+#if FMT_GCC_VERSION && FMT_GCC_VERSION < 409
+ // Workaround a GCC template argument substitution bug.
+ : public basic_format_args<Context>
+#endif
+{
private:
static const size_t num_args = sizeof...(Args);
static const bool is_packed = num_args < internal::max_packed_args;
: internal::is_unpacked_bit | num_args;
format_arg_store(const Args&... args)
- : data_{internal::make_arg<is_packed, Context>(args)...} {}
+ :
+#if FMT_GCC_VERSION && FMT_GCC_VERSION < 409
+ basic_format_args<Context>(*this),
+#endif
+ data_{internal::make_arg<is_packed, Context>(args)...} {
+ }
};
/**
return {args...};
}
-/** Formatting arguments. */
+/**
+ \rst
+ A dynamic version of `fmt::format_arg_store<>`.
+ It's equipped with a storage to potentially temporary objects which lifetime
+ could be shorter than the format arguments object.
+
+ It can be implicitly converted into `~fmt::basic_format_args` for passing
+ into type-erased formatting functions such as `~fmt::vformat`.
+ \endrst
+ */
+template <typename Context>
+class dynamic_format_arg_store
+#if FMT_GCC_VERSION && FMT_GCC_VERSION < 409
+ // Workaround a GCC template argument substitution bug.
+ : public basic_format_args<Context>
+#endif
+{
+ private:
+ using char_type = typename Context::char_type;
+
+ template <typename T> struct need_copy {
+ static constexpr internal::type mapped_type =
+ internal::mapped_type_constant<T, Context>::value;
+
+ enum {
+ value = !(internal::is_reference_wrapper<T>::value ||
+ std::is_same<T, basic_string_view<char_type>>::value ||
+ std::is_same<T, internal::std_string_view<char_type>>::value ||
+ (mapped_type != internal::type::cstring_type &&
+ mapped_type != internal::type::string_type &&
+ mapped_type != internal::type::custom_type &&
+ mapped_type != internal::type::named_arg_type))
+ };
+ };
+
+ template <typename T>
+ using stored_type = conditional_t<internal::is_string<T>::value,
+ std::basic_string<char_type>, T>;
+
+ // Storage of basic_format_arg must be contiguous.
+ std::vector<basic_format_arg<Context>> data_;
+
+ // Storage of arguments not fitting into basic_format_arg must grow
+ // without relocation because items in data_ refer to it.
+ internal::dynamic_arg_list dynamic_args_;
+
+ friend class basic_format_args<Context>;
+
+ unsigned long long get_types() const {
+ return internal::is_unpacked_bit | data_.size();
+ }
+
+ template <typename T> void emplace_arg(const T& arg) {
+ data_.emplace_back(internal::make_arg<Context>(arg));
+ }
+
+ public:
+ /**
+ \rst
+ Adds an argument into the dynamic store for later passing to a formating
+ function.
+
+ Note that custom types and string types (but not string views!) are copied
+ into the store with dynamic memory (in addition to resizing vector).
+
+ **Example**::
+
+ fmt::dynamic_format_arg_store<fmt::format_context> store;
+ store.push_back(42);
+ store.push_back("abc");
+ store.push_back(1.5f);
+ std::string result = fmt::vformat("{} and {} and {}", store);
+ \endrst
+ */
+ template <typename T> void push_back(const T& arg) {
+ static_assert(
+ !std::is_base_of<internal::named_arg_base<char_type>, T>::value,
+ "named arguments are not supported yet");
+ if (internal::const_check(need_copy<T>::value))
+ emplace_arg(dynamic_args_.push<stored_type<T>>(arg));
+ else
+ emplace_arg(arg);
+ }
+
+ /**
+ Adds a reference to the argument into the dynamic store for later passing to
+ a formating function.
+ */
+ template <typename T> void push_back(std::reference_wrapper<T> arg) {
+ static_assert(
+ need_copy<T>::value,
+ "objects of built-in types and string views are always copied");
+ emplace_arg(arg.get());
+ }
+};
+
+/**
+ \rst
+ A view of a collection of formatting arguments. To avoid lifetime issues it
+ should only be used as a parameter type in type-erased functions such as
+ ``vformat``::
+
+ void vlog(string_view format_str, format_args args); // OK
+ format_args args = make_format_args(42); // Error: dangling reference
+ \endrst
+ */
template <typename Context> class basic_format_args {
public:
using size_type = int;
}
if (index > internal::max_packed_args) return arg;
arg.type_ = type(index);
- if (arg.type_ == internal::none_type) return arg;
+ if (arg.type_ == internal::type::none_type) return arg;
internal::value<Context>& val = arg.value_;
val = values_[index];
return arg;
set_data(store.data_);
}
+ /**
+ \rst
+ Constructs a `basic_format_args` object from
+ `~fmt::dynamic_format_arg_store`.
+ \endrst
+ */
+ basic_format_args(const dynamic_format_arg_store<Context>& store)
+ : types_(store.get_types()) {
+ set_data(store.data_.data());
+ }
+
/**
\rst
Constructs a `basic_format_args` object from a dynamic set of arguments.
/** Returns the argument at specified index. */
format_arg get(int index) const {
format_arg arg = do_get(index);
- if (arg.type_ == internal::named_arg_type)
+ if (arg.type_ == internal::type::named_arg_type)
arg = arg.value_.named_arg->template deserialize<Context>();
return arg;
}
struct format_args : basic_format_args<format_context> {
template <typename... Args>
format_args(Args&&... args)
- : basic_format_args<format_context>(std::forward<Args>(args)...) {}
+ : basic_format_args<format_context>(static_cast<Args&&>(args)...) {}
};
struct wformat_args : basic_format_args<wformat_context> {
template <typename... Args>
wformat_args(Args&&... args)
- : basic_format_args<wformat_context>(std::forward<Args>(args)...) {}
+ : basic_format_args<wformat_context>(static_cast<Args&&>(args)...) {}
};
template <typename Container> struct is_contiguous : std::false_type {};
}
};
-template <typename T, typename Char> struct named_arg : named_arg_base<Char> {
+struct view {};
+
+template <typename T, typename Char>
+struct named_arg : view, named_arg_base<Char> {
const T& value;
named_arg(basic_string_view<Char> name, const T& val)
template <typename..., typename S, FMT_ENABLE_IF(is_compile_string<S>::value)>
void check_format_string(S);
-struct view {};
template <bool...> struct bool_pack;
template <bool... Args>
using all_true =
inline format_arg_store<buffer_context<Char>, remove_reference_t<Args>...>
make_args_checked(const S& format_str,
const remove_reference_t<Args>&... args) {
- static_assert(all_true<(!std::is_base_of<view, remove_reference_t<Args>>() ||
- !std::is_reference<Args>())...>::value,
- "passing views as lvalues is disallowed");
- check_format_string<remove_const_t<remove_reference_t<Args>>...>(format_str);
+ static_assert(
+ all_true<(!std::is_base_of<view, remove_reference_t<Args>>::value ||
+ !std::is_reference<Args>::value)...>::value,
+ "passing views as lvalues is disallowed");
+ check_format_string<Args...>(format_str);
return {args...};
}
template <typename Char>
-std::basic_string<Char> vformat(basic_string_view<Char> format_str,
- basic_format_args<buffer_context<Char>> args);
+std::basic_string<Char> vformat(
+ basic_string_view<Char> format_str,
+ basic_format_args<buffer_context<type_identity_t<Char>>> args);
template <typename Char>
typename buffer_context<Char>::iterator vformat_to(
buffer<Char>& buf, basic_string_view<Char> format_str,
- basic_format_args<buffer_context<Char>> args);
+ basic_format_args<buffer_context<type_identity_t<Char>>> args);
+
+template <typename Char, typename Args,
+ FMT_ENABLE_IF(!std::is_same<Char, char>::value)>
+inline void vprint_mojibake(std::FILE*, basic_string_view<Char>, const Args&) {}
+
+FMT_API void vprint_mojibake(std::FILE*, string_view, format_args);
+#ifndef _WIN32
+inline void vprint_mojibake(std::FILE*, string_view, format_args) {}
+#endif
} // namespace internal
/**
\rst
- Returns a named argument to be used in a formatting function.
-
- The named argument holds a reference and does not extend the lifetime
- of its arguments.
- Consequently, a dangling reference can accidentally be created.
- The user should take care to only pass this function temporaries when
- the named argument is itself a temporary, as per the following example.
+ Returns a named argument to be used in a formatting function. It should only
+ be used in a call to a formatting function.
**Example**::
template <typename OutputIt, typename S, typename Char = char_t<S>,
FMT_ENABLE_IF(
internal::is_contiguous_back_insert_iterator<OutputIt>::value)>
-OutputIt vformat_to(OutputIt out, const S& format_str,
- basic_format_args<buffer_context<Char>> args) {
+OutputIt vformat_to(
+ OutputIt out, const S& format_str,
+ basic_format_args<buffer_context<type_identity_t<Char>>> args) {
using container = remove_reference_t<decltype(internal::get_container(out))>;
internal::container_buffer<container> buf((internal::get_container(out)));
internal::vformat_to(buf, to_string_view(format_str), args);
inline std::back_insert_iterator<Container> format_to(
std::back_insert_iterator<Container> out, const S& format_str,
Args&&... args) {
- return vformat_to(
- out, to_string_view(format_str),
- {internal::make_args_checked<Args...>(format_str, args...)});
+ return vformat_to(out, to_string_view(format_str),
+ internal::make_args_checked<Args...>(format_str, args...));
}
template <typename S, typename Char = char_t<S>>
inline std::basic_string<Char> vformat(
- const S& format_str, basic_format_args<buffer_context<Char>> args) {
+ const S& format_str,
+ basic_format_args<buffer_context<type_identity_t<Char>>> args) {
return internal::vformat(to_string_view(format_str), args);
}
inline std::basic_string<Char> format(const S& format_str, Args&&... args) {
return internal::vformat(
to_string_view(format_str),
- {internal::make_args_checked<Args...>(format_str, args...)});
+ internal::make_args_checked<Args...>(format_str, args...));
}
-FMT_API void vprint(std::FILE* f, string_view format_str, format_args args);
-FMT_API void vprint(string_view format_str, format_args args);
+FMT_API void vprint(string_view, format_args);
+FMT_API void vprint(std::FILE*, string_view, format_args);
/**
\rst
- Prints formatted data to the file *f*. For wide format strings,
- *f* should be in wide-oriented mode set via ``fwide(f, 1)`` or
- ``_setmode(_fileno(f), _O_U8TEXT)`` on Windows.
+ Formats ``args`` according to specifications in ``format_str`` and writes the
+ output to the file ``f``. Strings are assumed to be Unicode-encoded unless the
+ ``FMT_UNICODE`` macro is set to 0.
**Example**::
fmt::print(stderr, "Don't {}!", "panic");
\endrst
*/
-template <typename S, typename... Args,
- FMT_ENABLE_IF(internal::is_string<S>::value)>
+template <typename S, typename... Args, typename Char = char_t<S>>
inline void print(std::FILE* f, const S& format_str, Args&&... args) {
- vprint(f, to_string_view(format_str),
- internal::make_args_checked<Args...>(format_str, args...));
+ return internal::is_unicode<Char>()
+ ? vprint(f, to_string_view(format_str),
+ internal::make_args_checked<Args...>(format_str, args...))
+ : internal::vprint_mojibake(
+ f, to_string_view(format_str),
+ internal::make_args_checked<Args...>(format_str, args...));
}
/**
\rst
- Prints formatted data to ``stdout``.
+ Formats ``args`` according to specifications in ``format_str`` and writes
+ the output to ``stdout``. Strings are assumed to be Unicode-encoded unless
+ the ``FMT_UNICODE`` macro is set to 0.
**Example**::
fmt::print("Elapsed time: {0:.2f} seconds", 1.23);
\endrst
*/
-template <typename S, typename... Args,
- FMT_ENABLE_IF(internal::is_string<S>::value)>
+template <typename S, typename... Args, typename Char = char_t<S>>
inline void print(const S& format_str, Args&&... args) {
- vprint(to_string_view(format_str),
- internal::make_args_checked<Args...>(format_str, args...));
+ return internal::is_unicode<Char>()
+ ? vprint(to_string_view(format_str),
+ internal::make_args_checked<Args...>(format_str, args...))
+ : internal::vprint_mojibake(
+ stdout, to_string_view(format_str),
+ internal::make_args_checked<Args...>(format_str, args...));
}
FMT_END_NAMESPACE
#ifndef FMT_FORMAT_INL_H_
#define FMT_FORMAT_INL_H_
-#include "format.h"
-
#include <cassert>
#include <cctype>
#include <climits>
#include <cstdarg>
#include <cstring> // for std::memmove
#include <cwchar>
+
+#include "format.h"
#if !defined(FMT_STATIC_THOUSANDS_SEPARATOR)
# include <locale>
#endif
-#if FMT_USE_WINDOWS_H
-# if !defined(FMT_HEADER_ONLY) && !defined(WIN32_LEAN_AND_MEAN)
-# define WIN32_LEAN_AND_MEAN
-# endif
-# if defined(NOMINMAX) || defined(FMT_WIN_MINMAX)
-# include <windows.h>
-# else
-# define NOMINMAX
-# include <windows.h>
-# undef NOMINMAX
-# endif
-#endif
-
-#if FMT_EXCEPTIONS
-# define FMT_TRY try
-# define FMT_CATCH(x) catch (x)
-#else
-# define FMT_TRY if (true)
-# define FMT_CATCH(x) if (false)
+#ifdef _WIN32
+# include <io.h>
+# include <windows.h>
#endif
#ifdef _MSC_VER
# define FMT_SNPRINTF fmt_snprintf
#endif // _MSC_VER
-using format_func = void (*)(internal::buffer<char>&, int, string_view);
-
// A portable thread-safe version of strerror.
// Sets buffer to point to a string describing the error code.
// This can be either a pointer to a string stored in buffer,
}
// Handle the result of GNU-specific version of strerror_r.
+ FMT_MAYBE_UNUSED
int handle(char* message) {
// If the buffer is full then the message is probably truncated.
if (message == buffer_ && strlen(buffer_) == buffer_size_ - 1)
}
// Handle the case when strerror_r is not available.
+ FMT_MAYBE_UNUSED
int handle(internal::null<>) {
return fallback(strerror_s(buffer_, buffer_size_, error_code_));
}
// Fallback to strerror_s when strerror_r is not available.
+ FMT_MAYBE_UNUSED
int fallback(int result) {
// If the buffer is full then the message is probably truncated.
return result == 0 && strlen(buffer_) == buffer_size_ - 1 ? ERANGE
assert(out.size() <= inline_buffer_size);
}
-// A wrapper around fwrite that throws on error.
-FMT_FUNC void fwrite_fully(const void* ptr, size_t size, size_t count,
- FILE* stream) {
- size_t written = std::fwrite(ptr, size, count, stream);
- if (written < count) {
- FMT_THROW(system_error(errno, "cannot write to file"));
- }
-}
-
FMT_FUNC void report_error(format_func func, int error_code,
string_view message) FMT_NOEXCEPT {
memory_buffer full_message;
(void)std::fwrite(full_message.data(), full_message.size(), 1, stderr);
std::fputc('\n', stderr);
}
+
+// A wrapper around fwrite that throws on error.
+FMT_FUNC void fwrite_fully(const void* ptr, size_t size, size_t count,
+ FILE* stream) {
+ size_t written = std::fwrite(ptr, size, count, stream);
+ if (written < count) FMT_THROW(system_error(errno, "cannot write to file"));
+}
} // namespace internal
#if !defined(FMT_STATIC_THOUSANDS_SEPARATOR)
private:
using significand_type = uint64_t;
+ public:
+ significand_type f;
+ int e;
+
// All sizes are in bits.
// Subtract 1 to account for an implicit most significant bit in the
// normalized form.
std::numeric_limits<double>::digits - 1;
static FMT_CONSTEXPR_DECL const uint64_t implicit_bit =
1ULL << double_significand_size;
-
- public:
- significand_type f;
- int e;
-
static FMT_CONSTEXPR_DECL const int significand_size =
bits<significand_type>::value;
// errors on platforms where double is not IEEE754.
template <typename Double> explicit fp(Double d) { assign(d); }
- // Normalizes the value converted from double and multiplied by (1 << SHIFT).
- template <int SHIFT> friend fp normalize(fp value) {
- // Handle subnormals.
- const auto shifted_implicit_bit = fp::implicit_bit << SHIFT;
- while ((value.f & shifted_implicit_bit) == 0) {
- value.f <<= 1;
- --value.e;
- }
- // Subtract 1 to account for hidden bit.
- const auto offset =
- fp::significand_size - fp::double_significand_size - SHIFT - 1;
- value.f <<= offset;
- value.e -= offset;
- return value;
- }
-
// Assigns d to this and return true iff predecessor is closer than successor.
template <typename Double, FMT_ENABLE_IF(sizeof(Double) == sizeof(uint64_t))>
bool assign(Double d) {
const int exponent_bias = (1 << exponent_size) - limits::max_exponent - 1;
auto u = bit_cast<uint64_t>(d);
f = u & significand_mask;
- auto biased_e = (u & exponent_mask) >> double_significand_size;
+ int biased_e =
+ static_cast<int>((u & exponent_mask) >> double_significand_size);
// Predecessor is closer if d is a normalized power of 2 (f == 0) other than
// the smallest normalized number (biased_e > 1).
bool is_predecessor_closer = f == 0 && biased_e > 1;
f += implicit_bit;
else
biased_e = 1; // Subnormals use biased exponent 1 (min exponent).
- e = static_cast<int>(biased_e - exponent_bias - double_significand_size);
+ e = biased_e - exponent_bias - double_significand_size;
return is_predecessor_closer;
}
}
};
+// Normalizes the value converted from double and multiplied by (1 << SHIFT).
+template <int SHIFT> fp normalize(fp value) {
+ // Handle subnormals.
+ const auto shifted_implicit_bit = fp::implicit_bit << SHIFT;
+ while ((value.f & shifted_implicit_bit) == 0) {
+ value.f <<= 1;
+ --value.e;
+ }
+ // Subtract 1 to account for hidden bit.
+ const auto offset =
+ fp::significand_size - fp::double_significand_size - SHIFT - 1;
+ value.f <<= offset;
+ value.e -= offset;
+ return value;
+}
+
inline bool operator==(fp x, fp y) { return x.f == y.f && x.e == y.e; }
// Computes lhs * rhs / pow(2, 64) rounded to nearest with half-up tie breaking.
// Returns a cached power of 10 `c_k = c_k.f * pow(2, c_k.e)` such that its
// (binary) exponent satisfies `min_exponent <= c_k.e <= min_exponent + 28`.
-FMT_FUNC fp get_cached_power(int min_exponent, int& pow10_exponent) {
- const uint64_t one_over_log2_10 = 0x4d104d42; // round(pow(2, 32) / log2(10))
+inline fp get_cached_power(int min_exponent, int& pow10_exponent) {
+ const int64_t one_over_log2_10 = 0x4d104d42; // round(pow(2, 32) / log2(10))
int index = static_cast<int>(
- static_cast<int64_t>(
- (min_exponent + fp::significand_size - 1) * one_over_log2_10 +
- ((uint64_t(1) << 32) - 1) // ceil
- ) >>
- 32 // arithmetic shift
+ ((min_exponent + fp::significand_size - 1) * one_over_log2_10 +
+ ((int64_t(1) << 32) - 1)) // ceil
+ >> 32 // arithmetic shift
);
// Decimal exponent of the first (smallest) cached power of 10.
const int first_dec_exp = -348;
basic_memory_buffer<bigit, bigits_capacity> bigits_;
int exp_;
+ bigit operator[](int index) const { return bigits_[to_unsigned(index)]; }
+ bigit& operator[](int index) { return bigits_[to_unsigned(index)]; }
+
static FMT_CONSTEXPR_DECL const int bigit_bits = bits<bigit>::value;
friend struct formatter<bigint>;
void subtract_bigits(int index, bigit other, bigit& borrow) {
- auto result = static_cast<double_bigit>(bigits_[index]) - other - borrow;
- bigits_[index] = static_cast<bigit>(result);
+ auto result = static_cast<double_bigit>((*this)[index]) - other - borrow;
+ (*this)[index] = static_cast<bigit>(result);
borrow = static_cast<bigit>(result >> (bigit_bits * 2 - 1));
}
void remove_leading_zeros() {
int num_bigits = static_cast<int>(bigits_.size()) - 1;
- while (num_bigits > 0 && bigits_[num_bigits] == 0) --num_bigits;
- bigits_.resize(num_bigits + 1);
+ while (num_bigits > 0 && (*this)[num_bigits] == 0) --num_bigits;
+ bigits_.resize(to_unsigned(num_bigits + 1));
}
// Computes *this -= other assuming aligned bigints and *this >= other.
FMT_ASSERT(compare(*this, other) >= 0, "");
bigit borrow = 0;
int i = other.exp_ - exp_;
- for (int j = 0, n = static_cast<int>(other.bigits_.size()); j != n;
- ++i, ++j) {
+ for (size_t j = 0, n = other.bigits_.size(); j != n; ++i, ++j) {
subtract_bigits(i, other.bigits_[j], borrow);
}
while (borrow > 0) subtract_bigits(i, 0, borrow);
}
void assign(uint64_t n) {
- int num_bigits = 0;
+ size_t num_bigits = 0;
do {
bigits_[num_bigits++] = n & ~bigit(0);
n >>= bigit_bits;
int end = i - j;
if (end < 0) end = 0;
for (; i >= end; --i, --j) {
- bigit lhs_bigit = lhs.bigits_[i], rhs_bigit = rhs.bigits_[j];
+ bigit lhs_bigit = lhs[i], rhs_bigit = rhs[j];
if (lhs_bigit != rhs_bigit) return lhs_bigit > rhs_bigit ? 1 : -1;
}
if (i != j) return i > j ? 1 : -1;
if (max_lhs_bigits + 1 < num_rhs_bigits) return -1;
if (max_lhs_bigits > num_rhs_bigits) return 1;
auto get_bigit = [](const bigint& n, int i) -> bigit {
- return i >= n.exp_ && i < n.num_bigits() ? n.bigits_[i - n.exp_] : 0;
+ return i >= n.exp_ && i < n.num_bigits() ? n[i - n.exp_] : 0;
};
double_bigit borrow = 0;
int min_exp = (std::min)((std::min)(lhs1.exp_, lhs2.exp_), rhs.exp_);
basic_memory_buffer<bigit, bigits_capacity> n(std::move(bigits_));
int num_bigits = static_cast<int>(bigits_.size());
int num_result_bigits = 2 * num_bigits;
- bigits_.resize(num_result_bigits);
+ bigits_.resize(to_unsigned(num_result_bigits));
using accumulator_t = conditional_t<FMT_USE_INT128, uint128_t, accumulator>;
auto sum = accumulator_t();
for (int bigit_index = 0; bigit_index < num_bigits; ++bigit_index) {
// Most terms are multiplied twice which can be optimized in the future.
sum += static_cast<double_bigit>(n[i]) * n[j];
}
- bigits_[bigit_index] = static_cast<bigit>(sum);
+ (*this)[bigit_index] = static_cast<bigit>(sum);
sum >>= bits<bigit>::value; // Compute the carry.
}
// Do the same for the top half.
++bigit_index) {
for (int j = num_bigits - 1, i = bigit_index - j; i < num_bigits;)
sum += static_cast<double_bigit>(n[i++]) * n[j--];
- bigits_[bigit_index] = static_cast<bigit>(sum);
+ (*this)[bigit_index] = static_cast<bigit>(sum);
sum >>= bits<bigit>::value;
}
--num_result_bigits;
FMT_ASSERT(this != &divisor, "");
if (compare(*this, divisor) < 0) return 0;
int num_bigits = static_cast<int>(bigits_.size());
- FMT_ASSERT(divisor.bigits_[divisor.bigits_.size() - 1] != 0, "");
+ FMT_ASSERT(divisor.bigits_[divisor.bigits_.size() - 1u] != 0, "");
int exp_difference = exp_ - divisor.exp_;
if (exp_difference > 0) {
// Align bigints by adding trailing zeros to simplify subtraction.
- bigits_.resize(num_bigits + exp_difference);
+ bigits_.resize(to_unsigned(num_bigits + exp_difference));
for (int i = num_bigits - 1, j = i + exp_difference; i >= 0; --i, --j)
bigits_[j] = bigits_[i];
std::uninitialized_fill_n(bigits_.data(), exp_difference, 0);
}
};
-enum round_direction { unknown, up, down };
+enum class round_direction { unknown, up, down };
// Given the divisor (normally a power of 10), the remainder = v % divisor for
// some number v and the error, returns whether v should be rounded up, down, or
FMT_ASSERT(error < divisor - error, ""); // error * 2 won't overflow.
// Round down if (remainder + error) * 2 <= divisor.
if (remainder <= divisor - remainder && error * 2 <= divisor - remainder * 2)
- return down;
+ return round_direction::down;
// Round up if (remainder - error) * 2 >= divisor.
if (remainder >= error &&
remainder - error >= divisor - (remainder - error)) {
- return up;
+ return round_direction::up;
}
- return unknown;
+ return round_direction::unknown;
}
namespace digits {
};
}
+// A version of count_digits optimized for grisu_gen_digits.
+inline int grisu_count_digits(uint32_t n) {
+ if (n < 10) return 1;
+ if (n < 100) return 2;
+ if (n < 1000) return 3;
+ if (n < 10000) return 4;
+ if (n < 100000) return 5;
+ if (n < 1000000) return 6;
+ if (n < 10000000) return 7;
+ if (n < 100000000) return 8;
+ if (n < 1000000000) return 9;
+ return 10;
+}
+
// Generates output using the Grisu digit-gen algorithm.
// error: the size of the region (lower, upper) outside of which numbers
// definitely do not round to value (Delta in Grisu3).
FMT_ASSERT(integral == value.f >> -one.e, "");
// The fractional part of scaled value (p2 in Grisu) c = value % one.
uint64_t fractional = value.f & (one.f - 1);
- exp = count_digits(integral); // kappa in Grisu.
+ exp = grisu_count_digits(integral); // kappa in Grisu.
// Divide by 10 to prevent overflow.
auto result = handler.on_start(data::powers_of_10_64[exp - 1] << -one.e,
value.f / 10, error * 10, exp);
if (precision > 0) return digits::more;
if (precision < 0) return digits::done;
auto dir = get_round_direction(divisor, remainder, error);
- if (dir == unknown) return digits::error;
- buf[size++] = dir == up ? '1' : '0';
+ if (dir == round_direction::unknown) return digits::error;
+ buf[size++] = dir == round_direction::up ? '1' : '0';
return digits::done;
}
FMT_ASSERT(error == 1 && divisor > 2, "");
}
auto dir = get_round_direction(divisor, remainder, error);
- if (dir != up) return dir == down ? digits::done : digits::error;
+ if (dir != round_direction::up)
+ return dir == round_direction::down ? digits::done : digits::error;
++buf[size - 1];
for (int i = size - 1; i > 0 && buf[i] > '9'; --i) {
buf[i] = '0';
if (result > 0 || (result == 0 && (digit % 2) != 0))
++data[num_digits - 1];
}
- buf.resize(num_digits);
+ buf.resize(to_unsigned(num_digits));
exp10 -= num_digits - 1;
return;
}
// if T is a IEEE754 binary32 or binary64 and snprintf otherwise.
template <typename T>
int format_float(T value, int precision, float_specs specs, buffer<char>& buf) {
- static_assert(!std::is_same<T, float>(), "");
+ static_assert(!std::is_same<T, float>::value, "");
FMT_ASSERT(value >= 0, "value is negative");
const bool fixed = specs.format == float_format::fixed;
int exp = 0;
const int min_exp = -60; // alpha in Grisu.
int cached_exp10 = 0; // K in Grisu.
- if (precision != -1) {
- if (precision > 17) return snprintf_float(value, precision, specs, buf);
- fp normalized = normalize(fp(value));
- const auto cached_pow = get_cached_power(
- min_exp - (normalized.e + fp::significand_size), cached_exp10);
- normalized = normalized * cached_pow;
- fixed_handler handler{buf.data(), 0, precision, -cached_exp10, fixed};
- if (grisu_gen_digits(normalized, 1, exp, handler) == digits::error)
- return snprintf_float(value, precision, specs, buf);
- int num_digits = handler.size;
- if (!fixed) {
- // Remove trailing zeros.
- while (num_digits > 0 && buf[num_digits - 1] == '0') {
- --num_digits;
- ++exp;
- }
- }
- buf.resize(to_unsigned(num_digits));
- } else {
+ if (precision < 0) {
fp fp_value;
auto boundaries = specs.binary32
? fp_value.assign_float_with_boundaries(value)
return exp;
}
buf.resize(to_unsigned(handler.size));
+ } else {
+ if (precision > 17) return snprintf_float(value, precision, specs, buf);
+ fp normalized = normalize(fp(value));
+ const auto cached_pow = get_cached_power(
+ min_exp - (normalized.e + fp::significand_size), cached_exp10);
+ normalized = normalized * cached_pow;
+ fixed_handler handler{buf.data(), 0, precision, -cached_exp10, fixed};
+ if (grisu_gen_digits(normalized, 1, exp, handler) == digits::error)
+ return snprintf_float(value, precision, specs, buf);
+ int num_digits = handler.size;
+ if (!fixed) {
+ // Remove trailing zeros.
+ while (num_digits > 0 && buf[num_digits - 1] == '0') {
+ --num_digits;
+ ++exp;
+ }
+ }
+ buf.resize(to_unsigned(num_digits));
}
return exp - cached_exp10;
}
buffer<char>& buf) {
// Buffer capacity must be non-zero, otherwise MSVC's vsnprintf_s will fail.
FMT_ASSERT(buf.capacity() > buf.size(), "empty buffer");
- static_assert(!std::is_same<T, float>(), "");
+ static_assert(!std::is_same<T, float>::value, "");
// Subtract 1 to account for the difference in precision since we use %e for
// both general and exponent format.
char format[max_format_size];
char* format_ptr = format;
*format_ptr++ = '%';
- if (specs.trailing_zeros) *format_ptr++ = '#';
+ if (specs.showpoint && specs.format == float_format::hex) *format_ptr++ = '#';
if (precision >= 0) {
*format_ptr++ = '.';
*format_ptr++ = '*';
"fuzz mode - avoid large allocation inside snprintf");
#endif
// Suppress the warning about a nonliteral format string.
- auto snprintf_ptr = FMT_SNPRINTF;
+ // Cannot use auto becase of a bug in MinGW (#1532).
+ int (*snprintf_ptr)(char*, size_t, const char*, ...) = FMT_SNPRINTF;
int result = precision >= 0
? snprintf_ptr(begin, capacity, format, precision, value)
: snprintf_ptr(begin, capacity, format, value);
buf.reserve(buf.capacity() + 1); // The buffer will grow exponentially.
continue;
}
- unsigned size = to_unsigned(result);
+ auto size = to_unsigned(result);
// Size equal to capacity means that the last character was truncated.
if (size >= capacity) {
buf.reserve(size + offset + 1); // Add 1 for the terminating '\0'.
--p;
} while (is_digit(*p));
int fraction_size = static_cast<int>(end - p - 1);
- std::memmove(p, p + 1, fraction_size);
+ std::memmove(p, p + 1, to_unsigned(fraction_size));
buf.resize(size - 1);
return -fraction_size;
}
while (*fraction_end == '0') --fraction_end;
// Move the fractional part left to get rid of the decimal point.
fraction_size = static_cast<int>(fraction_end - begin - 1);
- std::memmove(begin + 1, begin + 2, fraction_size);
+ std::memmove(begin + 1, begin + 2, to_unsigned(fraction_size));
}
- buf.resize(fraction_size + offset + 1);
+ buf.resize(to_unsigned(fraction_size) + offset + 1);
return exp - fraction_size;
}
}
+
+// A public domain branchless UTF-8 decoder by Christopher Wellons:
+// https://github.com/skeeto/branchless-utf8
+/* Decode the next character, c, from buf, reporting errors in e.
+ *
+ * Since this is a branchless decoder, four bytes will be read from the
+ * buffer regardless of the actual length of the next character. This
+ * means the buffer _must_ have at least three bytes of zero padding
+ * following the end of the data stream.
+ *
+ * Errors are reported in e, which will be non-zero if the parsed
+ * character was somehow invalid: invalid byte sequence, non-canonical
+ * encoding, or a surrogate half.
+ *
+ * The function returns a pointer to the next character. When an error
+ * occurs, this pointer will be a guess that depends on the particular
+ * error, but it will always advance at least one byte.
+ */
+FMT_FUNC const char* utf8_decode(const char* buf, uint32_t* c, int* e) {
+ static const char lengths[] = {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0,
+ 0, 0, 2, 2, 2, 2, 3, 3, 4, 0};
+ static const int masks[] = {0x00, 0x7f, 0x1f, 0x0f, 0x07};
+ static const uint32_t mins[] = {4194304, 0, 128, 2048, 65536};
+ static const int shiftc[] = {0, 18, 12, 6, 0};
+ static const int shifte[] = {0, 6, 4, 2, 0};
+
+ auto s = reinterpret_cast<const unsigned char*>(buf);
+ int len = lengths[s[0] >> 3];
+
+ // Compute the pointer to the next character early so that the next
+ // iteration can start working on the next character. Neither Clang
+ // nor GCC figure out this reordering on their own.
+ const char* next = buf + len + !len;
+
+ // Assume a four-byte character and load four bytes. Unused bits are
+ // shifted out.
+ *c = uint32_t(s[0] & masks[len]) << 18;
+ *c |= uint32_t(s[1] & 0x3f) << 12;
+ *c |= uint32_t(s[2] & 0x3f) << 6;
+ *c |= uint32_t(s[3] & 0x3f) << 0;
+ *c >>= shiftc[len];
+
+ // Accumulate the various error conditions.
+ *e = (*c < mins[len]) << 6; // non-canonical encoding
+ *e |= ((*c >> 11) == 0x1b) << 7; // surrogate half?
+ *e |= (*c > 0x10FFFF) << 8; // out of range?
+ *e |= (s[1] & 0xc0) >> 2;
+ *e |= (s[2] & 0xc0) >> 4;
+ *e |= (s[3]) >> 6;
+ *e ^= 0x2a; // top two bits of each tail byte correct?
+ *e >>= shifte[len];
+
+ return next;
+}
} // namespace internal
template <> struct formatter<internal::bigint> {
auto out = ctx.out();
bool first = true;
for (auto i = n.bigits_.size(); i > 0; --i) {
- auto value = n.bigits_[i - 1];
+ auto value = n.bigits_[i - 1u];
if (first) {
out = format_to(out, "{:x}", value);
first = false;
}
};
-#if FMT_USE_WINDOWS_H
-
FMT_FUNC internal::utf8_to_utf16::utf8_to_utf16(string_view s) {
- static const char ERROR_MSG[] = "cannot convert string from UTF-8 to UTF-16";
- if (s.size() > INT_MAX)
- FMT_THROW(windows_error(ERROR_INVALID_PARAMETER, ERROR_MSG));
- int s_size = static_cast<int>(s.size());
- if (s_size == 0) {
- // MultiByteToWideChar does not support zero length, handle separately.
- buffer_.resize(1);
- buffer_[0] = 0;
- return;
- }
-
- int length = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, s.data(),
- s_size, nullptr, 0);
- if (length == 0) FMT_THROW(windows_error(GetLastError(), ERROR_MSG));
- buffer_.resize(length + 1);
- length = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, s.data(), s_size,
- &buffer_[0], length);
- if (length == 0) FMT_THROW(windows_error(GetLastError(), ERROR_MSG));
- buffer_[length] = 0;
-}
-
-FMT_FUNC internal::utf16_to_utf8::utf16_to_utf8(wstring_view s) {
- if (int error_code = convert(s)) {
- FMT_THROW(windows_error(error_code,
- "cannot convert string from UTF-16 to UTF-8"));
- }
-}
-
-FMT_FUNC int internal::utf16_to_utf8::convert(wstring_view s) {
- if (s.size() > INT_MAX) return ERROR_INVALID_PARAMETER;
- int s_size = static_cast<int>(s.size());
- if (s_size == 0) {
- // WideCharToMultiByte does not support zero length, handle separately.
- buffer_.resize(1);
- buffer_[0] = 0;
- return 0;
- }
-
- int length = WideCharToMultiByte(CP_UTF8, 0, s.data(), s_size, nullptr, 0,
- nullptr, nullptr);
- if (length == 0) return GetLastError();
- buffer_.resize(length + 1);
- length = WideCharToMultiByte(CP_UTF8, 0, s.data(), s_size, &buffer_[0],
- length, nullptr, nullptr);
- if (length == 0) return GetLastError();
- buffer_[length] = 0;
- return 0;
-}
-
-FMT_FUNC void windows_error::init(int err_code, string_view format_str,
- format_args args) {
- error_code_ = err_code;
- memory_buffer buffer;
- internal::format_windows_error(buffer, err_code, vformat(format_str, args));
- std::runtime_error& base = *this;
- base = std::runtime_error(to_string(buffer));
-}
-
-FMT_FUNC void internal::format_windows_error(internal::buffer<char>& out,
- int error_code,
- string_view message) FMT_NOEXCEPT {
- FMT_TRY {
- wmemory_buffer buf;
- buf.resize(inline_buffer_size);
- for (;;) {
- wchar_t* system_message = &buf[0];
- int result = FormatMessageW(
- FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, nullptr,
- error_code, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), system_message,
- static_cast<uint32_t>(buf.size()), nullptr);
- if (result != 0) {
- utf16_to_utf8 utf8_message;
- if (utf8_message.convert(system_message) == ERROR_SUCCESS) {
- internal::writer w(out);
- w.write(message);
- w.write(": ");
- w.write(utf8_message);
- return;
- }
- break;
- }
- if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)
- break; // Can't get error message, report error code instead.
- buf.resize(buf.size() * 2);
+ auto transcode = [this](const char* p) {
+ auto cp = uint32_t();
+ auto error = 0;
+ p = utf8_decode(p, &cp, &error);
+ if (error != 0) FMT_THROW(std::runtime_error("invalid utf8"));
+ if (cp <= 0xFFFF) {
+ buffer_.push_back(static_cast<wchar_t>(cp));
+ } else {
+ cp -= 0x10000;
+ buffer_.push_back(static_cast<wchar_t>(0xD800 + (cp >> 10)));
+ buffer_.push_back(static_cast<wchar_t>(0xDC00 + (cp & 0x3FF)));
}
+ return p;
+ };
+ auto p = s.data();
+ const size_t block_size = 4; // utf8_decode always reads blocks of 4 chars.
+ if (s.size() >= block_size) {
+ for (auto end = p + s.size() - block_size + 1; p < end;) p = transcode(p);
}
- FMT_CATCH(...) {}
- format_error_code(out, error_code, message);
+ if (auto num_chars_left = s.data() + s.size() - p) {
+ char buf[2 * block_size - 1] = {};
+ memcpy(buf, p, to_unsigned(num_chars_left));
+ p = buf;
+ do {
+ p = transcode(p);
+ } while (p - buf < num_chars_left);
+ }
+ buffer_.push_back(0);
}
-#endif // FMT_USE_WINDOWS_H
-
FMT_FUNC void format_system_error(internal::buffer<char>& out, int error_code,
string_view message) FMT_NOEXCEPT {
FMT_TRY {
report_error(format_system_error, error_code, message);
}
-#if FMT_USE_WINDOWS_H
-FMT_FUNC void report_windows_error(int error_code,
- fmt::string_view message) FMT_NOEXCEPT {
- report_error(internal::format_windows_error, error_code, message);
-}
-#endif
-
FMT_FUNC void vprint(std::FILE* f, string_view format_str, format_args args) {
memory_buffer buffer;
internal::vformat_to(buffer, format_str,
basic_format_args<buffer_context<char>>(args));
+#ifdef _WIN32
+ auto fd = _fileno(f);
+ if (_isatty(fd)) {
+ internal::utf8_to_utf16 u16(string_view(buffer.data(), buffer.size()));
+ auto written = DWORD();
+ if (!WriteConsoleW(reinterpret_cast<HANDLE>(_get_osfhandle(fd)),
+ u16.c_str(), static_cast<DWORD>(u16.size()), &written,
+ nullptr)) {
+ FMT_THROW(format_error("failed to write to console"));
+ }
+ return;
+ }
+#endif
internal::fwrite_fully(buffer.data(), 1, buffer.size(), f);
}
+#ifdef _WIN32
+// Print assuming legacy (non-Unicode) encoding.
+FMT_FUNC void internal::vprint_mojibake(std::FILE* f, string_view format_str,
+ format_args args) {
+ memory_buffer buffer;
+ internal::vformat_to(buffer, format_str,
+ basic_format_args<buffer_context<char>>(args));
+ fwrite_fully(buffer.data(), 1, buffer.size(), f);
+}
+#endif
+
FMT_FUNC void vprint(string_view format_str, format_args args) {
vprint(stdout, format_str, args);
}
#ifndef FMT_FORMAT_H_
#define FMT_FORMAT_H_
-#include "core.h"
-
#include <algorithm>
#include <cerrno>
#include <cmath>
#include <memory>
#include <stdexcept>
-#ifdef __clang__
-# define FMT_CLANG_VERSION (__clang_major__ * 100 + __clang_minor__)
-#else
-# define FMT_CLANG_VERSION 0
+#include "core.h"
+
+#ifdef FMT_DEPRECATED_INCLUDE_OS
+# include "os.h"
#endif
#ifdef __INTEL_COMPILER
# define FMT_HAS_BUILTIN(x) 0
#endif
-#if FMT_HAS_CPP_ATTRIBUTE(fallthrough) && \
- (__cplusplus >= 201703 || FMT_GCC_VERSION != 0)
+#if FMT_GCC_VERSION || FMT_CLANG_VERSION
+# define FMT_NOINLINE __attribute__((noinline))
+#else
+# define FMT_NOINLINE
+#endif
+
+#if __cplusplus == 201103L || __cplusplus == 201402L
+# if defined(__clang__)
+# define FMT_FALLTHROUGH [[clang::fallthrough]]
+# elif FMT_GCC_VERSION >= 700 && !defined(__PGI)
+# define FMT_FALLTHROUGH [[gnu::fallthrough]]
+# else
+# define FMT_FALLTHROUGH
+# endif
+#elif FMT_HAS_CPP17_ATTRIBUTE(fallthrough) || \
+ (defined(_MSVC_LANG) && _MSVC_LANG >= 201703L)
# define FMT_FALLTHROUGH [[fallthrough]]
#else
# define FMT_FALLTHROUGH
#ifndef FMT_THROW
# if FMT_EXCEPTIONS
-# if FMT_MSC_VER
+# if FMT_MSC_VER || FMT_NVCC
FMT_BEGIN_NAMESPACE
namespace internal {
template <typename Exception> inline void do_throw(const Exception& x) {
- // Silence unreachable code warnings in MSVC because these are nearly
- // impossible to fix in a generic code.
+ // 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;
}
# endif
#endif
+#if FMT_EXCEPTIONS
+# define FMT_TRY try
+# define FMT_CATCH(x) catch (x)
+#else
+# define FMT_TRY if (true)
+# define FMT_CATCH(x) if (false)
+#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 || \
#endif
#ifndef FMT_USE_UDL_TEMPLATE
-// EDG front end based compilers (icc, nvcc) do not support UDL templates yet
-// and GCC 9 warns about them.
+// EDG front end based compilers (icc, nvcc) and GCC < 6.4 do not propertly
+// 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 >= 600 && FMT_GCC_VERSION <= 900 && \
+ ((FMT_GCC_VERSION >= 604 && FMT_GCC_VERSION <= 900 && \
__cplusplus >= 201402L) || \
FMT_CLANG_VERSION >= 304)
# define FMT_USE_UDL_TEMPLATE 1
# endif
#endif
+#ifndef FMT_USE_FLOAT
+# define FMT_USE_FLOAT 1
+#endif
+
+#ifndef FMT_USE_DOUBLE
+# define FMT_USE_DOUBLE 1
+#endif
+
+#ifndef FMT_USE_LONG_DOUBLE
+# define FMT_USE_LONG_DOUBLE 1
+#endif
+
// __builtin_clz is broken in clang with Microsoft CodeGen:
// https://github.com/fmtlib/fmt/issues/519
#if (FMT_GCC_VERSION || FMT_HAS_BUILTIN(__builtin_clz)) && !FMT_MSC_VER
FMT_BEGIN_NAMESPACE
namespace internal {
-// A helper function to suppress bogus "conditional expression is constant"
-// warnings.
-template <typename T> inline T const_check(T value) { return value; }
-
// An equivalent of `*reinterpret_cast<Dest*>(&source)` that doesn't have
// undefined behavior (e.g. due to type aliasing).
// Example: uint64_t d = bit_cast<uint64_t>(2.718);
}
inline bool is_big_endian() {
- auto u = 1u;
+ const auto u = 1u;
struct bytes {
char data[sizeof(u)];
};
using type = decltype(test<It>(typename iterator_category<It>::type{}));
public:
- static const bool value = !std::is_const<remove_reference_t<type>>::value;
+ enum { value = !std::is_const<remove_reference_t<type>>::value };
};
// A workaround for std::string not having mutable data() until C++17.
return c.data();
}
-#ifdef _SECURE_SCL
+#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) {
public:
using iterator_category = std::output_iterator_tag;
+ using value_type = typename std::iterator_traits<OutputIt>::value_type;
using difference_type = void;
using pointer = void;
using reference = void;
template <typename OutputIt>
class truncating_iterator<OutputIt, std::false_type>
: public truncating_iterator_base<OutputIt> {
- using traits = std::iterator_traits<OutputIt>;
-
- mutable typename traits::value_type blackhole_;
+ mutable typename truncating_iterator_base<OutputIt>::value_type blackhole_;
public:
- using value_type = typename traits::value_type;
+ using value_type = typename truncating_iterator_base<OutputIt>::value_type;
truncating_iterator(OutputIt out, std::size_t limit)
: truncating_iterator_base<OutputIt>(out, limit) {}
class truncating_iterator<OutputIt, std::true_type>
: public truncating_iterator_base<OutputIt> {
public:
- using value_type = typename OutputIt::container_type::value_type;
-
truncating_iterator(OutputIt out, std::size_t limit)
: truncating_iterator_base<OutputIt>(out, limit) {}
- truncating_iterator& operator=(value_type val) {
- if (this->count_++ < this->limit_) this->out_ = val;
+ template <typename T> truncating_iterator& operator=(T val) {
+ if (this->count_++ < this->limit_) *this->out_++ = val;
return *this;
}
}
// Counts the number of code points in a UTF-8 string.
-inline size_t count_code_points(basic_string_view<char8_t> s) {
- const char8_t* data = s.data();
+inline size_t count_code_points(basic_string_view<char> s) {
+ const char* data = s.data();
size_t num_code_points = 0;
for (size_t i = 0, size = s.size(); i != size; ++i) {
if ((data[i] & 0xc0) != 0x80) ++num_code_points;
return num_code_points;
}
+inline size_t count_code_points(basic_string_view<char8_type> s) {
+ return count_code_points(basic_string_view<char>(
+ reinterpret_cast<const char*>(s.data()), s.size()));
+}
+
template <typename Char>
inline size_t code_point_index(basic_string_view<Char> s, size_t n) {
size_t size = s.size();
}
// Calculates the index of the nth code point in a UTF-8 string.
-inline size_t code_point_index(basic_string_view<char8_t> s, size_t n) {
- const char8_t* data = s.data();
+inline size_t code_point_index(basic_string_view<char8_type> s, size_t n) {
+ const char8_type* data = s.data();
size_t num_code_points = 0;
for (size_t i = 0, size = s.size(); i != size; ++i) {
if ((data[i] & 0xc0) != 0x80 && ++num_code_points > n) {
return s.size();
}
-inline char8_t to_char8_t(char c) { return static_cast<char8_t>(c); }
+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,
char>::value &&
- std::is_same<OutChar, char8_t>::value>;
+ std::is_same<OutChar, char8_type>::value>;
template <typename OutChar, typename InputIt, typename OutputIt,
FMT_ENABLE_IF(!needs_conversion<InputIt, OutChar>::value)>
: internal::output_range<iterator, T>(std::back_inserter(buf)) {}
};
-// A UTF-8 string view.
-class u8string_view : public basic_string_view<char8_t> {
+class FMT_DEPRECATED u8string_view
+ : public basic_string_view<internal::char8_type> {
public:
u8string_view(const char* s)
- : basic_string_view<char8_t>(reinterpret_cast<const char8_t*>(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<char8_t>(reinterpret_cast<const char8_t*>(s), count) {
- }
+ : basic_string_view<internal::char8_type>(
+ reinterpret_cast<const internal::char8_type*>(s), count) {}
};
#if FMT_USE_USER_DEFINED_LITERALS
inline namespace literals {
-inline u8string_view operator"" _u(const char* s, std::size_t n) {
- return {s, n};
+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
using wmemory_buffer = basic_memory_buffer<wchar_t>;
/** A formatting error such as invalid format string. */
+FMT_CLASS_API
class FMT_API format_error : public std::runtime_error {
public:
explicit format_error(const char* message) : std::runtime_error(message) {}
return false;
}
+template <typename T, FMT_ENABLE_IF(std::is_floating_point<T>::value)>
+FMT_CONSTEXPR bool is_supported_floating_point(T) {
+ return (std::is_same<T, float>::value && FMT_USE_FLOAT) ||
+ (std::is_same<T, double>::value && FMT_USE_DOUBLE) ||
+ (std::is_same<T, long double>::value && FMT_USE_LONG_DOUBLE);
+}
+
// Smallest of uint32_t, uint64_t, uint128_t that is large enough to
// represent all values of T.
template <typename T>
return end;
}
-template <typename Int> constexpr int digits10() noexcept {
+template <typename Int> constexpr int digits10() FMT_NOEXCEPT {
return std::numeric_limits<Int>::digits10;
}
-template <> constexpr int digits10<int128_t>() noexcept { return 38; }
-template <> constexpr int digits10<uint128_t>() noexcept { return 38; }
+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,
return internal::copy_str<Char>(buffer, buffer + num_digits, out);
}
-#ifndef _WIN32
-# define FMT_USE_WINDOWS_H 0
-#elif !defined(FMT_USE_WINDOWS_H)
-# define FMT_USE_WINDOWS_H 1
-#endif
-
-// Define FMT_USE_WINDOWS_H to 0 to disable use of windows.h.
-// All the functionality that relies on it will be disabled too.
-#if FMT_USE_WINDOWS_H
// A converter from UTF-8 to UTF-16.
-// It is only provided for Windows since other systems support UTF-8 natively.
class utf8_to_utf16 {
private:
wmemory_buffer buffer_;
public:
FMT_API explicit utf8_to_utf16(string_view s);
- operator wstring_view() const { return wstring_view(&buffer_[0], size()); }
+ operator wstring_view() const { return {&buffer_[0], size()}; }
size_t size() const { return buffer_.size() - 1; }
const wchar_t* c_str() const { return &buffer_[0]; }
- std::wstring str() const { return std::wstring(&buffer_[0], size()); }
+ std::wstring str() const { return {&buffer_[0], size()}; }
};
-// A converter from UTF-16 to UTF-8.
-// It is only provided for Windows since other systems support UTF-8 natively.
-class utf16_to_utf8 {
- private:
- memory_buffer buffer_;
-
- public:
- utf16_to_utf8() {}
- FMT_API explicit utf16_to_utf8(wstring_view s);
- operator string_view() const { return string_view(&buffer_[0], size()); }
- size_t size() const { return buffer_.size() - 1; }
- const char* c_str() const { return &buffer_[0]; }
- std::string str() const { return std::string(&buffer_[0], size()); }
-
- // Performs conversion returning a system error code instead of
- // throwing exception on conversion error. This method may still throw
- // in case of memory allocation error.
- FMT_API int convert(wstring_view s);
-};
-
-FMT_API void format_windows_error(internal::buffer<char>& out, int error_code,
- string_view message) FMT_NOEXCEPT;
-#endif
-
template <typename T = void> struct null {};
// Workaround an array initialization issue in gcc 4.8.
template <typename Char> struct fill_t {
private:
- Char data_[6];
+ enum { max_size = 4 };
+ Char data_[max_size];
+ unsigned char size_;
public:
+ FMT_CONSTEXPR void operator=(basic_string_view<Char> s) {
+ auto size = s.size();
+ if (size > max_size) {
+ FMT_THROW(format_error("invalid fill"));
+ return;
+ }
+ for (size_t i = 0; i < size; ++i) data_[i] = s[i];
+ size_ = static_cast<unsigned char>(size);
+ }
+
+ size_t size() const { return size_; }
+ const Char* data() const { return data_; }
+
FMT_CONSTEXPR Char& operator[](size_t index) { return data_[index]; }
FMT_CONSTEXPR const Char& operator[](size_t index) const {
return data_[index];
static FMT_CONSTEXPR fill_t<Char> make() {
auto fill = fill_t<Char>();
fill[0] = Char(' ');
+ fill.size_ = 1;
return fill;
}
};
bool percent : 1;
bool binary32 : 1;
bool use_grisu : 1;
- bool trailing_zeros : 1;
+ bool showpoint : 1;
};
// Writes the exponent exp in the form "[+-]d{2,3}" to buffer.
// Insert a decimal point after the first digit and add an exponent.
*it++ = static_cast<Char>(*digits_);
int num_zeros = specs_.precision - num_digits_;
- bool trailing_zeros = num_zeros > 0 && specs_.trailing_zeros;
- if (num_digits_ > 1 || trailing_zeros) *it++ = decimal_point_;
+ if (num_digits_ > 1 || specs_.showpoint) *it++ = decimal_point_;
it = copy_str<Char>(digits_ + 1, digits_ + num_digits_, it);
- if (trailing_zeros)
+ if (num_zeros > 0 && specs_.showpoint)
it = std::fill_n(it, num_zeros, static_cast<Char>('0'));
*it++ = static_cast<Char>(specs_.upper ? 'E' : 'e');
return write_exponent<Char>(full_exp - 1, it);
// 1234e7 -> 12340000000[.0+]
it = copy_str<Char>(digits_, digits_ + num_digits_, it);
it = std::fill_n(it, full_exp - num_digits_, static_cast<Char>('0'));
- if (specs_.trailing_zeros) {
+ if (specs_.showpoint || specs_.precision < 0) {
*it++ = decimal_point_;
int num_zeros = specs_.precision - full_exp;
if (num_zeros <= 0) {
} else if (full_exp > 0) {
// 1234e-2 -> 12.34[0+]
it = copy_str<Char>(digits_, digits_ + full_exp, it);
- if (!specs_.trailing_zeros) {
+ if (!specs_.showpoint) {
// Remove trailing zeros.
int num_digits = num_digits_;
while (num_digits > full_exp && digits_[num_digits - 1] == '0')
// 1234e-6 -> 0.001234
*it++ = static_cast<Char>('0');
int num_zeros = -full_exp;
- if (specs_.precision >= 0 && specs_.precision < num_zeros)
- num_zeros = specs_.precision;
int num_digits = num_digits_;
- if (!specs_.trailing_zeros)
+ if (num_digits == 0 && specs_.precision >= 0 &&
+ specs_.precision < num_zeros) {
+ num_zeros = specs_.precision;
+ }
+ // Remove trailing zeros.
+ if (!specs_.showpoint)
while (num_digits > 0 && digits_[num_digits - 1] == '0') --num_digits;
- if (num_zeros != 0 || num_digits != 0) {
+ if (num_zeros != 0 || num_digits != 0 || specs_.showpoint) {
*it++ = decimal_point_;
it = std::fill_n(it, num_zeros, static_cast<Char>('0'));
it = copy_str<Char>(digits_, digits_ + num_digits, it);
buffer<char>& buf);
template <typename T> T promote_float(T value) { return value; }
-inline double promote_float(float value) { return value; }
+inline double promote_float(float value) { return static_cast<double>(value); }
template <typename Handler>
FMT_CONSTEXPR void handle_int_type_spec(char spec, Handler&& handler) {
handler.on_oct();
break;
case 'n':
+ case 'L':
handler.on_num();
break;
default:
FMT_CONSTEXPR float_specs parse_float_type_spec(
const basic_format_specs<Char>& specs, ErrorHandler&& eh = {}) {
auto result = float_specs();
- result.trailing_zeros = specs.alt;
+ result.showpoint = specs.alt;
switch (specs.type) {
case 0:
result.format = float_format::general;
- result.trailing_zeros |= specs.precision != 0;
+ result.showpoint |= specs.precision > 0;
break;
case 'G':
result.upper = true;
FMT_FALLTHROUGH;
case 'e':
result.format = float_format::exp;
- result.trailing_zeros |= specs.precision != 0;
+ result.showpoint |= specs.precision != 0;
break;
case 'F':
result.upper = true;
FMT_FALLTHROUGH;
case 'f':
result.format = float_format::fixed;
- result.trailing_zeros |= specs.precision != 0;
+ result.showpoint |= specs.precision != 0;
break;
#if FMT_DEPRECATED_PERCENT
case '%':
if (args.is_packed()) {
for (int i = 0;; ++i) {
internal::type arg_type = args.type(i);
- if (arg_type == internal::none_type) return;
- if (arg_type == internal::named_arg_type) push_back(args.values_[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::named_arg_type) push_back(args.args_[i].value_);
+ if (type == internal::type::named_arg_type) push_back(args.args_[i].value_);
}
}
}
};
+template <typename OutputIt, typename Char>
+FMT_NOINLINE OutputIt fill(OutputIt it, size_t n, const fill_t<Char>& fill) {
+ auto fill_size = fill.size();
+ if (fill_size == 1) return std::fill_n(it, n, fill[0]);
+ for (size_t i = 0; i < n; ++i) it = std::copy_n(fill.data(), fill_size, it);
+ return it;
+}
+
// This template provides operations for formatting and writing data into a
// character range.
template <typename Range> class basic_writer {
}
};
+ struct bytes_writer {
+ string_view bytes;
+
+ size_t size() const { return bytes.size(); }
+ size_t width() const { return bytes.size(); }
+
+ template <typename It> void operator()(It&& it) const {
+ const char* data = bytes.data();
+ it = copy_str<char>(data, data + size(), it);
+ }
+ };
+
template <typename UIntPtr> struct pointer_writer {
UIntPtr value;
int num_digits;
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));
- auto&& it = reserve(width + (size - num_code_points));
- char_type fill = specs.fill[0];
- std::size_t padding = width - num_code_points;
+ 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 = std::fill_n(it, padding, fill);
+ it = fill(it, padding, specs.fill);
f(it);
} else if (specs.align == align::center) {
std::size_t left_padding = padding / 2;
- it = std::fill_n(it, left_padding, fill);
+ it = fill(it, left_padding, specs.fill);
f(it);
- it = std::fill_n(it, padding - left_padding, fill);
+ it = fill(it, padding - left_padding, specs.fill);
} else {
f(it);
- it = std::fill_n(it, padding, fill);
+ it = fill(it, padding, specs.fill);
}
}
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.
return;
}
int precision = specs.precision >= 0 || !specs.type ? specs.precision : 6;
- if (fspecs.format == float_format::exp) ++precision;
+ 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;
write(data, size, 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);
template <typename T, FMT_ENABLE_IF(std::is_floating_point<T>::value)>
iterator operator()(T value) {
- writer_.write(value, specs_ ? *specs_ : format_specs());
+ if (const_check(is_supported_floating_point(value)))
+ writer_.write(value, specs_ ? *specs_ : format_specs());
+ else
+ FMT_ASSERT(false, "unsupported float argument type");
return out();
}
FMT_CONSTEXPR int parse_nonnegative_int(const Char*& begin, const Char* end,
ErrorHandler&& eh) {
FMT_ASSERT(begin != end && '0' <= *begin && *begin <= '9', "");
- if (*begin == '0') {
- ++begin;
- return 0;
- }
unsigned value = 0;
// Convert to unsigned to prevent a warning.
constexpr unsigned max_int = max_value<int>();
: specs_(other.specs_) {}
FMT_CONSTEXPR void on_align(align_t align) { specs_.align = align; }
- FMT_CONSTEXPR void on_fill(Char fill) { specs_.fill[0] = fill; }
+ FMT_CONSTEXPR void on_fill(basic_string_view<Char> fill) {
+ specs_.fill = fill;
+ }
FMT_CONSTEXPR void on_plus() { specs_.sign = sign::plus; }
FMT_CONSTEXPR void on_minus() { specs_.sign = sign::minus; }
FMT_CONSTEXPR void on_space() { specs_.sign = sign::space; }
FMT_CONSTEXPR void check_sign() {
require_numeric_argument();
- if (is_integral_type(arg_type_) && arg_type_ != int_type &&
- arg_type_ != long_long_type && arg_type_ != internal::char_type) {
+ if (is_integral_type(arg_type_) && arg_type_ != type::int_type &&
+ arg_type_ != type::long_long_type && arg_type_ != type::char_type) {
error_handler_.on_error("format specifier requires signed argument");
}
}
FMT_CONSTEXPR void check_precision() {
- if (is_integral_type(arg_type_) || arg_type_ == internal::pointer_type)
+ if (is_integral_type(arg_type_) || arg_type_ == type::pointer_type)
error_handler_.on_error("precision not allowed for this argument type");
}
// An argument reference.
template <typename Char> struct arg_ref {
FMT_CONSTEXPR arg_ref() : kind(arg_id_kind::none), val() {}
+
FMT_CONSTEXPR explicit arg_ref(int index)
: kind(arg_id_kind::index), val(index) {}
FMT_CONSTEXPR explicit arg_ref(basic_string_view<Char> name)
return begin;
}
if (c >= '0' && c <= '9') {
- int index = parse_nonnegative_int(begin, end, handler);
+ int index = 0;
+ if (c != '0')
+ index = parse_nonnegative_int(begin, end, handler);
+ else
+ ++begin;
if (begin == end || (*begin != '}' && *begin != ':'))
handler.on_error("invalid format string");
else
SpecHandler& handler;
};
+template <typename Char>
+FMT_CONSTEXPR const Char* next_code_point(const Char* begin, const Char* end) {
+ if (const_check(sizeof(Char) != 1) || (*begin & 0x80) == 0) return begin + 1;
+ do {
+ ++begin;
+ } while (begin != end && (*begin & 0xc0) == 0x80);
+ return begin;
+}
+
// Parses fill and alignment.
template <typename Char, typename Handler>
FMT_CONSTEXPR const Char* parse_align(const Char* begin, const Char* end,
Handler&& handler) {
FMT_ASSERT(begin != end, "");
auto align = align::none;
- int i = 0;
- if (begin + 1 != end) ++i;
- do {
- switch (static_cast<char>(begin[i])) {
+ auto p = next_code_point(begin, end);
+ if (p == end) p = begin;
+ for (;;) {
+ switch (static_cast<char>(*p)) {
case '<':
align = align::left;
break;
break;
}
if (align != align::none) {
- if (i > 0) {
+ if (p != begin) {
auto c = *begin;
if (c == '{')
return handler.on_error("invalid fill character '{'"), begin;
- begin += 2;
- handler.on_fill(c);
+ handler.on_fill(basic_string_view<Char>(begin, to_unsigned(p - begin)));
+ begin = p + 1;
} else
++begin;
handler.on_align(align);
break;
+ } else if (p == begin) {
+ break;
}
- } while (i-- > 0);
+ p = begin;
+ }
return begin;
}
// Doing two passes with memchr (one for '{' and another for '}') is up to
// 2.5x faster than the naive one-pass implementation on big format strings.
const Char* p = begin;
- if (*begin != '{' && !find<IS_CONSTEXPR>(begin, end, '{', p))
+ if (*begin != '{' && !find<IS_CONSTEXPR>(begin + 1, end, '{', p))
return write(begin, end);
write(begin, p);
++p;
using context = buffer_context<char_type>;
using mapped_type =
conditional_t<internal::mapped_type_constant<T, context>::value !=
- internal::custom_type,
+ 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>,
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, Args...>(
- to_string_view(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));
(void)invalid_format;
}
break;
}
}
+
+using format_func = void (*)(internal::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>
An error returned by an operating system or a language runtime,
for example a file opening error.
*/
+FMT_CLASS_API
class FMT_API system_error : public std::runtime_error {
private:
void init(int err_code, string_view format_str, format_args args);
FMT_API void report_system_error(int error_code,
string_view message) FMT_NOEXCEPT;
-#if FMT_USE_WINDOWS_H
-
-/** A Windows error. */
-class windows_error : public system_error {
- private:
- FMT_API void init(int error_code, string_view format_str, format_args args);
-
- public:
- /**
- \rst
- Constructs a :class:`fmt::windows_error` object with the description
- of the form
-
- .. parsed-literal::
- *<message>*: *<system-message>*
-
- where *<message>* is the formatted message and *<system-message>* is the
- system message corresponding to the error code.
- *error_code* is a Windows error code as given by ``GetLastError``.
- If *error_code* is not a valid error code such as -1, the system message
- will look like "error -1".
-
- **Example**::
-
- // This throws a windows_error with the description
- // cannot open file 'madeup': The system cannot find the file specified.
- // or similar (system message may vary).
- const char *filename = "madeup";
- LPOFSTRUCT of = LPOFSTRUCT();
- HFILE file = OpenFile(filename, &of, OF_READ);
- if (file == HFILE_ERROR) {
- throw fmt::windows_error(GetLastError(),
- "cannot open file '{}'", filename);
- }
- \endrst
- */
- template <typename... Args>
- windows_error(int error_code, string_view message, const Args&... args) {
- init(error_code, message, make_format_args(args...));
- }
-};
-
-// Reports a Windows error without throwing an exception.
-// Can be used to report errors from destructors.
-FMT_API void report_windows_error(int error_code,
- string_view message) FMT_NOEXCEPT;
-
-#endif
-
/** Fast integer formatter. */
class format_int {
private:
template <typename T, typename Char>
struct formatter<T, Char,
enable_if_t<internal::type_constant<T, Char>::value !=
- internal::custom_type>> {
+ internal::type::custom_type>> {
FMT_CONSTEXPR formatter() = default;
// Parses format specifiers stopping either at the end of the range or at the
auto it = parse_format_specs(ctx.begin(), ctx.end(), handler);
auto eh = ctx.error_handler();
switch (type) {
- case internal::none_type:
- case internal::named_arg_type:
+ case internal::type::none_type:
+ case internal::type::named_arg_type:
FMT_ASSERT(false, "invalid argument type");
break;
- case internal::int_type:
- case internal::uint_type:
- case internal::long_long_type:
- case internal::ulong_long_type:
- case internal::int128_type:
- case internal::uint128_type:
- case internal::bool_type:
+ 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:
handle_int_type_spec(specs_.type,
internal::int_type_checker<decltype(eh)>(eh));
break;
- case internal::char_type:
+ case internal::type::char_type:
handle_char_specs(
&specs_, internal::char_specs_checker<decltype(eh)>(specs_.type, eh));
break;
- case internal::float_type:
- case internal::double_type:
- case internal::long_double_type:
- internal::parse_float_type_spec(specs_, eh);
+ case internal::type::float_type:
+ if (internal::const_check(FMT_USE_FLOAT)) {
+ internal::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 {
+ 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 {
+ FMT_ASSERT(false, "long double support disabled");
+ }
break;
- case internal::cstring_type:
+ case internal::type::cstring_type:
internal::handle_cstring_type_spec(
specs_.type, internal::cstring_type_checker<decltype(eh)>(eh));
break;
- case internal::string_type:
+ case internal::type::string_type:
internal::check_string_type_spec(specs_.type, eh);
break;
- case internal::pointer_type:
+ case internal::type::pointer_type:
internal::check_pointer_type_spec(specs_.type, eh);
break;
- case internal::custom_type:
+ case internal::type::custom_type:
// Custom format specifiers should be checked in parse functions of
// formatter specializations.
break;
template <typename Char> \
struct formatter<Type, Char> : formatter<Base, Char> { \
template <typename FormatContext> \
- auto format(const Type& val, FormatContext& ctx) -> decltype(ctx.out()) { \
+ auto format(Type const& val, FormatContext& ctx) -> decltype(ctx.out()) { \
return formatter<Base, Char>::format(val, ctx); \
} \
}
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::none_type) this->on_error("argument not found");
+ if (arg.type() == internal::type::none_type)
+ this->on_error("argument not found");
return arg;
}
return p.get();
}
+class bytes {
+ private:
+ string_view data_;
+ friend struct formatter<bytes>;
+
+ public:
+ explicit bytes(string_view data) : data_(data) {}
+};
+
+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);
+ auto it = parse_format_specs(ctx.begin(), ctx.end(), handler);
+ internal::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>(
+ 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();
+ }
+
+ private:
+ internal::dynamic_format_specs<char> specs_;
+};
+
template <typename It, typename Char> struct arg_join : internal::view {
It begin;
It end;
std::vector<int> v = {1, 2, 3};
fmt::print("{}", fmt::join(v, ", "));
// Output: "1, 2, 3"
+
+ ``fmt::join`` applies passed format specifiers to the range elements::
+
+ fmt::print("{:02}", fmt::join(v, ", "));
+ // Output: "01, 02, 03"
\endrst
*/
template <typename Range>
/**
\rst
Converts *value* to ``std::string`` using the default format for type *T*.
- It doesn't support user-defined types with custom formatters.
**Example**::
template <typename Char>
typename buffer_context<Char>::iterator internal::vformat_to(
internal::buffer<Char>& buf, basic_string_view<Char> format_str,
- basic_format_args<buffer_context<Char>> args) {
+ basic_format_args<buffer_context<type_identity_t<Char>>> args) {
using range = buffer_range<Char>;
return vformat_to<arg_formatter<range>>(buf, to_string_view(format_str),
args);
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<Char>> args) {
+ basic_format_args<buffer_context<type_identity_t<Char>>> args) {
return internal::vformat_to(buf, to_string_view(format_str), args);
}
internal::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...)});
+ make_format_args<context>(args...));
}
template <typename OutputIt, typename Char = char>
FMT_ENABLE_IF(
internal::is_output_iterator<OutputIt>::value &&
!internal::is_contiguous_back_insert_iterator<OutputIt>::value)>
-inline OutputIt vformat_to(OutputIt out, const S& format_str,
- format_args_t<OutputIt, char_t<S>> args) {
+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);
internal::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...)});
+ make_format_args<context>(args...));
}
template <typename OutputIt> struct format_to_n_result {
FMT_ENABLE_IF(internal::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,
- format_to_n_args<OutputIt, Char> args) {
+ format_to_n_args<type_identity_t<OutputIt>, type_identity_t<Char>> args) {
auto it = vformat_to(internal::truncating_iterator<OutputIt>(out, n),
format_str, args);
return {it.base(), it.count()};
internal::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...)});
+ make_format_args<context>(args...));
}
template <typename Char>
inline std::basic_string<Char> internal::vformat(
basic_string_view<Char> format_str,
- basic_format_args<buffer_context<Char>> args) {
+ basic_format_args<buffer_context<type_identity_t<Char>>> args) {
basic_memory_buffer<Char> buffer;
internal::vformat_to(buffer, format_str, args);
return to_string(buffer);
}
};
+// 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
inline namespace literals {
#endif // FMT_USE_USER_DEFINED_LITERALS
FMT_END_NAMESPACE
-#define FMT_STRING_IMPL(s, ...) \
- [] { \
- struct str : fmt::compile_string { \
- using char_type = typename std::remove_cv<std::remove_pointer< \
- typename std::decay<decltype(s)>::type>::type>::type; \
- __VA_ARGS__ FMT_CONSTEXPR \
- operator fmt::basic_string_view<char_type>() const { \
- return {s, sizeof(s) / sizeof(char_type) - 1}; \
- } \
- } result; \
- /* Suppress Qt Creator warning about unused operator. */ \
- (void)static_cast<fmt::basic_string_view<typename str::char_type>>( \
- result); \
- return result; \
+#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.
+ Constructs a compile-time format string from a string literal *s*.
**Example**::
"fuzz mode - avoid large allocation inside snprintf");
#endif
// Suppress the warning about nonliteral format string.
- auto snprintf_ptr = FMT_SNPRINTF;
+ int (*snprintf_ptr)(char*, size_t, const char*, ...) = FMT_SNPRINTF;
return precision < 0 ? snprintf_ptr(buf, size, format, value)
: snprintf_ptr(buf, size, format, precision, value);
}
internal::buffer<char>&,
sprintf_specs);
-template struct FMT_API internal::basic_data<void>;
+template struct FMT_INSTANTIATION_DEF_API internal::basic_data<void>;
// Workaround a bug in MSVC2013 that prevents instantiation of format_float.
int (*instantiate_format_float)(double, int, internal::float_specs,