tformat.cc 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149
  1. // -*- mode: c++; coding: utf-8 -*-
  2. // ra-ra/test - Sandbox for std::format. Cf io.cc.
  3. // (c) Daniel Llorens - 2024
  4. // This library is free software; you can redistribute it and/or modify it under
  5. // the terms of the GNU Lesser General Public License as published by the Free
  6. // Software Foundation; either version 3 of the License, or (at your option) any
  7. // later version.
  8. // The binary cannot be called format bc gcc14 will try to include it (!!)
  9. #include <print>
  10. #include <cstdio>
  11. #include "ra/test.hh"
  12. using std::cout, std::endl, std::flush, ra::TestRecorder;
  13. using int3 = ra::Small<int, 3>;
  14. using int2 = ra::Small<int, 2>;
  15. using double2 = ra::Small<double, 2>;
  16. struct Q
  17. {
  18. int x = 1;
  19. };
  20. template <>
  21. struct std::formatter<Q>
  22. {
  23. constexpr auto
  24. parse(std::format_parse_context const & ctx)
  25. {
  26. auto i = ctx.begin();
  27. assert(i == ctx.end() || *i == '}');
  28. return i;
  29. }
  30. template <class Ctx>
  31. auto
  32. format(Q const & q, Ctx & ctx) const
  33. {
  34. return std::format_to(ctx.out(), "Q{}", q.x);
  35. }
  36. };
  37. // FIXME a way to use the formatter without parsing, like ra::format_t { .option = value ... }.
  38. // FIXME an ellipsis feature, e.g. max width/max length.
  39. // FIXME formatting options for the shape (needed?)
  40. template <class A> requires (ra::is_ra<A> || (ra::is_fov<A> && !std::is_convertible_v<A, std::string_view>))
  41. struct std::formatter<A>
  42. {
  43. ra::shape_t shape = ra::defaultshape;
  44. std::string open = "", close = "", sep0 = " ", sepn = "\n", rep = "\n";
  45. std::formatter<ra::value_t<A>> under;
  46. bool align = false;
  47. // https://stackoverflow.com/a/75549049
  48. template <class Ctx>
  49. constexpr auto
  50. parse(Ctx & ctx)
  51. {
  52. auto i = ctx.begin();
  53. for (; i!=ctx.end() && ':'!=*i && '}'!=*i; ++i) {
  54. switch (*i) {
  55. case 'j': break;
  56. case 'c': shape=ra::noshape; open="{"; close="}"; sep0=", "; sepn=",\n"; rep=""; align=true; break;
  57. case 'l': shape=ra::noshape; open="("; close=")"; sep0=" "; sepn="\n"; rep=""; align=true; break;
  58. case 'p': shape=ra::noshape; open="["; close="]"; sep0=", "; sepn=",\n"; rep="\n"; align=true; break;
  59. case 'a': align=true; break;
  60. case 'e': align=false; break;
  61. case 's': shape=ra::withshape; break;
  62. case 'n': shape=ra::noshape; break;
  63. case 'd': shape=ra::defaultshape; break;
  64. default: throw std::format_error("Bad format for ra:: object.");
  65. }
  66. }
  67. if (i!=ctx.end() && ':'==*i) {
  68. ctx.advance_to(i+1);
  69. i = under.parse(ctx);
  70. }
  71. if (i!=ctx.end() && '}'!=*i) {
  72. throw std::format_error("Unexpected input while parsing format for ra:: object.");
  73. }
  74. return i;
  75. }
  76. template <class Ctx>
  77. constexpr auto
  78. format(A const & a_, Ctx & ctx) const
  79. {
  80. static_assert(!ra::has_len<A>, "len outside subscript context.");
  81. static_assert(ra::BAD!=ra::size_s<A>(), "Cannot print undefined size expr.");
  82. auto a = ra::start(a_); // [ra35]
  83. auto sha = ra::shape(a);
  84. assert(every(ra::start(sha)>=0));
  85. auto out = ctx.out();
  86. // always print shape with defaultshape to avoid recursion on shape(shape(...)) = [1].
  87. if (shape==ra::withshape || (shape==ra::defaultshape && size_s(a)==ra::ANY)) {
  88. out = std::format_to(out, "{:d}\n", ra::start(sha));
  89. }
  90. ra::rank_t const rank = ra::rank(a);
  91. auto goin = [&](int k, auto & goin) -> void
  92. {
  93. if (k==rank) {
  94. ctx.advance_to(under.format(*a, ctx));
  95. out = ctx.out();
  96. } else {
  97. out = std::ranges::copy(open, out).out;
  98. for (int i=0; i<sha[k]; ++i) {
  99. goin(k+1, goin);
  100. if (i+1<sha[k]) {
  101. a.adv(k, 1);
  102. out = std::ranges::copy((k==rank-1 ? sep0 : sepn), out).out;
  103. for (int i=0; i<std::max(0, rank-2-k); ++i) {
  104. out = std::ranges::copy(rep, out).out;
  105. }
  106. if (align && k<rank-1) {
  107. for (int i=0; i<(k+1)*ra::size(open); ++i) {
  108. *out++ = ' ';
  109. }
  110. }
  111. } else {
  112. a.adv(k, 1-sha[k]);
  113. break;
  114. }
  115. }
  116. out = std::ranges::copy(close, out).out;
  117. }
  118. };
  119. goin(0, goin);
  120. return out;
  121. }
  122. };
  123. int main()
  124. {
  125. std::print(stdout, "a number {:015.9}\n", std::numbers::pi_v<double>);
  126. std::print(stdout, "a Q {}\n", Q { 3 });
  127. std::print(stdout, "a small {:}\n", ra::Small<int, 3> {1, 2, 3});
  128. std::print(stdout, "a small {::}\n", ra::Small<int, 3> {1, 2, 3});
  129. std::print(stdout, "a small {::9}\n", ra::Small<int, 3> {1, 2, 3});
  130. std::print(stdout, "a small {::<9}\n", ra::Small<int, 3> {1, 2, 3});
  131. std::print(stdout, "a big\n{::7}\n", ra::Big<int, 2>({3, 4}, ra::_0 + ra::_1));
  132. std::print(stdout, "a big\n{:s:7}\n", ra::Big<int, 2>({3, 4}, ra::_0 + ra::_1));
  133. std::print(stdout, "a big\n{:p:5}\n", ra::Big<int, 2>({3, 4}, ra::_0 + ra::_1));
  134. std::print(stdout, "a big\n{:pe:5}\n", ra::Big<int, 2>({3, 4}, ra::_0 + ra::_1));
  135. std::print(stdout, "a big\n{::5}\n", ra::Big<int>({3, 4}, ra::_0 + ra::_1));
  136. std::print(stdout, "a big(small)\n{:cs:p:06.3f}\n", ra::Big<double2>({3, 4}, ra::_0 + ra::_1));
  137. return 0;
  138. }