xo-facet: + doc scaffold + begin include scaffold
This commit is contained in:
parent
a18a819838
commit
85d4f0ee15
7 changed files with 176 additions and 1 deletions
1
docs/_static/README
vendored
Normal file
1
docs/_static/README
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
add any static {.html, .js, ..} files for sphinx to pickup here
|
||||
BIN
docs/_static/favicon.ico
vendored
Normal file
BIN
docs/_static/favicon.ico
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 303 KiB |
BIN
docs/_static/img/favicon.ico
vendored
Normal file
BIN
docs/_static/img/favicon.ico
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 303 KiB |
|
|
@ -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
|
||||
|
|
|
|||
68
include/xo/facet/AFacet.hpp
Normal file
68
include/xo/facet/AFacet.hpp
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
/** @file AFacet.hpp
|
||||
*
|
||||
* @author Roland Conybeare, Dec 2025
|
||||
**/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <concepts>
|
||||
#include <type_traits>
|
||||
|
||||
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 <typename T>
|
||||
concept abstract_facet = requires {
|
||||
std::is_abstract_v<T>,
|
||||
std::is_polymorphic_v<T>;
|
||||
/** require no state, just a single vtable pointer **/
|
||||
sizeof(T) == sizeof(detail::PlaceholderAbstractInterface);
|
||||
!std::has_virtual_destructor_v<T>;
|
||||
std::is_trivially_destructible_v<T>;
|
||||
};
|
||||
|
||||
/** Use: when defining an abstract facet AMyFacet
|
||||
*
|
||||
* struct AMyFacet {
|
||||
* virtual void foo(void * data) const = 0;
|
||||
* };
|
||||
*
|
||||
* static_assert(valid_abstract_facet<AMyFacet>());
|
||||
*
|
||||
**/
|
||||
template <typename T>
|
||||
consteval bool valid_abstract_facet()
|
||||
{
|
||||
static_assert
|
||||
(std::is_abstract_v<T>,
|
||||
"Abstract facet is expected to have all-abstract methods");
|
||||
static_assert
|
||||
(std::is_polymorphic_v<T>,
|
||||
"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<T>,
|
||||
"Abstract facet does not benefit from virtual dtor since no state");
|
||||
static_assert
|
||||
(std::is_trivially_destructible_v<T>,
|
||||
"Abstract facet expected to have trivial dtor since no state");
|
||||
return true;
|
||||
};
|
||||
|
||||
} /*namespace facet*/
|
||||
} /*namespace xo*/
|
||||
|
||||
/* end AFacet.hpp **/
|
||||
6
utest/facet_utest_main.cpp
Normal file
6
utest/facet_utest_main.cpp
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
/* file facet_utest_main.cpp */
|
||||
|
||||
#define CATCH_CONFIG_MAIN
|
||||
#include "catch2/catch.hpp"
|
||||
|
||||
/* end facet_utest_main.cpp */
|
||||
61
utest/objectmodel.test.cpp
Normal file
61
utest/objectmodel.test.cpp
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
/** @file objectmodel.test.cpp
|
||||
*
|
||||
* @author Roland Conybeare, Dec 2025
|
||||
**/
|
||||
|
||||
#include "xo/facet/AFacet.hpp"
|
||||
#include <catch2/catch.hpp>
|
||||
#include <cmath>
|
||||
#include <cassert>
|
||||
#include <cstring>
|
||||
|
||||
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<AComplex>();
|
||||
|
||||
// ----- 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<AComplex, IComplex_Any>();
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/* end objectmodel.test.cpp */
|
||||
Loading…
Add table
Add a link
Reference in a new issue