bootstrap.hh 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313
  1. // -*- mode: c++; coding: utf-8 -*-
  2. // ra-ra - Before all other ra:: includes.
  3. // (c) Daniel Llorens - 2013-2023
  4. // This library is free software; you can redistribute it and/or modify it under
  5. // the terms of the GNU 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. #pragma once
  9. #include <ranges>
  10. #include <array>
  11. #include <vector>
  12. #include <cstdint>
  13. #include "tuples.hh"
  14. #include <iosfwd> // for format, ss.
  15. #include <sstream>
  16. #include <version>
  17. #include <source_location>
  18. // benchmark shows it's bad by default; probably requires optimizing also +=, etc.
  19. #ifndef RA_DO_OPT_SMALLVECTOR
  20. #define RA_DO_OPT_SMALLVECTOR 0
  21. #endif
  22. // no real downside.
  23. #ifndef RA_DO_OPT_IOTA
  24. #define RA_DO_OPT_IOTA 1
  25. #endif
  26. namespace ra {
  27. constexpr int VERSION = 26;
  28. // negative rank is used as 'frame rank' in contrast to 'cell rank'. These values limit what can be handled.
  29. constexpr int ANY = -1944444444; // only at ct, meaning tbd at rt
  30. constexpr int BAD = -1988888888; // undefined, eg dead axes
  31. using rank_t = int;
  32. using dim_t = std::ptrdiff_t;
  33. static_assert(sizeof(rank_t)>=4 && sizeof(dim_t)>=4);
  34. static_assert(sizeof(rank_t)>=sizeof(int) && sizeof(dim_t)>=sizeof(rank_t));
  35. static_assert(std::is_signed_v<rank_t> && std::is_signed_v<dim_t>);
  36. template <dim_t V> using dim_c = std::integral_constant<dim_t, V>;
  37. template <rank_t V> using rank_c = std::integral_constant<rank_t, V>;
  38. enum none_t { none }; // in constructors to mean: don't initialize
  39. struct noarg { noarg() = delete; }; // in constructors to mean: don't instantiate
  40. // forward decl, extended in ra.hh
  41. constexpr bool any(bool const x) { return x; }
  42. constexpr bool every(bool const x) { return x; }
  43. // default storage for Big - see https://stackoverflow.com/a/21028912.
  44. // allocator adaptor that interposes construct() calls to convert value initialization into default initialization.
  45. template <class T, class A=std::allocator<T>>
  46. struct default_init_allocator: public A
  47. {
  48. using a_t = std::allocator_traits<A>;
  49. using A::A;
  50. template <class U>
  51. struct rebind
  52. {
  53. using other = default_init_allocator<U, typename a_t::template rebind_alloc<U>>;
  54. };
  55. template <class U>
  56. void construct(U * ptr) noexcept(std::is_nothrow_default_constructible<U>::value)
  57. {
  58. ::new(static_cast<void *>(ptr)) U;
  59. }
  60. template <class U, class... Args>
  61. void construct(U * ptr, Args &&... args)
  62. {
  63. a_t::construct(static_cast<A &>(*this), ptr, RA_FWD(args)...);
  64. }
  65. };
  66. template <class T> using vector_default_init = std::vector<T, default_init_allocator<T>>;
  67. // ---------------------
  68. // concepts
  69. // ---------------------
  70. template <class A>
  71. concept IteratorConcept = requires (A a, rank_t k, dim_t d, rank_t i, rank_t j)
  72. {
  73. { a.rank() } -> std::same_as<rank_t>;
  74. // FIXME still have ply(&) in places. Cf test/types.cc.
  75. { std::decay_t<A>::len_s(k) } -> std::same_as<dim_t>;
  76. { a.len(k) } -> std::same_as<dim_t>;
  77. { a.adv(k, d) } -> std::same_as<void>;
  78. { a.step(k) };
  79. { a.keep_step(d, i, j) } -> std::same_as<bool>;
  80. { a.save() };
  81. { a.load(std::declval<decltype(a.save())>()) } -> std::same_as<void>;
  82. { a.mov(d) } -> std::same_as<void>;
  83. { *a };
  84. };
  85. template <class A>
  86. concept SliceConcept = requires (A a)
  87. {
  88. { a.rank() } -> std::same_as<rank_t>;
  89. { a.iter() } -> IteratorConcept;
  90. };
  91. // --------------
  92. // type classification / introspection
  93. // --------------
  94. // FIXME https://wg21.link/p2841r0 ?
  95. #define RA_IS_DEF(NAME, PRED) \
  96. template <class A> constexpr bool JOIN(NAME, _def) = requires { requires PRED; }; \
  97. template <class A> concept NAME = JOIN(NAME, _def)<std::decay_t< A >>;
  98. RA_IS_DEF(is_scalar, (!std::is_pointer_v<A> && std::is_scalar_v<A> || ra::is_constant<A>))
  99. template <> constexpr bool is_scalar_def<std::strong_ordering> = true;
  100. template <> constexpr bool is_scalar_def<std::weak_ordering> = true;
  101. template <> constexpr bool is_scalar_def<std::partial_ordering> = true;
  102. // template <> constexpr bool is_scalar_def<std::string_view> = true; // [ra13]
  103. RA_IS_DEF(is_iterator, IteratorConcept<A>)
  104. template <class A> concept is_ra = is_iterator<A> || SliceConcept<A>;
  105. template <class A> concept is_builtin_array = std::is_array_v<std::remove_cvref_t<A>>;
  106. RA_IS_DEF(is_fov, (!is_scalar<A> && !is_ra<A> && !is_builtin_array<A> && std::ranges::random_access_range<A>))
  107. template <class VV> requires (!std::is_void_v<VV>)
  108. consteval rank_t
  109. rank_s()
  110. {
  111. using V = std::remove_cvref_t<VV>;
  112. if constexpr (is_builtin_array<V>) {
  113. return std::rank_v<V>;
  114. } else if constexpr (is_fov<V>) {
  115. return 1;
  116. } else if constexpr (requires { V::rank(); }) {
  117. return V::rank();
  118. } else if constexpr (requires (V v) { v.rank(); }) {
  119. return ANY;
  120. } else {
  121. return 0;
  122. }
  123. }
  124. template <class V> consteval rank_t rank_s(V const &) { return rank_s<V>(); } // c++23 p2280r4
  125. template <class V>
  126. constexpr rank_t
  127. rank(V const & v)
  128. {
  129. if constexpr (ANY!=rank_s<V>()) {
  130. return rank_s<V>();
  131. } else if constexpr (requires { v.rank(); }) {
  132. return v.rank();
  133. } else {
  134. static_assert(always_false<V>, "No rank() for this type.");
  135. }
  136. }
  137. RA_IS_DEF(is_pos, 0!=rank_s<A>())
  138. template <class A> concept is_ra_pos = is_ra<A> && is_pos<A>;
  139. template <class A> concept is_zero_or_scalar = (is_ra<A> && !is_pos<A>) || is_scalar<A>;
  140. // all args rank 0 (immediate application), but at least one ra:: (don't collide with the scalar version).
  141. RA_IS_DEF(is_special, false) // rank-0 types that we don't want reduced.
  142. template <class ... A> constexpr bool toreduce = (!is_scalar<A> || ...) && ((is_zero_or_scalar<A> && !is_special<A>) && ...);
  143. template <class ... A> constexpr bool tomap = ((is_ra_pos<A> || is_special<A>) || ...) && ((is_ra<A> || is_scalar<A> || is_fov<A> || is_builtin_array<A>) && ...);
  144. template <class VV> requires (!std::is_void_v<VV>)
  145. consteval dim_t
  146. size_s()
  147. {
  148. using V = std::remove_cvref_t<VV>;
  149. constexpr rank_t rs = rank_s<V>();
  150. if constexpr (0==rs) {
  151. return 1;
  152. } else if constexpr (is_builtin_array<V>) {
  153. return std::apply([] (auto ... i) { return (std::extent_v<V, i> * ... * 1); }, mp::iota<rs> {});
  154. } else if constexpr (is_fov<V> && requires { std::tuple_size<V>::value; }) {
  155. return std::tuple_size_v<V>;
  156. } else if constexpr (is_fov<V> || rs==ANY) {
  157. return ANY;
  158. } else if constexpr (requires { V::size_s(); }) {
  159. return V::size_s();
  160. } else {
  161. dim_t s = 1;
  162. for (int i=0; i<rs; ++i) {
  163. if (dim_t ss=V::len_s(i); ss>=0) { s *= ss; } else { return ss; } // ANY or BAD
  164. }
  165. return s;
  166. }
  167. }
  168. template <class V> consteval dim_t size_s(V const &) { return size_s<V>(); } // c++23 p2280r4
  169. template <class V>
  170. constexpr dim_t
  171. size(V const & v)
  172. {
  173. if constexpr (ANY!=size_s<V>()) {
  174. return size_s<V>();
  175. } else if constexpr (is_fov<V>) {
  176. return std::ssize(v);
  177. } else if constexpr (requires { v.size(); }) {
  178. return v.size();
  179. } else {
  180. dim_t s = 1;
  181. for (rank_t k=0; k<rank(v); ++k) { s *= v.len(k); }
  182. return s;
  183. }
  184. }
  185. // Returns concrete types, value or const &. FIXME return ra:: types, but only if it's in all cases.
  186. template <class V>
  187. constexpr decltype(auto)
  188. shape(V const & v)
  189. {
  190. constexpr rank_t rs = rank_s<V>();
  191. // FIXME __cpp_constexpr >= 202211L to return references to the constexpr cases
  192. if constexpr (is_builtin_array<V>) {
  193. return std::apply([] (auto ... i) { return std::array<dim_t, rs> { std::extent_v<V, i> ... }; }, mp::iota<rs> {});
  194. } else if constexpr (requires { v.shape(); }) {
  195. return v.shape();
  196. } else if constexpr (0==rs) {
  197. return std::array<dim_t, 0> {};
  198. } else if constexpr (1==rs) {
  199. return std::array<dim_t, 1> { ra::size(v) };
  200. } else if constexpr (1<rs) {
  201. return std::apply([&v](auto ... i) { return std::array<dim_t, rs> { v.len(i) ... }; }, mp::iota<rs> {});
  202. } else {
  203. static_assert(ANY==rs);
  204. auto i = std::ranges::iota_view { 0, rank(v) } | std::views::transform([&v](auto k) { return v.len(k); });
  205. return vector_default_init<dim_t>(i.begin(), i.end()); // FIXME C++23 p1206? Still fugly
  206. }
  207. }
  208. // --------------
  209. // format
  210. // --------------
  211. enum print_shape_t { defaultshape, withshape, noshape };
  212. template <class A>
  213. struct FormatArray
  214. {
  215. A const & a;
  216. print_shape_t shape;
  217. using pchar = char const *;
  218. pchar sep0, sep1, sep2;
  219. };
  220. template <class A>
  221. constexpr FormatArray<A>
  222. format_array(A const & a, char const * sep0=" ", char const * sep1="\n", char const * sep2="\n")
  223. {
  224. return FormatArray<A> { a, defaultshape, sep0, sep1, sep2 };
  225. }
  226. struct shape_manip_t
  227. {
  228. std::ostream & o;
  229. print_shape_t shape;
  230. };
  231. constexpr shape_manip_t
  232. operator<<(std::ostream & o, print_shape_t shape) { return shape_manip_t { o, shape }; }
  233. // include is_fov bc shape() may be std::vector or std::array.
  234. // exclude std::string_view to let it be is_fov and still print as a string [ra13].
  235. template <class A> requires (is_ra<A> || (is_fov<A> && !std::is_convertible_v<A, std::string_view>))
  236. constexpr std::ostream &
  237. operator<<(std::ostream & o, A && a) { return o << format_array(a); }
  238. template <class T>
  239. constexpr std::ostream &
  240. operator<<(std::ostream & o, std::initializer_list<T> const & a) { return o << format_array(a); }
  241. template <class A>
  242. constexpr std::ostream &
  243. operator<<(shape_manip_t const & sm, FormatArray<A> fa) { return sm.o << (fa.shape=sm.shape, fa); }
  244. template <class A>
  245. constexpr std::ostream &
  246. operator<<(shape_manip_t const & sm, A const & a) { return sm << format_array(a); }
  247. /* constexpr */ inline std::ostream &
  248. operator<<(std::ostream & o, std::source_location const & loc)
  249. {
  250. return o << loc.file_name() << ":" << loc.line() << "," << loc.column();
  251. }
  252. constexpr std::string
  253. format(auto && ... a)
  254. {
  255. if constexpr (sizeof... (a)>0) {
  256. std::ostringstream o; (o << ... << RA_FWD(a)); return o.str();
  257. } else {
  258. return "";
  259. }
  260. }
  261. constexpr std::string const &
  262. format(std::string const & s) { return s; }
  263. } // namespace ra
  264. #ifdef RA_AFTER_CHECK
  265. #error Bad header include order! Do not include ra/bootstrap.hh after other ra:: headers.
  266. #endif