Skip to content

Commit 5882494

Browse files
committed
Fix rank/size for 0-size builtin arrays
These can't be used for much of anything, but at least don't return wrong values. * ra/base.hh (is_builtin): Include 0-size, 1-rank arrays here. (rank_s): Special case for 0-size arrays. * test/types.cc: Basic tests. * ra/arrays.hh (SmallArray): Fix constraint on { T ... } constructor. * test/small-0.cc: Add test.
1 parent 98c120d commit 5882494

File tree

5 files changed

+68
-51
lines changed

5 files changed

+68
-51
lines changed

ra/arrays.hh

Lines changed: 32 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -247,14 +247,13 @@ SmallArray<T, Dimv_, std::tuple<nested_args ...>>
247247

248248
constexpr SmallArray(ra::none_t) {}
249249
constexpr SmallArray() {}
250-
// T not is_scalar [ra44]
251-
constexpr SmallArray(T const & t) { std::ranges::fill(cp, t); }
250+
constexpr SmallArray(T const & t) { std::ranges::fill(cp, t); } // [ra44]
252251
// braces, row-major and nested
253-
constexpr SmallArray(T const & x0, std::convertible_to<T> auto const & ... x) requires (1!=rank())
252+
constexpr SmallArray(T const & x0, std::convertible_to<T> auto const & ... x) requires (1+sizeof...(x)==size()) // FIXME p1219??
254253
{
255254
view() = { static_cast<T>(x0), static_cast<T>(x) ... };
256255
}
257-
constexpr SmallArray(nested_args const & ... x) requires (1!=rank() || 1!=len(0)) { view() = { x ... }; } // FIXME p1219??
256+
constexpr SmallArray(nested_args const & ... x) requires (1!=rank() || 1!=len(0)) { view() = { x ... }; }
258257
constexpr SmallArray(auto const & x) { view() = x; }
259258
constexpr SmallArray(Iterator auto && x) { view() = RA_FW(x); }
260259
#define RA_ASSIGNOPS(OP) \
@@ -380,93 +379,82 @@ struct Container: public ViewBig<typename storage_traits<Store>::T *, R>
380379
store = storage_traits<Store>::create(filldimv(ra::iter(s), dimv));
381380
cp = storage_traits<Store>::data(store);
382381
}
383-
384-
// provided so that {} calls sharg constructor below.
385-
constexpr static auto zdims = std::apply([](auto ... i){ return std::array<Dim, (R==ANY)?1:R>{Dim{i*0, 1} ... }; }, mp::iota<(R==ANY)?1:R>{});
386-
constexpr Container(): View(zdims, nullptr) {}
387-
constexpr Container() requires (R==0): Container({}, none) {}
388-
constexpr Container(auto const & x): Container(ra::shape(x), none) { view() = x; }
389-
constexpr Container(braces<T, R> x) requires (R!=ANY): Container(braces_shape<T, R>(x), none) { view() = x; }
390-
#define RA_BRACES(N) constexpr Container(braces<T, N> x) requires (R==ANY): Container(braces_shape<T, N>(x), none) { view() = x; }
391-
RA_FE(RA_BRACES, 1, 2, 3, 4)
392-
#undef RA_BRACES
393-
394-
using sharg = decltype(shape(std::declval<View>().iter()));
395-
// sharg overloads handle {...} arguments. Size check is at conversion if sharg is Small.
396-
constexpr Container(sharg const & s, none_t) { init(s); }
397-
constexpr Container(sharg const & s, auto const & x): Container(s, none) { view() = x; }
398-
constexpr Container(sharg const & s, braces<T, R> x) requires (1==R): Container(s, none) { view() = x; }
399-
400382
// FIXME requires copyable Y, which conflicts with semantics of view_.operator=. store(x) avoids the conflict for Big, but not for Unique. Should construct in place like std::vector.
383+
// FIXME skip ravel-through-iterator if rank is 1.
401384
constexpr void
402385
fill1(auto && xbegin, dim_t xsize)
403386
{
404387
RA_CK(size()==xsize, "Mismatched sizes ", size(), " ", xsize, ".");
405388
std::ranges::copy_n(RA_FW(xbegin), xsize, begin());
406389
}
407390

