diff --git a/include/fmt/compile.h b/include/fmt/compile.h index 65aeb445..becf0fe0 100644 --- a/include/fmt/compile.h +++ b/include/fmt/compile.h @@ -466,10 +466,10 @@ struct is_compiled_format> : std::true_type {}; // A replacement field that refers to argument N and has format specifiers. template struct spec_field { using char_type = Char; - mutable formatter fmt; + formatter fmt; template - 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(args...); const auto& vargs = diff --git a/include/fmt/core.h b/include/fmt/core.h index d7d2de50..dc2ca0b0 100644 --- a/include/fmt/core.h +++ b/include/fmt/core.h @@ -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 constexpr T const_check(T value) { return value; } @@ -481,7 +489,7 @@ inline basic_string_view to_string_view( } template -inline basic_string_view to_string_view(basic_string_view s) { +constexpr basic_string_view to_string_view(basic_string_view s) { return s; } @@ -938,9 +946,9 @@ struct arg_data { T args_[NUM_ARGS != 0 ? NUM_ARGS : +1]; template - 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 @@ -961,7 +969,7 @@ void init_named_args(named_arg_info* named_args, int arg_count, } template -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 struct is_named_arg : std::false_type {}; @@ -1073,17 +1081,20 @@ template 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 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 val) { string.data = val.data(); string.size = val.size(); } @@ -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 explicit locale_ref(const Locale& loc); explicit operator bool() const FMT_NOEXCEPT { return locale_ != nullptr; } @@ -1437,7 +1448,7 @@ template int check(unformattable) { "formatter specialization: https://fmt.dev/latest/api.html#udt"); return 0; } -template inline const U& check(const U& val) { +template constexpr const U& check(const U& val) { return val; } @@ -1446,7 +1457,7 @@ template inline const U& check(const U& val) { // another (not recommended). template -inline value make_arg(const T& val) { +constexpr value make_arg(const T& val) { return check(arg_mapper().map(val)); } @@ -1480,28 +1491,30 @@ template 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 ctx_args, - detail::locale_ref loc = detail::locale_ref()) + constexpr basic_format_context( + OutputIt out, basic_format_args 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 name) { return args_.get(name); } + constexpr format_arg arg(int id) const { return args_.get(id); } + FMT_CONSTEXPR format_arg arg(basic_string_view name) { + return args_.get(name); + } int arg_id(basic_string_view name) { return args_.get_id(name); } const basic_format_args& 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()) out_ = it; } - detail::locale_ref locale() { return loc_; } + FMT_CONSTEXPR detail::locale_ref locale() { return loc_; } }; template @@ -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(*this), @@ -1571,7 +1584,7 @@ class format_arg_store \endrst */ template -inline format_arg_store make_format_args( +constexpr format_arg_store make_format_args( const Args&... args) { return {args...}; } @@ -1644,25 +1657,27 @@ template 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((desc_ >> shift) & mask); } - basic_format_args(unsigned long long desc, - const detail::value* values) + constexpr basic_format_args(unsigned long long desc, + const detail::value* 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 @@ -1670,7 +1685,7 @@ template class basic_format_args { \endrst */ template - FMT_INLINE basic_format_args(const format_arg_store& store) + constexpr basic_format_args(const format_arg_store& store) : basic_format_args(store.desc, store.data_.args()) {} /** @@ -1679,7 +1694,7 @@ template class basic_format_args { `~fmt::dynamic_format_arg_store`. \endrst */ - FMT_INLINE basic_format_args(const dynamic_format_arg_store& store) + constexpr basic_format_args(const dynamic_format_arg_store& store) : basic_format_args(store.get_types(), store.data()) {} /** @@ -1687,12 +1702,12 @@ template class basic_format_args { 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]; diff --git a/include/fmt/format-inl.h b/include/fmt/format-inl.h index 1e38951e..a362468f 100644 --- a/include/fmt/format-inl.h +++ b/include/fmt/format-inl.h @@ -248,9 +248,6 @@ const typename basic_data::digit_pair basic_data::digits[] = { {'9', '0'}, {'9', '1'}, {'9', '2'}, {'9', '3'}, {'9', '4'}, {'9', '5'}, {'9', '6'}, {'9', '7'}, {'9', '8'}, {'9', '9'}}; -template -const char basic_data::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, \ @@ -1071,10 +1068,13 @@ const char basic_data::background_color[] = "\x1b[48;2;"; template const char basic_data::reset_color[] = "\x1b[0m"; template const wchar_t basic_data::wreset_color[] = L"\x1b[0m"; template const char basic_data::signs[] = {0, '-', '+', ' '}; + +#if __cplusplus < 201703L +template constexpr const char basic_data::hex_digits[]; +template constexpr const char basic_data::left_padding_shifts[]; template -const char basic_data::left_padding_shifts[] = {31, 31, 0, 1, 0}; -template -const char basic_data::right_padding_shifts[] = {0, 31, 0, 1, 0}; +constexpr const char basic_data::right_padding_shifts[]; +#endif template struct bits { static FMT_CONSTEXPR_DECL const int value = diff --git a/include/fmt/format.h b/include/fmt/format.h index c930b40f..580e9f29 100644 --- a/include/fmt/format.h +++ b/include/fmt/format.h @@ -283,20 +283,13 @@ FMT_END_NAMESPACE FMT_BEGIN_NAMESPACE namespace detail { -#if __cplusplus >= 202002L +#if __cplusplus >= 202002L || \ + (__cplusplus >= 201709L && FMT_GCC_VERSION >= 1002) # define FMT_CONSTEXPR20 constexpr #else # define FMT_CONSTEXPR20 inline #endif -constexpr bool is_constant_evaluated() FMT_DETECTED_NOEXCEPT { -#ifdef __cpp_lib_is_constant_evaluated - return std::is_constant_evaluated(); -#else - return false; -#endif -} - // An equivalent of `*reinterpret_cast(&source)` that doesn't have // undefined behavior (e.g. due to type aliasing). // Example: uint64_t d = bit_cast(2.718); @@ -552,7 +545,7 @@ inline size_t count_code_points(basic_string_view s) { } // Counts the number of code points in a UTF-8 string. -inline size_t count_code_points(basic_string_view s) { +FMT_CONSTEXPR size_t count_code_points(basic_string_view s) { const char* data = s.data(); size_t num_code_points = 0; for (size_t i = 0, size = s.size(); i != size; ++i) { @@ -585,12 +578,15 @@ inline size_t code_point_index(basic_string_view s, size_t n) { // is spectacularly slow to compile in C++20 so use a simple fill_n // instead (#1998). template -OutputIt fill_n(OutputIt out, Size count, const T& value) { +FMT_CONSTEXPR OutputIt fill_n(OutputIt out, Size count, const T& value) { for (Size i = 0; i < count; ++i) *out++ = value; return out; } template -inline T* fill_n(T* out, Size count, char value) { +FMT_CONSTEXPR20 T* fill_n(T* out, Size count, char value) { + if (is_constant_evaluated()) { + return fill_n(out, count, value); + } std::memset(out, value, to_unsigned(count)); return out + count; } @@ -938,14 +934,14 @@ template struct FMT_EXTERN_TEMPLATE_API basic_data { // GCC generates slightly better code for pairs than chars. using digit_pair = char[2]; static const digit_pair digits[]; - static const char hex_digits[]; + static constexpr const char hex_digits[] = "0123456789abcdef"; static const char foreground_color[]; static const char background_color[]; static const char reset_color[5]; static const wchar_t wreset_color[5]; static const char signs[]; - static const char left_padding_shifts[5]; - static const char right_padding_shifts[5]; + static constexpr const char left_padding_shifts[] = {31, 31, 0, 1, 0}; + static constexpr const char right_padding_shifts[] = {0, 31, 0, 1, 0}; // DEPRECATED! These are for ABI compatibility. static const uint32_t zero_or_powers_of_10_32[]; @@ -1144,8 +1140,8 @@ inline format_decimal_result format_decimal(Iterator out, UInt value, } template -inline Char* format_uint(Char* buffer, UInt value, int num_digits, - bool upper = false) { +FMT_CONSTEXPR Char* format_uint(Char* buffer, UInt value, int num_digits, + bool upper = false) { buffer += num_digits; Char* end = buffer; do { @@ -1224,8 +1220,8 @@ template struct fill_t { size_ = static_cast(size); } - size_t size() const { return size_; } - const Char* data() const { return data_; } + constexpr size_t size() const { return size_; } + constexpr const Char* data() const { return data_; } FMT_CONSTEXPR Char& operator[](size_t index) { return data_[index]; } FMT_CONSTEXPR const Char& operator[](size_t index) const { @@ -1544,7 +1540,8 @@ class cstring_type_checker : public ErrorHandler { }; template -FMT_NOINLINE OutputIt fill(OutputIt it, size_t n, const fill_t& fill) { +FMT_NOINLINE FMT_CONSTEXPR OutputIt fill(OutputIt it, size_t n, + const fill_t& fill) { auto fill_size = fill.size(); if (fill_size == 1) return detail::fill_n(it, n, fill[0]); auto data = fill.data(); @@ -1558,15 +1555,16 @@ FMT_NOINLINE OutputIt fill(OutputIt it, size_t n, const fill_t& fill) { // width: output display width in (terminal) column positions. template -inline OutputIt write_padded(OutputIt out, - const basic_format_specs& specs, size_t size, - size_t width, F&& f) { +FMT_CONSTEXPR OutputIt write_padded(OutputIt out, + const basic_format_specs& specs, + size_t size, size_t width, F&& f) { static_assert(align == align::left || align == align::right, ""); unsigned spec_width = to_unsigned(specs.width); size_t padding = spec_width > width ? spec_width - width : 0; + size_t left_padding = 0; auto* shifts = align == align::left ? data::left_padding_shifts : data::right_padding_shifts; - size_t left_padding = padding >> shifts[specs.align]; + left_padding = padding >> shifts[specs.align]; auto it = reserve(out, size + padding * specs.fill.size()); it = fill(it, left_padding, specs.fill); it = f(it); @@ -1576,9 +1574,9 @@ inline OutputIt write_padded(OutputIt out, template -inline OutputIt write_padded(OutputIt out, - const basic_format_specs& specs, size_t size, - F&& f) { +constexpr OutputIt write_padded(OutputIt out, + const basic_format_specs& specs, + size_t size, F&& f) { return write_padded(out, specs, size, size, f); } @@ -1598,8 +1596,8 @@ template struct write_int_data { size_t size; size_t padding; - write_int_data(int num_digits, string_view prefix, - const basic_format_specs& specs) + FMT_CONSTEXPR write_int_data(int num_digits, string_view prefix, + const basic_format_specs& specs) : size(prefix.size() + to_unsigned(num_digits)), padding(0) { if (specs.align == align::numeric) { auto width = to_unsigned(specs.width); @@ -1618,8 +1616,9 @@ template struct write_int_data { // // where are written by f(it). template -OutputIt write_int(OutputIt out, int num_digits, string_view prefix, - const basic_format_specs& specs, F f) { +FMT_CONSTEXPR OutputIt write_int(OutputIt out, int num_digits, + string_view prefix, + const basic_format_specs& specs, F f) { auto data = write_int_data(num_digits, prefix, specs); using iterator = remove_reference_t; return write_padded(out, specs, data.size, [=](iterator it) { @@ -1631,8 +1630,8 @@ OutputIt write_int(OutputIt out, int num_digits, string_view prefix, } template -OutputIt write(OutputIt out, basic_string_view s, - const basic_format_specs& specs) { +FMT_CONSTEXPR OutputIt write(OutputIt out, basic_string_view s, + const basic_format_specs& specs) { auto data = s.data(); auto size = s.size(); if (specs.precision >= 0 && to_unsigned(specs.precision) < size) @@ -1658,11 +1657,13 @@ template struct int_writer { using iterator = remove_reference_t(), 0))>; - string_view get_prefix() const { return string_view(prefix, prefix_size); } + constexpr string_view get_prefix() const { + return string_view(prefix, prefix_size); + } template - int_writer(OutputIt output, locale_ref loc, Int value, - const basic_format_specs& s) + FMT_CONSTEXPR int_writer(OutputIt output, locale_ref loc, Int value, + const basic_format_specs& s) : out(output), locale(loc), specs(s), @@ -1679,7 +1680,7 @@ template struct int_writer { } } - void on_dec() { + FMT_CONSTEXPR void on_dec() { auto num_digits = count_digits(abs_value); out = write_int( out, num_digits, get_prefix(), specs, [this, num_digits](iterator it) { @@ -1687,7 +1688,7 @@ template struct int_writer { }); } - void on_hex() { + FMT_CONSTEXPR void on_hex() { if (specs.alt) { prefix[prefix_size++] = '0'; prefix[prefix_size++] = specs.type; @@ -1700,7 +1701,7 @@ template struct int_writer { }); } - void on_bin() { + FMT_CONSTEXPR void on_bin() { if (specs.alt) { prefix[prefix_size++] = '0'; prefix[prefix_size++] = static_cast(specs.type); @@ -1712,7 +1713,7 @@ template struct int_writer { }); } - void on_oct() { + FMT_CONSTEXPR void on_oct() { int num_digits = count_digits<3>(abs_value); if (specs.alt && specs.precision <= num_digits && abs_value != 0) { // Octal prefix '0' is counted as a digit, so only add it if precision @@ -2041,8 +2042,8 @@ inline OutputIt write(OutputIt out, T value) { } template -OutputIt write_char(OutputIt out, Char value, - const basic_format_specs& specs) { +constexpr OutputIt write_char(OutputIt out, Char value, + const basic_format_specs& specs) { using iterator = remove_reference_t; return write_padded(out, specs, 1, [=](iterator it) { *it++ = value; @@ -2205,7 +2206,8 @@ class arg_formatter_base { using reserve_iterator = remove_reference_t(), 0))>; - template void write_int(T value, const format_specs& spec) { + template + FMT_CONSTEXPR void write_int(T value, const format_specs& spec) { using uint_type = uint32_or_64_or_128_t; int_writer w(out_, locale_, value, spec); handle_int_type_spec(spec.type, w); @@ -2243,7 +2245,8 @@ class arg_formatter_base { } template - void write(basic_string_view s, const format_specs& specs = {}) { + FMT_CONSTEXPR void write(basic_string_view s, + const format_specs& specs = {}) { out_ = detail::write(out_, s, specs); } @@ -2255,14 +2258,14 @@ class arg_formatter_base { arg_formatter_base& formatter; Char value; - char_spec_handler(arg_formatter_base& f, Char val) + constexpr char_spec_handler(arg_formatter_base& f, Char val) : formatter(f), value(val) {} - void on_int() { + FMT_CONSTEXPR void on_int() { // char is only formatted as int if there are specs. formatter.write_int(static_cast(value), *formatter.specs_); } - void on_char() { + FMT_CONSTEXPR void on_char() { if (formatter.specs_) formatter.out_ = write_char(formatter.out_, value, *formatter.specs_); else @@ -2285,7 +2288,7 @@ class arg_formatter_base { iterator out() { return out_; } format_specs* specs() { return specs_; } - void write(bool value) { + FMT_CONSTEXPR void write(bool value) { if (specs_) write(string_view(value ? "true" : "false"), *specs_); else @@ -2303,7 +2306,7 @@ class arg_formatter_base { } public: - arg_formatter_base(OutputIt out, format_specs* s, locale_ref loc) + constexpr arg_formatter_base(OutputIt out, format_specs* s, locale_ref loc) : out_(out), locale_(loc), specs_(s) {} iterator operator()(monostate) { @@ -2312,7 +2315,7 @@ class arg_formatter_base { } template ::value)> - FMT_INLINE iterator operator()(T value) { + FMT_CONSTEXPR iterator operator()(T value) { if (specs_) write_int(value, *specs_); else @@ -2320,13 +2323,13 @@ class arg_formatter_base { return out_; } - iterator operator()(Char value) { + FMT_CONSTEXPR iterator operator()(Char value) { handle_char_specs(specs_, char_spec_handler(*this, static_cast(value))); return out_; } - iterator operator()(bool value) { + FMT_CONSTEXPR iterator operator()(bool value) { if (specs_ && specs_->type) return (*this)(value ? 1 : 0); write(value != 0); return out_; @@ -2348,7 +2351,7 @@ class arg_formatter_base { return out_; } - iterator operator()(basic_string_view value) { + FMT_CONSTEXPR iterator operator()(basic_string_view value) { if (specs_) { check_string_type_spec(specs_->type, error_handler()); write(value, *specs_); @@ -2388,7 +2391,7 @@ class arg_formatter : public arg_formatter_base { *specs* contains format specifier information for standard argument types. \endrst */ - explicit arg_formatter( + constexpr explicit arg_formatter( context_type& ctx, basic_format_parse_context* parse_ctx = nullptr, format_specs* specs = nullptr, const Char* ptr = nullptr) @@ -3296,8 +3299,9 @@ void check_format_string(S format_str) { } template