bootstrap.hh 10 KB

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