408-
// shape + row-major ravel.
409-
// FIXME explicit it-is-ravel mark. Also iter<n> initializers.
410-
// FIXME regular (no need for fill1) for ANY if rank is 1.
411-
constexpr Container(sharg const & s, std::initializer_list<T> x) requires (1!=R): Container(s, none) { fill1(x.begin(), x.size()); }
412-
// FIXME remove these two
413-
constexpr Container(sharg const & s, auto * p): Container(s, none) { fill1(p, size()); } // FIXME fake check
414-
constexpr Container(sharg const & s, auto && pbegin, dim_t psize): Container(s, none) { fill1(RA_FW(pbegin), psize); }
415-
// for shape argument that doesn't convert implicitly to sharg
391+
constexpr static auto zdims = std::apply([](auto ... i){ return std::array<Dim, (R==ANY)?1:R>{Dim{i*0, 1} ... }; }, mp::iota<(R==ANY)?1:R>{});
392+
constexpr Container(): View(zdims, nullptr) {}
393+
constexpr Container() requires (R==0): Container({}, none) {}
394+
constexpr Container(auto const & x): Container(ra::shape(x), ra::none) { view() = x; }
395+
constexpr Container(braces<T, R> x) requires (R!=ANY): Container(braces_shape<T, R>(x), ra::none) { view() = x; }
396+
#define RA_BRACES(N) constexpr Container(braces<T, N> x) requires (R==ANY): Container(braces_shape<T, N>(x), ra::none) { view() = x; }
397+
RA_FE(RA_BRACES, 1, 2, 3, 4)
398+
#undef RA_BRACES
399+
// FIXME replace shape + row-major ravel by explicit it-is-ravel mark, also iter<n> initializers.
416400
constexpr Container(auto const & s, none_t) { init(s); }
417401
constexpr Container(auto const & s, auto const & x): Container(s, none) { view() = x; }
418-
constexpr Container(auto const & s, std::initializer_list<T> x): Container(s, none) { fill1(x.begin(), x.size()); }
402+
constexpr Container(auto const & s, braces<T, R> x) requires (1==R): Container(s, none) { view() = x; }
403+
constexpr Container(auto const & s, std::initializer_list<T> x) requires (1!=R): Container(s, none) { fill1(x.begin(), x.size()); }
404+
// sharg overloads handle {...} arguments.
405+
using sharg = decltype(shape(std::declval<View>()));
406+
constexpr Container(sharg const & s, none_t): Container(ra::iter(s), ra::none) {}
407+
constexpr Container(sharg const & s, auto const & x): Container(ra::iter(s), x) {}
408+
constexpr Container(sharg const & s, braces<T, R> x) requires (1==R): Container(ra::iter(s), x) {}
409+
constexpr Container(sharg const & s, std::initializer_list<T> x) requires (1!=R): Container(ra::iter(s), x) {}
410+
// FIXME remove these two
411+
constexpr Container(sharg const & s, auto * p): Container(ra::iter(s), none) { fill1(p, size()); }
412+
constexpr Container(sharg const & s, auto && pbegin, dim_t psize): Container(ra::iter(s), none) { fill1(RA_FW(pbegin), psize); }
419413

