/* * This source code is released into the public domain. */ module; #include #include #include #include #include export module nihil.ucl:array; import :object; namespace nihil::ucl { export template struct array; template struct array_iterator { using difference_type = std::ptrdiff_t; using value_type = T; using reference = T&; using pointer = T*; array_iterator() = default; auto operator* (this array_iterator const &self) -> T { auto uobj = ::ucl_array_find_index(self._array, self._idx); if (uobj == nullptr) throw error("failed to fetch UCL array index"); return T(nihil::ucl::ref, uobj); } auto operator[] (this array_iterator const &self, difference_type idx) -> T { return *(self + idx); } auto operator++ (this array_iterator &self) -> array_iterator& { ++self._idx; return self; } auto operator++ (this array_iterator &self, int) -> array_iterator { auto copy = self; ++self; return copy; } auto operator-- (this array_iterator &self) -> array_iterator& { if (self._idx == 0) throw std::out_of_range("attempt to iterate before " "start of UCL array"); --self._idx; return self; } auto operator-- (this array_iterator &self, int) -> array_iterator { auto copy = self; --self; return copy; } auto operator== (this array_iterator const &lhs, array_iterator const &rhs) -> bool { return lhs._idx == rhs._idx; } auto operator<=> (this array_iterator const &lhs, array_iterator const &rhs) { return lhs._idx <=> rhs._idx; } auto operator+= (this array_iterator &lhs, difference_type rhs) -> array_iterator & { lhs._idx += rhs; return lhs; } auto operator-= (this array_iterator &lhs, difference_type rhs) -> array_iterator & { lhs._idx -= rhs; return lhs; } auto operator- (this array_iterator const &lhs, array_iterator const &rhs) -> difference_type { return lhs._idx - rhs._idx; } private: friend struct array; ::ucl_object_t const *_array{}; std::size_t _idx{}; array_iterator(::ucl_object_t const *array, std::size_t idx) : _array(array) , _idx(idx) {} }; export template auto operator+(array_iterator const &lhs, typename array_iterator::difference_type rhs) -> array_iterator { auto copy = lhs; copy += rhs; return copy; } export template auto operator+(typename array_iterator::difference_type lhs, array_iterator const &rhs) -> array_iterator { return rhs - lhs; } export template auto operator-(array_iterator const &lhs, typename array_iterator::difference_type rhs) -> array_iterator { auto copy = lhs; copy -= rhs; return copy; } export template struct array final : object { inline static constexpr object_type ucl_type = object_type::array; using value_type = T; using size_type = std::size_t; using difference_type = std::ptrdiff_t; // Create an empty array. array() : object(noref, ::ucl_object_typed_new(UCL_ARRAY)) { if (_object == nullptr) throw error("failed to create UCL object"); } // Create a new array from a UCL object. array(ref_t, ::ucl_object_t const *uobj) : object(nihil::ucl::ref, uobj) { if (type() != ucl_type) throw type_mismatch(ucl_type, type()); } array(noref_t, ::ucl_object_t *uobj) : object(noref, uobj) { if (type() != ucl_type) throw type_mismatch(ucl_type, type()); } /* * Create an array from an iterator pair. */ template requires(std::convertible_to, T>) array(Iterator first, Iterator last) : array() { if (_object == nullptr) throw error("failed to create UCL object"); // This is exception safe, because if we throw here the // base class destructor will free the array. while (first != last) { push_back(*first); ++first; } } /* * Create an array from a range. */ template requires(std::convertible_to, T>) array(std::from_range_t, Range &&range) : array(std::ranges::begin(range), std::ranges::end(range)) { } /* * Create an array from an initializer_list. */ array(std::initializer_list const &list) : array(std::ranges::begin(list), std::ranges::end(list)) { } /* * Array iterator access. */ auto begin(this array const &self) -> array_iterator { return {self.get_ucl_object(), 0}; } auto end(this array const &self) -> array_iterator { return {self.get_ucl_object(), self.size()}; } /* * Return the size of this array. */ auto size(this array const &self) -> size_type { return ::ucl_array_size(self.get_ucl_object()); } /* * Test if this array is empty. */ auto empty(this array const &self) -> bool { return self.size() == 0; } /* * Reserve space for future insertions. */ auto reserve(this array &self, size_type nelems) -> void { ::ucl_object_reserve(self.get_ucl_object(), nelems); } /* * Append an element to the array. */ auto push_back(this array &self, value_type const &v) -> void { auto uobj = ::ucl_object_ref(v.get_ucl_object()); ::ucl_array_append(self.get_ucl_object(), uobj); } /* * Prepend an element to the array. */ auto push_front(this array &self, value_type const &v) -> void { auto uobj = ::ucl_object_ref(v.get_ucl_object()); ::ucl_array_prepend(self.get_ucl_object(), uobj); } /* * Access an array element by index. */ auto at(this array const &self, size_type idx) -> T { if (idx >= self.size()) throw std::out_of_range("UCL array index out of range"); auto uobj = ::ucl_array_find_index(self.get_ucl_object(), idx); if (uobj == nullptr) throw error("failed to fetch UCL array index"); return T(nihil::ucl::ref, uobj); } auto operator[] (this array const &self, size_type idx) -> T { return self.at(idx); } /* * Return the first element. */ auto front(this array const &self) -> T { return self.at(0); } /* * Return the last element. */ auto back(this array const &self) -> T { if (self.empty()) throw std::out_of_range("attempt to access back() on " "empty UCL array"); return self.at(self.size() - 1); } }; /* * Comparison operators. */ export template auto operator==(array const &a, array const &b) -> bool { if (a.size() != b.size()) return false; for (typename array::size_type i = 0; i < a.size(); ++i) if (a.at(i) != b.at(i)) return false; return true; } } // namespace nihil::ucl