1 // Formatting library for C++ - experimental format string compilation
3 // Copyright (c) 2012 - present, Victor Zverovich and fmt contributors
4 // All rights reserved.
6 // For the license information refer to format.h.
16 // An output iterator that counts the number of objects written to it and
18 class counting_iterator
{
23 using iterator_category
= std::output_iterator_tag
;
24 using difference_type
= std::ptrdiff_t;
26 using reference
= void;
27 using _Unchecked_type
= counting_iterator
; // Mark iterator as checked.
30 template <typename T
> void operator=(const T
&) {}
33 counting_iterator() : count_(0) {}
35 size_t count() const { return count_
; }
37 counting_iterator
& operator++() {
41 counting_iterator
operator++(int) {
47 friend counting_iterator
operator+(counting_iterator it
, difference_type n
) {
48 it
.count_
+= static_cast<size_t>(n
);
52 value_type
operator*() const { return {}; }
55 template <typename Char
, typename InputIt
>
56 inline counting_iterator
copy_str(InputIt begin
, InputIt end
,
57 counting_iterator it
) {
58 return it
+ (end
- begin
);
61 template <typename OutputIt
> class truncating_iterator_base
{
67 truncating_iterator_base() : out_(), limit_(0) {}
69 truncating_iterator_base(OutputIt out
, size_t limit
)
70 : out_(out
), limit_(limit
) {}
73 using iterator_category
= std::output_iterator_tag
;
74 using value_type
= typename
std::iterator_traits
<OutputIt
>::value_type
;
75 using difference_type
= std::ptrdiff_t;
77 using reference
= void;
78 using _Unchecked_type
=
79 truncating_iterator_base
; // Mark iterator as checked.
81 OutputIt
base() const { return out_
; }
82 size_t count() const { return count_
; }
85 // An output iterator that truncates the output and counts the number of objects
87 template <typename OutputIt
,
88 typename Enable
= typename
std::is_void
<
89 typename
std::iterator_traits
<OutputIt
>::value_type
>::type
>
90 class truncating_iterator
;
92 template <typename OutputIt
>
93 class truncating_iterator
<OutputIt
, std::false_type
>
94 : public truncating_iterator_base
<OutputIt
> {
95 mutable typename truncating_iterator_base
<OutputIt
>::value_type blackhole_
;
98 using value_type
= typename truncating_iterator_base
<OutputIt
>::value_type
;
100 truncating_iterator() = default;
102 truncating_iterator(OutputIt out
, size_t limit
)
103 : truncating_iterator_base
<OutputIt
>(out
, limit
) {}
105 truncating_iterator
& operator++() {
106 if (this->count_
++ < this->limit_
) ++this->out_
;
110 truncating_iterator
operator++(int) {
116 value_type
& operator*() const {
117 return this->count_
< this->limit_
? *this->out_
: blackhole_
;
121 template <typename OutputIt
>
122 class truncating_iterator
<OutputIt
, std::true_type
>
123 : public truncating_iterator_base
<OutputIt
> {
125 truncating_iterator() = default;
127 truncating_iterator(OutputIt out
, size_t limit
)
128 : truncating_iterator_base
<OutputIt
>(out
, limit
) {}
130 template <typename T
> truncating_iterator
& operator=(T val
) {
131 if (this->count_
++ < this->limit_
) *this->out_
++ = val
;
135 truncating_iterator
& operator++() { return *this; }
136 truncating_iterator
& operator++(int) { return *this; }
137 truncating_iterator
& operator*() { return *this; }
140 // A compile-time string which is compiled into fast formatting code.
141 class compiled_string
{};
143 template <typename S
>
144 struct is_compiled_string
: std::is_base_of
<compiled_string
, S
> {};
148 Converts a string literal *s* into a format string that will be parsed at
149 compile time and converted into efficient formatting code. Requires C++17
150 ``constexpr if`` compiler support.
154 // Converts 42 into std::string using the most efficient method and no
155 // runtime format string processing.
156 std::string s = fmt::format(FMT_COMPILE("{}"), 42);
159 #if defined(__cpp_if_constexpr) && defined(__cpp_return_type_deduction)
160 # define FMT_COMPILE(s) \
161 FMT_STRING_IMPL(s, fmt::detail::compiled_string, explicit)
163 # define FMT_COMPILE(s) FMT_STRING(s)
166 #if FMT_USE_NONTYPE_TEMPLATE_PARAMETERS
167 template <typename Char
, size_t N
,
168 fmt::detail_exported::fixed_string
<Char
, N
> Str
>
169 struct udl_compiled_string
: compiled_string
{
170 using char_type
= Char
;
171 constexpr operator basic_string_view
<char_type
>() const {
172 return {Str
.data
, N
- 1};
177 template <typename T
, typename
... Tail
>
178 const T
& first(const T
& value
, const Tail
&...) {
182 #if defined(__cpp_if_constexpr) && defined(__cpp_return_type_deduction)
183 template <typename
... Args
> struct type_list
{};
185 // Returns a reference to the argument at index N from [first, rest...].
186 template <int N
, typename T
, typename
... Args
>
187 constexpr const auto& get([[maybe_unused
]] const T
& first
,
188 [[maybe_unused
]] const Args
&... rest
) {
189 static_assert(N
< 1 + sizeof...(Args
), "index is out of bounds");
190 if constexpr (N
== 0)
193 return detail::get
<N
- 1>(rest
...);
196 template <typename Char
, typename
... Args
>
197 constexpr int get_arg_index_by_name(basic_string_view
<Char
> name
,
198 type_list
<Args
...>) {
199 return get_arg_index_by_name
<Args
...>(name
);
202 template <int N
, typename
> struct get_type_impl
;
204 template <int N
, typename
... Args
> struct get_type_impl
<N
, type_list
<Args
...>> {
206 remove_cvref_t
<decltype(detail::get
<N
>(std::declval
<Args
>()...))>;
209 template <int N
, typename T
>
210 using get_type
= typename get_type_impl
<N
, T
>::type
;
212 template <typename T
> struct is_compiled_format
: std::false_type
{};
214 template <typename Char
> struct text
{
215 basic_string_view
<Char
> data
;
216 using char_type
= Char
;
218 template <typename OutputIt
, typename
... Args
>
219 constexpr OutputIt
format(OutputIt out
, const Args
&...) const {
220 return write
<Char
>(out
, data
);
224 template <typename Char
>
225 struct is_compiled_format
<text
<Char
>> : std::true_type
{};
227 template <typename Char
>
228 constexpr text
<Char
> make_text(basic_string_view
<Char
> s
, size_t pos
,
230 return {{&s
[pos
], size
}};
233 template <typename Char
> struct code_unit
{
235 using char_type
= Char
;
237 template <typename OutputIt
, typename
... Args
>
238 constexpr OutputIt
format(OutputIt out
, const Args
&...) const {
239 return write
<Char
>(out
, value
);
243 // This ensures that the argument type is convertible to `const T&`.
244 template <typename T
, int N
, typename
... Args
>
245 constexpr const T
& get_arg_checked(const Args
&... args
) {
246 const auto& arg
= detail::get
<N
>(args
...);
247 if constexpr (detail::is_named_arg
<remove_cvref_t
<decltype(arg
)>>()) {
254 template <typename Char
>
255 struct is_compiled_format
<code_unit
<Char
>> : std::true_type
{};
257 // A replacement field that refers to argument N.
258 template <typename Char
, typename T
, int N
> struct field
{
259 using char_type
= Char
;
261 template <typename OutputIt
, typename
... Args
>
262 constexpr OutputIt
format(OutputIt out
, const Args
&... args
) const {
263 return write
<Char
>(out
, get_arg_checked
<T
, N
>(args
...));
267 template <typename Char
, typename T
, int N
>
268 struct is_compiled_format
<field
<Char
, T
, N
>> : std::true_type
{};
270 // A replacement field that refers to argument with name.
271 template <typename Char
> struct runtime_named_field
{
272 using char_type
= Char
;
273 basic_string_view
<Char
> name
;
275 template <typename OutputIt
, typename T
>
276 constexpr static bool try_format_argument(
278 // [[maybe_unused]] due to unused-but-set-parameter warning in GCC 7,8,9
279 [[maybe_unused
]] basic_string_view
<Char
> arg_name
, const T
& arg
) {
280 if constexpr (is_named_arg
<typename
std::remove_cv
<T
>::type
>::value
) {
281 if (arg_name
== arg
.name
) {
282 out
= write
<Char
>(out
, arg
.value
);
289 template <typename OutputIt
, typename
... Args
>
290 constexpr OutputIt
format(OutputIt out
, const Args
&... args
) const {
291 bool found
= (try_format_argument(out
, name
, args
) || ...);
293 FMT_THROW(format_error("argument with specified name is not found"));
299 template <typename Char
>
300 struct is_compiled_format
<runtime_named_field
<Char
>> : std::true_type
{};
302 // A replacement field that refers to argument N and has format specifiers.
303 template <typename Char
, typename T
, int N
> struct spec_field
{
304 using char_type
= Char
;
305 formatter
<T
, Char
> fmt
;
307 template <typename OutputIt
, typename
... Args
>
308 constexpr FMT_INLINE OutputIt
format(OutputIt out
,
309 const Args
&... args
) const {
311 fmt::make_format_args
<basic_format_context
<OutputIt
, Char
>>(args
...);
312 basic_format_context
<OutputIt
, Char
> ctx(out
, vargs
);
313 return fmt
.format(get_arg_checked
<T
, N
>(args
...), ctx
);
317 template <typename Char
, typename T
, int N
>
318 struct is_compiled_format
<spec_field
<Char
, T
, N
>> : std::true_type
{};
320 template <typename L
, typename R
> struct concat
{
323 using char_type
= typename
L::char_type
;
325 template <typename OutputIt
, typename
... Args
>
326 constexpr OutputIt
format(OutputIt out
, const Args
&... args
) const {
327 out
= lhs
.format(out
, args
...);
328 return rhs
.format(out
, args
...);
332 template <typename L
, typename R
>
333 struct is_compiled_format
<concat
<L
, R
>> : std::true_type
{};
335 template <typename L
, typename R
>
336 constexpr concat
<L
, R
> make_concat(L lhs
, R rhs
) {
340 struct unknown_format
{};
342 template <typename Char
>
343 constexpr size_t parse_text(basic_string_view
<Char
> str
, size_t pos
) {
344 for (size_t size
= str
.size(); pos
!= size
; ++pos
) {
345 if (str
[pos
] == '{' || str
[pos
] == '}') break;
350 template <typename Args
, size_t POS
, int ID
, typename S
>
351 constexpr auto compile_format_string(S format_str
);
353 template <typename Args
, size_t POS
, int ID
, typename T
, typename S
>
354 constexpr auto parse_tail(T head
, S format_str
) {
356 basic_string_view
<typename
S::char_type
>(format_str
).size()) {
357 constexpr auto tail
= compile_format_string
<Args
, POS
, ID
>(format_str
);
358 if constexpr (std::is_same
<remove_cvref_t
<decltype(tail
)>,
362 return make_concat(head
, tail
);
368 template <typename T
, typename Char
> struct parse_specs_result
{
369 formatter
<T
, Char
> fmt
;
374 constexpr int manual_indexing_id
= -1;
376 template <typename T
, typename Char
>
377 constexpr parse_specs_result
<T
, Char
> parse_specs(basic_string_view
<Char
> str
,
378 size_t pos
, int next_arg_id
) {
379 str
.remove_prefix(pos
);
380 auto ctx
= basic_format_parse_context
<Char
>(str
, {}, next_arg_id
);
381 auto f
= formatter
<T
, Char
>();
382 auto end
= f
.parse(ctx
);
383 return {f
, pos
+ fmt::detail::to_unsigned(end
- str
.data()) + 1,
384 next_arg_id
== 0 ? manual_indexing_id
: ctx
.next_arg_id()};
387 template <typename Char
> struct arg_id_handler
{
388 arg_ref
<Char
> arg_id
;
390 constexpr int operator()() {
391 FMT_ASSERT(false, "handler cannot be used with automatic indexing");
394 constexpr int operator()(int id
) {
395 arg_id
= arg_ref
<Char
>(id
);
398 constexpr int operator()(basic_string_view
<Char
> id
) {
399 arg_id
= arg_ref
<Char
>(id
);
403 constexpr void on_error(const char* message
) {
404 FMT_THROW(format_error(message
));
408 template <typename Char
> struct parse_arg_id_result
{
409 arg_ref
<Char
> arg_id
;
410 const Char
* arg_id_end
;
413 template <int ID
, typename Char
>
414 constexpr auto parse_arg_id(const Char
* begin
, const Char
* end
) {
415 auto handler
= arg_id_handler
<Char
>{arg_ref
<Char
>{}};
416 auto arg_id_end
= parse_arg_id(begin
, end
, handler
);
417 return parse_arg_id_result
<Char
>{handler
.arg_id
, arg_id_end
};
420 template <typename T
, typename Enable
= void> struct field_type
{
421 using type
= remove_cvref_t
<T
>;
424 template <typename T
>
425 struct field_type
<T
, enable_if_t
<detail::is_named_arg
<T
>::value
>> {
426 using type
= remove_cvref_t
<decltype(T::value
)>;
429 template <typename T
, typename Args
, size_t END_POS
, int ARG_INDEX
, int NEXT_ID
,
431 constexpr auto parse_replacement_field_then_tail(S format_str
) {
432 using char_type
= typename
S::char_type
;
433 constexpr auto str
= basic_string_view
<char_type
>(format_str
);
434 constexpr char_type c
= END_POS
!= str
.size() ? str
[END_POS
] : char_type();
435 if constexpr (c
== '}') {
436 return parse_tail
<Args
, END_POS
+ 1, NEXT_ID
>(
437 field
<char_type
, typename field_type
<T
>::type
, ARG_INDEX
>(),
439 } else if constexpr (c
== ':') {
440 constexpr auto result
= parse_specs
<typename field_type
<T
>::type
>(
441 str
, END_POS
+ 1, NEXT_ID
== manual_indexing_id
? 0 : NEXT_ID
);
442 return parse_tail
<Args
, result
.end
, result
.next_arg_id
>(
443 spec_field
<char_type
, typename field_type
<T
>::type
, ARG_INDEX
>{
449 // Compiles a non-empty format string and returns the compiled representation
450 // or unknown_format() on unrecognized input.
451 template <typename Args
, size_t POS
, int ID
, typename S
>
452 constexpr auto compile_format_string(S format_str
) {
453 using char_type
= typename
S::char_type
;
454 constexpr auto str
= basic_string_view
<char_type
>(format_str
);
455 if constexpr (str
[POS
] == '{') {
456 if constexpr (POS
+ 1 == str
.size())
457 FMT_THROW(format_error("unmatched '{' in format string"));
458 if constexpr (str
[POS
+ 1] == '{') {
459 return parse_tail
<Args
, POS
+ 2, ID
>(make_text(str
, POS
, 1), format_str
);
460 } else if constexpr (str
[POS
+ 1] == '}' || str
[POS
+ 1] == ':') {
461 static_assert(ID
!= manual_indexing_id
,
462 "cannot switch from manual to automatic argument indexing");
463 constexpr auto next_id
=
464 ID
!= manual_indexing_id
? ID
+ 1 : manual_indexing_id
;
465 return parse_replacement_field_then_tail
<get_type
<ID
, Args
>, Args
,
466 POS
+ 1, ID
, next_id
>(
469 constexpr auto arg_id_result
=
470 parse_arg_id
<ID
>(str
.data() + POS
+ 1, str
.data() + str
.size());
471 constexpr auto arg_id_end_pos
= arg_id_result
.arg_id_end
- str
.data();
472 constexpr char_type c
=
473 arg_id_end_pos
!= str
.size() ? str
[arg_id_end_pos
] : char_type();
474 static_assert(c
== '}' || c
== ':', "missing '}' in format string");
475 if constexpr (arg_id_result
.arg_id
.kind
== arg_id_kind::index
) {
477 ID
== manual_indexing_id
|| ID
== 0,
478 "cannot switch from automatic to manual argument indexing");
479 constexpr auto arg_index
= arg_id_result
.arg_id
.val
.index
;
480 return parse_replacement_field_then_tail
<get_type
<arg_index
, Args
>,
481 Args
, arg_id_end_pos
,
482 arg_index
, manual_indexing_id
>(
484 } else if constexpr (arg_id_result
.arg_id
.kind
== arg_id_kind::name
) {
485 constexpr auto arg_index
=
486 get_arg_index_by_name(arg_id_result
.arg_id
.val
.name
, Args
{});
487 if constexpr (arg_index
!= invalid_arg_index
) {
488 constexpr auto next_id
=
489 ID
!= manual_indexing_id
? ID
+ 1 : manual_indexing_id
;
490 return parse_replacement_field_then_tail
<
491 decltype(get_type
<arg_index
, Args
>::value
), Args
, arg_id_end_pos
,
492 arg_index
, next_id
>(format_str
);
494 if constexpr (c
== '}') {
495 return parse_tail
<Args
, arg_id_end_pos
+ 1, ID
>(
496 runtime_named_field
<char_type
>{arg_id_result
.arg_id
.val
.name
},
498 } else if constexpr (c
== ':') {
499 return unknown_format(); // no type info for specs parsing
504 } else if constexpr (str
[POS
] == '}') {
505 if constexpr (POS
+ 1 == str
.size())
506 FMT_THROW(format_error("unmatched '}' in format string"));
507 return parse_tail
<Args
, POS
+ 2, ID
>(make_text(str
, POS
, 1), format_str
);
509 constexpr auto end
= parse_text(str
, POS
+ 1);
510 if constexpr (end
- POS
> 1) {
511 return parse_tail
<Args
, end
, ID
>(make_text(str
, POS
, end
- POS
),
514 return parse_tail
<Args
, end
, ID
>(code_unit
<char_type
>{str
[POS
]},
520 template <typename
... Args
, typename S
,
521 FMT_ENABLE_IF(detail::is_compiled_string
<S
>::value
)>
522 constexpr auto compile(S format_str
) {
523 constexpr auto str
= basic_string_view
<typename
S::char_type
>(format_str
);
524 if constexpr (str
.size() == 0) {
525 return detail::make_text(str
, 0, 0);
527 constexpr auto result
=
528 detail::compile_format_string
<detail::type_list
<Args
...>, 0, 0>(
533 #endif // defined(__cpp_if_constexpr) && defined(__cpp_return_type_deduction)
534 } // namespace detail
536 FMT_MODULE_EXPORT_BEGIN
538 #if defined(__cpp_if_constexpr) && defined(__cpp_return_type_deduction)
540 template <typename CompiledFormat
, typename
... Args
,
541 typename Char
= typename
CompiledFormat::char_type
,
542 FMT_ENABLE_IF(detail::is_compiled_format
<CompiledFormat
>::value
)>
543 FMT_INLINE
std::basic_string
<Char
> format(const CompiledFormat
& cf
,
544 const Args
&... args
) {
545 auto s
= std::basic_string
<Char
>();
546 cf
.format(std::back_inserter(s
), args
...);
550 template <typename OutputIt
, typename CompiledFormat
, typename
... Args
,
551 FMT_ENABLE_IF(detail::is_compiled_format
<CompiledFormat
>::value
)>
552 constexpr FMT_INLINE OutputIt
format_to(OutputIt out
, const CompiledFormat
& cf
,
553 const Args
&... args
) {
554 return cf
.format(out
, args
...);
557 template <typename S
, typename
... Args
,
558 FMT_ENABLE_IF(detail::is_compiled_string
<S
>::value
)>
559 FMT_INLINE
std::basic_string
<typename
S::char_type
> format(const S
&,
561 if constexpr (std::is_same
<typename
S::char_type
, char>::value
) {
562 constexpr auto str
= basic_string_view
<typename
S::char_type
>(S());
563 if constexpr (str
.size() == 2 && str
[0] == '{' && str
[1] == '}') {
564 const auto& first
= detail::first(args
...);
565 if constexpr (detail::is_named_arg
<
566 remove_cvref_t
<decltype(first
)>>::value
) {
567 return fmt::to_string(first
.value
);
569 return fmt::to_string(first
);
573 constexpr auto compiled
= detail::compile
<Args
...>(S());
574 if constexpr (std::is_same
<remove_cvref_t
<decltype(compiled
)>,
575 detail::unknown_format
>()) {
576 return format(static_cast<basic_string_view
<typename
S::char_type
>>(S()),
577 std::forward
<Args
>(args
)...);
579 return format(compiled
, std::forward
<Args
>(args
)...);
583 template <typename OutputIt
, typename S
, typename
... Args
,
584 FMT_ENABLE_IF(detail::is_compiled_string
<S
>::value
)>
585 FMT_CONSTEXPR OutputIt
format_to(OutputIt out
, const S
&, Args
&&... args
) {
586 constexpr auto compiled
= detail::compile
<Args
...>(S());
587 if constexpr (std::is_same
<remove_cvref_t
<decltype(compiled
)>,
588 detail::unknown_format
>()) {
589 return format_to(out
,
590 static_cast<basic_string_view
<typename
S::char_type
>>(S()),
591 std::forward
<Args
>(args
)...);
593 return format_to(out
, compiled
, std::forward
<Args
>(args
)...);
598 template <typename OutputIt
, typename S
, typename
... Args
,
599 FMT_ENABLE_IF(detail::is_compiled_string
<S
>::value
)>
600 format_to_n_result
<OutputIt
> format_to_n(OutputIt out
, size_t n
,
601 const S
& format_str
, Args
&&... args
) {
602 auto it
= format_to(detail::truncating_iterator
<OutputIt
>(out
, n
), format_str
,
603 std::forward
<Args
>(args
)...);
604 return {it
.base(), it
.count()};
607 template <typename S
, typename
... Args
,
608 FMT_ENABLE_IF(detail::is_compiled_string
<S
>::value
)>
609 size_t formatted_size(const S
& format_str
, const Args
&... args
) {
610 return format_to(detail::counting_iterator(), format_str
, args
...).count();
613 template <typename S
, typename
... Args
,
614 FMT_ENABLE_IF(detail::is_compiled_string
<S
>::value
)>
615 void print(std::FILE* f
, const S
& format_str
, const Args
&... args
) {
616 memory_buffer buffer
;
617 format_to(std::back_inserter(buffer
), format_str
, args
...);
618 detail::print(f
, {buffer
.data(), buffer
.size()});
621 template <typename S
, typename
... Args
,
622 FMT_ENABLE_IF(detail::is_compiled_string
<S
>::value
)>
623 void print(const S
& format_str
, const Args
&... args
) {
624 print(stdout
, format_str
, args
...);
627 #if FMT_USE_NONTYPE_TEMPLATE_PARAMETERS
628 inline namespace literals
{
629 template <detail_exported::fixed_string Str
>
630 constexpr detail::udl_compiled_string
<
631 remove_cvref_t
<decltype(Str
.data
[0])>,
632 sizeof(Str
.data
) / sizeof(decltype(Str
.data
[0])), Str
>
636 } // namespace literals
639 FMT_MODULE_EXPORT_END
642 #endif // FMT_COMPILE_H_