From fbb0d4e6e3e71fd900221cab425a1adca0d1f018 Mon Sep 17 00:00:00 2001
From: Pauli Virtanen <pauli.t.virtanen@jyu.fi>
Date: Thu, 4 Aug 2022 10:39:41 +0300
Subject: [PATCH] array: boundscheck as template param + add some doc

---
 src/array.hpp | 134 ++++++++++++++++++++++++++++++++------------------
 1 file changed, 87 insertions(+), 47 deletions(-)

diff --git a/src/array.hpp b/src/array.hpp
index 086376f..1ac284c 100644
--- a/src/array.hpp
+++ b/src/array.hpp
@@ -1,3 +1,10 @@
+/* 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
-- 
GitLab