/* * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * Copyright 2017-2019 Danny Robson */ #pragma once #include "../view.hpp" #include "../iterator/zip.hpp" #include #include namespace cruft { /// An array-like object with capacity fixed at instantiation time, and a /// size which is fixed at construction time. /// /// \tparam S maximum number of elements /// \tparam T data type of elements template class sarray { public: sarray (sarray &&rhs) noexcept (std::is_nothrow_move_constructible_v) : m_size (rhs.m_size) { for (size_t i = 0; i < m_size; ++i) ::new (&m_data.objects[i]) (T) (std::move (rhs[i])); } //--------------------------------------------------------------------- sarray (sarray const &rhs) : sarray (cruft::view (rhs)) { ; } //--------------------------------------------------------------------- sarray (T const &data) : sarray (&data, &data + 1) { ; } //------------------------------------------------------------------------- sarray (const T(&data)[S], std::size_t count = S): sarray (data, data + count) { ; } template sarray (InputIt first, InputIt last): m_size (std::distance (first, last)) { if (m_size > S) throw std::length_error ("oversize sarray"); std::size_t i = 0; for (auto cursor = first; cursor != last; ++cursor) ::new (&m_data.objects[i++]) (T) (*cursor); } //--------------------------------------------------------------------- template explicit sarray (const ContainerT &_data): sarray (std::begin (_data), std::end (_data)) { ; } //--------------------------------------------------------------------- sarray& operator= (sarray &&rhs) noexcept (std::is_nothrow_move_constructible_v) { // Destroy the currently held objects. for (auto &obj: *this) obj.~T (); // Move-construct the new objects for (auto [idx, obj]: cruft::iterator::izip (rhs)) new (&m_data.objects[idx]) T (std::move (obj)); m_size = rhs.m_size; return *this; } //--------------------------------------------------------------------- ~sarray () { for (std::size_t i = 0; i < m_size; ++i) m_data.objects[i].~T (); } //--------------------------------------------------------------------- std::size_t size (void) const { return m_size; } //--------------------------------------------------------------------- bool empty (void) const { return m_size == 0; } //--------------------------------------------------------------------- T& operator[] (std::size_t i)& { return m_data.objects[i]; } const T& operator[] (std::size_t i) const& { return m_data.objects[i]; } auto begin (void) { return std::begin (m_data.objects); }; auto end (void) { return begin () + m_size; } auto begin (void) const { return std::begin (m_data.objects); }; auto end (void) const { return begin () + m_size; } auto cbegin (void) const { return std::cbegin (m_data.objects); }; auto cend (void) const { return cbegin () + m_size; } private: union alignas (T) data_t { // we manually define a trivial constructor/destructor to appease // gcc which doesn't like us using non-trivial T as the stored // object. // // we're explicitly controlling the lifetime ourselves so this // isn't a functional issue. data_t () { }; ~data_t () { }; char store[sizeof (T) * S]; T objects[S]; } m_data; std::size_t m_size; }; }