xo-gc: + CollectorTypeRegistry for streamlined init

This commit is contained in:
Roland Conybeare 2026-01-16 16:10:00 -05:00
commit 93429becad
16 changed files with 319 additions and 6 deletions

View file

@ -11,6 +11,7 @@ find_dependency(reflect)
find_dependency(xo_object2) find_dependency(xo_object2)
find_dependency(xo_printable2) find_dependency(xo_printable2)
find_dependency(xo_flatstring) find_dependency(xo_flatstring)
find_dependency(cmake)
find_dependency(indentlog) find_dependency(indentlog)
include("${CMAKE_CURRENT_LIST_DIR}/@PROJECT_NAME@Targets.cmake") include("${CMAKE_CURRENT_LIST_DIR}/@PROJECT_NAME@Targets.cmake")

View file

@ -0,0 +1,21 @@
/** @file init_expression2.hpp
*
* @author Roland Conybeare, Jan 2026
**/
#pragma once
#include <xo/subsys/Subsystem.hpp>
namespace xo {
/* tag to represent the xo-expression2/ subsystem within ordered initialization */
enum S_expression2_tag {};
template <>
struct InitSubsys<S_expression2_tag> {
static void init();
static InitEvidence require();
};
} /*namespace xo*/
/* end init_expression2.hpp */

View file

@ -2,13 +2,16 @@
set(SELF_LIB xo_expression2) set(SELF_LIB xo_expression2)
set(SELF_SRCS set(SELF_SRCS
init_expression2.cpp
DConstant.cpp DConstant.cpp
TypeRef.cpp TypeRef.cpp
IExpression_Any.cpp IExpression_Any.cpp
IExpression_DConstant.cpp IExpression_DConstant.cpp
StringTable.cpp StringTable.cpp
DUniqueString.cpp DUniqueString.cpp
IGCObject_DUniqueString.cpp
expression2_register_facets.cpp expression2_register_facets.cpp
expression2_register_types.cpp
) )
xo_add_shared_library4(${SELF_LIB} ${PROJECT_NAME}Targets ${PROJECT_VERSION} 1 ${SELF_SRCS}) xo_add_shared_library4(${SELF_LIB} ${PROJECT_NAME}Targets ${PROJECT_VERSION} 1 ${SELF_SRCS})
@ -18,4 +21,5 @@ xo_dependency(${SELF_LIB} reflect)
xo_dependency(${SELF_LIB} xo_object2) xo_dependency(${SELF_LIB} xo_object2)
xo_dependency(${SELF_LIB} xo_printable2) xo_dependency(${SELF_LIB} xo_printable2)
xo_dependency(${SELF_LIB} xo_flatstring) xo_dependency(${SELF_LIB} xo_flatstring)
xo_dependency(${SELF_LIB} subsys)
xo_dependency(${SELF_LIB} indentlog) xo_dependency(${SELF_LIB} indentlog)

View file

@ -0,0 +1,41 @@
/** @file init_expression2.cpp
*
* @author Roland Conybeare, Jan 2026
**/
#include "init_expression2.hpp"
#include "expression2_register_facets.hpp"
#include "expression2_register_types.hpp"
#include <xo/object2/init_object2.hpp>
#include <xo/gc/CollectorTypeRegistry.hpp>
namespace xo {
using xo::scm::expression2_register_facets;
using xo::scm::expression2_register_types;
using xo::mm::CollectorTypeRegistry;
void
InitSubsys<S_expression2_tag>::init()
{
expression2_register_facets();
CollectorTypeRegistry::instance().register_types(&expression2_register_types);
}
InitEvidence
InitSubsys<S_expression2_tag>::require()
{
InitEvidence retval;
/* direct subsystem deps for xo-object2/ */
retval ^= InitSubsys<S_object2_tag>::require();
/* xo-expression2/'s own initialization code */
retval ^= Subsystem::provide<S_expression2_tag>("expression2", &init);
return retval;
}
} /*namespace xo*/
/* end init_expression2.cpp */

View file

@ -1,6 +1,24 @@
/* file expression2_utest_main.cpp */ /* file expression2_utest_main.cpp */
#define CATCH_CONFIG_MAIN #include <xo/subsys/Subsystem.hpp>
#define CATCH_CONFIG_RUNNER
#include "catch2/catch.hpp" #include "catch2/catch.hpp"
int
main(int argc, char* argv[])
{
using xo::Subsystem;
// Your custom initialization code here
Subsystem::initialize_all();
// Run Catch2's test session
int result = Catch::Session().run(argc, argv);
// cleanup here, if any
return result;
}
/* end expression2_utest_main.cpp */ /* end expression2_utest_main.cpp */

