xo-objectd2 xo-printable xo-facet: pp working for List(Integer)

Also streamline facet switching
This commit is contained in:
Roland Conybeare 2026-01-09 17:48:54 -05:00
commit 09ee3a20ac
23 changed files with 508 additions and 27 deletions

24
FAQ-DEV.md Normal file
View file

@ -0,0 +1,24 @@
Error like this:
```
/home/roland/proj/xo-umbrella2-claude1/./xo-facet/include/xo/facet/OObject.hpp:63:19: required from struct xo::facet::OObject<xo::mm::AGCObject, xo::scm::DList>
63 | using ISpecific = FacetImplType<AFacet, DRepr>;
| ^~~~~~~~~
/home/roland/proj/xo-umbrella2-claude1/./xo-gc/include/xo/gc/detail/RGCObject.hpp:15:16: required from struct xo::mm::RGCObject<xo::facet::OObject<xo::mm::AGCObject, xo::scm::DList> >
15 | struct RGCObject : public Object {
| ^~~~~~~~~
/home/roland/proj/xo-umbrella2-claude1/./xo-facet/include/xo/facet/obj.hpp:49:16: required from struct xo::facet::obj<xo::mm::AGCObject, xo::scm::DList>
49 | struct obj : public RoutingType<AFacet, OObject<AFacet, DRepr>> {
| ^~~
/home/roland/proj/xo-umbrella2-claude1/xo-object2/utest/Printable.test.cpp:81:57: required from here
81 | auto l0_o = with_facet<AGCObject>::mkobj(l0);
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~
/home/roland/proj/xo-umbrella2-claude1/./xo-facet/include/xo/facet/facet_implementation.hpp:97:15: error: no type named ImplType in struct xo::facet::FacetImplementation<xo::mm::AGCObject, xo::scm::DList>
97 | using FacetImplType = FacetImplementation<AFacet, DRepr>::ImplType;
| ^~~~~~~~~~~~~
```
means implementation `xo::mm::AGCObject` for `xo::scm::DList` is not availabel to compiler.
Either missing implementation or missing header.
In example implementation would be in a file `IGCObject_DList.hpp`

View file

