diff --git a/CMakeLists.txt b/CMakeLists.txt index f4523f8..43ed092 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -30,6 +30,7 @@ set(HEADER_FILES io/misc.h math/math.h misc/memory.h + misc/multiarray.h misc/random.h misc/traits.h tests/testutils.h diff --git a/misc/multiarray.h b/misc/multiarray.h new file mode 100644 index 0000000..2fbaf7b --- /dev/null +++ b/misc/multiarray.h @@ -0,0 +1,189 @@ +#ifndef CPP_UTILITIES_MULTI_ARRAY_H +#define CPP_UTILITIES_MULTI_ARRAY_H + +#include +#include +#include + +namespace MiscUtilities { + +/// \cond +namespace Detail { +template struct DimensionsHelper { + static std::size_t requiredSize(const Tuple &dimensionSizes) + { + return DimensionsHelper::requiredSize(dimensionSizes) * std::get(dimensionSizes); + } + static std::size_t offset(const Tuple &dimensions, const Tuple &indices, std::size_t factor) + { + return DimensionsHelper::offset(dimensions, indices, factor * std::get(dimensions)) + + (factor * std::get(indices)); + } +}; +template struct DimensionsHelper { + static std::size_t requiredSize(const Tuple &dimensionSizes) + { + return std::get<0>(dimensionSizes); + } + static std::size_t offset(const Tuple &, const Tuple &indices, std::size_t factor) + { + return factor * std::get<0>(indices); + } +}; +} // namespace Detail +/// \endcond + +/// \brief The VectorBasedMultiArray struct allows using an std::vector with custom allocator as underlying container for the MultiArray class. +template struct VectorBasedMultiArray { + template using Type = std::vector; + template static constexpr Type init(std::size_t requiredSize) + { + return Type(requiredSize); + } +}; + +/// \brief The VectorBasedMultiArray struct allows using an std::vector as underlying container for the MultiArray class. +template <> struct VectorBasedMultiArray { + template using Type = std::vector>; + template static constexpr Type init(std::size_t requiredSize) + { + return Type(requiredSize); + } +}; + +/// \brief The ArrayBasedMultiArray struct allows using a fixed size array as underlying container for the MultiArray class. +template struct ArrayBasedMultiArray { + template using Type = std::array; + template static constexpr Type init(std::size_t) + { + return Type(); + } +}; + +/// \brief The NoneOwningMultiArray struct allows using a caller-managed buffer array as underlying container for the MultiArray class. +struct NoneOwningMultiArray { + template using Type = T *; + template static constexpr Type init(std::size_t) + { + return nullptr; + } +}; + +/// \brief The MultiArray class provides an *N*-dimensional array. +template class MultiArray { +public: + MultiArray(Dimensions... dimensionSizes); + std::size_t totalSize() const; + static constexpr std::size_t dimensionCount(); + template std::size_t dimensionSize() const; + T &at(Dimensions... indices); + const T &at(Dimensions... indices) const; + T *data(); + const T *data() const; + typename UnderlyingContainer::template Type &buffer(); + +private: + using HelperType = Detail::DimensionsHelper, dimensionCount()>; + const std::tuple m_dims; + const std::size_t m_size; + typename UnderlyingContainer::template Type m_buff; +}; + +/// \brief Constructs a new *N*-dimensional array. The sizes for the dimensions are passed as arguments. +/// \remarks The number of dimensions *N* is deduced from the number of \a dimensionSizes. +/// \sa makeMultiArray(), makeFixedSizeMultiArray() and makeNoneOwningMultiArray() for more convenient construction +template +MultiArray::MultiArray(Dimensions... dimensionSizes) + : m_dims(std::make_tuple(dimensionSizes...)) + , m_size(HelperType::requiredSize(m_dims)) + , m_buff(UnderlyingContainer::template init(m_size)) +{ +} + +/// \brief Returns the total number of elements. +template +std::size_t MultiArray::totalSize() const +{ + return m_size; +} + +/// \brief Returns the number of dimensions for that type of array. +template +constexpr std::size_t MultiArray::dimensionCount() +{ + return std::tuple_size>::value; +} + +/// \brief Returns the number of elements in the specified dimension. +template +template +std::size_t MultiArray::dimensionSize() const +{ + return std::get(m_dims); +} + +/// \brief Returns the element at the position specified via \a indices. +/// \remarks The number of \a indices must equal dimensionCount(). +template +T &MultiArray::at(Dimensions... indices) +{ + return m_buff[HelperType::offset(m_dims, std::make_tuple(indices...), 1)]; +} + +/// \brief Returns the element at the position specified via \a indices. +/// \remarks The number of \a indices must equal dimensionCount(). +template +const T &MultiArray::at(Dimensions... indices) const +{ + return m_buff[HelperType::offset(m_dims, std::make_tuple(indices...), 1)]; +} + +/// \brief Returns a pointer to the raw data. +/// \remarks Intended for debugging purposes only. The underlying data structure might change in future versions. +template T *MultiArray::data() +{ + return m_buff.data(); +} + +/// \brief Returns a pointer to the raw data. +/// \remarks Intended for debugging purposes only. The underlying data structure might change in future versions. +template const T *MultiArray::data() const +{ + return m_buff.data(); +} + +/// \brief Allows accessing the underlying buffer directly. +/// \remarks Assign the custom buffer using this method when using NoneOwningMultiArray as UnderlyingContainer. +template +typename UnderlyingContainer::template Type &MultiArray::buffer() +{ + return m_buff; +} + +/// \brief Constructs a new *N*-dimensional array using an std::vector with std::allocator as underlying container. +/// The sizes for the dimensions are passed as arguments. +/// \remarks The number of dimensions *N* is deduced from the number of \a dimensionSizes. +template inline auto makeMultiArray(DimensionSizes... dimensionSizes) +{ + return MultiArray, DimensionSizes...>(dimensionSizes...); +} + +/// \brief Constructs a new *N*-dimensional array using a fixed size array as underlying container. +/// The sizes for the dimensions are passed as arguments. +/// \remarks The number of dimensions *N* is deduced from the number of \a dimensionSizes. +template inline auto makeFixedSizeMultiArray(DimensionSizes... dimensionSizes) +{ + return MultiArray, DimensionSizes...>(dimensionSizes...); +} + +/// \brief Constructs a new *N*-dimensional array using a caller-managed buffer as underlying container. +/// The sizes for the dimensions are passed as arguments. +/// \remarks The number of dimensions *N* is deduced from the number of \a dimensionSizes. +template inline auto makeNoneOwningMultiArray(DimensionSizes... dimensionSizes) +{ + return MultiArray(dimensionSizes...); +} + +} // namespace MiscUtilities + +#endif // CPP_UTILITIES_MULTI_ARRAY_H