View file

@ -0,0 +1,74 @@
/** @file CollectorTypeRegistry.hpp
*
* @brief Runtime type registration for gc-aware types
*
* @author Roland Conybeare, Jan 2026
**/
#pragma once
#include "Collector.hpp"
#include <functional>
namespace xo {
namespace mm {
/** @class CollectorTypeRegistry
*
* @brief Runtime registry for gc-aware types
*
* Singleton to remember known gc-aware types;
* use to simplify registering such types
* with a collector instance.
*
* Remark: splitting work here between
* 1. static initializer work: tracking gc-aware types,
* 2. runtime post-configuration work: report
* gc-aware types to GC instances
*
* Use:
* 1. subsystem foo provides function foo_register_types(obj<ACollector> gc)
* Function calls
* gc.install_type(impl_for<AGCObject, DQuux>())
* for each gc-aware type provided by subsystem foo
*
* Example: in file xo-object2/src/object2/object2_register_types.cpp, see
* object2_register_types()
*
* 2. during subsystem init, call
* CollectorTypeRegistry::instance().register_types(&foo_register_types);
*
* Example: in file xo-object2/src/object2/init_object2.cpp, see
* InitSubsys<S_object2_tag>::init()
*
* 3. during Collector setup, call
* obj<ACollector> gc = ...;
* CollectorTypeRegistry::instance().install_types(gc);
*
* Example: in file xo-object2/utest/X1Collector.test.cpp
* TEST_CASE("x1")
**/
class CollectorTypeRegistry {
public:
using init_function_type = std::function<bool (obj<ACollector>)>;
public:
/** singleton instance **/
static CollectorTypeRegistry & instance();
/** remember a gc-aware type-registration function **/
void register_types(init_function_type init_fn);
/** register known GC-aware types with @p gc.
* Calls @c gc.isntall_type() for each
* such type.
**/
bool install_types(obj<ACollector> gc);
private:
/** initialization steps for a new Collector instance **/
std::vector<init_function_type> init_seq_v_;
};
}
}
/* end CollectorTypeRegistry.hpp */

View file

