constexpr support of dynamic width and precision

This commit is contained in:
Victor Zverovich 2017-10-22 09:32:46 -07:00
parent 6b3840b73c
commit d2f2a8b0ca
2 changed files with 36 additions and 35 deletions

View File

@ -850,13 +850,13 @@ constexpr const Char *pointer_from(null_terminating_iterator<Char> it) {
// Returns true if value is negative, false otherwise. // Returns true if value is negative, false otherwise.
// Same as (value < 0) but doesn't produce warnings if T is an unsigned type. // Same as (value < 0) but doesn't produce warnings if T is an unsigned type.
template <typename T> template <typename T>
inline typename std::enable_if<std::numeric_limits<T>::is_signed, bool>::type constexpr typename std::enable_if<
is_negative(T value) { std::numeric_limits<T>::is_signed, bool>::type is_negative(T value) {
return value < 0; return value < 0;
} }
template <typename T> template <typename T>
inline typename std::enable_if<!std::numeric_limits<T>::is_signed, bool>::type constexpr typename std::enable_if<
is_negative(T) { !std::numeric_limits<T>::is_signed, bool>::type is_negative(T) {
return false; return false;
} }
@ -1205,11 +1205,11 @@ class value {
custom_value<char_type> custom; custom_value<char_type> custom;
}; };
value() {} constexpr value() {}
value(bool val) { set<BOOL>(int_value, val); } value(bool val) { set<BOOL>(int_value, val); }
value(short val) { set<INT>(int_value, val); } value(short val) { set<INT>(int_value, val); }
value(unsigned short val) { set<UINT>(uint_value, val); } value(unsigned short val) { set<UINT>(uint_value, val); }
value(int val) { set<INT>(int_value, val); } constexpr value(int val) : int_value(val) {}
value(unsigned val) { set<UINT>(uint_value, val); } value(unsigned val) { set<UINT>(uint_value, val); }
value(long val) { value(long val) {
@ -1299,7 +1299,7 @@ class value {
private: private:
template <type TYPE, typename T, typename U> template <type TYPE, typename T, typename U>
void set(T &field, const U &value) { constexpr void set(T &field, const U &value) {
static_assert(get_type<U>() == TYPE, "invalid type"); static_assert(get_type<U>() == TYPE, "invalid type");
field = value; field = value;
} }
@ -1338,7 +1338,7 @@ template <typename Context>
class arg_map; class arg_map;
template <typename Context, typename T> template <typename Context, typename T>
basic_arg<Context> make_arg(const T &value); constexpr basic_arg<Context> make_arg(const T &value);
} // namespace internal } // namespace internal
struct monostate {}; struct monostate {};
@ -1355,17 +1355,17 @@ class basic_arg {
internal::type type_; internal::type type_;
template <typename ContextType, typename T> template <typename ContextType, typename T>
friend basic_arg<ContextType> internal::make_arg(const T &value); friend constexpr basic_arg<ContextType> internal::make_arg(const T &value);
template <typename Visitor, typename Ctx> template <typename Visitor, typename Ctx>
friend typename std::result_of<Visitor(int)>::type friend constexpr typename std::result_of<Visitor(int)>::type
visit(Visitor &&vis, basic_arg<Ctx> arg); visit(Visitor &&vis, basic_arg<Ctx> arg);
friend class basic_args<Context>; friend class basic_args<Context>;
friend class internal::arg_map<Context>; friend class internal::arg_map<Context>;
public: public:
basic_arg() : type_(internal::NONE) {} constexpr basic_arg() : type_(internal::NONE) {}
explicit operator bool() const noexcept { return type_ != internal::NONE; } explicit operator bool() const noexcept { return type_ != internal::NONE; }
@ -1384,7 +1384,7 @@ class basic_arg {
\endrst \endrst
*/ */
template <typename Visitor, typename Context> template <typename Visitor, typename Context>
typename std::result_of<Visitor(int)>::type constexpr typename std::result_of<Visitor(int)>::type
visit(Visitor &&vis, basic_arg<Context> arg) { visit(Visitor &&vis, basic_arg<Context> arg) {
typedef typename Context::char_type Char; typedef typename Context::char_type Char;
switch (arg.type_) { switch (arg.type_) {
@ -1427,7 +1427,7 @@ typename std::result_of<Visitor(int)>::type
namespace internal { namespace internal {
template <typename Context, typename T> template <typename Context, typename T>
basic_arg<Context> make_arg(const T &value) { constexpr basic_arg<Context> make_arg(const T &value) {
basic_arg<Context> arg; basic_arg<Context> arg;
arg.type_ = get_type<T>(); arg.type_ = get_type<T>();
arg.value_ = value; arg.value_ = value;
@ -3074,16 +3074,16 @@ struct is_integer {
struct width_checker { struct width_checker {
template <typename T> template <typename T>
typename std::enable_if<is_integer<T>::value, unsigned long long>::type constexpr typename std::enable_if<
operator()(T value) { is_integer<T>::value, unsigned long long>::type operator()(T value) {
if (is_negative(value)) if (is_negative(value))
FMT_THROW(format_error("negative width")); FMT_THROW(format_error("negative width"));
return value; return value;
} }
template <typename T> template <typename T>
typename std::enable_if<!is_integer<T>::value, unsigned long long>::type constexpr typename std::enable_if<
operator()(T) { !is_integer<T>::value, unsigned long long>::type operator()(T) {
FMT_THROW(format_error("width is not integer")); FMT_THROW(format_error("width is not integer"));
return 0; return 0;
} }
@ -3091,16 +3091,16 @@ struct width_checker {
struct precision_checker { struct precision_checker {
template <typename T> template <typename T>
typename std::enable_if<is_integer<T>::value, unsigned long long>::type constexpr typename std::enable_if<
operator()(T value) { is_integer<T>::value, unsigned long long>::type operator()(T value) {
if (is_negative(value)) if (is_negative(value))
FMT_THROW(format_error("negative precision")); FMT_THROW(format_error("negative precision"));
return value; return value;
} }
template <typename T> template <typename T>
typename std::enable_if<!is_integer<T>::value, unsigned long long>::type constexpr typename std::enable_if<
operator()(T) { !is_integer<T>::value, unsigned long long>::type operator()(T) {
FMT_THROW(format_error("precision is not integer")); FMT_THROW(format_error("precision is not integer"));
return 0; return 0;
} }
@ -3217,7 +3217,7 @@ class specs_checker : public Handler {
}; };
template <typename Handler, typename T, typename Context> template <typename Handler, typename T, typename Context>
inline void set_dynamic_spec(T &value, basic_arg<Context> arg) { constexpr void set_dynamic_spec(T &value, basic_arg<Context> arg) {
unsigned long long big_value = visit(Handler(), arg); unsigned long long big_value = visit(Handler(), arg);
if (big_value > (std::numeric_limits<int>::max)()) if (big_value > (std::numeric_limits<int>::max)())
FMT_THROW(format_error("number is too big")); FMT_THROW(format_error("number is too big"));
@ -3236,24 +3236,24 @@ class specs_handler: public specs_setter<typename Context::char_type> {
: specs_setter<char_type>(specs), context_(ctx) {} : specs_setter<char_type>(specs), context_(ctx) {}
template <typename Id> template <typename Id>
void on_dynamic_width(Id arg_id) { constexpr void on_dynamic_width(Id arg_id) {
set_dynamic_spec<internal::width_checker>( set_dynamic_spec<internal::width_checker>(
this->specs_.width_, get_arg(arg_id)); this->specs_.width_, get_arg(arg_id));
} }
template <typename Id> template <typename Id>
void on_dynamic_precision(Id arg_id) { constexpr void on_dynamic_precision(Id arg_id) {
set_dynamic_spec<internal::precision_checker>( set_dynamic_spec<internal::precision_checker>(
this->specs_.precision_, get_arg(arg_id)); this->specs_.precision_, get_arg(arg_id));
} }
private: private:
basic_arg<Context> get_arg(auto_id) { constexpr basic_arg<Context> get_arg(auto_id) {
return context_.next_arg(); return context_.next_arg();
} }
template <typename Id> template <typename Id>
basic_arg<Context> get_arg(Id arg_id) { constexpr basic_arg<Context> get_arg(Id arg_id) {
context_.check_arg_id(arg_id); context_.check_arg_id(arg_id);
return context_.get_arg(arg_id); return context_.get_arg(arg_id);
} }
@ -3511,11 +3511,8 @@ const Char *do_format_arg(basic_buffer<Char> &buffer,
basic_format_specs<Char> specs; basic_format_specs<Char> specs;
if (*it == ':') { if (*it == ':') {
ctx.advance_to(pointer_from(++it)); ctx.advance_to(pointer_from(++it));
if (visit(custom_formatter<Char, Context>(buffer, ctx), arg)) { if (visit(custom_formatter<Char, Context>(buffer, ctx), arg))
// TODO: if constexpr, then use formatter<T>::parse, else dispatch
// dynamically
return ctx.begin(); return ctx.begin();
}
specs_checker<specs_handler<Context>> specs_checker<specs_handler<Context>>
handler(specs_handler<Context>(specs, ctx), arg.type()); handler(specs_handler<Context>(specs, ctx), arg.type());
it = parse_format_specs(it, handler); it = parse_format_specs(it, handler);

View File

@ -1682,17 +1682,17 @@ TEST(FormatTest, ConstexprParseFormatSpecs) {
struct test_context { struct test_context {
using char_type = char; using char_type = char;
fmt::basic_arg<test_context> next_arg() { constexpr fmt::basic_arg<test_context> next_arg() {
return fmt::basic_arg<test_context>(); return fmt::internal::make_arg<test_context>(11);
} }
template <typename Id> template <typename Id>
fmt::basic_arg<test_context> get_arg(Id) { constexpr fmt::basic_arg<test_context> get_arg(Id) {
return fmt::basic_arg<test_context>(); return fmt::internal::make_arg<test_context>(22);
} }
template <typename Id> template <typename Id>
void check_arg_id(Id) {} constexpr void check_arg_id(Id) {}
}; };
constexpr fmt::format_specs parse_specs(const char *s) { constexpr fmt::format_specs parse_specs(const char *s) {
@ -1712,6 +1712,10 @@ TEST(FormatTest, ConstexprSpecsHandler) {
static_assert(parse_specs("#").flag(fmt::HASH_FLAG), ""); static_assert(parse_specs("#").flag(fmt::HASH_FLAG), "");
static_assert(parse_specs("0").align() == fmt::ALIGN_NUMERIC, ""); static_assert(parse_specs("0").align() == fmt::ALIGN_NUMERIC, "");
static_assert(parse_specs("42").width() == 42, ""); static_assert(parse_specs("42").width() == 42, "");
static_assert(parse_specs("{}").width() == 11, "");
static_assert(parse_specs("{0}").width() == 22, "");
static_assert(parse_specs(".42").precision() == 42, ""); static_assert(parse_specs(".42").precision() == 42, "");
static_assert(parse_specs(".{}").precision() == 11, "");
static_assert(parse_specs(".{0}").precision() == 22, "");
static_assert(parse_specs("d").type() == 'd', ""); static_assert(parse_specs("d").type() == 'd', "");
} }