mirror of
https://github.com/fmtlib/fmt.git
synced 2024-11-09 12:50:05 +00:00
Add opt out for built-in types
This commit is contained in:
parent
5a0a37340c
commit
377cf203e3
@ -291,6 +291,16 @@
|
||||
# define FMT_UNICODE 1
|
||||
#endif
|
||||
|
||||
// Specifies whether to handle built-in and string types specially.
|
||||
// FMT_BUILTIN_TYPE=0 may result in smaller library size at the cost of higher
|
||||
// per-call binary size.
|
||||
#ifndef FMT_BUILTIN_TYPES
|
||||
# define FMT_BUILTIN_TYPES 1
|
||||
#endif
|
||||
#if !FMT_BUILTIN_TYPES && !defined(__cpp_if_constexpr)
|
||||
# error FMT_BUILTIN_TYPES=0 requires constexpr if support
|
||||
#endif
|
||||
|
||||
// Check if rtti is available.
|
||||
#ifndef FMT_USE_RTTI
|
||||
// __RTTI is for EDG compilers. _CPPRTTI is for MSVC.
|
||||
@ -1317,9 +1327,19 @@ template <typename Char> struct named_arg_info {
|
||||
template <typename T> struct is_named_arg : std::false_type {};
|
||||
template <typename T> struct is_statically_named_arg : std::false_type {};
|
||||
|
||||
template <typename T, typename Char>
|
||||
template <typename Char, typename T>
|
||||
struct is_named_arg<named_arg<Char, T>> : std::true_type {};
|
||||
|
||||
template <typename Char, typename T>
|
||||
auto unwrap_named_arg(const named_arg<Char, T>& arg) -> const T& {
|
||||
return arg.value;
|
||||
}
|
||||
template <typename T,
|
||||
FMT_ENABLE_IF(!is_named_arg<remove_reference_t<T>>::value)>
|
||||
auto unwrap_named_arg(T&& value) -> T&& {
|
||||
return value;
|
||||
}
|
||||
|
||||
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 {
|
||||
return (B1 ? 1 : 0) + count<B2, Tail...>();
|
||||
@ -1354,6 +1374,8 @@ template <typename Context> struct custom_value {
|
||||
void (*format)(void* arg, parse_context& parse_ctx, Context& ctx);
|
||||
};
|
||||
|
||||
enum class custom_tag {};
|
||||
|
||||
// A formatting argument value.
|
||||
template <typename Context> class value {
|
||||
public:
|
||||
@ -1400,24 +1422,26 @@ template <typename Context> class value {
|
||||
string.size = val.size();
|
||||
}
|
||||
FMT_ALWAYS_INLINE value(const void* val) : pointer(val) {}
|
||||
FMT_ALWAYS_INLINE value(const named_arg_info<char_type>* args, size_t size)
|
||||
: named_args{args, size} {}
|
||||
|
||||
template <typename T> FMT_CONSTEXPR20 FMT_ALWAYS_INLINE value(T& val) {
|
||||
using value_type = remove_const_t<T>;
|
||||
template <typename T>
|
||||
FMT_CONSTEXPR20 FMT_ALWAYS_INLINE value(T& val, custom_tag = {}) {
|
||||
using value_type = typename std::remove_cv<T>::type;
|
||||
// T may overload operator& e.g. std::vector<bool>::reference in libc++.
|
||||
#if defined(__cpp_if_constexpr)
|
||||
if constexpr (std::is_same<decltype(&val), T*>::value)
|
||||
custom.value = const_cast<value_type*>(&val);
|
||||
#endif
|
||||
if (!is_constant_evaluated())
|
||||
custom.value = const_cast<char*>(&reinterpret_cast<const char&>(val));
|
||||
custom.value =
|
||||
const_cast<char*>(&reinterpret_cast<const volatile char&>(val));
|
||||
// Get the formatter type through the context to allow different contexts
|
||||
// have different extension points, e.g. `formatter<T>` for `format` and
|
||||
// `printf_formatter<T>` for `printf`.
|
||||
custom.format = format_custom_arg<
|
||||
value_type, typename Context::template formatter_type<value_type>>;
|
||||
}
|
||||
FMT_ALWAYS_INLINE value(const named_arg_info<char_type>* args, size_t size)
|
||||
: named_args{args, size} {}
|
||||
value(unformattable);
|
||||
value(unformattable_char);
|
||||
value(unformattable_pointer);
|
||||
@ -1606,6 +1630,12 @@ using mapped_type_constant =
|
||||
type_constant<decltype(arg_mapper<Context>().map(std::declval<const T&>())),
|
||||
typename Context::char_type>;
|
||||
|
||||
template <typename T, typename Context,
|
||||
type TYPE = mapped_type_constant<T, Context>::value>
|
||||
using stored_type_constant = std::integral_constant<
|
||||
type, Context::builtin_types || TYPE == type::int_type ? TYPE
|
||||
: type::custom_type>;
|
||||
|
||||
enum { packed_arg_bits = 4 };
|
||||
// Maximum number of arguments with packed types.
|
||||
enum { max_packed_args = 62 / packed_arg_bits };
|
||||
@ -1643,7 +1673,7 @@ template <typename> constexpr auto encode_types() -> unsigned long long {
|
||||
|
||||
template <typename Context, typename Arg, typename... Args>
|
||||
constexpr auto encode_types() -> unsigned long long {
|
||||
return static_cast<unsigned>(mapped_type_constant<Arg, Context>::value) |
|
||||
return static_cast<unsigned>(stored_type_constant<Arg, Context>::value) |
|
||||
(encode_types<Context, Args...>() << packed_arg_bits);
|
||||
}
|
||||
|
||||
@ -1686,6 +1716,8 @@ FMT_CONSTEXPR auto make_arg(T& val) -> value<Context> {
|
||||
#if defined(__cpp_if_constexpr)
|
||||
if constexpr (!formattable)
|
||||
type_is_unformattable_for<T, typename Context::char_type> _;
|
||||
if constexpr (!Context::builtin_types && !std::is_same<arg_type, int>::value)
|
||||
return {unwrap_named_arg(val), custom_tag()};
|
||||
#endif
|
||||
static_assert(
|
||||
formattable,
|
||||
@ -1697,7 +1729,7 @@ FMT_CONSTEXPR auto make_arg(T& val) -> value<Context> {
|
||||
template <typename Context, typename T>
|
||||
FMT_CONSTEXPR auto make_arg(T& val) -> basic_format_arg<Context> {
|
||||
auto arg = basic_format_arg<Context>();
|
||||
arg.type_ = mapped_type_constant<T, Context>::value;
|
||||
arg.type_ = stored_type_constant<T, Context>::value;
|
||||
arg.value_ = make_arg<true, Context>(val);
|
||||
return arg;
|
||||
}
|
||||
@ -1781,6 +1813,7 @@ template <typename Context> class basic_format_arg {
|
||||
|
||||
friend class basic_format_args<Context>;
|
||||
friend class dynamic_format_arg_store<Context>;
|
||||
friend class loc_value;
|
||||
|
||||
using char_type = typename Context::char_type;
|
||||
|
||||
@ -1999,6 +2032,7 @@ class context {
|
||||
using format_arg = basic_format_arg<context>;
|
||||
using parse_context_type = basic_format_parse_context<char>;
|
||||
template <typename T> using formatter_type = formatter<T, char>;
|
||||
enum { builtin_types = FMT_BUILTIN_TYPES };
|
||||
|
||||
/// Constructs a `basic_format_context` object. References to the arguments
|
||||
/// are stored in the object so make sure they have appropriate lifetimes.
|
||||
|
@ -1037,6 +1037,7 @@ template <typename OutputIt, typename Char> class generic_context {
|
||||
using iterator = OutputIt;
|
||||
using parse_context_type = basic_format_parse_context<Char>;
|
||||
template <typename T> using formatter_type = formatter<T, Char>;
|
||||
enum { builtin_types = FMT_BUILTIN_TYPES };
|
||||
|
||||
constexpr generic_context(OutputIt out,
|
||||
basic_format_args<generic_context> ctx_args,
|
||||
@ -1074,7 +1075,10 @@ class loc_value {
|
||||
|
||||
public:
|
||||
template <typename T, FMT_ENABLE_IF(!detail::is_float128<T>::value)>
|
||||
loc_value(T value) : value_(detail::make_arg<format_context>(value)) {}
|
||||
loc_value(T value) {
|
||||
value_.type_ = detail::mapped_type_constant<T, format_context>::value;
|
||||
value_.value_ = detail::arg_mapper<format_context>().map(value);
|
||||
}
|
||||
|
||||
template <typename T, FMT_ENABLE_IF(detail::is_float128<T>::value)>
|
||||
loc_value(T) {}
|
||||
@ -3672,6 +3676,10 @@ FMT_CONSTEXPR auto write(OutputIt out, const T& value)
|
||||
return formatter.format(value, ctx);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
using is_builtin =
|
||||
bool_constant<std::is_same<T, int>::value || FMT_BUILTIN_TYPES>;
|
||||
|
||||
// An argument visitor that formats the argument and writes it via the output
|
||||
// iterator. It's a class and not a generic lambda for compatibility with C++11.
|
||||
template <typename Char> struct default_arg_formatter {
|
||||
@ -3681,7 +3689,15 @@ template <typename Char> struct default_arg_formatter {
|
||||
|
||||
void operator()(monostate) { report_error("argument not found"); }
|
||||
|
||||
template <typename T> void operator()(T value) { write<Char>(out, value); }
|
||||
template <typename T, FMT_ENABLE_IF(is_builtin<T>::value)>
|
||||
void operator()(T value) {
|
||||
write<Char>(out, value);
|
||||
}
|
||||
|
||||
template <typename T, FMT_ENABLE_IF(!is_builtin<T>::value)>
|
||||
void operator()(T) {
|
||||
FMT_ASSERT(false, "");
|
||||
}
|
||||
|
||||
void operator()(typename basic_format_arg<context>::handle h) {
|
||||
// Use a null locale since the default format must be unlocalized.
|
||||
@ -3699,10 +3715,17 @@ template <typename Char> struct arg_formatter {
|
||||
const format_specs& specs;
|
||||
locale_ref locale;
|
||||
|
||||
template <typename T>
|
||||
template <typename T, FMT_ENABLE_IF(is_builtin<T>::value)>
|
||||
FMT_CONSTEXPR FMT_INLINE auto operator()(T value) -> iterator {
|
||||
return detail::write<Char>(out, value, specs, locale);
|
||||
}
|
||||
|
||||
template <typename T, FMT_ENABLE_IF(!is_builtin<T>::value)>
|
||||
auto operator()(T) -> iterator {
|
||||
FMT_ASSERT(false, "");
|
||||
return out;
|
||||
}
|
||||
|
||||
auto operator()(typename basic_format_arg<context>::handle) -> iterator {
|
||||
// User-defined types are handled separately because they require access
|
||||
// to the parse context.
|
||||
@ -3939,17 +3962,17 @@ FMT_FORMAT_AS(unsigned short, unsigned);
|
||||
FMT_FORMAT_AS(long, detail::long_type);
|
||||
FMT_FORMAT_AS(unsigned long, detail::ulong_type);
|
||||
FMT_FORMAT_AS(Char*, const Char*);
|
||||
FMT_FORMAT_AS(std::nullptr_t, const void*);
|
||||
FMT_FORMAT_AS(detail::std_string_view<Char>, basic_string_view<Char>);
|
||||
FMT_FORMAT_AS(std::nullptr_t, const void*);
|
||||
FMT_FORMAT_AS(void*, const void*);
|
||||
|
||||
template <typename Char, size_t N>
|
||||
struct formatter<Char[N], Char> : formatter<basic_string_view<Char>, Char> {};
|
||||
|
||||
template <typename Char, typename Traits, typename Allocator>
|
||||
class formatter<std::basic_string<Char, Traits, Allocator>, Char>
|
||||
: public formatter<basic_string_view<Char>, Char> {};
|
||||
|
||||
template <typename Char, size_t N>
|
||||
struct formatter<Char[N], Char> : formatter<basic_string_view<Char>, Char> {};
|
||||
|
||||
template <typename T, typename Char>
|
||||
struct formatter<T, Char,
|
||||
enable_if_t<(detail::bitint_traits<T>::is_formattable)>>
|
||||
@ -4208,8 +4231,6 @@ template <typename Char> struct format_handler {
|
||||
specs.precision_ref, context);
|
||||
}
|
||||
|
||||
if (begin == end || *begin != '}')
|
||||
report_error("missing '}' in format string");
|
||||
arg.visit(arg_formatter<Char>{context.out(), specs, context.locale()});
|
||||
return begin;
|
||||
}
|
||||
@ -4246,7 +4267,7 @@ FMT_END_EXPORT
|
||||
|
||||
template <typename T, typename Char, type TYPE>
|
||||
template <typename FormatContext>
|
||||
FMT_CONSTEXPR FMT_INLINE auto native_formatter<T, Char, TYPE>::format(
|
||||
FMT_CONSTEXPR auto native_formatter<T, Char, TYPE>::format(
|
||||
const T& val, FormatContext& ctx) const -> decltype(ctx.out()) {
|
||||
if (!specs_.dynamic())
|
||||
return write<Char>(ctx.out(), val, specs_, ctx.locale());
|
||||
|
@ -35,6 +35,7 @@ template <typename Char> class basic_printf_context {
|
||||
using char_type = Char;
|
||||
using parse_context_type = basic_format_parse_context<Char>;
|
||||
template <typename T> using formatter_type = printf_formatter<T>;
|
||||
enum { builtin_types = 1 };
|
||||
|
||||
/// Constructs a `printf_context` object. References to the arguments are
|
||||
/// stored in the context object so make sure they have appropriate lifetimes.
|
||||
@ -238,19 +239,23 @@ class printf_arg_formatter : public arg_formatter<Char> {
|
||||
write_bytes<Char>(this->out, is_string ? "(null)" : "(nil)", s);
|
||||
}
|
||||
|
||||
template <typename T> void write(T value) {
|
||||
detail::write<Char>(this->out, value, this->specs, this->locale);
|
||||
}
|
||||
|
||||
public:
|
||||
printf_arg_formatter(basic_appender<Char> iter, format_specs& s,
|
||||
context_type& ctx)
|
||||
: base(make_arg_formatter(iter, s)), context_(ctx) {}
|
||||
|
||||
void operator()(monostate value) { base::operator()(value); }
|
||||
void operator()(monostate value) { write(value); }
|
||||
|
||||
template <typename T, FMT_ENABLE_IF(detail::is_integral<T>::value)>
|
||||
void operator()(T value) {
|
||||
// MSVC2013 fails to compile separate overloads for bool and Char so use
|
||||
// std::is_same instead.
|
||||
if (!std::is_same<T, Char>::value) {
|
||||
base::operator()(value);
|
||||
write(value);
|
||||
return;
|
||||
}
|
||||
format_specs s = this->specs;
|
||||
@ -265,33 +270,33 @@ class printf_arg_formatter : public arg_formatter<Char> {
|
||||
// ignored for non-numeric types
|
||||
if (s.align() == align::none || s.align() == align::numeric)
|
||||
s.set_align(align::right);
|
||||
write<Char>(this->out, static_cast<Char>(value), s);
|
||||
detail::write<Char>(this->out, static_cast<Char>(value), s);
|
||||
}
|
||||
|
||||
template <typename T, FMT_ENABLE_IF(std::is_floating_point<T>::value)>
|
||||
void operator()(T value) {
|
||||
base::operator()(value);
|
||||
write(value);
|
||||
}
|
||||
|
||||
void operator()(const char* value) {
|
||||
if (value)
|
||||
base::operator()(value);
|
||||
write(value);
|
||||
else
|
||||
write_null_pointer(this->specs.type() != presentation_type::pointer);
|
||||
}
|
||||
|
||||
void operator()(const wchar_t* value) {
|
||||
if (value)
|
||||
base::operator()(value);
|
||||
write(value);
|
||||
else
|
||||
write_null_pointer(this->specs.type() != presentation_type::pointer);
|
||||
}
|
||||
|
||||
void operator()(basic_string_view<Char> value) { base::operator()(value); }
|
||||
void operator()(basic_string_view<Char> value) { write(value); }
|
||||
|
||||
void operator()(const void* value) {
|
||||
if (value)
|
||||
base::operator()(value);
|
||||
write(value);
|
||||
else
|
||||
write_null_pointer();
|
||||
}
|
||||
|
@ -372,15 +372,19 @@ VISIT_TYPE(long, long long);
|
||||
VISIT_TYPE(unsigned long, unsigned long long);
|
||||
#endif
|
||||
|
||||
#define CHECK_ARG(Char, expected, value) \
|
||||
{ \
|
||||
testing::StrictMock<mock_visitor<decltype(expected)>> visitor; \
|
||||
EXPECT_CALL(visitor, visit(expected)); \
|
||||
using iterator = fmt::basic_appender<Char>; \
|
||||
auto var = value; \
|
||||
fmt::detail::make_arg<fmt::basic_format_context<iterator, Char>>(var) \
|
||||
.visit(visitor); \
|
||||
}
|
||||
#if FMT_BUILTIN_TYPES
|
||||
# define CHECK_ARG(Char, expected, value) \
|
||||
{ \
|
||||
testing::StrictMock<mock_visitor<decltype(expected)>> visitor; \
|
||||
EXPECT_CALL(visitor, visit(expected)); \
|
||||
using iterator = fmt::basic_appender<Char>; \
|
||||
auto var = value; \
|
||||
fmt::detail::make_arg<fmt::basic_format_context<iterator, Char>>(var) \
|
||||
.visit(visitor); \
|
||||
}
|
||||
#else
|
||||
# define CHECK_ARG(Char, expected, value)
|
||||
#endif
|
||||
|
||||
#define CHECK_ARG_SIMPLE(value) \
|
||||
{ \
|
||||
@ -391,10 +395,14 @@ VISIT_TYPE(unsigned long, unsigned long long);
|
||||
|
||||
template <typename T> class numeric_arg_test : public testing::Test {};
|
||||
|
||||
#if FMT_BUILTIN_TYPES
|
||||
using test_types =
|
||||
testing::Types<bool, signed char, unsigned char, short, unsigned short, int,
|
||||
unsigned, long, unsigned long, long long, unsigned long long,
|
||||
float, double, long double>;
|
||||
#else
|
||||
using test_types = testing::Types<int>;
|
||||
#endif
|
||||
TYPED_TEST_SUITE(numeric_arg_test, test_types);
|
||||
|
||||
template <typename T, fmt::enable_if_t<std::is_integral<T>::value, int> = 0>
|
||||
@ -757,20 +765,12 @@ TEST(base_test, format_to_array) {
|
||||
EXPECT_TRUE(result.truncated);
|
||||
EXPECT_EQ("ABCD", fmt::string_view(buffer, 4));
|
||||
|
||||
result = fmt::format_to(buffer, "{}", std::string(1000, '*'));
|
||||
result = fmt::format_to(buffer, "{}", std::string(1000, '*').c_str());
|
||||
EXPECT_EQ(4, std::distance(&buffer[0], result.out));
|
||||
EXPECT_TRUE(result.truncated);
|
||||
EXPECT_EQ("****", fmt::string_view(buffer, 4));
|
||||
}
|
||||
|
||||
#ifdef __cpp_lib_byte
|
||||
TEST(base_test, format_byte) {
|
||||
auto s = std::string();
|
||||
fmt::format_to(std::back_inserter(s), "{}", std::byte(42));
|
||||
EXPECT_EQ(s, "42");
|
||||
}
|
||||
#endif
|
||||
|
||||
// Test that check is not found by ADL.
|
||||
template <typename T> void check(T);
|
||||
TEST(base_test, adl_check) {
|
||||
|
@ -795,20 +795,14 @@ TEST(chrono_test, cpp20_duration_subsecond_support) {
|
||||
"01.234000");
|
||||
EXPECT_EQ(fmt::format("{:.6%S}", std::chrono::milliseconds{-1234}),
|
||||
"-01.234000");
|
||||
EXPECT_EQ(fmt::format("{:.2%S}", std::chrono::milliseconds{12345}),
|
||||
"12.34");
|
||||
EXPECT_EQ(fmt::format("{:.2%S}", std::chrono::milliseconds{12375}),
|
||||
"12.37");
|
||||
EXPECT_EQ(fmt::format("{:.2%S}", std::chrono::milliseconds{12345}), "12.34");
|
||||
EXPECT_EQ(fmt::format("{:.2%S}", std::chrono::milliseconds{12375}), "12.37");
|
||||
EXPECT_EQ(fmt::format("{:.2%S}", std::chrono::milliseconds{-12375}),
|
||||
"-12.37");
|
||||
EXPECT_EQ(fmt::format("{:.0%S}", std::chrono::milliseconds{12054}),
|
||||
"12");
|
||||
EXPECT_EQ(fmt::format("{:.2%S}", std::chrono::milliseconds{99999}),
|
||||
"39.99");
|
||||
EXPECT_EQ(fmt::format("{:.2%S}", std::chrono::milliseconds{1000}),
|
||||
"01.00");
|
||||
EXPECT_EQ(fmt::format("{:.3%S}", std::chrono::milliseconds{1}),
|
||||
"00.001");
|
||||
EXPECT_EQ(fmt::format("{:.0%S}", std::chrono::milliseconds{12054}), "12");
|
||||
EXPECT_EQ(fmt::format("{:.2%S}", std::chrono::milliseconds{99999}), "39.99");
|
||||
EXPECT_EQ(fmt::format("{:.2%S}", std::chrono::milliseconds{1000}), "01.00");
|
||||
EXPECT_EQ(fmt::format("{:.3%S}", std::chrono::milliseconds{1}), "00.001");
|
||||
EXPECT_EQ(fmt::format("{:.3%S}", std::chrono::seconds{1234}), "34.000");
|
||||
EXPECT_EQ(fmt::format("{:.3%S}", std::chrono::hours{1234}), "00.000");
|
||||
EXPECT_EQ(fmt::format("{:.5%S}", dms(1.234)), "00.00123");
|
||||
|
@ -810,7 +810,7 @@ TEST(format_test, hash_flag) {
|
||||
EXPECT_EQ(fmt::format("{:#.2g}", 0.5), "0.50");
|
||||
EXPECT_EQ(fmt::format("{:#.0f}", 0.5), "0.");
|
||||
EXPECT_THROW_MSG((void)fmt::format(runtime("{0:#"), 'c'), format_error,
|
||||
"missing '}' in format string");
|
||||
"invalid format specifier for char");
|
||||
EXPECT_THROW_MSG((void)fmt::format(runtime("{0:#}"), 'c'), format_error,
|
||||
"invalid format specifier for char");
|
||||
EXPECT_THROW_MSG((void)fmt::format(runtime("{0:#}"), "abc"), format_error,
|
||||
@ -831,7 +831,7 @@ TEST(format_test, zero_flag) {
|
||||
EXPECT_EQ(fmt::format("{0:07}", -42.0), "-000042");
|
||||
EXPECT_EQ(fmt::format("{0:07}", -42.0l), "-000042");
|
||||
EXPECT_THROW_MSG((void)fmt::format(runtime("{0:0"), 'c'), format_error,
|
||||
"missing '}' in format string");
|
||||
"invalid format specifier for char");
|
||||
EXPECT_THROW_MSG((void)fmt::format(runtime("{0:05}"), 'c'), format_error,
|
||||
"invalid format specifier for char");
|
||||
EXPECT_THROW_MSG((void)fmt::format(runtime("{0:05}"), "abc"), format_error,
|
||||
@ -878,6 +878,10 @@ TEST(format_test, width) {
|
||||
EXPECT_EQ(fmt::format("{:>06.0f}", 0.00884311), " 0");
|
||||
}
|
||||
|
||||
auto bad_dynamic_spec_msg = FMT_BUILTIN_TYPES
|
||||
? "width/precision is out of range"
|
||||
: "width/precision is not integer";
|
||||
|
||||
TEST(format_test, runtime_width) {
|
||||
auto int_maxer = std::to_string(INT_MAX + 1u);
|
||||
EXPECT_THROW_MSG((void)fmt::format(runtime("{0:{" + int_maxer), 0),
|
||||
@ -902,16 +906,16 @@ TEST(format_test, runtime_width) {
|
||||
EXPECT_THROW_MSG((void)fmt::format(runtime("{0:{1}}"), 0, -1), format_error,
|
||||
"width/precision is out of range");
|
||||
EXPECT_THROW_MSG((void)fmt::format(runtime("{0:{1}}"), 0, (INT_MAX + 1u)),
|
||||
format_error, "width/precision is out of range");
|
||||
format_error, bad_dynamic_spec_msg);
|
||||
EXPECT_THROW_MSG((void)fmt::format(runtime("{0:{1}}"), 0, -1l), format_error,
|
||||
"width/precision is out of range");
|
||||
bad_dynamic_spec_msg);
|
||||
if (fmt::detail::const_check(sizeof(long) > sizeof(int))) {
|
||||
long value = INT_MAX;
|
||||
EXPECT_THROW_MSG((void)fmt::format(runtime("{0:{1}}"), 0, (value + 1)),
|
||||
format_error, "width/precision is out of range");
|
||||
format_error, bad_dynamic_spec_msg);
|
||||
}
|
||||
EXPECT_THROW_MSG((void)fmt::format(runtime("{0:{1}}"), 0, (INT_MAX + 1ul)),
|
||||
format_error, "width/precision is out of range");
|
||||
format_error, bad_dynamic_spec_msg);
|
||||
|
||||
EXPECT_THROW_MSG((void)fmt::format(runtime("{0:{1}}"), 0, '0'), format_error,
|
||||
"width/precision is not integer");
|
||||
@ -1127,16 +1131,16 @@ TEST(format_test, runtime_precision) {
|
||||
EXPECT_THROW_MSG((void)fmt::format(runtime("{0:.{1}}"), 0.0, -1),
|
||||
format_error, "width/precision is out of range");
|
||||
EXPECT_THROW_MSG((void)fmt::format(runtime("{0:.{1}}"), 0.0, (INT_MAX + 1u)),
|
||||
format_error, "width/precision is out of range");
|
||||
format_error, bad_dynamic_spec_msg);
|
||||
EXPECT_THROW_MSG((void)fmt::format(runtime("{0:.{1}}"), 0.0, -1l),
|
||||
format_error, "width/precision is out of range");
|
||||
format_error, bad_dynamic_spec_msg);
|
||||
if (fmt::detail::const_check(sizeof(long) > sizeof(int))) {
|
||||
long value = INT_MAX;
|
||||
EXPECT_THROW_MSG((void)fmt::format(runtime("{0:.{1}}"), 0.0, (value + 1)),
|
||||
format_error, "width/precision is out of range");
|
||||
format_error, bad_dynamic_spec_msg);
|
||||
}
|
||||
EXPECT_THROW_MSG((void)fmt::format(runtime("{0:.{1}}"), 0.0, (INT_MAX + 1ul)),
|
||||
format_error, "width/precision is out of range");
|
||||
format_error, bad_dynamic_spec_msg);
|
||||
|
||||
EXPECT_THROW_MSG((void)fmt::format(runtime("{0:.{1}}"), 0.0, '0'),
|
||||
format_error, "width/precision is not integer");
|
||||
@ -2408,6 +2412,7 @@ namespace adl_test {
|
||||
template <typename... T> void make_format_args(const T&...) = delete;
|
||||
|
||||
struct string : std::string {};
|
||||
auto format_as(const string& s) -> std::string { return s; }
|
||||
} // namespace adl_test
|
||||
|
||||
// Test that formatting functions compile when make_format_args is found by ADL.
|
||||
@ -2568,3 +2573,11 @@ TEST(format_test, bitint) {
|
||||
# endif
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef __cpp_lib_byte
|
||||
TEST(base_test, format_byte) {
|
||||
auto s = std::string();
|
||||
fmt::format_to(std::back_inserter(s), "{}", std::byte(42));
|
||||
EXPECT_EQ(s, "42");
|
||||
}
|
||||
#endif
|
||||
|
@ -111,9 +111,9 @@ TEST(scan_test, invalid_format) {
|
||||
}
|
||||
|
||||
namespace std {
|
||||
using fmt::scan;
|
||||
using fmt::scan_error;
|
||||
}
|
||||
using fmt::scan;
|
||||
using fmt::scan_error;
|
||||
} // namespace std
|
||||
|
||||
TEST(scan_test, example) {
|
||||
// Example from https://wg21.link/p1729r3.
|
||||
|
@ -36,8 +36,10 @@ TEST(std_test, path) {
|
||||
"Шчучыншчына");
|
||||
EXPECT_EQ(fmt::format("{}", path(L"\xd800")), "<EFBFBD>");
|
||||
EXPECT_EQ(fmt::format("{}", path(L"HEAD \xd800 TAIL")), "HEAD <20> TAIL");
|
||||
EXPECT_EQ(fmt::format("{}", path(L"HEAD \xD83D\xDE00 TAIL")), "HEAD \xF0\x9F\x98\x80 TAIL");
|
||||
EXPECT_EQ(fmt::format("{}", path(L"HEAD \xD83D\xD83D\xDE00 TAIL")), "HEAD <20>\xF0\x9F\x98\x80 TAIL");
|
||||
EXPECT_EQ(fmt::format("{}", path(L"HEAD \xD83D\xDE00 TAIL")),
|
||||
"HEAD \xF0\x9F\x98\x80 TAIL");
|
||||
EXPECT_EQ(fmt::format("{}", path(L"HEAD \xD83D\xD83D\xDE00 TAIL")),
|
||||
"HEAD <20>\xF0\x9F\x98\x80 TAIL");
|
||||
EXPECT_EQ(fmt::format("{:?}", path(L"\xd800")), "\"\\ud800\"");
|
||||
# endif
|
||||
}
|
||||
|
@ -36,12 +36,11 @@ std::locale do_get_locale(const char* name) {
|
||||
|
||||
std::locale get_locale(const char* name, const char* alt_name) {
|
||||
auto loc = do_get_locale(name);
|
||||
if (loc == std::locale::classic() && alt_name)
|
||||
loc = do_get_locale(alt_name);
|
||||
if (loc == std::locale::classic() && alt_name) loc = do_get_locale(alt_name);
|
||||
#ifdef __OpenBSD__
|
||||
// Locales are not working in OpenBSD:
|
||||
// https://github.com/fmtlib/fmt/issues/3670.
|
||||
loc = std::locale::classic();
|
||||
// Locales are not working in OpenBSD:
|
||||
// https://github.com/fmtlib/fmt/issues/3670.
|
||||
loc = std::locale::classic();
|
||||
#endif
|
||||
if (loc == std::locale::classic())
|
||||
fmt::print(stderr, "{} locale is missing.\n", name);
|
||||
|
@ -78,7 +78,6 @@ TEST(xchar_test, format) {
|
||||
EXPECT_EQ(L"z", fmt::format(L"{}", L'z'));
|
||||
EXPECT_THROW(fmt::format(fmt::runtime(L"{:*\x343E}"), 42), fmt::format_error);
|
||||
EXPECT_EQ(L"true", fmt::format(L"{}", true));
|
||||
EXPECT_EQ(L"a", fmt::format(L"{0}", 'a'));
|
||||
EXPECT_EQ(L"a", fmt::format(L"{0}", L'a'));
|
||||
EXPECT_EQ(L"Cyrillic letter \x42e",
|
||||
fmt::format(L"Cyrillic letter {}", L'\x42e'));
|
||||
@ -585,7 +584,8 @@ TEST(locale_test, sign) {
|
||||
TEST(std_test_xchar, complex) {
|
||||
auto s = fmt::format(L"{}", std::complex<double>(1, 2));
|
||||
EXPECT_EQ(s, L"(1+2i)");
|
||||
EXPECT_EQ(fmt::format(L"{:.2f}", std::complex<double>(1, 2)), L"(1.00+2.00i)");
|
||||
EXPECT_EQ(fmt::format(L"{:.2f}", std::complex<double>(1, 2)),
|
||||
L"(1.00+2.00i)");
|
||||
EXPECT_EQ(fmt::format(L"{:8}", std::complex<double>(1, 2)), L"(1+2i) ");
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user