From 7b46a764a80e68b30fef7b0d6796bd544b6c9f72 Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Mon, 8 Dec 2025 22:53:39 -0500 Subject: [PATCH] xo-alloc2: wip towards uany + fix utest build in xo-ordinaltree --- utest/objectmodel.test.cpp | 142 ++++++++++++++++++++++++++++++++++++- 1 file changed, 139 insertions(+), 3 deletions(-) diff --git a/utest/objectmodel.test.cpp b/utest/objectmodel.test.cpp index 19ce1b72..194357c3 100644 --- a/utest/objectmodel.test.cpp +++ b/utest/objectmodel.test.cpp @@ -34,9 +34,9 @@ * IComplex_DRectCoords IComplex_DPolarCoords IComplex_Any * = IComplex_Specific = IComplex_Specific * - * ^ - * | - * OUniqueBox + * ^ ^ + * | | + * ... OUniqueBox * * ^ * | @@ -104,6 +104,8 @@ namespace xo { virtual double ycoord(void * data) const = 0; virtual double argument(void * data) const = 0; virtual double magnitude(void * data) const = 0; + + virtual void destruct(void * data) const = 0; }; /** type-erased implementation of AComplex, for runtime polymorphism @@ -115,6 +117,8 @@ namespace xo { virtual double ycoord(void *) const final override { assert(false); return 0.0; } virtual double argument(void *) const final override { assert(false); return 0.0; } virtual double magnitude(void *) const final override { assert(false); return 0.0; } + + virtual void destruct(void *) const final override { assert(false); } }; template @@ -123,11 +127,26 @@ namespace xo { static double _ycoord(Repr *); static double _argument(Repr *); static double _magnitude(Repr *); + static void _destruct(Repr *); virtual double xcoord(void * data) const final override { return _xcoord((Repr*)data); } virtual double ycoord(void * data) const final override { return _ycoord((Repr*)data); } virtual double argument(void * data) const final override { return _argument((Repr*)data); } virtual double magnitude(void * data) const final override { return _magnitude((Repr*)data); } + virtual void destruct(void * data) const final override { _destruct((Repr*)data); } + }; + + // ----- Placeholder for opaque data ----- + + // Placeholder used for template specialization + + struct DOpaquePlaceholder {}; + + using IComplex_DOpaquePlaceholder = IComplex_Any; + + template <> + struct ISpecificFor { + using ImplType = IComplex_Any; }; // ----- Polar Coordinates ----- @@ -166,6 +185,12 @@ namespace xo { return data->mag_; } + template <> + void + IComplex_Specific::_destruct(DPolarCoords *) { + /*trivial*/ + } + template <> struct ISpecificFor { using ImplType = IComplex_Specific; @@ -211,6 +236,12 @@ namespace xo { return std::sqrt(x*x + y*y); } + template <> + void + IComplex_Specific::_destruct(DRectCoords * /*data*/) { + /*trivial*/ + } + template <> struct ISpecificFor { using ImplType = IComplex_Specific; @@ -250,8 +281,49 @@ namespace xo { DataBox data_; }; + // ----- polymorphic box ----- + + /** + * Unqiuely-owned instance with runtime polymorphism. + * + * Unlike OUniqueBox can use for variant data + * without additional overhead. Tradeoff is that avoiding such + * overhead excludes std::unique_ptr. + * + * We're going to instead rely on AInterface providing a destruct() method, + * so in practice get the deleter from interface state. + * + * Possibly means we need all abstract interfaces to share a common base + **/ + template + struct OUniqueAny : ISpecificFor::ImplType { + /* note: Data can be void here */ + using DataType = Data; + using DataBox = Data*; + + explicit OUniqueAny() {} + /* unsatisfactory b/c doesn't enforce that @p d is heap-allocated */ + explicit OUniqueAny(DataBox d) : data_{std::move(d)} {} + + ~OUniqueAny() { + if (data_ != nullptr) { + this->destruct(data_); + delete data_; + this->data_ = nullptr; + } + } + + /** note: load-bearing for routing classes such as RComplex **/ + Data * data() const { return data_; } + + DataBox data_ = nullptr; + }; + + // ----- Router; RFoo pairs with AFoo ----- + template struct RComplex : public Object { + RComplex() {} RComplex(Object::DataBox data) : Object{std::move(data)} {} double xcoord() const { return Object::_xcoord(Object::data()); } @@ -271,6 +343,8 @@ namespace xo { template using RoutingType = RoutingFor::RoutingType; + // ----- unique box; coordinates with OUniqueBox ----- + /** boxed object, held by unique pointer * * Example: @@ -283,8 +357,44 @@ namespace xo { explicit ubox(Super::DataBox d) : Super{std::move(d)} {} }; + + // ----- unique any; coordinates with OUniqueAny ----- + + /** boxed object, held by unique-pointer equiavelent. + * + * Example: + * std::unique_ptr z1_in = std::make_unique(1.0, 0.0): + * uany z1{z1_in.release()}; + * + * z1.xcoord(); + **/ + template + struct uany : public RoutingType> { + using Super = RoutingType>; + + uany() {} + explicit uany(Super::DataBox d) : Super(d) {} + + /** move constructor from a different representation. + * allowed given: + * - same abstract interface + * - same strategy (unique / refcounted / ..) + **/ + template + uany(uany && other) + requires (std::is_same_v + || std::is_convertible_v) + : Super(reinterpret_cast 1+0i */ @@ -363,6 +473,32 @@ namespace xo { REQUIRE(box.argument() == 0.0); REQUIRE(box.magnitude() == 1.0); } + + TEST_CASE("uany-1", "[objectmodel]") + { + /* default ctor */ + uany any; + } + + TEST_CASE("uany-2", "[objectmodel]") + { + /* equivalent to ubox, but impl doesn't use std::unique_ptr */ + uany any{new DRectCoords{1.0, 0.0}}; + + REQUIRE(any.xcoord() == 1.0); + REQUIRE(any.ycoord() == 0.0); + REQUIRE(any.argument() == 0.0); + REQUIRE(any.magnitude() == 1.0); + } + + TEST_CASE("uany-3", "[objectmodel]") + { + /* equivalent to ubox, but impl doesn't use std::unique_ptr */ + uany z1{new DRectCoords{1.0, 0.0}}; + + /* should be able to assign to a variant uany */ + uany uany = std::move(z1); + } } /*namespace ut*/ } /*namespace xo*/