Program Listing for File array.hpp¶
↰ Return to documentation for file (include/util/array.hpp)
// SPDX-FileCopyrightText: 2021 Christian Göhring <mostsig@gmail.com>
// SPDX-License-Identifier: MIT
#ifndef THAT_THIS_UTIL_ARRAY_HEADER_IS_ALREADY_INCLUDED
#define THAT_THIS_UTIL_ARRAY_HEADER_IS_ALREADY_INCLUDED
#include <cstddef>
#include <iterator>
#include <stdexcept>
#ifdef UTIL_ASSERT
#include "assert.hpp"
#endif
namespace util {
template <class T, std::size_t N>
class array {
public:
using value_type = T;
using size_type = std::size_t;
using difference_type = std::ptrdiff_t;
using reference = value_type&;
using const_reference = const value_type&;
using pointer = value_type*;
using const_pointer = const value_type*;
using iterator = pointer;
using const_iterator = const_pointer;
using reverse_iterator = std::reverse_iterator<iterator>;
using const_reverse_iterator = std::reverse_iterator<const_iterator>;
constexpr auto at(size_type pos) -> reference;
constexpr auto at(size_type pos) const -> const_reference;
constexpr auto operator[](size_type pos) -> reference;
constexpr auto operator[](size_type pos) const -> const_reference;
constexpr auto front() -> reference;
constexpr auto front() const -> const_reference;
constexpr auto back() -> reference;
constexpr auto back() const -> const_reference;
constexpr auto data() noexcept -> pointer;
constexpr auto data() const noexcept -> const_pointer;
constexpr auto begin() noexcept -> iterator;
constexpr auto begin() const noexcept -> const_iterator;
constexpr auto cbegin() const noexcept -> const_iterator;
constexpr auto end() noexcept -> iterator;
constexpr auto end() const noexcept -> const_iterator;
constexpr auto cend() const noexcept -> const_iterator;
constexpr auto rbegin() noexcept -> reverse_iterator;
constexpr auto rbegin() const noexcept -> const_reverse_iterator;
constexpr auto crbegin() const noexcept -> const_reverse_iterator;
constexpr auto rend() noexcept -> reverse_iterator;
constexpr auto rend() const noexcept -> const_reverse_iterator;
constexpr auto crend() const noexcept -> const_reverse_iterator;
constexpr auto empty() const noexcept -> bool;
constexpr auto size() const noexcept -> size_type;
constexpr auto max_size() const noexcept -> size_type;
constexpr void fill(const T& value);
void swap(array& other);
// NOLINTNEXTLINE(cppcoreguidelines-avoid-c-arrays,modernize-avoid-c-arrays,misc-non-private-member-variables-in-classes)
T elements[N];
};
template <std::size_t I, class T, std::size_t N>
constexpr auto get(array<T, N>& a) noexcept -> T&;
template <std::size_t I, class T, std::size_t N>
constexpr auto get(array<T, N>& a) noexcept -> T&&;
template <std::size_t I, class T, std::size_t N>
constexpr auto get(const array<T, N>& a) noexcept -> const T&;
template <std::size_t I, class T, std::size_t N>
constexpr auto get(const array<T, N>& a) noexcept -> const T&&;
template <class T, class... U>
array(T, U...) -> array<T, 1 + sizeof...(U)>;
template <class T, size_t N>
constexpr auto operator==(const array<T, N>& lhs, const array<T, N>& rhs) -> bool;
template <class T, size_t N>
constexpr auto operator!=(const array<T, N>& lhs, const array<T, N>& rhs) -> bool;
template <class T, size_t N>
constexpr auto operator<(const array<T, N>& lhs, const array<T, N>& rhs) -> bool;
template <class T, size_t N>
constexpr auto operator<=(const array<T, N>& lhs, const array<T, N>& rhs) -> bool;
template <class T, size_t N>
constexpr auto operator>(const array<T, N>& lhs, const array<T, N>& rhs) -> bool;
template <class T, size_t N>
constexpr auto operator>=(const array<T, N>& lhs, const array<T, N>& rhs) -> bool;
template <class T, std::size_t N>
constexpr auto array<T, N>::at(size_type pos) -> array<T, N>::reference {
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast) @see Effective C++ by Scott Meyers
return const_cast<reference>(const_cast<const array<T, N>*>(this)->at(pos));
}
template <class T, std::size_t N>
constexpr auto array<T, N>::at(size_type pos) const -> array<T, N>::const_reference {
if (!(pos < size())) {
throw std::out_of_range{"pos is out of range"};
}
return elements[pos];
}
template <class T, std::size_t N>
constexpr auto array<T, N>::operator[](size_type pos) -> array<T, N>::reference {
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast) @see Effective C++ by Scott Meyers
return const_cast<reference>(const_cast<const array<T, N>*>(this)->operator[](pos));
}
template <class T, std::size_t N>
constexpr auto array<T, N>::operator[](size_type pos) const -> array<T, N>::const_reference {
#ifdef UTIL_ASSERT
util_assert(pos < size());
#endif
return elements[pos];
}
template <class T, std::size_t N>
constexpr auto array<T, N>::front() -> reference {
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast) @see Effective C++ by Scott Meyers
return const_cast<reference>(const_cast<const array<T, N>*>(this)->front());
}
template <class T, std::size_t N>
constexpr auto array<T, N>::front() const -> const_reference {
return elements[0];
}
template <class T, std::size_t N>
constexpr auto array<T, N>::back() -> reference {
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast) @see Effective C++ by Scott Meyers
return const_cast<reference>(const_cast<const array<T, N>*>(this)->back());
}
template <class T, std::size_t N>
constexpr auto array<T, N>::back() const -> const_reference {
return elements[N - 1];
}
template <class T, std::size_t N>
constexpr auto array<T, N>::data() noexcept -> array<T, N>::pointer {
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast) @see Effective C++ by Scott Meyers
return const_cast<pointer>(const_cast<const array<T, N>*>(this)->data());
}
template <class T, std::size_t N>
constexpr auto array<T, N>::data() const noexcept -> array<T, N>::const_pointer {
return elements;
}
template <class T, std::size_t N>
constexpr auto array<T, N>::begin() noexcept -> iterator {
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast) @see Effective C++ by Scott Meyers
return const_cast<pointer>(const_cast<const array<T, N>*>(this)->begin());
}
template <class T, std::size_t N>
constexpr auto array<T, N>::begin() const noexcept -> const_iterator {
return elements;
}
template <class T, std::size_t N>
constexpr auto array<T, N>::cbegin() const noexcept -> const_iterator {
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast)
return const_cast<const array<T, N>*>(this)->begin();
}
template <class T, std::size_t N>
constexpr auto array<T, N>::end() noexcept -> iterator {
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast) @see Effective C++ by Scott Meyers
return const_cast<pointer>(const_cast<const array<T, N>*>(this)->end());
}
template <class T, std::size_t N>
constexpr auto array<T, N>::end() const noexcept -> const_iterator {
return elements + N;
}
template <class T, std::size_t N>
constexpr auto array<T, N>::cend() const noexcept -> const_iterator {
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast)
return const_cast<const array<T, N>*>(this)->end();
}
template <class T, std::size_t N>
constexpr auto array<T, N>::rbegin() noexcept -> reverse_iterator {
return reverse_iterator(end());
}
template <class T, std::size_t N>
constexpr auto array<T, N>::rbegin() const noexcept -> const_reverse_iterator {
return const_reverse_iterator(end());
}
template <class T, std::size_t N>
constexpr auto array<T, N>::crbegin() const noexcept -> const_reverse_iterator {
return const_reverse_iterator(cend());
}
template <class T, std::size_t N>
constexpr auto array<T, N>::rend() noexcept -> reverse_iterator {
return reverse_iterator(begin());
}
template <class T, std::size_t N>
constexpr auto array<T, N>::rend() const noexcept -> const_reverse_iterator {
return const_reverse_iterator(begin());
}
template <class T, std::size_t N>
constexpr auto array<T, N>::crend() const noexcept -> const_reverse_iterator {
return const_reverse_iterator(cbegin());
}
template <class T, std::size_t N>
constexpr auto array<T, N>::empty() const noexcept -> bool {
return N == 0;
}
template <class T, std::size_t N>
constexpr auto array<T, N>::size() const noexcept -> array<T, N>::size_type {
return N;
}
template <class T, std::size_t N>
constexpr auto array<T, N>::max_size() const noexcept -> array<T, N>::size_type {
return N;
}
template <class T, std::size_t N>
constexpr void array<T, N>::fill(const T& value) {
for (auto& element : elements) {
element = value;
}
}
template <class T, std::size_t N>
void array<T, N>::swap(array& other) {
const auto tmp = *this;
*this = other;
other = tmp;
}
template <std::size_t I, class T, std::size_t N>
constexpr auto get(array<T, N>& a) noexcept -> T& {
static_assert(I < N, "I is out of range");
return a[I];
}
template <std::size_t I, class T, std::size_t N>
constexpr auto get(array<T, N>& a) noexcept -> T&& {
static_assert(I < N, "I is out of range");
return a[I];
}
template <std::size_t I, class T, std::size_t N>
constexpr auto get(const array<T, N>& a) noexcept -> const T& {
static_assert(I < N, "I is out of range");
return a[I];
}
template <std::size_t I, class T, std::size_t N>
constexpr auto get(const array<T, N>& a) noexcept -> const T&& {
static_assert(I < N, "I is out of range");
return a[I];
}
template <class T, size_t N>
constexpr auto operator==(const array<T, N>& lhs, const array<T, N>& rhs) -> bool {
return !(lhs != rhs);
}
template <class T, size_t N>
constexpr auto operator!=(const array<T, N>& lhs, const array<T, N>& rhs) -> bool {
if constexpr (N == 0) {
return false;
} else {
for (std::size_t i = 0; i < N; ++i) {
if (!(lhs.elements[i] == rhs.elements[i])) {
return true;
}
}
return false;
}
}
template <class T, size_t N>
constexpr auto operator<(const array<T, N>& lhs, const array<T, N>& rhs) -> bool {
if constexpr (N == 0) {
return false;
} else {
for (std::size_t i = 0; i < N; ++i) {
if (lhs.elements[i] < rhs.elements[i]) {
return true;
}
if (rhs.elements[i] < lhs.elements[i]) {
return false;
}
}
return false;
}
}
template <class T, size_t N>
constexpr auto operator<=(const array<T, N>& lhs, const array<T, N>& rhs) -> bool {
return !(rhs < lhs);
}
template <class T, size_t N>
constexpr auto operator>(const array<T, N>& lhs, const array<T, N>& rhs) -> bool {
return !(lhs <= rhs);
}
template <class T, size_t N>
constexpr auto operator>=(const array<T, N>& lhs, const array<T, N>& rhs) -> bool {
return !(lhs < rhs);
}
template <class T>
class array<T, 0> {
public:
using value_type = T;
using size_type = std::size_t;
using difference_type = std::ptrdiff_t;
using reference = value_type&;
using const_reference = const value_type&;
using pointer = value_type*;
using const_pointer = const value_type*;
using iterator = pointer;
using const_iterator = const_pointer;
using reverse_iterator = std::reverse_iterator<iterator>;
using const_reverse_iterator = std::reverse_iterator<const_iterator>;
constexpr auto at(size_type pos) -> reference {
pos = pos;
throw std::out_of_range{"cannot access element of empty array"};
}
constexpr auto at(size_type pos) const -> const_reference {
pos = pos;
throw std::out_of_range{"cannot access element of empty array"};
}
constexpr auto operator[](size_type pos) -> reference {
pos = pos;
throw std::out_of_range{"cannot access element of empty array"};
}
constexpr auto operator[](size_type pos) const -> const_reference {
pos = pos;
throw std::out_of_range{"cannot access element of empty array"};
}
constexpr auto front() -> reference {
throw std::out_of_range{"cannot access element of empty array"};
}
constexpr auto front() const -> const_reference {
throw std::out_of_range{"cannot access element of empty array"};
}
constexpr auto back() -> reference {
throw std::out_of_range{"cannot access element of empty array"};
}
constexpr auto back() const -> const_reference {
throw std::out_of_range{"cannot access element of empty array"};
}
constexpr auto data() noexcept -> pointer {
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
return reinterpret_cast<value_type*>(this);
}
constexpr auto data() const noexcept -> const_pointer {
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
return reinterpret_cast<const value_type*>(this);
}
constexpr auto begin() noexcept -> iterator {
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
return reinterpret_cast<value_type*>(this);
}
constexpr auto begin() const noexcept -> const_iterator {
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
return reinterpret_cast<const value_type*>(this);
}
constexpr auto cbegin() const noexcept -> const_iterator {
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
return reinterpret_cast<const value_type*>(this);
}
constexpr auto end() noexcept -> iterator { return begin(); }
constexpr auto end() const noexcept -> const_iterator { return begin(); }
constexpr auto cend() const noexcept -> const_iterator { return cbegin(); }
constexpr auto rbegin() noexcept -> reverse_iterator { return reverse_iterator(begin()); }
constexpr auto rbegin() const noexcept -> const_reverse_iterator {
return const_reverse_iterator(begin());
}
constexpr auto crbegin() const noexcept -> const_reverse_iterator {
return const_reverse_iterator(cbegin());
}
constexpr auto rend() noexcept -> reverse_iterator { return rbegin(); }
constexpr auto rend() const noexcept -> const_reverse_iterator { return rbegin(); }
constexpr auto crend() const noexcept -> const_reverse_iterator { return crbegin(); }
constexpr auto empty() const noexcept -> bool { return true; }
constexpr auto size() const noexcept -> size_type { return 0; }
constexpr auto max_size() const noexcept -> size_type { return 0; }
constexpr void fill(const T& value) {}
constexpr void swap(array& other) {}
};
} // namespace util
#endif // THAT_THIS_UTIL_ARRAY_HEADER_IS_ALREADY_INCLUDED