Merge branch 'master' of github.com:andreagen0r/fmt
This commit is contained in:
commit
2d415bb497
@ -69,9 +69,7 @@
|
||||
# define FMT_HAS_FEATURE(x) 0
|
||||
#endif
|
||||
|
||||
#if (defined(__has_include) || FMT_ICC_VERSION >= 1600 || \
|
||||
FMT_MSC_VERSION > 1900) && \
|
||||
!defined(__INTELLISENSE__)
|
||||
#if defined(__has_include) || FMT_ICC_VERSION >= 1600 || FMT_MSC_VERSION > 1900
|
||||
# define FMT_HAS_INCLUDE(x) __has_include(x)
|
||||
#else
|
||||
# define FMT_HAS_INCLUDE(x) 0
|
||||
@ -332,7 +330,7 @@ struct monostate {
|
||||
#ifdef FMT_DOC
|
||||
# define FMT_ENABLE_IF(...)
|
||||
#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
|
||||
|
||||
FMT_BEGIN_DETAIL_NAMESPACE
|
||||
@ -486,6 +484,18 @@ template <typename Char> class basic_string_view {
|
||||
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.
|
||||
FMT_CONSTEXPR_CHAR_TRAITS auto compare(basic_string_view other) const -> int {
|
||||
size_t str_size = size_ < other.size_ ? size_ : other.size_;
|
||||
|
||||
@ -29,8 +29,6 @@
|
||||
#include "format.h"
|
||||
|
||||
FMT_BEGIN_NAMESPACE
|
||||
template <typename Locale> typename Locale::id num_format_facet<Locale>::id;
|
||||
|
||||
namespace detail {
|
||||
|
||||
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
|
||||
|
||||
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 {
|
||||
#ifndef FMT_STATIC_THOUSANDS_SEPARATOR
|
||||
auto locale = loc.get<std::locale>();
|
||||
// We cannot use the num_put<char> facet because it may produce output in
|
||||
// a wrong encoding.
|
||||
if (!std::has_facet<num_format_facet<std::locale>>(locale)) return {};
|
||||
std::use_facet<num_format_facet<std::locale>>(locale).put(out, value, specs,
|
||||
locale);
|
||||
return true;
|
||||
using facet = format_facet<std::locale>;
|
||||
if (std::has_facet<facet>(locale))
|
||||
return std::use_facet<facet>(locale).put(out, value, specs);
|
||||
return facet(locale).put(out, value, specs);
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
} // 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
|
||||
FMT_API FMT_FUNC format_error::~format_error() noexcept = default;
|
||||
#endif
|
||||
|
||||
@ -33,13 +33,14 @@
|
||||
#ifndef FMT_FORMAT_H_
|
||||
#define FMT_FORMAT_H_
|
||||
|
||||
#include <cmath> // std::signbit
|
||||
#include <cstdint> // uint32_t
|
||||
#include <cstring> // std::memcpy
|
||||
#include <limits> // std::numeric_limits
|
||||
#include <memory> // std::uninitialized_copy
|
||||
#include <stdexcept> // std::runtime_error
|
||||
#include <system_error> // std::system_error
|
||||
#include <cmath> // std::signbit
|
||||
#include <cstdint> // uint32_t
|
||||
#include <cstring> // std::memcpy
|
||||
#include <initializer_list> // std::initializer_list
|
||||
#include <limits> // std::numeric_limits
|
||||
#include <memory> // std::uninitialized_copy
|
||||
#include <stdexcept> // std::runtime_error
|
||||
#include <system_error> // std::system_error
|
||||
|
||||
#ifdef __cpp_lib_bit_cast
|
||||
# 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);
|
||||
}
|
||||
|
||||
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
|
||||
# ifdef __SIZEOF_FLOAT128__
|
||||
# define FMT_USE_FLOAT128 1
|
||||
@ -985,40 +1001,37 @@ constexpr auto compile_string_to_view(detail::std_string_view<Char> s)
|
||||
}
|
||||
} // namespace detail_exported
|
||||
|
||||
// A value to localize.
|
||||
struct loc_value {
|
||||
union {
|
||||
unsigned long long ulong_long_value;
|
||||
};
|
||||
};
|
||||
// A locale facet that formats values in UTF-8.
|
||||
// It is parameterized on the locale to avoid the heavy <locale> include.
|
||||
template <typename Locale> class format_facet : public Locale::facet {
|
||||
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:
|
||||
static FMT_API typename Locale::id id;
|
||||
|
||||
void put(appender out, loc_value val, const format_specs& specs,
|
||||
Locale& loc) const {
|
||||
do_put(out, val, specs, loc);
|
||||
}
|
||||
explicit format_facet(Locale& loc);
|
||||
explicit format_facet(string_view sep = "",
|
||||
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:
|
||||
virtual void do_put(appender out, loc_value val, const format_specs& specs,
|
||||
Locale& loc) const = 0;
|
||||
auto put(appender out, basic_format_arg<format_context> val,
|
||||
const format_specs& specs) const -> bool {
|
||||
return do_put(out, val, specs);
|
||||
}
|
||||
};
|
||||
|
||||
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.
|
||||
// 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)>
|
||||
@ -1956,19 +1969,19 @@ FMT_CONSTEXPR FMT_INLINE auto write_int(OutputIt out, int num_digits,
|
||||
|
||||
template <typename Char> class digit_grouping {
|
||||
private:
|
||||
thousands_sep_result<Char> sep_;
|
||||
std::string grouping_;
|
||||
std::basic_string<Char> thousands_sep_;
|
||||
|
||||
struct next_state {
|
||||
std::string::const_iterator group;
|
||||
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.
|
||||
int next(next_state& state) const {
|
||||
if (!sep_.thousands_sep) return max_value<int>();
|
||||
if (state.group == sep_.grouping.end())
|
||||
return state.pos += sep_.grouping.back();
|
||||
if (thousands_sep_.empty()) return max_value<int>();
|
||||
if (state.group == grouping_.end()) return state.pos += grouping_.back();
|
||||
if (*state.group <= 0 || *state.group == max_value<char>())
|
||||
return max_value<int>();
|
||||
state.pos += *state.group++;
|
||||
@ -1977,14 +1990,15 @@ template <typename Char> class digit_grouping {
|
||||
|
||||
public:
|
||||
explicit digit_grouping(locale_ref loc, bool localized = true) {
|
||||
if (localized)
|
||||
sep_ = thousands_sep<Char>(loc);
|
||||
else
|
||||
sep_.thousands_sep = Char();
|
||||
if (!localized) return;
|
||||
auto sep = thousands_sep<Char>(loc);
|
||||
grouping_ = sep.grouping;
|
||||
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 = 0;
|
||||
@ -2007,7 +2021,9 @@ template <typename Char> class digit_grouping {
|
||||
for (int i = 0, sep_index = static_cast<int>(separators.size() - 1);
|
||||
i < num_digits; ++i) {
|
||||
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;
|
||||
}
|
||||
*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,
|
||||
locale_ref loc) -> bool;
|
||||
template <typename OutputIt, typename Char>
|
||||
inline auto write_int(OutputIt, loc_value, const basic_format_specs<Char>&,
|
||||
locale_ref) -> bool {
|
||||
return false;
|
||||
}
|
||||
// Writes localized value.
|
||||
FMT_API auto write_loc(appender out, basic_format_arg<format_context> value,
|
||||
const format_specs& specs, locale_ref loc) -> bool;
|
||||
|
||||
template <typename OutputIt, typename UInt, typename Char>
|
||||
auto write_int(OutputIt& out, UInt value, unsigned prefix,
|
||||
const basic_format_specs<Char>& specs, locale_ref loc) -> bool {
|
||||
auto result = 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;
|
||||
template <typename OutputIt, typename Char>
|
||||
inline auto write_loc(OutputIt, basic_format_arg<buffer_context<Char>>,
|
||||
const basic_format_specs<Char>&, locale_ref) -> bool {
|
||||
return false;
|
||||
}
|
||||
|
||||
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};
|
||||
}
|
||||
|
||||
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>
|
||||
FMT_CONSTEXPR FMT_INLINE auto write_int(OutputIt out, write_int_arg<T> arg,
|
||||
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, "");
|
||||
auto abs_value = arg.abs_value;
|
||||
auto prefix = arg.prefix;
|
||||
switch (specs.type) {
|
||||
case presentation_type::none:
|
||||
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);
|
||||
return write_int(
|
||||
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,
|
||||
const basic_format_specs<Char>& specs,
|
||||
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,
|
||||
loc);
|
||||
}
|
||||
@ -2180,6 +2196,10 @@ template <typename Char, typename OutputIt, typename T,
|
||||
FMT_CONSTEXPR FMT_INLINE auto write(OutputIt out, T value,
|
||||
const basic_format_specs<Char>& specs,
|
||||
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);
|
||||
}
|
||||
|
||||
@ -2331,7 +2351,7 @@ template <typename Char, typename OutputIt, typename T, typename Grouping>
|
||||
FMT_CONSTEXPR20 auto write_significand(OutputIt out, T significand,
|
||||
int significand_size, int exponent,
|
||||
const Grouping& grouping) -> OutputIt {
|
||||
if (!grouping.separator()) {
|
||||
if (!grouping.has_separator()) {
|
||||
out = write_significand<Char>(out, significand, significand_size);
|
||||
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,
|
||||
Char decimal_point,
|
||||
const Grouping& grouping) -> OutputIt {
|
||||
if (!grouping.separator()) {
|
||||
if (!grouping.has_separator()) {
|
||||
return write_significand(out, significand, significand_size, integral_size,
|
||||
decimal_point);
|
||||
}
|
||||
@ -2515,7 +2535,7 @@ template <typename Char> class fallback_digit_grouping {
|
||||
public:
|
||||
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; }
|
||||
|
||||
@ -3460,12 +3480,6 @@ template <typename Char> struct custom_formatter {
|
||||
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 {
|
||||
public:
|
||||
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);
|
||||
return detail::write_int(
|
||||
ctx.out(), static_cast<detail::uint64_or_128_t<T>>(t.value), 0, specs_,
|
||||
detail::digit_grouping<char>({"\3", ','}));
|
||||
detail::digit_grouping<char>("\3", ","));
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@ -211,16 +211,16 @@ class is_tuple_formattable_ {
|
||||
static constexpr const bool value = false;
|
||||
};
|
||||
template <typename T, typename C> class is_tuple_formattable_<T, C, true> {
|
||||
template <std::size_t... I>
|
||||
static std::true_type check2(index_sequence<I...>,
|
||||
integer_sequence<bool, (I == I)...>);
|
||||
template <std::size_t... Is>
|
||||
static std::true_type check2(index_sequence<Is...>,
|
||||
integer_sequence<bool, (Is == Is)...>);
|
||||
static std::false_type check2(...);
|
||||
template <std::size_t... I>
|
||||
template <std::size_t... Is>
|
||||
static decltype(check2(
|
||||
index_sequence<I...>{},
|
||||
index_sequence<Is...>{},
|
||||
integer_sequence<
|
||||
bool, (is_formattable<typename std::tuple_element<I, T>::type,
|
||||
C>::value)...>{})) check(index_sequence<I...>);
|
||||
bool, (is_formattable<typename std::tuple_element<Is, T>::type,
|
||||
C>::value)...>{})) check(index_sequence<Is...>);
|
||||
|
||||
public:
|
||||
static constexpr const bool value =
|
||||
|
||||
@ -101,19 +101,16 @@ template <typename T>
|
||||
using variant_index_sequence =
|
||||
std::make_index_sequence<std::variant_size<T>::value>;
|
||||
|
||||
// variant_size and variant_alternative check.
|
||||
template <typename T, typename U = void>
|
||||
struct is_variant_like_ : std::false_type {};
|
||||
template <typename T>
|
||||
struct is_variant_like_<T, std::void_t<decltype(std::variant_size<T>::value)>>
|
||||
: std::true_type {};
|
||||
template <typename> struct is_variant_like_ : std::false_type {};
|
||||
template <typename... Types>
|
||||
struct is_variant_like_<std::variant<Types...>> : std::true_type {};
|
||||
|
||||
// formattable element check
|
||||
// formattable element check.
|
||||
template <typename T, typename C> class is_variant_formattable_ {
|
||||
template <std::size_t... I>
|
||||
template <std::size_t... Is>
|
||||
static std::conjunction<
|
||||
is_formattable<std::variant_alternative_t<I, T>, C>...>
|
||||
check(std::index_sequence<I...>);
|
||||
is_formattable<std::variant_alternative_t<Is, T>, C>...>
|
||||
check(std::index_sequence<Is...>);
|
||||
|
||||
public:
|
||||
static constexpr const bool value =
|
||||
|
||||
@ -12,11 +12,32 @@
|
||||
|
||||
#include "format.h"
|
||||
|
||||
#ifndef FMT_STATIC_THOUSANDS_SEPARATOR
|
||||
# include <locale>
|
||||
#endif
|
||||
|
||||
FMT_BEGIN_NAMESPACE
|
||||
namespace detail {
|
||||
|
||||
template <typename T>
|
||||
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
|
||||
|
||||
|
||||
@ -9,9 +9,7 @@
|
||||
#include "test-assert.h"
|
||||
// clang-format on
|
||||
|
||||
#define I 42 // simulate https://en.cppreference.com/w/c/numeric/complex/I
|
||||
#include "fmt/core.h"
|
||||
#undef I
|
||||
|
||||
#include <algorithm> // std::copy_n
|
||||
#include <climits> // INT_MAX
|
||||
@ -72,6 +70,16 @@ TEST(string_view_test, compare) {
|
||||
EXPECT_LT(string_view("foo").compare(string_view("fop")), 0);
|
||||
EXPECT_GT(string_view("foo").compare(string_view("fo")), 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::not_equal_to>();
|
||||
check_op<std::less>();
|
||||
|
||||
@ -8,12 +8,14 @@
|
||||
#include <iterator>
|
||||
#include <vector>
|
||||
|
||||
#define I 42 // simulate https://en.cppreference.com/w/c/numeric/complex/I
|
||||
#include "fmt/chrono.h"
|
||||
#include "fmt/color.h"
|
||||
#include "fmt/format.h"
|
||||
#include "fmt/ostream.h"
|
||||
#include "fmt/ranges.h"
|
||||
#include "fmt/xchar.h"
|
||||
#undef I
|
||||
|
||||
// Exercise the API to verify that everything we expect to can compile.
|
||||
void test_format_api() {
|
||||
|
||||
@ -2312,25 +2312,57 @@ TEST(format_int_test, format_int) {
|
||||
EXPECT_EQ(os.str(), fmt::format_int(max_value<int64_t>()).str());
|
||||
}
|
||||
|
||||
#ifdef FMT_STATIC_THOUSANDS_SEPARATOR
|
||||
#ifndef FMT_STATIC_THOUSANDS_SEPARATOR
|
||||
|
||||
# include <locale>
|
||||
|
||||
class num_format : public fmt::num_format_facet<std::locale> {
|
||||
class format_facet : public fmt::format_facet<std::locale> {
|
||||
protected:
|
||||
void do_put(fmt::appender out, fmt::loc_value, const fmt::format_specs&,
|
||||
std::locale&) const override;
|
||||
struct int_formatter {
|
||||
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,
|
||||
const fmt::format_specs&, std::locale&) const {
|
||||
fmt::format_to(out, "[{}]", value.ulong_long_value);
|
||||
auto format_facet::do_put(fmt::appender out,
|
||||
fmt::basic_format_arg<fmt::format_context> arg,
|
||||
const fmt::format_specs&) const -> bool {
|
||||
return visit_format_arg(int_formatter{out}, arg);
|
||||
}
|
||||
|
||||
TEST(format_test, num_format) {
|
||||
auto loc = std::locale(std::locale(), new num_format());
|
||||
TEST(format_test, format_facet) {
|
||||
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]");
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
@ -75,6 +75,9 @@ TEST(std_test, variant) {
|
||||
|
||||
EXPECT_EQ(fmt::format("{}", v4), "variant(monostate)");
|
||||
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
|
||||
}
|
||||
|
||||
|
||||
@ -448,11 +448,11 @@ TEST(locale_test, int_formatter) {
|
||||
auto f = fmt::formatter<int>();
|
||||
auto parse_ctx = fmt::format_parse_context("L");
|
||||
f.parse(parse_ctx);
|
||||
char buf[10] = {};
|
||||
fmt::basic_format_context<char*, char> format_ctx(
|
||||
buf, {}, fmt::detail::locale_ref(loc));
|
||||
*f.format(12345, format_ctx) = 0;
|
||||
EXPECT_STREQ("12,345", buf);
|
||||
auto buf = fmt::memory_buffer();
|
||||
fmt::basic_format_context<fmt::appender, char> format_ctx(
|
||||
fmt::appender(buf), {}, fmt::detail::locale_ref(loc));
|
||||
f.format(12345, format_ctx);
|
||||
EXPECT_EQ(fmt::to_string(buf), "12,345");
|
||||
}
|
||||
|
||||
FMT_BEGIN_NAMESPACE
|
||||
|
||||
Loading…
Reference in New Issue
Block a user