diff --git a/xo-alloc2/utest/DArenaIterator.test.cpp b/xo-alloc2/utest/DArenaIterator.test.cpp index 8eb1614a..7d26e623 100644 --- a/xo-alloc2/utest/DArenaIterator.test.cpp +++ b/xo-alloc2/utest/DArenaIterator.test.cpp @@ -169,7 +169,7 @@ namespace xo { /* arbitrary alloc size */ size_t req_z = 13; - byte * mem = a1o.alloc(typeseq::anon(), req_z); + byte * mem = a1o.alloc(typeseq::sentinel(), req_z); REQUIRE(arena.error_count_ == 0); REQUIRE(mem != nullptr); diff --git a/xo-alloc2/utest/arena.test.cpp b/xo-alloc2/utest/arena.test.cpp index 42ffa904..e9358a10 100644 --- a/xo-alloc2/utest/arena.test.cpp +++ b/xo-alloc2/utest/arena.test.cpp @@ -159,7 +159,7 @@ namespace xo { REQUIRE(a1o.allocated() == 0); size_t z0 = 1; - byte * m0 = a1o.alloc(typeseq::anon(), 1); + byte * m0 = a1o.alloc(typeseq::sentinel(), 1); REQUIRE(m0); REQUIRE(a1o.last_error().error_ == error::ok); @@ -171,7 +171,7 @@ namespace xo { REQUIRE(a1o.committed() <= a1o.reserved()); size_t z1 = 16; - byte * m1 = a1o.alloc(typeseq::anon(), z1); + byte * m1 = a1o.alloc(typeseq::sentinel(), z1); REQUIRE(m1); REQUIRE(a1o.last_error().error_ == error::ok); @@ -209,7 +209,7 @@ namespace xo { REQUIRE(a1o.allocated() == 0); size_t z0 = 1; - byte * m0 = a1o.alloc(typeseq::anon(), 1); + byte * m0 = a1o.alloc(typeseq::sentinel(), 1); REQUIRE(m0); @@ -253,7 +253,7 @@ namespace xo { REQUIRE(a1o.allocated() == 0); size_t z0 = 1; - byte * m0 = a1o.alloc(typeseq::anon(), 1); + byte * m0 = a1o.alloc(typeseq::sentinel(), 1); REQUIRE(m0); @@ -306,7 +306,7 @@ namespace xo { REQUIRE(a1o.allocated() == 0); size_t z0 = cfg.hugepage_z_ + 1; - byte * m0 = a1o.alloc(typeseq::anon(), z0); + byte * m0 = a1o.alloc(typeseq::sentinel(), z0); REQUIRE(!m0); diff --git a/xo-alloc2/utest/random_allocs.cpp b/xo-alloc2/utest/random_allocs.cpp index 1f7f833f..2325140d 100644 --- a/xo-alloc2/utest/random_allocs.cpp +++ b/xo-alloc2/utest/random_allocs.cpp @@ -67,7 +67,7 @@ namespace utest { bool ok_flag = true; - std::byte * mem = mm.alloc(typeseq::anon(), z); + std::byte * mem = mm.alloc(typeseq::sentinel(), z); log && log(xtag("i_alloc", i_alloc), xtag("si", si), diff --git a/xo-arena/include/xo/arena/MemorySizeInfo.hpp b/xo-arena/include/xo/arena/MemorySizeInfo.hpp index 764eae6d..37b5ce08 100644 --- a/xo-arena/include/xo/arena/MemorySizeInfo.hpp +++ b/xo-arena/include/xo/arena/MemorySizeInfo.hpp @@ -16,8 +16,10 @@ namespace xo { using size_type = std::size_t; MemorySizeInfo() = default; - MemorySizeInfo(std::string_view name, std::size_t u, std::size_t a, std::size_t c, std::size_t r) - : resource_name_{name}, used_{u}, allocated_{a}, committed_{c}, reserved_{r} + MemorySizeInfo(std::string_view name, + std::size_t u, std::size_t a, std::size_t c, std::size_t r) + : resource_name_{name}, + used_{u}, allocated_{a}, committed_{c}, reserved_{r} {} static MemorySizeInfo sentinel() { return MemorySizeInfo(); } diff --git a/xo-arena/src/arena/DArena.cpp b/xo-arena/src/arena/DArena.cpp index cb305f9a..72d8cbf7 100644 --- a/xo-arena/src/arena/DArena.cpp +++ b/xo-arena/src/arena/DArena.cpp @@ -67,7 +67,7 @@ namespace xo { DArena::DArena(const ArenaConfig & cfg) { - *this = std::move(map(cfg)); + *this = map(cfg); } DArena::DArena(const ArenaConfig & cfg, @@ -290,7 +290,7 @@ namespace xo { (complete_flag ? alloc_mode::sub_complete : alloc_mode::sub_incomplete), - typeseq::anon() /*typeseq: ignored*/, + typeseq::sentinel() /*typeseq: ignored*/, 0 /*age - ignored */); } diff --git a/xo-arena/utest/DArena.test.cpp b/xo-arena/utest/DArena.test.cpp index e999c35f..03e1633f 100644 --- a/xo-arena/utest/DArena.test.cpp +++ b/xo-arena/utest/DArena.test.cpp @@ -147,7 +147,7 @@ namespace xo { REQUIRE(arena.allocated() == 0); size_t z0 = 1; - byte * m0 = arena.alloc(typeseq::anon(), 1); + byte * m0 = arena.alloc(typeseq::sentinel(), 1); REQUIRE(m0); REQUIRE(arena.last_error().error_ == error::ok); @@ -159,7 +159,7 @@ namespace xo { REQUIRE(arena.committed() <= arena.reserved()); size_t z1 = 16; - byte * m1 = arena.alloc(typeseq::anon(), z1); + byte * m1 = arena.alloc(typeseq::sentinel(), z1); REQUIRE(m1); REQUIRE(arena.last_error().error_ == error::ok); @@ -195,7 +195,7 @@ namespace xo { REQUIRE(arena.allocated() == 0); size_t z0 = 1; - byte * m0 = arena.alloc(typeseq::anon(), 1); + byte * m0 = arena.alloc(typeseq::sentinel(), 1); REQUIRE(m0); diff --git a/xo-facet/include/xo/facet/FacetRegistry.hpp b/xo-facet/include/xo/facet/FacetRegistry.hpp index 9b7e7ade..a69735a9 100644 --- a/xo-facet/include/xo/facet/FacetRegistry.hpp +++ b/xo-facet/include/xo/facet/FacetRegistry.hpp @@ -7,8 +7,9 @@ #pragma once +#include "TypeRegistry.hpp" #include "facet_implementation.hpp" -#include "typeseq.hpp" +//#include "typeseq.hpp" #include "obj.hpp" #include #include @@ -74,6 +75,9 @@ namespace xo { static void register_impl() { static FacetImplType impl; + TypeRegistry::register_type(); + TypeRegistry::register_type(); + instance()._register_impl(typeseq::id(), typeseq::id(), &impl); diff --git a/xo-facet/include/xo/facet/TypeRegistry.hpp b/xo-facet/include/xo/facet/TypeRegistry.hpp new file mode 100644 index 00000000..19091630 --- /dev/null +++ b/xo-facet/include/xo/facet/TypeRegistry.hpp @@ -0,0 +1,137 @@ +/** @file TypeRegistry.hpp + * + * @brief Runtime facet implementation lookup + * + * @author Roland Conybeare, Jan 2026 + **/ + +#pragma once + +#include "typeseq.hpp" +#include +#include +#include + +namespace xo { + namespace facet { + + /** @class TypeRegistry + * + * @brief Runtime registry for types. + * + * Just assigns ids and remembers names. + * Not a full reflection implementation + **/ + class TypeRegistry { + public: + using ReprType = xo::mm::DArenaVector; + using ArenaConfig = xo::mm::ArenaConfig; + using MemorySizeVisitor = xo::mm::MemorySizeVisitor; + using typeseq = xo::reflect::typeseq; + + /** singleton instance. + * @p hint_max_capacity is a lower bound for registry capacity. + * Only honored the first time instance is called. + **/ + static TypeRegistry & instance(uint32_t hint_max_capacity = 1024) { + static TypeRegistry s_instance(hint_max_capacity); + return s_instance; + } + + /** Type-safe registration + * + * Registers the compile-time FacetImplementation + * for runtime lookup. + * + * @tparam AFacet abstract facet type + * @tparam DRepr data representation type + **/ + template + static void register_type() { + typerecd r = typerecd::recd(); + + instance()._register_type(r); + } + + /** Number of registered (facet, repr) pairs **/ + std::size_t size() const { return registry_.size(); } + + std::string_view id2name(typeseq id) const noexcept { + return instance()._id2name(id); + } + + /** visit memory pools owned by facet registry **/ + void visit_pools(const MemorySizeVisitor & visitor) { + registry_.visit_pools(visitor); + } + + /** Check if type is registered **/ + bool contains(typeseq id) const + { + if ((0 <= id.seqno()) + && (id.seqno() < static_cast(registry_.size()))) + { + return (registry_.at(id.seqno()).seqno() == id.seqno()); + } + + return false; + } + + void dump(std::ostream * p_out) const { + (*p_out) << std::endl; + (*p_out) << " " << item.name() << std::endl; + } + (*p_out) << ">" << std::endl; + } + + private: + /** Register a facet implementation (type-erased) + * + * @param facet_id typeseq for abstract facet (e.g., APrintable) + * @param repr_id typeseq for data representation (e.g., DFloat) + * @param impl pointer to stateless implementation instance + **/ + void _register_type(const typerecd & recd) + { + if ((recd.seqno() >= 0) + && (static_cast(registry_.size()) <= recd.seqno())) + { + registry_.resize(recd.seqno() + 1); + } + + registry_.at(recd.seqno()) = recd; + } + + /** Get typename from @p id. + **/ + std::string_view _id2name(typeseq id) const + { + if ((0 <= id.seqno()) + && (static_cast(id.seqno()) < registry_.size())) + { + return registry_.at(id.seqno()).name(); + } + + return typerecd::sentinel().name(); + } + + private: + TypeRegistry(uint32_t hint_max_capacity) + : registry_(ReprType::map(ArenaConfig() + .with_name("types") + .with_size(hint_max_capacity + * sizeof(typerecd)))) + {} + + /** runtime lookup table (AFacet,DRepr) -> impl **/ + ReprType registry_; + }; + + } /*namespace facet*/ +} /*namespace xo*/ + +/* end TypeRegistry.hpp */ diff --git a/xo-facet/include/xo/facet/typeseq.hpp b/xo-facet/include/xo/facet/typeseq.hpp index 23b84a92..d43d9e68 100644 --- a/xo-facet/include/xo/facet/typeseq.hpp +++ b/xo-facet/include/xo/facet/typeseq.hpp @@ -12,7 +12,7 @@ namespace xo { namespace facet { // Re-export from xo::arena namespace - using xo::reflect::typeseq_impl; + using xo::reflect::typerecd; using xo::reflect::typeseq; } } /*namespace xo*/ diff --git a/xo-gc/src/gc/DX1Collector.cpp b/xo-gc/src/gc/DX1Collector.cpp index ede4a734..8cbd0bbd 100644 --- a/xo-gc/src/gc/DX1Collector.cpp +++ b/xo-gc/src/gc/DX1Collector.cpp @@ -279,7 +279,7 @@ namespace xo { DX1Collector::add_gc_root_poly(obj * p_root) noexcept { std::byte * mem - = roots_.alloc(typeseq::anon(), + = roots_.alloc(typeseq::sentinel(), sizeof(obj*)); assert(mem); diff --git a/xo-gc/utest/DX1CollectorIterator.test.cpp b/xo-gc/utest/DX1CollectorIterator.test.cpp index 314eb4a8..137c07f3 100644 --- a/xo-gc/utest/DX1CollectorIterator.test.cpp +++ b/xo-gc/utest/DX1CollectorIterator.test.cpp @@ -110,7 +110,7 @@ namespace xo { REQUIRE(a1o.allocated() == 0); size_t req_z = 13; - byte * mem = gc.alloc(typeseq::anon(), req_z); + byte * mem = gc.alloc(typeseq::sentinel(), req_z); REQUIRE(mem != nullptr); diff --git a/xo-gc/utest/random_allocs.cpp b/xo-gc/utest/random_allocs.cpp index 1f7f833f..2325140d 100644 --- a/xo-gc/utest/random_allocs.cpp +++ b/xo-gc/utest/random_allocs.cpp @@ -67,7 +67,7 @@ namespace utest { bool ok_flag = true; - std::byte * mem = mm.alloc(typeseq::anon(), z); + std::byte * mem = mm.alloc(typeseq::sentinel(), z); log && log(xtag("i_alloc", i_alloc), xtag("si", si), diff --git a/xo-reader2/utest/SchematikaParser.test.cpp b/xo-reader2/utest/SchematikaParser.test.cpp index 5265922a..2f2272ab 100644 --- a/xo-reader2/utest/SchematikaParser.test.cpp +++ b/xo-reader2/utest/SchematikaParser.test.cpp @@ -78,7 +78,7 @@ namespace xo { bool log_memory_layout(scope * p_log) { auto visitor = [p_log](const MemorySizeInfo & info) { - *p_log && (*p_log)(xtag("resource", info.resource_name_), + *p_log && (*p_log)(xtag("name", info.resource_name_), xtag("used", info.used_), xtag("alloc", info.allocated_), xtag("commit", info.committed_), diff --git a/xo-refcnt/include/xo/cxxutil/demangle.hpp b/xo-refcnt/include/xo/cxxutil/demangle.hpp index e2184f5d..fd7425ba 100644 --- a/xo-refcnt/include/xo/cxxutil/demangle.hpp +++ b/xo-refcnt/include/xo/cxxutil/demangle.hpp @@ -2,91 +2,6 @@ #pragma once -#include -#include -#include // std::array -#include // std::index_sequence - -namespace xo { - namespace reflect { - - template - constexpr auto - substring_as_array(std::string_view str, - std::index_sequence indexes) - { - //return std::array{str[Idxs]..., '\n'}; - return std::array{str[Idxs]...}; - } /*substring_as_array*/ - - template constexpr auto type_name_array() { -#if defined(__clang__) - constexpr auto prefix = std::string_view{"[T = "}; - constexpr auto suffix = std::string_view{"]"}; - constexpr auto function = std::string_view{__PRETTY_FUNCTION__}; -#elif defined(__GNUC__) - constexpr auto prefix = std::string_view{"with T = "}; - constexpr auto suffix = std::string_view{"]"}; - constexpr auto function = std::string_view{__PRETTY_FUNCTION__}; -#elif defined(_MSC_VER) - constexpr auto prefix = std::string_view{"type_name_array<"}; - constexpr auto suffix = std::string_view{">(void)"}; - constexpr auto function = std::string_view{__FUNCSIG__}; -#else -# error type_name_array: Unsupported compiler -#endif - - constexpr auto start = function.find(prefix) + prefix.size(); - constexpr auto end = function.rfind(suffix); - - //static_assert(start < end); - - constexpr auto name = function.substr(start, (end - start)); - - constexpr auto ixseq = std::make_index_sequence{}; - - return substring_as_array(name, ixseq); - } /*type_name_array*/ - - template - struct type_name_holder { - static inline constexpr auto value = type_name_array(); - }; - - template - constexpr auto type_name() -> std::string_view - { - constexpr auto& value = type_name_holder::value; - return std::string_view{value.data(), value.size()}; - } - -#ifdef NOT_IN_USE - template - struct join - { - // Join all strings into a single std::array of chars - static constexpr auto impl() noexcept - { - constexpr std::size_t len = (Strs.size() + ... + 0); - std::array arr{}; - auto append = [i = 0, &arr](auto const& s) mutable { - for (auto c : s) arr[i++] = c; - }; - (append(Strs), ...); - arr[len] = 0; - return arr; - } - // Give the joined string static storage - static constexpr auto arr = impl(); - // View as a std::string_view - static constexpr std::string_view value {arr.data(), arr.size() - 1}; - }; - - // Helper to get the value out - template - static constexpr auto join_v = join::value; -#endif - } /*namespace reflect*/ -} /*namespace xo*/ +#include /* end demangle.hpp */ diff --git a/xo-refcnt/src/CMakeLists.txt b/xo-refcnt/src/CMakeLists.txt index 9cbc92ff..e8b5288f 100644 --- a/xo-refcnt/src/CMakeLists.txt +++ b/xo-refcnt/src/CMakeLists.txt @@ -7,4 +7,5 @@ xo_install_include_tree3(include/xo/cxxutil) # NOTE: # dependency set here must be kept consistent with refcnt/cmake/refcntConfig.cmake.in # +xo_dependency(${SELF_LIB} xo_reflectutil) xo_dependency(${SELF_LIB} indentlog) diff --git a/xo-reflectutil/include/xo/reflectutil/type_name.hpp b/xo-reflectutil/include/xo/reflectutil/type_name.hpp new file mode 100644 index 00000000..bdce1b0a --- /dev/null +++ b/xo-reflectutil/include/xo/reflectutil/type_name.hpp @@ -0,0 +1,94 @@ +/* @file type_name.hpp */ + +#pragma once + +//#include +#include +#include // std::array +#include // std::index_sequence + +namespace xo { + namespace reflect { + + template + constexpr auto + substring_as_array(std::string_view str, + std::index_sequence indexes) + { + //return std::array{str[Idxs]..., '\n'}; + return std::array{str[Idxs]...}; + } /*substring_as_array*/ + + template constexpr auto type_name_array() { +#if defined(__clang__) + constexpr auto prefix = std::string_view{"[T = "}; + constexpr auto suffix = std::string_view{"]"}; + constexpr auto function = std::string_view{__PRETTY_FUNCTION__}; +#elif defined(__GNUC__) + constexpr auto prefix = std::string_view{"with T = "}; + constexpr auto suffix = std::string_view{"]"}; + constexpr auto function = std::string_view{__PRETTY_FUNCTION__}; +#elif defined(_MSC_VER) + constexpr auto prefix = std::string_view{"type_name_array<"}; + constexpr auto suffix = std::string_view{">(void)"}; + constexpr auto function = std::string_view{__FUNCSIG__}; +#else +# error type_name_array: Unsupported compiler +#endif + + constexpr auto start = function.find(prefix) + prefix.size(); + constexpr auto end = function.rfind(suffix); + + //static_assert(start < end); + + constexpr auto name = function.substr(start, (end - start)); + + constexpr auto ixseq = std::make_index_sequence{}; + + return substring_as_array(name, ixseq); + } /*type_name_array*/ + + template + struct type_name_holder { + static inline constexpr auto value = type_name_array(); + }; + + /** report name of type T as a string_view; + * using constexpr deps so runs at **/ + template + constexpr auto type_name() -> std::string_view + { + constexpr auto& value = type_name_holder::value; + return std::string_view{value.data(), value.size()}; + } + +#ifdef NOT_IN_USE + template + struct join + { + // Join all strings into a single std::array of chars + static constexpr auto impl() noexcept + { + constexpr std::size_t len = (Strs.size() + ... + 0); + std::array arr{}; + auto append = [i = 0, &arr](auto const& s) mutable { + for (auto c : s) arr[i++] = c; + }; + (append(Strs), ...); + arr[len] = 0; + return arr; + } + // Give the joined string static storage + static constexpr auto arr = impl(); + // View as a std::string_view + static constexpr std::string_view value {arr.data(), arr.size() - 1}; + }; + + // Helper to get the value out + template + static constexpr auto join_v = join::value; +#endif + } /*namespace reflect*/ +} /*namespace xo*/ + +/* end type_name.hpp */ diff --git a/xo-reflectutil/include/xo/reflectutil/typeseq.hpp b/xo-reflectutil/include/xo/reflectutil/typeseq.hpp index d2f888cd..336f011c 100644 --- a/xo-reflectutil/include/xo/reflectutil/typeseq.hpp +++ b/xo-reflectutil/include/xo/reflectutil/typeseq.hpp @@ -5,22 +5,20 @@ #pragma once +#include "type_name.hpp" #include #include namespace xo { namespace reflect { - /** - * Tag here so we can preserve header-only implementation - * and still have static variable - */ - template - struct typeseq_impl { - /** create sentinel value **/ - typeseq_impl() = default; +// template + struct typerecd { + /** sentinel value **/ + typerecd() = default; - /** typeseq with specific unique id **/ - explicit typeseq_impl(int32_t s) : seqno_{s} {} + /** type-record with specific, unique id **/ + explicit typerecd(int32_t s, + std::string_view n) : seqno_{s}, name_{n} {} /** Can't have this be constexpr. * We need ids in shared libraries to be generated @@ -35,57 +33,88 @@ namespace xo { * when using clang. **/ template - static typeseq_impl id() { - static bool armed = true; + static typerecd recd() { + // reminder: {armed, id} are distint for each T + static bool s_armed = true; static int32_t id = 0; - if (armed) { - armed = false; - id = ++s_next_id; + if (s_armed) { + s_armed = false; + id = require_next_id(); } - return typeseq_impl(id); + return typerecd(id, xo::reflect::type_name()); } + static int32_t require_next_id() { + static int32_t s_next_id = 0; + return s_next_id++; + } + + int32_t seqno() const { return seqno_; } + std::string_view name() const { return name_; } + + /** sentinel typerecd instance **/ + static typerecd sentinel() { + return typerecd(-1, "_%sentinel%_"); + } + + private: + int32_t seqno_ = 0; + std::string_view name_; + }; + + //template + //int32_t typerecd_impl::s_next_id = 0; + + /** + * Tag here so we can preserve header-only implementation + * and still have static variable + */ + struct typeseq { + /** create sentinel value **/ + typeseq() = default; + + /** typeseq with specific unique id **/ + explicit typeseq(int32_t s) : seqno_{s} {} + /** 'anonymous' sentinel type. * Niche uses for this, e.g. untyped allocator **/ - static typeseq_impl anon() { - return typeseq_impl(-1); + static typeseq sentinel() { + return typeseq(typerecd::sentinel().seqno()); + } + + template + static typeseq id() { + return typeseq(xo::reflect::typerecd::recd().seqno()); } int32_t seqno() const { return seqno_; } private: - static int32_t s_next_id; - int32_t seqno_ = 0; }; - template - int32_t typeseq_impl::s_next_id = 0; + //template + //int32_t typeseq_impl::s_next_id = 0; - template inline bool - operator==(const typeseq_impl & lhs, const typeseq_impl & rhs) { + operator==(const typeseq & lhs, const typeseq & rhs) { return lhs.seqno() == rhs.seqno(); } - template inline bool - operator!=(const typeseq_impl & lhs, const typeseq_impl & rhs) { + operator!=(const typeseq & lhs, const typeseq & rhs) { return lhs.seqno() != rhs.seqno(); } - template inline std::ostream & - operator<<(std::ostream & s, const typeseq_impl & x) { + operator<<(std::ostream & s, const typeseq & x) { s << x.seqno(); return s; } - - using typeseq = typeseq_impl<>; } /*namespace reflect*/ } /*namespace xo*/