diff --git a/include/fmt/compile.h b/include/fmt/compile.h index 65aeb445..ea9ef97e 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) { // This ensures that the argument type is convertile to `const T&`. const T& arg = get(args...); const auto& vargs = @@ -488,7 +488,7 @@ template struct concat { using char_type = typename L::char_type; template - constexpr OutputIt format(OutputIt out, const Args&... args) const { + constexpr OutputIt format(OutputIt out, const Args&... args) { out = lhs.format(out, args...); return rhs.format(out, args...); } @@ -635,7 +635,7 @@ FMT_DEPRECATED auto compile(const Args&... args) template ::value)> -FMT_INLINE std::basic_string format(const CompiledFormat& cf, +FMT_INLINE std::basic_string format(CompiledFormat& cf, const Args&... args) { basic_memory_buffer buffer; cf.format(detail::buffer_appender(buffer), args...); @@ -644,7 +644,7 @@ FMT_INLINE std::basic_string format(const CompiledFormat& cf, template ::value)> -constexpr OutputIt format_to(OutputIt out, const CompiledFormat& cf, +constexpr OutputIt format_to(OutputIt out, CompiledFormat& cf, const Args&... args) { return cf.format(out, args...); } @@ -674,14 +674,14 @@ FMT_INLINE std::basic_string format(const S&, return fmt::to_string(detail::first(args...)); } #endif - constexpr auto compiled = detail::compile(S()); + auto compiled = detail::compile(S()); return format(compiled, std::forward(args)...); } template ::value)> -constexpr OutputIt format_to(OutputIt out, const CompiledFormat& cf, +constexpr OutputIt format_to(OutputIt out, CompiledFormat& cf, const Args&... args) { using char_type = typename CompiledFormat::char_type; using context = format_context_t; @@ -692,7 +692,7 @@ constexpr OutputIt format_to(OutputIt out, const CompiledFormat& cf, template ::value)> FMT_CONSTEXPR OutputIt format_to(OutputIt out, const S&, const Args&... args) { - constexpr auto compiled = detail::compile(S()); + auto compiled = detail::compile(S()); return format_to(out, compiled, args...); } @@ -714,7 +714,7 @@ template ::value)> format_to_n_result format_to_n(OutputIt out, size_t n, const S&, const Args&... args) { - constexpr auto compiled = detail::compile(S()); + auto compiled = detail::compile(S()); auto it = format_to(detail::truncating_iterator(out, n), compiled, args...); return {it.base(), it.count()}; diff --git a/include/fmt/core.h b/include/fmt/core.h index d7d2de50..839cb2dc 100644 --- a/include/fmt/core.h +++ b/include/fmt/core.h @@ -481,7 +481,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 +938,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 +961,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 +1073,17 @@ 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() { string.data = val; } + FMT_CONSTEXPR value(basic_string_view val) { string.data = val.data(); string.size = val.size(); } @@ -1406,7 +1406,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 +1437,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 +1446,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 +1480,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 +1552,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 +1573,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 +1646,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) {} /** \rst @@ -1670,7 +1674,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 +1683,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 +1691,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.h b/include/fmt/format.h index e8fe34c1..09f2d782 100644 --- a/include/fmt/format.h +++ b/include/fmt/format.h @@ -551,7 +551,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) { @@ -584,12 +584,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; } @@ -1143,12 +1146,14 @@ 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_CONSTEXPR20 Char* format_uint(Char* buffer, UInt value, int num_digits, + bool upper = false) { buffer += num_digits; Char* end = buffer; do { - const char* digits = upper ? "0123456789ABCDEF" : data::hex_digits; + const char* digits = upper ? "0123456789ABCDEF" + : (is_constant_evaluated() ? "0123456789abcdef" + : data::hex_digits); unsigned digit = (value & ((1 << BASE_BITS) - 1)); *--buffer = static_cast(BASE_BITS < 4 ? static_cast('0' + digit) : digits[digit]); @@ -1223,8 +1228,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 { @@ -1543,7 +1548,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(); @@ -1557,15 +1563,24 @@ 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_CONSTEXPR20 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; - auto* shifts = align == align::left ? data::left_padding_shifts - : data::right_padding_shifts; - size_t left_padding = padding >> shifts[specs.align]; + size_t left_padding = 0; + if (is_constant_evaluated()) { + const char left_padding_shifts[] = {31, 31, 0, 1, 0}; + const char right_padding_shifts[] = {0, 31, 0, 1, 0}; + auto* shifts = + align == align::left ? left_padding_shifts : right_padding_shifts; + left_padding = padding >> shifts[specs.align]; + } else { + auto* shifts = align == align::left ? data::left_padding_shifts + : data::right_padding_shifts; + 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); @@ -1575,9 +1590,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); } @@ -1597,8 +1612,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); @@ -1617,8 +1632,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) { @@ -1630,8 +1646,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) @@ -1657,11 +1673,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), @@ -1678,7 +1696,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) { @@ -1686,7 +1704,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; @@ -1699,7 +1717,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); @@ -1711,7 +1729,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 @@ -2040,8 +2058,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; @@ -2204,7 +2222,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); @@ -2242,7 +2261,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); } @@ -2254,14 +2274,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 @@ -2284,7 +2304,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 @@ -2302,7 +2322,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) { @@ -2311,7 +2331,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 @@ -2319,13 +2339,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_; @@ -2347,7 +2367,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_); @@ -2387,7 +2407,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) @@ -3294,8 +3314,9 @@ void check_format_string(S format_str) { } template