xo-facet: runtime facet lookup [PROTO]

This commit is contained in:
Roland Conybeare 2026-01-09 10:07:20 -05:00
commit 9ce465e84f
7 changed files with 111 additions and 10 deletions

View file

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

View file

@ -9,6 +9,7 @@
#include "facet_implementation.hpp"
#include "typeseq.hpp"
#include "obj.hpp"
#include <unordered_map>
#include <utility>
@ -88,7 +89,8 @@ namespace xo {
bool contains(typeseq facet_id,
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
@ -99,7 +101,37 @@ namespace xo {
**/
template <typename AFacet>
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:
@ -134,6 +166,7 @@ namespace xo {
private:
FacetRegistry() = default;
/** runtime lookup table (AFacet,DRepr) -> impl **/
std::unordered_map<key_type, const void *, KeyHash> registry_;
};

View file

@ -78,6 +78,22 @@ namespace xo {
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) {
_launder_from(oother);
}

View file

@ -54,6 +54,18 @@ namespace xo {
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
*
* Intended for use cases:
@ -96,6 +108,20 @@ namespace xo {
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.
* Note we don't need this trick for comparison operators,
* since return type is fixed.

View file

@ -4,11 +4,15 @@
**/
#include "DList.hpp"
#include <xo/printable2/Printable.hpp>
#include <xo/facet/FacetRegistry.hpp>
#include <xo/indentlog/print/pretty.hpp>
#include <xo/indentlog/print/tag.hpp>
namespace xo {
using xo::print::APrintable;
using xo::mm::AGCObject;
using xo::facet::FacetRegistry;
using xo::facet::typeseq;
namespace scm {
@ -96,17 +100,28 @@ namespace xo {
if (ppii.upto()) {
/* perhaps print on one line */
if (!pps->print_upto("(...)"))
return false;
pps->write("(");
#ifdef NOT_YET
/* TODO: probably use iterators here, when available */
const DList * l = this;
while (!l->is_empty()) {
obj<APrintable>(l->head_.data());
size_t i = 0;
while (!l->is_empty()) {
if (i > 0)
pps->write(" ");
obj<APrintable> elt
= FacetRegistry::instance().variant<APrintable, AGCObject>(l->head_);
// what if no converter registered ?
if (!pps->print_upto(elt))
return false;
l = l->rest_;
++i;
}
#endif
pps->write(")");
return true;
} else {
pps->write("(...)");

View file

@ -37,6 +37,9 @@ public:
///@{
RPrintable() {}
RPrintable(Object::DataPtr data) : Object{std::move(data)} {}
RPrintable(const APrintable * iface, void * data)
requires std::is_same_v<typename Object::DataType, xo::facet::DVariantPlaceholder>
: Object(iface, data) {}
///@}
/** @defgroup print-printable-router-methods **/
@ -44,7 +47,7 @@ public:
// const methods
int32_t _typeseq() const noexcept { return O::iface()->_typeseq(); }
bool pretty(const ppindentinfo & ppii) override {
bool pretty(const ppindentinfo & ppii) {
return O::iface()->pretty(O::data(), ppii);
}
@ -75,4 +78,4 @@ namespace xo { namespace facet {
};
} }
/* end RPrintable.hpp */
/* end RPrintable.hpp */