big.H 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604
  1. // (c) Daniel Llorens - 2013-2014
  2. // This library is free software; you can redistribute it and/or modify it under
  3. // the terms of the GNU Lesser General Public License as published by the Free
  4. // Software Foundation; either version 3 of the License, or (at your option) any
  5. // later version.
  6. /// @file big.H
  7. /// @brief Arrays with dynamic size.
  8. #pragma once
  9. #include "ra/small.H"
  10. #include "ra/iterator.H"
  11. #include "ra/wrank.H"
  12. #include <complex>
  13. #include <memory>
  14. #ifdef RA_CHECK_BOUNDS
  15. #define RA_CHECK_BOUNDS_RA_LARGE RA_CHECK_BOUNDS
  16. #else
  17. #ifndef RA_CHECK_BOUNDS_RA_LARGE
  18. #define RA_CHECK_BOUNDS_RA_LARGE 1
  19. #endif
  20. #endif
  21. #if RA_CHECK_BOUNDS_RA_LARGE==0
  22. #define CHECK_BOUNDS( cond )
  23. #else
  24. #define CHECK_BOUNDS( cond ) assert( cond )
  25. #endif
  26. namespace ra {
  27. // Always C order. If you need another, transpose this.
  28. template <class S, class D>
  29. dim_t filldim(S sbegin, S send, D dbegin, D dend)
  30. {
  31. dim_t next = 1;
  32. while (send>sbegin) {
  33. --send;
  34. --dend;
  35. assert(*send>=0);
  36. (*dend).size = *send;
  37. (*dend).stride = next;
  38. next *= *send;
  39. }
  40. return next;
  41. }
  42. template <class D>
  43. dim_t proddim(D d, D dend)
  44. {
  45. dim_t t = 1;
  46. for (; d!=dend; ++d) {
  47. t *= (*d).size;
  48. }
  49. return t;
  50. }
  51. // raw <- shared; raw <- unique; shared <-- unique.
  52. // layout is
  53. // [data] (fixed shape)
  54. // [size] p -> [data...] (fixed rank)
  55. // [rank] [size...] p -> [data...] (var rank)
  56. // TODO size is immutable so that it can be kept together with rank.
  57. // Would do this with a base class, but I like View being POD.
  58. // TODO In C++17 begin() end() may be different types. See if we can use this to simplify end() and !=end() test.
  59. #define DEF_ITERATORS(RANK) \
  60. template <rank_t c=0> using iterator = ra::ra_iterator<View<T, RANK>, c>; \
  61. template <rank_t c=0> using const_iterator = ra::ra_iterator<View<T const, RANK>, c>; \
  62. template <rank_t c=0> auto iter() { return iterator<c>(dim, p); } \
  63. template <rank_t c=0> auto iter() const { return const_iterator<c>(dim, p); } \
  64. auto begin() { return stl_iterator<iterator<0>>(dim, p); } \
  65. auto begin() const { return stl_iterator<const_iterator<0>>(dim, p); } \
  66. auto end() { return stl_iterator<iterator<0>> {}; } \
  67. auto end() const { return stl_iterator<const_iterator<0>> {}; }
  68. template <class I> constexpr bool is_initializer_list = false;
  69. template <class T> constexpr bool is_initializer_list<std::initializer_list<T> > = true;
  70. template <rank_t k, class T> struct ranked_ilist_def;
  71. template <rank_t k, class T> using ranked_ilist = typename ranked_ilist_def<k, T>::type;
  72. template <rank_t k, class T> struct ranked_ilist_def
  73. {
  74. static_assert(k>=0);
  75. using type = std::initializer_list<ranked_ilist<k-1, T>>;
  76. };
  77. template <class T> struct ranked_ilist_def<0, T> { using type = T; };
  78. template <class T> struct ranked_ilist_def<RANK_ANY, T> { using type = ranked_ilist<1, T>; };
  79. // See same thing for SmallBase.
  80. #define DEF_ASSIGNOPS(OP) \
  81. template <class X> View & operator OP (X && x) \
  82. { \
  83. for_each([](T & y, T const & x) { y OP x; }, *this, x); \
  84. return *this; \
  85. }
  86. #define DEF_ASSIGNMENT_OPS \
  87. FOR_EACH(DEF_ASSIGNOPS, =, *=, +=, -=, /=) \
  88. /* These three are unsafe */ \
  89. View(): p(nullptr) {} \
  90. View(Dimv const & dim_, T * p_): dim(dim_), p(p_) {} \
  91. View(std::initializer_list<dim_t> const s, T * p_): p(p_) \
  92. { \
  93. ra::resize(View::dim, s.size()); \
  94. filldim(s.begin(), s.end(), View::dim.begin(), View::dim.end()); \
  95. } \
  96. template <class SS> View(SS const & s, T * p_): p(p_) \
  97. { \
  98. ra::resize(View::dim, s.size()); \
  99. filldim(s.begin(), s.end(), View::dim.begin(), View::dim.end()); \
  100. } \
  101. View(View && x): dim(std::move(x.dim)), p(x.p) {} \
  102. View(View const & x): dim(x.dim), p(x.p) {} \
  103. View & operator=(View const & x) \
  104. { \
  105. for_each([](T & dest, T const & src) { dest = src; }, *this, x); \
  106. return *this; \
  107. } \
  108. /* Ignore rank, row-major fill. TODO Use xpr traversal */ \
  109. View & operator=(std::initializer_list<T> const x) \
  110. { \
  111. assert(p && this->size()==ra::dim_t(x.size()) && "bad assignment"); \
  112. std::copy(x.begin(), x.end(), this->begin()); \
  113. return *this; \
  114. } \
  115. bool const empty() const { return size()==0; } /* TODO Optimize */
  116. inline dim_t select(Dim * dim, Dim const * dim_src, dim_t i)
  117. {
  118. CHECK_BOUNDS(inside(i, dim_src->size));
  119. return dim_src->stride*i;
  120. }
  121. template <class II>
  122. inline dim_t select(Dim * dim, Dim const * dim_src, ra::Iota<II> i)
  123. {
  124. CHECK_BOUNDS((inside(i.org_, dim_src->size) && inside(i.org_+(i.size_-1)*i.stride_, dim_src->size))
  125. || (i.size_==0 && i.org_<=dim_src->size));
  126. dim->size = i.size_;
  127. dim->stride = dim_src->stride * i.stride_;
  128. return dim_src->stride*i.org_;
  129. }
  130. template <class I0, class ... I>
  131. inline dim_t select_loop(Dim * dim, Dim const * dim_src, I0 && i0, I && ... i)
  132. {
  133. return select(dim, dim_src, std::forward<I0>(i0))
  134. + select_loop(dim+is_beatable<I0>::skip, dim_src+is_beatable<I0>::skip_src, std::forward<I>(i) ...);
  135. }
  136. template <int n, class ... I>
  137. inline dim_t select_loop(Dim * dim, Dim const * dim_src, dots_t<n> dots, I && ... i)
  138. {
  139. for (Dim * end = dim+n; dim!=end; ++dim, ++dim_src) {
  140. *dim = *dim_src;
  141. }
  142. return select_loop(dim, dim_src, std::forward<I>(i) ...);
  143. }
  144. template <int n, class ... I>
  145. inline dim_t select_loop(Dim * dim, Dim const * dim_src, newaxis_t<n> dots, I && ... i)
  146. {
  147. for (Dim * end = dim+n; dim!=end; ++dim) {
  148. dim->size = 1;
  149. dim->stride = 0;
  150. }
  151. return select_loop(dim, dim_src, std::forward<I>(i) ...);
  152. }
  153. inline dim_t select_loop(Dim * dim, Dim const * dim_src)
  154. {
  155. return 0;
  156. }
  157. // TODO Parameterize on Child having .data() so that there's only one pointer.
  158. // TODO A constructor, if only for CHECK_BOUNDS (positive sizes, strides inside, etc.)
  159. template <class T, rank_t RANK>
  160. struct View
  161. {
  162. using Dimv = Small<Dim, RANK>;
  163. Dimv dim;
  164. T * p;
  165. constexpr static rank_t rank() { return RANK; }
  166. constexpr static rank_t rank_s() { return RANK; };
  167. constexpr dim_t size() const { return proddim(dim.begin(), dim.end()); }
  168. constexpr static dim_t size_s() { return DIM_ANY; }
  169. constexpr dim_t size(int j) const { return dim[j].size; }
  170. constexpr dim_t stride(int j) const { return dim[j].stride; }
  171. constexpr auto data() { return p; }
  172. constexpr auto data() const { return p; }
  173. // Specialize for rank() integer-args -> scalar, same in ra::SmallBase in small.H.
  174. #define SUBSCRIPTS(CONST) \
  175. template <class ... I, \
  176. std::enable_if_t<((0 + ... + std::is_integral<I>::value)<RANK \
  177. && (0 + ... + is_beatable<I>::value)==sizeof...(I)), int> = 0> \
  178. auto operator()(I const & ... i) CONST \
  179. { \
  180. constexpr rank_t extended = (0 + ... + (is_beatable<I>::skip-is_beatable<I>::skip_src)); \
  181. constexpr rank_t subrank = rank_sum(RANK, extended); \
  182. static_assert(subrank>=0, "bad subrank"); \
  183. View<T CONST, subrank> sub; \
  184. sub.p = data() + select_loop(sub.dim.data(), this->dim.data(), i ...); \
  185. /* fill the rest of dim, skipping over beatable subscripts */ \
  186. for (int i=(0 + ... + is_beatable<I>::skip); i<subrank; ++i) { \
  187. sub.dim[i] = this->dim[i-extended]; \
  188. } \
  189. return sub; \
  190. } \
  191. /* BUG doesn't handle generic zero-rank indices */ \
  192. template <class ... I, std::enable_if_t<(0 + ... + std::is_integral<I>::value)==RANK, int> = 0> \
  193. decltype(auto) operator()(I const & ... i) CONST \
  194. { \
  195. return data()[select_loop(nullptr, this->dim.data(), i ...)]; \
  196. } \
  197. /* TODO More than one selector... This still covers (unbeatable, integer) for example, which could be reduced. */ \
  198. template <class ... I, std::enable_if_t<!(is_beatable<I>::value && ...), int> = 0> \
  199. auto operator()(I && ... i) CONST \
  200. { \
  201. return from(*this, std::forward<I>(i) ...); \
  202. } \
  203. template <class ... I> \
  204. auto wipparens(I && ... i) CONST; \
  205. template <class I> \
  206. auto at(I const & i) CONST \
  207. { \
  208. using Sub = View<T CONST, rank_diff(RANK, I::size_s())>; /* gcc accepts i.size() */ \
  209. return Sub { typename Sub::Dimv(dim.begin()+i.size(0), dim.end()), \
  210. data() + Indexer1::index_p(dim, i) }; \
  211. } \
  212. T CONST & operator[](dim_t const i) CONST \
  213. { \
  214. static_assert(rank()==1, "bad rank"); \
  215. CHECK_BOUNDS(inside(i, dim[0].size)); \
  216. return data()[i*dim[0].stride]; \
  217. }
  218. SUBSCRIPTS(const)
  219. SUBSCRIPTS(/*const*/)
  220. #undef SUBSCRIPTS
  221. DEF_ITERATORS(RANK)
  222. DEF_ASSIGNMENT_OPS
  223. operator T & () { static_assert(RANK==0, "bad rank"); return data()[0]; }
  224. operator T const & () const { static_assert(RANK==0, "bad rank"); return data()[0]; }
  225. };
  226. // TODO To be placed as specialization of operator() above.
  227. template <class T, rank_t RANK>
  228. template <class ... I>
  229. auto View<T, RANK>::wipparens(I && ... i) /* const */
  230. {
  231. }
  232. template <class T, rank_t RANK>
  233. struct ra_traits_def<View<T, RANK>>
  234. {
  235. using V = View<T, RANK>;
  236. using value_type = T;
  237. using shape_type = Small<dim_t, RANK>; // TODO should be taken from v.iter().shape()
  238. static auto shape(V const & v) { return v.iter().shape(); }
  239. static dim_t size(V const & v) { return v.size(); }
  240. constexpr static rank_t rank(V const & v) { return v.rank(); }
  241. constexpr static rank_t rank_s() { return RANK; };
  242. constexpr static dim_t size_s() { return RANK==0 ? 1 : DIM_ANY; }
  243. };
  244. // Storage types.
  245. template <class V> struct storage_traits
  246. {
  247. using T = std::decay_t<decltype(*std::declval<V>().get())>;
  248. static V create(dim_t n) { return V(new T[n]); }
  249. static T const * data(V const & v) { return v.get(); }
  250. static T * data(V & v) { return v.get(); }
  251. };
  252. template <class T> struct storage_traits<std::vector<T>>
  253. {
  254. // BUG This uses T(), so would need a create(, T) ...
  255. static std::vector<T> create(dim_t n) { return std::vector<T>(n); }
  256. // BUG No such for std::vector<bool>
  257. static T const * data(std::vector<T> const & v) { return v.data(); }
  258. static T * data(std::vector<T> & v) { return v.data(); }
  259. };
  260. template <class T, rank_t RANK> inline
  261. bool is_c_order(View<T, RANK> const & a)
  262. {
  263. dim_t s = 1;
  264. for (int i=a.rank()-1; i>=0; --i) {
  265. if (s!=a.stride(i)) {
  266. return false;
  267. }
  268. s *= a.size(i);
  269. if (s==0) {
  270. return true;
  271. }
  272. }
  273. return true;
  274. }
  275. // TODO be convertible to View only, so that View::p is not duplicated
  276. template <class Store, rank_t RANK>
  277. struct WithStorage: public View<std::decay_t<decltype(*storage_traits<Store>::data(std::declval<Store>()))>, RANK>
  278. {
  279. Store store;
  280. using T = std::decay_t<decltype(*storage_traits<Store>::data(store))>;
  281. using View = ra::View<T, RANK>;
  282. using View::rank_s;
  283. // 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.
  284. WithStorage(WithStorage && w): store(std::move(w.store))
  285. {
  286. View::dim = std::move(w.dim);
  287. View::p = storage_traits<Store>::data(store);
  288. }
  289. WithStorage(WithStorage const & w): store(w.store)
  290. {
  291. View::dim = w.dim;
  292. View::p = storage_traits<Store>::data(store);
  293. }
  294. WithStorage(WithStorage & w): store(w.store)
  295. {
  296. View::dim = w.dim;
  297. View::p = storage_traits<Store>::data(store);
  298. }
  299. // Override View::operator= to allow initialization-of-reference. Unfortunately operator>>(std::istream &, WithStorage &) 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.C [ra20].
  300. // TODO do this through .replace() op.
  301. // TODO don't require copiable T from constructors, see fill1 below. That requires initialization and not update semantics for operator=.
  302. WithStorage & operator=(WithStorage && w)
  303. {
  304. store = std::move(w.store);
  305. View::dim = std::move(w.dim);
  306. View::p = storage_traits<Store>::data(store);
  307. return *this;
  308. }
  309. WithStorage & operator=(WithStorage const & w)
  310. {
  311. store = w.store;
  312. View::dim = w.dim;
  313. View::p = storage_traits<Store>::data(store);
  314. return *this;
  315. }
  316. WithStorage & operator=(WithStorage & w)
  317. {
  318. store = w.store;
  319. View::dim = w.dim;
  320. View::p = storage_traits<Store>::data(store);
  321. return *this;
  322. }
  323. // Provided b/c want {} to call initializer_list constructor below. TODO Why do I need to init p; warning on test-io.C.
  324. WithStorage()
  325. {
  326. for (Dim & dimi: View::dim) { dimi = {0, 1}; } // 1 so we can push_back()
  327. }
  328. template <class SS> void init(SS const & s)
  329. {
  330. constexpr dim_t sr = ra_traits<decltype(s)>::size_s();
  331. static_assert(sr==RANK || sr==DIM_ANY || RANK==RANK_ANY, "rank mismatch for init shape");
  332. ra::resize(View::dim, s.size());
  333. dim_t t = filldim(s.begin(), s.end(), View::dim.begin(), View::dim.end());
  334. store = storage_traits<Store>::create(t);
  335. View::p = storage_traits<Store>::data(store);
  336. }
  337. template <class Pbegin> void fill1(dim_t xsize, Pbegin xbegin)
  338. {
  339. CHECK_BOUNDS(this->size()==xsize && "mismatched sizes");
  340. std::copy_n(xbegin, xsize, this->begin()); // TODO Use xpr traversal.
  341. }
  342. template <class SS> WithStorage(SS const & s, unspecified_t) { init(s); }
  343. WithStorage(std::initializer_list<dim_t> const s, unspecified_t) { init(s); }
  344. template <class SS, class XX> WithStorage(SS const & s, XX && x): WithStorage(s, unspecified)
  345. {
  346. static_cast<View &>(*this) = x;
  347. }
  348. template <class XX> WithStorage(std::initializer_list<dim_t> const s, XX && x): WithStorage(s, unspecified)
  349. {
  350. static_cast<View &>(*this) = x;
  351. }
  352. template <class XX> WithStorage(XX && x): WithStorage(start(x).shape(), unspecified)
  353. {
  354. static_cast<View &>(*this) = x;
  355. }
  356. // FIXME use of fill1 requires T to be copiable, this is unfortunate as it conflicts with the semantics of View::operator=.
  357. // store(x) avoids it for Big, but doesn't work for Unique. Should construct in place as std::vector does.
  358. WithStorage(std::initializer_list<T> const x): WithStorage({dim_t(x.size())}, unspecified)
  359. {
  360. static_assert(RANK==RANK_ANY || RANK==1, "rank mismatch with init list (of rank 1)");
  361. fill1(x.size(), x.begin());
  362. }
  363. // TODO Replace these rank 1 -> rank N constructors by explicit reshape. See also small.H.
  364. WithStorage(std::initializer_list<dim_t> const s, std::initializer_list<T> const x): WithStorage(s, unspecified)
  365. {
  366. fill1(x.size(), x.begin());
  367. }
  368. template <class SS> WithStorage(SS const & s, std::initializer_list<T> const x): WithStorage(s, unspecified)
  369. {
  370. fill1(x.size(), x.begin());
  371. }
  372. template <class TT>
  373. WithStorage(std::initializer_list<dim_t> const s, TT * p): WithStorage(s, unspecified)
  374. {
  375. fill1(this->size(), p);
  376. }
  377. template <class P>
  378. WithStorage(std::initializer_list<dim_t> const s, P pbegin, P pend): WithStorage(s, unspecified)
  379. {
  380. fill1(this->size(), pbegin);
  381. }
  382. using View::operator=;
  383. // only for some kinds of store.
  384. void resize(dim_t const s)
  385. {
  386. assert(this->rank()>0);
  387. store.resize(proddim(View::dim.begin()+1, View::dim.end())*s /* std::unspecified_t someday... */);
  388. View::dim[0].size = s;
  389. View::p = store.data();
  390. }
  391. void resize(dim_t const s, T const & t)
  392. {
  393. assert(this->rank()>0);
  394. store.resize(proddim(View::dim.begin()+1, View::dim.end())*s, t);
  395. View::dim[0].size = s;
  396. View::p = store.data();
  397. }
  398. // lets us move. A template + std::forward wouldn't work for push_back(brace-enclosed-list).
  399. void push_back(T && t)
  400. {
  401. assert(this->rank()==1);
  402. store.push_back(std::move(t));
  403. ++View::dim[0].size;
  404. View::p = store.data();
  405. }
  406. void push_back(T const & t)
  407. {
  408. assert(this->rank()==1);
  409. store.push_back(t);
  410. ++View::dim[0].size;
  411. View::p = store.data();
  412. }
  413. template <class ... A>
  414. void emplace_back(A && ... a)
  415. {
  416. assert(this->rank()==1);
  417. store.emplace_back(std::forward<A>(a) ...);
  418. ++View::dim[0].size;
  419. View::p = store.data();
  420. }
  421. void pop_back()
  422. {
  423. assert(this->rank()==1 && View::dim[0].size>0);
  424. store.pop_back();
  425. --View::dim[0].size;
  426. }
  427. bool empty() const
  428. {
  429. return this->size()==0;
  430. }
  431. T const & back() const { assert(this->rank()==1 && "bad rank for back"); return store[this->size()-1]; }
  432. T & back() { assert(this->rank()==1 && "bad rank for back"); return store[this->size()-1]; }
  433. // WithStorage 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 elidable.
  434. auto begin() { assert(is_c_order(*this)); return this->data(); }
  435. auto begin() const { assert(is_c_order(*this)); return this->data(); }
  436. auto end() { return this->data()+this->size(); }
  437. auto end() const { return this->data()+this->size(); }
  438. };
  439. template <class Store, rank_t RANK>
  440. void swap(WithStorage<Store, RANK> & a, WithStorage<Store, RANK> & b)
  441. {
  442. std::swap(a.dim, b.dim);
  443. std::swap(a.store, b.store);
  444. std::swap(a.p, b.p);
  445. }
  446. // Beyond this, we probably should have fixed-size (~std::dynarray), resizeable (~std::vector).
  447. template <class T, rank_t RANK=RANK_ANY> using Unique = WithStorage<std::unique_ptr<T []>, RANK>;
  448. template <class T, rank_t RANK=RANK_ANY> using Big = WithStorage<std::vector<T>, RANK>;
  449. template <class T, rank_t RANK=RANK_ANY> using Shared = WithStorage<std::shared_ptr<T>, RANK>;
  450. // -------------
  451. // Used in the Guile wrappers to allow an array parameter to either borrow from Guile
  452. // storage or convert into a new array (e.g. passing 'f32 into 'f64).
  453. // TODO Can use unique_ptr's deleter for this?
  454. // TODO Shared/Unique should maybe have constructors with unique_ptr/shared_ptr args
  455. // -------------
  456. struct NullDeleter { template <class T> void operator()(T * p) {} };
  457. struct Deleter { template <class T> void operator()(T * p) { delete[] p; } };
  458. template <rank_t RANK, class T>
  459. Shared<T, RANK> shared_borrowing(View<T, RANK> & raw)
  460. {
  461. Shared<T, RANK> a;
  462. a.dim = raw.dim;
  463. a.p = raw.p;
  464. a.store = std::shared_ptr<T>(raw.data(), NullDeleter());
  465. return a;
  466. }
  467. template <class T>
  468. struct View<T, RANK_ANY>
  469. {
  470. using Dimv = std::vector<Dim>; // maybe use Unique<Dim, 1> here.
  471. Dimv dim;
  472. T * p;
  473. constexpr rank_t rank() const { return rank_t(dim.size()); }
  474. constexpr static rank_t rank_s() { return RANK_ANY; }
  475. constexpr dim_t size() const { return proddim(dim.begin(), dim.end()); }
  476. constexpr static dim_t size_s() { return DIM_ANY; }
  477. constexpr dim_t size(rank_t const j) const { return dim[j].size; }
  478. constexpr dim_t stride(rank_t const j) const { return dim[j].stride; }
  479. constexpr auto data() { return p; }
  480. constexpr auto data() const { return p; }
  481. // 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.
  482. #define SUBSCRIPTS(CONST) \
  483. template <class ... I, std::enable_if_t<(is_beatable<I>::value && ...), int> = 0> \
  484. auto operator()(I const & ... i) CONST \
  485. { \
  486. constexpr rank_t extended = (0 + ... + (is_beatable<I>::skip-is_beatable<I>::skip_src)); \
  487. assert(this->rank()+extended>=0); \
  488. View<T CONST, RANK_ANY> sub; \
  489. sub.dim.resize(this->rank()+extended); \
  490. sub.p = data() + select_loop(sub.dim.data(), this->dim.data(), i ...); \
  491. for (int i=(0 + ... + is_beatable<I>::skip); i<sub.rank(); ++i) { \
  492. sub.dim[i] = this->dim[i-extended]; \
  493. } \
  494. return sub; \
  495. } \
  496. /* TODO More than one selector... */ \
  497. template <class ... I, std::enable_if_t<!(is_beatable<I>::value && ...), int> = 0> \
  498. auto operator()(I && ... i) CONST \
  499. { \
  500. return from(*this, std::forward<I>(i) ...); \
  501. } \
  502. template <class I> \
  503. auto at(I const & i) CONST \
  504. { \
  505. return View<T CONST, RANK_ANY> { Dimv(dim.begin()+i.size(), dim.end()), \
  506. data() + Indexer1::index_p(dim, i) }; \
  507. } \
  508. T CONST & operator[](dim_t const i) CONST \
  509. { \
  510. CHECK_BOUNDS(rank()==1 && "bad rank"); \
  511. CHECK_BOUNDS(inside(i, dim[0].size)); \
  512. return data()[i*dim[0].stride]; \
  513. }
  514. SUBSCRIPTS(const)
  515. SUBSCRIPTS(/*const*/)
  516. #undef SUBSCRIPTS
  517. DEF_ITERATORS(RANK_ANY)
  518. DEF_ASSIGNMENT_OPS
  519. template <rank_t SRANK> View(View<T, SRANK> const & x): dim(x.dim.begin(), x.dim.end()), p(x.p) {}
  520. operator T & () { assert(rank()==0); return data()[0]; };
  521. operator T const & () const { assert(rank()==0); return data()[0]; };
  522. template <rank_t R> operator View<T, R> ()
  523. {
  524. return View<T, R>(typename View<T, R>::Dimv(dim), data());
  525. }
  526. template <rank_t R> operator View<T const, R> () const
  527. {
  528. return View<T const, R>(typename View<T const, R>::Dimv(dim), data());
  529. }
  530. };
  531. #undef DEF_ITERATORS
  532. #undef DEF_ASSIGNMENT_OPS
  533. #undef DEF_ASSIGNOPS
  534. template <class T>
  535. struct ra_traits_def<View<T, RANK_ANY>>
  536. {
  537. using V = View<T, RANK_ANY>;
  538. using value_type = T;
  539. using shape_type = std::vector<dim_t>; // TODO should be taken from v.iter().shape()
  540. static auto shape(V const & v) { return v.iter().shape(); }
  541. static dim_t size(V const & v) { return v.size(); }
  542. static rank_t rank(V const & v) { return v.rank(); }
  543. constexpr static rank_t rank_s() { return RANK_ANY; };
  544. constexpr static rank_t size_s() { return DIM_ANY; }
  545. };
  546. template <class Store, rank_t RANK>
  547. struct ra_traits_def<WithStorage<Store, RANK>>
  548. : public ra_traits_def<View<typename WithStorage<Store, RANK>::T, RANK>> {};
  549. } // namespace ra::
  550. #undef CHECK_BOUNDS
  551. #undef RA_CHECK_BOUNDS_RA_LARGE