Skip to content

Commit

Permalink
Add support of most format_specs for formatting at compile-time (#2056)
Browse files Browse the repository at this point in the history
  • Loading branch information
alexezeder authored Dec 25, 2020
1 parent a750bf3 commit bbd6ed5
Show file tree
Hide file tree
Showing 5 changed files with 182 additions and 121 deletions.
4 changes: 2 additions & 2 deletions include/fmt/compile.h
Original file line number Diff line number Diff line change
Expand Up @@ -466,10 +466,10 @@ struct is_compiled_format<field<Char, T, N>> : std::true_type {};
// A replacement field that refers to argument N and has format specifiers.
template <typename Char, typename T, int N> struct spec_field {
using char_type = Char;
mutable formatter<T, Char> fmt;
formatter<T, Char> fmt;

template <typename OutputIt, typename... Args>
OutputIt format(OutputIt out, const Args&... args) const {
constexpr OutputIt format(OutputIt out, const Args&... args) const {
// This ensures that the argument type is convertile to `const T&`.
const T& arg = get<N>(args...);
const auto& vargs =
Expand Down
83 changes: 49 additions & 34 deletions include/fmt/core.h
Original file line number Diff line number Diff line change
Expand Up @@ -283,6 +283,14 @@ struct monostate {};

namespace detail {

constexpr bool is_constant_evaluated() FMT_NOEXCEPT {
#ifdef __cpp_lib_is_constant_evaluated
return std::is_constant_evaluated();
#else
return false;
#endif
}

// A helper function to suppress "conditional expression is constant" warnings.
template <typename T> constexpr T const_check(T value) { return value; }

Expand Down Expand Up @@ -481,7 +489,7 @@ inline basic_string_view<Char> to_string_view(
}

template <typename Char>
inline basic_string_view<Char> to_string_view(basic_string_view<Char> s) {
constexpr basic_string_view<Char> to_string_view(basic_string_view<Char> s) {
return s;
}

Expand Down Expand Up @@ -938,9 +946,9 @@ struct arg_data<T, Char, NUM_ARGS, 0> {
T args_[NUM_ARGS != 0 ? NUM_ARGS : +1];

template <typename... U>
FMT_INLINE arg_data(const U&... init) : args_{init...} {}
FMT_INLINE const T* args() const { return args_; }
FMT_INLINE std::nullptr_t named_args() { return nullptr; }
FMT_CONSTEXPR arg_data(const U&... init) : args_{init...} {}
FMT_CONSTEXPR const T* args() const { return args_; }
FMT_CONSTEXPR std::nullptr_t named_args() { return nullptr; }
};

template <typename Char>
Expand All @@ -961,7 +969,7 @@ void init_named_args(named_arg_info<Char>* named_args, int arg_count,
}

template <typename... Args>
FMT_INLINE void init_named_args(std::nullptr_t, int, int, const Args&...) {}
FMT_CONSTEXPR void init_named_args(std::nullptr_t, int, int, const Args&...) {}

template <typename T> struct is_named_arg : std::false_type {};

Expand Down Expand Up @@ -1073,17 +1081,20 @@ template <typename Context> class value {

constexpr FMT_INLINE value(int val = 0) : int_value(val) {}
constexpr FMT_INLINE value(unsigned val) : uint_value(val) {}
FMT_INLINE value(long long val) : long_long_value(val) {}
FMT_INLINE value(unsigned long long val) : ulong_long_value(val) {}
constexpr FMT_INLINE value(long long val) : long_long_value(val) {}
constexpr FMT_INLINE value(unsigned long long val) : ulong_long_value(val) {}
FMT_INLINE value(int128_t val) : int128_value(val) {}
FMT_INLINE value(uint128_t val) : uint128_value(val) {}
FMT_INLINE value(float val) : float_value(val) {}
FMT_INLINE value(double val) : double_value(val) {}
FMT_INLINE value(long double val) : long_double_value(val) {}
FMT_INLINE value(bool val) : bool_value(val) {}
FMT_INLINE value(char_type val) : char_value(val) {}
FMT_INLINE value(const char_type* val) { string.data = val; }
FMT_INLINE value(basic_string_view<char_type> val) {
constexpr FMT_INLINE value(bool val) : bool_value(val) {}
constexpr FMT_INLINE value(char_type val) : char_value(val) {}
FMT_CONSTEXPR value(const char_type* val) {
string.data = val;
if (is_constant_evaluated()) string.size = {};
}
FMT_CONSTEXPR value(basic_string_view<char_type> val) {
string.data = val.data();
string.size = val.size();
}
Expand Down Expand Up @@ -1406,7 +1417,7 @@ class locale_ref {
const void* locale_; // A type-erased pointer to std::locale.

public:
locale_ref() : locale_(nullptr) {}
constexpr locale_ref() : locale_(nullptr) {}
template <typename Locale> explicit locale_ref(const Locale& loc);

explicit operator bool() const FMT_NOEXCEPT { return locale_ != nullptr; }
Expand Down Expand Up @@ -1437,7 +1448,7 @@ template <typename T> int check(unformattable) {
"formatter<T> specialization: https://fmt.dev/latest/api.html#udt");
return 0;
}
template <typename T, typename U> inline const U& check(const U& val) {
template <typename T, typename U> constexpr const U& check(const U& val) {
return val;
}

Expand All @@ -1446,7 +1457,7 @@ template <typename T, typename U> inline const U& check(const U& val) {
// another (not recommended).
template <bool IS_PACKED, typename Context, type, typename T,
FMT_ENABLE_IF(IS_PACKED)>
inline value<Context> make_arg(const T& val) {
constexpr value<Context> make_arg(const T& val) {
return check<T>(arg_mapper<Context>().map(val));
}

Expand Down Expand Up @@ -1480,28 +1491,30 @@ template <typename OutputIt, typename Char> class basic_format_context {
Constructs a ``basic_format_context`` object. References to the arguments are
stored in the object so make sure they have appropriate lifetimes.
*/
basic_format_context(OutputIt out,
basic_format_args<basic_format_context> ctx_args,
detail::locale_ref loc = detail::locale_ref())
constexpr basic_format_context(
OutputIt out, basic_format_args<basic_format_context> ctx_args,
detail::locale_ref loc = detail::locale_ref())
: out_(out), args_(ctx_args), loc_(loc) {}

format_arg arg(int id) const { return args_.get(id); }
format_arg arg(basic_string_view<char_type> name) { return args_.get(name); }
constexpr format_arg arg(int id) const { return args_.get(id); }
FMT_CONSTEXPR format_arg arg(basic_string_view<char_type> name) {
return args_.get(name);
}
int arg_id(basic_string_view<char_type> name) { return args_.get_id(name); }
const basic_format_args<basic_format_context>& args() const { return args_; }

detail::error_handler error_handler() { return {}; }
FMT_CONSTEXPR detail::error_handler error_handler() { return {}; }
void on_error(const char* message) { error_handler().on_error(message); }

// Returns an iterator to the beginning of the output range.
iterator out() { return out_; }
FMT_CONSTEXPR iterator out() { return out_; }

// Advances the begin iterator to ``it``.
void advance_to(iterator it) {
if (!detail::is_back_insert_iterator<iterator>()) out_ = it;
}

detail::locale_ref locale() { return loc_; }
FMT_CONSTEXPR detail::locale_ref locale() { return loc_; }
};

template <typename Char>
Expand Down Expand Up @@ -1550,7 +1563,7 @@ class format_arg_store
: 0);

public:
format_arg_store(const Args&... args)
FMT_CONSTEXPR format_arg_store(const Args&... args)
:
#if FMT_GCC_VERSION && FMT_GCC_VERSION < 409
basic_format_args<Context>(*this),
Expand All @@ -1571,7 +1584,7 @@ class format_arg_store
\endrst
*/
template <typename Context = format_context, typename... Args>
inline format_arg_store<Context, Args...> make_format_args(
constexpr format_arg_store<Context, Args...> make_format_args(
const Args&... args) {
return {args...};
}
Expand Down Expand Up @@ -1644,33 +1657,35 @@ template <typename Context> class basic_format_args {
const format_arg* args_;
};

bool is_packed() const { return (desc_ & detail::is_unpacked_bit) == 0; }
constexpr bool is_packed() const {
return (desc_ & detail::is_unpacked_bit) == 0;
}
bool has_named_args() const {
return (desc_ & detail::has_named_args_bit) != 0;
}

detail::type type(int index) const {
FMT_CONSTEXPR detail::type type(int index) const {
int shift = index * detail::packed_arg_bits;
unsigned int mask = (1 << detail::packed_arg_bits) - 1;
return static_cast<detail::type>((desc_ >> shift) & mask);
}

basic_format_args(unsigned long long desc,
const detail::value<Context>* values)
constexpr basic_format_args(unsigned long long desc,
const detail::value<Context>* values)
: desc_(desc), values_(values) {}
basic_format_args(unsigned long long desc, const format_arg* args)
constexpr basic_format_args(unsigned long long desc, const format_arg* args)
: desc_(desc), args_(args) {}

public:
basic_format_args() : desc_(0) {}
constexpr basic_format_args() : desc_(0), args_(nullptr) {}

/**
\rst
Constructs a `basic_format_args` object from `~fmt::format_arg_store`.
\endrst
*/
template <typename... Args>
FMT_INLINE basic_format_args(const format_arg_store<Context, Args...>& store)
constexpr basic_format_args(const format_arg_store<Context, Args...>& store)
: basic_format_args(store.desc, store.data_.args()) {}

/**
Expand All @@ -1679,20 +1694,20 @@ template <typename Context> class basic_format_args {
`~fmt::dynamic_format_arg_store`.
\endrst
*/
FMT_INLINE basic_format_args(const dynamic_format_arg_store<Context>& store)
constexpr basic_format_args(const dynamic_format_arg_store<Context>& store)
: basic_format_args(store.get_types(), store.data()) {}

/**
\rst
Constructs a `basic_format_args` object from a dynamic set of arguments.
\endrst
*/
basic_format_args(const format_arg* args, int count)
constexpr basic_format_args(const format_arg* args, int count)
: basic_format_args(detail::is_unpacked_bit | detail::to_unsigned(count),
args) {}

/** Returns the argument with the specified id. */
format_arg get(int id) const {
FMT_CONSTEXPR format_arg get(int id) const {
format_arg arg;
if (!is_packed()) {
if (id < max_size()) arg = args_[id];
Expand Down
12 changes: 6 additions & 6 deletions include/fmt/format-inl.h
Original file line number Diff line number Diff line change
Expand Up @@ -248,9 +248,6 @@ const typename basic_data<T>::digit_pair basic_data<T>::digits[] = {
{'9', '0'}, {'9', '1'}, {'9', '2'}, {'9', '3'}, {'9', '4'}, {'9', '5'},
{'9', '6'}, {'9', '7'}, {'9', '8'}, {'9', '9'}};

template <typename T>
const char basic_data<T>::hex_digits[] = "0123456789abcdef";

#define FMT_POWERS_OF_10(factor) \
factor * 10, (factor)*100, (factor)*1000, (factor)*10000, (factor)*100000, \
(factor)*1000000, (factor)*10000000, (factor)*100000000, \
Expand Down Expand Up @@ -1071,10 +1068,13 @@ const char basic_data<T>::background_color[] = "\x1b[48;2;";
template <typename T> const char basic_data<T>::reset_color[] = "\x1b[0m";
template <typename T> const wchar_t basic_data<T>::wreset_color[] = L"\x1b[0m";
template <typename T> const char basic_data<T>::signs[] = {0, '-', '+', ' '};

#if __cplusplus < 201703L
template <typename T> constexpr const char basic_data<T>::hex_digits[];
template <typename T> constexpr const char basic_data<T>::left_padding_shifts[];
template <typename T>
const char basic_data<T>::left_padding_shifts[] = {31, 31, 0, 1, 0};
template <typename T>
const char basic_data<T>::right_padding_shifts[] = {0, 31, 0, 1, 0};
constexpr const char basic_data<T>::right_padding_shifts[];
#endif

template <typename T> struct bits {
static FMT_CONSTEXPR_DECL const int value =
Expand Down
Loading

0 comments on commit bbd6ed5

Please sign in to comment.