xo-facet/include/xo/facet/obj.hpp

152 lines
5.5 KiB
C++

/** @file obj.hpp
*
* @author Roland Conybeare, Dec 2025
**/
#pragma once
#include "RRouter.hpp"
#include <utility>
#include <cassert>
namespace xo {
namespace facet {
/** object with borrowed state pointer
* - With default Data argument:
* type-erased polymorphic container
* - with specific Data argument:
* typed container. Trivially de-virtualizable
*
* Example:
* std::unique_ptr<DRectCoords> z1_in
* = std::make_unique<DRectCoords>(1.0, 0.0):
* ubox<AComplex> z1{z1_in.release()};
* z1.xcoord();
*
*
* +-----+ +-----------------+
* Interface | x-------------->| vtable for |
* +-----+ | some descendant |
* Data | x--------\ | of AInterface |
* +-----+ | | |
* | +-----------------+
* |
* | +--------------+
* \----->| data :: Repr |
* +--------------+
*
* Binary representation of unay<AInterface, Data>
* is compatible for different values of @tparam Data
* as long as vtable pointer moves along with data pointer.
*
* In particular binary representation for
* ubox<AInterface,D> is as if it inherited ubox<AInterface>
* (even though it does not as far as compiler is concerned)
*
* This is load-bearing for @ref move2any see below
**/
template <typename AFacet, typename DRepr = DVariantPlaceholder>
struct obj : public RoutingType<AFacet, OObject<AFacet, DRepr>> {
using Super = RoutingType<AFacet, OObject<AFacet, DRepr>>;
obj() : Super() {}
explicit obj(Super::DataPtr d) : Super(d) {}
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:
* obj<AFoo, DRepr> lhs = obj<AFoo, DRepr> // same type on rhs
* obj<AFoo> lhs = obj<AFoo, DRepr> // variant lhs, typed rhs
* obj<Afoo> lhs = obj<AFoo> // variant on both sides
**/
template <typename DOther>
obj(const obj<AFacet, DOther> & other)
requires (std::is_same_v<DRepr, DVariantPlaceholder>
|| std::is_convertible_v<DRepr, DOther>)
: Super()
{
this->from_obj(other);
}
/** move constructor from a different representation.
* allowed given:
* - same abstract interface
* - same strategy for holding state (naked / unique / refcounted ...)
**/
template <typename DOther>
obj(const obj<AFacet, DOther> && other)
requires (std::is_same_v<DRepr, DVariantPlaceholder>
|| std::is_convertible_v<DRepr, DOther>)
: Super()
{
/* replacing .iface_ along w/ .data_ */
this->from_obj(other);
}
obj & operator=(const obj & rhs) {
/* ensure we replace .iface_ along w/ .ata_ */
this->from_obj(rhs);
return *this;
}
/** safe downcast from variant. null if downcast fails **/
static obj from(const OObject<AFacet> & other) {
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.
*
* For example see comparison overloads in RAllocIterator.hpp
**/
obj & operator++() noexcept { this->_preincrement(); return *this; }
};
template <typename AFacet>
using vt = obj<AFacet, DVariantPlaceholder>;
/** Use:
* auto o = with_facet<AAllocator>::mkobj(&data);
**/
template <typename AFacet>
struct with_facet {
template <typename DRepr>
static obj<AFacet, DRepr> mkobj(DRepr * data) { obj<AFacet, DRepr> x(data); return x; }
};
} /*namespace facet*/
using facet::obj;
using facet::vt;
} /*namespace xo*/
/* end obj.hpp */