diff options
| author | Lexi Winter <lexi@le-fay.org> | 2025-07-01 17:07:04 +0100 |
|---|---|---|
| committer | Lexi Winter <lexi@le-fay.org> | 2025-07-01 17:07:04 +0100 |
| commit | 2e2d1bd3b6c7776b77c33b94f30ead89367a71e6 (patch) | |
| tree | 54d37ffadf8e677938d9b7a28e4e9b71be1e75c1 /nihil.ucl/map.ccm | |
| parent | 36427c0966faa7aecd586b397ed9b845f18172f5 (diff) | |
| download | nihil-2e2d1bd3b6c7776b77c33b94f30ead89367a71e6.tar.gz nihil-2e2d1bd3b6c7776b77c33b94f30ead89367a71e6.tar.bz2 | |
add nihil.std
Diffstat (limited to 'nihil.ucl/map.ccm')
| -rw-r--r-- | nihil.ucl/map.ccm | 263 |
1 files changed, 117 insertions, 146 deletions
diff --git a/nihil.ucl/map.ccm b/nihil.ucl/map.ccm index fa77601..73ef583 100644 --- a/nihil.ucl/map.ccm +++ b/nihil.ucl/map.ccm @@ -1,47 +1,41 @@ -/* - * This source code is released into the public domain. - */ - +// This source code is released into the public domain. module; -#include <cassert> -#include <cstdint> -#include <cstdlib> -#include <format> -#include <memory> -#include <optional> -#include <string> -#include <system_error> - #include <ucl.h> export module nihil.ucl:map; +import nihil.std; import :object; namespace nihil::ucl { // Exception thrown when map::operator[] does not find the key. -export struct key_not_found : error { - key_not_found(std::string_view key) +export struct key_not_found : error +{ + explicit key_not_found(std::string_view key) : error(std::format("key '{}' not found in map", key)) , m_key(key) - {} + { + } auto key(this key_not_found const &self) -> std::string_view { return self.m_key; } - + private: std::string m_key; }; -export template<datatype T> +export template <datatype T> struct map; -template<datatype T> -struct map_iterator { +// The map iterator. UCL doesn't provide a way to copy an iterator, so this is an +// input iterator: it can only go forwards. +template <datatype T> +struct map_iterator +{ using difference_type = std::ptrdiff_t; using value_type = std::pair<std::string_view, T>; using reference = value_type &; @@ -49,242 +43,219 @@ struct map_iterator { using pointer = value_type *; using const_pointer = value_type const *; - struct sentinel{}; + struct sentinel + { + }; - [[nodiscard]] auto operator==(this map_iterator const &self, sentinel) - -> bool + [[nodiscard]] auto operator==(this map_iterator const &self, sentinel) -> bool { - return (self.m_state->cur == nullptr); + return self.m_state->current_object() == nullptr; } auto operator++(this map_iterator &self) -> map_iterator & { - self.m_state->next(); + self.m_state->advance(); return self; } auto operator++(this map_iterator &self, int) -> map_iterator & { - self.m_state->next(); + self.m_state->advance(); return self; } - [[nodiscard]] auto operator*(this map_iterator const &self) - -> value_type + [[nodiscard]] auto operator*(this map_iterator const &self) -> value_type { - auto obj = T(ref, self.m_state->cur); + auto *cur = self.m_state->current_object(); + if (cur == nullptr) + throw std::logic_error("map_iterator::operator* called on end()"); + + auto obj = T(ref, cur); return {obj.key(), std::move(obj)}; } private: friend struct map<T>; - map_iterator(::ucl_object_t const *obj) + explicit map_iterator(::ucl_object_t const *obj) : m_state(std::make_shared<state>(obj)) { ++(*this); } - struct state { - state(::ucl_object_t const *obj) + struct state + { + explicit state(::ucl_object_t const *obj) + : m_ucl_iterator([obj] { + if (auto *iter = ::ucl_object_iterate_new(obj); iter != nullptr) + return iter; + throw std::system_error(make_error_code(sys_error())); + }()) { - iter = ::ucl_object_iterate_new(obj); - if (iter == nullptr) - throw std::system_error(make_error_code( - std::errc(errno))); } state(state const &) = delete; - auto operator=(this state &, state const &) -> state& = delete; + auto operator=(state const &) -> state & = delete; + + state(state &&) = delete; + auto operator=(state &&) -> state & = delete; ~state() { - if (iter != nullptr) - ::ucl_object_iterate_free(iter); + if (m_ucl_iterator != nullptr) + ::ucl_object_iterate_free(m_ucl_iterator); + } + + auto advance(this state &self) -> void + { + self.m_current_object = ::ucl_object_iterate_safe(self.m_ucl_iterator, true); } - auto next() -> void + auto current_object(this state const &self) -> ::ucl_object_t const * { - cur = ::ucl_object_iterate_safe(iter, true); + return self.m_current_object; } - ucl_object_iter_t iter = nullptr; - ucl_object_t const *cur = nullptr; + private: + ucl_object_iter_t m_ucl_iterator = nullptr; + ucl_object_t const *m_current_object = nullptr; }; std::shared_ptr<state> m_state; }; -export template<datatype T = object> -struct map final : object { - inline static constexpr object_type ucl_type = object_type::object; +export template <datatype T = object> +struct map final : object +{ + static constexpr auto ucl_type = object_type::object; using value_type = std::pair<std::string_view, T>; using size_type = std::size_t; using difference_type = std::ptrdiff_t; using iterator = map_iterator<T>; + using sentinel = typename iterator::sentinel; - /* - * Create an empty map. Throws std::system_error on failure. - */ - map() : object(noref, [] { + ~map() override = default; + + // Create an empty map. Throws std::system_error on failure. + map() + : object(noref, [] { auto *uobj = ::ucl_object_typed_new(UCL_OBJECT); if (uobj == nullptr) - throw std::system_error( - std::make_error_code(std::errc(errno))); + throw std::system_error(std::make_error_code(sys_error())); return uobj; }()) { } - /* - * Create a map from a UCL object. Throws type_mismatch on failure. - * - * Unlike object_cast<>, this does not check the type of the contained - * elements, which means object access can throw type_mismatch. - */ - map(ref_t, ::ucl_object_t const *uobj) - : object(nihil::ucl::ref, [&] { - auto actual_type = static_cast<object_type>( - ::ucl_object_type(uobj)); - if (actual_type != map::ucl_type) - throw type_mismatch(map::ucl_type, - actual_type); - return uobj; - }()) + // Create a map from a UCL object. Throws type_mismatch on failure. + // + // Unlike object_cast<>, this does not check the type of the contained + // elements, which means object access can throw type_mismatch. + map(ref_t, ::ucl_object_t const * const uobj) + : object(nihil::ucl::ref, ensure_ucl_type(uobj, map::ucl_type)) { - if (type() != ucl_type) - throw type_mismatch(ucl_type, type()); } - map(noref_t, ::ucl_object_t *uobj) - : object(nihil::ucl::noref, [&] { - auto actual_type = static_cast<object_type>( - ::ucl_object_type(uobj)); - if (actual_type != map::ucl_type) - throw type_mismatch(map::ucl_type, - actual_type); - return uobj; - }()) + map(noref_t, ::ucl_object_t * const uobj) + : object(nihil::ucl::noref, ensure_ucl_type(uobj, map::ucl_type)) { } - /* - * Create a map from an iterator pair. - */ - template<std::input_iterator Iterator> - requires(std::convertible_to<std::iter_value_t<Iterator>, value_type>) - map(Iterator first, Iterator last) - : map() + // Create a map from a range of value types. + template <std::ranges::range Range> + requires(std::convertible_to<std::ranges::range_value_t<Range>, value_type>) + explicit map(Range const &range) + : map() { // This is exception safe, because if we throw here the // base class destructor will free the map. - while (first != last) { - insert(*first); - ++first; - } + for (auto &&v: range) + insert(v); } - /* - * Create a map from a range. - */ - template<std::ranges::range Range> - requires(std::convertible_to<std::ranges::range_value_t<Range>, - value_type>) - map(std::from_range_t, Range &&range) - : map(std::ranges::begin(range), - std::ranges::end(range)) + // Create a map from an iterator pair. + template <std::input_iterator Iterator> + requires(std::convertible_to<std::iter_value_t<Iterator>, value_type>) + map(Iterator first, Iterator last) + : map(std::ranges::subrange(first, last)) { } - /* - * Create a map from an initializer_list. - */ + // Create a map from an initializer_list. map(std::initializer_list<value_type> const &list) - : map(std::ranges::begin(list), std::ranges::end(list)) + : map(std::ranges::subrange(std::ranges::begin(list), std::ranges::end(list))) { } - /* - * Map iterator access. - */ + // Copyable. Note that this copies the entire UCL object. + map(map const &) = default; + auto operator=(map const &) -> map & = default; + + // Movable. + map(map &&) = default; + auto operator=(map &&other) -> map & = default; + + // + // Map iterator access. + // [[nodiscard]] auto begin(this map const &self) -> iterator { - return {self.get_ucl_object()}; + return iterator(self.get_ucl_object()); } - [[nodiscard]] auto end(this map const &) -> iterator::sentinel + [[nodiscard]] auto end(this map const &) -> sentinel { return {}; } - /* - * Reserve space for future insertions. - */ - auto reserve(this map &self, size_type nelems) -> void + // Reserve space for future insertions. + auto reserve(this map &self, size_type const nelems) -> void { ::ucl_object_reserve(self.get_ucl_object(), nelems); } - /* - * Add an element to the map. - */ + // Add an element to the map. auto insert(this map &self, value_type const &v) -> void { auto uobj = ::ucl_object_ref(v.second.get_ucl_object()); - ::ucl_object_insert_key(self.get_ucl_object(), uobj, - v.first.data(), v.first.size(), true); + ::ucl_object_insert_key(self.get_ucl_object(), uobj, v.first.data(), v.first.size(), + true); } - /* - * Access a map element by key. - */ - [[nodiscard]] auto find(this map const &self, std::string_view key) - -> std::optional<T> + // Access a map element by key. + [[nodiscard]] auto find(this map const &self, std::string_view const key) -> std::optional<T> { - auto const *obj = ::ucl_object_lookup_len( - self.get_ucl_object(), - key.data(), key.size()); + auto const *obj = + ::ucl_object_lookup_len(self.get_ucl_object(), key.data(), key.size()); if (obj == nullptr) return {}; return {T(nihil::ucl::ref, obj)}; } - /* - * Remove an object from the map. - */ - auto remove(this map &self, std::string_view key) -> bool + // Remove an object from the map. + auto remove(this map &self, std::string_view const key) -> bool { - return ::ucl_object_delete_keyl(self.get_ucl_object(), - key.data(), key.size()); + return ::ucl_object_delete_keyl(self.get_ucl_object(), key.data(), key.size()); } - /* - * Remove an object from the map and return it. - */ - auto pop(this map &self, std::string_view key) - -> std::optional<T> + // Remove an object from the map and return it. If the map is empty, returns nullopt. + auto pop(this map &self, std::string_view const key) -> std::optional<T> { - auto *uobj = ::ucl_object_pop_keyl(self.get_ucl_object(), - key.data(), key.size()); + auto *uobj = ::ucl_object_pop_keyl(self.get_ucl_object(), key.data(), key.size()); if (uobj) return T(noref, uobj); return {}; } - /* - * Equivalent to find(), except it throws key_not_found if the key - * doesn't exist in the map. - */ - [[nodiscard]] auto operator[] (this map const &self, - std::string_view key) - -> T + // Equivalent to find(), except it throws key_not_found if the key + // doesn't exist in the map. + [[nodiscard]] auto operator[](this map const &self, std::string_view const key) -> T { - auto obj = self.find(key); - if (obj) + if (auto obj = self.find(key); obj ) return *obj; throw key_not_found(key); } |
