xo-facet: runtime facet lookup [PROTO]

This commit is contained in:
Roland Conybeare 2026-01-09 10:07:20 -05:00
commit fe4fc4a28d
5 changed files with 86 additions and 3 deletions

View file

@ -10,6 +10,8 @@ Library dependency tower for *xo-facet*:
+-----------------+ +-----------------+
| xo_facet | | xo_facet |
+-----------------+ +-----------------+
| xo_arena |
+-----------------+
| xo_cmake | | xo_cmake |
+-----------------+ +-----------------+
@ -18,6 +20,8 @@ Abstraction tower for *xo-facet* components.
.. ditaa:: .. ditaa::
:--scale: 0.85 :--scale: 0.85
+--------------------------------+
| FacetRegistry |
+--------------------------------+ +--------------------------------+
| obj(A,D) | | obj(A,D) |
+--------------------------------+ +--------------------------------+
@ -37,9 +41,12 @@ Abstraction tower for *xo-facet* components.
* - Component * - Component
- Use - Use
- Description - Description
* - ``FacetRegistry``
- ``FacetRegistry::convert_to<ATo>(obj<AFrom>)``
- polymorphic conversion between facets
* - ``obj<A,D>`` * - ``obj<A,D>``
- ``x.foo()`` - ``x.foo()``
- convenience wrapper with interface A, with state D* - convenience wrapper for interface A, with state D*
* - ``RRouter<A,D>`` * - ``RRouter<A,D>``
- ``x.foo()`` - ``x.foo()``
- auto injects data pointer - auto injects data pointer

View file

@ -45,6 +45,7 @@ This gives us several benefits:
simpler that the pathway for a data pointer. This increases scope for simpler that the pathway for a data pointer. This increases scope for
devirtualization. devirtualization.
.. toctree:: .. toctree::
:maxdepth: 2 :maxdepth: 2
:caption: xo-facet contents :caption: xo-facet contents

View file

@ -9,6 +9,7 @@
#include "facet_implementation.hpp" #include "facet_implementation.hpp"
#include "typeseq.hpp" #include "typeseq.hpp"
#include "obj.hpp"
#include <unordered_map> #include <unordered_map>
#include <utility> #include <utility>
@ -88,7 +89,8 @@ namespace xo {
bool contains(typeseq facet_id, bool contains(typeseq facet_id,
typeseq repr_id) const typeseq repr_id) const
{ {
return registry_.find(key_type(facet_id, repr_id)) != registry_.end(); return (registry_.find(key_type(facet_id, repr_id))
!= registry_.end());
} }
/** Type-safe lookup /** Type-safe lookup
@ -99,7 +101,37 @@ namespace xo {
**/ **/
template <typename AFacet> template <typename AFacet>
const AFacet * lookup(typeseq repr_id) const { const AFacet * lookup(typeseq repr_id) const {
return static_cast<const AFacet *>(this->_lookup(typeseq::id<AFacet>(), repr_id)); return static_cast<const AFacet *>
(this->_lookup(typeseq::id<AFacet>(), repr_id));
}
/** Runtime polymorphism:
* Switch @param from from interface @tp AFrom to interface @tp ATo.
*
* Use:
* obj<AFoo> foo
* = ...; // Foo instance with variant impl
* obj<ABar> bar
* = FacetRegistry::variant<ABar,AFoo>(foo);
**/
template <typename ATo, typename AFrom>
obj<ATo> variant(obj<AFrom> from) {
return variant<ATo>(from._typeseq(), from.data());
}
/** Runtime polymorphism:
* Create variant from representation @p data
* with actual type @p repr_id.
*
* Use:
* obj<AFoo> foo = ...; // Foo instance with variant impl
* obj<ABar> bar
* = FacetRegistry::variant<ABar>(foo._typeseq(), foo.opaque_data());
**/
template <typename AFacet>
obj<AFacet> variant(typeseq repr_id, void * data) {
const AFacet * iface = this->lookup<AFacet>(repr_id);
return obj<AFacet>::variant(iface, data);;
} }
private: private:
@ -134,6 +166,7 @@ namespace xo {
private: private:
FacetRegistry() = default; FacetRegistry() = default;
/** runtime lookup table (AFacet,DRepr) -> impl **/
std::unordered_map<key_type, const void *, KeyHash> registry_; std::unordered_map<key_type, const void *, KeyHash> registry_;
}; };

View file

@ -78,6 +78,22 @@ namespace xo {
memcpy(&(iface_[0]), (void*)&tmp, sizeof(ISpecific)); memcpy(&(iface_[0]), (void*)&tmp, sizeof(ISpecific));
} }
/**
* Runtime polymorphism:
* Create variant for specific interface @p impl
* with type-erased data @p data.
*
* Implements
* obj<AFacet>::variant(iface, data)
**/
OObject(const AFacet * impl, void * data)
requires std::is_same_v<DRepr, DVariantPlaceholder>
: data_{reinterpret_cast<DVariantPlaceholder*>(data)}
{
static_assert(sizeof(ISpecific) == sizeof(impl));
memcpy(&(iface_[0]), (void*)impl, sizeof(ISpecific));
}
OObject(const OObject & oother) { OObject(const OObject & oother) {
_launder_from(oother); _launder_from(oother);
} }

View file

@ -54,6 +54,18 @@ namespace xo {
obj(const obj & rhs) = default; obj(const obj & rhs) = default;
/** Runtime polymorphism:
* assemble variant from specific interface @p iface
* and type-erased representation @p data.
*
* Implements
* obj<AFacet>::variant(iface, data)
**/
obj(const AFacet * iface, void * data)
requires std::is_same_v<DRepr, DVariantPlaceholder>
: Super(iface, data)
{}
/** pseudo copy constructor /** pseudo copy constructor
* *
* Intended for use cases: * Intended for use cases:
@ -96,6 +108,20 @@ namespace xo {
return obj(other.template downcast<DRepr>()); return obj(other.template downcast<DRepr>());
} }
/** Runtime polymorphism.
* Create variant given interface @p iface,
* type-erased represention @p data
*
* Use:
* AFoo * impl = ....;
* auto x = obj<AFoo>::variant(impl, data)
**/
static obj variant(const AFacet * iface, void * data)
requires std::is_same_v<DRepr, DVariantPlaceholder>
{
return obj(iface, data);
}
/** enabled when RRouter<AFacet> provides _preincrement. /** enabled when RRouter<AFacet> provides _preincrement.
* Note we don't need this trick for comparison operators, * Note we don't need this trick for comparison operators,
* since return type is fixed. * since return type is fixed.