Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-01-18 09:57:20

0001 // Formatting library for C++ - experimental format string compilation
0002 //
0003 // Copyright (c) 2012 - present, Victor Zverovich and fmt contributors
0004 // All rights reserved.
0005 //
0006 // For the license information refer to format.h.
0007 
0008 #ifndef FMT_COMPILE_H_
0009 #define FMT_COMPILE_H_
0010 
0011 #include "format.h"
0012 
0013 FMT_BEGIN_NAMESPACE
0014 namespace detail {
0015 
0016 template <typename Char, typename InputIt>
0017 FMT_CONSTEXPR inline counting_iterator copy_str(InputIt begin, InputIt end,
0018                                                 counting_iterator it) {
0019   return it + (end - begin);
0020 }
0021 
0022 template <typename OutputIt> class truncating_iterator_base {
0023  protected:
0024   OutputIt out_;
0025   size_t limit_;
0026   size_t count_ = 0;
0027 
0028   truncating_iterator_base() : out_(), limit_(0) {}
0029 
0030   truncating_iterator_base(OutputIt out, size_t limit)
0031       : out_(out), limit_(limit) {}
0032 
0033  public:
0034   using iterator_category = std::output_iterator_tag;
0035   using value_type = typename std::iterator_traits<OutputIt>::value_type;
0036   using difference_type = std::ptrdiff_t;
0037   using pointer = void;
0038   using reference = void;
0039   FMT_UNCHECKED_ITERATOR(truncating_iterator_base);
0040 
0041   OutputIt base() const { return out_; }
0042   size_t count() const { return count_; }
0043 };
0044 
0045 // An output iterator that truncates the output and counts the number of objects
0046 // written to it.
0047 template <typename OutputIt,
0048           typename Enable = typename std::is_void<
0049               typename std::iterator_traits<OutputIt>::value_type>::type>
0050 class truncating_iterator;
0051 
0052 template <typename OutputIt>
0053 class truncating_iterator<OutputIt, std::false_type>
0054     : public truncating_iterator_base<OutputIt> {
0055   mutable typename truncating_iterator_base<OutputIt>::value_type blackhole_;
0056 
0057  public:
0058   using value_type = typename truncating_iterator_base<OutputIt>::value_type;
0059 
0060   truncating_iterator() = default;
0061 
0062   truncating_iterator(OutputIt out, size_t limit)
0063       : truncating_iterator_base<OutputIt>(out, limit) {}
0064 
0065   truncating_iterator& operator++() {
0066     if (this->count_++ < this->limit_) ++this->out_;
0067     return *this;
0068   }
0069 
0070   truncating_iterator operator++(int) {
0071     auto it = *this;
0072     ++*this;
0073     return it;
0074   }
0075 
0076   value_type& operator*() const {
0077     return this->count_ < this->limit_ ? *this->out_ : blackhole_;
0078   }
0079 };
0080 
0081 template <typename OutputIt>
0082 class truncating_iterator<OutputIt, std::true_type>
0083     : public truncating_iterator_base<OutputIt> {
0084  public:
0085   truncating_iterator() = default;
0086 
0087   truncating_iterator(OutputIt out, size_t limit)
0088       : truncating_iterator_base<OutputIt>(out, limit) {}
0089 
0090   template <typename T> truncating_iterator& operator=(T val) {
0091     if (this->count_++ < this->limit_) *this->out_++ = val;
0092     return *this;
0093   }
0094 
0095   truncating_iterator& operator++() { return *this; }
0096   truncating_iterator& operator++(int) { return *this; }
0097   truncating_iterator& operator*() { return *this; }
0098 };
0099 
0100 // A compile-time string which is compiled into fast formatting code.
0101 class compiled_string {};
0102 
0103 template <typename S>
0104 struct is_compiled_string : std::is_base_of<compiled_string, S> {};
0105 
0106 /**
0107   \rst
0108   Converts a string literal *s* into a format string that will be parsed at
0109   compile time and converted into efficient formatting code. Requires C++17
0110   ``constexpr if`` compiler support.
0111 
0112   **Example**::
0113 
0114     // Converts 42 into std::string using the most efficient method and no
0115     // runtime format string processing.
0116     std::string s = fmt::format(FMT_COMPILE("{}"), 42);
0117   \endrst
0118  */
0119 #if defined(__cpp_if_constexpr) && defined(__cpp_return_type_deduction)
0120 #  define FMT_COMPILE(s) \
0121     FMT_STRING_IMPL(s, fmt::detail::compiled_string, explicit)
0122 #else
0123 #  define FMT_COMPILE(s) FMT_STRING(s)
0124 #endif
0125 
0126 #if FMT_USE_NONTYPE_TEMPLATE_ARGS
0127 template <typename Char, size_t N,
0128           fmt::detail_exported::fixed_string<Char, N> Str>
0129 struct udl_compiled_string : compiled_string {
0130   using char_type = Char;
0131   explicit constexpr operator basic_string_view<char_type>() const {
0132     return {Str.data, N - 1};
0133   }
0134 };
0135 #endif
0136 
0137 template <typename T, typename... Tail>
0138 const T& first(const T& value, const Tail&...) {
0139   return value;
0140 }
0141 
0142 #if defined(__cpp_if_constexpr) && defined(__cpp_return_type_deduction)
0143 template <typename... Args> struct type_list {};
0144 
0145 // Returns a reference to the argument at index N from [first, rest...].
0146 template <int N, typename T, typename... Args>
0147 constexpr const auto& get([[maybe_unused]] const T& first,
0148                           [[maybe_unused]] const Args&... rest) {
0149   static_assert(N < 1 + sizeof...(Args), "index is out of bounds");
0150   if constexpr (N == 0)
0151     return first;
0152   else
0153     return detail::get<N - 1>(rest...);
0154 }
0155 
0156 template <typename Char, typename... Args>
0157 constexpr int get_arg_index_by_name(basic_string_view<Char> name,
0158                                     type_list<Args...>) {
0159   return get_arg_index_by_name<Args...>(name);
0160 }
0161 
0162 template <int N, typename> struct get_type_impl;
0163 
0164 template <int N, typename... Args> struct get_type_impl<N, type_list<Args...>> {
0165   using type =
0166       remove_cvref_t<decltype(detail::get<N>(std::declval<Args>()...))>;
0167 };
0168 
0169 template <int N, typename T>
0170 using get_type = typename get_type_impl<N, T>::type;
0171 
0172 template <typename T> struct is_compiled_format : std::false_type {};
0173 
0174 template <typename Char> struct text {
0175   basic_string_view<Char> data;
0176   using char_type = Char;
0177 
0178   template <typename OutputIt, typename... Args>
0179   constexpr OutputIt format(OutputIt out, const Args&...) const {
0180     return write<Char>(out, data);
0181   }
0182 };
0183 
0184 template <typename Char>
0185 struct is_compiled_format<text<Char>> : std::true_type {};
0186 
0187 template <typename Char>
0188 constexpr text<Char> make_text(basic_string_view<Char> s, size_t pos,
0189                                size_t size) {
0190   return {{&s[pos], size}};
0191 }
0192 
0193 template <typename Char> struct code_unit {
0194   Char value;
0195   using char_type = Char;
0196 
0197   template <typename OutputIt, typename... Args>
0198   constexpr OutputIt format(OutputIt out, const Args&...) const {
0199     return write<Char>(out, value);
0200   }
0201 };
0202 
0203 // This ensures that the argument type is convertible to `const T&`.
0204 template <typename T, int N, typename... Args>
0205 constexpr const T& get_arg_checked(const Args&... args) {
0206   const auto& arg = detail::get<N>(args...);
0207   if constexpr (detail::is_named_arg<remove_cvref_t<decltype(arg)>>()) {
0208     return arg.value;
0209   } else {
0210     return arg;
0211   }
0212 }
0213 
0214 template <typename Char>
0215 struct is_compiled_format<code_unit<Char>> : std::true_type {};
0216 
0217 // A replacement field that refers to argument N.
0218 template <typename Char, typename T, int N> struct field {
0219   using char_type = Char;
0220 
0221   template <typename OutputIt, typename... Args>
0222   constexpr OutputIt format(OutputIt out, const Args&... args) const {
0223     return write<Char>(out, get_arg_checked<T, N>(args...));
0224   }
0225 };
0226 
0227 template <typename Char, typename T, int N>
0228 struct is_compiled_format<field<Char, T, N>> : std::true_type {};
0229 
0230 // A replacement field that refers to argument with name.
0231 template <typename Char> struct runtime_named_field {
0232   using char_type = Char;
0233   basic_string_view<Char> name;
0234 
0235   template <typename OutputIt, typename T>
0236   constexpr static bool try_format_argument(
0237       OutputIt& out,
0238       // [[maybe_unused]] due to unused-but-set-parameter warning in GCC 7,8,9
0239       [[maybe_unused]] basic_string_view<Char> arg_name, const T& arg) {
0240     if constexpr (is_named_arg<typename std::remove_cv<T>::type>::value) {
0241       if (arg_name == arg.name) {
0242         out = write<Char>(out, arg.value);
0243         return true;
0244       }
0245     }
0246     return false;
0247   }
0248 
0249   template <typename OutputIt, typename... Args>
0250   constexpr OutputIt format(OutputIt out, const Args&... args) const {
0251     bool found = (try_format_argument(out, name, args) || ...);
0252     if (!found) {
0253       FMT_THROW(format_error("argument with specified name is not found"));
0254     }
0255     return out;
0256   }
0257 };
0258 
0259 template <typename Char>
0260 struct is_compiled_format<runtime_named_field<Char>> : std::true_type {};
0261 
0262 // A replacement field that refers to argument N and has format specifiers.
0263 template <typename Char, typename T, int N> struct spec_field {
0264   using char_type = Char;
0265   formatter<T, Char> fmt;
0266 
0267   template <typename OutputIt, typename... Args>
0268   constexpr FMT_INLINE OutputIt format(OutputIt out,
0269                                        const Args&... args) const {
0270     const auto& vargs =
0271         fmt::make_format_args<basic_format_context<OutputIt, Char>>(args...);
0272     basic_format_context<OutputIt, Char> ctx(out, vargs);
0273     return fmt.format(get_arg_checked<T, N>(args...), ctx);
0274   }
0275 };
0276 
0277 template <typename Char, typename T, int N>
0278 struct is_compiled_format<spec_field<Char, T, N>> : std::true_type {};
0279 
0280 template <typename L, typename R> struct concat {
0281   L lhs;
0282   R rhs;
0283   using char_type = typename L::char_type;
0284 
0285   template <typename OutputIt, typename... Args>
0286   constexpr OutputIt format(OutputIt out, const Args&... args) const {
0287     out = lhs.format(out, args...);
0288     return rhs.format(out, args...);
0289   }
0290 };
0291 
0292 template <typename L, typename R>
0293 struct is_compiled_format<concat<L, R>> : std::true_type {};
0294 
0295 template <typename L, typename R>
0296 constexpr concat<L, R> make_concat(L lhs, R rhs) {
0297   return {lhs, rhs};
0298 }
0299 
0300 struct unknown_format {};
0301 
0302 template <typename Char>
0303 constexpr size_t parse_text(basic_string_view<Char> str, size_t pos) {
0304   for (size_t size = str.size(); pos != size; ++pos) {
0305     if (str[pos] == '{' || str[pos] == '}') break;
0306   }
0307   return pos;
0308 }
0309 
0310 template <typename Args, size_t POS, int ID, typename S>
0311 constexpr auto compile_format_string(S format_str);
0312 
0313 template <typename Args, size_t POS, int ID, typename T, typename S>
0314 constexpr auto parse_tail(T head, S format_str) {
0315   if constexpr (POS !=
0316                 basic_string_view<typename S::char_type>(format_str).size()) {
0317     constexpr auto tail = compile_format_string<Args, POS, ID>(format_str);
0318     if constexpr (std::is_same<remove_cvref_t<decltype(tail)>,
0319                                unknown_format>())
0320       return tail;
0321     else
0322       return make_concat(head, tail);
0323   } else {
0324     return head;
0325   }
0326 }
0327 
0328 template <typename T, typename Char> struct parse_specs_result {
0329   formatter<T, Char> fmt;
0330   size_t end;
0331   int next_arg_id;
0332 };
0333 
0334 constexpr int manual_indexing_id = -1;
0335 
0336 template <typename T, typename Char>
0337 constexpr parse_specs_result<T, Char> parse_specs(basic_string_view<Char> str,
0338                                                   size_t pos, int next_arg_id) {
0339   str.remove_prefix(pos);
0340   auto ctx = compile_parse_context<Char>(str, max_value<int>(), nullptr, {},
0341                                          next_arg_id);
0342   auto f = formatter<T, Char>();
0343   auto end = f.parse(ctx);
0344   return {f, pos + fmt::detail::to_unsigned(end - str.data()),
0345           next_arg_id == 0 ? manual_indexing_id : ctx.next_arg_id()};
0346 }
0347 
0348 template <typename Char> struct arg_id_handler {
0349   arg_ref<Char> arg_id;
0350 
0351   constexpr int operator()() {
0352     FMT_ASSERT(false, "handler cannot be used with automatic indexing");
0353     return 0;
0354   }
0355   constexpr int operator()(int id) {
0356     arg_id = arg_ref<Char>(id);
0357     return 0;
0358   }
0359   constexpr int operator()(basic_string_view<Char> id) {
0360     arg_id = arg_ref<Char>(id);
0361     return 0;
0362   }
0363 
0364   constexpr void on_error(const char* message) {
0365     FMT_THROW(format_error(message));
0366   }
0367 };
0368 
0369 template <typename Char> struct parse_arg_id_result {
0370   arg_ref<Char> arg_id;
0371   const Char* arg_id_end;
0372 };
0373 
0374 template <int ID, typename Char>
0375 constexpr auto parse_arg_id(const Char* begin, const Char* end) {
0376   auto handler = arg_id_handler<Char>{arg_ref<Char>{}};
0377   auto arg_id_end = parse_arg_id(begin, end, handler);
0378   return parse_arg_id_result<Char>{handler.arg_id, arg_id_end};
0379 }
0380 
0381 template <typename T, typename Enable = void> struct field_type {
0382   using type = remove_cvref_t<T>;
0383 };
0384 
0385 template <typename T>
0386 struct field_type<T, enable_if_t<detail::is_named_arg<T>::value>> {
0387   using type = remove_cvref_t<decltype(T::value)>;
0388 };
0389 
0390 template <typename T, typename Args, size_t END_POS, int ARG_INDEX, int NEXT_ID,
0391           typename S>
0392 constexpr auto parse_replacement_field_then_tail(S format_str) {
0393   using char_type = typename S::char_type;
0394   constexpr auto str = basic_string_view<char_type>(format_str);
0395   constexpr char_type c = END_POS != str.size() ? str[END_POS] : char_type();
0396   if constexpr (c == '}') {
0397     return parse_tail<Args, END_POS + 1, NEXT_ID>(
0398         field<char_type, typename field_type<T>::type, ARG_INDEX>(),
0399         format_str);
0400   } else if constexpr (c != ':') {
0401     FMT_THROW(format_error("expected ':'"));
0402   } else {
0403     constexpr auto result = parse_specs<typename field_type<T>::type>(
0404         str, END_POS + 1, NEXT_ID == manual_indexing_id ? 0 : NEXT_ID);
0405     if constexpr (result.end >= str.size() || str[result.end] != '}') {
0406       FMT_THROW(format_error("expected '}'"));
0407       return 0;
0408     } else {
0409       return parse_tail<Args, result.end + 1, result.next_arg_id>(
0410           spec_field<char_type, typename field_type<T>::type, ARG_INDEX>{
0411               result.fmt},
0412           format_str);
0413     }
0414   }
0415 }
0416 
0417 // Compiles a non-empty format string and returns the compiled representation
0418 // or unknown_format() on unrecognized input.
0419 template <typename Args, size_t POS, int ID, typename S>
0420 constexpr auto compile_format_string(S format_str) {
0421   using char_type = typename S::char_type;
0422   constexpr auto str = basic_string_view<char_type>(format_str);
0423   if constexpr (str[POS] == '{') {
0424     if constexpr (POS + 1 == str.size())
0425       FMT_THROW(format_error("unmatched '{' in format string"));
0426     if constexpr (str[POS + 1] == '{') {
0427       return parse_tail<Args, POS + 2, ID>(make_text(str, POS, 1), format_str);
0428     } else if constexpr (str[POS + 1] == '}' || str[POS + 1] == ':') {
0429       static_assert(ID != manual_indexing_id,
0430                     "cannot switch from manual to automatic argument indexing");
0431       constexpr auto next_id =
0432           ID != manual_indexing_id ? ID + 1 : manual_indexing_id;
0433       return parse_replacement_field_then_tail<get_type<ID, Args>, Args,
0434                                                POS + 1, ID, next_id>(
0435           format_str);
0436     } else {
0437       constexpr auto arg_id_result =
0438           parse_arg_id<ID>(str.data() + POS + 1, str.data() + str.size());
0439       constexpr auto arg_id_end_pos = arg_id_result.arg_id_end - str.data();
0440       constexpr char_type c =
0441           arg_id_end_pos != str.size() ? str[arg_id_end_pos] : char_type();
0442       static_assert(c == '}' || c == ':', "missing '}' in format string");
0443       if constexpr (arg_id_result.arg_id.kind == arg_id_kind::index) {
0444         static_assert(
0445             ID == manual_indexing_id || ID == 0,
0446             "cannot switch from automatic to manual argument indexing");
0447         constexpr auto arg_index = arg_id_result.arg_id.val.index;
0448         return parse_replacement_field_then_tail<get_type<arg_index, Args>,
0449                                                  Args, arg_id_end_pos,
0450                                                  arg_index, manual_indexing_id>(
0451             format_str);
0452       } else if constexpr (arg_id_result.arg_id.kind == arg_id_kind::name) {
0453         constexpr auto arg_index =
0454             get_arg_index_by_name(arg_id_result.arg_id.val.name, Args{});
0455         if constexpr (arg_index != invalid_arg_index) {
0456           constexpr auto next_id =
0457               ID != manual_indexing_id ? ID + 1 : manual_indexing_id;
0458           return parse_replacement_field_then_tail<
0459               decltype(get_type<arg_index, Args>::value), Args, arg_id_end_pos,
0460               arg_index, next_id>(format_str);
0461         } else {
0462           if constexpr (c == '}') {
0463             return parse_tail<Args, arg_id_end_pos + 1, ID>(
0464                 runtime_named_field<char_type>{arg_id_result.arg_id.val.name},
0465                 format_str);
0466           } else if constexpr (c == ':') {
0467             return unknown_format();  // no type info for specs parsing
0468           }
0469         }
0470       }
0471     }
0472   } else if constexpr (str[POS] == '}') {
0473     if constexpr (POS + 1 == str.size())
0474       FMT_THROW(format_error("unmatched '}' in format string"));
0475     return parse_tail<Args, POS + 2, ID>(make_text(str, POS, 1), format_str);
0476   } else {
0477     constexpr auto end = parse_text(str, POS + 1);
0478     if constexpr (end - POS > 1) {
0479       return parse_tail<Args, end, ID>(make_text(str, POS, end - POS),
0480                                        format_str);
0481     } else {
0482       return parse_tail<Args, end, ID>(code_unit<char_type>{str[POS]},
0483                                        format_str);
0484     }
0485   }
0486 }
0487 
0488 template <typename... Args, typename S,
0489           FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
0490 constexpr auto compile(S format_str) {
0491   constexpr auto str = basic_string_view<typename S::char_type>(format_str);
0492   if constexpr (str.size() == 0) {
0493     return detail::make_text(str, 0, 0);
0494   } else {
0495     constexpr auto result =
0496         detail::compile_format_string<detail::type_list<Args...>, 0, 0>(
0497             format_str);
0498     return result;
0499   }
0500 }
0501 #endif  // defined(__cpp_if_constexpr) && defined(__cpp_return_type_deduction)
0502 }  // namespace detail
0503 
0504 FMT_MODULE_EXPORT_BEGIN
0505 
0506 #if defined(__cpp_if_constexpr) && defined(__cpp_return_type_deduction)
0507 
0508 template <typename CompiledFormat, typename... Args,
0509           typename Char = typename CompiledFormat::char_type,
0510           FMT_ENABLE_IF(detail::is_compiled_format<CompiledFormat>::value)>
0511 FMT_INLINE std::basic_string<Char> format(const CompiledFormat& cf,
0512                                           const Args&... args) {
0513   auto s = std::basic_string<Char>();
0514   cf.format(std::back_inserter(s), args...);
0515   return s;
0516 }
0517 
0518 template <typename OutputIt, typename CompiledFormat, typename... Args,
0519           FMT_ENABLE_IF(detail::is_compiled_format<CompiledFormat>::value)>
0520 constexpr FMT_INLINE OutputIt format_to(OutputIt out, const CompiledFormat& cf,
0521                                         const Args&... args) {
0522   return cf.format(out, args...);
0523 }
0524 
0525 template <typename S, typename... Args,
0526           FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
0527 FMT_INLINE std::basic_string<typename S::char_type> format(const S&,
0528                                                            Args&&... args) {
0529   if constexpr (std::is_same<typename S::char_type, char>::value) {
0530     constexpr auto str = basic_string_view<typename S::char_type>(S());
0531     if constexpr (str.size() == 2 && str[0] == '{' && str[1] == '}') {
0532       const auto& first = detail::first(args...);
0533       if constexpr (detail::is_named_arg<
0534                         remove_cvref_t<decltype(first)>>::value) {
0535         return fmt::to_string(first.value);
0536       } else {
0537         return fmt::to_string(first);
0538       }
0539     }
0540   }
0541   constexpr auto compiled = detail::compile<Args...>(S());
0542   if constexpr (std::is_same<remove_cvref_t<decltype(compiled)>,
0543                              detail::unknown_format>()) {
0544     return fmt::format(
0545         static_cast<basic_string_view<typename S::char_type>>(S()),
0546         std::forward<Args>(args)...);
0547   } else {
0548     return fmt::format(compiled, std::forward<Args>(args)...);
0549   }
0550 }
0551 
0552 template <typename OutputIt, typename S, typename... Args,
0553           FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
0554 FMT_CONSTEXPR OutputIt format_to(OutputIt out, const S&, Args&&... args) {
0555   constexpr auto compiled = detail::compile<Args...>(S());
0556   if constexpr (std::is_same<remove_cvref_t<decltype(compiled)>,
0557                              detail::unknown_format>()) {
0558     return fmt::format_to(
0559         out, static_cast<basic_string_view<typename S::char_type>>(S()),
0560         std::forward<Args>(args)...);
0561   } else {
0562     return fmt::format_to(out, compiled, std::forward<Args>(args)...);
0563   }
0564 }
0565 #endif
0566 
0567 template <typename OutputIt, typename S, typename... Args,
0568           FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
0569 format_to_n_result<OutputIt> format_to_n(OutputIt out, size_t n,
0570                                          const S& format_str, Args&&... args) {
0571   auto it = fmt::format_to(detail::truncating_iterator<OutputIt>(out, n),
0572                            format_str, std::forward<Args>(args)...);
0573   return {it.base(), it.count()};
0574 }
0575 
0576 template <typename S, typename... Args,
0577           FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
0578 FMT_CONSTEXPR20 size_t formatted_size(const S& format_str,
0579                                       const Args&... args) {
0580   return fmt::format_to(detail::counting_iterator(), format_str, args...)
0581       .count();
0582 }
0583 
0584 template <typename S, typename... Args,
0585           FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
0586 void print(std::FILE* f, const S& format_str, const Args&... args) {
0587   memory_buffer buffer;
0588   fmt::format_to(std::back_inserter(buffer), format_str, args...);
0589   detail::print(f, {buffer.data(), buffer.size()});
0590 }
0591 
0592 template <typename S, typename... Args,
0593           FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
0594 void print(const S& format_str, const Args&... args) {
0595   print(stdout, format_str, args...);
0596 }
0597 
0598 #if FMT_USE_NONTYPE_TEMPLATE_ARGS
0599 inline namespace literals {
0600 template <detail_exported::fixed_string Str> constexpr auto operator""_cf() {
0601   using char_t = remove_cvref_t<decltype(Str.data[0])>;
0602   return detail::udl_compiled_string<char_t, sizeof(Str.data) / sizeof(char_t),
0603                                      Str>();
0604 }
0605 }  // namespace literals
0606 #endif
0607 
0608 FMT_MODULE_EXPORT_END
0609 FMT_END_NAMESPACE
0610 
0611 #endif  // FMT_COMPILE_H_