@ -76,6 +76,10 @@ namespace utest {
*
* test function should use REQUIRE_ORCAPTURE() / REQUIRE_ORFAIL().
* It should *not* use REQUIRE() or CHECK().
*
* @p test_name banner for initial log message (only printed on 2nd pass)
* @p test_fn function to invoke test pass.
* @p n test size/id (cosmetic - printed in log messages)
**/
static inline bool bimodal_test(std::string test_name,
std::function<bool (bool dbg_flag, std::uint32_t n)> test_fn,

View file

@ -32,6 +32,7 @@ public:
{% endif %}
using ObjectType = Object;
using DataPtr = Object::DataPtr;
using typeseq = xo::reflect::typeseq;
{% for ty in types %}
using {{ty.name}} = {{abstract_facet}}::{{ty.name}};
{% endfor %}
@ -53,7 +54,7 @@ public:
{% endif %}
// const methods
int32_t _typeseq() const noexcept { return O::iface()->_typeseq(); }
typeseq _typeseq() const noexcept { return O::iface()->_typeseq(); }
{% for md in const_methods %}
{{md.return_type}} {{md.name}}({{md.args | argsnodata}}) {{md | qualifiers}} override {
return O::iface()->{{md.name}}({{md.args | argrouting}});

View file

@ -10,6 +10,8 @@
#include "facet_implementation.hpp"
#include "typeseq.hpp"
#include "obj.hpp"
#include <xo/indentlog/scope.hpp>
#include <xo/indentlog/print/tostr.hpp>
#include <unordered_map>
#include <utility>
@ -36,6 +38,7 @@ namespace xo {
**/
class FacetRegistry {
public:
using typeseq = xo::reflect::typeseq;
using key_type = std::pair<typeseq, typeseq>;
/** hash function for key_type **/
@ -113,10 +116,40 @@ namespace xo {
* = ...; // Foo instance with variant impl
* obj<ABar> bar
* = FacetRegistry::variant<ABar,AFoo>(foo);
*
* // exception thrown if bar has null data
*
* assert(bar);
**/
template <typename ATo, typename AFrom>
obj<ATo> variant(obj<AFrom> from) {
return variant<ATo>(from._typeseq(), from.data());
auto retval = try_variant<ATo>(from);
if (!retval)
throw std::runtime_error(tostr("FacetRegistry::try_variant failed",
xtag("AFrom.tseq", typeseq::id<AFrom>()),
xtag("ATo.tseq", typeseq::id<ATo>())));
return retval;
}
/** 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::try_variant<ABar,AFoo>(foo);
* if (bar) {
* // success
* } else {
* // foo::DataType doesn't appear to support ABar
* }
**/
template <typename ATo, typename AFrom>
obj<ATo> try_variant(obj<AFrom> from) {
return try_variant<ATo>(from._typeseq(), from.data());
}
/** Runtime polymorphism:
@ -129,9 +162,24 @@ namespace xo {
* = FacetRegistry::variant<ABar>(foo._typeseq(), foo.opaque_data());
**/
template <typename AFacet>
obj<AFacet> variant(typeseq repr_id, void * data) {
obj<AFacet> try_variant(typeseq repr_id, void * data) {
const AFacet * iface = this->lookup<AFacet>(repr_id);
return obj<AFacet>::variant(iface, data);;
if (iface)
return obj<AFacet>(iface, data);
else
return obj<AFacet>();
}
void dump(std::ostream * p_out) {
(*p_out) << std::endl;
(*p_out) << "<FacetRegistry" << std::endl;
for (auto & kv : registry_) {
(*p_out)
<< " [" << kv.first.first << "," << kv.first.second << "]"
<< " -> " << kv.second << std::endl;
}
(*p_out) << ">" << std::endl;
}
private:
@ -155,12 +203,20 @@ namespace xo {
const void * _lookup(typeseq facet_id,
typeseq repr_id) const
{
scope log(XO_DEBUG(false));
log && log(xtag("facet_id", facet_id),
xtag("repr_id", repr_id));
auto ix = registry_.find(key_type(facet_id, repr_id));
if (ix == registry_.end())
return nullptr;
else
return ix->second;
const void *retval = nullptr;
if (ix != registry_.end())
retval = ix->second;
log && log(xtag("retval", retval));
return retval;
}
private:

View file

@ -5,11 +5,32 @@
#pragma once
#include <xo/alloc2/Allocator.hpp>
#include <xo/indentlog/print/ppindentinfo.hpp>
#include <cstdint>
namespace xo {
namespace scm {
using DInteger = std::int64_t;
struct DInteger {
using AAllocator = xo::mm::AAllocator;
using ppindentinfo = xo::print::ppindentinfo;
explicit DInteger(long x) : value_{x} {}
/** allocate boxed value @p x using memory from @p mm **/
static DInteger * make(obj<AAllocator> mm,
long x);
double value() const noexcept { return value_; }
bool pretty(const ppindentinfo & ppii) const;
operator long() const noexcept { return value_; }
private:
/** boxed integer value **/
long value_;
};
} /*nmaespace obj*/
} /*namespace xo*/

View file

@ -13,6 +13,7 @@
namespace xo {
namespace scm {
// TODO: consider renaming to DCons
struct DList {
using size_type = std::size_t;
using AGCObject = xo::mm::AGCObject;
@ -22,8 +23,27 @@ namespace xo {
DList(xo::obj<AGCObject> h,
DList * r) : head_{h}, rest_{r} {}
template <typename AConsFacet = AGCObject>
static obj<AConsFacet,DList> nil();
/** shortcut for
* cons(mm, cdr, cdr.data())
**/
template <typename AConsFacet = AGCObject, typename ACdrFacet = AGCObject>
static obj<AConsFacet,DList> cons(obj<AAllocator> mm,
obj<AGCObject> car,
obj<ACdrFacet,DList> cdr);
/** sentinel for null list **/
static DList * null();
static DList * _nil();
/** list with first element @p car,
* followed by contents of list @p cdr.
* Shares structure with @p cdr
**/
static DList * _cons(obj<AAllocator> mm,
obj<AGCObject> car,
DList * cdr);
/** list with one element @p h1, allocated from @p mm **/
static DList * list(obj<AAllocator> mm,
@ -51,6 +71,22 @@ namespace xo {
DList * rest_ = nullptr;
};
template <typename AConsFacet>
obj<AConsFacet,DList>
DList::nil()
{
return obj<AConsFacet,DList>(DList::_nil());
}
template <typename AConsFacet, typename ACdrFacet>
obj<AConsFacet,DList>
DList::cons(obj<AAllocator> mm,
obj<AGCObject> car,
obj<ACdrFacet,DList> cdr)
{
return obj<AConsFacet,DList>(DList::_cons(mm, car, cdr.data()));
}
} /*namespace scm*/
} /*namespace xo*/

View file

@ -0,0 +1,57 @@
/** @file IPrintable_DInteger.hpp
*
* Generated automagically from ingredients:
* 1. code generator:
* [/home/roland/proj/xo-umbrella2/xo-facet/codegen/genfacet]
* arguments:
* --input [idl/IPrintable_DInteger.json5]
* 2. jinja2 template for abstract facet .hpp file:
* [iface_facet_any.hpp.j2]
* 3. idl for facet methods
* [idl/IPrintable_DInteger.json5]
**/
#pragma once
#include <xo/printable2/Printable.hpp>
#include <xo/printable2/detail/IPrintable_Xfer.hpp>
#include "DInteger.hpp"
namespace xo { namespace scm { class IPrintable_DInteger; } }
namespace xo {
namespace facet {
template <>
struct FacetImplementation<xo::print::APrintable,
xo::scm::DInteger>
{
using ImplType = xo::print::IPrintable_Xfer
<xo::scm::DInteger,
xo::scm::IPrintable_DInteger>;
};
}
}
namespace xo {
namespace scm {
/** @class IPrintable_DInteger
**/
class IPrintable_DInteger {
public:
/** @defgroup scm-printable-dinteger-type-traits **/
///@{
using ppindentinfo = xo::print::APrintable::ppindentinfo;
///@}
/** @defgroup scm-printable-dinteger-methods **/
///@{
/** Pretty-printing support for this object.
See [xo-indentlog/xo/indentlog/pretty.hpp] **/
static bool pretty(const DInteger & self, const ppindentinfo & ppii);
///@}
};
} /*namespace scm*/
} /*namespace xo*/
/* end */

View file

@ -13,6 +13,9 @@ namespace xo {
* Return true iff all types register successfully.
**/
bool object2_register_types(obj<xo::mm::ACollector> gc);
/** Register object2 (facet,impl) combinations with FacetRegistry **/
bool object2_register_facets();
}
}

View file

@ -7,10 +7,12 @@ set(SELF_SRCS
IGCObject_DList.cpp
ISequence_Any.cpp
ISequence_DList.cpp
IPrintable_DFloat.cpp
IPrintable_DList.cpp
IPrintable_DFloat.cpp
IPrintable_DInteger.cpp
DList.cpp
DFloat.cpp
DInteger.cpp
object2_register_types.cpp
)

View file

@ -0,0 +1,32 @@
/** @file DInteger.cpp
*
* @author Roland Conybeare, Jan 2026
**/
#include "DInteger.hpp"
#include <xo/indentlog/print/pretty.hpp>
namespace xo {
using xo::facet::typeseq;
using xo::print::ppdetail_atomic;
namespace scm {
DInteger *
DInteger::make(obj<AAllocator> mm,
long x)
{
void * mem = mm.alloc(typeseq::id<DInteger>(),
sizeof(DInteger));
return new (mem) DInteger(x);
}
bool
DInteger::pretty(const ppindentinfo & ppii) const
{
return ppdetail_atomic<long>::print_pretty(ppii, value_);
}
} /*namespace scm*/
} /*namespace xo*/
/* end DInteger.cpp */

View file

@ -19,18 +19,28 @@ namespace xo {
static DList s_null(obj<AGCObject>(), nullptr);
DList *
DList::null()
DList::_nil()
{
return &s_null;
}
DList *
DList::_cons(obj<AAllocator> mm,
obj<AGCObject> car,
DList * cdr)
{
void * mem = mm.alloc(typeseq::id<DList>(), sizeof(DList));
return new (mem) DList(car, cdr);
}
DList *
DList::list(obj<AAllocator> mm,
obj<AGCObject> h1)
{
void * mem = mm.alloc(typeseq::id<DList>(), sizeof(DList));
return new (mem) DList(h1, DList::null());
return new (mem) DList(h1, DList::_nil());
}
DList *
@ -112,7 +122,8 @@ namespace xo {
obj<APrintable> elt
= FacetRegistry::instance().variant<APrintable, AGCObject>(l->head_);
// what if no converter registered ?
assert(elt);
if (!pps->print_upto(elt))
return false;

View file

@ -0,0 +1,28 @@
/** @file IPrintable_DInteger.cpp
*
* Generated automagically from ingredients:
* 1. code generator:
* [/home/roland/proj/xo-umbrella2/xo-facet/codegen/genfacet]
* arguments:
* --input [idl/IPrintable_DInteger.json5]
* 2. jinja2 template for abstract facet .hpp file:
* [iface_facet_any.hpp.j2]
* 3. idl for facet methods
* [idl/IPrintable_DInteger.json5]
**/
#include "IPrintable_DInteger.hpp"
namespace xo {
namespace scm {
auto
IPrintable_DInteger::pretty(const DInteger & self, const ppindentinfo & ppii) -> bool
{
return self.pretty(ppii);
}
} /*namespace scm*/
} /*namespace xo*/
/* end IPrintable_DInteger.cpp */

View file

@ -4,22 +4,36 @@
**/
#include "object2_register_types.hpp"
#include "IGCObject_DList.hpp"
#include "IGCObject_DFloat.hpp"
#include "IGCObject_DInteger.hpp"
#include "IPrintable_DList.hpp"
//#include "IPrintable_DFloat.hpp"
#include "IPrintable_DInteger.hpp"
#include <xo/facet/FacetRegistry.hpp>
#include <xo/indentlog/scope.hpp>
namespace xo {
using xo::print::APrintable;
using xo::mm::AAllocator;
using xo::mm::ACollector;
using xo::mm::AGCObject;
using xo::mm::IGCObject_Any;
using xo::facet::FacetRegistry;
using xo::facet::impl_for;
using xo::facet::typeseq;
using xo::scope;
namespace scm {
bool
object2_register_types(obj<ACollector> gc)
{
scope log(XO_DEBUG(true));
bool ok = true;
ok &= gc.install_type(impl_for<AGCObject, DList>());
@ -28,6 +42,31 @@ namespace xo {
return ok;
}
bool
object2_register_facets()
{
scope log(XO_DEBUG(true));
FacetRegistry::register_impl<AGCObject, DList>();
FacetRegistry::register_impl<APrintable, DList>();
FacetRegistry::register_impl<AGCObject, DFloat>();
// FacetRegistry::register_impl<APrintable, DFloat>();
FacetRegistry::register_impl<AGCObject, DInteger>();
FacetRegistry::register_impl<APrintable, DInteger>();
log && log(xtag("DList.tseq", typeseq::id<DList>()));
log && log(xtag("DFloat.tseq", typeseq::id<DFloat>()));
log && log(xtag("DInteger.tseq", typeseq::id<DInteger>()));
log && log(xtag("AAllocator.tseq", typeseq::id<AAllocator>()));
log && log(xtag("APrintable.tseq", typeseq::id<APrintable>()));
log && log(xtag("AGCObject.tseq", typeseq::id<AGCObject>()));
return true;
}
}
} /*namespace xo*/

View file

@ -4,6 +4,7 @@ set(UTEST_EXE utest.object2)
set(UTEST_SRCS
object2_utest_main.cpp
X1Collector.test.cpp
Printable.test.cpp
)
xo_add_utest_executable(${UTEST_EXE} ${UTEST_SRCS})

View file

@ -0,0 +1,141 @@
/** @file Printable.test.cpp
*
* @author Roland Conybeare, Jan 2026
**/
#include "DList.hpp"
#include "object2_register_types.hpp"
#include <xo/object2/DList.hpp>
#include <xo/object2/IGCObject_DList.hpp>
#include <xo/object2/IPrintable_DList.hpp>
#include <xo/object2/DInteger.hpp>
#include <xo/object2/IGCObject_DInteger.hpp>
#include <xo/gc/Collector.hpp>
#include <xo/gc/DX1Collector.hpp>
#include <xo/gc/detail/IAllocator_DX1Collector.hpp>
#include <xo/gc/detail/ICollector_DX1Collector.hpp>
#include <xo/printable2/Printable.hpp>
#include <xo/facet/FacetRegistry.hpp>
#include <xo/indentlog/scope.hpp>
#include <xo/indentlog/print/tag.hpp>
#include <catch2/catch.hpp>
namespace ut {
using xo::scm::object2_register_types;
using xo::scm::object2_register_facets;
using xo::scm::DList;
using xo::scm::DInteger;
using xo::mm::AAllocator;
using xo::mm::ACollector;
using xo::mm::AGCObject;
using xo::mm::DX1Collector;
using xo::mm::CollectorConfig;
using xo::mm::ArenaConfig;
using xo::print::APrintable;
using xo::facet::FacetRegistry;
using xo::facet::with_facet;
using xo::facet::obj;
using xo::facet::typeseq;
using xo::print::ppstate_standalone;
using xo::print::ppconfig;
using xo::scope;
using xo::xtag;
using std::string;
namespace {
struct testcase_pp {
explicit testcase_pp(size_t gc_z, size_t gc_threshold, int z, const std::string & expected)
: gc_gen_size_{gc_z}, gc_trigger_threshold_{gc_threshold}, expected_{expected} {
for (int i = 0; i < z; ++i) {
list_.push_back(1000 + 197 * i);
}
}
size_t gc_gen_size_ = 0;
size_t gc_trigger_threshold_ = 0;
std::vector<int> list_;
std::string expected_;
};
std::vector<testcase_pp>
s_testcase_v = {
testcase_pp(16384, 8192, 0, "()"),
testcase_pp(16384, 8192, 1, "(1000)"),
testcase_pp(16384, 8192, 2, "(1000 1197)"),
testcase_pp(16384, 8192, 5, "(1000 1197 1394 1591 1788)"),
testcase_pp(16384, 8192, 10, "(1000 1197 1394 1591 1788 1985 2182 2379 2576 2773)"),
testcase_pp(16384, 8192, 20, "(...)"),
};
}
TEST_CASE("printable1", "[pp][x1][list]")
{
constexpr bool c_debug_flag = true;
scope log(XO_DEBUG(c_debug_flag));
bool ok = object2_register_facets();
REQUIRE(ok);
FacetRegistry::instance().dump(&std::cerr);
for (std::size_t i_tc = 0, n_tc = s_testcase_v.size(); i_tc < n_tc; ++i_tc) {
try {
const testcase_pp & tc = s_testcase_v[i_tc];
CollectorConfig cfg{
.name_ = "pp_test",
.arena_config_ = ArenaConfig{
.size_ = tc.gc_gen_size_,
.store_header_flag_ = true},
.object_types_z_ = 16384,
.gc_trigger_v_{{tc.gc_trigger_threshold_,
tc.gc_trigger_threshold_}},
.debug_flag_ = c_debug_flag
};
DX1Collector gc(cfg);
auto gc_o = with_facet<AAllocator>::mkobj(&gc);
auto c_o = with_facet<ACollector>::mkobj(&gc);
bool ok = object2_register_types(c_o);
REQUIRE(ok);
auto l0_o = DList::nil();
c_o.add_gc_root(&l0_o);
for(int ip1 = tc.list_.size(); ip1 > 0; --ip1) {
// auto xi_o = Integer::make(g_o, ...);
DInteger * xi = DInteger::make(gc_o, tc.list_[ip1 - 1]);
auto xi_o = with_facet<AGCObject>::mkobj(xi);
l0_o = DList::cons(gc_o, xi_o, l0_o);
}
// TODO: log_streambuf using DArena
std::stringstream ss;
ppconfig ppc;
ppstate_standalone pps(&ss, 0, &ppc);
obj<APrintable,DList> l0_po(static_cast<DList*>(l0_o.data()));
REQUIRE(l0_po._typeseq() == typeseq::id<DList>());
pps.pretty(l0_po);
REQUIRE(ss.str() == string(tc.expected_));
} catch (std::exception & ex) {
std::cerr << "caught exception: " << ex.what() << std::endl;
REQUIRE(false);
}
}
} /* TEST_CASE(printable1) */
}
/* end Printable.test.cpp */

View file

@ -74,7 +74,7 @@ namespace ut {
TEST_CASE("x1", "[gc][x1]")
{
constexpr bool c_debug_flag = true;
constexpr bool c_debug_flag = false;
scope log(XO_DEBUG(c_debug_flag));
for (std::size_t i_tc = 0, n_tc = s_testcase_v.size(); i_tc < n_tc; ++i_tc) {

View file

@ -18,4 +18,7 @@
#include "detail/IPrintable_Xfer.hpp"
#include "detail/RPrintable.hpp"
// todo: additional includes in idl above
#include "detail/ppdetail_Printable.hpp"
/* end Printable.hpp */

View file

@ -47,7 +47,7 @@ public:
/** Pretty-printing support for this object.
See [xo-indentlog/xo/indentlog/pretty.hpp]
**/
virtual bool pretty(Copaque data, const ppindentinfo & ppii) = 0;
virtual bool pretty(Copaque data, const ppindentinfo & ppii) const = 0;
// nonconst methods
///@}

View file

@ -56,7 +56,7 @@ namespace print {
// const methods
typeseq _typeseq() const noexcept override { return s_typeseq; }
[[noreturn]] bool pretty(Copaque, const ppindentinfo &) override { _fatal(); }
[[noreturn]] bool pretty(Copaque, const ppindentinfo &) const override { _fatal(); }
// nonconst methods

View file

@ -41,7 +41,7 @@ namespace print {
// const methods
typeseq _typeseq() const noexcept override { return s_typeseq; }
bool pretty(Copaque data, const ppindentinfo & ppii) override {
bool pretty(Copaque data, const ppindentinfo & ppii) const override {
return I::pretty(_dcast(data), ppii);
}

View file

@ -31,6 +31,7 @@ public:
using ObjectType = Object;
using DataPtr = Object::DataPtr;
using ppindentinfo = APrintable::ppindentinfo;
using typeseq = xo::facet::typeseq;
///@}
/** @defgroup print-printable-router-ctors **/
@ -46,8 +47,8 @@ public:
///@{
// const methods
int32_t _typeseq() const noexcept { return O::iface()->_typeseq(); }
bool pretty(const ppindentinfo & ppii) {
typeseq _typeseq() const noexcept { return O::iface()->_typeseq(); }
bool pretty(const ppindentinfo & ppii) const {
return O::iface()->pretty(O::data(), ppii);
}

View file

@ -0,0 +1,21 @@
/** @file ppdetail_Printable.hpp
*
* @author Roland Conybeare, Jan 2026
**/
#include <xo/indentlog/print/pretty.hpp>
#include "Printable.hpp"
namespace xo {
namespace print {
template <typename DRepr>
struct ppdetail<xo::facet::obj<APrintable, DRepr>> {
static bool print_pretty(const ppindentinfo & ppii,
const xo::facet::obj<APrintable, DRepr> & x) {
return x.pretty(ppii);
}
};
}
} /*namespace xo*/
/* end ppdetail_Printable.hpp */