From 73d8d1945faacb6a416ff15e3fc1cfabc8503950 Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Sat, 20 Dec 2025 22:40:33 -0500 Subject: [PATCH] xo-alloc2: bugfix: properly assign iface_ pointer for OObject ops --- xo-facet/include/xo/facet/OObject.hpp | 68 +++++++++++++++++++++------ xo-facet/include/xo/facet/obj.hpp | 4 +- 2 files changed, 55 insertions(+), 17 deletions(-) diff --git a/xo-facet/include/xo/facet/OObject.hpp b/xo-facet/include/xo/facet/OObject.hpp index 754e3ee1..0b7e34cc 100644 --- a/xo-facet/include/xo/facet/OObject.hpp +++ b/xo-facet/include/xo/facet/OObject.hpp @@ -64,8 +64,24 @@ namespace xo { using DataPtr = DRepr*; using Opaque = void *; - explicit OObject() {} - explicit OObject(DataPtr d) : data_{d} {} + /* required for vtable swapping to work */ + //static_assert(std::is_trivially_copyable_v); + + explicit OObject() { + ISpecific tmp; + memcpy(&(iface_[0]), (void*)&tmp, sizeof(ISpecific)); + } + explicit OObject(DataPtr d) : data_{d} { + ISpecific tmp; + memcpy(&(iface_[0]), (void*)&tmp, sizeof(ISpecific)); + } + + OObject(const OObject & oother) { + _launder_from(oother); + } + OObject(OObject && oother) { + _launder_from(oother); + } /** trivial: nothing to do for @ref iface_ and does not own @ref data_ **/ ~OObject() = default; @@ -88,7 +104,7 @@ namespace xo { * calls to ISpecific methods, based on mistaken belief that * vtable pointer is known at compile time. */ - return std::launder(&iface_); + return std::launder((FacetType *)(&iface_[0])); } /** non-const verison. Technically all interface methods are const. @@ -98,7 +114,7 @@ namespace xo { FacetType * iface() requires std::is_same_v { - return std::launder(&iface_); + return std::launder((FacetType *)(&iface_[0])); } // ----- iface() for typed fat pointer ----- @@ -114,7 +130,7 @@ namespace xo { /* don't use std::launder: want compiler to devirtualize * calls to virtual @ref iface_ methods */ - return &iface_; + return std::launder((FacetType *)(&iface_[0])); } /** non-const verison. Technically all interface methods are const. @@ -124,7 +140,7 @@ namespace xo { FacetType * iface() requires(!std::is_same_v) { - return &iface_; + return std::launder((FacetType *)&(iface_[0])); } DataPtr data() const { return data_; } @@ -134,16 +150,16 @@ namespace xo { void reset_opaque(Opaque data) { data_ = (DataPtr)data; } template - OObject & from_obj(const OObject & other) { + OObject & from_obj(const OObject & oother) { if constexpr (std::is_same_v) { /* Actual runtime type of other encoded in other.iface() * (whether or not DOther says other is variant). * Either way need to force vtable replacement, hence memcpy here */ - ::memcpy((void*)this, (void*)&other, sizeof(*this)); + _launder_from(oother); } else if constexpr (std::is_convertible_v) { /* other is typed, consistently with *this */ - this->from_data(other.data()); + this->from_data(oother.data()); } else { // downcast from variant must be explicit @@ -169,8 +185,8 @@ namespace xo { if constexpr (!std::is_same_v && std::is_convertible_v) { - /* assigning typed data with consistent representation - * keep .iface_ pointer + /* assigning to typed data, from something with consistent + * representation keep .iface_ pointer */ this->data_ = other; } else /*DRepr is DVariantPlaceholder*/ { @@ -184,7 +200,7 @@ namespace xo { static_assert(sizeof(*this) == sizeof(oother)); - ::memcpy((void*)this, (void*)&oother, sizeof(*this)); + _launder_from(oother); } return *this; @@ -213,8 +229,28 @@ namespace xo { } } + template + void _launder_from(const OObject & oother) { + ::memcpy((void*)this, &oother, sizeof(*this)); + //iface_ = *std::launder(&iface_); + } + DRepr & operator*() { return *data_; } + OObject & operator=(const OObject & oother) { + if (this != &oother) { + _launder_from(oother); + } + return *this; + } + + OObject & operator=(OObject && oother) { + if (this != &oother) { + _launder_from(oother); + } + return *this; + } + #ifdef NOT_IN_USE // not sure if this is a good idea. could just as well write // auto obj = ...; @@ -233,8 +269,12 @@ namespace xo { /** fetch data pointer. load-bearing for routing classes **/ static bool _valid; - /** runtime interface for this object **/ - ISpecific iface_; + /** runtime interface for this object. + * use byte array to make sure compiler doesn't get clever + * (but mistaken) ideas + **/ + alignas(ISpecific) std::byte iface_[sizeof(ISpecific)]; //ISpecific iface_; + /** runtime state for this object **/ DataPtr data_ = nullptr; }; diff --git a/xo-facet/include/xo/facet/obj.hpp b/xo-facet/include/xo/facet/obj.hpp index 49fb96ac..e514b5bc 100644 --- a/xo-facet/include/xo/facet/obj.hpp +++ b/xo-facet/include/xo/facet/obj.hpp @@ -52,9 +52,7 @@ namespace xo { obj() : Super() {} explicit obj(Super::DataPtr d) : Super(d) {} - obj(const obj & rhs) : Super() { - this->from_data(rhs.data_); - } + obj(const obj & rhs) = default; /** pseudo copy constructor *