420414
// resize first axis or full shape. Only for some kinds of store.
421415
constexpr void resize(dim_t const s)
422416
{
423417
if constexpr (ANY==R) { RA_CK(0<rank()); } else { static_assert(0<=R); }
424-
dimv[0].len = s;
425-
store.resize(size()); cp = store.data();
418+
dimv[0].len = s; store.resize(size()); cp = store.data();
426419
}
427420
constexpr void resize(dim_t const s, T const & t)
428421
{
429422
static_assert(ANY==R || 0<R); RA_CK(0<rank());
430-
dimv[0].len = s;
431-
store.resize(size(), t); cp = store.data();
423+
dimv[0].len = s; store.resize(size(), t); cp = store.data();
432424
}
433425
constexpr void resize(auto const & s) requires (1==rank_s(s))
434426
{
435427
store.resize(filldimv(ra::iter(s), dimv)); cp = store.data();
436428
}
437-
// template + RA_FW wouldn't work for push_back(brace-enclosed-list).
429+
// auto && + RA_FW wouldn't work for push_back(brace-enclosed-list). p1219??
438430
constexpr void push_back(T && t)
439431
{
440432
if constexpr (ANY==R) { RA_CK(1==rank()); } else { static_assert(1==R); }
441-
store.push_back(std::move(t));
442-
++dimv[0].len; cp = store.data();
433+
store.push_back(std::move(t)); ++dimv[0].len; cp = store.data();
443434
}
444435
constexpr void push_back(T const & t)
445436
{
446437
if constexpr (ANY==R) { RA_CK(1==rank()); } else { static_assert(1==R); }
447-
store.push_back(t);
448-
++dimv[0].len; cp = store.data();
438+
store.push_back(t); ++dimv[0].len; cp = store.data();
449439
}
450440
constexpr void emplace_back(auto && ... a)
451441
{
452442
if constexpr (ANY==R) { RA_CK(1==rank()); } else { static_assert(1==R); }
453-
store.emplace_back(RA_FW(a) ...);
454-
++dimv[0].len; cp = store.data();
443+
store.emplace_back(RA_FW(a) ...); ++dimv[0].len; cp = store.data();
455444
}
456445
constexpr void pop_back()
457446
{
458447
if constexpr (ANY==R) { RA_CK(1==rank()); } else { static_assert(1==R); }
459448
RA_CK(0<dimv[0].len, "Empty array pop_back().");
460-
--dimv[0].len;
461-
store.pop_back();
449+
--dimv[0].len; store.pop_back();
462450
}
463451
constexpr decltype(auto) back(this auto && self) { return RA_FW(self).view().back(); }
464452
constexpr decltype(auto) operator()(this auto && self, auto && ... i) { return RA_FW(self).view()(RA_FW(i) ...); }
465453
constexpr decltype(auto) operator[](this auto && self, auto && ... i) { return RA_FW(self).view()(RA_FW(i) ...); }
466454
constexpr decltype(auto) at(this auto && self, auto const & i) { return RA_FW(self).view().at(i); }
467455
constexpr auto begin(this auto && self) { assert(c_order(self.view().dimv)); return self.view().data(); }
468456
constexpr auto end(this auto && self) { return self.view().data()+self.size(); }
469-
template <rank_t c=0> constexpr auto iter(this auto && self) { if constexpr (1==R && 0==c) { return ptr(self.data(), self.size()); } else { return RA_FW(self).view().template iter<c>(); } }
457+
template <rank_t c=0> constexpr auto iter(this auto && self) { if constexpr (1==R && 0==c) return ptr(self.data(), self.size()); else return RA_FW(self).view().template iter<c>(); }
470458
constexpr auto iter(this auto && self, rank_t c) { return RA_FW(self).view().iter(c); }
471459
constexpr operator T & () { return view(); }
472460
constexpr operator T const & () const { return view(); }

ra/base.hh

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -316,8 +316,7 @@ constexpr bool inside(dim_t i, dim_t b) { return 0<=i && i<b; }
316316
constexpr bool any(bool const x) { return x; } // extended in ra.hh
317317
constexpr bool every(bool const x) { return x; }
318318

