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 #ifdef __cpp_if_constexpr
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 #ifdef __cpp_if_constexpr
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 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
...>> {
205 using type
= remove_cvref_t
<decltype(get
<N
>(std::declval
<Args
>()...))>;
208 template <int N
, typename T
>
209 using get_type
= typename get_type_impl
<N
, T
>::type
;
211 template <typename T
> struct is_compiled_format
: std::false_type
{};
213 template <typename Char
> struct text
{
214 basic_string_view
<Char
> data
;
215 using char_type
= Char
;
217 template <typename OutputIt
, typename
... Args
>
218 constexpr OutputIt
format(OutputIt out
, const Args
&...) const {
219 return write
<Char
>(out
, data
);
223 template <typename Char
>
224 struct is_compiled_format
<text
<Char
>> : std::true_type
{};
226 template <typename Char
>
227 constexpr text
<Char
> make_text(basic_string_view
<Char
> s
, size_t pos
,
229 return {{&s
[pos
], size
}};
232 template <typename Char
> struct code_unit
{
234 using char_type
= Char
;
236 template <typename OutputIt
, typename
... Args
>
237 constexpr OutputIt
format(OutputIt out
, const Args
&...) const {
238 return write
<Char
>(out
, value
);
242 // This ensures that the argument type is convertible to `const T&`.
243 template <typename T
, int N
, typename
... Args
>
244 constexpr const T
& get_arg_checked(const Args
&... args
) {
245 const auto& arg
= get
<N
>(args
...);
246 if constexpr (detail::is_named_arg
<remove_cvref_t
<decltype(arg
)>>()) {
253 template <typename Char
>
254 struct is_compiled_format
<code_unit
<Char
>> : std::true_type
{};
256 // A replacement field that refers to argument N.
257 template <typename Char
, typename T
, int N
> struct field
{
258 using char_type
= Char
;
260 template <typename OutputIt
, typename
... Args
>
261 constexpr OutputIt
format(OutputIt out
, const Args
&... args
) const {
262 return write
<Char
>(out
, get_arg_checked
<T
, N
>(args
...));
266 template <typename Char
, typename T
, int N
>
267 struct is_compiled_format
<field
<Char
, T
, N
>> : std::true_type
{};
269 // A replacement field that refers to argument with name.
270 template <typename Char
> struct runtime_named_field
{
271 using char_type
= Char
;
272 basic_string_view
<Char
> name
;
274 template <typename OutputIt
, typename T
>
275 constexpr static bool try_format_argument(
277 // [[maybe_unused]] due to unused-but-set-parameter warning in GCC 7,8,9
278 [[maybe_unused
]] basic_string_view
<Char
> arg_name
, const T
& arg
) {
279 if constexpr (is_named_arg
<typename
std::remove_cv
<T
>::type
>::value
) {
280 if (arg_name
== arg
.name
) {
281 out
= write
<Char
>(out
, arg
.value
);
288 template <typename OutputIt
, typename
... Args
>
289 constexpr OutputIt
format(OutputIt out
, const Args
&... args
) const {
290 bool found
= (try_format_argument(out
, name
, args
) || ...);
292 throw format_error("argument with specified name is not found");
298 template <typename Char
>
299 struct is_compiled_format
<runtime_named_field
<Char
>> : std::true_type
{};
301 // A replacement field that refers to argument N and has format specifiers.
302 template <typename Char
, typename T
, int N
> struct spec_field
{
303 using char_type
= Char
;
304 formatter
<T
, Char
> fmt
;
306 template <typename OutputIt
, typename
... Args
>
307 constexpr FMT_INLINE OutputIt
format(OutputIt out
,
308 const Args
&... args
) const {
310 fmt::make_format_args
<basic_format_context
<OutputIt
, Char
>>(args
...);
311 basic_format_context
<OutputIt
, Char
> ctx(out
, vargs
);
312 return fmt
.format(get_arg_checked
<T
, N
>(args
...), ctx
);
316 template <typename Char
, typename T
, int N
>
317 struct is_compiled_format
<spec_field
<Char
, T
, N
>> : std::true_type
{};
319 template <typename L
, typename R
> struct concat
{
322 using char_type
= typename
L::char_type
;
324 template <typename OutputIt
, typename
... Args
>
325 constexpr OutputIt
format(OutputIt out
, const Args
&... args
) const {
326 out
= lhs
.format(out
, args
...);
327 return rhs
.format(out
, args
...);
331 template <typename L
, typename R
>
332 struct is_compiled_format
<concat
<L
, R
>> : std::true_type
{};
334 template <typename L
, typename R
>
335 constexpr concat
<L
, R
> make_concat(L lhs
, R rhs
) {
339 struct unknown_format
{};
341 template <typename Char
>
342 constexpr size_t parse_text(basic_string_view
<Char
> str
, size_t pos
) {
343 for (size_t size
= str
.size(); pos
!= size
; ++pos
) {
344 if (str
[pos
] == '{' || str
[pos
] == '}') break;
349 template <typename Args
, size_t POS
, int ID
, typename S
>
350 constexpr auto compile_format_string(S format_str
);
352 template <typename Args
, size_t POS
, int ID
, typename T
, typename S
>
353 constexpr auto parse_tail(T head
, S format_str
) {
355 basic_string_view
<typename
S::char_type
>(format_str
).size()) {
356 constexpr auto tail
= compile_format_string
<Args
, POS
, ID
>(format_str
);
357 if constexpr (std::is_same
<remove_cvref_t
<decltype(tail
)>,
361 return make_concat(head
, tail
);
367 template <typename T
, typename Char
> struct parse_specs_result
{
368 formatter
<T
, Char
> fmt
;
373 constexpr int manual_indexing_id
= -1;
375 template <typename T
, typename Char
>
376 constexpr parse_specs_result
<T
, Char
> parse_specs(basic_string_view
<Char
> str
,
377 size_t pos
, int next_arg_id
) {
378 str
.remove_prefix(pos
);
379 auto ctx
= basic_format_parse_context
<Char
>(str
, {}, next_arg_id
);
380 auto f
= formatter
<T
, Char
>();
381 auto end
= f
.parse(ctx
);
382 return {f
, pos
+ fmt::detail::to_unsigned(end
- str
.data()) + 1,
383 next_arg_id
== 0 ? manual_indexing_id
: ctx
.next_arg_id()};
386 template <typename Char
> struct arg_id_handler
{
387 arg_ref
<Char
> arg_id
;
389 constexpr int operator()() {
390 FMT_ASSERT(false, "handler cannot be used with automatic indexing");
393 constexpr int operator()(int id
) {
394 arg_id
= arg_ref
<Char
>(id
);
397 constexpr int operator()(basic_string_view
<Char
> id
) {
398 arg_id
= arg_ref
<Char
>(id
);
402 constexpr void on_error(const char* message
) { throw format_error(message
); }
405 template <typename Char
> struct parse_arg_id_result
{
406 arg_ref
<Char
> arg_id
;
407 const Char
* arg_id_end
;
410 template <int ID
, typename Char
>
411 constexpr auto parse_arg_id(const Char
* begin
, const Char
* end
) {
412 auto handler
= arg_id_handler
<Char
>{arg_ref
<Char
>{}};
413 auto arg_id_end
= parse_arg_id(begin
, end
, handler
);
414 return parse_arg_id_result
<Char
>{handler
.arg_id
, arg_id_end
};
417 template <typename T
, typename Enable
= void> struct field_type
{
418 using type
= remove_cvref_t
<T
>;
421 template <typename T
>
422 struct field_type
<T
, enable_if_t
<detail::is_named_arg
<T
>::value
>> {
423 using type
= remove_cvref_t
<decltype(T::value
)>;
426 template <typename T
, typename Args
, size_t END_POS
, int ARG_INDEX
, int NEXT_ID
,
428 constexpr auto parse_replacement_field_then_tail(S format_str
) {
429 using char_type
= typename
S::char_type
;
430 constexpr auto str
= basic_string_view
<char_type
>(format_str
);
431 constexpr char_type c
= END_POS
!= str
.size() ? str
[END_POS
] : char_type();
432 if constexpr (c
== '}') {
433 return parse_tail
<Args
, END_POS
+ 1, NEXT_ID
>(
434 field
<char_type
, typename field_type
<T
>::type
, ARG_INDEX
>(),
436 } else if constexpr (c
== ':') {
437 constexpr auto result
= parse_specs
<typename field_type
<T
>::type
>(
438 str
, END_POS
+ 1, NEXT_ID
== manual_indexing_id
? 0 : NEXT_ID
);
439 return parse_tail
<Args
, result
.end
, result
.next_arg_id
>(
440 spec_field
<char_type
, typename field_type
<T
>::type
, ARG_INDEX
>{
446 // Compiles a non-empty format string and returns the compiled representation
447 // or unknown_format() on unrecognized input.
448 template <typename Args
, size_t POS
, int ID
, typename S
>
449 constexpr auto compile_format_string(S format_str
) {
450 using char_type
= typename
S::char_type
;
451 constexpr auto str
= basic_string_view
<char_type
>(format_str
);
452 if constexpr (str
[POS
] == '{') {
453 if constexpr (POS
+ 1 == str
.size())
454 throw format_error("unmatched '{' in format string");
455 if constexpr (str
[POS
+ 1] == '{') {
456 return parse_tail
<Args
, POS
+ 2, ID
>(make_text(str
, POS
, 1), format_str
);
457 } else if constexpr (str
[POS
+ 1] == '}' || str
[POS
+ 1] == ':') {
458 static_assert(ID
!= manual_indexing_id
,
459 "cannot switch from manual to automatic argument indexing");
460 constexpr auto next_id
=
461 ID
!= manual_indexing_id
? ID
+ 1 : manual_indexing_id
;
462 return parse_replacement_field_then_tail
<get_type
<ID
, Args
>, Args
,
463 POS
+ 1, ID
, next_id
>(
466 constexpr auto arg_id_result
=
467 parse_arg_id
<ID
>(str
.data() + POS
+ 1, str
.data() + str
.size());
468 constexpr auto arg_id_end_pos
= arg_id_result
.arg_id_end
- str
.data();
469 constexpr char_type c
=
470 arg_id_end_pos
!= str
.size() ? str
[arg_id_end_pos
] : char_type();
471 static_assert(c
== '}' || c
== ':', "missing '}' in format string");
472 if constexpr (arg_id_result
.arg_id
.kind
== arg_id_kind::index
) {
474 ID
== manual_indexing_id
|| ID
== 0,
475 "cannot switch from automatic to manual argument indexing");
476 constexpr auto arg_index
= arg_id_result
.arg_id
.val
.index
;
477 return parse_replacement_field_then_tail
<get_type
<arg_index
, Args
>,
478 Args
, arg_id_end_pos
,
479 arg_index
, manual_indexing_id
>(
481 } else if constexpr (arg_id_result
.arg_id
.kind
== arg_id_kind::name
) {
482 constexpr auto arg_index
=
483 get_arg_index_by_name(arg_id_result
.arg_id
.val
.name
, Args
{});
484 if constexpr (arg_index
!= invalid_arg_index
) {
485 constexpr auto next_id
=
486 ID
!= manual_indexing_id
? ID
+ 1 : manual_indexing_id
;
487 return parse_replacement_field_then_tail
<
488 decltype(get_type
<arg_index
, Args
>::value
), Args
, arg_id_end_pos
,
489 arg_index
, next_id
>(format_str
);
491 if constexpr (c
== '}') {
492 return parse_tail
<Args
, arg_id_end_pos
+ 1, ID
>(
493 runtime_named_field
<char_type
>{arg_id_result
.arg_id
.val
.name
},
495 } else if constexpr (c
== ':') {
496 return unknown_format(); // no type info for specs parsing
501 } else if constexpr (str
[POS
] == '}') {
502 if constexpr (POS
+ 1 == str
.size())
503 throw format_error("unmatched '}' in format string");
504 return parse_tail
<Args
, POS
+ 2, ID
>(make_text(str
, POS
, 1), format_str
);
506 constexpr auto end
= parse_text(str
, POS
+ 1);
507 if constexpr (end
- POS
> 1) {
508 return parse_tail
<Args
, end
, ID
>(make_text(str
, POS
, end
- POS
),
511 return parse_tail
<Args
, end
, ID
>(code_unit
<char_type
>{str
[POS
]},
517 template <typename
... Args
, typename S
,
518 FMT_ENABLE_IF(detail::is_compiled_string
<S
>::value
)>
519 constexpr auto compile(S format_str
) {
520 constexpr auto str
= basic_string_view
<typename
S::char_type
>(format_str
);
521 if constexpr (str
.size() == 0) {
522 return detail::make_text(str
, 0, 0);
524 constexpr auto result
=
525 detail::compile_format_string
<detail::type_list
<Args
...>, 0, 0>(
530 #endif // __cpp_if_constexpr
531 } // namespace detail
533 FMT_MODULE_EXPORT_BEGIN
535 #ifdef __cpp_if_constexpr
537 template <typename CompiledFormat
, typename
... Args
,
538 typename Char
= typename
CompiledFormat::char_type
,
539 FMT_ENABLE_IF(detail::is_compiled_format
<CompiledFormat
>::value
)>
540 FMT_INLINE
std::basic_string
<Char
> format(const CompiledFormat
& cf
,
541 const Args
&... args
) {
542 auto s
= std::basic_string
<Char
>();
543 cf
.format(std::back_inserter(s
), args
...);
547 template <typename OutputIt
, typename CompiledFormat
, typename
... Args
,
548 FMT_ENABLE_IF(detail::is_compiled_format
<CompiledFormat
>::value
)>
549 constexpr FMT_INLINE OutputIt
format_to(OutputIt out
, const CompiledFormat
& cf
,
550 const Args
&... args
) {
551 return cf
.format(out
, args
...);
554 template <typename S
, typename
... Args
,
555 FMT_ENABLE_IF(detail::is_compiled_string
<S
>::value
)>
556 FMT_INLINE
std::basic_string
<typename
S::char_type
> format(const S
&,
558 if constexpr (std::is_same
<typename
S::char_type
, char>::value
) {
559 constexpr auto str
= basic_string_view
<typename
S::char_type
>(S());
560 if constexpr (str
.size() == 2 && str
[0] == '{' && str
[1] == '}') {
561 const auto& first
= detail::first(args
...);
562 if constexpr (detail::is_named_arg
<
563 remove_cvref_t
<decltype(first
)>>::value
) {
564 return fmt::to_string(first
.value
);
566 return fmt::to_string(first
);
570 constexpr auto compiled
= detail::compile
<Args
...>(S());
571 if constexpr (std::is_same
<remove_cvref_t
<decltype(compiled
)>,
572 detail::unknown_format
>()) {
573 return format(static_cast<basic_string_view
<typename
S::char_type
>>(S()),
574 std::forward
<Args
>(args
)...);
576 return format(compiled
, std::forward
<Args
>(args
)...);
580 template <typename OutputIt
, typename S
, typename
... Args
,
581 FMT_ENABLE_IF(detail::is_compiled_string
<S
>::value
)>
582 FMT_CONSTEXPR OutputIt
format_to(OutputIt out
, const S
&, Args
&&... args
) {
583 constexpr auto compiled
= detail::compile
<Args
...>(S());
584 if constexpr (std::is_same
<remove_cvref_t
<decltype(compiled
)>,
585 detail::unknown_format
>()) {
586 return format_to(out
,
587 static_cast<basic_string_view
<typename
S::char_type
>>(S()),
588 std::forward
<Args
>(args
)...);
590 return format_to(out
, compiled
, std::forward
<Args
>(args
)...);
595 template <typename OutputIt
, typename S
, typename
... Args
,
596 FMT_ENABLE_IF(detail::is_compiled_string
<S
>::value
)>
597 format_to_n_result
<OutputIt
> format_to_n(OutputIt out
, size_t n
,
598 const S
& format_str
, Args
&&... args
) {
599 auto it
= format_to(detail::truncating_iterator
<OutputIt
>(out
, n
), format_str
,
600 std::forward
<Args
>(args
)...);
601 return {it
.base(), it
.count()};
604 template <typename S
, typename
... Args
,
605 FMT_ENABLE_IF(detail::is_compiled_string
<S
>::value
)>
606 size_t formatted_size(const S
& format_str
, const Args
&... args
) {
607 return format_to(detail::counting_iterator(), format_str
, args
...).count();
610 template <typename S
, typename
... Args
,
611 FMT_ENABLE_IF(detail::is_compiled_string
<S
>::value
)>
612 void print(std::FILE* f
, const S
& format_str
, const Args
&... args
) {
613 memory_buffer buffer
;
614 format_to(std::back_inserter(buffer
), format_str
, args
...);
615 detail::print(f
, {buffer
.data(), buffer
.size()});
618 template <typename S
, typename
... Args
,
619 FMT_ENABLE_IF(detail::is_compiled_string
<S
>::value
)>
620 void print(const S
& format_str
, const Args
&... args
) {
621 print(stdout
, format_str
, args
...);
624 #if FMT_USE_NONTYPE_TEMPLATE_PARAMETERS
625 inline namespace literals
{
626 template <detail_exported::fixed_string Str
>
627 constexpr detail::udl_compiled_string
<
628 remove_cvref_t
<decltype(Str
.data
[0])>,
629 sizeof(Str
.data
) / sizeof(decltype(Str
.data
[0])), Str
>
633 } // namespace literals
636 FMT_MODULE_EXPORT_END
639 #endif // FMT_COMPILE_H_