xo-facet: + doc scaffold + begin include scaffold

This commit is contained in:
Roland Conybeare 2025-12-10 09:56:09 -05:00
commit a4e88b674e
12 changed files with 181 additions and 3 deletions

View file

@ -75,6 +75,7 @@ set(DOX_EXCLUDE_PATTERNS [=[
# in reverse topological order i.e. dependencies first
add_subdirectory(xo-cmake)
add_subdirectory(xo-facet)
add_subdirectory(xo-indentlog)
add_subdirectory(xo-allocutil)
add_subdirectory(xo-refcnt)

View file

@ -17,7 +17,7 @@ author = 'Roland Conybeare'
extensions = [ "breathe",
"sphinx.ext.mathjax", # inline math
"sphinx.ext.autodoc", # generate info from docstrings
"sphinxcontrib.ditaa", # diagrams-through-ascii-art
# "sphinxcontrib.ditaa", # diagrams-through-ascii-art
"sphinxcontrib.plantuml", # text -> uml diagrams
]

View file

@ -421,6 +421,7 @@ in
pkgs = pkgs;
xo = {
cmake = pkgs.xo-cmake;
# facet = pkgs.xo-facet;
indentlog = pkgs.xo-indentlog;
refcnt = pkgs.xo-refcnt;
subsys = pkgs.xo-subsys;

View file

@ -12,6 +12,7 @@ Some features: kalman filters, stochastic processes, complex event processing, s
:caption: XO contents
docs/install
xo-facet/docs/index
xo-alloc/docs/index
xo-indentlog/docs/index
xo-flatstring/docs/index

View file

@ -261,7 +261,7 @@ namespace xo {
};
bool
IComplex_Any::_valid = valid_interface_implementation<AComplex, IComplex_Any>;
IComplex_Any::_valid = valid_interface_implementation<AComplex, IComplex_Any>();
// ----------------------------------------------------------------

1
xo-facet/docs/_static/README vendored Normal file
View file

@ -0,0 +1 @@
add any static {.html, .js, ..} files for sphinx to pickup here

BIN
xo-facet/docs/_static/favicon.ico vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 303 KiB

BIN
xo-facet/docs/_static/img/favicon.ico vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 303 KiB

View file

@ -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

View 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 **/

View file

@ -0,0 +1,6 @@
/* file facet_utest_main.cpp */
#define CATCH_CONFIG_MAIN
#include "catch2/catch.hpp"
/* end facet_utest_main.cpp */

View 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 */