#pragma once #include #include #include #include #include "appfat.h" #include "utils/attributes.h" namespace devilution { /** * @brief A stack-allocated vector with a fixed capacity. * * @tparam T element type. * @tparam N capacity. */ template class StaticVector { public: using value_type = T; using reference = T &; using const_reference = const T &; using pointer = T *; using const_pointer = const T *; using size_type = size_t; using iterator = T *; using const_iterator = const T *; using difference_type = std::ptrdiff_t; StaticVector() = default; template StaticVector(std::initializer_list elements) { for (auto &&element : elements) { emplace_back(element); } } [[nodiscard]] const T *begin() const { return &(*this)[0]; } [[nodiscard]] T *begin() { return &(*this)[0]; } [[nodiscard]] const T *end() const { return begin() + size_; } [[nodiscard]] T *end() { return begin() + size_; } [[nodiscard]] size_t size() const { return size_; } [[nodiscard]] bool empty() const DVL_PURE { return size_ == 0; } [[nodiscard]] const T &front() const { return (*this)[0]; } [[nodiscard]] T &front() { return (*this)[0]; } [[nodiscard]] const T &back() const { return (*this)[size_ - 1]; } [[nodiscard]] T &back() { return (*this)[size_ - 1]; } [[nodiscard]] const T *data() const { return data_[0].ptr(); } [[nodiscard]] T *data() { return data_[0].ptr(); } template void push_back(Args &&...args) // NOLINT(readability-identifier-naming) { emplace_back(std::forward(args)...); } template T &emplace_back(Args &&...args) // NOLINT(readability-identifier-naming) { assert(size_ < N); return *::new (&data_[size_++]) T(std::forward(args)...); } const T &operator[](std::size_t pos) const { return *data_[pos].ptr(); } T &operator[](std::size_t pos) { return *data_[pos].ptr(); } void erase(const T *first, const T *last) { if (last == first) return; assert(first >= begin() && last <= end() && first <= last); const auto count = last - first; auto tail = std::move(const_cast(last), end(), const_cast(first)); std::destroy(tail, end()); size_ -= count; } void erase(const T *element) { assert(element >= begin() && element < end()); erase(element, element + 1); } void pop_back() // NOLINT(readability-identifier-naming) { std::destroy_at(&back()); --size_; } void clear() { erase(begin(), end()); } ~StaticVector() { std::destroy_n(data(), size_); } private: struct AlignedStorage { alignas(alignof(T)) std::byte data[sizeof(T)]; [[nodiscard]] const T *ptr() const { return std::launder(reinterpret_cast(data)); } [[nodiscard]] T *ptr() { return std::launder(reinterpret_cast(data)); } }; AlignedStorage data_[N]; std::size_t size_ = 0; }; } // namespace devilution