@ -3,8 +3,11 @@
set(SELF_LIB xo_gc) set(SELF_LIB xo_gc)
set(SELF_SRCS set(SELF_SRCS
CollectorTypeRegistry.cpp
ICollector_Any.cpp ICollector_Any.cpp
IGCObject_Any.cpp IGCObject_Any.cpp
IAllocator_DX1Collector.cpp IAllocator_DX1Collector.cpp
ICollector_DX1Collector.cpp ICollector_DX1Collector.cpp
IAllocIterator_DX1CollectorIterator.cpp IAllocIterator_DX1CollectorIterator.cpp

View file

@ -0,0 +1,45 @@
/** @file CollectorTypeRegistry.cpp
**/
#include "CollectorTypeRegistry.hpp"
#include <xo/indentlog/scope.hpp>
namespace xo {
namespace mm {
CollectorTypeRegistry &
CollectorTypeRegistry::instance() {
static CollectorTypeRegistry s_instance;
return s_instance;
}
void
CollectorTypeRegistry::register_types(init_function_type fn) {
scope log(XO_DEBUG(true));
init_seq_v_.push_back(fn);
}
bool
CollectorTypeRegistry::install_types(obj<ACollector> gc) {
scope log(XO_DEBUG(true));
bool ok = true;
size_t i = 0;
size_t n = init_seq_v_.size();
log && log("run n init steps", xtag("n", n));
for (const auto & fn : init_seq_v_) {
log && log("do install fn (", i+1, "/", n, ")");
ok = ok & fn(gc);
}
return ok;
}
} /*namespace mm*/
} /*namespace xo*/
/* end CollectorTypeRegistry.cpp */

View file

@ -3,6 +3,7 @@
include(CMakeFindDependencyMacro) include(CMakeFindDependencyMacro)
find_dependency(xo_gc) find_dependency(xo_gc)
find_dependency(xo_printable2) find_dependency(xo_printable2)
find_dependency(subsys)
find_dependency(indentlog) find_dependency(indentlog)
include("${CMAKE_CURRENT_LIST_DIR}/@PROJECT_NAME@Targets.cmake") include("${CMAKE_CURRENT_LIST_DIR}/@PROJECT_NAME@Targets.cmake")
check_required_components("@PROJECT_NAME@") check_required_components("@PROJECT_NAME@")

View file

@ -0,0 +1,21 @@
/** @file init_object2.hpp
*
* @author Roland Conybeare, Jan 2026
**/
#pragma once
#include <xo/subsys/Subsystem.hpp>
namespace xo {
/* tag to represent the xo-expression2/ subsystem within ordered initialization */
enum S_object2_tag {};
template <>
struct InitSubsys<S_object2_tag> {
static void init();
static InitEvidence require();
};
} /*namespace xo*/
/* end init_object2.hpp */

View file

@ -19,6 +19,7 @@ set(SELF_SRCS
DFloat.cpp DFloat.cpp
DInteger.cpp DInteger.cpp
DString.cpp DString.cpp
init_object2.cpp
object2_register_types.cpp object2_register_types.cpp
object2_register_facets.cpp object2_register_facets.cpp
) )
@ -27,4 +28,5 @@ xo_add_shared_library4(${SELF_LIB} ${PROJECT_NAME}Targets ${PROJECT_VERSION} 1 $
# note: deps here must also appear in cmake/xo_object2Config.cmake.in # note: deps here must also appear in cmake/xo_object2Config.cmake.in
xo_dependency(${SELF_LIB} xo_gc) xo_dependency(${SELF_LIB} xo_gc)
xo_dependency(${SELF_LIB} xo_printable2) xo_dependency(${SELF_LIB} xo_printable2)
xo_dependency(${SELF_LIB} subsys)
xo_dependency(${SELF_LIB} indentlog) xo_dependency(${SELF_LIB} indentlog)

View file

@ -0,0 +1,39 @@
/** @file init_object2.cpp
*
* @author Roland Conybeare, Jan 2026
**/
#include "init_object2.hpp"
#include "object2_register_facets.hpp"
#include "object2_register_types.hpp"
#include <xo/gc/CollectorTypeRegistry.hpp>
namespace xo {
using xo::scm::object2_register_facets;
using xo::scm::object2_register_types;
using xo::mm::CollectorTypeRegistry;
void
InitSubsys<S_object2_tag>::init()
{
object2_register_facets();
CollectorTypeRegistry::instance().register_types(&object2_register_types);
}
InitEvidence
InitSubsys<S_object2_tag>::require()
{
InitEvidence retval;
/* direct subsystem deps for xo-object2/ */
// retval ^= InitSubsys<S_somedep_tag>::require();
/* xo-expression2/'s own initialization code */
retval ^= Subsystem::provide<S_object2_tag>("object2", &init);
return retval;
}
} /*namespace xo*/
/* end init_object2.cpp */

View file

@ -3,7 +3,8 @@
* @author Roland Conybeare, Jan 2026 * @author Roland Conybeare, Jan 2026
**/ **/
#include <xo/object2/StringOps.hpp> #include "init_object2.hpp"
#include "StringOps.hpp"
#include <xo/alloc2/arena/IAllocator_DArena.hpp> #include <xo/alloc2/arena/IAllocator_DArena.hpp>
#include <catch2/catch.hpp> #include <catch2/catch.hpp>
#include <cctype> #include <cctype>
@ -19,6 +20,14 @@ namespace xo {
using xo::facet::obj; using xo::facet::obj;
namespace ut { namespace ut {
static InitEvidence s_init = (InitSubsys<S_object2_tag>::require());
TEST_CASE("DString-init", "[object2][DString]")
{
// real purpose: ensure s_init survives static linking
REQUIRE(s_init.evidence());
}
TEST_CASE("DString-empty", "[object2][DString]") TEST_CASE("DString-empty", "[object2][DString]")
{ {
ArenaConfig cfg { .name_ = "testarena", ArenaConfig cfg { .name_ = "testarena",

View file

@ -3,6 +3,7 @@
* @author Roland Conybeare, Dec 2025 * @author Roland Conybeare, Dec 2025
**/ **/
#include "init_object2.hpp"
#include "ListOps.hpp" #include "ListOps.hpp"
#include "DFloat.hpp" #include "DFloat.hpp"
#include "DInteger.hpp" #include "DInteger.hpp"
@ -14,6 +15,7 @@
#include "number/IGCObject_DInteger.hpp" #include "number/IGCObject_DInteger.hpp"
#include "list/IGCObject_DList.hpp" #include "list/IGCObject_DList.hpp"
#include <xo/gc/CollectorTypeRegistry.hpp>
#include <xo/gc/Collector.hpp> #include <xo/gc/Collector.hpp>
#include <xo/gc/DX1Collector.hpp> #include <xo/gc/DX1Collector.hpp>
@ -23,18 +25,22 @@
#include <xo/arena/AllocInfo.hpp> #include <xo/arena/AllocInfo.hpp>
#include <xo/arena/padding.hpp> #include <xo/arena/padding.hpp>
#include <xo/subsys/Subsystem.hpp>
#include <xo/indentlog/scope.hpp> #include <xo/indentlog/scope.hpp>
#include <xo/indentlog/print/tag.hpp> #include <xo/indentlog/print/tag.hpp>
#include <catch2/catch.hpp> #include <catch2/catch.hpp>
namespace ut { namespace ut {
using xo::S_object2_tag;
using xo::scm::object2_register_types; using xo::scm::object2_register_types;
using xo::scm::ListOps; using xo::scm::ListOps;
using xo::scm::DList; using xo::scm::DList;
using xo::scm::DArray; using xo::scm::DArray;
using xo::scm::DFloat; using xo::scm::DFloat;
using xo::scm::DInteger; using xo::scm::DInteger;
using xo::mm::CollectorTypeRegistry;
using xo::mm::AAllocator; using xo::mm::AAllocator;
using xo::mm::ACollector; using xo::mm::ACollector;
using xo::mm::AllocHeader; using xo::mm::AllocHeader;
@ -49,6 +55,9 @@ namespace ut {
using xo::mm::padding; using xo::mm::padding;
using xo::facet::with_facet; using xo::facet::with_facet;
using xo::facet::typeseq; using xo::facet::typeseq;
using xo::Subsystem;
using xo::InitEvidence;
using xo::InitSubsys;
using xo::scope; using xo::scope;
using xo::xtag; using xo::xtag;
@ -79,8 +88,13 @@ namespace ut {
}; };
} }
static InitEvidence s_init = (InitSubsys<S_object2_tag>::require());
TEST_CASE("x1", "[gc][x1]") TEST_CASE("x1", "[gc][x1]")
{ {
// real purpose: ensure s_init survives static linking
REQUIRE(s_init.evidence());
/** /**
* This is a basic Collector test for xo-object2 data types * This is a basic Collector test for xo-object2 data types
**/ **/
@ -89,6 +103,8 @@ namespace ut {
scope log(XO_DEBUG(c_debug_flag)); 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) { for (std::size_t i_tc = 0, n_tc = s_testcase_v.size(); i_tc < n_tc; ++i_tc) {
scope log(XO_DEBUG(true), xtag("i_tc", i_tc));
try { try {
const testcase_x1 & tc = s_testcase_v[i_tc]; const testcase_x1 & tc = s_testcase_v[i_tc];
@ -169,7 +185,7 @@ namespace ut {
auto c_o = with_facet<ACollector>::mkobj(&gc); auto c_o = with_facet<ACollector>::mkobj(&gc);
/* register object types */ /* register object types */
bool ok = object2_register_types(c_o); bool ok = CollectorTypeRegistry::instance().install_types(c_o);
REQUIRE(ok); REQUIRE(ok);

View file

@ -1,6 +1,24 @@
/* file object2_utest_main.cpp */ /* file object2_utest_main.cpp */
#define CATCH_CONFIG_MAIN #include <xo/subsys/Subsystem.hpp>
#define CATCH_CONFIG_RUNNER
#include "catch2/catch.hpp" #include "catch2/catch.hpp"
int
main(int argc, char* argv[])
{
using xo::Subsystem;
// Your custom initialization code here
Subsystem::initialize_all();
// Run Catch2's test session
int result = Catch::Session().run(argc, argv);
// cleanup here, if any
return result;
}
/* end object2_utest_main.cpp */ /* end object2_utest_main.cpp */

View file

@ -130,8 +130,8 @@ namespace xo {
public: public:
SubsystemImpl() = default; SubsystemImpl() = default;
SubsystemImpl(bool require_flag, SubsystemImpl(bool require_flag,
std::string_view subsys_name, std::string_view subsys_name,
std::function<void ()> init_fn) std::function<void ()> init_fn)
: require_flag_{require_flag}, : require_flag_{require_flag},
subsys_name_{subsys_name}, subsys_name_{subsys_name},
init_fn_{init_fn} {} init_fn_{init_fn} {}