compile.h 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643
  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. #ifndef FMT_COMPILE_H_
  8. #define FMT_COMPILE_H_
  9. #include "format.h"
  10. FMT_BEGIN_NAMESPACE
  11. namespace detail {
  12. // An output iterator that counts the number of objects written to it and
  13. // discards them.
  14. class counting_iterator {
  15. private:
  16. size_t count_;
  17. public:
  18. using iterator_category = std::output_iterator_tag;
  19. using difference_type = std::ptrdiff_t;
  20. using pointer = void;
  21. using reference = void;
  22. using _Unchecked_type = counting_iterator; // Mark iterator as checked.
  23. struct value_type {
  24. template <typename T> void operator=(const T&) {}
  25. };
  26. counting_iterator() : count_(0) {}
  27. size_t count() const { return count_; }
  28. counting_iterator& operator++() {
  29. ++count_;
  30. return *this;
  31. }
  32. counting_iterator operator++(int) {
  33. auto it = *this;
  34. ++*this;
  35. return it;
  36. }
  37. friend counting_iterator operator+(counting_iterator it, difference_type n) {
  38. it.count_ += static_cast<size_t>(n);
  39. return it;
  40. }
  41. value_type operator*() const { return {}; }
  42. };
  43. template <typename Char, typename InputIt>
  44. inline counting_iterator copy_str(InputIt begin, InputIt end,
  45. counting_iterator it) {
  46. return it + (end - begin);
  47. }
  48. template <typename OutputIt> class truncating_iterator_base {
  49. protected:
  50. OutputIt out_;
  51. size_t limit_;
  52. size_t count_ = 0;
  53. truncating_iterator_base() : out_(), limit_(0) {}
  54. truncating_iterator_base(OutputIt out, size_t limit)
  55. : out_(out), limit_(limit) {}
  56. public:
  57. using iterator_category = std::output_iterator_tag;
  58. using value_type = typename std::iterator_traits<OutputIt>::value_type;
  59. using difference_type = std::ptrdiff_t;
  60. using pointer = void;
  61. using reference = void;
  62. using _Unchecked_type =
  63. truncating_iterator_base; // Mark iterator as checked.
  64. OutputIt base() const { return out_; }
  65. size_t count() const { return count_; }
  66. };
  67. // An output iterator that truncates the output and counts the number of objects
  68. // written to it.
  69. template <typename OutputIt,
  70. typename Enable = typename std::is_void<
  71. typename std::iterator_traits<OutputIt>::value_type>::type>
  72. class truncating_iterator;
  73. template <typename OutputIt>
  74. class truncating_iterator<OutputIt, std::false_type>
  75. : public truncating_iterator_base<OutputIt> {
  76. mutable typename truncating_iterator_base<OutputIt>::value_type blackhole_;
  77. public:
  78. using value_type = typename truncating_iterator_base<OutputIt>::value_type;
  79. truncating_iterator() = default;
  80. truncating_iterator(OutputIt out, size_t limit)
  81. : truncating_iterator_base<OutputIt>(out, limit) {}
  82. truncating_iterator& operator++() {
  83. if (this->count_++ < this->limit_) ++this->out_;
  84. return *this;
  85. }
  86. truncating_iterator operator++(int) {
  87. auto it = *this;
  88. ++*this;
  89. return it;
  90. }
  91. value_type& operator*() const {
  92. return this->count_ < this->limit_ ? *this->out_ : blackhole_;
  93. }
  94. };
  95. template <typename OutputIt>
  96. class truncating_iterator<OutputIt, std::true_type>
  97. : public truncating_iterator_base<OutputIt> {
  98. public:
  99. truncating_iterator() = default;
  100. truncating_iterator(OutputIt out, size_t limit)
  101. : truncating_iterator_base<OutputIt>(out, limit) {}
  102. template <typename T> truncating_iterator& operator=(T val) {
  103. if (this->count_++ < this->limit_) *this->out_++ = val;
  104. return *this;
  105. }
  106. truncating_iterator& operator++() { return *this; }
  107. truncating_iterator& operator++(int) { return *this; }
  108. truncating_iterator& operator*() { return *this; }
  109. };
  110. // A compile-time string which is compiled into fast formatting code.
  111. class compiled_string {};
  112. template <typename S>
  113. struct is_compiled_string : std::is_base_of<compiled_string, S> {};
  114. /**
  115. \rst
  116. Converts a string literal *s* into a format string that will be parsed at
  117. compile time and converted into efficient formatting code. Requires C++17
  118. ``constexpr if`` compiler support.
  119. **Example**::
  120. // Converts 42 into std::string using the most efficient method and no
  121. // runtime format string processing.
  122. std::string s = fmt::format(FMT_COMPILE("{}"), 42);
  123. \endrst
  124. */
  125. #if defined(__cpp_if_constexpr) && defined(__cpp_return_type_deduction)
  126. # define FMT_COMPILE(s) \
  127. FMT_STRING_IMPL(s, fmt::detail::compiled_string, explicit)
  128. #else
  129. # define FMT_COMPILE(s) FMT_STRING(s)
  130. #endif
  131. #if FMT_USE_NONTYPE_TEMPLATE_PARAMETERS
  132. template <typename Char, size_t N,
  133. fmt::detail_exported::fixed_string<Char, N> Str>
  134. struct udl_compiled_string : compiled_string {
  135. using char_type = Char;
  136. constexpr operator basic_string_view<char_type>() const {
  137. return {Str.data, N - 1};
  138. }
  139. };
  140. #endif
  141. template <typename T, typename... Tail>
  142. const T& first(const T& value, const Tail&...) {
  143. return value;
  144. }
  145. #if defined(__cpp_if_constexpr) && defined(__cpp_return_type_deduction)
  146. template <typename... Args> struct type_list {};
  147. // Returns a reference to the argument at index N from [first, rest...].
  148. template <int N, typename T, typename... Args>
  149. constexpr const auto& get([[maybe_unused]] const T& first,
  150. [[maybe_unused]] const Args&... rest) {
  151. static_assert(N < 1 + sizeof...(Args), "index is out of bounds");
  152. if constexpr (N == 0)
  153. return first;
  154. else
  155. return detail::get<N - 1>(rest...);
  156. }
  157. template <typename Char, typename... Args>
  158. constexpr int get_arg_index_by_name(basic_string_view<Char> name,
  159. type_list<Args...>) {
  160. return get_arg_index_by_name<Args...>(name);
  161. }
  162. template <int N, typename> struct get_type_impl;
  163. template <int N, typename... Args> struct get_type_impl<N, type_list<Args...>> {
  164. using type =
  165. remove_cvref_t<decltype(detail::get<N>(std::declval<Args>()...))>;
  166. };
  167. template <int N, typename T>
  168. using get_type = typename get_type_impl<N, T>::type;
  169. template <typename T> struct is_compiled_format : std::false_type {};
  170. template <typename Char> struct text {
  171. basic_string_view<Char> data;
  172. using char_type = Char;
  173. template <typename OutputIt, typename... Args>
  174. constexpr OutputIt format(OutputIt out, const Args&...) const {
  175. return write<Char>(out, data);
  176. }
  177. };
  178. template <typename Char>
  179. struct is_compiled_format<text<Char>> : std::true_type {};
  180. template <typename Char>
  181. constexpr text<Char> make_text(basic_string_view<Char> s, size_t pos,
  182. size_t size) {
  183. return {{&s[pos], size}};
  184. }
  185. template <typename Char> struct code_unit {
  186. Char value;
  187. using char_type = Char;
  188. template <typename OutputIt, typename... Args>
  189. constexpr OutputIt format(OutputIt out, const Args&...) const {
  190. return write<Char>(out, value);
  191. }
  192. };
  193. // This ensures that the argument type is convertible to `const T&`.
  194. template <typename T, int N, typename... Args>
  195. constexpr const T& get_arg_checked(const Args&... args) {
  196. const auto& arg = detail::get<N>(args...);
  197. if constexpr (detail::is_named_arg<remove_cvref_t<decltype(arg)>>()) {
  198. return arg.value;
  199. } else {
  200. return arg;
  201. }
  202. }
  203. template <typename Char>
  204. struct is_compiled_format<code_unit<Char>> : std::true_type {};
  205. // A replacement field that refers to argument N.
  206. template <typename Char, typename T, int N> struct field {
  207. using char_type = Char;
  208. template <typename OutputIt, typename... Args>
  209. constexpr OutputIt format(OutputIt out, const Args&... args) const {
  210. return write<Char>(out, get_arg_checked<T, N>(args...));
  211. }
  212. };
  213. template <typename Char, typename T, int N>
  214. struct is_compiled_format<field<Char, T, N>> : std::true_type {};
  215. // A replacement field that refers to argument with name.
  216. template <typename Char> struct runtime_named_field {
  217. using char_type = Char;
  218. basic_string_view<Char> name;
  219. template <typename OutputIt, typename T>
  220. constexpr static bool try_format_argument(
  221. OutputIt& out,
  222. // [[maybe_unused]] due to unused-but-set-parameter warning in GCC 7,8,9
  223. [[maybe_unused]] basic_string_view<Char> arg_name, const T& arg) {
  224. if constexpr (is_named_arg<typename std::remove_cv<T>::type>::value) {
  225. if (arg_name == arg.name) {
  226. out = write<Char>(out, arg.value);
  227. return true;
  228. }
  229. }
  230. return false;
  231. }
  232. template <typename OutputIt, typename... Args>
  233. constexpr OutputIt format(OutputIt out, const Args&... args) const {
  234. bool found = (try_format_argument(out, name, args) || ...);
  235. if (!found) {
  236. FMT_THROW(format_error("argument with specified name is not found"));
  237. }
  238. return out;
  239. }
  240. };
  241. template <typename Char>
  242. struct is_compiled_format<runtime_named_field<Char>> : std::true_type {};
  243. // A replacement field that refers to argument N and has format specifiers.
  244. template <typename Char, typename T, int N> struct spec_field {
  245. using char_type = Char;
  246. formatter<T, Char> fmt;
  247. template <typename OutputIt, typename... Args>
  248. constexpr FMT_INLINE OutputIt format(OutputIt out,
  249. const Args&... args) const {
  250. const auto& vargs =
  251. fmt::make_format_args<basic_format_context<OutputIt, Char>>(args...);
  252. basic_format_context<OutputIt, Char> ctx(out, vargs);
  253. return fmt.format(get_arg_checked<T, N>(args...), ctx);
  254. }
  255. };
  256. template <typename Char, typename T, int N>
  257. struct is_compiled_format<spec_field<Char, T, N>> : std::true_type {};
  258. template <typename L, typename R> struct concat {
  259. L lhs;
  260. R rhs;
  261. using char_type = typename L::char_type;
  262. template <typename OutputIt, typename... Args>
  263. constexpr OutputIt format(OutputIt out, const Args&... args) const {
  264. out = lhs.format(out, args...);
  265. return rhs.format(out, args...);
  266. }
  267. };
  268. template <typename L, typename R>
  269. struct is_compiled_format<concat<L, R>> : std::true_type {};
  270. template <typename L, typename R>
  271. constexpr concat<L, R> make_concat(L lhs, R rhs) {
  272. return {lhs, rhs};
  273. }
  274. struct unknown_format {};
  275. template <typename Char>
  276. constexpr size_t parse_text(basic_string_view<Char> str, size_t pos) {
  277. for (size_t size = str.size(); pos != size; ++pos) {
  278. if (str[pos] == '{' || str[pos] == '}') break;
  279. }
  280. return pos;
  281. }
  282. template <typename Args, size_t POS, int ID, typename S>
  283. constexpr auto compile_format_string(S format_str);
  284. template <typename Args, size_t POS, int ID, typename T, typename S>
  285. constexpr auto parse_tail(T head, S format_str) {
  286. if constexpr (POS !=
  287. basic_string_view<typename S::char_type>(format_str).size()) {
  288. constexpr auto tail = compile_format_string<Args, POS, ID>(format_str);
  289. if constexpr (std::is_same<remove_cvref_t<decltype(tail)>,
  290. unknown_format>())
  291. return tail;
  292. else
  293. return make_concat(head, tail);
  294. } else {
  295. return head;
  296. }
  297. }
  298. template <typename T, typename Char> struct parse_specs_result {
  299. formatter<T, Char> fmt;
  300. size_t end;
  301. int next_arg_id;
  302. };
  303. constexpr int manual_indexing_id = -1;
  304. template <typename T, typename Char>
  305. constexpr parse_specs_result<T, Char> parse_specs(basic_string_view<Char> str,
  306. size_t pos, int next_arg_id) {
  307. str.remove_prefix(pos);
  308. auto ctx = basic_format_parse_context<Char>(str, {}, next_arg_id);
  309. auto f = formatter<T, Char>();
  310. auto end = f.parse(ctx);
  311. return {f, pos + fmt::detail::to_unsigned(end - str.data()) + 1,
  312. next_arg_id == 0 ? manual_indexing_id : ctx.next_arg_id()};
  313. }
  314. template <typename Char> struct arg_id_handler {
  315. arg_ref<Char> arg_id;
  316. constexpr int operator()() {
  317. FMT_ASSERT(false, "handler cannot be used with automatic indexing");
  318. return 0;
  319. }
  320. constexpr int operator()(int id) {
  321. arg_id = arg_ref<Char>(id);
  322. return 0;
  323. }
  324. constexpr int operator()(basic_string_view<Char> id) {
  325. arg_id = arg_ref<Char>(id);
  326. return 0;
  327. }
  328. constexpr void on_error(const char* message) {
  329. FMT_THROW(format_error(message));
  330. }
  331. };
  332. template <typename Char> struct parse_arg_id_result {
  333. arg_ref<Char> arg_id;
  334. const Char* arg_id_end;
  335. };
  336. template <int ID, typename Char>
  337. constexpr auto parse_arg_id(const Char* begin, const Char* end) {
  338. auto handler = arg_id_handler<Char>{arg_ref<Char>{}};
  339. auto arg_id_end = parse_arg_id(begin, end, handler);
  340. return parse_arg_id_result<Char>{handler.arg_id, arg_id_end};
  341. }
  342. template <typename T, typename Enable = void> struct field_type {
  343. using type = remove_cvref_t<T>;
  344. };
  345. template <typename T>
  346. struct field_type<T, enable_if_t<detail::is_named_arg<T>::value>> {
  347. using type = remove_cvref_t<decltype(T::value)>;
  348. };
  349. template <typename T, typename Args, size_t END_POS, int ARG_INDEX, int NEXT_ID,
  350. typename S>
  351. constexpr auto parse_replacement_field_then_tail(S format_str) {
  352. using char_type = typename S::char_type;
  353. constexpr auto str = basic_string_view<char_type>(format_str);
  354. constexpr char_type c = END_POS != str.size() ? str[END_POS] : char_type();
  355. if constexpr (c == '}') {
  356. return parse_tail<Args, END_POS + 1, NEXT_ID>(
  357. field<char_type, typename field_type<T>::type, ARG_INDEX>(),
  358. format_str);
  359. } else if constexpr (c == ':') {
  360. constexpr auto result = parse_specs<typename field_type<T>::type>(
  361. str, END_POS + 1, NEXT_ID == manual_indexing_id ? 0 : NEXT_ID);
  362. return parse_tail<Args, result.end, result.next_arg_id>(
  363. spec_field<char_type, typename field_type<T>::type, ARG_INDEX>{
  364. result.fmt},
  365. format_str);
  366. }
  367. }
  368. // Compiles a non-empty format string and returns the compiled representation
  369. // or unknown_format() on unrecognized input.
  370. template <typename Args, size_t POS, int ID, typename S>
  371. constexpr auto compile_format_string(S format_str) {
  372. using char_type = typename S::char_type;
  373. constexpr auto str = basic_string_view<char_type>(format_str);
  374. if constexpr (str[POS] == '{') {
  375. if constexpr (POS + 1 == str.size())
  376. FMT_THROW(format_error("unmatched '{' in format string"));
  377. if constexpr (str[POS + 1] == '{') {
  378. return parse_tail<Args, POS + 2, ID>(make_text(str, POS, 1), format_str);
  379. } else if constexpr (str[POS + 1] == '}' || str[POS + 1] == ':') {
  380. static_assert(ID != manual_indexing_id,
  381. "cannot switch from manual to automatic argument indexing");
  382. constexpr auto next_id =
  383. ID != manual_indexing_id ? ID + 1 : manual_indexing_id;
  384. return parse_replacement_field_then_tail<get_type<ID, Args>, Args,
  385. POS + 1, ID, next_id>(
  386. format_str);
  387. } else {
  388. constexpr auto arg_id_result =
  389. parse_arg_id<ID>(str.data() + POS + 1, str.data() + str.size());
  390. constexpr auto arg_id_end_pos = arg_id_result.arg_id_end - str.data();
  391. constexpr char_type c =
  392. arg_id_end_pos != str.size() ? str[arg_id_end_pos] : char_type();
  393. static_assert(c == '}' || c == ':', "missing '}' in format string");
  394. if constexpr (arg_id_result.arg_id.kind == arg_id_kind::index) {
  395. static_assert(
  396. ID == manual_indexing_id || ID == 0,
  397. "cannot switch from automatic to manual argument indexing");
  398. constexpr auto arg_index = arg_id_result.arg_id.val.index;
  399. return parse_replacement_field_then_tail<get_type<arg_index, Args>,
  400. Args, arg_id_end_pos,
  401. arg_index, manual_indexing_id>(
  402. format_str);
  403. } else if constexpr (arg_id_result.arg_id.kind == arg_id_kind::name) {
  404. constexpr auto arg_index =
  405. get_arg_index_by_name(arg_id_result.arg_id.val.name, Args{});
  406. if constexpr (arg_index != invalid_arg_index) {
  407. constexpr auto next_id =
  408. ID != manual_indexing_id ? ID + 1 : manual_indexing_id;
  409. return parse_replacement_field_then_tail<
  410. decltype(get_type<arg_index, Args>::value), Args, arg_id_end_pos,
  411. arg_index, next_id>(format_str);
  412. } else {
  413. if constexpr (c == '}') {
  414. return parse_tail<Args, arg_id_end_pos + 1, ID>(
  415. runtime_named_field<char_type>{arg_id_result.arg_id.val.name},
  416. format_str);
  417. } else if constexpr (c == ':') {
  418. return unknown_format(); // no type info for specs parsing
  419. }
  420. }
  421. }
  422. }
  423. } else if constexpr (str[POS] == '}') {
  424. if constexpr (POS + 1 == str.size())
  425. FMT_THROW(format_error("unmatched '}' in format string"));
  426. return parse_tail<Args, POS + 2, ID>(make_text(str, POS, 1), format_str);
  427. } else {
  428. constexpr auto end = parse_text(str, POS + 1);
  429. if constexpr (end - POS > 1) {
  430. return parse_tail<Args, end, ID>(make_text(str, POS, end - POS),
  431. format_str);
  432. } else {
  433. return parse_tail<Args, end, ID>(code_unit<char_type>{str[POS]},
  434. format_str);
  435. }
  436. }
  437. }
  438. template <typename... Args, typename S,
  439. FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
  440. constexpr auto compile(S format_str) {
  441. constexpr auto str = basic_string_view<typename S::char_type>(format_str);
  442. if constexpr (str.size() == 0) {
  443. return detail::make_text(str, 0, 0);
  444. } else {
  445. constexpr auto result =
  446. detail::compile_format_string<detail::type_list<Args...>, 0, 0>(
  447. format_str);
  448. return result;
  449. }
  450. }
  451. #endif // defined(__cpp_if_constexpr) && defined(__cpp_return_type_deduction)
  452. } // namespace detail
  453. FMT_MODULE_EXPORT_BEGIN
  454. #if defined(__cpp_if_constexpr) && defined(__cpp_return_type_deduction)
  455. template <typename CompiledFormat, typename... Args,
  456. typename Char = typename CompiledFormat::char_type,
  457. FMT_ENABLE_IF(detail::is_compiled_format<CompiledFormat>::value)>
  458. FMT_INLINE std::basic_string<Char> format(const CompiledFormat& cf,
  459. const Args&... args) {
  460. auto s = std::basic_string<Char>();
  461. cf.format(std::back_inserter(s), args...);
  462. return s;
  463. }
  464. template <typename OutputIt, typename CompiledFormat, typename... Args,
  465. FMT_ENABLE_IF(detail::is_compiled_format<CompiledFormat>::value)>
  466. constexpr FMT_INLINE OutputIt format_to(OutputIt out, const CompiledFormat& cf,
  467. const Args&... args) {
  468. return cf.format(out, args...);
  469. }
  470. template <typename S, typename... Args,
  471. FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
  472. FMT_INLINE std::basic_string<typename S::char_type> format(const S&,
  473. Args&&... args) {
  474. if constexpr (std::is_same<typename S::char_type, char>::value) {
  475. constexpr auto str = basic_string_view<typename S::char_type>(S());
  476. if constexpr (str.size() == 2 && str[0] == '{' && str[1] == '}') {
  477. const auto& first = detail::first(args...);
  478. if constexpr (detail::is_named_arg<
  479. remove_cvref_t<decltype(first)>>::value) {
  480. return fmt::to_string(first.value);
  481. } else {
  482. return fmt::to_string(first);
  483. }
  484. }
  485. }
  486. constexpr auto compiled = detail::compile<Args...>(S());
  487. if constexpr (std::is_same<remove_cvref_t<decltype(compiled)>,
  488. detail::unknown_format>()) {
  489. return format(static_cast<basic_string_view<typename S::char_type>>(S()),
  490. std::forward<Args>(args)...);
  491. } else {
  492. return format(compiled, std::forward<Args>(args)...);
  493. }
  494. }
  495. template <typename OutputIt, typename S, typename... Args,
  496. FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
  497. FMT_CONSTEXPR OutputIt format_to(OutputIt out, const S&, Args&&... args) {
  498. constexpr auto compiled = detail::compile<Args...>(S());
  499. if constexpr (std::is_same<remove_cvref_t<decltype(compiled)>,
  500. detail::unknown_format>()) {
  501. return format_to(out,
  502. static_cast<basic_string_view<typename S::char_type>>(S()),
  503. std::forward<Args>(args)...);
  504. } else {
  505. return format_to(out, compiled, std::forward<Args>(args)...);
  506. }
  507. }
  508. #endif
  509. template <typename OutputIt, typename S, typename... Args,
  510. FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
  511. format_to_n_result<OutputIt> format_to_n(OutputIt out, size_t n,
  512. const S& format_str, Args&&... args) {
  513. auto it = format_to(detail::truncating_iterator<OutputIt>(out, n), format_str,
  514. std::forward<Args>(args)...);
  515. return {it.base(), it.count()};
  516. }
  517. template <typename S, typename... Args,
  518. FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
  519. size_t formatted_size(const S& format_str, const Args&... args) {
  520. return format_to(detail::counting_iterator(), format_str, args...).count();
  521. }
  522. template <typename S, typename... Args,
  523. FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
  524. void print(std::FILE* f, const S& format_str, const Args&... args) {
  525. memory_buffer buffer;
  526. format_to(std::back_inserter(buffer), format_str, args...);
  527. detail::print(f, {buffer.data(), buffer.size()});
  528. }
  529. template <typename S, typename... Args,
  530. FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
  531. void print(const S& format_str, const Args&... args) {
  532. print(stdout, format_str, args...);
  533. }
  534. #if FMT_USE_NONTYPE_TEMPLATE_PARAMETERS
  535. inline namespace literals {
  536. template <detail_exported::fixed_string Str>
  537. constexpr detail::udl_compiled_string<
  538. remove_cvref_t<decltype(Str.data[0])>,
  539. sizeof(Str.data) / sizeof(decltype(Str.data[0])), Str>
  540. operator""_cf() {
  541. return {};
  542. }
  543. } // namespace literals
  544. #endif
  545. FMT_MODULE_EXPORT_END
  546. FMT_END_NAMESPACE
  547. #endif // FMT_COMPILE_H_