Reduce code bloat

This commit is contained in:
Victor Zverovich 2021-05-19 16:08:56 -07:00
parent 2a2e4c5801
commit 61b4c923d7
5 changed files with 305 additions and 390 deletions

View File

@ -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

View File

@ -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>;

View File

@ -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.

View File

@ -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 {

View File

@ -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;