big.hh 35 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849
  1. // -*- mode: c++; coding: utf-8 -*-
  2. /// @file big.hh
  3. /// @brief Arrays with dynamic size.
  4. // (c) Daniel Llorens - 2013-2014, 2017-2020
  5. // This library is free software; you can redistribute it and/or modify it under
  6. // the terms of the GNU Lesser General Public License as published by the Free
  7. // Software Foundation; either version 3 of the License, or (at your option) any
  8. // later version.
  9. #pragma once
  10. #include "ra/small.hh"
  11. #include <memory>
  12. #include <iostream>
  13. namespace ra {
  14. // Dope vector element
  15. struct Dim { dim_t size, stride; };
  16. // For debugging
  17. inline std::ostream & operator<<(std::ostream & o, Dim const & dim)
  18. { o << "[Dim " << dim.size << " " << dim.stride << "]"; return o; }
  19. // --------------------
  20. // nested braces for Container initializers
  21. // --------------------
  22. // Avoid clash of T with scalar constructors (for rank 0).
  23. template <class T, rank_t rank>
  24. struct nested_braces { using list = no_arg; };
  25. template <class T, rank_t rank>
  26. requires (rank==1)
  27. struct nested_braces<T, rank>
  28. {
  29. using list = std::initializer_list<T>;
  30. template <size_t N> constexpr static
  31. void shape(list l, std::array<dim_t, N> & s)
  32. {
  33. static_assert(N>0);
  34. s[N-1] = l.size();
  35. }
  36. };
  37. template <class T, rank_t rank>
  38. requires (rank>1)
  39. struct nested_braces<T, rank>
  40. {
  41. using sub = nested_braces<T, rank-1>;
  42. using list = std::initializer_list<typename sub::list>;
  43. template <size_t N> constexpr static
  44. void shape(list l, std::array<dim_t, N> & s)
  45. {
  46. s[N-rank] = l.size();
  47. if (l.size()>0) {
  48. sub::template shape(*(l.begin()), s);
  49. } else {
  50. std::fill(s.begin()+N-rank+1, s.end(), 0);
  51. }
  52. }
  53. };
  54. // --------------------
  55. // Develop indices for Big
  56. // --------------------
  57. struct Indexer1
  58. {
  59. template <class Dim, class P>
  60. constexpr static dim_t index_p(Dim const & dim, P && p)
  61. {
  62. RA_CHECK(dim_t(dim.size())>=start(p).size(0), "too many indices");
  63. // use dim.data() to skip the size check.
  64. dim_t c = 0;
  65. for_each([&c](auto && d, auto && p) { RA_CHECK(inside(p, d.size)); c += d.stride*p; },
  66. ptr(dim.data()), p);
  67. return c;
  68. }
  69. // used by cell_iterator::at() for rank matching on rank<driving rank, no slicing. TODO Static check.
  70. template <class Dim, class P>
  71. constexpr static dim_t index_short(rank_t framer, Dim const & dim, P const & p)
  72. {
  73. dim_t c = 0;
  74. for (rank_t k=0; k<framer; ++k) {
  75. RA_CHECK(inside(p[k], dim[k].size) || (dim[k].size==DIM_BAD && dim[k].stride==0));
  76. c += dim[k].stride * p[k];
  77. }
  78. return c;
  79. }
  80. };
  81. // --------------------
  82. // Big iterator
  83. // --------------------
  84. // TODO Refactor with cell_iterator_small for SmallView?
  85. // V is View. FIXME Parameterize? apparently only for order-of-decl.
  86. template <class V, rank_t cellr_=0>
  87. struct cell_iterator
  88. {
  89. constexpr static rank_t cellr_spec = cellr_;
  90. static_assert(cellr_spec!=RANK_ANY && cellr_spec!=RANK_BAD, "bad cell rank");
  91. constexpr static rank_t fullr = ra::rank_s<V>();
  92. constexpr static rank_t cellr = dependent_cell_rank(fullr, cellr_spec);
  93. constexpr static rank_t framer = dependent_frame_rank(fullr, cellr_spec);
  94. static_assert(cellr>=0 || cellr==RANK_ANY, "bad cell rank");
  95. static_assert(framer>=0 || framer==RANK_ANY, "bad frame rank");
  96. static_assert(fullr==cellr || gt_rank(fullr, cellr), "bad cell rank");
  97. using Dimv_ = typename std::decay_t<V>::Dimv;
  98. using P = typename std::decay_t<V>::P;
  99. using Dimv = std::conditional_t<std::is_lvalue_reference_v<V>, Dimv_ const &, Dimv_>;
  100. Dimv dim;
  101. constexpr static rank_t rank_s() { return framer; }
  102. constexpr rank_t rank() const { return dependent_frame_rank(rank_t(dim.size()), cellr_spec); }
  103. using shape_type = std::conditional_t<rank_s()==DIM_ANY, std::vector<dim_t>,
  104. Small<dim_t, rank_s()==DIM_ANY ? 0 : rank_s()>>; // still needs protection :-/
  105. using atom_type = std::remove_reference_t<decltype(*(std::declval<V>().data()))>;
  106. using cell_type = View<P, cellr>;
  107. using value_type = std::conditional_t<0==cellr, atom_type, cell_type>;
  108. cell_type c;
  109. constexpr cell_iterator(cell_iterator const & ci): dim(ci.dim), c { ci.c.dim, ci.c.p } {}
  110. // s_ is array's full shape; split it into dim/i (frame) and c (cell).
  111. constexpr cell_iterator(Dimv const & dim_, atom_type * p_): dim(dim_)
  112. {
  113. rank_t rank = this->rank();
  114. // see stl_iterator for the case of dim_[0]=0, etc. [ra12].
  115. c.p = p_;
  116. rank_t cellr = dependent_cell_rank(rank_t(dim.size()), cellr_spec);
  117. resize(c.dim, cellr);
  118. for (int k=0; k<cellr; ++k) {
  119. c.dim[k] = dim_[k+rank];
  120. }
  121. }
  122. constexpr static dim_t size_s(int i) { /* RA_CHECK(inside(k, rank())); */ return DIM_ANY; }
  123. constexpr dim_t size(int k) const { RA_CHECK(inside(k, rank())); return dim[k].size; }
  124. constexpr dim_t stride(int k) const { return k<rank() ? dim[k].stride : 0; }
  125. // FIXME handle z or j over rank()? check cell_iterator_small versions.
  126. constexpr bool keep_stride(dim_t st, int z, int j) const { return st*stride(z)==stride(j); }
  127. constexpr void adv(rank_t k, dim_t d) { c.p += stride(k)*d; }
  128. constexpr
  129. auto flat() const
  130. {
  131. if constexpr (0==cellr) {
  132. return c.p;
  133. } else {
  134. return CellFlat<cell_type> { c };
  135. }
  136. }
  137. // Return type to allow either View & or View const & verb. Can't set self b/c original p isn't kept. TODO Think this over.
  138. template <class I> constexpr
  139. decltype(auto) at(I const & i_)
  140. {
  141. RA_CHECK(rank()<=dim_t(i_.size()), "too few indices");
  142. if constexpr (0==cellr) {
  143. return c.p[Indexer1::index_short(rank(), dim, i_)];
  144. } else {
  145. return cell_type { c.dim, c.p+Indexer1::index_short(rank(), dim, i_) };
  146. }
  147. }
  148. FOR_EACH(RA_DEF_ASSIGNOPS, =, *=, +=, -=, /=)
  149. };
  150. // --------------------
  151. // Indexing views
  152. // --------------------
  153. // Always C order. If you need another, transpose this.
  154. // Works on dim vector with sizes already assigned, so that I can work from an expr. Not pretty though.
  155. template <class D>
  156. dim_t filldim(int n, D dend)
  157. {
  158. dim_t next = 1;
  159. for (; n>0; --n) {
  160. --dend;
  161. RA_CHECK((*dend).size>=0, "bad dim", (*dend).size);
  162. (*dend).stride = next;
  163. next *= (*dend).size;
  164. }
  165. return next;
  166. }
  167. template <class D>
  168. dim_t proddim(D d, D dend)
  169. {
  170. dim_t t = 1;
  171. for (; d!=dend; ++d) {
  172. t *= (*d).size;
  173. }
  174. return t;
  175. }
  176. // allow conversion only from T * to T const *.
  177. // FIXME constrain the members instead of using no_arg.
  178. template <class P> struct sub_const { using type = no_arg; };
  179. template <class P> struct add_const { using type = P; };
  180. template <class P> using nop_t = P;
  181. template <class P> using sub_const_t = sub_const<P>::type;
  182. template <class P> using add_const_t = add_const<P>::type;
  183. template <class T> struct sub_const<T const *> { using type = T *; };
  184. template <class T> requires (!std::is_const_v<T>) struct add_const<T *> { using type = T const *; };
  185. // raw <- shared; raw <- unique; shared <-- unique.
  186. // layout is
  187. // [data] (fixed shape)
  188. // [size] p -> [data...] (fixed rank)
  189. // [rank] [size...] p -> [data...] (var rank)
  190. // TODO size is immutable so that it can be kept together with rank.
  191. #define DEF_ITERATORS(RANK) \
  192. template <rank_t c=0> constexpr auto iter() && { return ra::cell_iterator<View<P, RANK>, c>(std::move(dim), p); } \
  193. template <rank_t c=0> constexpr auto iter() const & { return ra::cell_iterator<View<P, RANK> &, c>(dim, p); } \
  194. constexpr auto begin() const { return stl_iterator(iter()); } \
  195. constexpr auto begin() { return stl_iterator(iter()); } \
  196. /* here dim doesn't matter, but we have to give it if it's a ref. */ \
  197. constexpr auto end() const { return stl_iterator(decltype(iter())(dim, nullptr)); } \
  198. constexpr auto end() { return stl_iterator(decltype(iter())(dim, nullptr)); }
  199. // See same thing for SmallBase.
  200. #define DEF_ASSIGNOPS(OP) \
  201. template <class X> View & operator OP (X && x) { ra::start(*this) OP x; return *this; }
  202. #define DEF_VIEW_COMMON(RANK) \
  203. FOR_EACH(DEF_ASSIGNOPS, =, *=, +=, -=, /=) \
  204. /* Constructors using pointers need extra care */ \
  205. constexpr View(): p(nullptr) {} \
  206. constexpr View(Dimv const & dim_, P p_): dim(dim_), p(p_) {} /* [ra36] */ \
  207. template <class SS> \
  208. View(SS && s, P p_): p(p_) \
  209. { \
  210. if constexpr (std::is_convertible_v<value_t<SS>, Dim>) { \
  211. ra::resize(View::dim, start(s).size(0)); /* [ra37] */ \
  212. start(View::dim) = s; \
  213. } else { \
  214. ra::resize(View::dim, start(s).size(0)); /* [ra37] */ \
  215. for_each([](Dim & dim, auto && s) { dim.size = s; }, View::dim, s); \
  216. filldim(View::dim.size(), View::dim.end()); \
  217. } \
  218. } \
  219. View(std::initializer_list<dim_t> s, P p_): View(start(s), p_) {} \
  220. /* lack of these causes runtime bug [ra38] FIXME why? */ \
  221. View(View && x) = default; \
  222. View(View const & x) = default; \
  223. /* declaring View(View &&) deletes this, so we need to repeat it [ra34] */ \
  224. View & operator=(View const & x) \
  225. { \
  226. ra::start(*this) = x; \
  227. return *this; \
  228. } \
  229. /* array type is not deduced by (X &&) */ \
  230. View & operator=(typename nested_braces<T, RANK>::list x) \
  231. { \
  232. ra::iter<-1>(*this) = x; \
  233. return *this; \
  234. } \
  235. /* braces row-major ravel for rank!=1 */ \
  236. using ravel_arg = std::conditional_t<RANK==1, no_arg, std::initializer_list<T>>; \
  237. View & operator=(ravel_arg const x) \
  238. { \
  239. RA_CHECK(p && this->size()==ra::dim_t(x.size()), "bad assignment"); \
  240. std::copy(x.begin(), x.end(), this->begin()); \
  241. return *this; \
  242. } \
  243. bool const empty() const { return 0==size(); } /* TODO Optimize */
  244. inline dim_t select(Dim * dim, Dim const * dim_src, dim_t i)
  245. {
  246. RA_CHECK(inside(i, dim_src->size), " i ", i, " size ", dim_src->size);
  247. return dim_src->stride*i;
  248. }
  249. template <class II>
  250. inline dim_t select(Dim * dim, Dim const * dim_src, ra::Iota<II> i)
  251. {
  252. RA_CHECK((inside(i.i_, dim_src->size) && inside(i.i_+(i.size_-1)*i.stride_, dim_src->size))
  253. || (i.size_==0 && i.i_<=dim_src->size));
  254. dim->size = i.size_;
  255. dim->stride = dim_src->stride * i.stride_;
  256. return dim_src->stride*i.i_;
  257. }
  258. template <class I0, class ... I>
  259. inline dim_t select_loop(Dim * dim, Dim const * dim_src, I0 && i0, I && ... i)
  260. {
  261. return select(dim, dim_src, std::forward<I0>(i0))
  262. + select_loop(dim+is_beatable<I0>::skip, dim_src+is_beatable<I0>::skip_src, std::forward<I>(i) ...);
  263. }
  264. template <int n, class ... I>
  265. inline dim_t select_loop(Dim * dim, Dim const * dim_src, dots_t<n> dots, I && ... i)
  266. {
  267. for (Dim * end = dim+n; dim!=end; ++dim, ++dim_src) {
  268. *dim = *dim_src;
  269. }
  270. return select_loop(dim, dim_src, std::forward<I>(i) ...);
  271. }
  272. template <int n, class ... I>
  273. inline dim_t select_loop(Dim * dim, Dim const * dim_src, insert_t<n> insert, I && ... i)
  274. {
  275. for (Dim * end = dim+n; dim!=end; ++dim) {
  276. dim->size = DIM_BAD;
  277. dim->stride = 0;
  278. }
  279. return select_loop(dim, dim_src, std::forward<I>(i) ...);
  280. }
  281. inline dim_t select_loop(Dim * dim, Dim const * dim_src)
  282. {
  283. return 0;
  284. }
  285. // --------------------
  286. // View for fixed rank
  287. // --------------------
  288. // TODO Parameterize on Child having .data() so that there's only one pointer.
  289. // TODO A constructor, if only for RA_CHECK (positive sizes, strides inside, etc.)
  290. template <std::random_access_iterator P_, rank_t RANK> // FIXME [ra27]
  291. struct View
  292. {
  293. using Dimv = Small<Dim, RANK>;
  294. using P = P_;
  295. Dimv dim;
  296. P p;
  297. using T = std::remove_reference_t<decltype(*p)>;
  298. constexpr static rank_t rank_s() { return RANK; };
  299. constexpr static rank_t rank() { return RANK; }
  300. constexpr static dim_t size_s(int j) { return DIM_ANY; }
  301. constexpr dim_t size() const { return proddim(dim.begin(), dim.end()); }
  302. constexpr dim_t size(int j) const { return dim[j].size; }
  303. constexpr dim_t stride(int j) const { return dim[j].stride; }
  304. constexpr auto data() const { return p; }
  305. // Specialize for rank() integer-args -> scalar, same in ra::SmallBase in small.hh.
  306. #define SUBSCRIPTS(CONST, ADD_CONST) \
  307. template <class ... I> \
  308. requires ((0 + ... + std::is_integral_v<std::decay_t<I>>)<RANK \
  309. && (0 + ... + is_beatable<I>::value)==sizeof...(I)) \
  310. auto operator()(I && ... i) CONST \
  311. { \
  312. constexpr rank_t extended = (0 + ... + (is_beatable<I>::skip-is_beatable<I>::skip_src)); \
  313. constexpr rank_t subrank = rank_sum(RANK, extended); \
  314. static_assert(subrank>=0, "bad subrank"); \
  315. View<ADD_CONST<P>, subrank> sub; \
  316. sub.p = data() + select_loop(sub.dim.data(), this->dim.data(), i ...); \
  317. /* fill the rest of dim, skipping over beatable subscripts */ \
  318. for (int i=(0 + ... + is_beatable<I>::skip); i<subrank; ++i) { \
  319. sub.dim[i] = this->dim[i-extended]; \
  320. } \
  321. return sub; \
  322. } \
  323. /* BUG doesn't handle generic zero-rank indices */ \
  324. template <class ... I> \
  325. requires ((0 + ... + std::is_integral_v<I>)==RANK) \
  326. decltype(auto) operator()(I const & ... i) CONST \
  327. { \
  328. return data()[select_loop(nullptr, this->dim.data(), i ...)]; \
  329. } \
  330. /* TODO > 1 selector... This still covers (unbeatable, integer) for example, which could be reduced. */ \
  331. template <class ... I> \
  332. requires (!(is_beatable<I>::value && ...)) \
  333. decltype(auto) operator()(I && ... i) CONST \
  334. { \
  335. return from(*this, std::forward<I>(i) ...); \
  336. } \
  337. template <class I> \
  338. decltype(auto) at(I && i) CONST \
  339. { \
  340. constexpr rank_t subrank = rank_diff(RANK, ra::size_s<I>()); /* gcc accepts i.size() */ \
  341. using Sub = View<ADD_CONST<P>, subrank>; \
  342. if constexpr (subrank==RANK_ANY) { \
  343. return Sub { typename Sub::Dimv(dim.begin()+ra::size(i), dim.end()), /* Dimv is std::vector */ \
  344. data() + Indexer1::index_p(dim, i) }; \
  345. } else { \
  346. return Sub { typename Sub::Dimv(ptr(dim.begin()+ra::size(i))), /* Dimv is ra::Small */ \
  347. data() + Indexer1::index_p(dim, i) }; \
  348. } \
  349. } \
  350. constexpr decltype(auto) operator[](dim_t const i) CONST \
  351. { \
  352. return (*this)(i); \
  353. }
  354. SUBSCRIPTS(/* const */, nop_t)
  355. SUBSCRIPTS(const, add_const_t)
  356. #undef SUBSCRIPTS
  357. DEF_ITERATORS(RANK)
  358. DEF_VIEW_COMMON(RANK)
  359. // implicit conversion from var rank [ra18]
  360. template <rank_t R>
  361. requires (R==RANK_ANY && R!=RANK)
  362. View(View<P, R> const & x): dim(x.dim), p(x.p) {}
  363. // implicit conversion from var rank non const [ra29]
  364. template <rank_t R>
  365. requires (R==RANK_ANY && R!=RANK)
  366. View(View<sub_const_t<P>, R> const & x): dim(x.dim), p(x.p) {}
  367. // conversion from non const [ra29]
  368. operator View<add_const_t<P>, RANK> const & () const
  369. {
  370. return *reinterpret_cast<View<add_const_t<P>, RANK> const *>(this);
  371. }
  372. operator View<add_const_t<P>, RANK> & ()
  373. {
  374. return *reinterpret_cast<View<add_const_t<P>, RANK> *>(this);
  375. }
  376. // conversions to scalar.
  377. operator T & () { static_assert(RANK==0, "bad rank"); return data()[0]; }
  378. operator T const & () const { static_assert(RANK==0, "bad rank"); return data()[0]; }
  379. };
  380. template <class P, rank_t RANK>
  381. struct ra_traits_def<View<P, RANK>>
  382. {
  383. using V = View<P, RANK>;
  384. static decltype(auto) shape(V const & v) { return ra::shape(v.iter()); }
  385. static dim_t size(V const & v) { return v.size(); }
  386. constexpr static rank_t rank(V const & v) { return v.rank(); }
  387. constexpr static rank_t rank_s() { return RANK; };
  388. constexpr static dim_t size_s() { return RANK==0 ? 1 : DIM_ANY; }
  389. };
  390. // --------------------
  391. // View for var rank
  392. // --------------------
  393. template <std::random_access_iterator P_> // FIXME [ra27]
  394. struct View<P_, RANK_ANY>
  395. {
  396. using Dimv = std::vector<Dim>; // hmm
  397. using P = P_;
  398. Dimv dim;
  399. P p;
  400. using T = std::remove_reference_t<decltype(*p)>;
  401. constexpr static rank_t rank_s() { return RANK_ANY; }
  402. constexpr rank_t rank() const { return rank_t(dim.size()); }
  403. constexpr static dim_t size_s() { return DIM_ANY; }
  404. constexpr dim_t size() const { return proddim(dim.begin(), dim.end()); }
  405. // checking because std::vector doesn't.
  406. constexpr dim_t size(int const j) const { RA_CHECK(j<rank(), " j : ", j, " rank ", rank()); return dim[j].size; }
  407. constexpr dim_t stride(int const j) const { RA_CHECK(j<rank(), " j : ", j, " rank ", rank()); return dim[j].stride; }
  408. constexpr auto data() const { return p; }
  409. // Contrary to RANK!=RANK_ANY, the scalar case cannot be separated at compile time. So operator() will return a rank 0 view in that case (and rely on conversion if, say, this ends up assigned to a scalar).
  410. #define SUBSCRIPTS(CONST, ADD_CONST) \
  411. template <class ... I> \
  412. requires (is_beatable<I>::value && ...) \
  413. decltype(auto) operator()(I && ... i) CONST \
  414. { \
  415. constexpr rank_t extended = (0 + ... + (is_beatable<I>::skip-is_beatable<I>::skip_src)); \
  416. assert(this->rank()+extended>=0); \
  417. View<ADD_CONST<P>, RANK_ANY> sub; \
  418. sub.dim.resize(this->rank()+extended); \
  419. sub.p = data() + select_loop(sub.dim.data(), this->dim.data(), i ...); \
  420. for (int i=(0 + ... + is_beatable<I>::skip); i<sub.rank(); ++i) { \
  421. sub.dim[i] = this->dim[i-extended]; \
  422. } \
  423. return sub; \
  424. } \
  425. /* TODO More than one selector... */ \
  426. template <class ... I> \
  427. requires (!(is_beatable<I>::value && ...)) \
  428. decltype(auto) operator()(I && ... i) CONST \
  429. { \
  430. return from(*this, std::forward<I>(i) ...); \
  431. } \
  432. template <class I> \
  433. decltype(auto) at(I && i) CONST \
  434. { \
  435. return View<ADD_CONST<P>, RANK_ANY> { Dimv(dim.begin()+i.size(), dim.end()), /* Dimv is std::vector */ \
  436. data() + Indexer1::index_p(dim, i) }; \
  437. } \
  438. constexpr decltype(auto) operator[](dim_t const i) CONST \
  439. { \
  440. return (*this)(i); \
  441. }
  442. SUBSCRIPTS(/* const */, nop_t)
  443. SUBSCRIPTS(const, add_const_t)
  444. DEF_ITERATORS(RANK_ANY)
  445. DEF_VIEW_COMMON(RANK_ANY)
  446. // implicit conversion from fixed rank [ra18]
  447. template <rank_t R>
  448. requires (R!=RANK_ANY)
  449. View(View<P, R> const & x): dim(x.dim.begin(), x.dim.end()), p(x.p) {}
  450. // implicit conversion from fixed rank non const [ra29]
  451. template <rank_t R>
  452. requires (R!=RANK_ANY)
  453. View(View<sub_const_t<P>, R> const & x): dim(x.dim.begin(), x.dim.end()), p(x.p) {}
  454. // conversion from non const [ra29]
  455. operator View<add_const_t<P>> const & () const
  456. {
  457. return *reinterpret_cast<View<add_const_t<P>> const *>(this);
  458. }
  459. operator View<add_const_t<P>> & ()
  460. {
  461. return *reinterpret_cast<View<add_const_t<P>> *>(this);
  462. }
  463. // conversions to scalar
  464. operator T & () { RA_CHECK(rank()==0, "converting rank ", rank(), " to scalar"); return data()[0]; };
  465. operator T const & () const { RA_CHECK(rank()==0, "converting rank ", rank(), " to scalar"); return data()[0]; };
  466. };
  467. #undef DEF_ITERATORS
  468. #undef DEF_VIEW_COMMON
  469. #undef DEF_ASSIGNOPS
  470. // --------------------
  471. // Container types
  472. // --------------------
  473. template <class V> struct storage_traits
  474. {
  475. using T = std::decay_t<decltype(*std::declval<V>().get())>;
  476. static V create(dim_t n) { RA_CHECK(n>=0); return V(new T[n]); }
  477. static T const * data(V const & v) { return v.get(); }
  478. static T * data(V & v) { return v.get(); }
  479. };
  480. template <class T_, class A> struct storage_traits<std::vector<T_, A>>
  481. {
  482. using T = T_;
  483. static_assert(!std::is_same_v<std::remove_const_t<T>, bool>, "No pointers to bool in std::vector<bool>");
  484. static std::vector<T, A> create(dim_t n) { return std::vector<T, A>(n); }
  485. static T const * data(std::vector<T, A> const & v) { return v.data(); }
  486. static T * data(std::vector<T, A> & v) { return v.data(); }
  487. };
  488. template <RaView A>
  489. inline bool
  490. is_c_order(A const & a)
  491. {
  492. dim_t s = 1;
  493. for (int i=a.rank()-1; i>=0; --i) {
  494. if (s!=a.stride(i)) {
  495. return false;
  496. }
  497. s *= a.size(i);
  498. if (s==0) {
  499. return true;
  500. }
  501. }
  502. return true;
  503. }
  504. // TODO be convertible to View only, so that View::p is not duplicated
  505. template <class Store, rank_t RANK>
  506. struct Container: private View<typename storage_traits<Store>::T *, RANK>
  507. {
  508. Store store;
  509. using T = typename storage_traits<Store>::T;
  510. using ViewTrue = View<typename storage_traits<Store>::T *, RANK>;
  511. using ViewConst = View<typename storage_traits<Store>::T const *, RANK>;
  512. using shape_arg = typename decltype(std::declval<ViewTrue>().iter())::shape_type;
  513. using ViewTrue::rank;
  514. using ViewTrue::rank_s;
  515. using ViewTrue::size_s;
  516. using ViewTrue::size;
  517. using ViewTrue::stride;
  518. using ViewTrue::operator=;
  519. // True/Const for const transfer to View
  520. operator ViewTrue & () { return view; }
  521. operator ViewConst & () { return *reinterpret_cast<ViewConst *>(this); }
  522. operator ViewConst const & () const { return *reinterpret_cast<ViewConst const *>(this); }
  523. ViewTrue & view() { return *this; }
  524. ViewConst const & view() const { return *this; }
  525. template <class ... A> decltype(auto) operator()(A && ... a) { return view()(std::forward<A>(a) ...); }
  526. template <class ... A> decltype(auto) operator()(A && ... a) const { return view()(std::forward<A>(a) ...); }
  527. template <class A> decltype(auto) operator[](A && a) { return view()[std::forward<A>(a)]; }
  528. template <class A> decltype(auto) operator[](A && a) const { return view()[std::forward<A>(a)]; }
  529. decltype(auto) data() { return view().data(); }
  530. decltype(auto) data() const { return view().data(); }
  531. template <class I> constexpr decltype(auto) at(I const & i) { return view().at(i); }
  532. template <class I> constexpr decltype(auto) at(I const & i) const { return view().at(i); }
  533. template <rank_t c=0> constexpr decltype(auto) iter() { return view().template iter<c>(); }
  534. template <rank_t c=0> constexpr decltype(auto) iter() const { return view().template iter<c>(); }
  535. // TODO Explicit definitions are needed only to have view.p set. Remove the duplication as in SmallBase/SmallArray, then remove these, both the constructors and the operator=s.
  536. Container(Container && w): store(std::move(w.store))
  537. {
  538. ViewTrue::dim = std::move(w.dim);
  539. ViewTrue::p = storage_traits<Store>::data(store);
  540. }
  541. Container(Container const & w): store(w.store)
  542. {
  543. ViewTrue::dim = w.dim;
  544. ViewTrue::p = storage_traits<Store>::data(store);
  545. }
  546. Container(Container & w): store(w.store)
  547. {
  548. ViewTrue::dim = w.dim;
  549. ViewTrue::p = storage_traits<Store>::data(store);
  550. }
  551. // Override View::operator= to allow initialization-of-reference. Unfortunately operator>>(std::istream &, Container &) requires it. The presence of these operator= means that A(shape 2 3) = type-of-A [1 2 3] initializes so it doesn't behave as A(shape 2 3) = not-type-of-A [1 2 3] which will use View::operator= and frame match. See test/ownership.cc [ra20].
  552. // TODO do this through .set() op.
  553. // TODO don't require copiable T from constructors, see fill1 below. That requires initialization and not update semantics for operator=.
  554. Container & operator=(Container && w)
  555. {
  556. store = std::move(w.store);
  557. ViewTrue::dim = std::move(w.dim);
  558. ViewTrue::p = storage_traits<Store>::data(store);
  559. return *this;
  560. }
  561. Container & operator=(Container const & w)
  562. {
  563. store = w.store;
  564. ViewTrue::dim = w.dim;
  565. ViewTrue::p = storage_traits<Store>::data(store);
  566. return *this;
  567. }
  568. Container & operator=(Container & w)
  569. {
  570. store = w.store;
  571. ViewTrue::dim = w.dim;
  572. ViewTrue::p = storage_traits<Store>::data(store);
  573. return *this;
  574. }
  575. // Provided so that {} calls shape_arg constructor below.
  576. Container()
  577. {
  578. if constexpr (RANK==RANK_ANY) {
  579. View::dim = { Dim {0, 1} }; // rank 1 to avoid store init
  580. } else {
  581. for (Dim & dimi: View::dim) { dimi = {0, 1}; } // 1 so we can push_back()
  582. }
  583. }
  584. template <class S> void init(S && s)
  585. {
  586. static_assert(!std::is_convertible_v<value_t<S>, Dim>);
  587. // no rank extension here, because it's error prone and not very useful.
  588. static_assert(1==ra::rank_s<S>(), "rank mismatch for init shape");
  589. // [ra37] Need two parts because Dimv might be STL type. Otherwise I'd just ViewTrue::dim.set(map(...)).
  590. if constexpr (RANK==RANK_ANY) {
  591. ra::resize(ViewTrue::dim, ra::size(s));
  592. }
  593. for_each([](Dim & dim, auto const & s) { dim.size = s; }, ViewTrue::dim, s);
  594. dim_t t = filldim(ViewTrue::dim.size(), ViewTrue::dim.end());
  595. store = storage_traits<Store>::create(t);
  596. ViewTrue::p = storage_traits<Store>::data(store);
  597. }
  598. // FIXME use of fill1 requires T to be copiable, this is unfortunate as it conflicts with the semantics of view_.operator=.
  599. // store(x) avoids it for Big, but doesn't work for Unique. Should construct in place as std::vector does.
  600. template <class Pbegin> void fill1(dim_t xsize, Pbegin xbegin)
  601. {
  602. RA_CHECK(this->size()==xsize, "mismatched sizes");
  603. std::copy_n(xbegin, xsize, this->begin()); // TODO Use xpr traversal.
  604. }
  605. // shape_arg overloads handle {...} arguments. Size check is courtesy of conversion (if shape_arg is Small) or init().
  606. // explicit shape.
  607. Container(shape_arg const & s, none_t) { init(s); }
  608. template <class XX> Container(shape_arg const & s, XX && x): Container(s, none) { view() = x; }
  609. // shape from data.
  610. template <class XX> Container(XX && x): Container(ra::shape(x), none) { view() = x; }
  611. Container(typename nested_braces<T, RANK>::list x)
  612. {
  613. static_assert(RANK!=RANK_ANY);
  614. std::array<dim_t, RANK> s;
  615. nested_braces<T, RANK>::template shape(x, s);
  616. init(s);
  617. view() = x;
  618. }
  619. // braces row-major ravel for rank!=1
  620. Container(typename ViewTrue::ravel_arg x)
  621. : Container({dim_t(x.size())}, none) { fill1(x.size(), x.begin()); }
  622. // shape + row-major ravel. // TODO Maybe remove these? See also small.hh.
  623. Container(shape_arg const & s, std::initializer_list<T> x)
  624. : Container(s, none) { fill1(x.size(), x.begin()); }
  625. template <class TT>
  626. Container(shape_arg const & s, TT * p)
  627. : Container(s, none) { fill1(this->size(), p); }
  628. template <class P>
  629. Container(shape_arg const & s, P pbegin, P pend)
  630. : Container(s, none) { fill1(this->size(), pbegin); }
  631. // these are needed when shape_arg is std::vector, since that doesn't handle conversions like Small does.
  632. template <class SS> Container(SS && s, none_t) { init(s); }
  633. template <class SS, class XX> Container(SS && s, XX && x): Container(s, none) { view() = x; }
  634. template <class SS> Container(SS const & s, std::initializer_list<T> x)
  635. : Container(s, none) { fill1(x.size(), x.begin()); }
  636. // only for some kinds of store.
  637. void resize(dim_t const s)
  638. {
  639. static_assert(RANK==RANK_ANY || RANK>0); RA_CHECK(this->rank()>0);
  640. store.resize(proddim(ViewTrue::dim.begin()+1, ViewTrue::dim.end())*s);
  641. ViewTrue::dim[0].size = s;
  642. ViewTrue::p = store.data();
  643. }
  644. void resize(dim_t const s, T const & t)
  645. {
  646. static_assert(RANK==RANK_ANY || RANK>0); RA_CHECK(this->rank()>0);
  647. store.resize(proddim(ViewTrue::dim.begin()+1, ViewTrue::dim.end())*s, t);
  648. ViewTrue::dim[0].size = s;
  649. ViewTrue::p = store.data();
  650. }
  651. // lets us move. A template + std::forward wouldn't work for push_back(brace-enclosed-list).
  652. void push_back(T && t)
  653. {
  654. static_assert(RANK==1 || RANK==RANK_ANY); RA_CHECK(this->rank()==1);
  655. store.push_back(std::move(t));
  656. ++ViewTrue::dim[0].size;
  657. ViewTrue::p = store.data();
  658. }
  659. void push_back(T const & t)
  660. {
  661. static_assert(RANK==1 || RANK==RANK_ANY); RA_CHECK(this->rank()==1);
  662. store.push_back(t);
  663. ++ViewTrue::dim[0].size;
  664. ViewTrue::p = store.data();
  665. }
  666. template <class ... A>
  667. void emplace_back(A && ... a)
  668. {
  669. static_assert(RANK==1 || RANK==RANK_ANY); RA_CHECK(this->rank()==1);
  670. store.emplace_back(std::forward<A>(a) ...);
  671. ++ViewTrue::dim[0].size;
  672. ViewTrue::p = store.data();
  673. }
  674. void pop_back()
  675. {
  676. static_assert(RANK==1 || RANK==RANK_ANY); RA_CHECK(this->rank()==1);
  677. RA_CHECK(ViewTrue::dim[0].size>0);
  678. store.pop_back();
  679. --ViewTrue::dim[0].size;
  680. }
  681. bool empty() const
  682. {
  683. return this->size()==0;
  684. }
  685. T const & back() const { RA_CHECK(this->rank()==1 && this->size()>0); return store[this->size()-1]; }
  686. T & back() { RA_CHECK(this->rank()==1 && this->size()>0); return store[this->size()-1]; }
  687. // Container is always compact/row-major. Then the 0-rank STL-like iterators can be raw pointers. TODO But .iter() should also be able to benefit from this constraint, and the check should be faster for some cases (like RANK==1) or ellidable.
  688. auto begin() { assert(is_c_order(*this)); return this->data(); }
  689. auto begin() const { assert(is_c_order(*this)); return this->data(); }
  690. auto end() { return this->data()+this->size(); }
  691. auto end() const { return this->data()+this->size(); }
  692. // conversions to scalar.
  693. operator T & () { return view(); }
  694. operator T const & () const { return view(); }
  695. };
  696. template <class Store, rank_t RANK>
  697. struct ra_traits_def<Container<Store, RANK>>
  698. : public ra_traits_def<typename Container<Store, RANK>::ViewTrue> {};
  699. template <class Store, rank_t RANK>
  700. void swap(Container<Store, RANK> & a, Container<Store, RANK> & b)
  701. {
  702. std::swap(a.dim, b.dim);
  703. std::swap(a.store, b.store);
  704. std::swap(a.p, b.p);
  705. }
  706. // Default storage for Big - see https://stackoverflow.com/a/21028912
  707. // Allocator adaptor that interposes construct() calls to convert value initialization into default initialization.
  708. template <typename T, typename A=std::allocator<T>>
  709. struct default_init_allocator: public A
  710. {
  711. using a_t = std::allocator_traits<A>;
  712. using A::A;
  713. template <typename U>
  714. struct rebind
  715. {
  716. using other = default_init_allocator<U, typename a_t::template rebind_alloc<U>>;
  717. };
  718. template <typename U>
  719. void construct(U * ptr) noexcept(std::is_nothrow_default_constructible<U>::value)
  720. {
  721. ::new(static_cast<void *>(ptr)) U;
  722. }
  723. template <typename U, typename... Args>
  724. void construct(U * ptr, Args &&... args)
  725. {
  726. a_t::construct(static_cast<A &>(*this), ptr, std::forward<Args>(args)...);
  727. }
  728. };
  729. // Beyond this, we probably should have fixed-size (~std::dynarray), resizeable (~std::vector).
  730. template <class T, rank_t RANK=RANK_ANY> using Big = Container<std::vector<T, default_init_allocator<T>>, RANK>;
  731. template <class T, rank_t RANK=RANK_ANY> using Unique = Container<std::unique_ptr<T []>, RANK>;
  732. template <class T, rank_t RANK=RANK_ANY> using Shared = Container<std::shared_ptr<T>, RANK>;
  733. // -------------
  734. // Used in the Guile wrappers to allow an array parameter to either borrow from Guile
  735. // storage or convert into a new array (e.g. passing 'f32 into 'f64).
  736. // TODO Can use unique_ptr's deleter for this?
  737. // TODO Shared/Unique should maybe have constructors with unique_ptr/shared_ptr args
  738. // -------------
  739. struct NullDeleter { template <class T> void operator()(T * p) {} };
  740. struct Deleter { template <class T> void operator()(T * p) { delete[] p; } };
  741. template <rank_t RANK, class T>
  742. Shared<T, RANK> shared_borrowing(View<T *, RANK> & raw)
  743. {
  744. Shared<T, RANK> a;
  745. a.dim = raw.dim;
  746. a.p = raw.p;
  747. a.store = std::shared_ptr<T>(raw.data(), NullDeleter());
  748. return a;
  749. }
  750. } // namespace ra