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 template <typename Char
, typename InputIt
>
17 FMT_CONSTEXPR
inline counting_iterator
copy_str(InputIt begin
, InputIt end
,
18 counting_iterator it
) {
19 return it
+ (end
- begin
);
22 template <typename OutputIt
> class truncating_iterator_base
{
28 truncating_iterator_base() : out_(), limit_(0) {}
30 truncating_iterator_base(OutputIt out
, size_t limit
)
31 : out_(out
), limit_(limit
) {}
34 using iterator_category
= std::output_iterator_tag
;
35 using value_type
= typename
std::iterator_traits
<OutputIt
>::value_type
;
36 using difference_type
= std::ptrdiff_t;
38 using reference
= void;
39 FMT_UNCHECKED_ITERATOR(truncating_iterator_base
);
41 OutputIt
base() const { return out_
; }
42 size_t count() const { return count_
; }
45 // An output iterator that truncates the output and counts the number of objects
47 template <typename OutputIt
,
48 typename Enable
= typename
std::is_void
<
49 typename
std::iterator_traits
<OutputIt
>::value_type
>::type
>
50 class truncating_iterator
;
52 template <typename OutputIt
>
53 class truncating_iterator
<OutputIt
, std::false_type
>
54 : public truncating_iterator_base
<OutputIt
> {
55 mutable typename truncating_iterator_base
<OutputIt
>::value_type blackhole_
;
58 using value_type
= typename truncating_iterator_base
<OutputIt
>::value_type
;
60 truncating_iterator() = default;
62 truncating_iterator(OutputIt out
, size_t limit
)
63 : truncating_iterator_base
<OutputIt
>(out
, limit
) {}
65 truncating_iterator
& operator++() {
66 if (this->count_
++ < this->limit_
) ++this->out_
;
70 truncating_iterator
operator++(int) {
76 value_type
& operator*() const {
77 return this->count_
< this->limit_
? *this->out_
: blackhole_
;
81 template <typename OutputIt
>
82 class truncating_iterator
<OutputIt
, std::true_type
>
83 : public truncating_iterator_base
<OutputIt
> {
85 truncating_iterator() = default;
87 truncating_iterator(OutputIt out
, size_t limit
)
88 : truncating_iterator_base
<OutputIt
>(out
, limit
) {}
90 template <typename T
> truncating_iterator
& operator=(T val
) {
91 if (this->count_
++ < this->limit_
) *this->out_
++ = val
;
95 truncating_iterator
& operator++() { return *this; }
96 truncating_iterator
& operator++(int) { return *this; }
97 truncating_iterator
& operator*() { return *this; }
100 // A compile-time string which is compiled into fast formatting code.
101 class compiled_string
{};
103 template <typename S
>
104 struct is_compiled_string
: std::is_base_of
<compiled_string
, S
> {};
108 Converts a string literal *s* into a format string that will be parsed at
109 compile time and converted into efficient formatting code. Requires C++17
110 ``constexpr if`` compiler support.
114 // Converts 42 into std::string using the most efficient method and no
115 // runtime format string processing.
116 std::string s = fmt::format(FMT_COMPILE("{}"), 42);
119 #if defined(__cpp_if_constexpr) && defined(__cpp_return_type_deduction)
120 # define FMT_COMPILE(s) \
121 FMT_STRING_IMPL(s, fmt::detail::compiled_string, explicit)
123 # define FMT_COMPILE(s) FMT_STRING(s)
126 #if FMT_USE_NONTYPE_TEMPLATE_ARGS
127 template <typename Char
, size_t N
,
128 fmt::detail_exported::fixed_string
<Char
, N
> Str
>
129 struct udl_compiled_string
: compiled_string
{
130 using char_type
= Char
;
131 explicit constexpr operator basic_string_view
<char_type
>() const {
132 return {Str
.data
, N
- 1};
137 template <typename T
, typename
... Tail
>
138 const T
& first(const T
& value
, const Tail
&...) {
142 #if defined(__cpp_if_constexpr) && defined(__cpp_return_type_deduction)
143 template <typename
... Args
> struct type_list
{};
145 // Returns a reference to the argument at index N from [first, rest...].
146 template <int N
, typename T
, typename
... Args
>
147 constexpr const auto& get([[maybe_unused
]] const T
& first
,
148 [[maybe_unused
]] const Args
&... rest
) {
149 static_assert(N
< 1 + sizeof...(Args
), "index is out of bounds");
150 if constexpr (N
== 0)
153 return detail::get
<N
- 1>(rest
...);
156 template <typename Char
, typename
... Args
>
157 constexpr int get_arg_index_by_name(basic_string_view
<Char
> name
,
158 type_list
<Args
...>) {
159 return get_arg_index_by_name
<Args
...>(name
);
162 template <int N
, typename
> struct get_type_impl
;
164 template <int N
, typename
... Args
> struct get_type_impl
<N
, type_list
<Args
...>> {
166 remove_cvref_t
<decltype(detail::get
<N
>(std::declval
<Args
>()...))>;
169 template <int N
, typename T
>
170 using get_type
= typename get_type_impl
<N
, T
>::type
;
172 template <typename T
> struct is_compiled_format
: std::false_type
{};
174 template <typename Char
> struct text
{
175 basic_string_view
<Char
> data
;
176 using char_type
= Char
;
178 template <typename OutputIt
, typename
... Args
>
179 constexpr OutputIt
format(OutputIt out
, const Args
&...) const {
180 return write
<Char
>(out
, data
);
184 template <typename Char
>
185 struct is_compiled_format
<text
<Char
>> : std::true_type
{};
187 template <typename Char
>
188 constexpr text
<Char
> make_text(basic_string_view
<Char
> s
, size_t pos
,
190 return {{&s
[pos
], size
}};
193 template <typename Char
> struct code_unit
{
195 using char_type
= Char
;
197 template <typename OutputIt
, typename
... Args
>
198 constexpr OutputIt
format(OutputIt out
, const Args
&...) const {
199 return write
<Char
>(out
, value
);
203 // This ensures that the argument type is convertible to `const T&`.
204 template <typename T
, int N
, typename
... Args
>
205 constexpr const T
& get_arg_checked(const Args
&... args
) {
206 const auto& arg
= detail::get
<N
>(args
...);
207 if constexpr (detail::is_named_arg
<remove_cvref_t
<decltype(arg
)>>()) {
214 template <typename Char
>
215 struct is_compiled_format
<code_unit
<Char
>> : std::true_type
{};
217 // A replacement field that refers to argument N.
218 template <typename Char
, typename T
, int N
> struct field
{
219 using char_type
= Char
;
221 template <typename OutputIt
, typename
... Args
>
222 constexpr OutputIt
format(OutputIt out
, const Args
&... args
) const {
223 return write
<Char
>(out
, get_arg_checked
<T
, N
>(args
...));
227 template <typename Char
, typename T
, int N
>
228 struct is_compiled_format
<field
<Char
, T
, N
>> : std::true_type
{};
230 // A replacement field that refers to argument with name.
231 template <typename Char
> struct runtime_named_field
{
232 using char_type
= Char
;
233 basic_string_view
<Char
> name
;
235 template <typename OutputIt
, typename T
>
236 constexpr static bool try_format_argument(
238 // [[maybe_unused]] due to unused-but-set-parameter warning in GCC 7,8,9
239 [[maybe_unused
]] basic_string_view
<Char
> arg_name
, const T
& arg
) {
240 if constexpr (is_named_arg
<typename
std::remove_cv
<T
>::type
>::value
) {
241 if (arg_name
== arg
.name
) {
242 out
= write
<Char
>(out
, arg
.value
);
249 template <typename OutputIt
, typename
... Args
>
250 constexpr OutputIt
format(OutputIt out
, const Args
&... args
) const {
251 bool found
= (try_format_argument(out
, name
, args
) || ...);
253 FMT_THROW(format_error("argument with specified name is not found"));
259 template <typename Char
>
260 struct is_compiled_format
<runtime_named_field
<Char
>> : std::true_type
{};
262 // A replacement field that refers to argument N and has format specifiers.
263 template <typename Char
, typename T
, int N
> struct spec_field
{
264 using char_type
= Char
;
265 formatter
<T
, Char
> fmt
;
267 template <typename OutputIt
, typename
... Args
>
268 constexpr FMT_INLINE OutputIt
format(OutputIt out
,
269 const Args
&... args
) const {
271 fmt::make_format_args
<basic_format_context
<OutputIt
, Char
>>(args
...);
272 basic_format_context
<OutputIt
, Char
> ctx(out
, vargs
);
273 return fmt
.format(get_arg_checked
<T
, N
>(args
...), ctx
);
277 template <typename Char
, typename T
, int N
>
278 struct is_compiled_format
<spec_field
<Char
, T
, N
>> : std::true_type
{};
280 template <typename L
, typename R
> struct concat
{
283 using char_type
= typename
L::char_type
;
285 template <typename OutputIt
, typename
... Args
>
286 constexpr OutputIt
format(OutputIt out
, const Args
&... args
) const {
287 out
= lhs
.format(out
, args
...);
288 return rhs
.format(out
, args
...);
292 template <typename L
, typename R
>
293 struct is_compiled_format
<concat
<L
, R
>> : std::true_type
{};
295 template <typename L
, typename R
>
296 constexpr concat
<L
, R
> make_concat(L lhs
, R rhs
) {
300 struct unknown_format
{};
302 template <typename Char
>
303 constexpr size_t parse_text(basic_string_view
<Char
> str
, size_t pos
) {
304 for (size_t size
= str
.size(); pos
!= size
; ++pos
) {
305 if (str
[pos
] == '{' || str
[pos
] == '}') break;
310 template <typename Args
, size_t POS
, int ID
, typename S
>
311 constexpr auto compile_format_string(S format_str
);
313 template <typename Args
, size_t POS
, int ID
, typename T
, typename S
>
314 constexpr auto parse_tail(T head
, S format_str
) {
316 basic_string_view
<typename
S::char_type
>(format_str
).size()) {
317 constexpr auto tail
= compile_format_string
<Args
, POS
, ID
>(format_str
);
318 if constexpr (std::is_same
<remove_cvref_t
<decltype(tail
)>,
322 return make_concat(head
, tail
);
328 template <typename T
, typename Char
> struct parse_specs_result
{
329 formatter
<T
, Char
> fmt
;
334 constexpr int manual_indexing_id
= -1;
336 template <typename T
, typename Char
>
337 constexpr parse_specs_result
<T
, Char
> parse_specs(basic_string_view
<Char
> str
,
338 size_t pos
, int next_arg_id
) {
339 str
.remove_prefix(pos
);
340 auto ctx
= compile_parse_context
<Char
>(str
, max_value
<int>(), nullptr, {},
342 auto f
= formatter
<T
, Char
>();
343 auto end
= f
.parse(ctx
);
344 return {f
, pos
+ fmt::detail::to_unsigned(end
- str
.data()),
345 next_arg_id
== 0 ? manual_indexing_id
: ctx
.next_arg_id()};
348 template <typename Char
> struct arg_id_handler
{
349 arg_ref
<Char
> arg_id
;
351 constexpr int operator()() {
352 FMT_ASSERT(false, "handler cannot be used with automatic indexing");
355 constexpr int operator()(int id
) {
356 arg_id
= arg_ref
<Char
>(id
);
359 constexpr int operator()(basic_string_view
<Char
> id
) {
360 arg_id
= arg_ref
<Char
>(id
);
364 constexpr void on_error(const char* message
) {
365 FMT_THROW(format_error(message
));
369 template <typename Char
> struct parse_arg_id_result
{
370 arg_ref
<Char
> arg_id
;
371 const Char
* arg_id_end
;
374 template <int ID
, typename Char
>
375 constexpr auto parse_arg_id(const Char
* begin
, const Char
* end
) {
376 auto handler
= arg_id_handler
<Char
>{arg_ref
<Char
>{}};
377 auto arg_id_end
= parse_arg_id(begin
, end
, handler
);
378 return parse_arg_id_result
<Char
>{handler
.arg_id
, arg_id_end
};
381 template <typename T
, typename Enable
= void> struct field_type
{
382 using type
= remove_cvref_t
<T
>;
385 template <typename T
>
386 struct field_type
<T
, enable_if_t
<detail::is_named_arg
<T
>::value
>> {
387 using type
= remove_cvref_t
<decltype(T::value
)>;
390 template <typename T
, typename Args
, size_t END_POS
, int ARG_INDEX
, int NEXT_ID
,
392 constexpr auto parse_replacement_field_then_tail(S format_str
) {
393 using char_type
= typename
S::char_type
;
394 constexpr auto str
= basic_string_view
<char_type
>(format_str
);
395 constexpr char_type c
= END_POS
!= str
.size() ? str
[END_POS
] : char_type();
396 if constexpr (c
== '}') {
397 return parse_tail
<Args
, END_POS
+ 1, NEXT_ID
>(
398 field
<char_type
, typename field_type
<T
>::type
, ARG_INDEX
>(),
400 } else if constexpr (c
!= ':') {
401 FMT_THROW(format_error("expected ':'"));
403 constexpr auto result
= parse_specs
<typename field_type
<T
>::type
>(
404 str
, END_POS
+ 1, NEXT_ID
== manual_indexing_id
? 0 : NEXT_ID
);
405 if constexpr (result
.end
>= str
.size() || str
[result
.end
] != '}') {
406 FMT_THROW(format_error("expected '}'"));
409 return parse_tail
<Args
, result
.end
+ 1, result
.next_arg_id
>(
410 spec_field
<char_type
, typename field_type
<T
>::type
, ARG_INDEX
>{
417 // Compiles a non-empty format string and returns the compiled representation
418 // or unknown_format() on unrecognized input.
419 template <typename Args
, size_t POS
, int ID
, typename S
>
420 constexpr auto compile_format_string(S format_str
) {
421 using char_type
= typename
S::char_type
;
422 constexpr auto str
= basic_string_view
<char_type
>(format_str
);
423 if constexpr (str
[POS
] == '{') {
424 if constexpr (POS
+ 1 == str
.size())
425 FMT_THROW(format_error("unmatched '{' in format string"));
426 if constexpr (str
[POS
+ 1] == '{') {
427 return parse_tail
<Args
, POS
+ 2, ID
>(make_text(str
, POS
, 1), format_str
);
428 } else if constexpr (str
[POS
+ 1] == '}' || str
[POS
+ 1] == ':') {
429 static_assert(ID
!= manual_indexing_id
,
430 "cannot switch from manual to automatic argument indexing");
431 constexpr auto next_id
=
432 ID
!= manual_indexing_id
? ID
+ 1 : manual_indexing_id
;
433 return parse_replacement_field_then_tail
<get_type
<ID
, Args
>, Args
,
434 POS
+ 1, ID
, next_id
>(
437 constexpr auto arg_id_result
=
438 parse_arg_id
<ID
>(str
.data() + POS
+ 1, str
.data() + str
.size());
439 constexpr auto arg_id_end_pos
= arg_id_result
.arg_id_end
- str
.data();
440 constexpr char_type c
=
441 arg_id_end_pos
!= str
.size() ? str
[arg_id_end_pos
] : char_type();
442 static_assert(c
== '}' || c
== ':', "missing '}' in format string");
443 if constexpr (arg_id_result
.arg_id
.kind
== arg_id_kind::index
) {
445 ID
== manual_indexing_id
|| ID
== 0,
446 "cannot switch from automatic to manual argument indexing");
447 constexpr auto arg_index
= arg_id_result
.arg_id
.val
.index
;
448 return parse_replacement_field_then_tail
<get_type
<arg_index
, Args
>,
449 Args
, arg_id_end_pos
,
450 arg_index
, manual_indexing_id
>(
452 } else if constexpr (arg_id_result
.arg_id
.kind
== arg_id_kind::name
) {
453 constexpr auto arg_index
=
454 get_arg_index_by_name(arg_id_result
.arg_id
.val
.name
, Args
{});
455 if constexpr (arg_index
!= invalid_arg_index
) {
456 constexpr auto next_id
=
457 ID
!= manual_indexing_id
? ID
+ 1 : manual_indexing_id
;
458 return parse_replacement_field_then_tail
<
459 decltype(get_type
<arg_index
, Args
>::value
), Args
, arg_id_end_pos
,
460 arg_index
, next_id
>(format_str
);
462 if constexpr (c
== '}') {
463 return parse_tail
<Args
, arg_id_end_pos
+ 1, ID
>(
464 runtime_named_field
<char_type
>{arg_id_result
.arg_id
.val
.name
},
466 } else if constexpr (c
== ':') {
467 return unknown_format(); // no type info for specs parsing
472 } else if constexpr (str
[POS
] == '}') {
473 if constexpr (POS
+ 1 == str
.size())
474 FMT_THROW(format_error("unmatched '}' in format string"));
475 return parse_tail
<Args
, POS
+ 2, ID
>(make_text(str
, POS
, 1), format_str
);
477 constexpr auto end
= parse_text(str
, POS
+ 1);
478 if constexpr (end
- POS
> 1) {
479 return parse_tail
<Args
, end
, ID
>(make_text(str
, POS
, end
- POS
),
482 return parse_tail
<Args
, end
, ID
>(code_unit
<char_type
>{str
[POS
]},
488 template <typename
... Args
, typename S
,
489 FMT_ENABLE_IF(detail::is_compiled_string
<S
>::value
)>
490 constexpr auto compile(S format_str
) {
491 constexpr auto str
= basic_string_view
<typename
S::char_type
>(format_str
);
492 if constexpr (str
.size() == 0) {
493 return detail::make_text(str
, 0, 0);
495 constexpr auto result
=
496 detail::compile_format_string
<detail::type_list
<Args
...>, 0, 0>(
501 #endif // defined(__cpp_if_constexpr) && defined(__cpp_return_type_deduction)
502 } // namespace detail
504 FMT_MODULE_EXPORT_BEGIN
506 #if defined(__cpp_if_constexpr) && defined(__cpp_return_type_deduction)
508 template <typename CompiledFormat
, typename
... Args
,
509 typename Char
= typename
CompiledFormat::char_type
,
510 FMT_ENABLE_IF(detail::is_compiled_format
<CompiledFormat
>::value
)>
511 FMT_INLINE
std::basic_string
<Char
> format(const CompiledFormat
& cf
,
512 const Args
&... args
) {
513 auto s
= std::basic_string
<Char
>();
514 cf
.format(std::back_inserter(s
), args
...);
518 template <typename OutputIt
, typename CompiledFormat
, typename
... Args
,
519 FMT_ENABLE_IF(detail::is_compiled_format
<CompiledFormat
>::value
)>
520 constexpr FMT_INLINE OutputIt
format_to(OutputIt out
, const CompiledFormat
& cf
,
521 const Args
&... args
) {
522 return cf
.format(out
, args
...);
525 template <typename S
, typename
... Args
,
526 FMT_ENABLE_IF(detail::is_compiled_string
<S
>::value
)>
527 FMT_INLINE
std::basic_string
<typename
S::char_type
> format(const S
&,
529 if constexpr (std::is_same
<typename
S::char_type
, char>::value
) {
530 constexpr auto str
= basic_string_view
<typename
S::char_type
>(S());
531 if constexpr (str
.size() == 2 && str
[0] == '{' && str
[1] == '}') {
532 const auto& first
= detail::first(args
...);
533 if constexpr (detail::is_named_arg
<
534 remove_cvref_t
<decltype(first
)>>::value
) {
535 return fmt::to_string(first
.value
);
537 return fmt::to_string(first
);
541 constexpr auto compiled
= detail::compile
<Args
...>(S());
542 if constexpr (std::is_same
<remove_cvref_t
<decltype(compiled
)>,
543 detail::unknown_format
>()) {
545 static_cast<basic_string_view
<typename
S::char_type
>>(S()),
546 std::forward
<Args
>(args
)...);
548 return fmt::format(compiled
, std::forward
<Args
>(args
)...);
552 template <typename OutputIt
, typename S
, typename
... Args
,
553 FMT_ENABLE_IF(detail::is_compiled_string
<S
>::value
)>
554 FMT_CONSTEXPR OutputIt
format_to(OutputIt out
, const S
&, Args
&&... args
) {
555 constexpr auto compiled
= detail::compile
<Args
...>(S());
556 if constexpr (std::is_same
<remove_cvref_t
<decltype(compiled
)>,
557 detail::unknown_format
>()) {
558 return fmt::format_to(
559 out
, static_cast<basic_string_view
<typename
S::char_type
>>(S()),
560 std::forward
<Args
>(args
)...);
562 return fmt::format_to(out
, compiled
, std::forward
<Args
>(args
)...);
567 template <typename OutputIt
, typename S
, typename
... Args
,
568 FMT_ENABLE_IF(detail::is_compiled_string
<S
>::value
)>
569 format_to_n_result
<OutputIt
> format_to_n(OutputIt out
, size_t n
,
570 const S
& format_str
, Args
&&... args
) {
571 auto it
= fmt::format_to(detail::truncating_iterator
<OutputIt
>(out
, n
),
572 format_str
, std::forward
<Args
>(args
)...);
573 return {it
.base(), it
.count()};
576 template <typename S
, typename
... Args
,
577 FMT_ENABLE_IF(detail::is_compiled_string
<S
>::value
)>
578 FMT_CONSTEXPR20
size_t formatted_size(const S
& format_str
,
579 const Args
&... args
) {
580 return fmt::format_to(detail::counting_iterator(), format_str
, args
...)
584 template <typename S
, typename
... Args
,
585 FMT_ENABLE_IF(detail::is_compiled_string
<S
>::value
)>
586 void print(std::FILE* f
, const S
& format_str
, const Args
&... args
) {
587 memory_buffer buffer
;
588 fmt::format_to(std::back_inserter(buffer
), format_str
, args
...);
589 detail::print(f
, {buffer
.data(), buffer
.size()});
592 template <typename S
, typename
... Args
,
593 FMT_ENABLE_IF(detail::is_compiled_string
<S
>::value
)>
594 void print(const S
& format_str
, const Args
&... args
) {
595 print(stdout
, format_str
, args
...);
598 #if FMT_USE_NONTYPE_TEMPLATE_ARGS
599 inline namespace literals
{
600 template <detail_exported::fixed_string Str
> constexpr auto operator""_cf() {
601 using char_t
= remove_cvref_t
<decltype(Str
.data
[0])>;
602 return detail::udl_compiled_string
<char_t
, sizeof(Str
.data
) / sizeof(char_t
),
605 } // namespace literals
608 FMT_MODULE_EXPORT_END
611 #endif // FMT_COMPILE_H_