319-
// adaptor that inserts construct() calls to convert value init into default init. See https://stackoverflow.com/a/21028912
320-
// default storage for Big.
319+
// convert value init into default init (https://stackoverflow.com/a/21028912). Default storage for Big.
321320
template <class T, class A=std::allocator<T>>
322321
struct default_init_allocator: public A
323322
{
@@ -400,18 +399,19 @@ template <> constexpr bool is_scalar_def<std::partial_ordering> = true;
400399

401400
RA_IS_DEF(is_iterator, Iterator<A>)
402401
template <class A> concept is_ra = is_iterator<A> || Slice<A>;
403-
template <class A> concept is_builtin = std::is_array_v<std::remove_cvref_t<A>>;
402+
template <class A> concept is_builtin_0 = requires (A & v) { []<class T>(T (&x)[0]){}(v); };
403+
template <class A> concept is_builtin = std::is_array_v<std::remove_cvref_t<A>> || is_builtin_0<A>;
404404
RA_IS_DEF(is_fov, !is_scalar<A> && !is_ra<A> && !is_builtin<A> && std::ranges::bidirectional_range<A>)
405405

406406
template <class VV> requires (!std::is_void_v<VV>)
407407
consteval rank_t
408408
rank_s()
409409
{
410410
using V = std::remove_cvref_t<VV>;
411-
if constexpr (is_builtin<V>) {
412-
return std::rank_v<V>;
413-
} else if constexpr (is_fov<V>) {
411+
if constexpr (is_builtin_0<V> || is_fov<V>) {
414412
return 1;
413+
} else if constexpr (is_builtin<V>) {
414+
return std::rank_v<V>;
415415
} else if constexpr (requires { V::rank(); }) {
416416
return V::rank();
417417
} else if constexpr (requires (V v) { v.rank(); }) {

test/ra-14.cc

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,11 @@ int main()
2121
using int2 = ra::Small<int, 2>;
2222
tr.section("Values");
2323
{
24+
tr.section("Rank 0 constructor");
25+
{
26+
ra::Big<int> a({}, ra::none);
27+
tr.test_eq(0, rank(a));
28+
}
2429
tr.section("From var rank 0 [ra15]");
2530
{
2631
ra::Big<int2> a({}, {{ 1, 2 }});

test/small-0.cc

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -565,5 +565,15 @@ int main()
565565
cout << c << endl;
566566
cout << d << endl;
567567
}
568+
tr.section("constructor from cells");
569+
{
570+
ra::Small<int, 4> a { 1, 2, 3, 4 };
571+
ra::Small<int, 4> b { 3, 1, 4, 2 };
572+
ra::Small<int, 3, 4> ref { { 1, 2, 3, 4 }, { 3, 1, 4, 2 }, { 1, 2, 3, 4 } };
573+
ra::Small<int, 3, 4> c { a, b, a.view() };
574+
tr.test_eq(ref, c);
575+
auto d = [](auto && a, auto && b, auto && c) { return ra::Small<int, 3, 4> { a, b, c }; }(a, b, a.view());
576+
tr.test_eq(ref, d);
577+
}
568578
return tr.summary();
569579
}

test/types.cc

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
// -*- mode: c++; coding: utf-8 -*-
22
// ra-ra/test - Show what types conform to what type predicates.
33

4-
// (c) Daniel Llorens - 2016-2017
4+
// (c) Daniel Llorens - 2016-2026
55
// This library is free software; you can redistribute it and/or modify it under
66
// the terms of the GNU Lesser General Public License as published by the Free
77
// Software Foundation; either version 3 of the License, or (at your option) any
@@ -257,5 +257,19 @@ int main()
257257
std::println(std::cout, "no float128_t support!");
258258
#endif // RA_FLOAT128
259259
}
260+
tr.section("zero rank arrays");
261+
{
262+
static_assert(ra::is_builtin<decltype((ra::dim_t[1]){})>);
263+
static_assert(ra::is_builtin<decltype((ra::dim_t[0]){})>);
264+
static_assert(1==ra::rank_s((ra::dim_t[1]){}));
265+
static_assert(1==ra::rank_s((ra::dim_t[0]){}));
266+
static_assert(1==ra::size_s((ra::dim_t[1]){}));
267+
static_assert(0==ra::size_s((ra::dim_t[0]){}));
268+
int z[0];
269+
static_assert(1==ra::rank_s(z));
270+
static_assert(0==ra::size_s(z));
271+
static_assert(1==ra::rank(z));
272+
static_assert(0==ra::size(z));
273+
}
260274
return tr.summary();
261275
}

0 commit comments

Comments
 (0)