Conversation
A method's virtual parameters may now carry different registries, and so dispatch on classes that use different RTTI systems. Each parameter dispatches in the registry it carries - its traits, v-table pointer and type ids all come from there - or in the method's when it carries none. Parameters that carry different registries still require the method to name one; it may name any registry. Type ids never cross registries: two registries may give the same id to different classes. The method registers a record per foreign parameter with the parameter's registry, whose initialize() maps the ids, checks the classes, reserves a slot in its v-tables and publishes the cone of the parameter's class by position. The method's registry builds its dispatch data from that and writes the entries at commit. Dispatch code is unchanged, and parameters may come in any order. The parameter's registry must therefore be initialized first, and the method's again after it is re-initialized; a new error, parameter_registry_not_initialized, is raised by initialize() and, under runtime checks, by a call. A method and the registries of its parameters must agree on deferred static rtti. The five compile-fail tests that expected "registry mismatch" become test_mixed_registries_affinity.cpp. Also fixes an ambiguous validate_overrider_parameter specialization when an overrider's `const virtual_ptr<C>&` parameter is exactly the method's. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
An automated preview of the documentation is available at https://126.openmethod.prtest3.cppalliance.org/libs/openmethod/doc/html/index.html If more commits are pushed to the pull request, the docs will rebuild at the same URL. 2026-09-26 19:19:55 UTC |
Three follow-ups to the mixed registries commit. Module copies of a method now share a slot. Each module's copy registers its own foreign_parameter_info, and the parameter's registry could not tell them apart, so it gave each one a slot. It now groups them by the address of the method registry's state, the method's type_id and the parameter's position. It cannot compare the method's ids itself - they belong to another rtti policy - so the method template supplies `same_method`, built from its own registry's rtti::type_index. Each parameter's type ids follow the deferral of the registry it dispatches in, not the method's, so a registry with deferred_static_rtti mixes with one without, in either direction. resolve_type_ids sets the method's own ids and the native positions. A foreign position whose registry does not defer is set at construction; one whose registry does is set by that registry's initialize(), through `resolve_vp` function pointers the templates supply, because that registry knows neither the method's type nor the overriders'. The static_assert that required agreement, and its compile-fail test, are gone. The slot-sharing key calls `method_type()` rather than reading method_type_id, which a deferring method registry has not set yet when the parameter's registry initializes. The trace follows the handshake across both registries. The parameter's registry lists each overrider's class for the parameter, says which parameter an entry it leaves empty belongs to instead of printing "empty", and ends with "Publishing to methods of other registries". The method's registry prints what it received per foreign parameter, names the cone classes foreign#k, and ends with "Entries in the v-tables of other registries, written at commit" - the step nothing showed before. init_type_ids gains a Positions argument, hence test_core.cpp. It is marked [[maybe_unused]] on its `ids` parameter: the pack is empty for a method with no eager foreign parameter, which -Wextra rejects under b2. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011Eo4q4cYTx457W7oaunqsQ
`value` is a method of node_registry itself, so the example now shows a registry hosting its own method beside the slot it reserves for another registry's parameter - slot 0 and slot 1 in the trace - and the call block prints each rendering followed by its value. node_rtti gave every non-Node type the id nullptr. That was harmless with no methods in node_registry, but two methods would share one key in augment_methods, their overriders would collide, and the first call would abort in fn_ambiguous with no message. It now allocates those ids counting down from the top of the type_id range, where they cannot meet the node ids 1 to 3, and type_name prints them as <1>, <2>... - distinct from the #n the library's trace uses for indices. The assertions moved out of the tagged region: the example captures stdout and checks it afterwards, so the rendered listing shows only the printing. CLAUDE.md documents the trace, and the per-parameter deferral and slot sharing of the previous commit. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011Eo4q4cYTx457W7oaunqsQ
`test_mixed_registries_affinity.cpp` wrapped its classes in an anonymous
namespace, so the `boost_openmethod_registry` hidden friend got internal
linkage. It is only ever declared - the affinity machinery reads its return
type and never calls it - and clang rejects that under
-Wunneeded-internal-declaration, which -Wall -Wextra -Werror makes an error:
error: function 'boost_openmethod_registry' is not needed and will not be
emitted [-Werror,-Wunneeded-internal-declaration]
Every clang job on the branch failed on it, and no gcc or msvc job did: gcc
has no such warning, and the CMake test flags are -Wall -Werror without
-Wextra, so a local ctest run never saw it.
The classes now sit at namespace scope, as in test_adl_registry_hidden_friend
and the other affinity tests, which is why those never tripped it.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011Eo4q4cYTx457W7oaunqsQ
The `operator<<` that prints a container of classes uses its `classes`
parameter only inside `if constexpr (Compiler::has_trace)`. MSVC 14.2
(VS2019) counts that as unreferenced and reports C4100, which /W4 /WX turns
into an error:
initialize.hpp(721): warning C4100: 'classes': unreferenced formal
parameter
The operator is not new, but nothing instantiated it in a non-tracing build
until the mixed registries commit made `augment_methods` print `method.vp` -
a vector of class_* - where it printed `range{vp_begin, vp_end}` before. The
vector selects this overload; the range selects another.
Newer MSVC does not warn for a parameter used only in a discarded
`if constexpr` branch, so VS2022, VS2026 and the CMake jobs were all green;
only Drone's VS2019 stage caught it, and it had been failing since the first
commit of the branch.
Reproduced locally with the v142 toolset, which VS Build Tools 18 still
ships: `vcvarsall x64_x86 -vcvars_ver=14.29`, /std:c++20 /O2 /W4 /WX. Note
that passing the Boost headers with /external:I and /external:W0 hides this
warning even though it is raised in an openmethod header - use /I to see it.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011Eo4q4cYTx457W7oaunqsQ
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## develop #126 +/- ##
===========================================
- Coverage 92.34% 92.03% -0.31%
===========================================
Files 27 27
Lines 2234 2524 +290
Branches 679 801 +122
===========================================
+ Hits 2063 2323 +260
- Misses 112 123 +11
- Partials 59 78 +19
... and 2 files with indirect coverage changes Continue to review full report in Codecov by Harness.
🚀 New features to boost your workflow:
|
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
(Written by Claude Code, on behalf of @jll63.)
A method's virtual parameters may now carry different registries, which lets a multi-method dispatch on classes that use different RTTI systems, e.g. a hierarchy with a custom RTTI next to one with standard RTTI:
Rules
parameter_registry_not_initializederror is raised: byinitialize(), and by a call underruntime_checks. Two registries that each hold a method with a parameter in the other therefore cannot both be initialized.deferred_static_rttimix with those that don't, in either direction: each parameter's type ids are resolved when its own registry resolves them.How it works
Type ids never cross registries: two registries may give the same id to different classes, or use incomparable ids. The method registers one
foreign_parameter_infoper foreign parameter with that parameter's registry. That registry'sinitialize():missing_class/missing_baseitself;The method's registry builds proxy classes from the cone and writes the entries in its noexcept commit. The dispatch code is unchanged, and a foreign parameter may be in any position, including the first. A generation counter detects re-initialization.
Module copies share a slot. When modules share a registry, each may hold its own copy of a method, and each registers its own record. The parameter's registry groups them by the address of the method registry's state, the method's type id, and the parameter's position. It cannot compare the method's ids itself - they belong to another rtti policy - so the method template supplies a comparator built from its own registry's
rtti::type_index.Deferral is per parameter. The method's own ids and those of its native parameters follow the method registry's deferral, as before. A foreign parameter whose registry does not defer gets its ids at construction; one whose registry does gets them in that registry's
initialize(), through function pointers the templates supply, since that registry knows neither the method's type nor the overriders'. No registry calls another's rtti during static construction.trace()shows the handshake from both ends. The parameter's registry prints the overrider classes it resolved and what it publishes; the method's registry prints what it received, names the cone classesforeign#k, and lists the entries it writes back into the other registry's v-tables. The addresses match between the two.Tests and docs
test_mixed_registries.cppuses three registries with different RTTI, two of which deliberately assign the same ids to unrelated classes. It covers a foreign first, second and only parameter,next, the bad-call errors, re-initialization, and (copies_share_the_slot) two module copies of a method sharing one slot.test_mixed_registries_affinity.cppreplaces the five compile-fail tests that expected "registry mismatch", which is now legal.test_mixed_registries_deferred.cppmixes deferred and non-deferred registries both ways. Its ids are assigned at the start of the test, so an id read during static construction is 0 and dispatch fails.registries_and_policies.adoc, with examplemixed_registries.cpp: an expression tree with custom RTTI, rendered through standard-RTTI output formats, and evaluated by a method of the node registry itself. Reference comments are updated.Verified locally. Headers (0470caf): GCC 13 CMake with
-Werror(196/196), GCC 16 with C++26 reflection + shared libs +-Werror(202/202), b2 with-Wall -Wextra -Werror(125 tests), MSVC v18/W4 /WXon the three mixed-registry tests. Example and docs (a84fc9f): GCC 13 CMake only; the Antora render predates thevaluesection.Notes
validate_overrider_parameterspecialization when an overrider'sconst virtual_ptr<C>¶meter is exactly the method's. This predates the change.virtual_<const std::any&, S>in a method of another registry now dispatches inS(a manual probe works). No test covers it yet, and the other interop headers were not tried. This changes the behaviour described in interop: a registry spelled on ananyvirtual parameter is never validated against the method's #123: the spelled registry is now honoured rather than ignored.🤖 Generated with Claude Code
https://claude.ai/code/session_011Eo4q4cYTx457W7oaunqsQ