From 09b7a494bd5de24f380095003fb7da4939de43e5 Mon Sep 17 00:00:00 2001 From: Lexi Winter Date: Sun, 22 Jun 2025 17:39:27 +0100 Subject: nihil.ucl: improve reference management --- nihil.ucl/array.ccm | 39 ++++++++++++++------------------- nihil.ucl/boolean.ccm | 24 +++++++++++++++----- nihil.ucl/integer.ccm | 24 +++++++++++++++----- nihil.ucl/object.ccm | 56 +++++++++++++++++++++++++++++++++-------------- nihil.ucl/object_cast.ccm | 7 +++--- nihil.ucl/parser.ccm | 3 ++- nihil.ucl/real.ccm | 23 ++++++++++++++----- nihil.ucl/string.ccm | 46 ++++++++++++++++++++++++++++++++------ nihil.ucl/tests/array.cc | 2 +- 9 files changed, 154 insertions(+), 70 deletions(-) (limited to 'nihil.ucl') diff --git a/nihil.ucl/array.ccm b/nihil.ucl/array.ccm index 7488bde..87e175e 100644 --- a/nihil.ucl/array.ccm +++ b/nihil.ucl/array.ccm @@ -35,7 +35,7 @@ struct array_iterator { if (uobj == nullptr) throw error("failed to fetch UCL array index"); - return T(::ucl_object_ref(uobj)); + return T(nihil::ucl::ref, uobj); } auto operator[] (this array_iterator const &self, @@ -158,15 +158,26 @@ struct array final : object { using size_type = std::size_t; using difference_type = std::ptrdiff_t; - array() : object(::ucl_object_typed_new(UCL_ARRAY)) + // Create an empty array. + array() : object(noref, ::ucl_object_typed_new(UCL_ARRAY)) { if (_object == nullptr) throw error("failed to create UCL object"); } - explicit array(::ucl_object_t *uobj) : object(uobj) + // Create a new array from a UCL object. + array(ref_t, ::ucl_object_t const *uobj) + : object(nihil::ucl::ref, uobj) { - assert(type() == object_type::array); + 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()); } /* @@ -175,7 +186,7 @@ struct array final : object { template requires(std::convertible_to, T>) array(Iterator first, Iterator last) - : object(::ucl_object_typed_new(UCL_ARRAY)) + : array() { if (_object == nullptr) throw error("failed to create UCL object"); @@ -249,14 +260,6 @@ struct array final : object { /* * Append an element to the array. */ - auto push_back(this array &self, value_type &&v) -> void - { - // There's no real benefit to moving the object here, but - // move it anyway to preserve the expected semantics. - auto copy = std::move(v); - self.push_back(copy); - } - auto push_back(this array &self, value_type const &v) -> void { auto uobj = ::ucl_object_ref(v.get_ucl_object()); @@ -266,14 +269,6 @@ struct array final : object { /* * Prepend an element to the array. */ - auto push_front(this array &self, value_type &&v) -> void - { - // There's no real benefit to moving the object here, but - // move it anyway to preserve the expected semantics. - auto copy = std::move(v); - self.push_front(copy); - } - auto push_front(this array &self, value_type const &v) -> void { auto uobj = ::ucl_object_ref(v.get_ucl_object()); @@ -292,7 +287,7 @@ struct array final : object { if (uobj == nullptr) throw error("failed to fetch UCL array index"); - return T(::ucl_object_ref(uobj)); + return T(nihil::ucl::ref, uobj); } auto operator[] (this array const &self, size_type idx) -> T diff --git a/nihil.ucl/boolean.ccm b/nihil.ucl/boolean.ccm index db6c864..e9c161b 100644 --- a/nihil.ucl/boolean.ccm +++ b/nihil.ucl/boolean.ccm @@ -22,18 +22,30 @@ export struct boolean final : object { inline static constexpr object_type ucl_type = object_type::boolean; - boolean(value_type value) - : object(::ucl_object_frombool(value)) + // Create a new boolean from a UCL object. + boolean(ref_t, ::ucl_object_t const *uobj) + : object(nihil::ucl::ref, uobj) { - if (_object == nullptr) - throw error("failed to create UCL object"); + if (type() != ucl_type) + throw type_mismatch(ucl_type, type()); } - explicit boolean(::ucl_object_t *uobj) : object(uobj) + boolean(noref_t, ::ucl_object_t *uobj) + : object(noref, uobj) { - assert(type() == object_type::boolean); + if (type() != ucl_type) + throw type_mismatch(ucl_type, type()); + } + + // Create a new boolean from a value. + explicit boolean(value_type value) + : object(noref, ::ucl_object_frombool(value)) + { + if (_object == nullptr) + throw error("failed to create UCL object"); } + // Return this object's value. auto value(this boolean const &self) -> value_type { auto v = value_type{}; diff --git a/nihil.ucl/integer.ccm b/nihil.ucl/integer.ccm index d3009a1..18f0049 100644 --- a/nihil.ucl/integer.ccm +++ b/nihil.ucl/integer.ccm @@ -22,18 +22,30 @@ export struct integer final : object { inline static constexpr object_type ucl_type = object_type::integer; - integer(value_type value) - : object(::ucl_object_fromint(value)) + // Create a new integer from a UCL object. + integer(ref_t, ::ucl_object_t const *uobj) + : object(nihil::ucl::ref, uobj) { - if (_object == nullptr) - throw error("failed to create UCL object"); + if (type() != ucl_type) + throw type_mismatch(ucl_type, type()); } - explicit integer(::ucl_object_t *uobj) : object(uobj) + integer(noref_t, ::ucl_object_t *uobj) + : object(noref, uobj) { - assert(type() == object_type::integer); + if (type() != ucl_type) + throw type_mismatch(ucl_type, type()); + } + + // Create a new integer from a value. + integer(value_type value) + : object(noref, ::ucl_object_fromint(value)) + { + if (_object == nullptr) + throw error("failed to create UCL object"); } + // Return the value of this object. auto value(this integer const &self) -> value_type { auto v = value_type{}; diff --git a/nihil.ucl/object.ccm b/nihil.ucl/object.ccm index 7286301..72abc00 100644 --- a/nihil.ucl/object.ccm +++ b/nihil.ucl/object.ccm @@ -33,16 +33,30 @@ export struct parser; * The basic object type. */ +// Ref the UCL object when creating an object. +export inline constexpr struct ref_t {} ref; +// Don't ref the UCL object. +export inline constexpr struct noref_t {} noref; + export struct object { inline static constexpr object_type ucl_type = object_type::object; - // Create an object from an existing ucl_object_t. We assume the - // object has already been referenced. - object(::ucl_object_t *object) : _object(object) {} + // Create an object from an existing ucl_object_t. The first argument + // determines whether we ref the object or not. + + object(ref_t, ::ucl_object_t const *object) + : _object(::ucl_object_ref(object)) + { + } + + object(noref_t, ::ucl_object_t *object) + : _object(object) + { + } // Free our object on destruction. virtual ~object() { - if (_object) + if (_object != nullptr) ::ucl_object_unref(_object); } @@ -70,21 +84,24 @@ export struct object { -> object & { if (&self != &other) { + auto *new_uobj = ::ucl_object_copy(other.get_ucl_object()); + if (new_uobj == nullptr) + throw error("failed to copy UCL object"); + if (self._object != nullptr) ::ucl_object_unref(self._object); - - if (other._object != nullptr) { - self._object = ::ucl_object_copy(other._object); - if (self._object == nullptr) - throw error("failed to copy UCL object"); - } else { - self._object = nullptr; - } + self._object = new_uobj; } return self; } + // Increase the refcount of this object. + auto ref(this object const &self) -> object + { + return object(nihil::ucl::ref, self.get_ucl_object()); + } + // Return the type of this object. auto type(this object const &self) -> object_type { @@ -95,11 +112,15 @@ export struct object { // Return the underlying object. auto get_ucl_object(this object &self) -> ::ucl_object_t * { + if (self._object == nullptr) + throw error("attempt to access empty UCL object"); return self._object; } auto get_ucl_object(this object const &self) -> ::ucl_object_t const * { + if (self._object == nullptr) + throw error("attempt to access empty UCL object"); return self._object; } @@ -107,7 +128,8 @@ export struct object { auto key(this object const &self) -> std::string_view { auto dlen = std::size_t{}; - auto const *dptr = ::ucl_object_keyl(self._object, &dlen); + auto const *dptr = ::ucl_object_keyl(self.get_ucl_object(), + &dlen); return {dptr, dlen}; } @@ -116,11 +138,12 @@ export struct object { -> std::optional { auto const *obj = ::ucl_object_lookup_any( - self._object, key.data(), key.size()); + self.get_ucl_object(), + key.data(), key.size()); if (obj == nullptr) return {}; - return {object(::ucl_object_ref(obj))}; + return {object(nihil::ucl::ref, obj)}; } protected: @@ -194,8 +217,7 @@ export struct iterator { } auto operator*(this iterator const &self) -> object { - auto *ptr = ::ucl_object_ref(self._state->cur); - return object(ptr); + return object(ref, self._state->cur); } private: diff --git a/nihil.ucl/object_cast.ccm b/nihil.ucl/object_cast.ccm index 7291960..b10ffbc 100644 --- a/nihil.ucl/object_cast.ccm +++ b/nihil.ucl/object_cast.ccm @@ -71,11 +71,10 @@ struct convert_check> export template auto object_cast(object const &from) -> To { - convert_check{}.check(from.get_ucl_object()); + auto uobj = from.get_ucl_object(); - auto const *uobj = from.get_ucl_object(); - auto *refptr = ::ucl_object_ref(uobj); - return To(refptr); + convert_check{}.check(uobj); + return To(nihil::ucl::ref, uobj); } } // namespace nihil::ucl diff --git a/nihil.ucl/parser.ccm b/nihil.ucl/parser.ccm index 17ed79c..968e906 100644 --- a/nihil.ucl/parser.ccm +++ b/nihil.ucl/parser.ccm @@ -147,7 +147,8 @@ export struct parser { if (obj == nullptr) throw error("attempt to call top() on an empty parser"); - return {obj}; + // ucl_parser_get_objects() refs the object for us. + return {noref, obj}; } private: diff --git a/nihil.ucl/real.ccm b/nihil.ucl/real.ccm index 4639109..d968667 100644 --- a/nihil.ucl/real.ccm +++ b/nihil.ucl/real.ccm @@ -21,16 +21,27 @@ export struct real final : object { inline static constexpr object_type ucl_type = object_type::real; - real(value_type value) - : object(::ucl_object_fromdouble(value)) + // Create a new real from a UCL object. + real(ref_t, ::ucl_object_t const *uobj) + : object(nihil::ucl::ref, uobj) { - if (_object == nullptr) - throw error("failed to create UCL object"); + if (type() != ucl_type) + throw type_mismatch(ucl_type, type()); } - explicit real(::ucl_object_t *uobj) : object(uobj) + real(noref_t, ::ucl_object_t *uobj) + : object(noref, uobj) { - assert(type() == object_type::real); + if (type() != ucl_type) + throw type_mismatch(ucl_type, type()); + } + + // Create a new real from a value. + real(value_type value) + : object(noref, ::ucl_object_fromdouble(value)) + { + if (_object == nullptr) + throw error("failed to create UCL object"); } auto value(this real const &self) -> value_type diff --git a/nihil.ucl/string.ccm b/nihil.ucl/string.ccm index e41c70f..ffad847 100644 --- a/nihil.ucl/string.ccm +++ b/nihil.ucl/string.ccm @@ -21,19 +21,51 @@ export struct string final : object { inline static constexpr object_type ucl_type = object_type::string; - string(value_type value) - : object(::ucl_object_fromstring_common( - value.data(), value.size(), UCL_STRING_RAW)) + // Create a new string from a UCL object. + string(ref_t, ::ucl_object_t const *uobj) + : object(nihil::ucl::ref, uobj) { - if (_object == nullptr) - throw error("failed to create UCL object"); + if (type() != ucl_type) + throw type_mismatch(ucl_type, type()); } - explicit string(::ucl_object_t *uobj) : object(uobj) + string(noref_t, ::ucl_object_t *uobj) + : object(noref, uobj) { - assert(type() == object_type::string); + if (type() != ucl_type) + throw type_mismatch(ucl_type, type()); + } + + // Create a new UCL string from a string. + string(std::string_view value) + : object(nihil::ucl::ref, + ::ucl_object_fromstring_common( + value.data(), value.size(), + UCL_STRING_RAW)) + { + if (_object == nullptr) + throw error("failed to create UCL object"); } + // Create a new UCL string from an iterator pair. + template + string(Iterator first, Iterator last) + : string(std::string_view(first, last)) + {} + + template + requires(!std::contiguous_iterator) + string(Iterator first, Iterator last) + : string(std::string(first, last)) + {} + + // Create a new UCL string from a range. + string(std::from_range_t, std::ranges::range auto &&range) + : string(std::ranges::begin(range), + std::ranges::end(range)) + {} + + // Return the value of this string. auto value(this string const &self) -> value_type { char const *dptr{}; diff --git a/nihil.ucl/tests/array.cc b/nihil.ucl/tests/array.cc index 220564d..a4f26b6 100644 --- a/nihil.ucl/tests/array.cc +++ b/nihil.ucl/tests/array.cc @@ -210,7 +210,7 @@ TEST_CASE("ucl: array: homogeneous cast", "[ucl]") arr.push_back(integer(1)); arr.push_back(integer(42)); - auto obj = object(arr.get_ucl_object()); + auto obj = object(ref, arr.get_ucl_object()); // Converting to array should fail. REQUIRE_THROWS_AS(object_cast>(obj), type_mismatch); -- cgit v1.2.3