mirror of
https://github.com/fmtlib/fmt.git
synced 2025-07-31 19:24:48 +02:00
Make format spec parsing constexpr
This commit is contained in:
@@ -58,9 +58,11 @@
|
|||||||
# define FMT_CATCH(x) if (false)
|
# define FMT_CATCH(x) if (false)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef __GNUC__
|
||||||
// Disable the warning about declaration shadowing because it affects too
|
// Disable the warning about declaration shadowing because it affects too
|
||||||
// many valid cases.
|
// many valid cases.
|
||||||
# pragma GCC diagnostic ignored "-Wshadow"
|
# pragma GCC diagnostic ignored "-Wshadow"
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef _MSC_VER
|
#ifdef _MSC_VER
|
||||||
# pragma warning(push)
|
# pragma warning(push)
|
||||||
|
37
fmt/format.h
37
fmt/format.h
@@ -3263,10 +3263,16 @@ template <typename Char>
|
|||||||
struct arg_ref {
|
struct arg_ref {
|
||||||
enum Kind { NONE, INDEX, NAME };
|
enum Kind { NONE, INDEX, NAME };
|
||||||
|
|
||||||
arg_ref() : kind(NONE) {}
|
constexpr arg_ref() : kind(NONE), index(0) {}
|
||||||
explicit arg_ref(unsigned index) : kind(INDEX), index(index) {}
|
explicit arg_ref(unsigned index) : kind(INDEX), index(index) {}
|
||||||
explicit arg_ref(basic_string_view<Char> name) : kind(NAME), name(name) {}
|
explicit arg_ref(basic_string_view<Char> name) : kind(NAME), name(name) {}
|
||||||
|
|
||||||
|
constexpr arg_ref &operator=(unsigned index) {
|
||||||
|
kind = INDEX;
|
||||||
|
this->index = index;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
Kind kind;
|
Kind kind;
|
||||||
union {
|
union {
|
||||||
unsigned index;
|
unsigned index;
|
||||||
@@ -3361,13 +3367,14 @@ constexpr Iterator parse_arg_id(Iterator it, Handler& handler) {
|
|||||||
// characters, possibly emulated via null_terminating_iterator, representing
|
// characters, possibly emulated via null_terminating_iterator, representing
|
||||||
// format specifiers.
|
// format specifiers.
|
||||||
template <typename Iterator, typename Handler>
|
template <typename Iterator, typename Handler>
|
||||||
Iterator parse_format_specs(Iterator it, Handler &handler) {
|
constexpr Iterator parse_format_specs(Iterator it, Handler &handler) {
|
||||||
using char_type = typename std::iterator_traits<Iterator>::value_type;
|
using char_type = typename std::iterator_traits<Iterator>::value_type;
|
||||||
// Parse fill and alignment.
|
// Parse fill and alignment.
|
||||||
if (char_type c = *it) {
|
if (char_type c = *it) {
|
||||||
auto p = it + 1;
|
|
||||||
alignment align = ALIGN_DEFAULT;
|
alignment align = ALIGN_DEFAULT;
|
||||||
|
int i = 1;
|
||||||
do {
|
do {
|
||||||
|
auto p = it + i;
|
||||||
switch (*p) {
|
switch (*p) {
|
||||||
case '<':
|
case '<':
|
||||||
align = ALIGN_LEFT;
|
align = ALIGN_LEFT;
|
||||||
@@ -3395,7 +3402,7 @@ Iterator parse_format_specs(Iterator it, Handler &handler) {
|
|||||||
} else ++it;
|
} else ++it;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
} while (--p >= it);
|
} while (--i >= 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse sign.
|
// Parse sign.
|
||||||
@@ -3430,15 +3437,15 @@ Iterator parse_format_specs(Iterator it, Handler &handler) {
|
|||||||
handler.on_width(parse_nonnegative_int(it));
|
handler.on_width(parse_nonnegative_int(it));
|
||||||
} else if (*it == '{') {
|
} else if (*it == '{') {
|
||||||
struct width_handler {
|
struct width_handler {
|
||||||
explicit width_handler(Handler &h) : handler(h) {}
|
explicit constexpr width_handler(Handler &h) : handler(h) {}
|
||||||
|
|
||||||
void operator()() { handler.on_dynamic_width(auto_id()); }
|
constexpr void operator()() { handler.on_dynamic_width(auto_id()); }
|
||||||
void operator()(unsigned id) { handler.on_dynamic_width(id); }
|
constexpr void operator()(unsigned id) { handler.on_dynamic_width(id); }
|
||||||
void operator()(basic_string_view<char_type> id) {
|
constexpr void operator()(basic_string_view<char_type> id) {
|
||||||
handler.on_dynamic_width(id);
|
handler.on_dynamic_width(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
void on_error(const char *message) {
|
constexpr void on_error(const char *message) {
|
||||||
handler.on_error(message);
|
handler.on_error(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3458,15 +3465,17 @@ Iterator parse_format_specs(Iterator it, Handler &handler) {
|
|||||||
handler.on_precision(parse_nonnegative_int(it));
|
handler.on_precision(parse_nonnegative_int(it));
|
||||||
} else if (*it == '{') {
|
} else if (*it == '{') {
|
||||||
struct precision_handler {
|
struct precision_handler {
|
||||||
explicit precision_handler(Handler &h) : handler(h) {}
|
explicit constexpr precision_handler(Handler &h) : handler(h) {}
|
||||||
|
|
||||||
void operator()() { handler.on_dynamic_precision(auto_id()); }
|
constexpr void operator()() { handler.on_dynamic_precision(auto_id()); }
|
||||||
void operator()(unsigned id) { handler.on_dynamic_precision(id); }
|
constexpr void operator()(unsigned id) {
|
||||||
void operator()(basic_string_view<char_type> id) {
|
handler.on_dynamic_precision(id);
|
||||||
|
}
|
||||||
|
constexpr void operator()(basic_string_view<char_type> id) {
|
||||||
handler.on_dynamic_precision(id);
|
handler.on_dynamic_precision(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
void on_error(const char *message) {
|
constexpr void on_error(const char *message) {
|
||||||
handler.on_error(message);
|
handler.on_error(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -1582,7 +1582,7 @@ TEST(FormatTest, DynamicFormatter) {
|
|||||||
format_error, "precision not allowed in integer format specifier");
|
format_error, "precision not allowed in integer format specifier");
|
||||||
}
|
}
|
||||||
|
|
||||||
struct TestHandler {
|
struct TestArgIDHandler {
|
||||||
enum Result { NONE, EMPTY, INDEX, NAME, ERROR };
|
enum Result { NONE, EMPTY, INDEX, NAME, ERROR };
|
||||||
Result result = NONE;
|
Result result = NONE;
|
||||||
unsigned index = 0;
|
unsigned index = 0;
|
||||||
@@ -1603,18 +1603,77 @@ struct TestHandler {
|
|||||||
constexpr void on_error(const char *) { result = ERROR; }
|
constexpr void on_error(const char *) { result = ERROR; }
|
||||||
};
|
};
|
||||||
|
|
||||||
constexpr TestHandler parse_arg_id(const char* id) {
|
constexpr TestArgIDHandler parse_arg_id(const char* s) {
|
||||||
TestHandler h;
|
TestArgIDHandler h;
|
||||||
fmt::internal::parse_arg_id(id, h);
|
fmt::internal::parse_arg_id(s, h);
|
||||||
return h;
|
return h;
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(FormatTest, ConstexprParseArgId) {
|
TEST(FormatTest, ConstexprParseArgID) {
|
||||||
static_assert(parse_arg_id(":").result == TestHandler::EMPTY, "");
|
static_assert(parse_arg_id(":").result == TestArgIDHandler::EMPTY, "");
|
||||||
static_assert(parse_arg_id("}").result == TestHandler::EMPTY, "");
|
static_assert(parse_arg_id("}").result == TestArgIDHandler::EMPTY, "");
|
||||||
static_assert(parse_arg_id("42:").result == TestHandler::INDEX, "");
|
static_assert(parse_arg_id("42:").result == TestArgIDHandler::INDEX, "");
|
||||||
static_assert(parse_arg_id("42:").index == 42, "");
|
static_assert(parse_arg_id("42:").index == 42, "");
|
||||||
static_assert(parse_arg_id("foo:").result == TestHandler::NAME, "");
|
static_assert(parse_arg_id("foo:").result == TestArgIDHandler::NAME, "");
|
||||||
static_assert(parse_arg_id("foo:").name.size() == 3, "");
|
static_assert(parse_arg_id("foo:").name.size() == 3, "");
|
||||||
static_assert(parse_arg_id("!").result == TestHandler::ERROR, "");
|
static_assert(parse_arg_id("!").result == TestArgIDHandler::ERROR, "");
|
||||||
|
}
|
||||||
|
|
||||||
|
struct TestFormatSpecsHandler {
|
||||||
|
enum Result { NONE, PLUS, MINUS, SPACE, HASH, ZERO, ERROR };
|
||||||
|
Result result = NONE;
|
||||||
|
|
||||||
|
fmt::alignment align = fmt::ALIGN_DEFAULT;
|
||||||
|
char fill = 0;
|
||||||
|
unsigned width = 0;
|
||||||
|
fmt::internal::arg_ref<char> width_ref;
|
||||||
|
unsigned precision = 0;
|
||||||
|
fmt::internal::arg_ref<char> precision_ref;
|
||||||
|
char type = 0;
|
||||||
|
|
||||||
|
constexpr void on_align(fmt::alignment align) { this->align = align; }
|
||||||
|
constexpr void on_fill(char fill) { this->fill = fill; }
|
||||||
|
constexpr void on_plus() { result = PLUS; }
|
||||||
|
constexpr void on_minus() { result = MINUS; }
|
||||||
|
constexpr void on_space() { result = SPACE; }
|
||||||
|
constexpr void on_hash() { result = HASH; }
|
||||||
|
constexpr void on_zero() { result = ZERO; }
|
||||||
|
|
||||||
|
constexpr void on_width(unsigned width) { this->width = width; }
|
||||||
|
constexpr void on_dynamic_width(fmt::internal::auto_id) {}
|
||||||
|
constexpr void on_dynamic_width(unsigned index) { width_ref = index; }
|
||||||
|
constexpr void on_dynamic_width(string_view) {}
|
||||||
|
|
||||||
|
constexpr void on_precision(unsigned precision) {
|
||||||
|
this->precision = precision;
|
||||||
|
}
|
||||||
|
constexpr void on_dynamic_precision(fmt::internal::auto_id) {}
|
||||||
|
constexpr void on_dynamic_precision(unsigned index) { precision_ref = index; }
|
||||||
|
constexpr void on_dynamic_precision(string_view) {}
|
||||||
|
|
||||||
|
constexpr void end_precision() {}
|
||||||
|
constexpr void on_type(char type) { this->type = type; }
|
||||||
|
constexpr void on_error(const char *) { result = ERROR; }
|
||||||
|
};
|
||||||
|
|
||||||
|
constexpr TestFormatSpecsHandler parse_specs(const char *s) {
|
||||||
|
TestFormatSpecsHandler h;
|
||||||
|
fmt::internal::parse_format_specs(s, h);
|
||||||
|
return h;
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(FormatTest, ConstexprParseFormatSpecs) {
|
||||||
|
static_assert(parse_specs("<").align == fmt::ALIGN_LEFT, "");
|
||||||
|
static_assert(parse_specs("*^").fill == '*', "");
|
||||||
|
static_assert(parse_specs("+").result == TestFormatSpecsHandler::PLUS, "");
|
||||||
|
static_assert(parse_specs("-").result == TestFormatSpecsHandler::MINUS, "");
|
||||||
|
static_assert(parse_specs(" ").result == TestFormatSpecsHandler::SPACE, "");
|
||||||
|
static_assert(parse_specs("#").result == TestFormatSpecsHandler::HASH, "");
|
||||||
|
static_assert(parse_specs("0").result == TestFormatSpecsHandler::ZERO, "");
|
||||||
|
static_assert(parse_specs("42").width == 42, "");
|
||||||
|
static_assert(parse_specs("{42}").width_ref.index == 42, "");
|
||||||
|
static_assert(parse_specs(".42").precision == 42, "");
|
||||||
|
static_assert(parse_specs(".{42}").precision_ref.index == 42, "");
|
||||||
|
static_assert(parse_specs("d").type == 'd', "");
|
||||||
|
static_assert(parse_specs("{<").result == TestFormatSpecsHandler::ERROR, "");
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user