From 191614ce730fe69c7c95668629f5c31855c6a193 Mon Sep 17 00:00:00 2001 From: Jamie Pond Date: Sun, 8 Jun 2025 16:53:23 -0700 Subject: [PATCH 01/10] init --- inc/zoo/Any/eta/TEC.hpp | 48 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 inc/zoo/Any/eta/TEC.hpp diff --git a/inc/zoo/Any/eta/TEC.hpp b/inc/zoo/Any/eta/TEC.hpp new file mode 100644 index 00000000..e5bc757d --- /dev/null +++ b/inc/zoo/Any/eta/TEC.hpp @@ -0,0 +1,48 @@ + +#include "zoo/AlignedStorage.h" +#include "zoo/pp/platform.h" +#include "zoo/Any/Traits.h" +#include "zoo/utility.h" + +#include "zoo/meta/copy_and_move_abilities.h" +#include + +namespace zoo::any::eta { + +template +struct TEC { + using Policy = Policy_; + + TEC() = delete; + TEC(const TEC &) = delete; + + using DefaultManager = typename Policy::MemoryLayout; + + AlignedStorageFor space_; + + using TokenType = void (TEC::*)(); + + constexpr static inline TokenType Token = nullptr; + + template + TEC(TokenType, Params &&...params) + noexcept( + std::is_nothrow_constructible_v + ) + { + space_.template build(std::forward(params)...); + } + + ~TEC() { + space_.template as()->destroy(); + } + +}; + +struct Foo { + using MemoryLayout = void *; +}; + +static_assert(!std::is_move_constructible_v>); + +} From 1754f1aa6f2c9820b907315e6fb972b174e9c63b Mon Sep 17 00:00:00 2001 From: Jamie Pond Date: Sun, 8 Jun 2025 16:55:55 -0700 Subject: [PATCH 02/10] VTableHolder -> VTablePonterWrapper --- design/generic-policy.md | 4 ++-- inc/zoo/Any/VTablePolicy.h | 6 +++--- inc/zoo/Any/eta/TEC.hpp | 14 ++++++++++++++ 3 files changed, 19 insertions(+), 5 deletions(-) diff --git a/design/generic-policy.md b/design/generic-policy.md index deb9b462..030ff9ad 100644 --- a/design/generic-policy.md +++ b/design/generic-policy.md @@ -10,7 +10,7 @@ Description of how to add polymorphic interfaces to `AnyContainer` by assembling The great software engineer Louis Dionne explains very well what normal C++ polymorphism, based on `virtual` overrides, leaves to be desired in his project [Dyno](https://github.com/ldionne/dyno). -Additionally, in this repository there are a few extra pointers with regards to more subtle needs for polymorphism, in particular sub-typing relationships as in the original meaning of [Barbara Liskov's substitution principle](https://en.wikipedia.org/wiki/Liskov_substitution_principle) that should not force their representation to be sub-classing in C++. This is better explained in the foundational work by Kevlin Henney, in the [ACCU September 2000 article "From Mechanism to Method: Substitutability"](https://accu.org/index.php/journals/475) and the [C++ Report magazine article with the original implementation of the `any` component](http://www.two-sdg.demon.co.uk/curbralan/papers/ValuedConversions.pdf), which led to `boost::function` and `std::function` and the "mainstreaming" of type erasure as an alternative for polymorphism, this has been a 20 year journey that keeps going. +Additionally, in this repository there are a few extra pointers with regards to more subtle needs for polymorphism, in particular sub-typing relationships as in the original meaning of [Barbara Liskov's substitution principle](https://en.wikipedia.org/wiki/Liskov_substitution_principle) that should not force their representation to be sub-classing in C++. This is better explained in the foundational work by Kevlin Henney, in the [ACCU September 2000 article "From Mechanism to Method: Substitutability"](https://accu.org/index.php/journals/475) and the [C++ Report magazine article with the original implementation of the `any` component](http://www.two-sdg.demon.co.uk/curbralan/papers/ValuedConversions.pdf), which led to `boost::function` and `std::function` and the "mainstreaming" of type erasure as an alternative for polymorphism, this has been a 20 year journey that keeps going. One particularly complete way of doing polymorphism (implementing sub-typing relationships) without inheritance and `virtual` overrides of member functions is "type erasure". Within "type erasure", educator Arthur O'Dwyer has coined the concept of "affordance" to refer to the polymorphic operations a "type erased" object is capable of doing, described in his excellent blog article ["What is Type Erasure?"](https://quuxplusone.github.io/blog/2019/03/18/what-is-type-erasure/). @@ -96,7 +96,7 @@ In this example, the container implementations and the affordance of copyability template struct GenericPolicy { struct VTable: Affordances::VTableEntry... {}; - using VTHolder = VTableHolder; + using VTHolder = VTablePointerWrapper; using HoldingModel = void *; struct Container: VTHolder, Affordances::template Mixin... { diff --git a/inc/zoo/Any/VTablePolicy.h b/inc/zoo/Any/VTablePolicy.h index 84b17fcb..f7236fab 100644 --- a/inc/zoo/Any/VTablePolicy.h +++ b/inc/zoo/Any/VTablePolicy.h @@ -211,7 +211,7 @@ struct CallableViaVTable { }; template -struct VTableHolder { +struct VTablePointerWrapper { const VirtualTable *pointer_; /// \brief from the vtable returns the entry corresponding to the affordance @@ -221,7 +221,7 @@ struct VTableHolder { pointer_->template upcast(); } - VTableHolder(const VirtualTable *p): pointer_(p) {} + VTablePointerWrapper(const VirtualTable *p): pointer_(p) {} auto pointer() const noexcept { return pointer_; } @@ -251,7 +251,7 @@ struct GenericPolicy { } }; - using VTHolder = VTableHolder; + using VTHolder = VTablePointerWrapper; struct MSVC_EMPTY_BASES Container: VTHolder, diff --git a/inc/zoo/Any/eta/TEC.hpp b/inc/zoo/Any/eta/TEC.hpp index e5bc757d..88730df1 100644 --- a/inc/zoo/Any/eta/TEC.hpp +++ b/inc/zoo/Any/eta/TEC.hpp @@ -33,6 +33,20 @@ struct TEC { space_.template build(std::forward(params)...); } +// AnyContainerBase(AnyContainerBase &&moveable) noexcept: +// SuperContainer( +// SuperContainer::Token, +// static_cast(moveable) +// ) +// { +// auto source = moveable.container(); +// source->move(container()); +// } + + + void move( + + ~TEC() { space_.template as()->destroy(); } From a581e5de0527abbb63cbdbb6dac91ec96a44986f Mon Sep 17 00:00:00 2001 From: Jamie Pond Date: Sun, 8 Jun 2025 17:18:20 -0700 Subject: [PATCH 03/10] builds --- benchmark/cm/main.cpp | 28 +++++++++---------- inc/zoo/AnyContainer.h | 2 +- inc/zoo/FunctionPolicy.h | 2 +- inc/zoo/{Any => tea}/DerivedVTablePolicy.h | 0 inc/zoo/{Any/eta => tea}/TEC.hpp | 4 +-- inc/zoo/{Any => tea}/Traits.h | 4 +-- inc/zoo/{Any => tea}/VTable.h | 22 +++++++++------ inc/zoo/tea/VTablePointerWrapper.h | 25 +++++++++++++++++ inc/zoo/{Any => tea}/VTablePolicy.h | 21 ++------------ test/GenericPolicy.cpp | 2 +- ...e_erasure_shared_pointer_value_manager.hpp | 4 +-- test/tec/tec.cpp | 4 +++ 12 files changed, 67 insertions(+), 51 deletions(-) rename inc/zoo/{Any => tea}/DerivedVTablePolicy.h (100%) rename inc/zoo/{Any/eta => tea}/TEC.hpp (95%) rename inc/zoo/{Any => tea}/Traits.h (95%) rename inc/zoo/{Any => tea}/VTable.h (90%) create mode 100644 inc/zoo/tea/VTablePointerWrapper.h rename inc/zoo/{Any => tea}/VTablePolicy.h (94%) create mode 100644 test/tec/tec.cpp diff --git a/benchmark/cm/main.cpp b/benchmark/cm/main.cpp index 73aeb053..8c7ef46b 100644 --- a/benchmark/cm/main.cpp +++ b/benchmark/cm/main.cpp @@ -1,4 +1,4 @@ -#include "zoo/Any/DerivedVTablePolicy.h" +#include "zoo/tea/DerivedVTablePolicy.h" #include "zoo/AnyContainer.h" #include @@ -28,7 +28,7 @@ class square : public shape_base square(f32 SideInit) : Side(SideInit) {} f32 AreaNP() {return Side*Side;} virtual f32 Area() {return Side*Side;} - + private: f32 Side; }; @@ -39,7 +39,7 @@ class rectangle : public shape_base rectangle(f32 WidthInit, f32 HeightInit) : Width(WidthInit), Height(HeightInit) {} f32 AreaNP() {return Width*Height;} virtual f32 Area() {return Width*Height;} - + private: f32 Width, Height; }; @@ -50,7 +50,7 @@ class triangle : public shape_base triangle(f32 BaseInit, f32 HeightInit) : Base(BaseInit), Height(HeightInit) {} f32 AreaNP() {return 0.5f*Base*Height;} virtual f32 Area() {return 0.5f*Base*Height;} - + private: f32 Base, Height; }; @@ -61,7 +61,7 @@ class circle : public shape_base circle(f32 RadiusInit) : Radius(RadiusInit) {} f32 AreaNP() {return Pi32*Radius*Radius;} virtual f32 Area() {return Pi32*Radius*Radius;} - + private: f32 Radius; }; @@ -77,7 +77,7 @@ f32 TotalAreaVTBL(u32 ShapeCount, shape_base **Shapes) { Accum += Shapes[ShapeIndex]->Area(); } - + return Accum; } @@ -209,7 +209,7 @@ enum shape_type : u32 Shape_Rectangle, Shape_Triangle, Shape_Circle, - + Shape_Count, }; @@ -223,17 +223,17 @@ struct shape_union f32 GetAreaSwitch(shape_union Shape) { f32 Result = 0.0f; - + switch(Shape.Type) { case Shape_Square: {Result = Shape.Width*Shape.Width;} break; case Shape_Rectangle: {Result = Shape.Width*Shape.Height;} break; case Shape_Triangle: {Result = 0.5f*Shape.Width*Shape.Height;} break; case Shape_Circle: {Result = Pi32*Shape.Width*Shape.Width;} break; - + case Shape_Count: {} break; } - + return Result; } @@ -244,7 +244,7 @@ f32 GetAreaSwitch(shape_union Shape) f32 TotalAreaSwitch(u32 ShapeCount, shape_union *Shapes) { f32 Accum = 0.0f; - + for(u32 ShapeIndex = 0; ShapeIndex < ShapeCount; ++ShapeIndex) { Accum += GetAreaSwitch(Shapes[ShapeIndex]); @@ -259,7 +259,7 @@ f32 TotalAreaSwitch4(u32 ShapeCount, shape_union *Shapes) f32 Accum1 = 0.0f; f32 Accum2 = 0.0f; f32 Accum3 = 0.0f; - + ShapeCount /= 4; while(ShapeCount--) { @@ -267,10 +267,10 @@ f32 TotalAreaSwitch4(u32 ShapeCount, shape_union *Shapes) Accum1 += GetAreaSwitch(Shapes[1]); Accum2 += GetAreaSwitch(Shapes[2]); Accum3 += GetAreaSwitch(Shapes[3]); - + Shapes += 4; } - + f32 Result = (Accum0 + Accum1 + Accum2 + Accum3); return Result; } diff --git a/inc/zoo/AnyContainer.h b/inc/zoo/AnyContainer.h index c0bdf7b1..d81033c8 100644 --- a/inc/zoo/AnyContainer.h +++ b/inc/zoo/AnyContainer.h @@ -2,7 +2,7 @@ #define ZOO_ANYCONTAINER_H #include "zoo/pp/platform.h" -#include "zoo/Any/Traits.h" +#include "zoo/tea/Traits.h" #include "zoo/utility.h" #include "zoo/meta/NotBasedOn.h" diff --git a/inc/zoo/FunctionPolicy.h b/inc/zoo/FunctionPolicy.h index b24e21c1..c624d865 100644 --- a/inc/zoo/FunctionPolicy.h +++ b/inc/zoo/FunctionPolicy.h @@ -1,7 +1,7 @@ #ifndef ZOO_FUNCTIONPOLICY_H #define ZOO_FUNCTIONPOLICY_H -#include "zoo/Any/DerivedVTablePolicy.h" +#include "zoo/tea/DerivedVTablePolicy.h" #include "zoo/AnyContainer.h" diff --git a/inc/zoo/Any/DerivedVTablePolicy.h b/inc/zoo/tea/DerivedVTablePolicy.h similarity index 100% rename from inc/zoo/Any/DerivedVTablePolicy.h rename to inc/zoo/tea/DerivedVTablePolicy.h diff --git a/inc/zoo/Any/eta/TEC.hpp b/inc/zoo/tea/TEC.hpp similarity index 95% rename from inc/zoo/Any/eta/TEC.hpp rename to inc/zoo/tea/TEC.hpp index 88730df1..7807ef74 100644 --- a/inc/zoo/Any/eta/TEC.hpp +++ b/inc/zoo/tea/TEC.hpp @@ -1,13 +1,13 @@ #include "zoo/AlignedStorage.h" #include "zoo/pp/platform.h" -#include "zoo/Any/Traits.h" +#include "zoo/tea/Traits.h" #include "zoo/utility.h" #include "zoo/meta/copy_and_move_abilities.h" #include -namespace zoo::any::eta { +namespace zoo::tea { template struct TEC { diff --git a/inc/zoo/Any/Traits.h b/inc/zoo/tea/Traits.h similarity index 95% rename from inc/zoo/Any/Traits.h rename to inc/zoo/tea/Traits.h index 60589411..31c2ddc1 100644 --- a/inc/zoo/Any/Traits.h +++ b/inc/zoo/tea/Traits.h @@ -1,5 +1,5 @@ -#ifndef ZOO_ANY_Traits_H -#define ZOO_ANY_Traits_H +#ifndef ZOO_TEA_Traits_H +#define ZOO_TEA_Traits_H #include diff --git a/inc/zoo/Any/VTable.h b/inc/zoo/tea/VTable.h similarity index 90% rename from inc/zoo/Any/VTable.h rename to inc/zoo/tea/VTable.h index 32668007..d5b77c00 100644 --- a/inc/zoo/Any/VTable.h +++ b/inc/zoo/tea/VTable.h @@ -1,8 +1,7 @@ -#ifndef Zoo_Any_VTable_h -#define Zoo_Any_VTable_h - -#include "zoo/Any/Traits.h" +#ifndef Zoo_TEA_VTable_h +#define Zoo_TEA_VTable_h +#include "zoo/AlignedStorage.h" #include #include #include @@ -16,7 +15,7 @@ struct TypeErasureOperations { template struct TypeErasedContainer { - alignas(Alignment) char space_[Size]; + AlignedStorage space_; const TypeErasureOperations *vTable_ = &Empty; template @@ -28,7 +27,12 @@ struct TypeErasedContainer { } // note: the policy operations do not assume an "Empty" to allow inheritance - // contravariance of the AnyContainer + // contravariance of a type erasure container. + // What this means is that we might want to override, for example, + // the destructor in a derived class, to replicate the behaviour of virtual + // inheritance or the normal or contravariant of destructors in conditions + // of inheritance. Therefore we can not asssume that we're working directly + // with an `Empty`. inline const static TypeErasureOperations Empty = { [](void *) noexcept {}, reinterpret_cast(copyVTable) @@ -73,9 +77,9 @@ struct SmallBufferTypeEraser: template struct ReferentialTypeEraser: TypeErasedContainer { using Me = ReferentialTypeEraser; - + V *&value() noexcept { return *this->template as(); } - + inline const static TypeErasureOperations VTable = { [](void *who) noexcept { delete static_cast(who)->value(); }, [](void *from, void *to) noexcept { @@ -86,7 +90,7 @@ struct ReferentialTypeEraser: TypeErasedContainer { source->vTable = &TypeErasedContainer::Empty; } }; - + template ReferentialTypeEraser(Args &&...args) { this->vTable_ = &VTable; diff --git a/inc/zoo/tea/VTablePointerWrapper.h b/inc/zoo/tea/VTablePointerWrapper.h new file mode 100644 index 00000000..96aac9f0 --- /dev/null +++ b/inc/zoo/tea/VTablePointerWrapper.h @@ -0,0 +1,25 @@ +#ifndef ZOO_VTABLE_POINTER_WRAPPER_H +#define ZOO_VTABLE_POINTER_WRAPPER_H + + +template +struct VTablePointerWrapper { + const VirtualTable *pointer_; + + /// \brief from the vtable returns the entry corresponding to the affordance + template + const typename Affordance::VTableEntry *vTable() const noexcept { + return //static_cast(pointer_); + pointer_->template upcast(); + } + + VTablePointerWrapper(const VirtualTable *p): pointer_(p) {} + + auto pointer() const noexcept { return pointer_; } + + void change(const VirtualTable *p) noexcept { pointer_ = p; } +}; + + +#endif // ZOO_VTABLE_POINTER_WRAPPER_H + diff --git a/inc/zoo/Any/VTablePolicy.h b/inc/zoo/tea/VTablePolicy.h similarity index 94% rename from inc/zoo/Any/VTablePolicy.h rename to inc/zoo/tea/VTablePolicy.h index f7236fab..81002075 100644 --- a/inc/zoo/Any/VTablePolicy.h +++ b/inc/zoo/tea/VTablePolicy.h @@ -1,8 +1,9 @@ #ifndef ZOO_VTABLE_POLICY_H #define ZOO_VTABLE_POLICY_H -#include "zoo/Any/Traits.h" +#include "zoo/tea/Traits.h" +#include "zoo/tea/VTablePointerWrapper.h" #include "zoo/AlignedStorage.h" #include "zoo/pp/platform.h" @@ -210,24 +211,6 @@ struct CallableViaVTable { }; }; -template -struct VTablePointerWrapper { - const VirtualTable *pointer_; - - /// \brief from the vtable returns the entry corresponding to the affordance - template - const typename Affordance::VTableEntry *vTable() const noexcept { - return //static_cast(pointer_); - pointer_->template upcast(); - } - - VTablePointerWrapper(const VirtualTable *p): pointer_(p) {} - - auto pointer() const noexcept { return pointer_; } - - void change(const VirtualTable *p) noexcept { pointer_ = p; } -}; - template struct BuilderDecider { constexpr static auto NoThrowMovable = diff --git a/test/GenericPolicy.cpp b/test/GenericPolicy.cpp index 781b7fbe..b617146e 100644 --- a/test/GenericPolicy.cpp +++ b/test/GenericPolicy.cpp @@ -2,7 +2,7 @@ #include "zoo/FundamentalOperationTracing.h" -#include "zoo/Any/DerivedVTablePolicy.h" +#include "zoo/tea/DerivedVTablePolicy.h" #include #include diff --git a/test/inc/demo/type_erasure_shared_pointer_value_manager.hpp b/test/inc/demo/type_erasure_shared_pointer_value_manager.hpp index d7b0760a..99fd18d1 100644 --- a/test/inc/demo/type_erasure_shared_pointer_value_manager.hpp +++ b/test/inc/demo/type_erasure_shared_pointer_value_manager.hpp @@ -1,4 +1,4 @@ -#include "zoo/Any/VTablePolicy.h" +#include "zoo/tea/VTablePolicy.h" #include "zoo/AnyContainer.h" #include // for shared pointer @@ -37,7 +37,7 @@ struct UserValueManagement { using VP = std::shared_ptr; /// Abbreviation using SPM = SharedPointerManager; - + VP *sharedPointer() noexcept { return this->space_.template as(); } V *value() noexcept { return &**sharedPointer(); } diff --git a/test/tec/tec.cpp b/test/tec/tec.cpp new file mode 100644 index 00000000..a21c0f9b --- /dev/null +++ b/test/tec/tec.cpp @@ -0,0 +1,4 @@ + +namespace zoo::tea { + +}; From 5b6ccaf483c8306b054940cdbc57c22f955178b7 Mon Sep 17 00:00:00 2001 From: Jamie Pond Date: Sun, 8 Jun 2025 17:20:21 -0700 Subject: [PATCH 04/10] namespace --- inc/zoo/tea/VTablePointerWrapper.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/inc/zoo/tea/VTablePointerWrapper.h b/inc/zoo/tea/VTablePointerWrapper.h index 96aac9f0..2ee1a966 100644 --- a/inc/zoo/tea/VTablePointerWrapper.h +++ b/inc/zoo/tea/VTablePointerWrapper.h @@ -2,6 +2,8 @@ #define ZOO_VTABLE_POINTER_WRAPPER_H +namespace zoo { + template struct VTablePointerWrapper { const VirtualTable *pointer_; @@ -20,6 +22,7 @@ struct VTablePointerWrapper { void change(const VirtualTable *p) noexcept { pointer_ = p; } }; +} // zoo #endif // ZOO_VTABLE_POINTER_WRAPPER_H From 92ed6ab550a1807cd2059f569b9af680795992e5 Mon Sep 17 00:00:00 2001 From: Jamie Pond Date: Sun, 8 Jun 2025 17:28:41 -0700 Subject: [PATCH 05/10] wip --- inc/zoo/{tea => Any}/DerivedVTablePolicy.h | 0 inc/zoo/{tea => Any}/VTablePolicy.h | 0 test/GenericPolicy.cpp | 2 +- 3 files changed, 1 insertion(+), 1 deletion(-) rename inc/zoo/{tea => Any}/DerivedVTablePolicy.h (100%) rename inc/zoo/{tea => Any}/VTablePolicy.h (100%) diff --git a/inc/zoo/tea/DerivedVTablePolicy.h b/inc/zoo/Any/DerivedVTablePolicy.h similarity index 100% rename from inc/zoo/tea/DerivedVTablePolicy.h rename to inc/zoo/Any/DerivedVTablePolicy.h diff --git a/inc/zoo/tea/VTablePolicy.h b/inc/zoo/Any/VTablePolicy.h similarity index 100% rename from inc/zoo/tea/VTablePolicy.h rename to inc/zoo/Any/VTablePolicy.h diff --git a/test/GenericPolicy.cpp b/test/GenericPolicy.cpp index b617146e..781b7fbe 100644 --- a/test/GenericPolicy.cpp +++ b/test/GenericPolicy.cpp @@ -2,7 +2,7 @@ #include "zoo/FundamentalOperationTracing.h" -#include "zoo/tea/DerivedVTablePolicy.h" +#include "zoo/Any/DerivedVTablePolicy.h" #include #include From 2679076d2bc32b9cf9e3885fa03a5a85c03f2b49 Mon Sep 17 00:00:00 2001 From: Jamie Pond Date: Sun, 8 Jun 2025 17:33:52 -0700 Subject: [PATCH 06/10] fix build, mv back vtable to any --- benchmark/cm/main.cpp | 2 +- inc/zoo/Any/VTable.h | 178 ++++++++++++++++++ inc/zoo/FunctionPolicy.h | 2 +- inc/zoo/tea/VTable.h | 2 +- ...e_erasure_shared_pointer_value_manager.hpp | 2 +- 5 files changed, 182 insertions(+), 4 deletions(-) create mode 100644 inc/zoo/Any/VTable.h diff --git a/benchmark/cm/main.cpp b/benchmark/cm/main.cpp index 8c7ef46b..ae9e7fb9 100644 --- a/benchmark/cm/main.cpp +++ b/benchmark/cm/main.cpp @@ -1,4 +1,4 @@ -#include "zoo/tea/DerivedVTablePolicy.h" +#include "zoo/Any/DerivedVTablePolicy.h" #include "zoo/AnyContainer.h" #include diff --git a/inc/zoo/Any/VTable.h b/inc/zoo/Any/VTable.h new file mode 100644 index 00000000..d5b77c00 --- /dev/null +++ b/inc/zoo/Any/VTable.h @@ -0,0 +1,178 @@ +#ifndef Zoo_TEA_VTable_h +#define Zoo_TEA_VTable_h + +#include "zoo/AlignedStorage.h" +#include +#include +#include + +namespace zoo { + +struct TypeErasureOperations { + void (*destroy)(void *) noexcept; + void (*move)(void *from, void *to) noexcept; +}; + +template +struct TypeErasedContainer { + AlignedStorage space_; + const TypeErasureOperations *vTable_ = &Empty; + + template + T *as() noexcept { return reinterpret_cast(space_); } + + static void copyVTable(const void *from, void *to) noexcept { + static_cast(to)->vTable_ = + static_cast(from)->vTable_; + } + + // note: the policy operations do not assume an "Empty" to allow inheritance + // contravariance of a type erasure container. + // What this means is that we might want to override, for example, + // the destructor in a derived class, to replicate the behaviour of virtual + // inheritance or the normal or contravariant of destructors in conditions + // of inheritance. Therefore we can not asssume that we're working directly + // with an `Empty`. + inline const static TypeErasureOperations Empty = { + [](void *) noexcept {}, + reinterpret_cast(copyVTable) + }; + + void destroy() noexcept { vTable_->destroy(this); } + void move(void *to) noexcept { vTable_->move(this, to); } +}; + +template +struct SmallBufferTypeEraserCRT { + V *value() noexcept { return static_cast(this)->template as(); } + + inline const static TypeErasureOperations VTable = { + [](void *who) noexcept { static_cast(who)->value()->~V(); }, + [](void *from, void *to) noexcept { + new (to) D(std::move(*static_cast(from)->value())); + } + }; + + template + void build(Args &&...args) { + new(value()) V(std::forward(args)...); + } +}; + +template +struct SmallBufferTypeEraser: + TypeErasedContainer, + SmallBufferTypeEraserCRT, V> +{ + using B = + SmallBufferTypeEraserCRT, V>; + + template + SmallBufferTypeEraser(Args &&...args) { + this->vTable_ = &B::VTable; + this->build(std::forward(args)...); + } +}; + +template +struct ReferentialTypeEraser: TypeErasedContainer { + using Me = ReferentialTypeEraser; + + V *&value() noexcept { return *this->template as(); } + + inline const static TypeErasureOperations VTable = { + [](void *who) noexcept { delete static_cast(who)->value(); }, + [](void *from, void *to) noexcept { + auto recipient = static_cast(to); + auto source = static_cast(from); + recipient->vTable_ = source->vTable_; + recipient->value() = source->value(); + source->vTable = &TypeErasedContainer::Empty; + } + }; + + template + ReferentialTypeEraser(Args &&...args) { + this->vTable_ = &VTable; + value() = new V(std::forward(args)...); + } +}; + +template +struct VTablePolicy { + using MemoryLayout = TypeErasedContainer; + + template + using Builder = std::conditional_t< + sizeof(T) <= Size && (0 == Alignment % alignof(T)) && + std::is_nothrow_move_constructible_v, + SmallBufferTypeEraser, + ReferentialTypeEraser + >; +}; + +namespace RTTI { + +struct RTTIOperation: TypeErasureOperations { + const std::type_info &(*type)() noexcept; +}; + +template +struct VTCBase { + const std::type_info &type() const noexcept { + return + static_cast( + static_cast(this)->vTable_ + )->type(); + } +}; + +template +struct TEC: TypeErasedContainer, VTCBase> { + TEC() { this->vTable_ = &Defaulted; } + + using B = TypeErasedContainer; + + inline static const RTTIOperation Defaulted = { + B::Empty.destroy, + B::Empty.move, + []() noexcept -> decltype(auto) { return typeid(void); } + }; +}; + +template +struct SBTE: SmallBufferTypeEraserCRT, V>, TEC { + using Base = SmallBufferTypeEraserCRT, V>; + + template + SBTE(Args &&...args): Base(std::forward(args)...) { + this->vTable_ = &VTable; + } + + RTTIOperation VTable = { + Base::VTable::destroy, + Base::VTable::move, + []() noexcept -> decltype(auto) { return typeid(V); } + }; +}; + +template +struct RTTI { + using MemoryLayout = TEC; + + template + using Builder = SBTE; + + template + struct Affordances { + const std::type_info &type() const noexcept { + return static_cast(this)->container()->type(); + } + }; + + constexpr static auto RequireMoveOnly = true; +}; + +}} + +#endif /* VTable_h */ diff --git a/inc/zoo/FunctionPolicy.h b/inc/zoo/FunctionPolicy.h index c624d865..b24e21c1 100644 --- a/inc/zoo/FunctionPolicy.h +++ b/inc/zoo/FunctionPolicy.h @@ -1,7 +1,7 @@ #ifndef ZOO_FUNCTIONPOLICY_H #define ZOO_FUNCTIONPOLICY_H -#include "zoo/tea/DerivedVTablePolicy.h" +#include "zoo/Any/DerivedVTablePolicy.h" #include "zoo/AnyContainer.h" diff --git a/inc/zoo/tea/VTable.h b/inc/zoo/tea/VTable.h index d5b77c00..922dab5e 100644 --- a/inc/zoo/tea/VTable.h +++ b/inc/zoo/tea/VTable.h @@ -32,7 +32,7 @@ struct TypeErasedContainer { // the destructor in a derived class, to replicate the behaviour of virtual // inheritance or the normal or contravariant of destructors in conditions // of inheritance. Therefore we can not asssume that we're working directly - // with an `Empty`. + // with an `Empty. inline const static TypeErasureOperations Empty = { [](void *) noexcept {}, reinterpret_cast(copyVTable) diff --git a/test/inc/demo/type_erasure_shared_pointer_value_manager.hpp b/test/inc/demo/type_erasure_shared_pointer_value_manager.hpp index 99fd18d1..05b0109b 100644 --- a/test/inc/demo/type_erasure_shared_pointer_value_manager.hpp +++ b/test/inc/demo/type_erasure_shared_pointer_value_manager.hpp @@ -1,4 +1,4 @@ -#include "zoo/tea/VTablePolicy.h" +#include "zoo/Any/VTablePolicy.h" #include "zoo/AnyContainer.h" #include // for shared pointer From 10fe76811a436df16aef12ac30e06aaaaaaf864b Mon Sep 17 00:00:00 2001 From: Jamie Pond Date: Sun, 8 Jun 2025 17:34:32 -0700 Subject: [PATCH 07/10] rm again vtable tea --- inc/zoo/tea/VTable.h | 178 ------------------------------------------- 1 file changed, 178 deletions(-) delete mode 100644 inc/zoo/tea/VTable.h diff --git a/inc/zoo/tea/VTable.h b/inc/zoo/tea/VTable.h deleted file mode 100644 index 922dab5e..00000000 --- a/inc/zoo/tea/VTable.h +++ /dev/null @@ -1,178 +0,0 @@ -#ifndef Zoo_TEA_VTable_h -#define Zoo_TEA_VTable_h - -#include "zoo/AlignedStorage.h" -#include -#include -#include - -namespace zoo { - -struct TypeErasureOperations { - void (*destroy)(void *) noexcept; - void (*move)(void *from, void *to) noexcept; -}; - -template -struct TypeErasedContainer { - AlignedStorage space_; - const TypeErasureOperations *vTable_ = &Empty; - - template - T *as() noexcept { return reinterpret_cast(space_); } - - static void copyVTable(const void *from, void *to) noexcept { - static_cast(to)->vTable_ = - static_cast(from)->vTable_; - } - - // note: the policy operations do not assume an "Empty" to allow inheritance - // contravariance of a type erasure container. - // What this means is that we might want to override, for example, - // the destructor in a derived class, to replicate the behaviour of virtual - // inheritance or the normal or contravariant of destructors in conditions - // of inheritance. Therefore we can not asssume that we're working directly - // with an `Empty. - inline const static TypeErasureOperations Empty = { - [](void *) noexcept {}, - reinterpret_cast(copyVTable) - }; - - void destroy() noexcept { vTable_->destroy(this); } - void move(void *to) noexcept { vTable_->move(this, to); } -}; - -template -struct SmallBufferTypeEraserCRT { - V *value() noexcept { return static_cast(this)->template as(); } - - inline const static TypeErasureOperations VTable = { - [](void *who) noexcept { static_cast(who)->value()->~V(); }, - [](void *from, void *to) noexcept { - new (to) D(std::move(*static_cast(from)->value())); - } - }; - - template - void build(Args &&...args) { - new(value()) V(std::forward(args)...); - } -}; - -template -struct SmallBufferTypeEraser: - TypeErasedContainer, - SmallBufferTypeEraserCRT, V> -{ - using B = - SmallBufferTypeEraserCRT, V>; - - template - SmallBufferTypeEraser(Args &&...args) { - this->vTable_ = &B::VTable; - this->build(std::forward(args)...); - } -}; - -template -struct ReferentialTypeEraser: TypeErasedContainer { - using Me = ReferentialTypeEraser; - - V *&value() noexcept { return *this->template as(); } - - inline const static TypeErasureOperations VTable = { - [](void *who) noexcept { delete static_cast(who)->value(); }, - [](void *from, void *to) noexcept { - auto recipient = static_cast(to); - auto source = static_cast(from); - recipient->vTable_ = source->vTable_; - recipient->value() = source->value(); - source->vTable = &TypeErasedContainer::Empty; - } - }; - - template - ReferentialTypeEraser(Args &&...args) { - this->vTable_ = &VTable; - value() = new V(std::forward(args)...); - } -}; - -template -struct VTablePolicy { - using MemoryLayout = TypeErasedContainer; - - template - using Builder = std::conditional_t< - sizeof(T) <= Size && (0 == Alignment % alignof(T)) && - std::is_nothrow_move_constructible_v, - SmallBufferTypeEraser, - ReferentialTypeEraser - >; -}; - -namespace RTTI { - -struct RTTIOperation: TypeErasureOperations { - const std::type_info &(*type)() noexcept; -}; - -template -struct VTCBase { - const std::type_info &type() const noexcept { - return - static_cast( - static_cast(this)->vTable_ - )->type(); - } -}; - -template -struct TEC: TypeErasedContainer, VTCBase> { - TEC() { this->vTable_ = &Defaulted; } - - using B = TypeErasedContainer; - - inline static const RTTIOperation Defaulted = { - B::Empty.destroy, - B::Empty.move, - []() noexcept -> decltype(auto) { return typeid(void); } - }; -}; - -template -struct SBTE: SmallBufferTypeEraserCRT, V>, TEC { - using Base = SmallBufferTypeEraserCRT, V>; - - template - SBTE(Args &&...args): Base(std::forward(args)...) { - this->vTable_ = &VTable; - } - - RTTIOperation VTable = { - Base::VTable::destroy, - Base::VTable::move, - []() noexcept -> decltype(auto) { return typeid(V); } - }; -}; - -template -struct RTTI { - using MemoryLayout = TEC; - - template - using Builder = SBTE; - - template - struct Affordances { - const std::type_info &type() const noexcept { - return static_cast(this)->container()->type(); - } - }; - - constexpr static auto RequireMoveOnly = true; -}; - -}} - -#endif /* VTable_h */ From 6921956d87528ff4dc03ee67a8e9a4643ab44e08 Mon Sep 17 00:00:00 2001 From: Jamie Pond Date: Sun, 8 Jun 2025 17:35:51 -0700 Subject: [PATCH 08/10] rename guard --- inc/zoo/Any/VTable.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/inc/zoo/Any/VTable.h b/inc/zoo/Any/VTable.h index d5b77c00..28039281 100644 --- a/inc/zoo/Any/VTable.h +++ b/inc/zoo/Any/VTable.h @@ -1,5 +1,5 @@ -#ifndef Zoo_TEA_VTable_h -#define Zoo_TEA_VTable_h +#ifndef Zoo_Any_VTable_h +#define Zoo_Any_VTable_h #include "zoo/AlignedStorage.h" #include From 1fafc601097f892bf58449b2cc797bd76cf37f8c Mon Sep 17 00:00:00 2001 From: Jamie Pond Date: Sun, 8 Jun 2025 18:03:45 -0700 Subject: [PATCH 09/10] builds --- inc/zoo/tea/TEC.hpp | 21 ++++++++++++++++++++- test/CMakeLists.txt | 2 +- test/tec/tec.cpp | 1 + 3 files changed, 22 insertions(+), 2 deletions(-) diff --git a/inc/zoo/tea/TEC.hpp b/inc/zoo/tea/TEC.hpp index 7807ef74..443040c2 100644 --- a/inc/zoo/tea/TEC.hpp +++ b/inc/zoo/tea/TEC.hpp @@ -2,6 +2,7 @@ #include "zoo/AlignedStorage.h" #include "zoo/pp/platform.h" #include "zoo/tea/Traits.h" +#include "zoo/tea/VTablePointerWrapper.h" #include "zoo/utility.h" #include "zoo/meta/copy_and_move_abilities.h" @@ -9,13 +10,31 @@ namespace zoo::tea { +template +struct HasVTableMember: std::false_type {}; + +template +struct HasVTableMember>: std::true_type {}; + +struct Boo { + using VTable = int; +}; + +static_assert(!HasVTableMember::value); +static_assert(HasVTableMember::value); + + template struct TEC { using Policy = Policy_; + // static_assert(std:: + TEC() = delete; TEC(const TEC &) = delete; + + using DefaultManager = typename Policy::MemoryLayout; AlignedStorageFor space_; @@ -44,7 +63,7 @@ struct TEC { // } - void move( + // void move( ~TEC() { diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index ef981efc..39880626 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -110,7 +110,7 @@ else() set( TYPE_ERASURE_SOURCES any.cpp AlignedStorage.cpp AnyCallable.cpp AnyCallSignature.cpp - AnyExtended.cpp GenericPolicy.cpp FunctionPolicy.cpp + AnyExtended.cpp GenericPolicy.cpp FunctionPolicy.cpp tec/tec.cpp ) set( SWAR_SOURCES diff --git a/test/tec/tec.cpp b/test/tec/tec.cpp index a21c0f9b..17e79a36 100644 --- a/test/tec/tec.cpp +++ b/test/tec/tec.cpp @@ -1,3 +1,4 @@ +#include "zoo/tea/TEC.hpp" namespace zoo::tea { From cbc02179549324d036b789bc26b4cd7e4824fbba Mon Sep 17 00:00:00 2001 From: Jamie Pond Date: Sun, 8 Jun 2025 18:35:19 -0700 Subject: [PATCH 10/10] builds again --- inc/zoo/AnyContainer.h | 41 ++++++++-------------------------- inc/zoo/tea/AffordsCopying.h | 10 +++++++++ inc/zoo/tea/AffordsCopying.hpp | 38 +++++++++++++++++++++++++++++++ inc/zoo/tea/TEC.hpp | 24 ++++++++++++++------ test/FunctionPolicy.cpp | 2 +- test/GenericPolicy.cpp | 6 ++--- 6 files changed, 78 insertions(+), 43 deletions(-) create mode 100644 inc/zoo/tea/AffordsCopying.h create mode 100644 inc/zoo/tea/AffordsCopying.hpp diff --git a/inc/zoo/AnyContainer.h b/inc/zoo/AnyContainer.h index d81033c8..7563bec9 100644 --- a/inc/zoo/AnyContainer.h +++ b/inc/zoo/AnyContainer.h @@ -3,6 +3,7 @@ #include "zoo/pp/platform.h" #include "zoo/tea/Traits.h" +#include "zoo/tea/AffordsCopying.hpp" #include "zoo/utility.h" #include "zoo/meta/NotBasedOn.h" @@ -24,32 +25,7 @@ struct PolicyDefaultBuilder> { using type = typename P::DefaultImplementation; }; -template -struct MemoryLayoutHasCopy: std::false_type {}; -template -struct MemoryLayoutHasCopy< - Policy, - std::void_t ->: std::true_type {}; - -template -struct ExtraAffordanceOfCopying: std::false_type {}; -template -struct ExtraAffordanceOfCopying< - Policy, - std::void_t ->: std::true_type {}; - -/// Copy constructibility and assignability are fundamental operations that -/// can not be enabled/disabled with SFINAE, this trait is to detect copyability -/// in a policy -template -using AffordsCopying = - std::disjunction< - MemoryLayoutHasCopy, ExtraAffordanceOfCopying - >; - -} +} // detail template struct CompositionChain { @@ -90,7 +66,7 @@ struct MSVC_EMPTY_BASES AnyContainerBase: using Policy = Policy_; using SuperContainer = typename CompositionChain::Base; using Container = typename Policy::MemoryLayout; - constexpr static auto Copyable = detail::AffordsCopying::value; + constexpr static auto Copyable = tea::detail::AffordsCopying::value; AnyContainerBase() noexcept: SuperContainer(SuperContainer::Token, nullptr) @@ -316,10 +292,10 @@ struct AnyCopyable: AnyContainerBase { Base(Base::Token, model) { auto source = model.container(); - if constexpr(detail::MemoryLayoutHasCopy::value) { + if constexpr(tea::detail::MemoryLayoutHasCopy::value) { source->copy(this->container()); } else { - static_assert(detail::ExtraAffordanceOfCopying::value); + static_assert(tea::detail::ExtraAffordanceOfCopying::value); Policy::ExtraAffordances::copy(this->container(), source); } } @@ -334,10 +310,10 @@ struct AnyCopyable: AnyContainerBase { myself->destroy(); try { auto source = model.container(); - if constexpr(detail::MemoryLayoutHasCopy::value) { + if constexpr(tea::detail::MemoryLayoutHasCopy::value) { source->copy(this->container()); } else { - static_assert(detail::ExtraAffordanceOfCopying::value); + static_assert(tea::detail::ExtraAffordanceOfCopying::value); Policy::ExtraAffordances::copy(this->container(), source); } } catch(...) { @@ -354,7 +330,7 @@ struct AnyCopyable: AnyContainerBase { template #define PP_BASE_TYPE \ std::conditional_t< \ - detail::AffordsCopying::value, \ + tea::detail::AffordsCopying::value, \ AnyCopyable, \ AnyContainerBase \ > @@ -378,6 +354,7 @@ T *anyContainerCast(const AnyContainer *ptr) noexcept { return const_cast(ptr->template state()); } + } #endif diff --git a/inc/zoo/tea/AffordsCopying.h b/inc/zoo/tea/AffordsCopying.h new file mode 100644 index 00000000..e59b229d --- /dev/null +++ b/inc/zoo/tea/AffordsCopying.h @@ -0,0 +1,10 @@ + AnyContainerBase(AnyContainerBase &&moveable) noexcept: + SuperContainer( + SuperContainer::Token, + static_cast(moveable) + ) + { + auto source = moveable.container(); + source->move(container()); + } + diff --git a/inc/zoo/tea/AffordsCopying.hpp b/inc/zoo/tea/AffordsCopying.hpp new file mode 100644 index 00000000..ebe4e0ec --- /dev/null +++ b/inc/zoo/tea/AffordsCopying.hpp @@ -0,0 +1,38 @@ +#ifndef ZOO_TEA_AFFORDSCOPYING_H +#define ZOO_TEA_AFFORDSCOPYING_H +#include + +namespace zoo::tea { + +namespace detail { + +template +struct MemoryLayoutHasCopy: std::false_type {}; +template +struct MemoryLayoutHasCopy< + Policy, + std::void_t +>: std::true_type {}; + +template +struct ExtraAffordanceOfCopying: std::false_type {}; +template +struct ExtraAffordanceOfCopying< + Policy, + std::void_t +>: std::true_type {}; + +/// Copy constructibility and assignability are fundamental operations that +/// can not be enabled/disabled with SFINAE, this trait is to detect copyability +/// in a policy +template +using AffordsCopying = + std::disjunction< + detail::MemoryLayoutHasCopy, detail::ExtraAffordanceOfCopying + >; + +} // detail + +} // zoo::tea + +#endif diff --git a/inc/zoo/tea/TEC.hpp b/inc/zoo/tea/TEC.hpp index 443040c2..5893ffed 100644 --- a/inc/zoo/tea/TEC.hpp +++ b/inc/zoo/tea/TEC.hpp @@ -10,6 +10,8 @@ namespace zoo::tea { +namespace detail { + template struct HasVTableMember: std::false_type {}; @@ -23,18 +25,17 @@ struct Boo { static_assert(!HasVTableMember::value); static_assert(HasVTableMember::value); +} // detail + template struct TEC { using Policy = Policy_; - - // static_assert(std:: + static_assert(zoo::tea::detail::HasVTableMember::value); TEC() = delete; TEC(const TEC &) = delete; - - using DefaultManager = typename Policy::MemoryLayout; AlignedStorageFor space_; @@ -72,10 +73,19 @@ struct TEC { }; -struct Foo { - using MemoryLayout = void *; + +template +struct CopyableTEC: TEC { + +}; + + +struct FoolishPolicy { + using VTable = int; + struct MemoryLayout {}; }; -static_assert(!std::is_move_constructible_v>); + +static_assert(!std::is_move_constructible_v>); } diff --git a/test/FunctionPolicy.cpp b/test/FunctionPolicy.cpp index 74fbd67c..4cd4f663 100644 --- a/test/FunctionPolicy.cpp +++ b/test/FunctionPolicy.cpp @@ -109,7 +109,7 @@ TEST_CASE("New zoo function", "[any][generic-policy][type-erasure][functional]") static_assert(is_base_of_v); static_assert(is_same_v); static_assert(is_constructible_v); - static_assert(zoo::detail::AffordsCopying::value); + static_assert(zoo::tea::detail::AffordsCopying::value); RCF withRTTI(std::move(doubler)); REQUIRE(2.0 == withRTTI(1)); auto &type = withRTTI.type2(); diff --git a/test/GenericPolicy.cpp b/test/GenericPolicy.cpp index 781b7fbe..bc6a344d 100644 --- a/test/GenericPolicy.cpp +++ b/test/GenericPolicy.cpp @@ -55,7 +55,7 @@ static_assert(alignof(SizedContainer<7>) == alignof(void *)); // containers without copy are move-only using MoveOnlyPolicy = Policy; -static_assert(!detail::AffordsCopying::value); +static_assert(!tea::detail::AffordsCopying::value); using MOAC = AnyContainer; static_assert(2 * sizeof(void *) == sizeof(MOAC)); static_assert(!is_copy_constructible_v); @@ -63,13 +63,13 @@ static_assert(is_nothrow_move_constructible_v); // containers with copy are copyable using CopyableNonComposedPolicy = Policy; -static_assert(detail::AffordsCopying::value); +static_assert(tea::detail::AffordsCopying::value); using CopyableNonComposedAC = AnyContainer; static_assert(is_copy_constructible_v); static_assert(is_nothrow_move_constructible_v); using CopyableAnyContainerBasedPolicy = DerivedVTablePolicy; -static_assert(detail::AffordsCopying::value); +static_assert(tea::detail::AffordsCopying::value); using DerivedCopyableAC = AnyContainer; static_assert(is_copy_constructible_v); static_assert(is_nothrow_move_constructible_v);