mirror of
https://github.com/fmtlib/fmt.git
synced 2024-11-15 07:20:05 +00:00
constexpr support of dynamic width and precision
This commit is contained in:
parent
6b3840b73c
commit
d2f2a8b0ca
@ -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);
|
||||||
|
@ -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', "");
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user