diff --git a/docs/_static/README b/docs/_static/README new file mode 100644 index 0000000..7297d04 --- /dev/null +++ b/docs/_static/README @@ -0,0 +1 @@ +add any static {.html, .js, ..} files for sphinx to pickup here diff --git a/docs/_static/favicon.ico b/docs/_static/favicon.ico new file mode 100644 index 0000000..4163dd6 Binary files /dev/null and b/docs/_static/favicon.ico differ diff --git a/docs/_static/img/favicon.ico b/docs/_static/img/favicon.ico new file mode 100644 index 0000000..4163dd6 Binary files /dev/null and b/docs/_static/img/favicon.ico differ diff --git a/docs/index.rst b/docs/index.rst index fdbef68..bc230b0 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -4,7 +4,46 @@ xo-facet documentation ====================== xo-facet provides an object model that supports runtime polymorphism with interfaces and data kept separate. -Similar to rust traits, haskell type clases, go interfaces. +Design operates on similar lines to rust traits, haskell type clases, and go interfaces. + +Principles +---------- + +* Keep interfaces and data structures separate. + An object is represented using a combination of exactly two pointers: + an interface pointer and a data pointer. + +* An interface pointer implements an abstract facet. + A facet has only abstract methods, and no state. + +* An interface pointer is analogous to a vtable pointer in a regular + c++ object. It identifies a suite of related functions that operate + on a particular data type. + +* A data pointer is like a pointer to a c struct. + Data objects are passive, except for necessary ctors/dtors. + Runtime polymorphism works seamlessly across different data types + without requiring any prearrangement such as sharing a common + base class. + +* We make 'familiar c++ objects', on demand, by pairing an interface pointer + with a data pointer. Unlike usual c++ practice, we expect such objects + to be transient. To represent persistent state, we rely + solely on data pointers. + +* Since interface+data are separate, + we can easily swap out one interface for another. + +This gives us several benefits: + +* A data type can easily particpate in polymorphism across different facets, + without complicating object representation. To convert an object to use a + different facet, we just swap out the interface pointer. + +* Interface and data pointers can arrive at the doorstep of a computation + by different pathways. Often the pathway for an interface pointer is + simpler that the pathway for a data pointer. This increases scope for + devirtualization. .. toctree:: :maxdepth: 2 diff --git a/include/xo/facet/AFacet.hpp b/include/xo/facet/AFacet.hpp new file mode 100644 index 0000000..2e48eb6 --- /dev/null +++ b/include/xo/facet/AFacet.hpp @@ -0,0 +1,68 @@ +/** @file AFacet.hpp + * + * @author Roland Conybeare, Dec 2025 + **/ + +#pragma once + +#include +#include + +namespace xo { + namespace facet { + namespace detail { + struct PlaceholderAbstractInterface { + virtual double foo(void * data) const = 0; + }; + + static_assert(sizeof(PlaceholderAbstractInterface) == sizeof(void*)); + } + + /** Concept: abstract interface requirements + * Use: when inheriting an abstract interface + * (see also valid_abstract_interface() below) + **/ + template + concept abstract_facet = requires { + std::is_abstract_v, + std::is_polymorphic_v; + /** require no state, just a single vtable pointer **/ + sizeof(T) == sizeof(detail::PlaceholderAbstractInterface); + !std::has_virtual_destructor_v; + std::is_trivially_destructible_v; + }; + + /** Use: when defining an abstract facet AMyFacet + * + * struct AMyFacet { + * virtual void foo(void * data) const = 0; + * }; + * + * static_assert(valid_abstract_facet()); + * + **/ + template + consteval bool valid_abstract_facet() + { + static_assert + (std::is_abstract_v, + "Abstract facet is expected to have all-abstract methods"); + static_assert + (std::is_polymorphic_v, + "Abstract facet is expected to have vtable"); + static_assert + (sizeof(T) == sizeof(detail::PlaceholderAbstractInterface), + "Abstract facet is expected to have no state except for a single vtable pointer"); + static_assert + (!std::has_virtual_destructor_v, + "Abstract facet does not benefit from virtual dtor since no state"); + static_assert + (std::is_trivially_destructible_v, + "Abstract facet expected to have trivial dtor since no state"); + return true; + }; + + } /*namespace facet*/ +} /*namespace xo*/ + +/* end AFacet.hpp **/ diff --git a/utest/facet_utest_main.cpp b/utest/facet_utest_main.cpp new file mode 100644 index 0000000..fc7126f --- /dev/null +++ b/utest/facet_utest_main.cpp @@ -0,0 +1,6 @@ +/* file facet_utest_main.cpp */ + +#define CATCH_CONFIG_MAIN +#include "catch2/catch.hpp" + +/* end facet_utest_main.cpp */ diff --git a/utest/objectmodel.test.cpp b/utest/objectmodel.test.cpp new file mode 100644 index 0000000..1dd0ece --- /dev/null +++ b/utest/objectmodel.test.cpp @@ -0,0 +1,61 @@ +/** @file objectmodel.test.cpp + * + * @author Roland Conybeare, Dec 2025 + **/ + +#include "xo/facet/AFacet.hpp" +#include +#include +#include +#include + +namespace xo { + using xo::facet::valid_abstract_facet; + + namespace ut { + // ------ AComplex ----- + + /** abstract interface for a complex number **/ + struct AComplex { + using TypeErasedIface = struct IComplex_Any; + + virtual double xcoord(void * data) const = 0; + virtual double ycoord(void * data) const = 0; + virtual double argument(void * data) const = 0; + virtual double magnitude(void * data) const = 0; + + virtual void destruct_data(void * data) const = 0; + + private: + static bool _valid; + }; + + bool + AComplex::_valid = valid_abstract_facet(); + + // ----- IComplex_Any ----- + + /** type-erased implementation of AComplex, for runtime polymorphism + * Usable by (and only by) overwriting with a typed implementation, + * such as IComplex_RectCoords or IComplex_PolarCoords. + **/ + struct IComplex_Any : public AComplex { + virtual double xcoord(void *) const final override { assert(false); return 0.0; } + 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_data(void *) const final override { assert(false); } + + private: + static bool _valid; + }; + + bool + IComplex_Any::_valid = true; //valid_interface_implementation(); + + + } +} + +/* end objectmodel.test.cpp */