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.
12 # include <iterator> // std::back_inserter
19 // A compile-time string which is compiled into fast formatting code.
20 FMT_EXPORT
class compiled_string
{};
24 template <typename T
, typename InputIt
>
25 FMT_CONSTEXPR
inline auto copy(InputIt begin
, InputIt end
, counting_iterator it
)
26 -> counting_iterator
{
27 return it
+ (end
- begin
);
31 struct is_compiled_string
: std::is_base_of
<compiled_string
, S
> {};
34 * Converts a string literal `s` into a format string that will be parsed at
35 * compile time and converted into efficient formatting code. Requires C++17
36 * `constexpr if` compiler support.
40 * // Converts 42 into std::string using the most efficient method and no
41 * // runtime format string processing.
42 * std::string s = fmt::format(FMT_COMPILE("{}"), 42);
44 #if defined(__cpp_if_constexpr) && defined(__cpp_return_type_deduction)
45 # define FMT_COMPILE(s) FMT_STRING_IMPL(s, fmt::compiled_string, explicit)
47 # define FMT_COMPILE(s) FMT_STRING(s)
50 #if FMT_USE_NONTYPE_TEMPLATE_ARGS
51 template <typename Char
, size_t N
,
52 fmt::detail_exported::fixed_string
<Char
, N
> Str
>
53 struct udl_compiled_string
: compiled_string
{
54 using char_type
= Char
;
55 explicit constexpr operator basic_string_view
<char_type
>() const {
56 return {Str
.data
, N
- 1};
61 template <typename T
, typename
... Tail
>
62 auto first(const T
& value
, const Tail
&...) -> const T
& {
66 #if defined(__cpp_if_constexpr) && defined(__cpp_return_type_deduction)
67 template <typename
... Args
> struct type_list
{};
69 // Returns a reference to the argument at index N from [first, rest...].
70 template <int N
, typename T
, typename
... Args
>
71 constexpr const auto& get([[maybe_unused
]] const T
& first
,
72 [[maybe_unused
]] const Args
&... rest
) {
73 static_assert(N
< 1 + sizeof...(Args
), "index is out of bounds");
77 return detail::get
<N
- 1>(rest
...);
80 template <typename Char
, typename
... Args
>
81 constexpr int get_arg_index_by_name(basic_string_view
<Char
> name
,
83 return get_arg_index_by_name
<Args
...>(name
);
86 template <int N
, typename
> struct get_type_impl
;
88 template <int N
, typename
... Args
> struct get_type_impl
<N
, type_list
<Args
...>> {
90 remove_cvref_t
<decltype(detail::get
<N
>(std::declval
<Args
>()...))>;
93 template <int N
, typename T
>
94 using get_type
= typename get_type_impl
<N
, T
>::type
;
96 template <typename T
> struct is_compiled_format
: std::false_type
{};
98 template <typename Char
> struct text
{
99 basic_string_view
<Char
> data
;
100 using char_type
= Char
;
102 template <typename OutputIt
, typename
... Args
>
103 constexpr OutputIt
format(OutputIt out
, const Args
&...) const {
104 return write
<Char
>(out
, data
);
108 template <typename Char
>
109 struct is_compiled_format
<text
<Char
>> : std::true_type
{};
111 template <typename Char
>
112 constexpr text
<Char
> make_text(basic_string_view
<Char
> s
, size_t pos
,
114 return {{&s
[pos
], size
}};
117 template <typename Char
> struct code_unit
{
119 using char_type
= Char
;
121 template <typename OutputIt
, typename
... Args
>
122 constexpr OutputIt
format(OutputIt out
, const Args
&...) const {
128 // This ensures that the argument type is convertible to `const T&`.
129 template <typename T
, int N
, typename
... Args
>
130 constexpr const T
& get_arg_checked(const Args
&... args
) {
131 const auto& arg
= detail::get
<N
>(args
...);
132 if constexpr (detail::is_named_arg
<remove_cvref_t
<decltype(arg
)>>()) {
139 template <typename Char
>
140 struct is_compiled_format
<code_unit
<Char
>> : std::true_type
{};
142 // A replacement field that refers to argument N.
143 template <typename Char
, typename T
, int N
> struct field
{
144 using char_type
= Char
;
146 template <typename OutputIt
, typename
... Args
>
147 constexpr OutputIt
format(OutputIt out
, const Args
&... args
) const {
148 const T
& arg
= get_arg_checked
<T
, N
>(args
...);
149 if constexpr (std::is_convertible
<T
, basic_string_view
<Char
>>::value
) {
150 auto s
= basic_string_view
<Char
>(arg
);
151 return copy
<Char
>(s
.begin(), s
.end(), out
);
153 return write
<Char
>(out
, arg
);
157 template <typename Char
, typename T
, int N
>
158 struct is_compiled_format
<field
<Char
, T
, N
>> : std::true_type
{};
160 // A replacement field that refers to argument with name.
161 template <typename Char
> struct runtime_named_field
{
162 using char_type
= Char
;
163 basic_string_view
<Char
> name
;
165 template <typename OutputIt
, typename T
>
166 constexpr static bool try_format_argument(
168 // [[maybe_unused]] due to unused-but-set-parameter warning in GCC 7,8,9
169 [[maybe_unused
]] basic_string_view
<Char
> arg_name
, const T
& arg
) {
170 if constexpr (is_named_arg
<typename
std::remove_cv
<T
>::type
>::value
) {
171 if (arg_name
== arg
.name
) {
172 out
= write
<Char
>(out
, arg
.value
);
179 template <typename OutputIt
, typename
... Args
>
180 constexpr OutputIt
format(OutputIt out
, const Args
&... args
) const {
181 bool found
= (try_format_argument(out
, name
, args
) || ...);
183 FMT_THROW(format_error("argument with specified name is not found"));
189 template <typename Char
>
190 struct is_compiled_format
<runtime_named_field
<Char
>> : std::true_type
{};
192 // A replacement field that refers to argument N and has format specifiers.
193 template <typename Char
, typename T
, int N
> struct spec_field
{
194 using char_type
= Char
;
195 formatter
<T
, Char
> fmt
;
197 template <typename OutputIt
, typename
... Args
>
198 constexpr FMT_INLINE OutputIt
format(OutputIt out
,
199 const Args
&... args
) const {
201 fmt::make_format_args
<basic_format_context
<OutputIt
, Char
>>(args
...);
202 basic_format_context
<OutputIt
, Char
> ctx(out
, vargs
);
203 return fmt
.format(get_arg_checked
<T
, N
>(args
...), ctx
);
207 template <typename Char
, typename T
, int N
>
208 struct is_compiled_format
<spec_field
<Char
, T
, N
>> : std::true_type
{};
210 template <typename L
, typename R
> struct concat
{
213 using char_type
= typename
L::char_type
;
215 template <typename OutputIt
, typename
... Args
>
216 constexpr OutputIt
format(OutputIt out
, const Args
&... args
) const {
217 out
= lhs
.format(out
, args
...);
218 return rhs
.format(out
, args
...);
222 template <typename L
, typename R
>
223 struct is_compiled_format
<concat
<L
, R
>> : std::true_type
{};
225 template <typename L
, typename R
>
226 constexpr concat
<L
, R
> make_concat(L lhs
, R rhs
) {
230 struct unknown_format
{};
232 template <typename Char
>
233 constexpr size_t parse_text(basic_string_view
<Char
> str
, size_t pos
) {
234 for (size_t size
= str
.size(); pos
!= size
; ++pos
) {
235 if (str
[pos
] == '{' || str
[pos
] == '}') break;
240 template <typename Args
, size_t POS
, int ID
, typename S
>
241 constexpr auto compile_format_string(S fmt
);
243 template <typename Args
, size_t POS
, int ID
, typename T
, typename S
>
244 constexpr auto parse_tail(T head
, S fmt
) {
245 if constexpr (POS
!= basic_string_view
<typename
S::char_type
>(fmt
).size()) {
246 constexpr auto tail
= compile_format_string
<Args
, POS
, ID
>(fmt
);
247 if constexpr (std::is_same
<remove_cvref_t
<decltype(tail
)>,
251 return make_concat(head
, tail
);
257 template <typename T
, typename Char
> struct parse_specs_result
{
258 formatter
<T
, Char
> fmt
;
263 enum { manual_indexing_id
= -1 };
265 template <typename T
, typename Char
>
266 constexpr parse_specs_result
<T
, Char
> parse_specs(basic_string_view
<Char
> str
,
267 size_t pos
, int next_arg_id
) {
268 str
.remove_prefix(pos
);
270 compile_parse_context
<Char
>(str
, max_value
<int>(), nullptr, next_arg_id
);
271 auto f
= formatter
<T
, Char
>();
272 auto end
= f
.parse(ctx
);
273 return {f
, pos
+ fmt::detail::to_unsigned(end
- str
.data()),
274 next_arg_id
== 0 ? manual_indexing_id
: ctx
.next_arg_id()};
277 template <typename Char
> struct arg_id_handler
{
278 arg_ref
<Char
> arg_id
;
280 constexpr int on_auto() {
281 FMT_ASSERT(false, "handler cannot be used with automatic indexing");
284 constexpr int on_index(int id
) {
285 arg_id
= arg_ref
<Char
>(id
);
288 constexpr int on_name(basic_string_view
<Char
> id
) {
289 arg_id
= arg_ref
<Char
>(id
);
294 template <typename Char
> struct parse_arg_id_result
{
295 arg_ref
<Char
> arg_id
;
296 const Char
* arg_id_end
;
299 template <int ID
, typename Char
>
300 constexpr auto parse_arg_id(const Char
* begin
, const Char
* end
) {
301 auto handler
= arg_id_handler
<Char
>{arg_ref
<Char
>{}};
302 auto arg_id_end
= parse_arg_id(begin
, end
, handler
);
303 return parse_arg_id_result
<Char
>{handler
.arg_id
, arg_id_end
};
306 template <typename T
, typename Enable
= void> struct field_type
{
307 using type
= remove_cvref_t
<T
>;
310 template <typename T
>
311 struct field_type
<T
, enable_if_t
<detail::is_named_arg
<T
>::value
>> {
312 using type
= remove_cvref_t
<decltype(T::value
)>;
315 template <typename T
, typename Args
, size_t END_POS
, int ARG_INDEX
, int NEXT_ID
,
317 constexpr auto parse_replacement_field_then_tail(S fmt
) {
318 using char_type
= typename
S::char_type
;
319 constexpr auto str
= basic_string_view
<char_type
>(fmt
);
320 constexpr char_type c
= END_POS
!= str
.size() ? str
[END_POS
] : char_type();
321 if constexpr (c
== '}') {
322 return parse_tail
<Args
, END_POS
+ 1, NEXT_ID
>(
323 field
<char_type
, typename field_type
<T
>::type
, ARG_INDEX
>(), fmt
);
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 fmt
) {
345 using char_type
= typename
S::char_type
;
346 constexpr auto str
= basic_string_view
<char_type
>(fmt
);
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), fmt
);
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
>(fmt
);
360 constexpr auto arg_id_result
=
361 parse_arg_id
<ID
>(str
.data() + POS
+ 1, str
.data() + str
.size());
362 constexpr auto arg_id_end_pos
= arg_id_result
.arg_id_end
- str
.data();
363 constexpr char_type c
=
364 arg_id_end_pos
!= str
.size() ? str
[arg_id_end_pos
] : char_type();
365 static_assert(c
== '}' || c
== ':', "missing '}' in format string");
366 if constexpr (arg_id_result
.arg_id
.kind
== arg_id_kind::index
) {
368 ID
== manual_indexing_id
|| ID
== 0,
369 "cannot switch from automatic to manual argument indexing");
370 constexpr auto arg_index
= arg_id_result
.arg_id
.val
.index
;
371 return parse_replacement_field_then_tail
<get_type
<arg_index
, Args
>,
372 Args
, arg_id_end_pos
,
373 arg_index
, manual_indexing_id
>(
375 } else if constexpr (arg_id_result
.arg_id
.kind
== arg_id_kind::name
) {
376 constexpr auto arg_index
=
377 get_arg_index_by_name(arg_id_result
.arg_id
.val
.name
, Args
{});
378 if constexpr (arg_index
>= 0) {
379 constexpr auto next_id
=
380 ID
!= manual_indexing_id
? ID
+ 1 : manual_indexing_id
;
381 return parse_replacement_field_then_tail
<
382 decltype(get_type
<arg_index
, Args
>::value
), Args
, arg_id_end_pos
,
383 arg_index
, next_id
>(fmt
);
384 } else if constexpr (c
== '}') {
385 return parse_tail
<Args
, arg_id_end_pos
+ 1, ID
>(
386 runtime_named_field
<char_type
>{arg_id_result
.arg_id
.val
.name
},
388 } else if constexpr (c
== ':') {
389 return unknown_format(); // no type info for specs parsing
393 } else if constexpr (str
[POS
] == '}') {
394 if constexpr (POS
+ 1 == str
.size())
395 FMT_THROW(format_error("unmatched '}' in format string"));
396 return parse_tail
<Args
, POS
+ 2, ID
>(make_text(str
, POS
, 1), fmt
);
398 constexpr auto end
= parse_text(str
, POS
+ 1);
399 if constexpr (end
- POS
> 1) {
400 return parse_tail
<Args
, end
, ID
>(make_text(str
, POS
, end
- POS
), fmt
);
402 return parse_tail
<Args
, end
, ID
>(code_unit
<char_type
>{str
[POS
]}, fmt
);
407 template <typename
... Args
, typename S
,
408 FMT_ENABLE_IF(detail::is_compiled_string
<S
>::value
)>
409 constexpr auto compile(S fmt
) {
410 constexpr auto str
= basic_string_view
<typename
S::char_type
>(fmt
);
411 if constexpr (str
.size() == 0) {
412 return detail::make_text(str
, 0, 0);
414 constexpr auto result
=
415 detail::compile_format_string
<detail::type_list
<Args
...>, 0, 0>(fmt
);
419 #endif // defined(__cpp_if_constexpr) && defined(__cpp_return_type_deduction)
420 } // namespace detail
424 #if defined(__cpp_if_constexpr) && defined(__cpp_return_type_deduction)
426 template <typename CompiledFormat
, typename
... Args
,
427 typename Char
= typename
CompiledFormat::char_type
,
428 FMT_ENABLE_IF(detail::is_compiled_format
<CompiledFormat
>::value
)>
429 FMT_INLINE
std::basic_string
<Char
> format(const CompiledFormat
& cf
,
430 const Args
&... args
) {
431 auto s
= std::basic_string
<Char
>();
432 cf
.format(std::back_inserter(s
), args
...);
436 template <typename OutputIt
, typename CompiledFormat
, typename
... Args
,
437 FMT_ENABLE_IF(detail::is_compiled_format
<CompiledFormat
>::value
)>
438 constexpr FMT_INLINE OutputIt
format_to(OutputIt out
, const CompiledFormat
& cf
,
439 const Args
&... args
) {
440 return cf
.format(out
, args
...);
443 template <typename S
, typename
... Args
,
444 FMT_ENABLE_IF(detail::is_compiled_string
<S
>::value
)>
445 FMT_INLINE
std::basic_string
<typename
S::char_type
> format(const S
&,
447 if constexpr (std::is_same
<typename
S::char_type
, char>::value
) {
448 constexpr auto str
= basic_string_view
<typename
S::char_type
>(S());
449 if constexpr (str
.size() == 2 && str
[0] == '{' && str
[1] == '}') {
450 const auto& first
= detail::first(args
...);
451 if constexpr (detail::is_named_arg
<
452 remove_cvref_t
<decltype(first
)>>::value
) {
453 return fmt::to_string(first
.value
);
455 return fmt::to_string(first
);
459 constexpr auto compiled
= detail::compile
<Args
...>(S());
460 if constexpr (std::is_same
<remove_cvref_t
<decltype(compiled
)>,
461 detail::unknown_format
>()) {
463 static_cast<basic_string_view
<typename
S::char_type
>>(S()),
464 std::forward
<Args
>(args
)...);
466 return fmt::format(compiled
, std::forward
<Args
>(args
)...);
470 template <typename OutputIt
, typename S
, typename
... Args
,
471 FMT_ENABLE_IF(detail::is_compiled_string
<S
>::value
)>
472 FMT_CONSTEXPR OutputIt
format_to(OutputIt out
, const S
&, Args
&&... args
) {
473 constexpr auto compiled
= detail::compile
<Args
...>(S());
474 if constexpr (std::is_same
<remove_cvref_t
<decltype(compiled
)>,
475 detail::unknown_format
>()) {
476 return fmt::format_to(
477 out
, static_cast<basic_string_view
<typename
S::char_type
>>(S()),
478 std::forward
<Args
>(args
)...);
480 return fmt::format_to(out
, compiled
, std::forward
<Args
>(args
)...);
485 template <typename OutputIt
, typename S
, typename
... Args
,
486 FMT_ENABLE_IF(detail::is_compiled_string
<S
>::value
)>
487 auto format_to_n(OutputIt out
, size_t n
, const S
& fmt
, Args
&&... args
)
488 -> format_to_n_result
<OutputIt
> {
489 using traits
= detail::fixed_buffer_traits
;
490 auto buf
= detail::iterator_buffer
<OutputIt
, char, traits
>(out
, n
);
491 fmt::format_to(std::back_inserter(buf
), fmt
, std::forward
<Args
>(args
)...);
492 return {buf
.out(), buf
.count()};
495 template <typename S
, typename
... Args
,
496 FMT_ENABLE_IF(detail::is_compiled_string
<S
>::value
)>
497 FMT_CONSTEXPR20
auto formatted_size(const S
& fmt
, const Args
&... args
)
499 return fmt::format_to(detail::counting_iterator(), fmt
, args
...).count();
502 template <typename S
, typename
... Args
,
503 FMT_ENABLE_IF(detail::is_compiled_string
<S
>::value
)>
504 void print(std::FILE* f
, const S
& fmt
, const Args
&... args
) {
505 memory_buffer buffer
;
506 fmt::format_to(std::back_inserter(buffer
), fmt
, args
...);
507 detail::print(f
, {buffer
.data(), buffer
.size()});
510 template <typename S
, typename
... Args
,
511 FMT_ENABLE_IF(detail::is_compiled_string
<S
>::value
)>
512 void print(const S
& fmt
, const Args
&... args
) {
513 print(stdout
, fmt
, args
...);
516 #if FMT_USE_NONTYPE_TEMPLATE_ARGS
517 inline namespace literals
{
518 template <detail_exported::fixed_string Str
> constexpr auto operator""_cf() {
519 using char_t
= remove_cvref_t
<decltype(Str
.data
[0])>;
520 return detail::udl_compiled_string
<char_t
, sizeof(Str
.data
) / sizeof(char_t
),
523 } // namespace literals
529 #endif // FMT_COMPILE_H_