Skip to content
Snippets Groups Projects
Commit fbb0d4e6 authored by patavirt's avatar patavirt
Browse files

array: boundscheck as template param + add some doc

parent 205aca8f
No related branches found
No related tags found
No related merge requests found
/* usadelndsoc
*
* Copyright © 2022 Pauli Virtanen
* @author Pauli Virtanen <pauli.t.virtanen@jyu.fi>
*
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
#ifndef ARRAY_HPP_
#define ARRAY_HPP_
......@@ -11,7 +18,14 @@ namespace array {
constexpr size_t Dynamic = SIZE_MAX;
/*! Array dimensions and strides: known at compile-time & dynamic
/**
* \class Shape
*
* \brief Array dimensions and strides. Either known at compile-time or dynamic.
*
* The Shape template keeps track of the dimensions and strides of a
* N-dimensional row-major array, with the shape given in its template argument
* pack.
*/
template <size_t...>
struct Shape;
......@@ -70,7 +84,12 @@ struct Shape<Dim0,Dims...>
namespace detail
{
/*! Extract Nth tail shape
/** \class TailNth
*
* \brief Extract Nth tail shape.
*
* Helper template class to convert e.g. Shape<1,2,3,4,5>
* to its 2-nd tail Shape<3,4,5>.
*/
template <size_t I, typename Shape>
struct TailNth;
......@@ -87,18 +106,24 @@ namespace detail
using type = typename TailNth<I-1, typename Shape::Tail>::type;
};
/*! Base class for compile-time fixed size arrays
/**
* \class fixed_base
*
* \brief Base class for compile-time fixed size arrays
*
* The shape and stride information for arrays with known size. It is kept
* fully in compile-time quantities.
*/
template <typename Shape>
template <typename Shape, bool BoundsCheck=true>
class fixed_base
{
protected:
void data_bounds_check(size_t size) const
{
#ifndef NO_BOUNDS_CHECK
if (Shape::size > size)
throw std::out_of_range("data array too small");
#endif
if constexpr(BoundsCheck) {
if (Shape::size > size)
throw std::out_of_range("data array too small");
}
}
public:
......@@ -112,28 +137,35 @@ namespace detail
constexpr size_t size() const { return Shape::size; }
};
/*! Base class for compile-time dynamic size arrays
/**
* \class dynamic_base
*
* \brief Base class for compile-time dynamic size arrays
*
* Shape and stride information for dynamic arrays, stored in member
* variables.
*/
template <typename Shape>
template <typename Shape, bool BoundsCheck=true>
class dynamic_base
{
protected:
private:
std::array<size_t, Shape::ndim> shape_;
std::array<size_t, Shape::ndim> strides_;
protected:
void data_bounds_check(size_t size) const
{
#ifndef NO_BOUNDS_CHECK
size_t total = 1;
for (size_t i = 0; i < Shape::ndim; ++i) {
total *= dim(i);
if (shape_[i] != dim(i))
throw std::out_of_range("mismatch with fixed shape");
if constexpr(BoundsCheck) {
size_t total = 1;
for (size_t i = 0; i < Shape::ndim; ++i) {
total *= dim(i);
if (shape_[i] != dim(i))
throw std::out_of_range("mismatch with fixed shape");
}
if (total > size)
throw std::out_of_range("data array too small");
}
if (total > size)
throw std::out_of_range("data array too small");
#endif
}
public:
......@@ -152,20 +184,20 @@ namespace detail
size_t dim(size_t axis) const
{
#ifndef NO_BOUNDS_CHECK
if (axis >= Shape::ndim)
throw std::out_of_range("array axis must be < ndim");
#endif
if constexpr(BoundsCheck) {
if (axis >= Shape::ndim)
throw std::out_of_range("array axis must be < ndim");
}
size_t n = Shape::dim(axis);
return n == Dynamic ? shape_[axis] : n;
}
size_t stride(size_t axis) const
{
#ifndef NO_BOUNDS_CHECK
if (axis >= Shape::ndim)
throw std::out_of_range("array axis must be < ndim");
#endif
if constexpr(BoundsCheck) {
if (axis >= Shape::ndim)
throw std::out_of_range("array axis must be < ndim");
}
size_t n = Shape::stride(axis);
return n == Dynamic ? strides_[axis] : n;
}
......@@ -180,15 +212,23 @@ namespace detail
return size;
}
};
template <typename Shape, bool BoundsCheck>
using Base = std::conditional_t<Shape::fixed, detail::fixed_base<Shape, BoundsCheck>, detail::dynamic_base<Shape, BoundsCheck> >;
}
/*! Simple data-by-reference strided array class a la Fortran
/**
* \class Array
*
* \brief N-dimensional view to 1-dimensional array data
*
* Array view for index mapping to 1-dim array.
*/
template <typename Scalar, typename Shape, size_t Alignment=0>
class Array : public std::conditional_t<Shape::fixed, detail::fixed_base<Shape>, detail::dynamic_base<Shape> >
template <typename Scalar, typename Shape, size_t Alignment=0, bool BoundsCheck=true>
class Array : public detail::Base<Shape, BoundsCheck>
{
private:
typedef std::conditional_t<Shape::fixed, detail::fixed_base<Shape>, detail::dynamic_base<Shape> > Base;
typedef detail::Base<Shape, BoundsCheck> Base;
Scalar *data_;
......@@ -218,10 +258,10 @@ public:
{
static_assert(!Shape::fixed, "array shape must not be compile-time fixed");
Base::data_bounds_check(size);
#ifndef NO_BOUNDS_CHECK
if (reinterpret_cast<intptr_t>(data) % alignment() != 0)
throw std::runtime_error("data is not aligned");
#endif
if constexpr(BoundsCheck) {
if (reinterpret_cast<intptr_t>(data) % alignment() != 0)
throw std::runtime_error("data is not aligned");
}
}
Array(Scalar *data, size_t size)
......@@ -229,10 +269,10 @@ public:
{
static_assert(Shape::fixed, "array shape must be compile-time fixed");
Base::data_bounds_check(size);
#ifndef NO_BOUNDS_CHECK
if (reinterpret_cast<intptr_t>(data) % alignment() != 0)
throw std::runtime_error("data is not aligned");
#endif
if constexpr(BoundsCheck) {
if (reinterpret_cast<intptr_t>(data) % alignment() != 0)
throw std::runtime_error("data is not aligned");
}
}
template <typename... Idx>
......@@ -276,16 +316,16 @@ public:
for (size_t i = 0; i < sizeof...(idxs); ++i) {
idx += Base::stride(i) * m[i];
#ifndef NO_BOUNDS_CHECK
if (m[i] >= Base::dim(i))
throw std::out_of_range("index out of bounds");
#endif
if constexpr(BoundsCheck) {
if (m[i] >= Base::dim(i))
throw std::out_of_range("index out of bounds");
}
}
return idx;
}
/*! Extract partial matrix (compile-time fixed size) */
/** Extract partial matrix (compile-time fixed size) */
template <typename... Idx>
typename std::enable_if<detail::TailNth<sizeof...(Idx), Shape>::type::fixed,
Array<Scalar, typename detail::TailNth<sizeof...(Idx), Shape>::type > >::type
......@@ -299,7 +339,7 @@ public:
return {data_ + offset, Base::size() - offset};
}
/*! Extract partial matrix (compile-time dynamic size) */
/** Extract partial matrix (compile-time dynamic size) */
template <typename... Idx>
typename std::enable_if<!detail::TailNth<sizeof...(Idx), Shape>::type::fixed,
Array<Scalar, typename detail::TailNth<sizeof...(Idx), Shape>::type > >::type
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment