aboutsummaryrefslogtreecommitdiffstats
path: root/nihil.ucl/map.ccm
diff options
context:
space:
mode:
authorLexi Winter <lexi@le-fay.org>2025-07-01 17:07:04 +0100
committerLexi Winter <lexi@le-fay.org>2025-07-01 17:07:04 +0100
commit2e2d1bd3b6c7776b77c33b94f30ead89367a71e6 (patch)
tree54d37ffadf8e677938d9b7a28e4e9b71be1e75c1 /nihil.ucl/map.ccm
parent36427c0966faa7aecd586b397ed9b845f18172f5 (diff)
downloadnihil-2e2d1bd3b6c7776b77c33b94f30ead89367a71e6.tar.gz
nihil-2e2d1bd3b6c7776b77c33b94f30ead89367a71e6.tar.bz2
add nihil.std
Diffstat (limited to 'nihil.ucl/map.ccm')
-rw-r--r--nihil.ucl/map.ccm263
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);
}