Skip to content

Commit 81b2930

Browse files
committed
Allow format_to_n to be executed at compile time
1 parent 7f76955 commit 81b2930

File tree

4 files changed

+58
-32
lines changed

4 files changed

+58
-32
lines changed

include/fmt/base.h

+26-19
Original file line numberDiff line numberDiff line change
@@ -1835,6 +1835,7 @@ class fixed_buffer_traits {
18351835

18361836
public:
18371837
constexpr explicit fixed_buffer_traits(size_t limit) : limit_(limit) {}
1838+
FMT_CONSTEXPR20 ~fixed_buffer_traits(){};
18381839
constexpr auto count() const -> size_t { return count_; }
18391840
FMT_CONSTEXPR auto limit(size_t size) -> size_t {
18401841
size_t n = limit_ > count_ ? limit_ - count_ : 0;
@@ -1855,7 +1856,7 @@ class iterator_buffer : public Traits, public buffer<T> {
18551856
if (buf.size() == buffer_size) static_cast<iterator_buffer&>(buf).flush();
18561857
}
18571858

1858-
void flush() {
1859+
FMT_CONSTEXPR void flush() {
18591860
auto size = this->size();
18601861
this->clear();
18611862
const T* begin = data_;
@@ -1864,9 +1865,9 @@ class iterator_buffer : public Traits, public buffer<T> {
18641865
}
18651866

18661867
public:
1867-
explicit iterator_buffer(OutputIt out, size_t n = buffer_size)
1868+
FMT_CONSTEXPR explicit iterator_buffer(OutputIt out, size_t n = buffer_size)
18681869
: Traits(n), buffer<T>(grow, data_, 0, buffer_size), out_(out) {}
1869-
iterator_buffer(iterator_buffer&& other) noexcept
1870+
FMT_CONSTEXPR iterator_buffer(iterator_buffer&& other) noexcept
18701871
: Traits(other),
18711872
buffer<T>(grow, data_, 0, buffer_size),
18721873
out_(other.out_) {}
@@ -1876,11 +1877,13 @@ class iterator_buffer : public Traits, public buffer<T> {
18761877
FMT_CATCH(...) {}
18771878
}
18781879

1879-
auto out() -> OutputIt {
1880+
FMT_CONSTEXPR auto out() -> OutputIt {
18801881
flush();
18811882
return out_;
18821883
}
1883-
auto count() const -> size_t { return Traits::count() + this->size(); }
1884+
FMT_CONSTEXPR auto count() const -> size_t {
1885+
return Traits::count() + this->size();
1886+
}
18841887
};
18851888

18861889
template <typename T>
@@ -1896,7 +1899,7 @@ class iterator_buffer<T*, T, fixed_buffer_traits> : public fixed_buffer_traits,
18961899
static_cast<iterator_buffer&>(buf).flush();
18971900
}
18981901

1899-
void flush() {
1902+
FMT_CONSTEXPR void flush() {
19001903
size_t n = this->limit(this->size());
19011904
if (this->data() == out_) {
19021905
out_ += n;
@@ -1906,9 +1909,9 @@ class iterator_buffer<T*, T, fixed_buffer_traits> : public fixed_buffer_traits,
19061909
}
19071910

19081911
public:
1909-
explicit iterator_buffer(T* out, size_t n = buffer_size)
1912+
FMT_CONSTEXPR explicit iterator_buffer(T* out, size_t n = buffer_size)
19101913
: fixed_buffer_traits(n), buffer<T>(grow, out, 0, n), out_(out) {}
1911-
iterator_buffer(iterator_buffer&& other) noexcept
1914+
FMT_CONSTEXPR iterator_buffer(iterator_buffer&& other) noexcept
19121915
: fixed_buffer_traits(other),
19131916
buffer<T>(static_cast<iterator_buffer&&>(other)),
19141917
out_(other.out_) {
@@ -1917,13 +1920,13 @@ class iterator_buffer<T*, T, fixed_buffer_traits> : public fixed_buffer_traits,
19171920
this->clear();
19181921
}
19191922
}
1920-
~iterator_buffer() { flush(); }
1923+
FMT_CONSTEXPR20 ~iterator_buffer() { flush(); }
19211924

1922-
auto out() -> T* {
1925+
FMT_CONSTEXPR auto out() -> T* {
19231926
flush();
19241927
return out_;
19251928
}
1926-
auto count() const -> size_t {
1929+
FMT_CONSTEXPR auto count() const -> size_t {
19271930
return fixed_buffer_traits::count() + this->size();
19281931
}
19291932
};
@@ -2077,7 +2080,9 @@ template <typename T, typename Char> struct type_is_unformattable_for;
20772080
template <typename Char> struct string_value {
20782081
const Char* data;
20792082
size_t size;
2080-
auto str() const -> basic_string_view<Char> { return {data, size}; }
2083+
FMT_CONSTEXPR auto str() const -> basic_string_view<Char> {
2084+
return {data, size};
2085+
}
20812086
};
20822087

20832088
template <typename Context> struct custom_value {
@@ -2392,8 +2397,8 @@ FMT_CONSTEXPR inline auto is_locking() -> bool {
23922397
return locking<T1>::value || is_locking<T2, Tail...>();
23932398
}
23942399

2395-
FMT_API void vformat_to(buffer<char>& buf, string_view fmt, format_args args,
2396-
locale_ref loc = {});
2400+
FMT_API FMT_CONSTEXPR void vformat_to(buffer<char>& buf, string_view fmt,
2401+
format_args args, locale_ref loc = {});
23972402

23982403
#if FMT_WIN32
23992404
FMT_API void vprint_mojibake(FILE*, string_view, format_args, bool);
@@ -2803,7 +2808,7 @@ inline auto arg(const Char* name, const T& arg) -> detail::named_arg<Char, T> {
28032808
template <typename OutputIt,
28042809
FMT_ENABLE_IF(detail::is_output_iterator<remove_cvref_t<OutputIt>,
28052810
char>::value)>
2806-
auto vformat_to(OutputIt&& out, string_view fmt, format_args args)
2811+
FMT_CONSTEXPR auto vformat_to(OutputIt&& out, string_view fmt, format_args args)
28072812
-> remove_cvref_t<OutputIt> {
28082813
auto&& buf = detail::get_buffer<char>(out);
28092814
detail::vformat_to(buf, fmt, args, {});
@@ -2837,7 +2842,8 @@ template <typename OutputIt> struct format_to_n_result {
28372842

28382843
template <typename OutputIt, typename... T,
28392844
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, char>::value)>
2840-
auto vformat_to_n(OutputIt out, size_t n, string_view fmt, format_args args)
2845+
FMT_CONSTEXPR auto vformat_to_n(OutputIt out, size_t n, string_view fmt,
2846+
format_args args)
28412847
-> format_to_n_result<OutputIt> {
28422848
using traits = detail::fixed_buffer_traits;
28432849
auto buf = detail::iterator_buffer<OutputIt, char, traits>(out, n);
@@ -2853,8 +2859,9 @@ auto vformat_to_n(OutputIt out, size_t n, string_view fmt, format_args args)
28532859
*/
28542860
template <typename OutputIt, typename... T,
28552861
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, char>::value)>
2856-
FMT_INLINE auto format_to_n(OutputIt out, size_t n, format_string<T...> fmt,
2857-
T&&... args) -> format_to_n_result<OutputIt> {
2862+
FMT_CONSTEXPR FMT_INLINE auto format_to_n(OutputIt out, size_t n,
2863+
format_string<T...> fmt, T&&... args)
2864+
-> format_to_n_result<OutputIt> {
28582865
return vformat_to_n(out, n, fmt.str, vargs<T...>{{args...}});
28592866
}
28602867

@@ -2872,7 +2879,7 @@ struct format_to_result {
28722879
};
28732880

28742881
template <size_t N>
2875-
auto vformat_to(char (&out)[N], string_view fmt, format_args args)
2882+
FMT_CONSTEXPR auto vformat_to(char (&out)[N], string_view fmt, format_args args)
28762883
-> format_to_result {
28772884
auto result = vformat_to_n(out, N, fmt, args);
28782885
return {result.out, result.size > N};

include/fmt/format-inl.h

+1-2
Original file line numberDiff line numberDiff line change
@@ -1449,8 +1449,7 @@ FMT_FUNC auto vformat(string_view fmt, format_args args) -> std::string {
14491449

14501450
namespace detail {
14511451

1452-
FMT_FUNC void vformat_to(buffer<char>& buf, string_view fmt, format_args args,
1453-
locale_ref loc) {
1452+
FMT_CONSTEXPR FMT_FUNC void vformat_to(buffer<char>& buf, string_view fmt, format_args args, locale_ref loc) {
14541453
auto out = appender(buf);
14551454
if (fmt.size() == 2 && equal2(fmt.data(), "{}"))
14561455
return args.get(0).visit(default_arg_formatter<char>{out});

include/fmt/format.h

+16-11
Original file line numberDiff line numberDiff line change
@@ -1154,11 +1154,12 @@ FMT_END_EXPORT
11541154
#endif // FMT_HEADER_ONLY
11551155

11561156
// Compares two characters for equality.
1157-
template <typename Char> auto equal2(const Char* lhs, const char* rhs) -> bool {
1157+
template <typename Char>
1158+
FMT_CONSTEXPR auto equal2(const Char* lhs, const char* rhs) -> bool {
11581159
return lhs[0] == Char(rhs[0]) && lhs[1] == Char(rhs[1]);
11591160
}
1160-
inline auto equal2(const char* lhs, const char* rhs) -> bool {
1161-
return memcmp(lhs, rhs, 2) == 0;
1161+
FMT_CONSTEXPR inline auto equal2(const char* lhs, const char* rhs) -> bool {
1162+
return lhs[0] == rhs[0] && lhs[1] == rhs[1];
11621163
}
11631164

11641165
// Writes a two-digit value to out.
@@ -3469,19 +3470,21 @@ template <typename Char> struct default_arg_formatter {
34693470

34703471
basic_appender<Char> out;
34713472

3472-
void operator()(monostate) { report_error("argument not found"); }
3473+
FMT_CONSTEXPR void operator()(monostate) {
3474+
report_error("argument not found");
3475+
}
34733476

34743477
template <typename T, FMT_ENABLE_IF(is_builtin<T>::value)>
3475-
void operator()(T value) {
3478+
FMT_CONSTEXPR void operator()(T value) {
34763479
write<Char>(out, value);
34773480
}
34783481

34793482
template <typename T, FMT_ENABLE_IF(!is_builtin<T>::value)>
3480-
void operator()(T) {
3483+
FMT_CONSTEXPR void operator()(T) {
34813484
FMT_ASSERT(false, "");
34823485
}
34833486

3484-
void operator()(typename basic_format_arg<context>::handle h) {
3487+
FMT_CONSTEXPR void operator()(typename basic_format_arg<context>::handle h) {
34853488
// Use a null locale since the default format must be unlocalized.
34863489
auto parse_ctx = parse_context<Char>({});
34873490
auto format_ctx = context(out, {}, {});
@@ -3590,7 +3593,7 @@ template <typename Char> struct format_handler {
35903593
parse_context<Char> parse_ctx;
35913594
buffered_context<Char> ctx;
35923595

3593-
void on_text(const Char* begin, const Char* end) {
3596+
FMT_CONSTEXPR void on_text(const Char* begin, const Char* end) {
35943597
copy_noinline<Char>(begin, end, ctx.out());
35953598
}
35963599

@@ -3606,11 +3609,11 @@ template <typename Char> struct format_handler {
36063609
return arg_id;
36073610
}
36083611

3609-
FMT_INLINE void on_replacement_field(int id, const Char*) {
3612+
FMT_INLINE FMT_CONSTEXPR void on_replacement_field(int id, const Char*) {
36103613
ctx.arg(id).visit(default_arg_formatter<Char>{ctx.out()});
36113614
}
36123615

3613-
auto on_format_specs(int id, const Char* begin, const Char* end)
3616+
FMT_CONSTEXPR auto on_format_specs(int id, const Char* begin, const Char* end)
36143617
-> const Char* {
36153618
auto arg = get_arg(ctx, id);
36163619
// Not using a visitor for custom types gives better codegen.
@@ -3629,7 +3632,9 @@ template <typename Char> struct format_handler {
36293632
return begin;
36303633
}
36313634

3632-
FMT_NORETURN void on_error(const char* message) { report_error(message); }
3635+
FMT_NORETURN FMT_CONSTEXPR void on_error(const char* message) {
3636+
report_error(message);
3637+
}
36333638
};
36343639

36353640
using format_func = void (*)(detail::buffer<char>&, int, const char*);

test/format-impl-test.cc

+15
Original file line numberDiff line numberDiff line change
@@ -475,3 +475,18 @@ TEST(format_impl_test, to_utf8) {
475475
EXPECT_EQ(s, u.str());
476476
EXPECT_EQ(s.size(), u.size());
477477
}
478+
479+
#if FMT_USE_CONSTEVAL
480+
TEST(format_test, format_to_n_constexpr) {
481+
// This test doesn't have to be extensive -
482+
// it just checks format_to_n can be done in constexpr context
483+
constexpr bool result = []{
484+
std::array buffer {'x', 'x', 'x', 'x'};
485+
fmt::format_to_n(buffer.data(), buffer.size(), "{}", 42);
486+
fmt::format_to_n(buffer.data() + 2, 1, "{}", 'F');
487+
return buffer == std::array{ '4', '2', 'F', 'x'};
488+
}();
489+
490+
ASSERT_TRUE(result);
491+
}
492+
#endif

0 commit comments

Comments
 (0)