1 // Formatting library for C++ - experimental format string compilation
2 //
3 // Copyright (c) 2012 - present, Victor Zverovich and fmt contributors
4 // All rights reserved.
5 //
6 // For the license information refer to format.h.
7 
8 #ifndef FMT_COMPILE_H_
9 #define FMT_COMPILE_H_
10 
11 #include <algorithm>
12 #include <vector>
13 
14 #include "format.h"
15 
16 #ifndef FMT_USE_NONTYPE_TEMPLATE_PARAMETERS
17 #  if defined(__cpp_nontype_template_parameter_class) && \
18       (!FMT_GCC_VERSION || FMT_GCC_VERSION >= 903)
19 #    define FMT_USE_NONTYPE_TEMPLATE_PARAMETERS 1
20 #  else
21 #    define FMT_USE_NONTYPE_TEMPLATE_PARAMETERS 0
22 #  endif
23 #endif
24 
25 FMT_BEGIN_NAMESPACE
26 namespace detail {
27 
28 template <typename OutputIt> class truncating_iterator_base {
29  protected:
30   OutputIt out_;
31   size_t limit_;
32   size_t count_ = 0;
33 
truncating_iterator_base()34   truncating_iterator_base() : out_(), limit_(0) {}
35 
truncating_iterator_base(OutputIt out,size_t limit)36   truncating_iterator_base(OutputIt out, size_t limit)
37       : out_(out), limit_(limit) {}
38 
39  public:
40   using iterator_category = std::output_iterator_tag;
41   using value_type = typename std::iterator_traits<OutputIt>::value_type;
42   using difference_type = std::ptrdiff_t;
43   using pointer = void;
44   using reference = void;
45   using _Unchecked_type =
46       truncating_iterator_base;  // Mark iterator as checked.
47 
base()48   OutputIt base() const { return out_; }
count()49   size_t count() const { return count_; }
50 };
51 
52 // An output iterator that truncates the output and counts the number of objects
53 // written to it.
54 template <typename OutputIt,
55           typename Enable = typename std::is_void<
56               typename std::iterator_traits<OutputIt>::value_type>::type>
57 class truncating_iterator;
58 
59 template <typename OutputIt>
60 class truncating_iterator<OutputIt, std::false_type>
61     : public truncating_iterator_base<OutputIt> {
62   mutable typename truncating_iterator_base<OutputIt>::value_type blackhole_;
63 
64  public:
65   using value_type = typename truncating_iterator_base<OutputIt>::value_type;
66 
67   truncating_iterator() = default;
68 
truncating_iterator(OutputIt out,size_t limit)69   truncating_iterator(OutputIt out, size_t limit)
70       : truncating_iterator_base<OutputIt>(out, limit) {}
71 
72   truncating_iterator& operator++() {
73     if (this->count_++ < this->limit_) ++this->out_;
74     return *this;
75   }
76 
77   truncating_iterator operator++(int) {
78     auto it = *this;
79     ++*this;
80     return it;
81   }
82 
83   value_type& operator*() const {
84     return this->count_ < this->limit_ ? *this->out_ : blackhole_;
85   }
86 };
87 
88 template <typename OutputIt>
89 class truncating_iterator<OutputIt, std::true_type>
90     : public truncating_iterator_base<OutputIt> {
91  public:
92   truncating_iterator() = default;
93 
truncating_iterator(OutputIt out,size_t limit)94   truncating_iterator(OutputIt out, size_t limit)
95       : truncating_iterator_base<OutputIt>(out, limit) {}
96 
97   template <typename T> truncating_iterator& operator=(T val) {
98     if (this->count_++ < this->limit_) *this->out_++ = val;
99     return *this;
100   }
101 
102   truncating_iterator& operator++() { return *this; }
103   truncating_iterator& operator++(int) { return *this; }
104   truncating_iterator& operator*() { return *this; }
105 };
106 
107 // A compile-time string which is compiled into fast formatting code.
108 class compiled_string {};
109 
110 template <typename S>
111 struct is_compiled_string : std::is_base_of<compiled_string, S> {};
112 
113 /**
114   \rst
115   Converts a string literal *s* into a format string that will be parsed at
116   compile time and converted into efficient formatting code. Requires C++17
117   ``constexpr if`` compiler support.
118 
119   **Example**::
120 
121     // Converts 42 into std::string using the most efficient method and no
122     // runtime format string processing.
123     std::string s = axom::fmt::format(FMT_COMPILE("{}"), 42);
124   \endrst
125  */
126 #define FMT_COMPILE(s) FMT_STRING_IMPL(s, axom::fmt::detail::compiled_string)
127 
128 #if FMT_USE_NONTYPE_TEMPLATE_PARAMETERS
129 template <typename Char, size_t N> struct fixed_string {
fixed_stringfixed_string130   constexpr fixed_string(const Char (&str)[N]) {
131     copy_str<Char, const Char*, Char*>(static_cast<const Char*>(str), str + N,
132                                        data);
133   }
134   Char data[N]{};
135 };
136 
137 template <typename Char, size_t N, fixed_string<Char, N> Str>
138 struct udl_compiled_string : compiled_string {
139   using char_type = Char;
140   constexpr operator basic_string_view<char_type>() const {
141     return {Str.data, N - 1};
142   }
143 };
144 #endif
145 
146 template <typename T, typename... Tail>
first(const T & value,const Tail &...)147 const T& first(const T& value, const Tail&...) {
148   return value;
149 }
150 
151 // Part of a compiled format string. It can be either literal text or a
152 // replacement field.
153 template <typename Char> struct format_part {
154   enum class kind { arg_index, arg_name, text, replacement };
155 
156   struct replacement {
157     arg_ref<Char> arg_id;
158     dynamic_format_specs<Char> specs;
159   };
160 
161   kind part_kind;
162   union value {
163     int arg_index;
164     basic_string_view<Char> str;
165     replacement repl;
166 
arg_index(index)167     FMT_CONSTEXPR value(int index = 0) : arg_index(index) {}
value(basic_string_view<Char> s)168     FMT_CONSTEXPR value(basic_string_view<Char> s) : str(s) {}
value(replacement r)169     FMT_CONSTEXPR value(replacement r) : repl(r) {}
170   } val;
171   // Position past the end of the argument id.
172   const Char* arg_id_end = nullptr;
173 
174   FMT_CONSTEXPR format_part(kind k = kind::arg_index, value v = {})
part_kindformat_part175       : part_kind(k), val(v) {}
176 
make_arg_indexformat_part177   static FMT_CONSTEXPR format_part make_arg_index(int index) {
178     return format_part(kind::arg_index, index);
179   }
make_arg_nameformat_part180   static FMT_CONSTEXPR format_part make_arg_name(basic_string_view<Char> name) {
181     return format_part(kind::arg_name, name);
182   }
make_textformat_part183   static FMT_CONSTEXPR format_part make_text(basic_string_view<Char> text) {
184     return format_part(kind::text, text);
185   }
make_replacementformat_part186   static FMT_CONSTEXPR format_part make_replacement(replacement repl) {
187     return format_part(kind::replacement, repl);
188   }
189 };
190 
191 template <typename Char> struct part_counter {
192   unsigned num_parts = 0;
193 
on_textpart_counter194   FMT_CONSTEXPR void on_text(const Char* begin, const Char* end) {
195     if (begin != end) ++num_parts;
196   }
197 
on_arg_idpart_counter198   FMT_CONSTEXPR int on_arg_id() { return ++num_parts, 0; }
on_arg_idpart_counter199   FMT_CONSTEXPR int on_arg_id(int) { return ++num_parts, 0; }
on_arg_idpart_counter200   FMT_CONSTEXPR int on_arg_id(basic_string_view<Char>) {
201     return ++num_parts, 0;
202   }
203 
on_replacement_fieldpart_counter204   FMT_CONSTEXPR void on_replacement_field(int, const Char*) {}
205 
on_format_specspart_counter206   FMT_CONSTEXPR const Char* on_format_specs(int, const Char* begin,
207                                             const Char* end) {
208     // Find the matching brace.
209     unsigned brace_counter = 0;
210     for (; begin != end; ++begin) {
211       if (*begin == '{') {
212         ++brace_counter;
213       } else if (*begin == '}') {
214         if (brace_counter == 0u) break;
215         --brace_counter;
216       }
217     }
218     return begin;
219   }
220 
on_errorpart_counter221   FMT_CONSTEXPR void on_error(const char*) {}
222 };
223 
224 // Counts the number of parts in a format string.
225 template <typename Char>
count_parts(basic_string_view<Char> format_str)226 FMT_CONSTEXPR unsigned count_parts(basic_string_view<Char> format_str) {
227   part_counter<Char> counter;
228   parse_format_string<true>(format_str, counter);
229   return counter.num_parts;
230 }
231 
232 template <typename Char, typename PartHandler>
233 class format_string_compiler : public error_handler {
234  private:
235   using part = format_part<Char>;
236 
237   PartHandler handler_;
238   part part_;
239   basic_string_view<Char> format_str_;
240   basic_format_parse_context<Char> parse_context_;
241 
242  public:
format_string_compiler(basic_string_view<Char> format_str,PartHandler handler)243   FMT_CONSTEXPR format_string_compiler(basic_string_view<Char> format_str,
244                                        PartHandler handler)
245       : handler_(handler),
246         format_str_(format_str),
247         parse_context_(format_str) {}
248 
on_text(const Char * begin,const Char * end)249   FMT_CONSTEXPR void on_text(const Char* begin, const Char* end) {
250     if (begin != end)
251       handler_(part::make_text({begin, to_unsigned(end - begin)}));
252   }
253 
on_arg_id()254   FMT_CONSTEXPR int on_arg_id() {
255     part_ = part::make_arg_index(parse_context_.next_arg_id());
256     return 0;
257   }
258 
on_arg_id(int id)259   FMT_CONSTEXPR int on_arg_id(int id) {
260     parse_context_.check_arg_id(id);
261     part_ = part::make_arg_index(id);
262     return 0;
263   }
264 
on_arg_id(basic_string_view<Char> id)265   FMT_CONSTEXPR int on_arg_id(basic_string_view<Char> id) {
266     part_ = part::make_arg_name(id);
267     return 0;
268   }
269 
on_replacement_field(int,const Char * ptr)270   FMT_CONSTEXPR void on_replacement_field(int, const Char* ptr) {
271     part_.arg_id_end = ptr;
272     handler_(part_);
273   }
274 
on_format_specs(int,const Char * begin,const Char * end)275   FMT_CONSTEXPR const Char* on_format_specs(int, const Char* begin,
276                                             const Char* end) {
277     auto repl = typename part::replacement();
278     dynamic_specs_handler<basic_format_parse_context<Char>> handler(
279         repl.specs, parse_context_);
280     auto it = parse_format_specs(begin, end, handler);
281     if (*it != '}') on_error("missing '}' in format string");
282     repl.arg_id = part_.part_kind == part::kind::arg_index
283                       ? arg_ref<Char>(part_.val.arg_index)
284                       : arg_ref<Char>(part_.val.str);
285     auto replacement_part = part::make_replacement(repl);
286     replacement_part.arg_id_end = begin;
287     handler_(replacement_part);
288     return it;
289   }
290 };
291 
292 // Compiles a format string and invokes handler(part) for each parsed part.
293 template <bool IS_CONSTEXPR, typename Char, typename PartHandler>
compile_format_string(basic_string_view<Char> format_str,PartHandler handler)294 FMT_CONSTEXPR void compile_format_string(basic_string_view<Char> format_str,
295                                          PartHandler handler) {
296   parse_format_string<IS_CONSTEXPR>(
297       format_str,
298       format_string_compiler<Char, PartHandler>(format_str, handler));
299 }
300 
301 template <typename OutputIt, typename Context, typename Id>
format_arg(basic_format_parse_context<typename Context::char_type> & parse_ctx,Context & ctx,Id arg_id)302 void format_arg(
303     basic_format_parse_context<typename Context::char_type>& parse_ctx,
304     Context& ctx, Id arg_id) {
305   auto arg = ctx.arg(arg_id);
306   if (arg.type() == type::custom_type) {
307     visit_format_arg(custom_formatter<Context>(parse_ctx, ctx), arg);
308   } else {
309     ctx.advance_to(visit_format_arg(
310         default_arg_formatter<OutputIt, typename Context::char_type>{
311             ctx.out(), ctx.args(), ctx.locale()},
312         arg));
313   }
314 }
315 
316 // vformat_to is defined in a subnamespace to prevent ADL.
317 namespace cf {
318 template <typename Context, typename OutputIt, typename CompiledFormat>
319 auto vformat_to(OutputIt out, CompiledFormat& cf,
320                 basic_format_args<Context> args) -> typename Context::iterator {
321   using char_type = typename Context::char_type;
322   basic_format_parse_context<char_type> parse_ctx(
323       to_string_view(cf.format_str_));
324   Context ctx(out, args);
325 
326   const auto& parts = cf.parts();
327   for (auto part_it = std::begin(parts); part_it != std::end(parts);
328        ++part_it) {
329     const auto& part = *part_it;
330     const auto& value = part.val;
331 
332     using format_part_t = format_part<char_type>;
333     switch (part.part_kind) {
334     case format_part_t::kind::text: {
335       const auto text = value.str;
336       auto output = ctx.out();
337       auto&& it = reserve(output, text.size());
338       it = std::copy_n(text.begin(), text.size(), it);
339       ctx.advance_to(output);
340       break;
341     }
342 
343     case format_part_t::kind::arg_index:
344       advance_to(parse_ctx, part.arg_id_end);
345       detail::format_arg<OutputIt>(parse_ctx, ctx, value.arg_index);
346       break;
347 
348     case format_part_t::kind::arg_name:
349       advance_to(parse_ctx, part.arg_id_end);
350       detail::format_arg<OutputIt>(parse_ctx, ctx, value.str);
351       break;
352 
353     case format_part_t::kind::replacement: {
354       const auto& arg_id_value = value.repl.arg_id.val;
355       const auto arg = value.repl.arg_id.kind == arg_id_kind::index
356                            ? ctx.arg(arg_id_value.index)
357                            : ctx.arg(arg_id_value.name);
358 
359       auto specs = value.repl.specs;
360 
361       handle_dynamic_spec<width_checker>(specs.width, specs.width_ref, ctx);
362       handle_dynamic_spec<precision_checker>(specs.precision,
363                                              specs.precision_ref, ctx);
364 
365       error_handler h;
366       numeric_specs_checker<error_handler> checker(h, arg.type());
367       if (specs.align == align::numeric) checker.require_numeric_argument();
368       if (specs.sign != sign::none) checker.check_sign();
369       if (specs.alt) checker.require_numeric_argument();
370       if (specs.precision >= 0) checker.check_precision();
371 
372       advance_to(parse_ctx, part.arg_id_end);
373       ctx.advance_to(visit_format_arg(
374           arg_formatter<OutputIt, typename Context::char_type>(ctx, specs),
375           arg));
376       break;
377     }
378     }
379   }
380   return ctx.out();
381 }
382 }  // namespace cf
383 
384 struct basic_compiled_format {};
385 
386 template <typename S, typename = void>
387 struct compiled_format_base : basic_compiled_format {
388   using char_type = char_t<S>;
389   using parts_container = std::vector<detail::format_part<char_type>>;
390 
391   parts_container compiled_parts;
392 
compiled_format_basecompiled_format_base393   explicit compiled_format_base(basic_string_view<char_type> format_str) {
394     compile_format_string<false>(format_str,
395                                  [this](const format_part<char_type>& part) {
396                                    compiled_parts.push_back(part);
397                                  });
398   }
399 
partscompiled_format_base400   const parts_container& parts() const { return compiled_parts; }
401 };
402 
403 template <typename Char, unsigned N> struct format_part_array {
404   format_part<Char> data[N] = {};
405   FMT_CONSTEXPR format_part_array() = default;
406 };
407 
408 template <typename Char, unsigned N>
compile_to_parts(basic_string_view<Char> format_str)409 FMT_CONSTEXPR format_part_array<Char, N> compile_to_parts(
410     basic_string_view<Char> format_str) {
411   format_part_array<Char, N> parts;
412   unsigned counter = 0;
413   // This is not a lambda for compatibility with older compilers.
414   struct {
415     format_part<Char>* parts;
416     unsigned* counter;
417     FMT_CONSTEXPR void operator()(const format_part<Char>& part) {
418       parts[(*counter)++] = part;
419     }
420   } collector{parts.data, &counter};
421   compile_format_string<true>(format_str, collector);
422   if (counter < N) {
423     parts.data[counter] =
424         format_part<Char>::make_text(basic_string_view<Char>());
425   }
426   return parts;
427 }
428 
constexpr_max(const T & a,const T & b)429 template <typename T> constexpr const T& constexpr_max(const T& a, const T& b) {
430   return (a < b) ? b : a;
431 }
432 
433 template <typename S>
434 struct compiled_format_base<S, enable_if_t<is_compile_string<S>::value>>
435     : basic_compiled_format {
436   using char_type = char_t<S>;
437 
438   FMT_CONSTEXPR explicit compiled_format_base(basic_string_view<char_type>) {}
439 
440 // Workaround for old compilers. Format string compilation will not be
441 // performed there anyway.
442 #if FMT_USE_CONSTEXPR
443   static FMT_CONSTEXPR_DECL const unsigned num_format_parts =
444       constexpr_max(count_parts(to_string_view(S())), 1u);
445 #else
446   static const unsigned num_format_parts = 1;
447 #endif
448 
449   using parts_container = format_part<char_type>[num_format_parts];
450 
451   const parts_container& parts() const {
452     static FMT_CONSTEXPR_DECL const auto compiled_parts =
453         compile_to_parts<char_type, num_format_parts>(
454             detail::to_string_view(S()));
455     return compiled_parts.data;
456   }
457 };
458 
459 template <typename S, typename... Args>
460 class compiled_format : private compiled_format_base<S> {
461  public:
462   using typename compiled_format_base<S>::char_type;
463 
464  private:
465   basic_string_view<char_type> format_str_;
466 
467   template <typename Context, typename OutputIt, typename CompiledFormat>
468   friend auto cf::vformat_to(OutputIt out, CompiledFormat& cf,
469                              basic_format_args<Context> args) ->
470       typename Context::iterator;
471 
472  public:
473   compiled_format() = delete;
474   explicit constexpr compiled_format(basic_string_view<char_type> format_str)
475       : compiled_format_base<S>(format_str), format_str_(format_str) {}
476 };
477 
478 // start axom change
479 #if defined(__cpp_if_constexpr) && defined(__cpp_return_type_deduction)
480 // end axom change
481 template <typename... Args> struct type_list {};
482 
483 // Returns a reference to the argument at index N from [first, rest...].
484 template <int N, typename T, typename... Args>
485 constexpr const auto& get([[maybe_unused]] const T& first,
486                           [[maybe_unused]] const Args&... rest) {
487   static_assert(N < 1 + sizeof...(Args), "index is out of bounds");
488   if constexpr (N == 0)
489     return first;
490   else
491     return get<N - 1>(rest...);
492 }
493 
494 template <int N, typename> struct get_type_impl;
495 
496 template <int N, typename... Args> struct get_type_impl<N, type_list<Args...>> {
497   using type = remove_cvref_t<decltype(get<N>(std::declval<Args>()...))>;
498 };
499 
500 template <int N, typename T>
501 using get_type = typename get_type_impl<N, T>::type;
502 
503 template <typename T> struct is_compiled_format : std::false_type {};
504 
505 template <typename Char> struct text {
506   basic_string_view<Char> data;
507   using char_type = Char;
508 
509   template <typename OutputIt, typename... Args>
510   constexpr OutputIt format(OutputIt out, const Args&...) const {
511     return write<Char>(out, data);
512   }
513 };
514 
515 template <typename Char>
516 struct is_compiled_format<text<Char>> : std::true_type {};
517 
518 template <typename Char>
519 constexpr text<Char> make_text(basic_string_view<Char> s, size_t pos,
520                                size_t size) {
521   return {{&s[pos], size}};
522 }
523 
524 template <typename Char> struct code_unit {
525   Char value;
526   using char_type = Char;
527 
528   template <typename OutputIt, typename... Args>
529   constexpr OutputIt format(OutputIt out, const Args&...) const {
530     return write<Char>(out, value);
531   }
532 };
533 
534 template <typename Char>
535 struct is_compiled_format<code_unit<Char>> : std::true_type {};
536 
537 // A replacement field that refers to argument N.
538 template <typename Char, typename T, int N> struct field {
539   using char_type = Char;
540 
541   template <typename OutputIt, typename... Args>
542   constexpr OutputIt format(OutputIt out, const Args&... args) const {
543     if constexpr (is_named_arg<typename std::remove_cv<T>::type>::value) {
544       const auto& arg = get<N>(args...).value;
545       return write<Char>(out, arg);
546     } else {
547       // This ensures that the argument type is convertile to `const T&`.
548       const T& arg = get<N>(args...);
549       return write<Char>(out, arg);
550     }
551   }
552 };
553 
554 template <typename Char, typename T, int N>
555 struct is_compiled_format<field<Char, T, N>> : std::true_type {};
556 
557 // A replacement field that refers to argument with name.
558 template <typename Char> struct runtime_named_field {
559   using char_type = Char;
560   basic_string_view<Char> name;
561 
562   template <typename OutputIt, typename T>
563   constexpr static bool try_format_argument(
564       OutputIt& out,
565       // [[maybe_unused]] due to unused-but-set-parameter warning in GCC 7,8,9
566       [[maybe_unused]] basic_string_view<Char> arg_name, const T& arg) {
567     if constexpr (is_named_arg<typename std::remove_cv<T>::type>::value) {
568       if (arg_name == arg.name) {
569         out = write<Char>(out, arg.value);
570         return true;
571       }
572     }
573     return false;
574   }
575 
576   template <typename OutputIt, typename... Args>
577   constexpr OutputIt format(OutputIt out, const Args&... args) const {
578     bool found = (try_format_argument(out, name, args) || ...);
579     if (!found) {
580       throw format_error("argument with specified name is not found");
581     }
582     return out;
583   }
584 };
585 
586 template <typename Char>
587 struct is_compiled_format<runtime_named_field<Char>> : std::true_type {};
588 
589 // A replacement field that refers to argument N and has format specifiers.
590 template <typename Char, typename T, int N> struct spec_field {
591   using char_type = Char;
592   formatter<T, Char> fmt;
593 
594   template <typename OutputIt, typename... Args>
595   constexpr OutputIt format(OutputIt out, const Args&... args) const {
596     // This ensures that the argument type is convertile to `const T&`.
597     const T& arg = get<N>(args...);
598     const auto& vargs =
599         make_format_args<basic_format_context<OutputIt, Char>>(args...);
600     basic_format_context<OutputIt, Char> ctx(out, vargs);
601     return fmt.format(arg, ctx);
602   }
603 };
604 
605 template <typename Char, typename T, int N>
606 struct is_compiled_format<spec_field<Char, T, N>> : std::true_type {};
607 
608 template <typename L, typename R> struct concat {
609   L lhs;
610   R rhs;
611   using char_type = typename L::char_type;
612 
613   template <typename OutputIt, typename... Args>
614   constexpr OutputIt format(OutputIt out, const Args&... args) const {
615     out = lhs.format(out, args...);
616     return rhs.format(out, args...);
617   }
618 };
619 
620 template <typename L, typename R>
621 struct is_compiled_format<concat<L, R>> : std::true_type {};
622 
623 template <typename L, typename R>
624 constexpr concat<L, R> make_concat(L lhs, R rhs) {
625   return {lhs, rhs};
626 }
627 
628 struct unknown_format {};
629 
630 template <typename Char>
631 constexpr size_t parse_text(basic_string_view<Char> str, size_t pos) {
632   for (size_t size = str.size(); pos != size; ++pos) {
633     if (str[pos] == '{' || str[pos] == '}') break;
634   }
635   return pos;
636 }
637 
638 template <typename Args, size_t POS, int ID, typename S>
639 constexpr auto compile_format_string(S format_str);
640 
641 template <typename Args, size_t POS, int ID, typename T, typename S>
642 constexpr auto parse_tail(T head, S format_str) {
643   if constexpr (POS !=
644                 basic_string_view<typename S::char_type>(format_str).size()) {
645     constexpr auto tail = compile_format_string<Args, POS, ID>(format_str);
646     if constexpr (std::is_same<remove_cvref_t<decltype(tail)>,
647                                unknown_format>())
648       return tail;
649     else
650       return make_concat(head, tail);
651   } else {
652     return head;
653   }
654 }
655 
656 template <typename T, typename Char> struct parse_specs_result {
657   formatter<T, Char> fmt;
658   size_t end;
659   int next_arg_id;
660 };
661 
662 constexpr int manual_indexing_id = -1;
663 
664 template <typename T, typename Char>
665 constexpr parse_specs_result<T, Char> parse_specs(basic_string_view<Char> str,
666                                                   size_t pos, int next_arg_id) {
667   str.remove_prefix(pos);
668   auto ctx = basic_format_parse_context<Char>(str, {}, next_arg_id);
669   auto f = formatter<T, Char>();
670   auto end = f.parse(ctx);
671   return {f, pos + axom::fmt::detail::to_unsigned(end - str.data()) + 1,
672           next_arg_id == 0 ? manual_indexing_id : ctx.next_arg_id()};
673 }
674 
675 template <typename Char> struct arg_id_handler {
676   constexpr void on_error(const char* message) { throw format_error(message); }
677 
678   constexpr int on_arg_id() {
679     FMT_ASSERT(false, "handler cannot be used with automatic indexing");
680     return 0;
681   }
682 
683   constexpr int on_arg_id(int id) {
684     arg_id = arg_ref<Char>(id);
685     return 0;
686   }
687 
688   constexpr int on_arg_id(basic_string_view<Char> id) {
689     arg_id = arg_ref<Char>(id);
690     return 0;
691   }
692 
693   arg_ref<Char> arg_id;
694 };
695 
696 template <typename Char> struct parse_arg_id_result {
697   arg_ref<Char> arg_id;
698   const Char* arg_id_end;
699 };
700 
701 template <int ID, typename Char>
702 constexpr auto parse_arg_id(const Char* begin, const Char* end) {
703   auto handler = arg_id_handler<Char>{arg_ref<Char>{}};
704   auto adapter = id_adapter<arg_id_handler<Char>, Char>{handler, 0};
705   auto arg_id_end = parse_arg_id(begin, end, adapter);
706   return parse_arg_id_result<Char>{handler.arg_id, arg_id_end};
707 }
708 
709 // Compiles a non-empty format string and returns the compiled representation
710 // or unknown_format() on unrecognized input.
711 template <typename Args, size_t POS, int ID, typename S>
712 constexpr auto compile_format_string(S format_str) {
713   using char_type = typename S::char_type;
714   constexpr basic_string_view<char_type> str = format_str;
715   if constexpr (str[POS] == '{') {
716     if constexpr (POS + 1 == str.size())
717       throw format_error("unmatched '{' in format string");
718     if constexpr (str[POS + 1] == '{') {
719       return parse_tail<Args, POS + 2, ID>(make_text(str, POS, 1), format_str);
720     } else if constexpr (str[POS + 1] == '}' || str[POS + 1] == ':') {
721       static_assert(ID != manual_indexing_id,
722                     "cannot switch from manual to automatic argument indexing");
723       using id_type = get_type<ID, Args>;
724       if constexpr (str[POS + 1] == '}') {
725         constexpr auto next_id =
726             ID != manual_indexing_id ? ID + 1 : manual_indexing_id;
727         return parse_tail<Args, POS + 2, next_id>(
728             field<char_type, id_type, ID>(), format_str);
729       } else {
730         constexpr auto result = parse_specs<id_type>(str, POS + 2, ID + 1);
731         return parse_tail<Args, result.end, result.next_arg_id>(
732             spec_field<char_type, id_type, ID>{result.fmt}, format_str);
733       }
734     } else {
735       constexpr auto arg_id_result =
736           parse_arg_id<ID>(str.data() + POS + 1, str.data() + str.size());
737       constexpr auto arg_id_end_pos = arg_id_result.arg_id_end - str.data();
738       constexpr char_type c =
739           arg_id_end_pos != str.size() ? str[arg_id_end_pos] : char_type();
740       static_assert(c == '}' || c == ':', "missing '}' in format string");
741       if constexpr (arg_id_result.arg_id.kind == arg_id_kind::index) {
742         static_assert(
743             ID == manual_indexing_id || ID == 0,
744             "cannot switch from automatic to manual argument indexing");
745         constexpr auto arg_index = arg_id_result.arg_id.val.index;
746         using id_type = get_type<arg_index, Args>;
747         if constexpr (c == '}') {
748           return parse_tail<Args, arg_id_end_pos + 1, manual_indexing_id>(
749               field<char_type, id_type, arg_index>(), format_str);
750         } else if constexpr (c == ':') {
751           constexpr auto result =
752               parse_specs<id_type>(str, arg_id_end_pos + 1, 0);
753           return parse_tail<Args, result.end, result.next_arg_id>(
754               spec_field<char_type, id_type, arg_index>{result.fmt},
755               format_str);
756         }
757       } else if constexpr (arg_id_result.arg_id.kind == arg_id_kind::name) {
758         if constexpr (c == '}') {
759           return parse_tail<Args, arg_id_end_pos + 1, ID>(
760               runtime_named_field<char_type>{arg_id_result.arg_id.val.name},
761               format_str);
762         } else if constexpr (c == ':') {
763           return unknown_format();  // no type info for specs parsing
764         }
765       }
766     }
767   } else if constexpr (str[POS] == '}') {
768     if constexpr (POS + 1 == str.size())
769       throw format_error("unmatched '}' in format string");
770     return parse_tail<Args, POS + 2, ID>(make_text(str, POS, 1), format_str);
771   } else {
772     constexpr auto end = parse_text(str, POS + 1);
773     if constexpr (end - POS > 1) {
774       return parse_tail<Args, end, ID>(make_text(str, POS, end - POS),
775                                        format_str);
776     } else {
777       return parse_tail<Args, end, ID>(code_unit<char_type>{str[POS]},
778                                        format_str);
779     }
780   }
781 }
782 
783 template <typename... Args, typename S,
784           FMT_ENABLE_IF(is_compile_string<S>::value ||
785                         detail::is_compiled_string<S>::value)>
786 constexpr auto compile(S format_str) {
787   constexpr basic_string_view<typename S::char_type> str = format_str;
788   if constexpr (str.size() == 0) {
789     return detail::make_text(str, 0, 0);
790   } else {
791     constexpr auto result =
792         detail::compile_format_string<detail::type_list<Args...>, 0, 0>(
793             format_str);
794     return result;
795   }
796 }
797 #else
798 template <typename... Args, typename S,
799           FMT_ENABLE_IF(is_compile_string<S>::value)>
800 constexpr auto compile(S format_str) -> detail::compiled_format<S, Args...> {
801   return detail::compiled_format<S, Args...>(to_string_view(format_str));
802 }
803 // start axom change
804 #endif  // defined(__cpp_if_constexpr) && defined(__cpp_return_type_deduction)
805 // end axom change
806 
807 // Compiles the format string which must be a string literal.
808 template <typename... Args, typename Char, size_t N>
809 auto compile(const Char (&format_str)[N])
810     -> detail::compiled_format<const Char*, Args...> {
811   return detail::compiled_format<const Char*, Args...>(
812       basic_string_view<Char>(format_str, N - 1));
813 }
814 }  // namespace detail
815 
816 // DEPRECATED! use FMT_COMPILE instead.
817 template <typename... Args>
818 FMT_DEPRECATED auto compile(const Args&... args)
819     -> decltype(detail::compile(args...)) {
820   return detail::compile(args...);
821 }
822 
823 #if FMT_USE_CONSTEXPR
824 #  ifdef __cpp_if_constexpr
825 
826 template <typename CompiledFormat, typename... Args,
827           typename Char = typename CompiledFormat::char_type,
828           FMT_ENABLE_IF(detail::is_compiled_format<CompiledFormat>::value)>
829 FMT_INLINE std::basic_string<Char> format(const CompiledFormat& cf,
830                                           const Args&... args) {
831   basic_memory_buffer<Char> buffer;
832   cf.format(detail::buffer_appender<Char>(buffer), args...);
833   return to_string(buffer);
834 }
835 
836 template <typename OutputIt, typename CompiledFormat, typename... Args,
837           FMT_ENABLE_IF(detail::is_compiled_format<CompiledFormat>::value)>
838 constexpr OutputIt format_to(OutputIt out, const CompiledFormat& cf,
839                              const Args&... args) {
840   return cf.format(out, args...);
841 }
842 #  endif  // __cpp_if_constexpr
843 #endif    // FMT_USE_CONSTEXPR
844 
845 template <typename CompiledFormat, typename... Args,
846           typename Char = typename CompiledFormat::char_type,
847           FMT_ENABLE_IF(std::is_base_of<detail::basic_compiled_format,
848                                         CompiledFormat>::value)>
849 std::basic_string<Char> format(const CompiledFormat& cf, const Args&... args) {
850   basic_memory_buffer<Char> buffer;
851   using context = buffer_context<Char>;
852   detail::cf::vformat_to<context>(detail::buffer_appender<Char>(buffer), cf,
853                                   make_format_args<context>(args...));
854   return to_string(buffer);
855 }
856 
857 template <typename S, typename... Args,
858           FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
859 FMT_INLINE std::basic_string<typename S::char_type> format(const S&,
860                                                            Args&&... args) {
861 #ifdef __cpp_if_constexpr
862   if constexpr (std::is_same<typename S::char_type, char>::value) {
863     constexpr basic_string_view<typename S::char_type> str = S();
864     if constexpr (str.size() == 2 && str[0] == '{' && str[1] == '}') {
865       const auto& first = detail::first(args...);
866       if constexpr (detail::is_named_arg<
867                         remove_cvref_t<decltype(first)>>::value) {
868         return axom::fmt::to_string(first.value);
869       } else {
870         return axom::fmt::to_string(first);
871       }
872     }
873   }
874 #endif
875   constexpr auto compiled = detail::compile<Args...>(S());
876 #ifdef __cpp_if_constexpr
877   if constexpr (std::is_same<remove_cvref_t<decltype(compiled)>,
878                              detail::unknown_format>()) {
879     return format(static_cast<basic_string_view<typename S::char_type>>(S()),
880                   std::forward<Args>(args)...);
881   } else {
882     return format(compiled, std::forward<Args>(args)...);
883   }
884 #else
885   return format(compiled, std::forward<Args>(args)...);
886 #endif
887 }
888 
889 template <typename OutputIt, typename CompiledFormat, typename... Args,
890           FMT_ENABLE_IF(std::is_base_of<detail::basic_compiled_format,
891                                         CompiledFormat>::value)>
892 constexpr OutputIt format_to(OutputIt out, const CompiledFormat& cf,
893                              const Args&... args) {
894   using char_type = typename CompiledFormat::char_type;
895   using context = format_context_t<OutputIt, char_type>;
896   return detail::cf::vformat_to<context>(out, cf,
897                                          make_format_args<context>(args...));
898 }
899 
900 template <typename OutputIt, typename S, typename... Args,
901           FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
902 FMT_CONSTEXPR OutputIt format_to(OutputIt out, const S&, Args&&... args) {
903   constexpr auto compiled = detail::compile<Args...>(S());
904 #ifdef __cpp_if_constexpr
905   if constexpr (std::is_same<remove_cvref_t<decltype(compiled)>,
906                              detail::unknown_format>()) {
907     return format_to(out,
908                      static_cast<basic_string_view<typename S::char_type>>(S()),
909                      std::forward<Args>(args)...);
910   } else {
911     return format_to(out, compiled, std::forward<Args>(args)...);
912   }
913 #else
914   return format_to(out, compiled, std::forward<Args>(args)...);
915 #endif
916 }
917 
918 template <typename OutputIt, typename CompiledFormat, typename... Args>
919 auto format_to_n(OutputIt out, size_t n, const CompiledFormat& cf,
920                  const Args&... args) ->
921     typename std::enable_if<
922         detail::is_output_iterator<OutputIt,
923                                    typename CompiledFormat::char_type>::value &&
924             std::is_base_of<detail::basic_compiled_format,
925                             CompiledFormat>::value,
926         format_to_n_result<OutputIt>>::type {
927   auto it =
928       format_to(detail::truncating_iterator<OutputIt>(out, n), cf, args...);
929   return {it.base(), it.count()};
930 }
931 
932 template <typename OutputIt, typename S, typename... Args,
933           FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
934 format_to_n_result<OutputIt> format_to_n(OutputIt out, size_t n, const S&,
935                                          Args&&... args) {
936   auto it = format_to(detail::truncating_iterator<OutputIt>(out, n), S(),
937                       std::forward<Args>(args)...);
938   return {it.base(), it.count()};
939 }
940 
941 template <typename CompiledFormat, typename... Args,
942           FMT_ENABLE_IF(std::is_base_of<detail::basic_compiled_format,
943                                         CompiledFormat>::value ||
944                         detail::is_compiled_string<CompiledFormat>::value)>
945 size_t formatted_size(const CompiledFormat& cf, const Args&... args) {
946   return format_to(detail::counting_iterator(), cf, args...).count();
947 }
948 
949 #if FMT_USE_NONTYPE_TEMPLATE_PARAMETERS
950 inline namespace literals {
951 template <detail::fixed_string Str>
952 constexpr detail::udl_compiled_string<remove_cvref_t<decltype(Str.data[0])>,
953                                       sizeof(Str.data), Str>
954 operator""_cf() {
955   return {};
956 }
957 }  // namespace literals
958 #endif
959 
960 FMT_END_NAMESPACE
961 
962 #endif  // FMT_COMPILE_H_
963