Reduce code bloat
This commit is contained in:
parent
2a2e4c5801
commit
61b4c923d7
@ -874,7 +874,7 @@ template <typename T = void> struct basic_data {
|
||||
FMT_API static constexpr const char hex_digits[] = "0123456789abcdef";
|
||||
FMT_API static constexpr const char signs[] = {0, '-', '+', ' '};
|
||||
FMT_API static constexpr const unsigned prefixes[4] = {0, 0, 0x1000000u | '+',
|
||||
0x1000000u | ' '};
|
||||
0x1000000u | ' '};
|
||||
FMT_API static constexpr const char left_padding_shifts[] = {31, 31, 0, 1, 0};
|
||||
FMT_API static constexpr const char right_padding_shifts[] = {0, 31, 0, 1, 0};
|
||||
};
|
||||
@ -1919,58 +1919,52 @@ auto write(OutputIt out, const T& value) -> typename std::enable_if<
|
||||
|
||||
// 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 OutputIt, typename Char> struct default_arg_formatter {
|
||||
using context = basic_format_context<OutputIt, Char>;
|
||||
template <typename Char> struct default_arg_formatter {
|
||||
using iterator = buffer_appender<Char>;
|
||||
using context = buffer_context<Char>;
|
||||
|
||||
OutputIt out;
|
||||
iterator out;
|
||||
basic_format_args<context> args;
|
||||
locale_ref loc;
|
||||
|
||||
template <typename T> auto operator()(T value) -> OutputIt {
|
||||
template <typename T> auto operator()(T value) -> iterator {
|
||||
return write<Char>(out, value);
|
||||
}
|
||||
auto operator()(typename basic_format_arg<context>::handle h) -> OutputIt {
|
||||
auto operator()(typename basic_format_arg<context>::handle h) -> iterator {
|
||||
basic_format_parse_context<Char> parse_ctx({});
|
||||
basic_format_context<OutputIt, Char> format_ctx(out, args, loc);
|
||||
context format_ctx(out, args, loc);
|
||||
h.format(parse_ctx, format_ctx);
|
||||
return format_ctx.out();
|
||||
}
|
||||
};
|
||||
|
||||
template <typename OutputIt, typename Char> struct arg_formatter {
|
||||
using context = basic_format_context<OutputIt, Char>;
|
||||
template <typename Char> struct arg_formatter {
|
||||
using iterator = buffer_appender<Char>;
|
||||
using context = buffer_context<Char>;
|
||||
|
||||
OutputIt out;
|
||||
iterator out;
|
||||
const basic_format_specs<Char>& specs;
|
||||
locale_ref locale;
|
||||
|
||||
template <typename T>
|
||||
FMT_CONSTEXPR FMT_INLINE auto operator()(T value) -> OutputIt {
|
||||
FMT_CONSTEXPR FMT_INLINE auto operator()(T value) -> iterator {
|
||||
return detail::write(out, value, specs, locale);
|
||||
}
|
||||
auto operator()(typename basic_format_arg<context>::handle) -> OutputIt {
|
||||
auto operator()(typename basic_format_arg<context>::handle) -> iterator {
|
||||
// User-defined types are handled separately because they require access
|
||||
// to the parse context.
|
||||
return out;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Context> class custom_formatter {
|
||||
private:
|
||||
using char_type = typename Context::char_type;
|
||||
template <typename Char> struct custom_formatter {
|
||||
basic_format_parse_context<Char>& parse_ctx;
|
||||
buffer_context<Char>& ctx;
|
||||
|
||||
basic_format_parse_context<char_type>& parse_ctx_;
|
||||
Context& ctx_;
|
||||
|
||||
public:
|
||||
explicit custom_formatter(basic_format_parse_context<char_type>& parse_ctx,
|
||||
Context& ctx)
|
||||
: parse_ctx_(parse_ctx), ctx_(ctx) {}
|
||||
|
||||
void operator()(typename basic_format_arg<Context>::handle h) const {
|
||||
h.format(parse_ctx_, ctx_);
|
||||
void operator()(
|
||||
typename basic_format_arg<buffer_context<Char>>::handle h) const {
|
||||
h.format(parse_ctx, ctx);
|
||||
}
|
||||
|
||||
template <typename T> void operator()(T) const {}
|
||||
};
|
||||
|
||||
@ -2036,16 +2030,33 @@ FMT_CONSTEXPR typename Context::format_arg get_arg(Context& ctx, ID id) {
|
||||
}
|
||||
|
||||
// The standard format specifier handler with checking.
|
||||
template <typename ParseContext, typename Context>
|
||||
class specs_handler : public specs_setter<typename Context::char_type> {
|
||||
public:
|
||||
using char_type = typename Context::char_type;
|
||||
template <typename Char> class specs_handler : public specs_setter<Char> {
|
||||
private:
|
||||
basic_format_parse_context<Char>& parse_context_;
|
||||
buffer_context<Char>& context_;
|
||||
|
||||
FMT_CONSTEXPR specs_handler(basic_format_specs<char_type>& specs,
|
||||
ParseContext& parse_ctx, Context& ctx)
|
||||
: specs_setter<char_type>(specs),
|
||||
parse_context_(parse_ctx),
|
||||
context_(ctx) {}
|
||||
// This is only needed for compatibility with gcc 4.4.
|
||||
using format_arg = basic_format_arg<buffer_context<Char>>;
|
||||
|
||||
FMT_CONSTEXPR format_arg get_arg(auto_id) {
|
||||
return detail::get_arg(context_, parse_context_.next_arg_id());
|
||||
}
|
||||
|
||||
FMT_CONSTEXPR format_arg get_arg(int arg_id) {
|
||||
parse_context_.check_arg_id(arg_id);
|
||||
return detail::get_arg(context_, arg_id);
|
||||
}
|
||||
|
||||
FMT_CONSTEXPR format_arg get_arg(basic_string_view<Char> arg_id) {
|
||||
parse_context_.check_arg_id(arg_id);
|
||||
return detail::get_arg(context_, arg_id);
|
||||
}
|
||||
|
||||
public:
|
||||
FMT_CONSTEXPR specs_handler(basic_format_specs<Char>& specs,
|
||||
basic_format_parse_context<Char>& parse_ctx,
|
||||
buffer_context<Char>& ctx)
|
||||
: specs_setter<Char>(specs), parse_context_(parse_ctx), context_(ctx) {}
|
||||
|
||||
template <typename Id> FMT_CONSTEXPR void on_dynamic_width(Id arg_id) {
|
||||
this->specs_.width = get_dynamic_spec<width_checker>(
|
||||
@ -2058,79 +2069,6 @@ class specs_handler : public specs_setter<typename Context::char_type> {
|
||||
}
|
||||
|
||||
void on_error(const char* message) { context_.on_error(message); }
|
||||
|
||||
private:
|
||||
// This is only needed for compatibility with gcc 4.4.
|
||||
using format_arg = typename Context::format_arg;
|
||||
|
||||
FMT_CONSTEXPR format_arg get_arg(auto_id) {
|
||||
return detail::get_arg(context_, parse_context_.next_arg_id());
|
||||
}
|
||||
|
||||
FMT_CONSTEXPR format_arg get_arg(int arg_id) {
|
||||
parse_context_.check_arg_id(arg_id);
|
||||
return detail::get_arg(context_, arg_id);
|
||||
}
|
||||
|
||||
FMT_CONSTEXPR format_arg get_arg(basic_string_view<char_type> arg_id) {
|
||||
parse_context_.check_arg_id(arg_id);
|
||||
return detail::get_arg(context_, arg_id);
|
||||
}
|
||||
|
||||
ParseContext& parse_context_;
|
||||
Context& context_;
|
||||
};
|
||||
|
||||
template <typename OutputIt, typename Char, typename Context>
|
||||
struct format_handler : detail::error_handler {
|
||||
basic_format_parse_context<Char> parse_context;
|
||||
Context context;
|
||||
|
||||
format_handler(OutputIt out, basic_string_view<Char> str,
|
||||
basic_format_args<Context> format_args, detail::locale_ref loc)
|
||||
: parse_context(str), context(out, format_args, loc) {}
|
||||
|
||||
void on_text(const Char* begin, const Char* end) {
|
||||
auto text = basic_string_view<Char>(begin, to_unsigned(end - begin));
|
||||
context.advance_to(write<Char>(context.out(), text));
|
||||
}
|
||||
|
||||
int on_arg_id() { return parse_context.next_arg_id(); }
|
||||
int on_arg_id(int id) { return parse_context.check_arg_id(id), id; }
|
||||
int on_arg_id(basic_string_view<Char> id) {
|
||||
int arg_id = context.arg_id(id);
|
||||
if (arg_id < 0) on_error("argument not found");
|
||||
return arg_id;
|
||||
}
|
||||
|
||||
FMT_INLINE void on_replacement_field(int id, const Char*) {
|
||||
auto arg = get_arg(context, id);
|
||||
context.advance_to(visit_format_arg(
|
||||
default_arg_formatter<OutputIt, Char>{context.out(), context.args(),
|
||||
context.locale()},
|
||||
arg));
|
||||
}
|
||||
|
||||
const Char* on_format_specs(int id, const Char* begin, const Char* end) {
|
||||
auto arg = get_arg(context, id);
|
||||
if (arg.type() == type::custom_type) {
|
||||
parse_context.advance_to(parse_context.begin() +
|
||||
(begin - &*parse_context.begin()));
|
||||
visit_format_arg(custom_formatter<Context>(parse_context, context), arg);
|
||||
return parse_context.begin();
|
||||
}
|
||||
auto specs = basic_format_specs<Char>();
|
||||
using parse_context_t = basic_format_parse_context<Char>;
|
||||
specs_checker<specs_handler<parse_context_t, Context>> handler(
|
||||
specs_handler<parse_context_t, Context>(specs, parse_context, context),
|
||||
arg.type());
|
||||
begin = parse_format_specs(begin, end, handler);
|
||||
if (begin == end || *begin != '}') on_error("missing '}' in format string");
|
||||
auto f = detail::arg_formatter<OutputIt, Char>{context.out(), specs,
|
||||
context.locale()};
|
||||
context.advance_to(visit_format_arg(f, arg));
|
||||
return begin;
|
||||
}
|
||||
};
|
||||
|
||||
template <template <typename> class Handler, typename Context>
|
||||
@ -2616,7 +2554,6 @@ std::basic_string<Char> to_string(const basic_memory_buffer<Char, SIZE>& buf) {
|
||||
detail::assume(size < std::basic_string<Char>().max_size());
|
||||
return std::basic_string<Char>(buf.data(), size);
|
||||
}
|
||||
|
||||
FMT_MODULE_EXPORT_END
|
||||
|
||||
template <typename Char>
|
||||
@ -2624,17 +2561,65 @@ void detail::vformat_to(
|
||||
detail::buffer<Char>& buf, basic_string_view<Char> fmt,
|
||||
basic_format_args<buffer_context<type_identity_t<Char>>> args,
|
||||
detail::locale_ref loc) {
|
||||
using iterator = typename buffer_context<Char>::iterator;
|
||||
auto out = buffer_appender<Char>(buf);
|
||||
if (fmt.size() == 2 && equal2(fmt.data(), "{}")) {
|
||||
auto arg = args.get(0);
|
||||
if (!arg) error_handler().on_error("argument not found");
|
||||
visit_format_arg(default_arg_formatter<iterator, Char>{out, args, loc},
|
||||
arg);
|
||||
visit_format_arg(default_arg_formatter<Char>{out, args, loc}, arg);
|
||||
return;
|
||||
}
|
||||
format_handler<iterator, Char, buffer_context<Char>> h(out, fmt, args, loc);
|
||||
parse_format_string<false>(fmt, h);
|
||||
|
||||
struct format_handler : detail::error_handler {
|
||||
basic_format_parse_context<Char> parse_context;
|
||||
buffer_context<Char> context;
|
||||
|
||||
format_handler(buffer_appender<Char> out, basic_string_view<Char> str,
|
||||
basic_format_args<buffer_context<Char>> args,
|
||||
detail::locale_ref loc)
|
||||
: parse_context(str), context(out, args, loc) {}
|
||||
|
||||
void on_text(const Char* begin, const Char* end) {
|
||||
auto text = basic_string_view<Char>(begin, to_unsigned(end - begin));
|
||||
context.advance_to(write<Char>(context.out(), text));
|
||||
}
|
||||
|
||||
int on_arg_id() { return parse_context.next_arg_id(); }
|
||||
int on_arg_id(int id) { return parse_context.check_arg_id(id), id; }
|
||||
int on_arg_id(basic_string_view<Char> id) {
|
||||
int arg_id = context.arg_id(id);
|
||||
if (arg_id < 0) on_error("argument not found");
|
||||
return arg_id;
|
||||
}
|
||||
|
||||
FMT_INLINE void on_replacement_field(int id, const Char*) {
|
||||
auto arg = get_arg(context, id);
|
||||
context.advance_to(visit_format_arg(
|
||||
default_arg_formatter<Char>{context.out(), context.args(),
|
||||
context.locale()},
|
||||
arg));
|
||||
}
|
||||
|
||||
const Char* on_format_specs(int id, const Char* begin, const Char* end) {
|
||||
auto arg = get_arg(context, id);
|
||||
if (arg.type() == type::custom_type) {
|
||||
parse_context.advance_to(parse_context.begin() +
|
||||
(begin - &*parse_context.begin()));
|
||||
visit_format_arg(custom_formatter<Char>{parse_context, context}, arg);
|
||||
return parse_context.begin();
|
||||
}
|
||||
auto specs = basic_format_specs<Char>();
|
||||
specs_checker<specs_handler<Char>> handler(
|
||||
specs_handler<Char>(specs, parse_context, context), arg.type());
|
||||
begin = parse_format_specs(begin, end, handler);
|
||||
if (begin == end || *begin != '}')
|
||||
on_error("missing '}' in format string");
|
||||
auto f =
|
||||
detail::arg_formatter<Char>{context.out(), specs, context.locale()};
|
||||
context.advance_to(visit_format_arg(f, arg));
|
||||
return begin;
|
||||
}
|
||||
};
|
||||
parse_format_string<false>(fmt, format_handler(out, fmt, args, loc));
|
||||
}
|
||||
|
||||
#ifndef FMT_HEADER_ONLY
|
||||
|
@ -191,9 +191,9 @@ template <typename Char> class printf_width_handler {
|
||||
|
||||
// The ``printf`` argument formatter.
|
||||
template <typename OutputIt, typename Char>
|
||||
class printf_arg_formatter : public arg_formatter<OutputIt, Char> {
|
||||
class printf_arg_formatter : public arg_formatter<Char> {
|
||||
private:
|
||||
using base = arg_formatter<OutputIt, Char>;
|
||||
using base = arg_formatter<Char>;
|
||||
using context_type = basic_printf_context<OutputIt, Char>;
|
||||
using format_specs = basic_format_specs<Char>;
|
||||
|
||||
|
@ -28,31 +28,6 @@ template FMT_API dragonbox::decimal_fp<float> dragonbox::to_decimal(float x)
|
||||
FMT_NOEXCEPT;
|
||||
template FMT_API dragonbox::decimal_fp<double> dragonbox::to_decimal(double x)
|
||||
FMT_NOEXCEPT;
|
||||
|
||||
// DEPRECATED! This function exists for ABI compatibility.
|
||||
template <typename Char>
|
||||
typename basic_format_context<std::back_insert_iterator<buffer<Char>>,
|
||||
Char>::iterator
|
||||
vformat_to(buffer<Char>& buf, basic_string_view<Char> format_str,
|
||||
basic_format_args<basic_format_context<
|
||||
std::back_insert_iterator<buffer<type_identity_t<Char>>>,
|
||||
type_identity_t<Char>>>
|
||||
args) {
|
||||
using iterator = std::back_insert_iterator<buffer<char>>;
|
||||
using context = basic_format_context<
|
||||
std::back_insert_iterator<buffer<type_identity_t<Char>>>,
|
||||
type_identity_t<Char>>;
|
||||
auto out = iterator(buf);
|
||||
format_handler<iterator, Char, context> h(out, format_str, args, {});
|
||||
parse_format_string<false>(format_str, h);
|
||||
return out;
|
||||
}
|
||||
template basic_format_context<std::back_insert_iterator<buffer<char>>,
|
||||
char>::iterator
|
||||
vformat_to(buffer<char>&, string_view,
|
||||
basic_format_args<basic_format_context<
|
||||
std::back_insert_iterator<buffer<type_identity_t<char>>>,
|
||||
type_identity_t<char>>>);
|
||||
} // namespace detail
|
||||
|
||||
// Workaround a bug in MSVC2013 that prevents instantiation of format_float.
|
||||
|
@ -501,6 +501,210 @@ TEST(arg_test, visit_invalid_arg) {
|
||||
fmt::visit_format_arg(visitor, arg);
|
||||
}
|
||||
|
||||
#if FMT_USE_CONSTEXPR
|
||||
|
||||
enum class arg_id_result { none, empty, index, name, error };
|
||||
struct test_arg_id_handler {
|
||||
arg_id_result res = arg_id_result::none;
|
||||
int index = 0;
|
||||
string_view name;
|
||||
|
||||
constexpr void operator()() { res = arg_id_result::empty; }
|
||||
|
||||
constexpr void operator()(int i) {
|
||||
res = arg_id_result::index;
|
||||
index = i;
|
||||
}
|
||||
|
||||
constexpr void operator()(string_view n) {
|
||||
res = arg_id_result::name;
|
||||
name = n;
|
||||
}
|
||||
|
||||
constexpr void on_error(const char*) { res = arg_id_result::error; }
|
||||
};
|
||||
|
||||
template <size_t N>
|
||||
constexpr test_arg_id_handler parse_arg_id(const char (&s)[N]) {
|
||||
test_arg_id_handler h;
|
||||
fmt::detail::parse_arg_id(s, s + N, h);
|
||||
return h;
|
||||
}
|
||||
|
||||
TEST(format_test, constexpr_parse_arg_id) {
|
||||
static_assert(parse_arg_id(":").res == arg_id_result::empty, "");
|
||||
static_assert(parse_arg_id("}").res == arg_id_result::empty, "");
|
||||
static_assert(parse_arg_id("42:").res == arg_id_result::index, "");
|
||||
static_assert(parse_arg_id("42:").index == 42, "");
|
||||
static_assert(parse_arg_id("foo:").res == arg_id_result::name, "");
|
||||
static_assert(parse_arg_id("foo:").name.size() == 3, "");
|
||||
static_assert(parse_arg_id("!").res == arg_id_result::error, "");
|
||||
}
|
||||
|
||||
struct test_format_specs_handler {
|
||||
enum result { none, hash, zero, loc, error };
|
||||
result res = none;
|
||||
|
||||
fmt::align_t alignment = fmt::align::none;
|
||||
fmt::sign_t sign = fmt::sign::none;
|
||||
char fill = 0;
|
||||
int width = 0;
|
||||
fmt::detail::arg_ref<char> width_ref;
|
||||
int precision = 0;
|
||||
fmt::detail::arg_ref<char> precision_ref;
|
||||
char type = 0;
|
||||
|
||||
// Workaround for MSVC2017 bug that results in "expression did not evaluate
|
||||
// to a constant" with compiler-generated copy ctor.
|
||||
constexpr test_format_specs_handler() {}
|
||||
constexpr test_format_specs_handler(const test_format_specs_handler& other) =
|
||||
default;
|
||||
|
||||
constexpr void on_align(fmt::align_t a) { alignment = a; }
|
||||
constexpr void on_fill(fmt::string_view f) { fill = f[0]; }
|
||||
constexpr void on_sign(fmt::sign_t s) { sign = s; }
|
||||
constexpr void on_hash() { res = hash; }
|
||||
constexpr void on_zero() { res = zero; }
|
||||
constexpr void on_localized() { res = loc; }
|
||||
|
||||
constexpr void on_width(int w) { width = w; }
|
||||
constexpr void on_dynamic_width(fmt::detail::auto_id) {}
|
||||
constexpr void on_dynamic_width(int index) { width_ref = index; }
|
||||
constexpr void on_dynamic_width(string_view) {}
|
||||
|
||||
constexpr void on_precision(int p) { precision = p; }
|
||||
constexpr void on_dynamic_precision(fmt::detail::auto_id) {}
|
||||
constexpr void on_dynamic_precision(int index) { precision_ref = index; }
|
||||
constexpr void on_dynamic_precision(string_view) {}
|
||||
|
||||
constexpr void end_precision() {}
|
||||
constexpr void on_type(char t) { type = t; }
|
||||
constexpr void on_error(const char*) { res = error; }
|
||||
};
|
||||
|
||||
template <size_t N>
|
||||
constexpr test_format_specs_handler parse_test_specs(const char (&s)[N]) {
|
||||
auto h = test_format_specs_handler();
|
||||
fmt::detail::parse_format_specs(s, s + N, h);
|
||||
return h;
|
||||
}
|
||||
|
||||
TEST(core_test, constexpr_parse_format_specs) {
|
||||
using handler = test_format_specs_handler;
|
||||
static_assert(parse_test_specs("<").alignment == fmt::align::left, "");
|
||||
static_assert(parse_test_specs("*^").fill == '*', "");
|
||||
static_assert(parse_test_specs("+").sign == fmt::sign::plus, "");
|
||||
static_assert(parse_test_specs("-").sign == fmt::sign::minus, "");
|
||||
static_assert(parse_test_specs(" ").sign == fmt::sign::space, "");
|
||||
static_assert(parse_test_specs("#").res == handler::hash, "");
|
||||
static_assert(parse_test_specs("0").res == handler::zero, "");
|
||||
static_assert(parse_test_specs("L").res == handler::loc, "");
|
||||
static_assert(parse_test_specs("42").width == 42, "");
|
||||
static_assert(parse_test_specs("{42}").width_ref.val.index == 42, "");
|
||||
static_assert(parse_test_specs(".42").precision == 42, "");
|
||||
static_assert(parse_test_specs(".{42}").precision_ref.val.index == 42, "");
|
||||
static_assert(parse_test_specs("d").type == 'd', "");
|
||||
static_assert(parse_test_specs("{<").res == handler::error, "");
|
||||
}
|
||||
|
||||
struct test_parse_context {
|
||||
using char_type = char;
|
||||
|
||||
constexpr int next_arg_id() { return 11; }
|
||||
template <typename Id> FMT_CONSTEXPR void check_arg_id(Id) {}
|
||||
|
||||
constexpr const char* begin() { return nullptr; }
|
||||
constexpr const char* end() { return nullptr; }
|
||||
|
||||
void on_error(const char*) {}
|
||||
};
|
||||
|
||||
template <size_t N>
|
||||
constexpr fmt::detail::dynamic_format_specs<char> parse_dynamic_specs(
|
||||
const char (&s)[N]) {
|
||||
auto specs = fmt::detail::dynamic_format_specs<char>();
|
||||
auto ctx = test_parse_context();
|
||||
auto h = fmt::detail::dynamic_specs_handler<test_parse_context>(specs, ctx);
|
||||
parse_format_specs(s, s + N, h);
|
||||
return specs;
|
||||
}
|
||||
|
||||
TEST(format_test, constexpr_dynamic_specs_handler) {
|
||||
static_assert(parse_dynamic_specs("<").align == fmt::align::left, "");
|
||||
static_assert(parse_dynamic_specs("*^").fill[0] == '*', "");
|
||||
static_assert(parse_dynamic_specs("+").sign == fmt::sign::plus, "");
|
||||
static_assert(parse_dynamic_specs("-").sign == fmt::sign::minus, "");
|
||||
static_assert(parse_dynamic_specs(" ").sign == fmt::sign::space, "");
|
||||
static_assert(parse_dynamic_specs("#").alt, "");
|
||||
static_assert(parse_dynamic_specs("0").align == fmt::align::numeric, "");
|
||||
static_assert(parse_dynamic_specs("42").width == 42, "");
|
||||
static_assert(parse_dynamic_specs("{}").width_ref.val.index == 11, "");
|
||||
static_assert(parse_dynamic_specs("{42}").width_ref.val.index == 42, "");
|
||||
static_assert(parse_dynamic_specs(".42").precision == 42, "");
|
||||
static_assert(parse_dynamic_specs(".{}").precision_ref.val.index == 11, "");
|
||||
static_assert(parse_dynamic_specs(".{42}").precision_ref.val.index == 42, "");
|
||||
static_assert(parse_dynamic_specs("d").type == 'd', "");
|
||||
}
|
||||
|
||||
template <size_t N>
|
||||
constexpr test_format_specs_handler check_specs(const char (&s)[N]) {
|
||||
fmt::detail::specs_checker<test_format_specs_handler> checker(
|
||||
test_format_specs_handler(), fmt::detail::type::double_type);
|
||||
parse_format_specs(s, s + N, checker);
|
||||
return checker;
|
||||
}
|
||||
|
||||
TEST(format_test, constexpr_specs_checker) {
|
||||
using handler = test_format_specs_handler;
|
||||
static_assert(check_specs("<").alignment == fmt::align::left, "");
|
||||
static_assert(check_specs("*^").fill == '*', "");
|
||||
static_assert(check_specs("+").sign == fmt::sign::plus, "");
|
||||
static_assert(check_specs("-").sign == fmt::sign::minus, "");
|
||||
static_assert(check_specs(" ").sign == fmt::sign::space, "");
|
||||
static_assert(check_specs("#").res == handler::hash, "");
|
||||
static_assert(check_specs("0").res == handler::zero, "");
|
||||
static_assert(check_specs("42").width == 42, "");
|
||||
static_assert(check_specs("{42}").width_ref.val.index == 42, "");
|
||||
static_assert(check_specs(".42").precision == 42, "");
|
||||
static_assert(check_specs(".{42}").precision_ref.val.index == 42, "");
|
||||
static_assert(check_specs("d").type == 'd', "");
|
||||
static_assert(check_specs("{<").res == handler::error, "");
|
||||
}
|
||||
|
||||
struct test_format_string_handler {
|
||||
constexpr void on_text(const char*, const char*) {}
|
||||
|
||||
constexpr int on_arg_id() { return 0; }
|
||||
|
||||
template <typename T> constexpr int on_arg_id(T) { return 0; }
|
||||
|
||||
constexpr void on_replacement_field(int, const char*) {}
|
||||
|
||||
constexpr const char* on_format_specs(int, const char* begin, const char*) {
|
||||
return begin;
|
||||
}
|
||||
|
||||
constexpr void on_error(const char*) { error = true; }
|
||||
|
||||
bool error = false;
|
||||
};
|
||||
|
||||
template <size_t N> constexpr bool parse_string(const char (&s)[N]) {
|
||||
auto h = test_format_string_handler();
|
||||
fmt::detail::parse_format_string<true>(fmt::string_view(s, N - 1), h);
|
||||
return !h.error;
|
||||
}
|
||||
|
||||
TEST(format_test, constexpr_parse_format_string) {
|
||||
static_assert(parse_string("foo"), "");
|
||||
static_assert(!parse_string("}"), "");
|
||||
static_assert(parse_string("{}"), "");
|
||||
static_assert(parse_string("{42}"), "");
|
||||
static_assert(parse_string("{foo}"), "");
|
||||
static_assert(parse_string("{:}"), "");
|
||||
}
|
||||
#endif // FMT_USE_CONSTEXPR
|
||||
|
||||
struct enabled_formatter {};
|
||||
struct disabled_formatter {};
|
||||
struct disabled_formatter_convertible {
|
||||
|
@ -2014,255 +2014,6 @@ TEST(format_test, format_to_n_output_iterator) {
|
||||
}
|
||||
|
||||
#if FMT_USE_CONSTEXPR
|
||||
enum class arg_id_result { none, empty, index, name, error };
|
||||
struct test_arg_id_handler {
|
||||
arg_id_result res = arg_id_result::none;
|
||||
int index = 0;
|
||||
string_view name;
|
||||
|
||||
FMT_CONSTEXPR void operator()() { res = arg_id_result::empty; }
|
||||
|
||||
FMT_CONSTEXPR void operator()(int i) {
|
||||
res = arg_id_result::index;
|
||||
index = i;
|
||||
}
|
||||
|
||||
FMT_CONSTEXPR void operator()(string_view n) {
|
||||
res = arg_id_result::name;
|
||||
name = n;
|
||||
}
|
||||
|
||||
FMT_CONSTEXPR void on_error(const char*) { res = arg_id_result::error; }
|
||||
};
|
||||
|
||||
template <size_t N>
|
||||
FMT_CONSTEXPR test_arg_id_handler parse_arg_id(const char (&s)[N]) {
|
||||
test_arg_id_handler h;
|
||||
fmt::detail::parse_arg_id(s, s + N, h);
|
||||
return h;
|
||||
}
|
||||
|
||||
TEST(format_test, constexpr_parse_arg_id) {
|
||||
static_assert(parse_arg_id(":").res == arg_id_result::empty, "");
|
||||
static_assert(parse_arg_id("}").res == arg_id_result::empty, "");
|
||||
static_assert(parse_arg_id("42:").res == arg_id_result::index, "");
|
||||
static_assert(parse_arg_id("42:").index == 42, "");
|
||||
static_assert(parse_arg_id("foo:").res == arg_id_result::name, "");
|
||||
static_assert(parse_arg_id("foo:").name.size() == 3, "");
|
||||
static_assert(parse_arg_id("!").res == arg_id_result::error, "");
|
||||
}
|
||||
|
||||
struct test_format_specs_handler {
|
||||
enum result { none, hash, zero, loc, error };
|
||||
result res = none;
|
||||
|
||||
fmt::align_t alignment = fmt::align::none;
|
||||
fmt::sign_t sign = fmt::sign::none;
|
||||
char fill = 0;
|
||||
int width = 0;
|
||||
fmt::detail::arg_ref<char> width_ref;
|
||||
int precision = 0;
|
||||
fmt::detail::arg_ref<char> precision_ref;
|
||||
char type = 0;
|
||||
|
||||
// Workaround for MSVC2017 bug that results in "expression did not evaluate
|
||||
// to a constant" with compiler-generated copy ctor.
|
||||
FMT_CONSTEXPR test_format_specs_handler() {}
|
||||
FMT_CONSTEXPR test_format_specs_handler(
|
||||
const test_format_specs_handler& other) = default;
|
||||
|
||||
FMT_CONSTEXPR void on_align(fmt::align_t a) { alignment = a; }
|
||||
FMT_CONSTEXPR void on_fill(fmt::string_view f) { fill = f[0]; }
|
||||
FMT_CONSTEXPR void on_sign(fmt::sign_t s) { sign = s; }
|
||||
FMT_CONSTEXPR void on_hash() { res = hash; }
|
||||
FMT_CONSTEXPR void on_zero() { res = zero; }
|
||||
FMT_CONSTEXPR void on_localized() { res = loc; }
|
||||
|
||||
FMT_CONSTEXPR void on_width(int w) { width = w; }
|
||||
FMT_CONSTEXPR void on_dynamic_width(fmt::detail::auto_id) {}
|
||||
FMT_CONSTEXPR void on_dynamic_width(int index) { width_ref = index; }
|
||||
FMT_CONSTEXPR void on_dynamic_width(string_view) {}
|
||||
|
||||
FMT_CONSTEXPR void on_precision(int p) { precision = p; }
|
||||
FMT_CONSTEXPR void on_dynamic_precision(fmt::detail::auto_id) {}
|
||||
FMT_CONSTEXPR void on_dynamic_precision(int index) { precision_ref = index; }
|
||||
FMT_CONSTEXPR void on_dynamic_precision(string_view) {}
|
||||
|
||||
FMT_CONSTEXPR void end_precision() {}
|
||||
FMT_CONSTEXPR void on_type(char t) { type = t; }
|
||||
FMT_CONSTEXPR void on_error(const char*) { res = error; }
|
||||
};
|
||||
|
||||
template <size_t N>
|
||||
FMT_CONSTEXPR test_format_specs_handler parse_test_specs(const char (&s)[N]) {
|
||||
auto h = test_format_specs_handler();
|
||||
fmt::detail::parse_format_specs(s, s + N, h);
|
||||
return h;
|
||||
}
|
||||
|
||||
TEST(format_test, constexpr_parse_format_specs) {
|
||||
using handler = test_format_specs_handler;
|
||||
static_assert(parse_test_specs("<").alignment == fmt::align::left, "");
|
||||
static_assert(parse_test_specs("*^").fill == '*', "");
|
||||
static_assert(parse_test_specs("+").sign == fmt::sign::plus, "");
|
||||
static_assert(parse_test_specs("-").sign == fmt::sign::minus, "");
|
||||
static_assert(parse_test_specs(" ").sign == fmt::sign::space, "");
|
||||
static_assert(parse_test_specs("#").res == handler::hash, "");
|
||||
static_assert(parse_test_specs("0").res == handler::zero, "");
|
||||
static_assert(parse_test_specs("L").res == handler::loc, "");
|
||||
static_assert(parse_test_specs("42").width == 42, "");
|
||||
static_assert(parse_test_specs("{42}").width_ref.val.index == 42, "");
|
||||
static_assert(parse_test_specs(".42").precision == 42, "");
|
||||
static_assert(parse_test_specs(".{42}").precision_ref.val.index == 42, "");
|
||||
static_assert(parse_test_specs("d").type == 'd', "");
|
||||
static_assert(parse_test_specs("{<").res == handler::error, "");
|
||||
}
|
||||
|
||||
struct test_parse_context {
|
||||
typedef char char_type;
|
||||
|
||||
FMT_CONSTEXPR int next_arg_id() { return 11; }
|
||||
template <typename Id> FMT_CONSTEXPR void check_arg_id(Id) {}
|
||||
|
||||
FMT_CONSTEXPR const char* begin() { return nullptr; }
|
||||
FMT_CONSTEXPR const char* end() { return nullptr; }
|
||||
|
||||
void on_error(const char*) {}
|
||||
};
|
||||
|
||||
struct test_context {
|
||||
using char_type = char;
|
||||
using format_arg = fmt::basic_format_arg<test_context>;
|
||||
using parse_context_type = fmt::format_parse_context;
|
||||
|
||||
template <typename T> struct formatter_type {
|
||||
using type = fmt::formatter<T, char_type>;
|
||||
};
|
||||
|
||||
template <typename Id>
|
||||
FMT_CONSTEXPR fmt::basic_format_arg<test_context> arg(Id id) {
|
||||
return fmt::detail::make_arg<test_context>(id);
|
||||
}
|
||||
|
||||
void on_error(const char*) {}
|
||||
|
||||
FMT_CONSTEXPR test_context error_handler() { return *this; }
|
||||
};
|
||||
|
||||
template <size_t N>
|
||||
FMT_CONSTEXPR fmt::format_specs parse_specs(const char (&s)[N]) {
|
||||
auto specs = fmt::format_specs();
|
||||
auto parse_ctx = test_parse_context();
|
||||
auto ctx = test_context();
|
||||
fmt::detail::specs_handler<test_parse_context, test_context> h(
|
||||
specs, parse_ctx, ctx);
|
||||
parse_format_specs(s, s + N, h);
|
||||
return specs;
|
||||
}
|
||||
|
||||
TEST(format_test, constexpr_specs_handler) {
|
||||
static_assert(parse_specs("<").align == fmt::align::left, "");
|
||||
static_assert(parse_specs("*^").fill[0] == '*', "");
|
||||
static_assert(parse_specs("+").sign == fmt::sign::plus, "");
|
||||
static_assert(parse_specs("-").sign == fmt::sign::minus, "");
|
||||
static_assert(parse_specs(" ").sign == fmt::sign::space, "");
|
||||
static_assert(parse_specs("#").alt, "");
|
||||
static_assert(parse_specs("0").align == fmt::align::numeric, "");
|
||||
static_assert(parse_specs("42").width == 42, "");
|
||||
static_assert(parse_specs("{}").width == 11, "");
|
||||
static_assert(parse_specs("{22}").width == 22, "");
|
||||
static_assert(parse_specs(".42").precision == 42, "");
|
||||
static_assert(parse_specs(".{}").precision == 11, "");
|
||||
static_assert(parse_specs(".{22}").precision == 22, "");
|
||||
static_assert(parse_specs("d").type == 'd', "");
|
||||
}
|
||||
|
||||
template <size_t N>
|
||||
FMT_CONSTEXPR fmt::detail::dynamic_format_specs<char> parse_dynamic_specs(
|
||||
const char (&s)[N]) {
|
||||
auto specs = fmt::detail::dynamic_format_specs<char>();
|
||||
auto ctx = test_parse_context();
|
||||
auto h = fmt::detail::dynamic_specs_handler<test_parse_context>(specs, ctx);
|
||||
parse_format_specs(s, s + N, h);
|
||||
return specs;
|
||||
}
|
||||
|
||||
TEST(format_test, constexpr_dynamic_specs_handler) {
|
||||
static_assert(parse_dynamic_specs("<").align == fmt::align::left, "");
|
||||
static_assert(parse_dynamic_specs("*^").fill[0] == '*', "");
|
||||
static_assert(parse_dynamic_specs("+").sign == fmt::sign::plus, "");
|
||||
static_assert(parse_dynamic_specs("-").sign == fmt::sign::minus, "");
|
||||
static_assert(parse_dynamic_specs(" ").sign == fmt::sign::space, "");
|
||||
static_assert(parse_dynamic_specs("#").alt, "");
|
||||
static_assert(parse_dynamic_specs("0").align == fmt::align::numeric, "");
|
||||
static_assert(parse_dynamic_specs("42").width == 42, "");
|
||||
static_assert(parse_dynamic_specs("{}").width_ref.val.index == 11, "");
|
||||
static_assert(parse_dynamic_specs("{42}").width_ref.val.index == 42, "");
|
||||
static_assert(parse_dynamic_specs(".42").precision == 42, "");
|
||||
static_assert(parse_dynamic_specs(".{}").precision_ref.val.index == 11, "");
|
||||
static_assert(parse_dynamic_specs(".{42}").precision_ref.val.index == 42, "");
|
||||
static_assert(parse_dynamic_specs("d").type == 'd', "");
|
||||
}
|
||||
|
||||
template <size_t N>
|
||||
FMT_CONSTEXPR test_format_specs_handler check_specs(const char (&s)[N]) {
|
||||
fmt::detail::specs_checker<test_format_specs_handler> checker(
|
||||
test_format_specs_handler(), fmt::detail::type::double_type);
|
||||
parse_format_specs(s, s + N, checker);
|
||||
return checker;
|
||||
}
|
||||
|
||||
TEST(format_test, constexpr_specs_checker) {
|
||||
using handler = test_format_specs_handler;
|
||||
static_assert(check_specs("<").alignment == fmt::align::left, "");
|
||||
static_assert(check_specs("*^").fill == '*', "");
|
||||
static_assert(check_specs("+").sign == fmt::sign::plus, "");
|
||||
static_assert(check_specs("-").sign == fmt::sign::minus, "");
|
||||
static_assert(check_specs(" ").sign == fmt::sign::space, "");
|
||||
static_assert(check_specs("#").res == handler::hash, "");
|
||||
static_assert(check_specs("0").res == handler::zero, "");
|
||||
static_assert(check_specs("42").width == 42, "");
|
||||
static_assert(check_specs("{42}").width_ref.val.index == 42, "");
|
||||
static_assert(check_specs(".42").precision == 42, "");
|
||||
static_assert(check_specs(".{42}").precision_ref.val.index == 42, "");
|
||||
static_assert(check_specs("d").type == 'd', "");
|
||||
static_assert(check_specs("{<").res == handler::error, "");
|
||||
}
|
||||
|
||||
struct test_format_string_handler {
|
||||
FMT_CONSTEXPR void on_text(const char*, const char*) {}
|
||||
|
||||
FMT_CONSTEXPR int on_arg_id() { return 0; }
|
||||
|
||||
template <typename T> FMT_CONSTEXPR int on_arg_id(T) { return 0; }
|
||||
|
||||
FMT_CONSTEXPR void on_replacement_field(int, const char*) {}
|
||||
|
||||
FMT_CONSTEXPR const char* on_format_specs(int, const char* begin,
|
||||
const char*) {
|
||||
return begin;
|
||||
}
|
||||
|
||||
FMT_CONSTEXPR void on_error(const char*) { error = true; }
|
||||
|
||||
bool error = false;
|
||||
};
|
||||
|
||||
template <size_t N> FMT_CONSTEXPR bool parse_string(const char (&s)[N]) {
|
||||
auto h = test_format_string_handler();
|
||||
fmt::detail::parse_format_string<true>(fmt::string_view(s, N - 1), h);
|
||||
return !h.error;
|
||||
}
|
||||
|
||||
TEST(format_test, constexpr_parse_format_string) {
|
||||
static_assert(parse_string("foo"), "");
|
||||
static_assert(!parse_string("}"), "");
|
||||
static_assert(parse_string("{}"), "");
|
||||
static_assert(parse_string("{42}"), "");
|
||||
static_assert(parse_string("{foo}"), "");
|
||||
static_assert(parse_string("{:}"), "");
|
||||
}
|
||||
|
||||
struct test_error_handler {
|
||||
const char*& error;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user