Merge branch 'master' of github.com:andreagen0r/fmt

This commit is contained in:
André Agenor 2022-09-05 22:51:57 -03:00
commit 2d415bb497
11 changed files with 237 additions and 134 deletions

View File

@ -69,9 +69,7 @@
# define FMT_HAS_FEATURE(x) 0 # define FMT_HAS_FEATURE(x) 0
#endif #endif
#if (defined(__has_include) || FMT_ICC_VERSION >= 1600 || \ #if defined(__has_include) || FMT_ICC_VERSION >= 1600 || FMT_MSC_VERSION > 1900
FMT_MSC_VERSION > 1900) && \
!defined(__INTELLISENSE__)
# define FMT_HAS_INCLUDE(x) __has_include(x) # define FMT_HAS_INCLUDE(x) __has_include(x)
#else #else
# define FMT_HAS_INCLUDE(x) 0 # define FMT_HAS_INCLUDE(x) 0
@ -332,7 +330,7 @@ struct monostate {
#ifdef FMT_DOC #ifdef FMT_DOC
# define FMT_ENABLE_IF(...) # define FMT_ENABLE_IF(...)
#else #else
# define FMT_ENABLE_IF(...) enable_if_t<(__VA_ARGS__), int> = 0 # define FMT_ENABLE_IF(...) fmt::enable_if_t<(__VA_ARGS__), int> = 0
#endif #endif
FMT_BEGIN_DETAIL_NAMESPACE FMT_BEGIN_DETAIL_NAMESPACE
@ -486,6 +484,18 @@ template <typename Char> class basic_string_view {
size_ -= n; size_ -= n;
} }
FMT_CONSTEXPR_CHAR_TRAITS bool starts_with(
basic_string_view<Char> sv) const noexcept {
return size_ >= sv.size_ &&
std::char_traits<Char>::compare(data_, sv.data_, sv.size_) == 0;
}
FMT_CONSTEXPR_CHAR_TRAITS bool starts_with(Char c) const noexcept {
return size_ >= 1 && std::char_traits<Char>::eq(*data_, c);
}
FMT_CONSTEXPR_CHAR_TRAITS bool starts_with(const Char* s) const {
return starts_with(basic_string_view<Char>(s));
}
// Lexicographically compare this string reference to other. // Lexicographically compare this string reference to other.
FMT_CONSTEXPR_CHAR_TRAITS auto compare(basic_string_view other) const -> int { FMT_CONSTEXPR_CHAR_TRAITS auto compare(basic_string_view other) const -> int {
size_t str_size = size_ < other.size_ ? size_ : other.size_; size_t str_size = size_ < other.size_ ? size_ : other.size_;

View File

@ -29,8 +29,6 @@
#include "format.h" #include "format.h"
FMT_BEGIN_NAMESPACE FMT_BEGIN_NAMESPACE
template <typename Locale> typename Locale::id num_format_facet<Locale>::id;
namespace detail { namespace detail {
FMT_FUNC void assert_fail(const char* file, int line, const char* message) { FMT_FUNC void assert_fail(const char* file, int line, const char* message) {
@ -118,22 +116,40 @@ template <typename Char> FMT_FUNC Char decimal_point_impl(locale_ref) {
} }
#endif #endif
FMT_FUNC auto write_int(appender out, loc_value value, FMT_FUNC auto write_loc(appender out, basic_format_arg<format_context> value,
const format_specs& specs, locale_ref loc) -> bool { const format_specs& specs, locale_ref loc) -> bool {
#ifndef FMT_STATIC_THOUSANDS_SEPARATOR #ifndef FMT_STATIC_THOUSANDS_SEPARATOR
auto locale = loc.get<std::locale>(); auto locale = loc.get<std::locale>();
// We cannot use the num_put<char> facet because it may produce output in // We cannot use the num_put<char> facet because it may produce output in
// a wrong encoding. // a wrong encoding.
if (!std::has_facet<num_format_facet<std::locale>>(locale)) return {}; using facet = format_facet<std::locale>;
std::use_facet<num_format_facet<std::locale>>(locale).put(out, value, specs, if (std::has_facet<facet>(locale))
locale); return std::use_facet<facet>(locale).put(out, value, specs);
return true; return facet(locale).put(out, value, specs);
#endif #endif
return false; return false;
} }
} // namespace detail } // namespace detail
template <typename Locale> typename Locale::id format_facet<Locale>::id;
#ifndef FMT_STATIC_THOUSANDS_SEPARATOR
template <typename Locale> format_facet<Locale>::format_facet(Locale& loc) {
auto& numpunct = std::use_facet<std::numpunct<char>>(loc);
grouping_ = numpunct.grouping();
if (!grouping_.empty()) separator_ = std::string(1, numpunct.thousands_sep());
}
template <>
FMT_API FMT_FUNC auto format_facet<std::locale>::do_put(
appender out, basic_format_arg<format_context> val,
const format_specs& specs) const -> bool {
return visit_format_arg(
detail::loc_writer<>{out, specs, separator_, grouping_, decimal_point_},
val);
}
#endif
#if !FMT_MSC_VERSION #if !FMT_MSC_VERSION
FMT_API FMT_FUNC format_error::~format_error() noexcept = default; FMT_API FMT_FUNC format_error::~format_error() noexcept = default;
#endif #endif

View File

@ -33,13 +33,14 @@
#ifndef FMT_FORMAT_H_ #ifndef FMT_FORMAT_H_
#define FMT_FORMAT_H_ #define FMT_FORMAT_H_
#include <cmath> // std::signbit #include <cmath> // std::signbit
#include <cstdint> // uint32_t #include <cstdint> // uint32_t
#include <cstring> // std::memcpy #include <cstring> // std::memcpy
#include <limits> // std::numeric_limits #include <initializer_list> // std::initializer_list
#include <memory> // std::uninitialized_copy #include <limits> // std::numeric_limits
#include <stdexcept> // std::runtime_error #include <memory> // std::uninitialized_copy
#include <system_error> // std::system_error #include <stdexcept> // std::runtime_error
#include <system_error> // std::system_error
#ifdef __cpp_lib_bit_cast #ifdef __cpp_lib_bit_cast
# include <bit> // std::bitcast # include <bit> // std::bitcast
@ -738,6 +739,21 @@ inline auto code_point_index(basic_string_view<char8_type> s, size_t n)
string_view(reinterpret_cast<const char*>(s.data()), s.size()), n); string_view(reinterpret_cast<const char*>(s.data()), s.size()), n);
} }
template <typename T> struct is_integral : std::is_integral<T> {};
template <> struct is_integral<int128_opt> : std::true_type {};
template <> struct is_integral<uint128_t> : std::true_type {};
template <typename T>
using is_signed =
std::integral_constant<bool, std::numeric_limits<T>::is_signed ||
std::is_same<T, int128_opt>::value>;
template <typename T>
using is_integer =
bool_constant<is_integral<T>::value && !std::is_same<T, bool>::value &&
!std::is_same<T, char>::value &&
!std::is_same<T, wchar_t>::value>;
#ifndef FMT_USE_FLOAT128 #ifndef FMT_USE_FLOAT128
# ifdef __SIZEOF_FLOAT128__ # ifdef __SIZEOF_FLOAT128__
# define FMT_USE_FLOAT128 1 # define FMT_USE_FLOAT128 1
@ -985,40 +1001,37 @@ constexpr auto compile_string_to_view(detail::std_string_view<Char> s)
} }
} // namespace detail_exported } // namespace detail_exported
// A value to localize. // A locale facet that formats values in UTF-8.
struct loc_value { // It is parameterized on the locale to avoid the heavy <locale> include.
union { template <typename Locale> class format_facet : public Locale::facet {
unsigned long long ulong_long_value; private:
}; std::string separator_;
}; std::string grouping_;
std::string decimal_point_;
protected:
virtual auto do_put(appender out, basic_format_arg<format_context> val,
const format_specs& specs) const -> bool;
// A locale facet that formats numeric values in UTF-8.
// It is parameterized on the locale to avoid heavy <locale> include.
template <typename Locale> class num_format_facet : public Locale::facet {
public: public:
static FMT_API typename Locale::id id; static FMT_API typename Locale::id id;
void put(appender out, loc_value val, const format_specs& specs, explicit format_facet(Locale& loc);
Locale& loc) const { explicit format_facet(string_view sep = "",
do_put(out, val, specs, loc); std::initializer_list<unsigned char> g = {3},
} std::string decimal_point = ".")
: separator_(sep.data(), sep.size()),
grouping_(g.begin(), g.end()),
decimal_point_(decimal_point) {}
protected: auto put(appender out, basic_format_arg<format_context> val,
virtual void do_put(appender out, loc_value val, const format_specs& specs, const format_specs& specs) const -> bool {
Locale& loc) const = 0; return do_put(out, val, specs);
}
}; };
FMT_BEGIN_DETAIL_NAMESPACE FMT_BEGIN_DETAIL_NAMESPACE
template <typename T> struct is_integral : std::is_integral<T> {};
template <> struct is_integral<int128_opt> : std::true_type {};
template <> struct is_integral<uint128_t> : std::true_type {};
template <typename T>
using is_signed =
std::integral_constant<bool, std::numeric_limits<T>::is_signed ||
std::is_same<T, int128_opt>::value>;
// Returns true if value is negative, false otherwise. // Returns true if value is negative, false otherwise.
// Same as `value < 0` but doesn't produce warnings if T is an unsigned type. // Same as `value < 0` but doesn't produce warnings if T is an unsigned type.
template <typename T, FMT_ENABLE_IF(is_signed<T>::value)> template <typename T, FMT_ENABLE_IF(is_signed<T>::value)>
@ -1956,19 +1969,19 @@ FMT_CONSTEXPR FMT_INLINE auto write_int(OutputIt out, int num_digits,
template <typename Char> class digit_grouping { template <typename Char> class digit_grouping {
private: private:
thousands_sep_result<Char> sep_; std::string grouping_;
std::basic_string<Char> thousands_sep_;
struct next_state { struct next_state {
std::string::const_iterator group; std::string::const_iterator group;
int pos; int pos;
}; };
next_state initial_state() const { return {sep_.grouping.begin(), 0}; } next_state initial_state() const { return {grouping_.begin(), 0}; }
// Returns the next digit group separator position. // Returns the next digit group separator position.
int next(next_state& state) const { int next(next_state& state) const {
if (!sep_.thousands_sep) return max_value<int>(); if (thousands_sep_.empty()) return max_value<int>();
if (state.group == sep_.grouping.end()) if (state.group == grouping_.end()) return state.pos += grouping_.back();
return state.pos += sep_.grouping.back();
if (*state.group <= 0 || *state.group == max_value<char>()) if (*state.group <= 0 || *state.group == max_value<char>())
return max_value<int>(); return max_value<int>();
state.pos += *state.group++; state.pos += *state.group++;
@ -1977,14 +1990,15 @@ template <typename Char> class digit_grouping {
public: public:
explicit digit_grouping(locale_ref loc, bool localized = true) { explicit digit_grouping(locale_ref loc, bool localized = true) {
if (localized) if (!localized) return;
sep_ = thousands_sep<Char>(loc); auto sep = thousands_sep<Char>(loc);
else grouping_ = sep.grouping;
sep_.thousands_sep = Char(); if (sep.thousands_sep) thousands_sep_.assign(1, sep.thousands_sep);
} }
explicit digit_grouping(thousands_sep_result<Char> sep) : sep_(sep) {} digit_grouping(std::string grouping, std::basic_string<Char> sep)
: grouping_(std::move(grouping)), thousands_sep_(std::move(sep)) {}
Char separator() const { return sep_.thousands_sep; } bool has_separator() const { return !thousands_sep_.empty(); }
int count_separators(int num_digits) const { int count_separators(int num_digits) const {
int count = 0; int count = 0;
@ -2007,7 +2021,9 @@ template <typename Char> class digit_grouping {
for (int i = 0, sep_index = static_cast<int>(separators.size() - 1); for (int i = 0, sep_index = static_cast<int>(separators.size() - 1);
i < num_digits; ++i) { i < num_digits; ++i) {
if (num_digits - i == separators[sep_index]) { if (num_digits - i == separators[sep_index]) {
*out++ = separator(); out =
copy_str<Char>(thousands_sep_.data(),
thousands_sep_.data() + thousands_sep_.size(), out);
--sep_index; --sep_index;
} }
*out++ = static_cast<Char>(digits[to_unsigned(i)]); *out++ = static_cast<Char>(digits[to_unsigned(i)]);
@ -2037,37 +2053,14 @@ auto write_int(OutputIt out, UInt value, unsigned prefix,
}); });
} }
FMT_API auto write_int(appender out, loc_value value, const format_specs& specs, // Writes localized value.
locale_ref loc) -> bool; FMT_API auto write_loc(appender out, basic_format_arg<format_context> value,
template <typename OutputIt, typename Char> const format_specs& specs, locale_ref loc) -> bool;
inline auto write_int(OutputIt, loc_value, const basic_format_specs<Char>&,
locale_ref) -> bool {
return false;
}
template <typename OutputIt, typename UInt, typename Char> template <typename OutputIt, typename Char>
auto write_int(OutputIt& out, UInt value, unsigned prefix, inline auto write_loc(OutputIt, basic_format_arg<buffer_context<Char>>,
const basic_format_specs<Char>& specs, locale_ref loc) -> bool { const basic_format_specs<Char>&, locale_ref) -> bool {
auto result = false; return false;
auto buf = memory_buffer();
if (sizeof(value) <= sizeof(unsigned long long))
result = write_int(appender(buf), {static_cast<unsigned long long>(value)},
specs, loc);
if (!result) {
auto grouping = digit_grouping<Char>(loc);
out = write_int(out, value, prefix, specs, grouping);
return true;
}
size_t size = to_unsigned((prefix != 0 ? 1 : 0) + buf.size());
out = write_padded<align::right>(
out, specs, size, size, [&](reserve_iterator<OutputIt> it) {
if (prefix != 0) {
char sign = static_cast<char>(prefix);
*it++ = static_cast<Char>(sign);
}
return copy_str<Char>(buf.data(), buf.data() + buf.size(), it);
});
return true;
} }
FMT_CONSTEXPR inline void prefix_append(unsigned& prefix, unsigned value) { FMT_CONSTEXPR inline void prefix_append(unsigned& prefix, unsigned value) {
@ -2096,20 +2089,39 @@ FMT_CONSTEXPR auto make_write_int_arg(T value, sign_t sign)
return {abs_value, prefix}; return {abs_value, prefix};
} }
template <typename Char = char> struct loc_writer {
buffer_appender<Char> out;
const basic_format_specs<Char>& specs;
std::basic_string<Char> sep;
std::string grouping;
std::basic_string<Char> decimal_point;
template <typename T, FMT_ENABLE_IF(is_integer<T>::value)>
auto operator()(T value) -> bool {
auto arg = make_write_int_arg(value, specs.sign);
write_int(out, static_cast<uint64_or_128_t<T>>(arg.abs_value), arg.prefix,
specs, digit_grouping<Char>(grouping, sep));
return true;
}
template <typename T, FMT_ENABLE_IF(is_floating_point<T>::value)>
auto operator()(T) -> bool {
return false;
}
auto operator()(...) -> bool { return false; }
};
template <typename Char, typename OutputIt, typename T> template <typename Char, typename OutputIt, typename T>
FMT_CONSTEXPR FMT_INLINE auto write_int(OutputIt out, write_int_arg<T> arg, FMT_CONSTEXPR FMT_INLINE auto write_int(OutputIt out, write_int_arg<T> arg,
const basic_format_specs<Char>& specs, const basic_format_specs<Char>& specs,
locale_ref loc) -> OutputIt { locale_ref) -> OutputIt {
static_assert(std::is_same<T, uint32_or_64_or_128_t<T>>::value, ""); static_assert(std::is_same<T, uint32_or_64_or_128_t<T>>::value, "");
auto abs_value = arg.abs_value; auto abs_value = arg.abs_value;
auto prefix = arg.prefix; auto prefix = arg.prefix;
switch (specs.type) { switch (specs.type) {
case presentation_type::none: case presentation_type::none:
case presentation_type::dec: { case presentation_type::dec: {
if (specs.localized &&
write_int(out, static_cast<uint64_or_128_t<T>>(abs_value), prefix,
specs, loc))
return out;
auto num_digits = count_digits(abs_value); auto num_digits = count_digits(abs_value);
return write_int( return write_int(
out, num_digits, prefix, specs, [=](reserve_iterator<OutputIt> it) { out, num_digits, prefix, specs, [=](reserve_iterator<OutputIt> it) {
@ -2169,6 +2181,10 @@ template <typename Char, typename OutputIt, typename T,
FMT_CONSTEXPR FMT_INLINE auto write(OutputIt out, T value, FMT_CONSTEXPR FMT_INLINE auto write(OutputIt out, T value,
const basic_format_specs<Char>& specs, const basic_format_specs<Char>& specs,
locale_ref loc) -> OutputIt { locale_ref loc) -> OutputIt {
if (specs.localized &&
write_loc(out, make_arg<buffer_context<Char>>(value), specs, loc)) {
return out;
}
return write_int_noinline(out, make_write_int_arg(value, specs.sign), specs, return write_int_noinline(out, make_write_int_arg(value, specs.sign), specs,
loc); loc);
} }
@ -2180,6 +2196,10 @@ template <typename Char, typename OutputIt, typename T,
FMT_CONSTEXPR FMT_INLINE auto write(OutputIt out, T value, FMT_CONSTEXPR FMT_INLINE auto write(OutputIt out, T value,
const basic_format_specs<Char>& specs, const basic_format_specs<Char>& specs,
locale_ref loc) -> OutputIt { locale_ref loc) -> OutputIt {
if (specs.localized &&
write_loc(out, make_arg<buffer_context<Char>>(value), specs, loc)) {
return out;
}
return write_int(out, make_write_int_arg(value, specs.sign), specs, loc); return write_int(out, make_write_int_arg(value, specs.sign), specs, loc);
} }
@ -2331,7 +2351,7 @@ template <typename Char, typename OutputIt, typename T, typename Grouping>
FMT_CONSTEXPR20 auto write_significand(OutputIt out, T significand, FMT_CONSTEXPR20 auto write_significand(OutputIt out, T significand,
int significand_size, int exponent, int significand_size, int exponent,
const Grouping& grouping) -> OutputIt { const Grouping& grouping) -> OutputIt {
if (!grouping.separator()) { if (!grouping.has_separator()) {
out = write_significand<Char>(out, significand, significand_size); out = write_significand<Char>(out, significand, significand_size);
return detail::fill_n(out, exponent, static_cast<Char>('0')); return detail::fill_n(out, exponent, static_cast<Char>('0'));
} }
@ -2393,7 +2413,7 @@ FMT_CONSTEXPR20 auto write_significand(OutputIt out, T significand,
int significand_size, int integral_size, int significand_size, int integral_size,
Char decimal_point, Char decimal_point,
const Grouping& grouping) -> OutputIt { const Grouping& grouping) -> OutputIt {
if (!grouping.separator()) { if (!grouping.has_separator()) {
return write_significand(out, significand, significand_size, integral_size, return write_significand(out, significand, significand_size, integral_size,
decimal_point); decimal_point);
} }
@ -2515,7 +2535,7 @@ template <typename Char> class fallback_digit_grouping {
public: public:
constexpr fallback_digit_grouping(locale_ref, bool) {} constexpr fallback_digit_grouping(locale_ref, bool) {}
constexpr Char separator() const { return Char(); } constexpr bool has_separator() const { return false; }
constexpr int count_separators(int) const { return 0; } constexpr int count_separators(int) const { return 0; }
@ -3460,12 +3480,6 @@ template <typename Char> struct custom_formatter {
template <typename T> void operator()(T) const {} template <typename T> void operator()(T) const {}
}; };
template <typename T>
using is_integer =
bool_constant<is_integral<T>::value && !std::is_same<T, bool>::value &&
!std::is_same<T, char>::value &&
!std::is_same<T, wchar_t>::value>;
template <typename ErrorHandler> class width_checker { template <typename ErrorHandler> class width_checker {
public: public:
explicit FMT_CONSTEXPR width_checker(ErrorHandler& eh) : handler_(eh) {} explicit FMT_CONSTEXPR width_checker(ErrorHandler& eh) : handler_(eh) {}
@ -3963,7 +3977,7 @@ template <typename T> struct formatter<group_digits_view<T>> : formatter<T> {
specs_.precision, specs_.precision_ref, ctx); specs_.precision, specs_.precision_ref, ctx);
return detail::write_int( return detail::write_int(
ctx.out(), static_cast<detail::uint64_or_128_t<T>>(t.value), 0, specs_, ctx.out(), static_cast<detail::uint64_or_128_t<T>>(t.value), 0, specs_,
detail::digit_grouping<char>({"\3", ','})); detail::digit_grouping<char>("\3", ","));
} }
}; };

View File

@ -211,16 +211,16 @@ class is_tuple_formattable_ {
static constexpr const bool value = false; static constexpr const bool value = false;
}; };
template <typename T, typename C> class is_tuple_formattable_<T, C, true> { template <typename T, typename C> class is_tuple_formattable_<T, C, true> {
template <std::size_t... I> template <std::size_t... Is>
static std::true_type check2(index_sequence<I...>, static std::true_type check2(index_sequence<Is...>,
integer_sequence<bool, (I == I)...>); integer_sequence<bool, (Is == Is)...>);
static std::false_type check2(...); static std::false_type check2(...);
template <std::size_t... I> template <std::size_t... Is>
static decltype(check2( static decltype(check2(
index_sequence<I...>{}, index_sequence<Is...>{},
integer_sequence< integer_sequence<
bool, (is_formattable<typename std::tuple_element<I, T>::type, bool, (is_formattable<typename std::tuple_element<Is, T>::type,
C>::value)...>{})) check(index_sequence<I...>); C>::value)...>{})) check(index_sequence<Is...>);
public: public:
static constexpr const bool value = static constexpr const bool value =

View File

@ -101,19 +101,16 @@ template <typename T>
using variant_index_sequence = using variant_index_sequence =
std::make_index_sequence<std::variant_size<T>::value>; std::make_index_sequence<std::variant_size<T>::value>;
// variant_size and variant_alternative check. template <typename> struct is_variant_like_ : std::false_type {};
template <typename T, typename U = void> template <typename... Types>
struct is_variant_like_ : std::false_type {}; struct is_variant_like_<std::variant<Types...>> : std::true_type {};
template <typename T>
struct is_variant_like_<T, std::void_t<decltype(std::variant_size<T>::value)>>
: std::true_type {};
// formattable element check // formattable element check.
template <typename T, typename C> class is_variant_formattable_ { template <typename T, typename C> class is_variant_formattable_ {
template <std::size_t... I> template <std::size_t... Is>
static std::conjunction< static std::conjunction<
is_formattable<std::variant_alternative_t<I, T>, C>...> is_formattable<std::variant_alternative_t<Is, T>, C>...>
check(std::index_sequence<I...>); check(std::index_sequence<Is...>);
public: public:
static constexpr const bool value = static constexpr const bool value =

View File

@ -12,11 +12,32 @@
#include "format.h" #include "format.h"
#ifndef FMT_STATIC_THOUSANDS_SEPARATOR
# include <locale>
#endif
FMT_BEGIN_NAMESPACE FMT_BEGIN_NAMESPACE
namespace detail { namespace detail {
template <typename T> template <typename T>
using is_exotic_char = bool_constant<!std::is_same<T, char>::value>; using is_exotic_char = bool_constant<!std::is_same<T, char>::value>;
template <typename OutputIt>
auto write_loc(OutputIt out, basic_format_arg<buffer_context<wchar_t>> val,
const basic_format_specs<wchar_t>& specs, locale_ref loc)
-> bool {
#ifndef FMT_STATIC_THOUSANDS_SEPARATOR
auto& numpunct =
std::use_facet<std::numpunct<wchar_t>>(loc.get<std::locale>());
auto separator = std::wstring();
auto grouping = numpunct.grouping();
if (!grouping.empty()) separator = std::wstring(1, numpunct.thousands_sep());
return visit_format_arg(
loc_writer<wchar_t>{out, specs, separator, grouping, {}}, val);
#endif
return false;
} }
} // namespace detail
FMT_MODULE_EXPORT_BEGIN FMT_MODULE_EXPORT_BEGIN

View File

@ -9,9 +9,7 @@
#include "test-assert.h" #include "test-assert.h"
// clang-format on // clang-format on
#define I 42 // simulate https://en.cppreference.com/w/c/numeric/complex/I
#include "fmt/core.h" #include "fmt/core.h"
#undef I
#include <algorithm> // std::copy_n #include <algorithm> // std::copy_n
#include <climits> // INT_MAX #include <climits> // INT_MAX
@ -72,6 +70,16 @@ TEST(string_view_test, compare) {
EXPECT_LT(string_view("foo").compare(string_view("fop")), 0); EXPECT_LT(string_view("foo").compare(string_view("fop")), 0);
EXPECT_GT(string_view("foo").compare(string_view("fo")), 0); EXPECT_GT(string_view("foo").compare(string_view("fo")), 0);
EXPECT_LT(string_view("fo").compare(string_view("foo")), 0); EXPECT_LT(string_view("fo").compare(string_view("foo")), 0);
EXPECT_TRUE(string_view("foo").starts_with('f'));
EXPECT_FALSE(string_view("foo").starts_with('o'));
EXPECT_FALSE(string_view().starts_with('o'));
EXPECT_TRUE(string_view("foo").starts_with("fo"));
EXPECT_TRUE(string_view("foo").starts_with("foo"));
EXPECT_FALSE(string_view("foo").starts_with("fooo"));
EXPECT_FALSE(string_view().starts_with("fooo"));
check_op<std::equal_to>(); check_op<std::equal_to>();
check_op<std::not_equal_to>(); check_op<std::not_equal_to>();
check_op<std::less>(); check_op<std::less>();

View File

@ -8,12 +8,14 @@
#include <iterator> #include <iterator>
#include <vector> #include <vector>
#define I 42 // simulate https://en.cppreference.com/w/c/numeric/complex/I
#include "fmt/chrono.h" #include "fmt/chrono.h"
#include "fmt/color.h" #include "fmt/color.h"
#include "fmt/format.h" #include "fmt/format.h"
#include "fmt/ostream.h" #include "fmt/ostream.h"
#include "fmt/ranges.h" #include "fmt/ranges.h"
#include "fmt/xchar.h" #include "fmt/xchar.h"
#undef I
// Exercise the API to verify that everything we expect to can compile. // Exercise the API to verify that everything we expect to can compile.
void test_format_api() { void test_format_api() {

View File

@ -2312,25 +2312,57 @@ TEST(format_int_test, format_int) {
EXPECT_EQ(os.str(), fmt::format_int(max_value<int64_t>()).str()); EXPECT_EQ(os.str(), fmt::format_int(max_value<int64_t>()).str());
} }
#ifdef FMT_STATIC_THOUSANDS_SEPARATOR #ifndef FMT_STATIC_THOUSANDS_SEPARATOR
# include <locale> # include <locale>
class num_format : public fmt::num_format_facet<std::locale> { class format_facet : public fmt::format_facet<std::locale> {
protected: protected:
void do_put(fmt::appender out, fmt::loc_value, const fmt::format_specs&, struct int_formatter {
std::locale&) const override; fmt::appender out;
template <typename T, FMT_ENABLE_IF(fmt::detail::is_integer<T>::value)>
auto operator()(T value) -> bool {
fmt::format_to(out, "[{}]", value);
return true;
}
template <typename T, FMT_ENABLE_IF(!fmt::detail::is_integer<T>::value)>
auto operator()(T) -> bool {
return false;
}
};
auto do_put(fmt::appender out, fmt::basic_format_arg<fmt::format_context> arg,
const fmt::format_specs&) const -> bool override;
}; };
void num_format::do_put(fmt::appender out, fmt::loc_value value, auto format_facet::do_put(fmt::appender out,
const fmt::format_specs&, std::locale&) const { fmt::basic_format_arg<fmt::format_context> arg,
fmt::format_to(out, "[{}]", value.ulong_long_value); const fmt::format_specs&) const -> bool {
return visit_format_arg(int_formatter{out}, arg);
} }
TEST(format_test, num_format) { TEST(format_test, format_facet) {
auto loc = std::locale(std::locale(), new num_format()); auto loc = std::locale(std::locale(), new format_facet());
EXPECT_EQ(fmt::format(loc, "{:L}", 42), "[42]"); EXPECT_EQ(fmt::format(loc, "{:L}", 42), "[42]");
EXPECT_EQ(fmt::format(loc, "{:L}", -42), "[-42]"); EXPECT_EQ(fmt::format(loc, "{:L}", -42), "[-42]");
} }
TEST(format_test, format_facet_separator) {
// U+2019 RIGHT SINGLE QUOTATION MARK is a digit separator in the de_CH
// locale.
auto loc =
std::locale({}, new fmt::format_facet<std::locale>("\xe2\x80\x99"));
EXPECT_EQ(fmt::format(loc, "{:L}", 1000),
"1\xe2\x80\x99"
"000");
}
TEST(format_test, format_facet_grouping) {
auto loc =
std::locale({}, new fmt::format_facet<std::locale>(",", {1, 2, 3}));
EXPECT_EQ(fmt::format(loc, "{:L}", 1234567890), "1,234,567,89,0");
}
#endif // FMT_STATIC_THOUSANDS_SEPARATOR #endif // FMT_STATIC_THOUSANDS_SEPARATOR

View File

@ -75,6 +75,9 @@ TEST(std_test, variant) {
EXPECT_EQ(fmt::format("{}", v4), "variant(monostate)"); EXPECT_EQ(fmt::format("{}", v4), "variant(monostate)");
EXPECT_EQ(fmt::format("{}", v5), "variant(\"yes, this is variant\")"); EXPECT_EQ(fmt::format("{}", v5), "variant(\"yes, this is variant\")");
volatile int i = 42; // Test compile error before GCC 11 described in #3068.
EXPECT_EQ(fmt::format("{}", i), "42");
#endif #endif
} }

View File

@ -448,11 +448,11 @@ TEST(locale_test, int_formatter) {
auto f = fmt::formatter<int>(); auto f = fmt::formatter<int>();
auto parse_ctx = fmt::format_parse_context("L"); auto parse_ctx = fmt::format_parse_context("L");
f.parse(parse_ctx); f.parse(parse_ctx);
char buf[10] = {}; auto buf = fmt::memory_buffer();
fmt::basic_format_context<char*, char> format_ctx( fmt::basic_format_context<fmt::appender, char> format_ctx(
buf, {}, fmt::detail::locale_ref(loc)); fmt::appender(buf), {}, fmt::detail::locale_ref(loc));
*f.format(12345, format_ctx) = 0; f.format(12345, format_ctx);
EXPECT_STREQ("12,345", buf); EXPECT_EQ(fmt::to_string(buf), "12,345");
} }
FMT_BEGIN_NAMESPACE FMT_BEGIN_NAMESPACE