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 // A compile-time string which is compiled into fast formatting code.
23 class compiled_string
{};
26 struct is_compiled_string
: std::is_base_of
<compiled_string
, S
> {};
30 Converts a string literal *s* into a format string that will be parsed at
31 compile time and converted into efficient formatting code. Requires C++17
32 ``constexpr if`` compiler support.
36 // Converts 42 into std::string using the most efficient method and no
37 // runtime format string processing.
38 std::string s = fmt::format(FMT_COMPILE("{}"), 42);
41 #if defined(__cpp_if_constexpr) && defined(__cpp_return_type_deduction)
42 # define FMT_COMPILE(s) \
43 FMT_STRING_IMPL(s, fmt::detail::compiled_string, explicit)
45 # define FMT_COMPILE(s) FMT_STRING(s)
48 #if FMT_USE_NONTYPE_TEMPLATE_ARGS
49 template <typename Char
, size_t N
,
50 fmt::detail_exported::fixed_string
<Char
, N
> Str
>
51 struct udl_compiled_string
: compiled_string
{
52 using char_type
= Char
;
53 explicit constexpr operator basic_string_view
<char_type
>() const {
54 return {Str
.data
, N
- 1};
59 template <typename T
, typename
... Tail
>
60 const T
& first(const T
& value
, const Tail
&...) {
64 #if defined(__cpp_if_constexpr) && defined(__cpp_return_type_deduction)
65 template <typename
... Args
> struct type_list
{};
67 // Returns a reference to the argument at index N from [first, rest...].
68 template <int N
, typename T
, typename
... Args
>
69 constexpr const auto& get([[maybe_unused
]] const T
& first
,
70 [[maybe_unused
]] const Args
&... rest
) {
71 static_assert(N
< 1 + sizeof...(Args
), "index is out of bounds");
75 return detail::get
<N
- 1>(rest
...);
78 template <typename Char
, typename
... Args
>
79 constexpr int get_arg_index_by_name(basic_string_view
<Char
> name
,
81 return get_arg_index_by_name
<Args
...>(name
);
84 template <int N
, typename
> struct get_type_impl
;
86 template <int N
, typename
... Args
> struct get_type_impl
<N
, type_list
<Args
...>> {
88 remove_cvref_t
<decltype(detail::get
<N
>(std::declval
<Args
>()...))>;
91 template <int N
, typename T
>
92 using get_type
= typename get_type_impl
<N
, T
>::type
;
94 template <typename T
> struct is_compiled_format
: std::false_type
{};
96 template <typename Char
> struct text
{
97 basic_string_view
<Char
> data
;
98 using char_type
= Char
;
100 template <typename OutputIt
, typename
... Args
>
101 constexpr OutputIt
format(OutputIt out
, const Args
&...) const {
102 return write
<Char
>(out
, data
);
106 template <typename Char
>
107 struct is_compiled_format
<text
<Char
>> : std::true_type
{};
109 template <typename Char
>
110 constexpr text
<Char
> make_text(basic_string_view
<Char
> s
, size_t pos
,
112 return {{&s
[pos
], size
}};
115 template <typename Char
> struct code_unit
{
117 using char_type
= Char
;
119 template <typename OutputIt
, typename
... Args
>
120 constexpr OutputIt
format(OutputIt out
, const Args
&...) const {
126 // This ensures that the argument type is convertible to `const T&`.
127 template <typename T
, int N
, typename
... Args
>
128 constexpr const T
& get_arg_checked(const Args
&... args
) {
129 const auto& arg
= detail::get
<N
>(args
...);
130 if constexpr (detail::is_named_arg
<remove_cvref_t
<decltype(arg
)>>()) {
137 template <typename Char
>
138 struct is_compiled_format
<code_unit
<Char
>> : std::true_type
{};
140 // A replacement field that refers to argument N.
141 template <typename Char
, typename T
, int N
> struct field
{
142 using char_type
= Char
;
144 template <typename OutputIt
, typename
... Args
>
145 constexpr OutputIt
format(OutputIt out
, const Args
&... args
) const {
146 const T
& arg
= get_arg_checked
<T
, N
>(args
...);
147 if constexpr (std::is_convertible_v
<T
, basic_string_view
<Char
>>) {
148 auto s
= basic_string_view
<Char
>(arg
);
149 return copy_str
<Char
>(s
.begin(), s
.end(), out
);
151 return write
<Char
>(out
, arg
);
155 template <typename Char
, typename T
, int N
>
156 struct is_compiled_format
<field
<Char
, T
, N
>> : std::true_type
{};
158 // A replacement field that refers to argument with name.
159 template <typename Char
> struct runtime_named_field
{
160 using char_type
= Char
;
161 basic_string_view
<Char
> name
;
163 template <typename OutputIt
, typename T
>
164 constexpr static bool try_format_argument(
166 // [[maybe_unused]] due to unused-but-set-parameter warning in GCC 7,8,9
167 [[maybe_unused
]] basic_string_view
<Char
> arg_name
, const T
& arg
) {
168 if constexpr (is_named_arg
<typename
std::remove_cv
<T
>::type
>::value
) {
169 if (arg_name
== arg
.name
) {
170 out
= write
<Char
>(out
, arg
.value
);
177 template <typename OutputIt
, typename
... Args
>
178 constexpr OutputIt
format(OutputIt out
, const Args
&... args
) const {
179 bool found
= (try_format_argument(out
, name
, args
) || ...);
181 FMT_THROW(format_error("argument with specified name is not found"));
187 template <typename Char
>
188 struct is_compiled_format
<runtime_named_field
<Char
>> : std::true_type
{};
190 // A replacement field that refers to argument N and has format specifiers.
191 template <typename Char
, typename T
, int N
> struct spec_field
{
192 using char_type
= Char
;
193 formatter
<T
, Char
> fmt
;
195 template <typename OutputIt
, typename
... Args
>
196 constexpr FMT_INLINE OutputIt
format(OutputIt out
,
197 const Args
&... args
) const {
199 fmt::make_format_args
<basic_format_context
<OutputIt
, Char
>>(args
...);
200 basic_format_context
<OutputIt
, Char
> ctx(out
, vargs
);
201 return fmt
.format(get_arg_checked
<T
, N
>(args
...), ctx
);
205 template <typename Char
, typename T
, int N
>
206 struct is_compiled_format
<spec_field
<Char
, T
, N
>> : std::true_type
{};
208 template <typename L
, typename R
> struct concat
{
211 using char_type
= typename
L::char_type
;
213 template <typename OutputIt
, typename
... Args
>
214 constexpr OutputIt
format(OutputIt out
, const Args
&... args
) const {
215 out
= lhs
.format(out
, args
...);
216 return rhs
.format(out
, args
...);
220 template <typename L
, typename R
>
221 struct is_compiled_format
<concat
<L
, R
>> : std::true_type
{};
223 template <typename L
, typename R
>
224 constexpr concat
<L
, R
> make_concat(L lhs
, R rhs
) {
228 struct unknown_format
{};
230 template <typename Char
>
231 constexpr size_t parse_text(basic_string_view
<Char
> str
, size_t pos
) {
232 for (size_t size
= str
.size(); pos
!= size
; ++pos
) {
233 if (str
[pos
] == '{' || str
[pos
] == '}') break;
238 template <typename Args
, size_t POS
, int ID
, typename S
>
239 constexpr auto compile_format_string(S format_str
);
241 template <typename Args
, size_t POS
, int ID
, typename T
, typename S
>
242 constexpr auto parse_tail(T head
, S format_str
) {
244 basic_string_view
<typename
S::char_type
>(format_str
).size()) {
245 constexpr auto tail
= compile_format_string
<Args
, POS
, ID
>(format_str
);
246 if constexpr (std::is_same
<remove_cvref_t
<decltype(tail
)>,
250 return make_concat(head
, tail
);
256 template <typename T
, typename Char
> struct parse_specs_result
{
257 formatter
<T
, Char
> fmt
;
262 enum { manual_indexing_id
= -1 };
264 template <typename T
, typename Char
>
265 constexpr parse_specs_result
<T
, Char
> parse_specs(basic_string_view
<Char
> str
,
266 size_t pos
, int next_arg_id
) {
267 str
.remove_prefix(pos
);
269 compile_parse_context
<Char
>(str
, max_value
<int>(), nullptr, next_arg_id
);
270 auto f
= formatter
<T
, Char
>();
271 auto end
= f
.parse(ctx
);
272 return {f
, pos
+ fmt::detail::to_unsigned(end
- str
.data()),
273 next_arg_id
== 0 ? manual_indexing_id
: ctx
.next_arg_id()};
276 template <typename Char
> struct arg_id_handler
{
277 arg_ref
<Char
> arg_id
;
279 constexpr int on_auto() {
280 FMT_ASSERT(false, "handler cannot be used with automatic indexing");
283 constexpr int on_index(int id
) {
284 arg_id
= arg_ref
<Char
>(id
);
287 constexpr int on_name(basic_string_view
<Char
> id
) {
288 arg_id
= arg_ref
<Char
>(id
);
293 template <typename Char
> struct parse_arg_id_result
{
294 arg_ref
<Char
> arg_id
;
295 const Char
* arg_id_end
;
298 template <int ID
, typename Char
>
299 constexpr auto parse_arg_id(const Char
* begin
, const Char
* end
) {
300 auto handler
= arg_id_handler
<Char
>{arg_ref
<Char
>{}};
301 auto arg_id_end
= parse_arg_id(begin
, end
, handler
);
302 return parse_arg_id_result
<Char
>{handler
.arg_id
, arg_id_end
};
305 template <typename T
, typename Enable
= void> struct field_type
{
306 using type
= remove_cvref_t
<T
>;
309 template <typename T
>
310 struct field_type
<T
, enable_if_t
<detail::is_named_arg
<T
>::value
>> {
311 using type
= remove_cvref_t
<decltype(T::value
)>;
314 template <typename T
, typename Args
, size_t END_POS
, int ARG_INDEX
, int NEXT_ID
,
316 constexpr auto parse_replacement_field_then_tail(S format_str
) {
317 using char_type
= typename
S::char_type
;
318 constexpr auto str
= basic_string_view
<char_type
>(format_str
);
319 constexpr char_type c
= END_POS
!= str
.size() ? str
[END_POS
] : char_type();
320 if constexpr (c
== '}') {
321 return parse_tail
<Args
, END_POS
+ 1, NEXT_ID
>(
322 field
<char_type
, typename field_type
<T
>::type
, ARG_INDEX
>(),
324 } else if constexpr (c
!= ':') {
325 FMT_THROW(format_error("expected ':'"));
327 constexpr auto result
= parse_specs
<typename field_type
<T
>::type
>(
328 str
, END_POS
+ 1, NEXT_ID
== manual_indexing_id
? 0 : NEXT_ID
);
329 if constexpr (result
.end
>= str
.size() || str
[result
.end
] != '}') {
330 FMT_THROW(format_error("expected '}'"));
333 return parse_tail
<Args
, result
.end
+ 1, result
.next_arg_id
>(
334 spec_field
<char_type
, typename field_type
<T
>::type
, ARG_INDEX
>{
341 // Compiles a non-empty format string and returns the compiled representation
342 // or unknown_format() on unrecognized input.
343 template <typename Args
, size_t POS
, int ID
, typename S
>
344 constexpr auto compile_format_string(S format_str
) {
345 using char_type
= typename
S::char_type
;
346 constexpr auto str
= basic_string_view
<char_type
>(format_str
);
347 if constexpr (str
[POS
] == '{') {
348 if constexpr (POS
+ 1 == str
.size())
349 FMT_THROW(format_error("unmatched '{' in format string"));
350 if constexpr (str
[POS
+ 1] == '{') {
351 return parse_tail
<Args
, POS
+ 2, ID
>(make_text(str
, POS
, 1), format_str
);
352 } else if constexpr (str
[POS
+ 1] == '}' || str
[POS
+ 1] == ':') {
353 static_assert(ID
!= manual_indexing_id
,
354 "cannot switch from manual to automatic argument indexing");
355 constexpr auto next_id
=
356 ID
!= manual_indexing_id
? ID
+ 1 : manual_indexing_id
;
357 return parse_replacement_field_then_tail
<get_type
<ID
, Args
>, Args
,
358 POS
+ 1, ID
, next_id
>(
361 constexpr auto arg_id_result
=
362 parse_arg_id
<ID
>(str
.data() + POS
+ 1, str
.data() + str
.size());
363 constexpr auto arg_id_end_pos
= arg_id_result
.arg_id_end
- str
.data();
364 constexpr char_type c
=
365 arg_id_end_pos
!= str
.size() ? str
[arg_id_end_pos
] : char_type();
366 static_assert(c
== '}' || c
== ':', "missing '}' in format string");
367 if constexpr (arg_id_result
.arg_id
.kind
== arg_id_kind::index
) {
369 ID
== manual_indexing_id
|| ID
== 0,
370 "cannot switch from automatic to manual argument indexing");
371 constexpr auto arg_index
= arg_id_result
.arg_id
.val
.index
;
372 return parse_replacement_field_then_tail
<get_type
<arg_index
, Args
>,
373 Args
, arg_id_end_pos
,
374 arg_index
, manual_indexing_id
>(
376 } else if constexpr (arg_id_result
.arg_id
.kind
== arg_id_kind::name
) {
377 constexpr auto arg_index
=
378 get_arg_index_by_name(arg_id_result
.arg_id
.val
.name
, Args
{});
379 if constexpr (arg_index
>= 0) {
380 constexpr auto next_id
=
381 ID
!= manual_indexing_id
? ID
+ 1 : manual_indexing_id
;
382 return parse_replacement_field_then_tail
<
383 decltype(get_type
<arg_index
, Args
>::value
), Args
, arg_id_end_pos
,
384 arg_index
, next_id
>(format_str
);
385 } else if constexpr (c
== '}') {
386 return parse_tail
<Args
, arg_id_end_pos
+ 1, ID
>(
387 runtime_named_field
<char_type
>{arg_id_result
.arg_id
.val
.name
},
389 } else if constexpr (c
== ':') {
390 return unknown_format(); // no type info for specs parsing
394 } else if constexpr (str
[POS
] == '}') {
395 if constexpr (POS
+ 1 == str
.size())
396 FMT_THROW(format_error("unmatched '}' in format string"));
397 return parse_tail
<Args
, POS
+ 2, ID
>(make_text(str
, POS
, 1), format_str
);
399 constexpr auto end
= parse_text(str
, POS
+ 1);
400 if constexpr (end
- POS
> 1) {
401 return parse_tail
<Args
, end
, ID
>(make_text(str
, POS
, end
- POS
),
404 return parse_tail
<Args
, end
, ID
>(code_unit
<char_type
>{str
[POS
]},
410 template <typename
... Args
, typename S
,
411 FMT_ENABLE_IF(detail::is_compiled_string
<S
>::value
)>
412 constexpr auto compile(S format_str
) {
413 constexpr auto str
= basic_string_view
<typename
S::char_type
>(format_str
);
414 if constexpr (str
.size() == 0) {
415 return detail::make_text(str
, 0, 0);
417 constexpr auto result
=
418 detail::compile_format_string
<detail::type_list
<Args
...>, 0, 0>(
423 #endif // defined(__cpp_if_constexpr) && defined(__cpp_return_type_deduction)
424 } // namespace detail
428 #if defined(__cpp_if_constexpr) && defined(__cpp_return_type_deduction)
430 template <typename CompiledFormat
, typename
... Args
,
431 typename Char
= typename
CompiledFormat::char_type
,
432 FMT_ENABLE_IF(detail::is_compiled_format
<CompiledFormat
>::value
)>
433 FMT_INLINE
std::basic_string
<Char
> format(const CompiledFormat
& cf
,
434 const Args
&... args
) {
435 auto s
= std::basic_string
<Char
>();
436 cf
.format(std::back_inserter(s
), args
...);
440 template <typename OutputIt
, typename CompiledFormat
, typename
... Args
,
441 FMT_ENABLE_IF(detail::is_compiled_format
<CompiledFormat
>::value
)>
442 constexpr FMT_INLINE OutputIt
format_to(OutputIt out
, const CompiledFormat
& cf
,
443 const Args
&... args
) {
444 return cf
.format(out
, args
...);
447 template <typename S
, typename
... Args
,
448 FMT_ENABLE_IF(detail::is_compiled_string
<S
>::value
)>
449 FMT_INLINE
std::basic_string
<typename
S::char_type
> format(const S
&,
451 if constexpr (std::is_same
<typename
S::char_type
, char>::value
) {
452 constexpr auto str
= basic_string_view
<typename
S::char_type
>(S());
453 if constexpr (str
.size() == 2 && str
[0] == '{' && str
[1] == '}') {
454 const auto& first
= detail::first(args
...);
455 if constexpr (detail::is_named_arg
<
456 remove_cvref_t
<decltype(first
)>>::value
) {
457 return fmt::to_string(first
.value
);
459 return fmt::to_string(first
);
463 constexpr auto compiled
= detail::compile
<Args
...>(S());
464 if constexpr (std::is_same
<remove_cvref_t
<decltype(compiled
)>,
465 detail::unknown_format
>()) {
467 static_cast<basic_string_view
<typename
S::char_type
>>(S()),
468 std::forward
<Args
>(args
)...);
470 return fmt::format(compiled
, std::forward
<Args
>(args
)...);
474 template <typename OutputIt
, typename S
, typename
... Args
,
475 FMT_ENABLE_IF(detail::is_compiled_string
<S
>::value
)>
476 FMT_CONSTEXPR OutputIt
format_to(OutputIt out
, const S
&, Args
&&... args
) {
477 constexpr auto compiled
= detail::compile
<Args
...>(S());
478 if constexpr (std::is_same
<remove_cvref_t
<decltype(compiled
)>,
479 detail::unknown_format
>()) {
480 return fmt::format_to(
481 out
, static_cast<basic_string_view
<typename
S::char_type
>>(S()),
482 std::forward
<Args
>(args
)...);
484 return fmt::format_to(out
, compiled
, std::forward
<Args
>(args
)...);
489 template <typename OutputIt
, typename S
, typename
... Args
,
490 FMT_ENABLE_IF(detail::is_compiled_string
<S
>::value
)>
491 format_to_n_result
<OutputIt
> format_to_n(OutputIt out
, size_t n
,
492 const S
& format_str
, Args
&&... args
) {
493 using traits
= detail::fixed_buffer_traits
;
494 auto buf
= detail::iterator_buffer
<OutputIt
, char, traits
>(out
, n
);
495 format_to(std::back_inserter(buf
), format_str
, std::forward
<Args
>(args
)...);
496 return {buf
.out(), buf
.count()};
499 template <typename S
, typename
... Args
,
500 FMT_ENABLE_IF(detail::is_compiled_string
<S
>::value
)>
501 FMT_CONSTEXPR20
size_t formatted_size(const S
& format_str
,
502 const Args
&... args
) {
503 return fmt::format_to(detail::counting_iterator(), format_str
, args
...)
507 template <typename S
, typename
... Args
,
508 FMT_ENABLE_IF(detail::is_compiled_string
<S
>::value
)>
509 void print(std::FILE* f
, const S
& format_str
, const Args
&... args
) {
510 memory_buffer buffer
;
511 fmt::format_to(std::back_inserter(buffer
), format_str
, args
...);
512 detail::print(f
, {buffer
.data(), buffer
.size()});
515 template <typename S
, typename
... Args
,
516 FMT_ENABLE_IF(detail::is_compiled_string
<S
>::value
)>
517 void print(const S
& format_str
, const Args
&... args
) {
518 print(stdout
, format_str
, args
...);
521 #if FMT_USE_NONTYPE_TEMPLATE_ARGS
522 inline namespace literals
{
523 template <detail_exported::fixed_string Str
> constexpr auto operator""_cf() {
524 using char_t
= remove_cvref_t
<decltype(Str
.data
[0])>;
525 return detail::udl_compiled_string
<char_t
, sizeof(Str
.data
) / sizeof(char_t
),
528 } // namespace literals
534 #endif // FMT_COMPILE_H_