Squashed 'external/fmt/' changes from 3b70966df..093b39ca5

093b39ca5 Update docs for meson (#4291)
2c3a5698e Simplify a copying the fill from basic_specs
fc1b0f348 Clarify use of FMT_THROW in a comment
1d066890c Resolve C4702 unreachable code warnings
dad323751 Fix a bug when copying the fill from basic_specs
880e1494d Improve xchar support for std::bitset formatter
e3ddede6c Update version
e9ec4fdc8 Bump version
feb72126b Readd FMT_NO_UNIQUE_ADDRESS
8d517e54c Update changelog
563fc74ae Update changelog
3e04222d5 Restore ABI compatibility with 11.0.2
853df39d0 Mention compile-time formatting
11742a09c Clarify that format_string should be used instead of fstring
da24fac10 Document fstring
5fa4bdd75 Define CMake components to allow docs to be installed separately (#4276)
3c8aad8df Update the release script
0e8aad961 Update version
debe784aa Update changelog
f6d112567 Update changelog
73d0d3f75 Fix github API call
08f60f1ef Update changelog
faf3f8408 Bump version
f3a41441d Replace requests with urllib
3f33cb21d Update changelog
b07a90386 Update changelog
a6fba5177 Update changelog
25e292998 Update changelog
00ab2e98b Update changelog
a3ef285ae Always inline const_check to improve debug codegen in clang
28d1abc9d Update changelog
90704b9ef Update changelog
86dae01c2 Fix compatibility with older versions of VS (#4271)
d8a79eafd Document formatting of bit-fields and fields of packed structs
7c3d0152e Use the _MSVC_STL_UPDATE macro to detect STL (#4267)
7c50da538 Allow getting size of dynamic format arg store (#4270)
873670ba3 Make parameter basic_memory_buffer<char, SIZE>& buf of to_string const
735d4cc05 Update changelog
141380172 Allow disabling <filesystem> by define FMT_CPP_LIB_FILESYSTEM=0 (#4259)
4302d7429 Update changelog
0f51ea79d Update changelog
9600fee02 Include <filesystem> only if FMT_CPP_LIB_FILESYSTEM is set (#4258)
47a66c5ec Bump msys2/setup-msys2 from 2.24.0 to 2.25.0 (#4250)
385c01dc7 Allow bit_cast to work for 80bit long double (#4246)
df249d8ad Remove an old workaround
dfad80d1c Remove an old workaround
536cabd56 Export all range join overloads (#4239)
b1a054706 Remove more MSVC 2015 workarounds and fix string_view checks
bfd95392c Remove MSVC 2015 workaround
9ced61bca Replace std::forward for clang-tidy (#4236)
75e5be6ad Sort specifiers
a169d7fa4 Fix chrono formatting syntax doc (#4235)
a6c45dfea Fix modular build
a35389b3c Corrently handle buffer flush
5a3576acc Implement fmt::join for tuple-like objects (#4230)
542600013 Suppress MSVC warnings "C4127: conditional expression is constant" by used const_check (#4233)
720da57ba Remove reference to unused intrinsic
680db66c3 Explicitly export symbols from detail
56ce41ef6 Remove initializer_list dependency
cf50e4d6a Fix const[expr] in context API
6580d7b80 Cleanup the format API
7e73566ce Minor cleanup
8523dba2d Make constexpr precede explicit consistently
e3d3b24fc Minor cleanup
1521bba70 Use consistent types for argument count
00649552a Bump github/codeql-action from 3.26.6 to 3.27.0 (#4223)
4b8e2838f More cleanup
7d4662f7a Remove FMT_BUILTIN_CTZ
27110bc47 Minor cleanup
68f315376 Fix narrowing conversion warning in struct fstring (#4210)
168df9a06 Implement fmt::format_to into std::vector<char> (#4211)
4daa3d591 Fix error: cannot use 'try' with exceptions disabled in Win LLVM Clang (#4208)
e9eaa27e5 Add std::exception to the docs
2b6a786e3 Use standard context in print
a16ff5787 Add support for code units > 0xFFFF in fill
601be1cbe Add support for code units > 0xFFFF in fill
58c185b63 Changing type of data_ to size_t to avoid compilation warnings (#4200)
a0a9ba2af Fix hashes
cc2ba8f9e Cleanup cifuzz action
a18d42b20 Simplify lint (#4197)
4046f9727 Fix -Wmissing-noreturn warning (#4194)
6bdc12a19 detail_exported -> detail
786a4b096 Cleanup fixed_string
2cb3b7c64 Update README.md
e9cba6905 Update README.md
02537548f Cleanup an example
c68c5fa7c Test FMT_BUILTIN_TYPES
22701d5f6 Address build failures when using Tip-of-Tree clang. (#4187)
e62c41ffb Conform `std::iterator_traits<fmt::appender>` to [iterator.traits]/1 (#4185)
18792893d Silencing Wextra-semi warning (#4188)
c90bc9186 Bump actions/checkout from 4.1.6 to 4.2.0 (#4182)
c95722ad6 Improve naming consistency
db06b0df8 Use countl_zero in bigint
b9ec48d9c Cleanup bigint
3faf6f181 Add min_of/max_of
d64b100a3 Relax constexpr
ff9ee0461 Fix handling FMT_BUILTIN_TYPES
1c5883bef Test nondeterministic conversion to format string
cacc3108c Don't assume repeated evaluation of string literal produce the same pointer
fade652ad Require clang >=15 for _BitInt support (#4176)
96dca569a Module linkage fixes for shared build (#4169)
891c9a73a Cleanup format API
9282222b7 Export more
e5b20ff0d Deprecate detail::locale_ref
ff9222354 Simplify locale handling
80c4d42c6 Cleanup format.h

git-subtree-dir: external/fmt
git-subtree-split: 093b39ca5eea129b111060839602bcfaf295125a
This commit is contained in:
Simone
2025-01-07 15:10:49 +00:00
parent 2abfcd02f1
commit 5ff285a9ed
41 changed files with 1068 additions and 774 deletions

View File

@@ -210,6 +210,9 @@ template <typename Context> class dynamic_format_arg_store {
data_.reserve(new_cap);
named_info_.reserve(new_cap_named);
}
/// Returns the number of elements in the store.
size_t size() const noexcept { return data_.size(); }
};
FMT_END_NAMESPACE

View File

@@ -21,7 +21,7 @@
#endif
// The fmt library version in the form major * 10000 + minor * 100 + patch.
#define FMT_VERSION 110002
#define FMT_VERSION 110101
// Detect compiler versions.
#if defined(__clang__) && !defined(__ibmxl__)
@@ -146,6 +146,8 @@
// Use the provided definition.
#elif defined(__GNUC__) && !defined(__EXCEPTIONS)
# define FMT_USE_EXCEPTIONS 0
#elif defined(__clang__) && !defined(__cpp_exceptions)
# define FMT_USE_EXCEPTIONS 0
#elif FMT_MSC_VERSION && !_HAS_EXCEPTIONS
# define FMT_USE_EXCEPTIONS 0
#else
@@ -159,6 +161,20 @@
# define FMT_CATCH(x) if (false)
#endif
#ifdef FMT_NO_UNIQUE_ADDRESS
// Use the provided definition.
#elif FMT_CPLUSPLUS < 202002L
// Not supported.
#elif FMT_HAS_CPP_ATTRIBUTE(no_unique_address)
# define FMT_NO_UNIQUE_ADDRESS [[no_unique_address]]
// VS2019 v16.10 and later except clang-cl (https://reviews.llvm.org/D110485).
#elif FMT_MSC_VERSION >= 1929 && !FMT_CLANG_VERSION
# define FMT_NO_UNIQUE_ADDRESS [[msvc::no_unique_address]]
#endif
#ifndef FMT_NO_UNIQUE_ADDRESS
# define FMT_NO_UNIQUE_ADDRESS
#endif
#if FMT_HAS_CPP17_ATTRIBUTE(fallthrough)
# define FMT_FALLTHROUGH [[fallthrough]]
#elif defined(__clang__)
@@ -332,6 +348,13 @@ struct monostate {
# define FMT_ENABLE_IF(...) fmt::enable_if_t<(__VA_ARGS__), int> = 0
#endif
template <typename T> constexpr auto min_of(T a, T b) -> T {
return a < b ? a : b;
}
template <typename T> constexpr auto max_of(T a, T b) -> T {
return a > b ? a : b;
}
namespace detail {
// Suppresses "unused variable" warnings with the method described in
// https://herbsutter.com/2009/10/18/mailbag-shutting-up-compiler-warnings/.
@@ -355,7 +378,9 @@ constexpr auto is_constant_evaluated(bool default_value = false) noexcept
}
// Suppresses "conditional expression is constant" warnings.
template <typename T> constexpr auto const_check(T value) -> T { return value; }
template <typename T> FMT_ALWAYS_INLINE constexpr auto const_check(T val) -> T {
return val;
}
FMT_NORETURN FMT_API void assert_fail(const char* file, int line,
const char* message);
@@ -394,7 +419,7 @@ inline auto map(uint128_opt) -> monostate { return {}; }
#endif
#ifndef FMT_USE_BITINT
# define FMT_USE_BITINT (FMT_CLANG_VERSION >= 1400)
# define FMT_USE_BITINT (FMT_CLANG_VERSION >= 1500)
#endif
#if FMT_USE_BITINT
@@ -562,8 +587,8 @@ template <typename Char> class basic_string_view {
// Lexicographically compare this string reference to other.
FMT_CONSTEXPR auto compare(basic_string_view other) const -> int {
size_t str_size = size_ < other.size_ ? size_ : other.size_;
int result = detail::compare(data_, other.data_, str_size);
int result =
detail::compare(data_, other.data_, min_of(size_, other.size_));
if (result != 0) return result;
return size_ == other.size_ ? 0 : (size_ < other.size_ ? -1 : 1);
}
@@ -714,7 +739,7 @@ class basic_specs {
max_fill_size = 4
};
unsigned long data_ = 1 << fill_size_shift;
size_t data_ = 1 << fill_size_shift;
// Character (code unit) type is erased to prevent template bloat.
char fill_data_[max_fill_size] = {' '};
@@ -793,7 +818,8 @@ class basic_specs {
template <typename Char> constexpr auto fill_unit() const -> Char {
using uchar = unsigned char;
return static_cast<Char>(static_cast<uchar>(fill_data_[0]) |
(static_cast<uchar>(fill_data_[1]) << 8));
(static_cast<uchar>(fill_data_[1]) << 8) |
(static_cast<uchar>(fill_data_[2]) << 16));
}
FMT_CONSTEXPR void set_fill(char c) {
@@ -809,12 +835,19 @@ class basic_specs {
unsigned uchar = static_cast<detail::unsigned_char<Char>>(s[0]);
fill_data_[0] = static_cast<char>(uchar);
fill_data_[1] = static_cast<char>(uchar >> 8);
fill_data_[2] = static_cast<char>(uchar >> 16);
return;
}
FMT_ASSERT(size <= max_fill_size, "invalid fill");
for (size_t i = 0; i < size; ++i)
fill_data_[i & 3] = static_cast<char>(s[i]);
}
FMT_CONSTEXPR void set_fill(const basic_specs& specs) {
set_fill_size(specs.fill_size());
for (size_t i = 0; i < max_fill_size; ++i)
fill_data_[i] = specs.fill_data_[i];
}
};
// Format specifiers for built-in and string types.
@@ -842,7 +875,7 @@ template <typename Char = char> class parse_context {
using char_type = Char;
using iterator = const Char*;
explicit constexpr parse_context(basic_string_view<Char> fmt,
constexpr explicit parse_context(basic_string_view<Char> fmt,
int next_arg_id = 0)
: fmt_(fmt), next_arg_id_(next_arg_id) {}
@@ -1012,15 +1045,15 @@ template <typename Char, typename T> struct named_arg : view {
static_assert(!is_named_arg<T>::value, "nested named arguments");
};
template <bool B = false> constexpr auto count() -> size_t { return B ? 1 : 0; }
template <bool B1, bool B2, bool... Tail> constexpr auto count() -> size_t {
template <bool B = false> constexpr auto count() -> int { return B ? 1 : 0; }
template <bool B1, bool B2, bool... Tail> constexpr auto count() -> int {
return (B1 ? 1 : 0) + count<B2, Tail...>();
}
template <typename... Args> constexpr auto count_named_args() -> size_t {
template <typename... Args> constexpr auto count_named_args() -> int {
return count<is_named_arg<Args>::value...>();
}
template <typename... Args> constexpr auto count_static_named_args() -> size_t {
template <typename... Args> constexpr auto count_static_named_args() -> int {
return count<is_static_named_arg<Args>::value...>();
}
@@ -1180,7 +1213,7 @@ class compile_parse_context : public parse_context<Char> {
using base = parse_context<Char>;
public:
explicit FMT_CONSTEXPR compile_parse_context(basic_string_view<Char> fmt,
FMT_CONSTEXPR explicit compile_parse_context(basic_string_view<Char> fmt,
int num_args, const type* types,
int next_arg_id = 0)
: base(fmt, next_arg_id), num_args_(num_args), types_(types) {}
@@ -1627,16 +1660,16 @@ template <typename... T> struct arg_pack {};
template <typename Char, int NUM_ARGS, int NUM_NAMED_ARGS, bool DYNAMIC_NAMES>
class format_string_checker {
private:
type types_[NUM_ARGS > 0 ? NUM_ARGS : 1];
named_arg_info<Char> named_args_[NUM_NAMED_ARGS > 0 ? NUM_NAMED_ARGS : 1];
type types_[max_of(1, NUM_ARGS)];
named_arg_info<Char> named_args_[max_of(1, NUM_NAMED_ARGS)];
compile_parse_context<Char> context_;
using parse_func = auto (*)(parse_context<Char>&) -> const Char*;
parse_func parse_funcs_[NUM_ARGS > 0 ? NUM_ARGS : 1];
parse_func parse_funcs_[max_of(1, NUM_ARGS)];
public:
template <typename... T>
explicit FMT_CONSTEXPR format_string_checker(basic_string_view<Char> fmt,
FMT_CONSTEXPR explicit format_string_checker(basic_string_view<Char> fmt,
arg_pack<T...>)
: types_{mapped_type_constant<T, Char>::value...},
named_args_{},
@@ -1694,7 +1727,7 @@ template <typename T> class buffer {
protected:
// Don't initialize ptr_ since it is not accessed to save a few cycles.
FMT_MSC_WARNING(suppress : 26495)
FMT_CONSTEXPR20 buffer(grow_fun grow, size_t sz) noexcept
FMT_CONSTEXPR buffer(grow_fun grow, size_t sz) noexcept
: size_(sz), capacity_(sz), grow_(grow) {}
constexpr buffer(grow_fun grow, T* p = nullptr, size_t sz = 0,
@@ -1740,7 +1773,7 @@ template <typename T> class buffer {
// the new elements may not be initialized.
FMT_CONSTEXPR void try_resize(size_t count) {
try_reserve(count);
size_ = count <= capacity_ ? count : capacity_;
size_ = min_of(count, capacity_);
}
// Tries increasing the buffer capacity to `new_capacity`. It can increase the
@@ -1788,9 +1821,9 @@ template <typename T> class buffer {
};
struct buffer_traits {
explicit buffer_traits(size_t) {}
auto count() const -> size_t { return 0; }
auto limit(size_t size) -> size_t { return size; }
constexpr explicit buffer_traits(size_t) {}
constexpr auto count() const -> size_t { return 0; }
constexpr auto limit(size_t size) const -> size_t { return size; }
};
class fixed_buffer_traits {
@@ -1799,12 +1832,12 @@ class fixed_buffer_traits {
size_t limit_;
public:
explicit fixed_buffer_traits(size_t limit) : limit_(limit) {}
auto count() const -> size_t { return count_; }
auto limit(size_t size) -> size_t {
constexpr explicit fixed_buffer_traits(size_t limit) : limit_(limit) {}
constexpr auto count() const -> size_t { return count_; }
FMT_CONSTEXPR auto limit(size_t size) -> size_t {
size_t n = limit_ > count_ ? limit_ - count_ : 0;
count_ += size;
return size < n ? size : n;
return min_of(size, n);
}
};
@@ -1962,15 +1995,37 @@ template <typename T = char> class counting_buffer : public buffer<T> {
template <typename T>
struct is_back_insert_iterator<basic_appender<T>> : std::true_type {};
template <typename OutputIt, typename InputIt, typename = void>
struct has_back_insert_iterator_container_append : std::false_type {};
template <typename OutputIt, typename InputIt>
struct has_back_insert_iterator_container_append<
OutputIt, InputIt,
void_t<decltype(get_container(std::declval<OutputIt>())
.append(std::declval<InputIt>(),
std::declval<InputIt>()))>> : std::true_type {};
// An optimized version of std::copy with the output value type (T).
template <typename T, typename InputIt, typename OutputIt,
FMT_ENABLE_IF(is_back_insert_iterator<OutputIt>::value)>
FMT_ENABLE_IF(is_back_insert_iterator<OutputIt>::value&&
has_back_insert_iterator_container_append<
OutputIt, InputIt>::value)>
FMT_CONSTEXPR20 auto copy(InputIt begin, InputIt end, OutputIt out)
-> OutputIt {
get_container(out).append(begin, end);
return out;
}
template <typename T, typename InputIt, typename OutputIt,
FMT_ENABLE_IF(is_back_insert_iterator<OutputIt>::value &&
!has_back_insert_iterator_container_append<
OutputIt, InputIt>::value)>
FMT_CONSTEXPR20 auto copy(InputIt begin, InputIt end, OutputIt out)
-> OutputIt {
auto& c = get_container(out);
c.insert(c.end(), begin, end);
return out;
}
template <typename T, typename InputIt, typename OutputIt,
FMT_ENABLE_IF(!is_back_insert_iterator<OutputIt>::value)>
FMT_CONSTEXPR auto copy(InputIt begin, InputIt end, OutputIt out) -> OutputIt {
@@ -2146,7 +2201,8 @@ template <typename Context> class value {
template <typename T, FMT_ENABLE_IF(is_named_arg<T>::value)>
value(const T& named_arg) : value(named_arg.value) {}
template <typename T, FMT_ENABLE_IF(use_formatter<T>::value)>
template <typename T,
FMT_ENABLE_IF(use_formatter<T>::value || !FMT_BUILTIN_TYPES)>
FMT_CONSTEXPR20 FMT_INLINE value(T& x) : value(x, custom_tag()) {}
FMT_ALWAYS_INLINE value(const named_arg_info<char_type>* args, size_t size)
@@ -2220,9 +2276,12 @@ struct locale_ref {
public:
constexpr locale_ref() : locale_(nullptr) {}
template <typename Locale> explicit locale_ref(const Locale& loc);
explicit operator bool() const noexcept { return locale_ != nullptr; }
#endif
template <typename Locale, FMT_ENABLE_IF(sizeof(Locale::collate) != 0)>
locale_ref(const Locale& loc);
inline explicit operator bool() const noexcept { return locale_ != nullptr; }
#endif // FMT_USE_LOCALE
template <typename Locale> auto get() const -> Locale;
};
@@ -2243,16 +2302,15 @@ constexpr auto make_descriptor() -> unsigned long long {
: is_unpacked_bit | NUM_ARGS;
}
template <typename Context, size_t NUM_ARGS>
template <typename Context, int NUM_ARGS>
using arg_t = conditional_t<NUM_ARGS <= max_packed_args, value<Context>,
basic_format_arg<Context>>;
template <typename Context, size_t NUM_ARGS, size_t NUM_NAMED_ARGS,
template <typename Context, int NUM_ARGS, int NUM_NAMED_ARGS,
unsigned long long DESC>
struct named_arg_store {
// args_[0].named_args points to named_args to avoid bloating format_args.
// +1 to workaround a bug in gcc 7.5 that causes duplicated-branches warning.
arg_t<Context, NUM_ARGS> args[1 + (NUM_ARGS != 0 ? NUM_ARGS : +1)];
arg_t<Context, NUM_ARGS> args[1 + NUM_ARGS];
named_arg_info<typename Context::char_type> named_args[NUM_NAMED_ARGS];
template <typename... T>
@@ -2280,13 +2338,13 @@ struct named_arg_store {
// An array of references to arguments. It can be implicitly converted to
// `basic_format_args` for passing into type-erased formatting functions
// such as `vformat`. It is a plain struct to reduce binary size in debug mode.
template <typename Context, size_t NUM_ARGS, size_t NUM_NAMED_ARGS,
template <typename Context, int NUM_ARGS, int NUM_NAMED_ARGS,
unsigned long long DESC>
struct format_arg_store {
// +1 to workaround a bug in gcc 7.5 that causes duplicated-branches warning.
using type =
conditional_t<NUM_NAMED_ARGS == 0,
arg_t<Context, NUM_ARGS>[NUM_ARGS != 0 ? NUM_ARGS : +1],
arg_t<Context, NUM_ARGS>[max_of(1, NUM_ARGS)],
named_arg_store<Context, NUM_ARGS, NUM_NAMED_ARGS, DESC>>;
type args;
};
@@ -2372,11 +2430,6 @@ template <typename T> class basic_appender {
detail::buffer<T>* container;
public:
using iterator_category = int;
using value_type = T;
using pointer = T*;
using reference = T&;
using difference_type = decltype(pointer() - pointer());
using container_type = detail::buffer<T>;
FMT_CONSTEXPR basic_appender(detail::buffer<T>& buf) : container(&buf) {}
@@ -2503,7 +2556,7 @@ template <typename Context> class basic_format_args {
return static_cast<detail::type>((desc_ >> shift) & mask);
}
template <size_t NUM_ARGS, size_t NUM_NAMED_ARGS, unsigned long long DESC>
template <int NUM_ARGS, int NUM_NAMED_ARGS, unsigned long long DESC>
using store =
detail::format_arg_store<Context, NUM_ARGS, NUM_NAMED_ARGS, DESC>;
@@ -2513,14 +2566,14 @@ template <typename Context> class basic_format_args {
constexpr basic_format_args() : desc_(0), args_(nullptr) {}
/// Constructs a `basic_format_args` object from `format_arg_store`.
template <size_t NUM_ARGS, size_t NUM_NAMED_ARGS, unsigned long long DESC,
template <int NUM_ARGS, int NUM_NAMED_ARGS, unsigned long long DESC,
FMT_ENABLE_IF(NUM_ARGS <= detail::max_packed_args)>
constexpr FMT_ALWAYS_INLINE basic_format_args(
const store<NUM_ARGS, NUM_NAMED_ARGS, DESC>& s)
: desc_(DESC | (NUM_NAMED_ARGS != 0 ? +detail::has_named_args_bit : 0)),
values_(s.args) {}
template <size_t NUM_ARGS, size_t NUM_NAMED_ARGS, unsigned long long DESC,
template <int NUM_ARGS, int NUM_NAMED_ARGS, unsigned long long DESC,
FMT_ENABLE_IF(NUM_ARGS > detail::max_packed_args)>
constexpr basic_format_args(const store<NUM_ARGS, NUM_NAMED_ARGS, DESC>& s)
: desc_(DESC | (NUM_NAMED_ARGS != 0 ? +detail::has_named_args_bit : 0)),
@@ -2571,10 +2624,11 @@ template <typename Context> class basic_format_args {
};
// A formatting context.
class context : private detail::locale_ref {
class context {
private:
appender out_;
format_args args_;
FMT_NO_UNIQUE_ADDRESS detail::locale_ref loc_;
public:
/// The character type for the output.
@@ -2590,24 +2644,26 @@ class context : private detail::locale_ref {
/// in the object so make sure they have appropriate lifetimes.
FMT_CONSTEXPR context(iterator out, format_args args,
detail::locale_ref loc = {})
: locale_ref(loc), out_(out), args_(args) {}
: out_(out), args_(args), loc_(loc) {}
context(context&&) = default;
context(const context&) = delete;
void operator=(const context&) = delete;
FMT_CONSTEXPR auto arg(int id) const -> format_arg { return args_.get(id); }
auto arg(string_view name) -> format_arg { return args_.get(name); }
FMT_CONSTEXPR auto arg_id(string_view name) -> int {
inline auto arg(string_view name) const -> format_arg {
return args_.get(name);
}
FMT_CONSTEXPR auto arg_id(string_view name) const -> int {
return args_.get_id(name);
}
// Returns an iterator to the beginning of the output range.
FMT_CONSTEXPR auto out() -> iterator { return out_; }
FMT_CONSTEXPR auto out() const -> iterator { return out_; }
// Advances the begin iterator to `it`.
void advance_to(iterator) {}
FMT_CONSTEXPR void advance_to(iterator) {}
FMT_CONSTEXPR auto locale() -> detail::locale_ref { return *this; }
FMT_CONSTEXPR auto locale() const -> detail::locale_ref { return loc_; }
};
template <typename Char = char> struct runtime_format_string {
@@ -2624,7 +2680,8 @@ template <typename Char = char> struct runtime_format_string {
*/
inline auto runtime(string_view s) -> runtime_format_string<> { return {{s}}; }
/// A compile-time format string.
/// A compile-time format string. Use `format_string` in the public API to
/// prevent type deduction.
template <typename... T> struct fstring {
private:
static constexpr int num_static_named_args =
@@ -2657,8 +2714,9 @@ template <typename... T> struct fstring {
template <typename S,
FMT_ENABLE_IF(std::is_convertible<const S&, string_view>::value)>
FMT_CONSTEVAL FMT_ALWAYS_INLINE fstring(const S& s) : str(s) {
auto sv = string_view(str);
if (FMT_USE_CONSTEVAL)
detail::parse_format_string<char>(s, checker(s, arg_pack()));
detail::parse_format_string<char>(sv, checker(sv, arg_pack()));
#ifdef FMT_ENFORCE_COMPILE_STRING
static_assert(
FMT_USE_CONSTEVAL && sizeof(s) != 0,
@@ -2711,8 +2769,8 @@ struct formatter<T, Char,
// Take arguments by lvalue references to avoid some lifetime issues, e.g.
// auto args = make_format_args(std::string());
template <typename Context = context, typename... T,
size_t NUM_ARGS = sizeof...(T),
size_t NUM_NAMED_ARGS = detail::count_named_args<T...>(),
int NUM_ARGS = sizeof...(T),
int NUM_NAMED_ARGS = detail::count_named_args<T...>(),
unsigned long long DESC = detail::make_descriptor<Context, T...>()>
constexpr FMT_ALWAYS_INLINE auto make_format_args(T&... args)
-> detail::format_arg_store<Context, NUM_ARGS, NUM_NAMED_ARGS, DESC> {
@@ -2851,7 +2909,7 @@ FMT_API void vprint_buffered(FILE* f, string_view fmt, format_args args);
template <typename... T>
FMT_INLINE void print(format_string<T...> fmt, T&&... args) {
vargs<T...> va = {{args...}};
if (!detail::use_utf8)
if (detail::const_check(!detail::use_utf8))
return detail::vprint_mojibake(stdout, fmt.str, va, false);
return detail::is_locking<T...>() ? vprint_buffered(stdout, fmt.str, va)
: vprint(fmt.str, va);
@@ -2868,7 +2926,8 @@ FMT_INLINE void print(format_string<T...> fmt, T&&... args) {
template <typename... T>
FMT_INLINE void print(FILE* f, format_string<T...> fmt, T&&... args) {
vargs<T...> va = {{args...}};
if (!detail::use_utf8) return detail::vprint_mojibake(f, fmt.str, va, false);
if (detail::const_check(!detail::use_utf8))
return detail::vprint_mojibake(f, fmt.str, va, false);
return detail::is_locking<T...>() ? vprint_buffered(f, fmt.str, va)
: vprint(f, fmt.str, va);
}
@@ -2878,8 +2937,9 @@ FMT_INLINE void print(FILE* f, format_string<T...> fmt, T&&... args) {
template <typename... T>
FMT_INLINE void println(FILE* f, format_string<T...> fmt, T&&... args) {
vargs<T...> va = {{args...}};
return detail::use_utf8 ? vprintln(f, fmt.str, va)
: detail::vprint_mojibake(f, fmt.str, va, true);
return detail::const_check(detail::use_utf8)
? vprintln(f, fmt.str, va)
: detail::vprint_mojibake(f, fmt.str, va, true);
}
/// Formats `args` according to specifications in `fmt` and writes the output

View File

@@ -444,7 +444,7 @@ struct is_same_arithmetic_type
std::is_floating_point<Rep2>::value)> {
};
inline void throw_duration_error() {
FMT_NORETURN inline void throw_duration_error() {
FMT_THROW(format_error("cannot format duration"));
}
@@ -540,24 +540,24 @@ inline auto localtime(std::time_t time) -> std::tm {
std::time_t time_;
std::tm tm_;
dispatcher(std::time_t t) : time_(t) {}
inline dispatcher(std::time_t t) : time_(t) {}
auto run() -> bool {
inline auto run() -> bool {
using namespace fmt::detail;
return handle(localtime_r(&time_, &tm_));
}
auto handle(std::tm* tm) -> bool { return tm != nullptr; }
inline auto handle(std::tm* tm) -> bool { return tm != nullptr; }
auto handle(detail::null<>) -> bool {
inline auto handle(detail::null<>) -> bool {
using namespace fmt::detail;
return fallback(localtime_s(&tm_, &time_));
}
auto fallback(int res) -> bool { return res == 0; }
inline auto fallback(int res) -> bool { return res == 0; }
#if !FMT_MSC_VERSION
auto fallback(detail::null<>) -> bool {
inline auto fallback(detail::null<>) -> bool {
using namespace fmt::detail;
std::tm* tm = std::localtime(&time_);
if (tm) tm_ = *tm;
@@ -591,24 +591,24 @@ inline auto gmtime(std::time_t time) -> std::tm {
std::time_t time_;
std::tm tm_;
dispatcher(std::time_t t) : time_(t) {}
inline dispatcher(std::time_t t) : time_(t) {}
auto run() -> bool {
inline auto run() -> bool {
using namespace fmt::detail;
return handle(gmtime_r(&time_, &tm_));
}
auto handle(std::tm* tm) -> bool { return tm != nullptr; }
inline auto handle(std::tm* tm) -> bool { return tm != nullptr; }
auto handle(detail::null<>) -> bool {
inline auto handle(detail::null<>) -> bool {
using namespace fmt::detail;
return fallback(gmtime_s(&tm_, &time_));
}
auto fallback(int res) -> bool { return res == 0; }
inline auto fallback(int res) -> bool { return res == 0; }
#if !FMT_MSC_VERSION
auto fallback(detail::null<>) -> bool {
inline auto fallback(detail::null<>) -> bool {
std::tm* tm = std::gmtime(&time_);
if (tm) tm_ = *tm;
return tm != nullptr;
@@ -912,7 +912,9 @@ template <typename Derived> struct null_chrono_spec_handler {
};
struct tm_format_checker : null_chrono_spec_handler<tm_format_checker> {
FMT_NORETURN void unsupported() { FMT_THROW(format_error("no format")); }
FMT_NORETURN inline void unsupported() {
FMT_THROW(format_error("no format"));
}
template <typename Char>
FMT_CONSTEXPR void on_text(const Char*, const Char*) {}
@@ -1069,7 +1071,7 @@ void write_fractional_seconds(OutputIt& out, Duration d, int precision = -1) {
}
} else if (precision > 0) {
*out++ = '.';
leading_zeroes = (std::min)(leading_zeroes, precision);
leading_zeroes = min_of(leading_zeroes, precision);
int remaining = precision - leading_zeroes;
out = detail::fill_n(out, leading_zeroes, '0');
if (remaining < num_digits) {
@@ -1572,7 +1574,7 @@ class tm_writer {
struct chrono_format_checker : null_chrono_spec_handler<chrono_format_checker> {
bool has_precision_integral = false;
FMT_NORETURN void unsupported() { FMT_THROW(format_error("no date")); }
FMT_NORETURN inline void unsupported() { FMT_THROW(format_error("no date")); }
template <typename Char>
FMT_CONSTEXPR void on_text(const Char*, const Char*) {}
@@ -1693,14 +1695,14 @@ class get_locale {
bool has_locale_ = false;
public:
get_locale(bool localized, locale_ref loc) : has_locale_(localized) {
inline get_locale(bool localized, locale_ref loc) : has_locale_(localized) {
if (localized)
::new (&locale_) std::locale(loc.template get<std::locale>());
}
~get_locale() {
inline ~get_locale() {
if (has_locale_) locale_.~locale();
}
operator const std::locale&() const {
inline operator const std::locale&() const {
return has_locale_ ? locale_ : get_classic_locale();
}
};

View File

@@ -42,11 +42,10 @@ struct is_compiled_string : std::is_base_of<compiled_string, S> {};
#endif
#if FMT_USE_NONTYPE_TEMPLATE_ARGS
template <typename Char, size_t N,
fmt::detail_exported::fixed_string<Char, N> Str>
template <typename Char, size_t N, fmt::detail::fixed_string<Char, N> Str>
struct udl_compiled_string : compiled_string {
using char_type = Char;
explicit constexpr operator basic_string_view<char_type>() const {
constexpr explicit operator basic_string_view<char_type>() const {
return {Str.data, N - 1};
}
};
@@ -525,9 +524,9 @@ FMT_CONSTEXPR20 auto formatted_size(const S& fmt, const Args&... args)
template <typename S, typename... Args,
FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
void print(std::FILE* f, const S& fmt, const Args&... args) {
memory_buffer buffer;
fmt::format_to(std::back_inserter(buffer), fmt, args...);
detail::print(f, {buffer.data(), buffer.size()});
auto buf = memory_buffer();
fmt::format_to(appender(buf), fmt, args...);
detail::print(f, {buf.data(), buf.size()});
}
template <typename S, typename... Args,
@@ -538,7 +537,7 @@ void print(const S& fmt, const Args&... args) {
#if FMT_USE_NONTYPE_TEMPLATE_ARGS
inline namespace literals {
template <detail_exported::fixed_string Str> constexpr auto operator""_cf() {
template <detail::fixed_string Str> constexpr auto operator""_cf() {
using char_t = remove_cvref_t<decltype(Str.data[0])>;
return detail::udl_compiled_string<char_t, sizeof(Str.data) / sizeof(char_t),
Str>();

View File

@@ -26,6 +26,10 @@
# include <locale>
#endif
#ifndef FMT_FUNC
# define FMT_FUNC
#endif
FMT_BEGIN_NAMESPACE
namespace detail {
@@ -59,8 +63,8 @@ FMT_FUNC void format_error_code(detail::buffer<char>& out, int error_code,
FMT_ASSERT(out.size() <= inline_buffer_size, "");
}
FMT_FUNC void report_error(format_func func, int error_code,
const char* message) noexcept {
FMT_FUNC void do_report_error(format_func func, int error_code,
const char* message) noexcept {
memory_buffer full_message;
func(full_message, error_code, message);
// Don't use fwrite_all because the latter may throw.
@@ -80,7 +84,7 @@ using std::locale;
using std::numpunct;
using std::use_facet;
template <typename Locale>
template <typename Locale, enable_if_t<(sizeof(Locale::collate) != 0), int>>
locale_ref::locale_ref(const Locale& loc) : locale_(&loc) {
static_assert(std::is_same<Locale, locale>::value, "");
}
@@ -130,7 +134,9 @@ FMT_FUNC auto write_loc(appender out, loc_value value,
FMT_FUNC void report_error(const char* message) {
#if FMT_USE_EXCEPTIONS
throw format_error(message);
// Use FMT_THROW instead of throw to avoid bogus unreachable code warnings
// from MSVC.
FMT_THROW(format_error(message));
#else
fputs(message, stderr);
abort();
@@ -1430,7 +1436,7 @@ FMT_FUNC void format_system_error(detail::buffer<char>& out, int error_code,
FMT_FUNC void report_system_error(int error_code,
const char* message) noexcept {
report_error(format_system_error, error_code, message);
do_report_error(format_system_error, error_code, message);
}
FMT_FUNC auto vformat(string_view fmt, format_args args) -> std::string {

File diff suppressed because it is too large Load Diff

View File

@@ -176,24 +176,24 @@ class buffered_file {
friend class file;
explicit buffered_file(FILE* f) : file_(f) {}
inline explicit buffered_file(FILE* f) : file_(f) {}
public:
buffered_file(const buffered_file&) = delete;
void operator=(const buffered_file&) = delete;
// Constructs a buffered_file object which doesn't represent any file.
buffered_file() noexcept : file_(nullptr) {}
inline buffered_file() noexcept : file_(nullptr) {}
// Destroys the object closing the file it represents if any.
FMT_API ~buffered_file() noexcept;
public:
buffered_file(buffered_file&& other) noexcept : file_(other.file_) {
inline buffered_file(buffered_file&& other) noexcept : file_(other.file_) {
other.file_ = nullptr;
}
auto operator=(buffered_file&& other) -> buffered_file& {
inline auto operator=(buffered_file&& other) -> buffered_file& {
close();
file_ = other.file_;
other.file_ = nullptr;
@@ -207,7 +207,7 @@ class buffered_file {
FMT_API void close();
// Returns the pointer to a FILE object representing this file.
auto get() const noexcept -> FILE* { return file_; }
inline auto get() const noexcept -> FILE* { return file_; }
FMT_API auto descriptor() const -> int;
@@ -248,7 +248,7 @@ class FMT_API file {
};
// Constructs a file object which doesn't represent any file.
file() noexcept : fd_(-1) {}
inline file() noexcept : fd_(-1) {}
// Opens a file and constructs a file object representing this file.
file(cstring_view path, int oflag);
@@ -257,10 +257,10 @@ class FMT_API file {
file(const file&) = delete;
void operator=(const file&) = delete;
file(file&& other) noexcept : fd_(other.fd_) { other.fd_ = -1; }
inline file(file&& other) noexcept : fd_(other.fd_) { other.fd_ = -1; }
// Move assignment is not noexcept because close may throw.
auto operator=(file&& other) -> file& {
inline auto operator=(file&& other) -> file& {
close();
fd_ = other.fd_;
other.fd_ = -1;
@@ -271,7 +271,7 @@ class FMT_API file {
~file() noexcept;
// Returns the file descriptor.
auto descriptor() const noexcept -> int { return fd_; }
inline auto descriptor() const noexcept -> int { return fd_; }
// Closes the file.
void close();
@@ -324,9 +324,9 @@ auto getpagesize() -> long;
namespace detail {
struct buffer_size {
buffer_size() = default;
constexpr buffer_size() = default;
size_t value = 0;
auto operator=(size_t val) const -> buffer_size {
FMT_CONSTEXPR auto operator=(size_t val) const -> buffer_size {
auto bs = buffer_size();
bs.value = val;
return bs;
@@ -337,7 +337,7 @@ struct ostream_params {
int oflag = file::WRONLY | file::CREATE | file::TRUNC;
size_t buffer_size = BUFSIZ > 32768 ? BUFSIZ : 32768;
ostream_params() {}
constexpr ostream_params() {}
template <typename... T>
ostream_params(T... params, int new_oflag) : ostream_params(params...) {
@@ -381,7 +381,7 @@ class FMT_API ostream : private detail::buffer<char> {
return buf;
}
void flush() {
inline void flush() {
if (size() == 0) return;
file_.write(data(), size() * sizeof(data()[0]));
clear();
@@ -390,7 +390,7 @@ class FMT_API ostream : private detail::buffer<char> {
template <typename... T>
friend auto output_file(cstring_view path, T... params) -> ostream;
void close() {
inline void close() {
flush();
file_.close();
}

View File

@@ -22,6 +22,14 @@
#include "chrono.h" // formatbuf
#ifdef _MSVC_STL_UPDATE
# define FMT_MSVC_STL_UPDATE _MSVC_STL_UPDATE
#elif defined(_MSC_VER) && _MSC_VER < 1912 // VS 15.5
# define FMT_MSVC_STL_UPDATE _MSVC_LANG
#else
# define FMT_MSVC_STL_UPDATE 0
#endif
FMT_BEGIN_NAMESPACE
namespace detail {
@@ -35,7 +43,7 @@ class file_access {
friend auto get_file(BufType& obj) -> FILE* { return obj.*FileMemberPtr; }
};
#if FMT_MSC_VERSION
#if FMT_MSVC_STL_UPDATE
template class file_access<file_access_tag, std::filebuf,
&std::filebuf::_Myfile>;
auto get_file(std::filebuf&) -> FILE*;
@@ -109,7 +117,7 @@ inline void vprint(std::ostream& os, string_view fmt, format_args args) {
auto buffer = memory_buffer();
detail::vformat_to(buffer, fmt, args);
FILE* f = nullptr;
#if FMT_MSC_VERSION && FMT_USE_RTTI
#if FMT_MSVC_STL_UPDATE && FMT_USE_RTTI
if (auto* buf = dynamic_cast<std::filebuf*>(os.rdbuf()))
f = detail::get_file(*buf);
#elif defined(_WIN32) && defined(__GLIBCXX__) && FMT_USE_RTTI

View File

@@ -79,7 +79,7 @@ template <bool IsSigned> struct int_checker {
unsigned max = to_unsigned(max_value<int>());
return value <= max;
}
static auto fits_in_int(bool) -> bool { return true; }
inline static auto fits_in_int(bool) -> bool { return true; }
};
template <> struct int_checker<true> {
@@ -87,7 +87,7 @@ template <> struct int_checker<true> {
return value >= (std::numeric_limits<int>::min)() &&
value <= max_value<int>();
}
static auto fits_in_int(int) -> bool { return true; }
inline static auto fits_in_int(int) -> bool { return true; }
};
struct printf_precision_handler {
@@ -205,7 +205,7 @@ class printf_width_handler {
format_specs& specs_;
public:
explicit printf_width_handler(format_specs& specs) : specs_(specs) {}
inline explicit printf_width_handler(format_specs& specs) : specs_(specs) {}
template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
auto operator()(T value) -> unsigned {

View File

@@ -357,12 +357,9 @@ template <typename R>
using maybe_const_range =
conditional_t<has_const_begin_end<R>::value, const R, R>;
// Workaround a bug in MSVC 2015 and earlier.
#if !FMT_MSC_VERSION || FMT_MSC_VERSION >= 1910
template <typename R, typename Char>
struct is_formattable_delayed
: is_formattable<uncvref_type<maybe_const_range<R>>, Char> {};
#endif
} // namespace detail
template <typename...> struct conjunction : std::true_type {};
@@ -498,13 +495,8 @@ struct formatter<
range_format_kind<R, Char>::value != range_format::disabled &&
range_format_kind<R, Char>::value != range_format::map &&
range_format_kind<R, Char>::value != range_format::string &&
range_format_kind<R, Char>::value != range_format::debug_string>
// Workaround a bug in MSVC 2015 and earlier.
#if !FMT_MSC_VERSION || FMT_MSC_VERSION >= 1910
,
detail::is_formattable_delayed<R, Char>
#endif
>::value>> {
range_format_kind<R, Char>::value != range_format::debug_string>,
detail::is_formattable_delayed<R, Char>>::value>> {
private:
using range_type = detail::maybe_const_range<R>;
range_formatter<detail::uncvref_type<range_type>, Char> range_formatter_;
@@ -646,9 +638,9 @@ struct formatter<join_view<It, Sentinel, Char>, Char> {
#endif
formatter<remove_cvref_t<value_type>, Char> value_formatter_;
using view_ref = conditional_t<std::is_copy_constructible<It>::value,
const join_view<It, Sentinel, Char>&,
join_view<It, Sentinel, Char>&&>;
using view = conditional_t<std::is_copy_constructible<It>::value,
const join_view<It, Sentinel, Char>,
join_view<It, Sentinel, Char>>;
public:
using nonlocking = void;
@@ -658,9 +650,10 @@ struct formatter<join_view<It, Sentinel, Char>, Char> {
}
template <typename FormatContext>
auto format(view_ref& value, FormatContext& ctx) const
-> decltype(ctx.out()) {
auto it = std::forward<view_ref>(value).begin;
auto format(view& value, FormatContext& ctx) const -> decltype(ctx.out()) {
using iter =
conditional_t<std::is_copy_constructible<view>::value, It, It&>;
iter it = value.begin;
auto out = ctx.out();
if (it == value.end) return out;
out = value_formatter_.format(*it, ctx);
@@ -675,39 +668,11 @@ struct formatter<join_view<It, Sentinel, Char>, Char> {
}
};
/// Returns a view that formats the iterator range `[begin, end)` with elements
/// separated by `sep`.
template <typename It, typename Sentinel>
auto join(It begin, Sentinel end, string_view sep) -> join_view<It, Sentinel> {
return {std::move(begin), end, sep};
}
/**
* Returns a view that formats `range` with elements separated by `sep`.
*
* **Example**:
*
* auto v = std::vector<int>{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
*/
template <typename Range>
auto join(Range&& r, string_view sep)
-> join_view<decltype(detail::range_begin(r)),
decltype(detail::range_end(r))> {
return {detail::range_begin(r), detail::range_end(r), sep};
}
template <typename Char, typename... T> struct tuple_join_view : detail::view {
const std::tuple<T...>& tuple;
template <typename Char, typename Tuple> struct tuple_join_view : detail::view {
const Tuple& tuple;
basic_string_view<Char> sep;
tuple_join_view(const std::tuple<T...>& t, basic_string_view<Char> s)
tuple_join_view(const Tuple& t, basic_string_view<Char> s)
: tuple(t), sep{s} {}
};
@@ -718,21 +683,22 @@ template <typename Char, typename... T> struct tuple_join_view : detail::view {
# define FMT_TUPLE_JOIN_SPECIFIERS 0
#endif
template <typename Char, typename... T>
struct formatter<tuple_join_view<Char, T...>, Char> {
template <typename Char, typename Tuple>
struct formatter<tuple_join_view<Char, Tuple>, Char,
enable_if_t<is_tuple_like<Tuple>::value>> {
FMT_CONSTEXPR auto parse(parse_context<Char>& ctx) -> const Char* {
return do_parse(ctx, std::integral_constant<size_t, sizeof...(T)>());
return do_parse(ctx, std::tuple_size<Tuple>());
}
template <typename FormatContext>
auto format(const tuple_join_view<Char, T...>& value,
auto format(const tuple_join_view<Char, Tuple>& value,
FormatContext& ctx) const -> typename FormatContext::iterator {
return do_format(value, ctx,
std::integral_constant<size_t, sizeof...(T)>());
return do_format(value, ctx, std::tuple_size<Tuple>());
}
private:
std::tuple<formatter<typename std::decay<T>::type, Char>...> formatters_;
decltype(detail::tuple::get_formatters<Tuple, Char>(
detail::tuple_index_sequence<Tuple>())) formatters_;
FMT_CONSTEXPR auto do_parse(parse_context<Char>& ctx,
std::integral_constant<size_t, 0>)
@@ -746,7 +712,7 @@ struct formatter<tuple_join_view<Char, T...>, Char> {
-> const Char* {
auto end = ctx.begin();
#if FMT_TUPLE_JOIN_SPECIFIERS
end = std::get<sizeof...(T) - N>(formatters_).parse(ctx);
end = std::get<std::tuple_size<Tuple>::value - N>(formatters_).parse(ctx);
if (N > 1) {
auto end1 = do_parse(ctx, std::integral_constant<size_t, N - 1>());
if (end != end1)
@@ -757,18 +723,20 @@ struct formatter<tuple_join_view<Char, T...>, Char> {
}
template <typename FormatContext>
auto do_format(const tuple_join_view<Char, T...>&, FormatContext& ctx,
auto do_format(const tuple_join_view<Char, Tuple>&, FormatContext& ctx,
std::integral_constant<size_t, 0>) const ->
typename FormatContext::iterator {
return ctx.out();
}
template <typename FormatContext, size_t N>
auto do_format(const tuple_join_view<Char, T...>& value, FormatContext& ctx,
auto do_format(const tuple_join_view<Char, Tuple>& value, FormatContext& ctx,
std::integral_constant<size_t, N>) const ->
typename FormatContext::iterator {
auto out = std::get<sizeof...(T) - N>(formatters_)
.format(std::get<sizeof...(T) - N>(value.tuple), ctx);
using std::get;
auto out =
std::get<std::tuple_size<Tuple>::value - N>(formatters_)
.format(get<std::tuple_size<Tuple>::value - N>(value.tuple), ctx);
if (N <= 1) return out;
out = detail::copy<Char>(value.sep, out);
ctx.advance_to(out);
@@ -816,6 +784,34 @@ struct formatter<
FMT_BEGIN_EXPORT
/// Returns a view that formats the iterator range `[begin, end)` with elements
/// separated by `sep`.
template <typename It, typename Sentinel>
auto join(It begin, Sentinel end, string_view sep) -> join_view<It, Sentinel> {
return {std::move(begin), end, sep};
}
/**
* Returns a view that formats `range` with elements separated by `sep`.
*
* **Example**:
*
* auto v = std::vector<int>{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
*/
template <typename Range, FMT_ENABLE_IF(!is_tuple_like<Range>::value)>
auto join(Range&& r, string_view sep)
-> join_view<decltype(detail::range_begin(r)),
decltype(detail::range_end(r))> {
return {detail::range_begin(r), detail::range_end(r), sep};
}
/**
* Returns an object that formats `std::tuple` with elements separated by `sep`.
*
@@ -825,9 +821,9 @@ FMT_BEGIN_EXPORT
* fmt::print("{}", fmt::join(t, ", "));
* // Output: 1, a
*/
template <typename... T>
FMT_CONSTEXPR auto join(const std::tuple<T...>& tuple, string_view sep)
-> tuple_join_view<char, T...> {
template <typename Tuple, FMT_ENABLE_IF(is_tuple_like<Tuple>::value)>
FMT_CONSTEXPR auto join(const Tuple& tuple, string_view sep)
-> tuple_join_view<char, Tuple> {
return {tuple, sep};
}

View File

@@ -27,7 +27,8 @@
// Check FMT_CPLUSPLUS to suppress a bogus warning in MSVC.
# if FMT_CPLUSPLUS >= 201703L
# if FMT_HAS_INCLUDE(<filesystem>)
# if FMT_HAS_INCLUDE(<filesystem>) && \
(!defined(FMT_CPP_LIB_FILESYSTEM) || FMT_CPP_LIB_FILESYSTEM != 0)
# include <filesystem>
# endif
# if FMT_HAS_INCLUDE(<variant>)
@@ -183,7 +184,8 @@ FMT_END_NAMESPACE
FMT_BEGIN_NAMESPACE
FMT_EXPORT
template <std::size_t N, typename Char>
struct formatter<std::bitset<N>, Char> : nested_formatter<string_view> {
struct formatter<std::bitset<N>, Char>
: nested_formatter<basic_string_view<Char>, Char> {
private:
// Functor because C++11 doesn't support generic lambdas.
struct writer {
@@ -203,7 +205,7 @@ struct formatter<std::bitset<N>, Char> : nested_formatter<string_view> {
template <typename FormatContext>
auto format(const std::bitset<N>& bs, FormatContext& ctx) const
-> decltype(ctx.out()) {
return write_padded(ctx, writer{bs});
return this->write_padded(ctx, writer{bs});
}
};
@@ -433,8 +435,8 @@ template <> struct formatter<std::error_code> {
}
template <typename FormatContext>
FMT_CONSTEXPR20 auto format(const std::error_code& ec, FormatContext& ctx) const
-> decltype(ctx.out()) {
FMT_CONSTEXPR20 auto format(const std::error_code& ec,
FormatContext& ctx) const -> decltype(ctx.out()) {
auto specs = specs_;
detail::handle_dynamic_spec(specs.dynamic_width(), specs.width, width_ref_,
ctx);
@@ -694,9 +696,7 @@ template <typename T, typename Char> struct formatter<std::complex<T>, Char> {
auto outer_specs = format_specs();
outer_specs.width = specs.width;
auto fill = specs.template fill<Char>();
if (fill)
outer_specs.set_fill(basic_string_view<Char>(fill, specs.fill_size()));
outer_specs.set_fill(specs);
outer_specs.set_align(specs.align());
specs.width = 0;

View File

@@ -140,7 +140,7 @@ auto join(It begin, Sentinel end, wstring_view sep)
return {begin, end, sep};
}
template <typename Range>
template <typename Range, FMT_ENABLE_IF(!is_tuple_like<Range>::value)>
auto join(Range&& range, wstring_view sep)
-> join_view<decltype(std::begin(range)), decltype(std::end(range)),
wchar_t> {
@@ -153,9 +153,9 @@ auto join(std::initializer_list<T> list, wstring_view sep)
return join(std::begin(list), std::end(list), sep);
}
template <typename... T>
auto join(const std::tuple<T...>& tuple, basic_string_view<wchar_t> sep)
-> tuple_join_view<wchar_t, T...> {
template <typename Tuple, FMT_ENABLE_IF(is_tuple_like<Tuple>::value)>
auto join(const Tuple& tuple, basic_string_view<wchar_t> sep)
-> tuple_join_view<wchar_t, Tuple> {
return {tuple, sep};
}
@@ -191,11 +191,9 @@ auto format(const S& fmt, T&&... args) -> std::basic_string<Char> {
fmt::make_format_args<buffered_context<Char>>(args...));
}
template <typename Locale, typename S,
typename Char = detail::format_string_char_t<S>,
FMT_ENABLE_IF(detail::is_locale<Locale>::value&&
detail::is_exotic_char<Char>::value)>
inline auto vformat(const Locale& loc, const S& fmt,
template <typename S, typename Char = detail::format_string_char_t<S>,
FMT_ENABLE_IF(detail::is_exotic_char<Char>::value)>
inline auto vformat(detail::locale_ref loc, const S& fmt,
typename detail::vformat_args<Char>::type args)
-> std::basic_string<Char> {
auto buf = basic_memory_buffer<Char>();
@@ -204,11 +202,10 @@ inline auto vformat(const Locale& loc, const S& fmt,
return {buf.data(), buf.size()};
}
template <typename Locale, typename S, typename... T,
template <typename S, typename... T,
typename Char = detail::format_string_char_t<S>,
FMT_ENABLE_IF(detail::is_locale<Locale>::value&&
detail::is_exotic_char<Char>::value)>
inline auto format(const Locale& loc, const S& fmt, T&&... args)
FMT_ENABLE_IF(detail::is_exotic_char<Char>::value)>
inline auto format(detail::locale_ref loc, const S& fmt, T&&... args)
-> std::basic_string<Char> {
return vformat(loc, detail::to_string_view(fmt),
fmt::make_format_args<buffered_context<Char>>(args...));
@@ -235,12 +232,11 @@ inline auto format_to(OutputIt out, const S& fmt, T&&... args) -> OutputIt {
fmt::make_format_args<buffered_context<Char>>(args...));
}
template <typename Locale, typename S, typename OutputIt, typename... Args,
template <typename S, typename OutputIt, typename... Args,
typename Char = detail::format_string_char_t<S>,
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value&&
detail::is_locale<Locale>::value&&
detail::is_exotic_char<Char>::value)>
inline auto vformat_to(OutputIt out, const Locale& loc, const S& fmt,
detail::is_exotic_char<Char>::value)>
inline auto vformat_to(OutputIt out, detail::locale_ref loc, const S& fmt,
typename detail::vformat_args<Char>::type args)
-> OutputIt {
auto&& buf = detail::get_buffer<Char>(out);
@@ -248,12 +244,11 @@ inline auto vformat_to(OutputIt out, const Locale& loc, const S& fmt,
return detail::get_iterator(buf, out);
}
template <typename OutputIt, typename Locale, typename S, typename... T,
template <typename OutputIt, typename S, typename... T,
typename Char = detail::format_string_char_t<S>,
bool enable = detail::is_output_iterator<OutputIt, Char>::value &&
detail::is_locale<Locale>::value &&
detail::is_exotic_char<Char>::value>
inline auto format_to(OutputIt out, const Locale& loc, const S& fmt,
inline auto format_to(OutputIt out, detail::locale_ref loc, const S& fmt,
T&&... args) ->
typename std::enable_if<enable, OutputIt>::type {
return vformat_to(out, loc, detail::to_string_view(fmt),