Skip to content

Commit dd2c86f

Browse files
jll63claude
andauthored
doc: forward-declare the registry instead of including preamble.hpp (#93)
* doc: forward-declare the registry instead of including preamble.hpp Overriding BOOST_OPENMETHOD_DEFAULT_REGISTRY forced users to learn the header layering: include a "pre-core" header (preamble.hpp or default_registry.hpp), define the registry, #define the macro, then include <boost/openmethod.hpp>. ref_headers.adoc codified that as a "Pre-Core Headers" section, and custom_rtti.adoc told the reader not to include <boost/openmethod.hpp> yet. None of it is necessary. Every use of the macro in the headers is a name-only context - a default template argument, an alias, a deduction guide, or a member typedef inside a template - so the registry can be forward-declared before the include and defined after it, where <boost/openmethod.hpp> has already supplied registry, default_registry, indirect_registry and the five stock policies: struct my_registry; #define BOOST_OPENMETHOD_DEFAULT_REGISTRY my_registry #include <boost/openmethod.hpp> #include <boost/openmethod/policies/vptr_map.hpp> struct my_registry : boost::openmethod::default_registry::with< boost::openmethod::policies::vptr_map<>> {}; A registry the library provides needs no declaration at all: core.hpp includes default_registry.hpp *before* it tests the macro, so #define plus the include is the whole recipe. preamble.hpp and default_registry.hpp now appear nowhere outside include/. Three rules, all documented: the registry must be complete before the first construct that instantiates it (a hard error otherwise, never silent); it must name a class declared with the same class-key as the definition (a mismatch is MSVC C4099, an error under /W4 /WX); and the name must be qualified if it could also be found in namespace boost::openmethod - `registry` in particular, which otherwise resolves to boost::openmethod::registry and fails with "missing template arguments". Migrated the 17 tests that override the default, plus test_policies.cpp. test_capture_errors.hpp now includes <boost/openmethod.hpp>; test_core.cpp drops a redundant preamble.hpp; dynamic_loading/registry.hpp loses its whole #ifndef fallback, which only existed because the alias preceded the include. The two custom_rtti step-by-step examples get a tag::setup[] region so tag::policy[] no longer opens with an include. ref_headers.adoc renames "Pre-Core Headers" to "Policy Headers" and demotes preamble.hpp and default_registry.hpp to "Headers Included by Other Headers". registries_and_policies.adoc becomes the canonical home of the recipe; its reference to <boost/openmethod/registry.hpp>, a file that does not exist, is fixed. shared_libraries.adoc's my_registry.hpp listing gains the #define and include it was missing - as written it never actually overrode the default. Removed doc/modules/ROOT/examples/deferred_custom_rtti.cpp: an orphan no page included, whose virtual-inheritance and dynamic_cast_ref coverage test_custom_rtti_virtual_base.cpp already provides. Also dropped three stray "end::" tags with no opener, and the dead Quickbook markers in examples/static_rtti.cpp. Closes #90 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix: unbreak the GCC build, and address review of the registry recipe The doc comment added for BOOST_OPENMETHOD_DEFAULT_REGISTRY ended a `//!` line with a backslash, making it a line continuation. GCC's -Wcomment (in -Wall) rejects that under -Werror, so every GCC job on both CIs failed while every clang, MSVC and Xcode job passed. Put the #define on one line. Verified by reproducing the exact diagnostic with the pre-fix header and confirming g++ -Wall -Wextra -Werror accepts the fixed one. Restore the coverage lost with deferred_custom_rtti.cpp. That example was the only place combining deferred_static_rtti with virtual bases and dynamic_cast_ref: test_custom_rtti_virtual_base.cpp has the virtual bases and the cast but derives from policies::rtti, and test_custom_rtti_deferred.cpp had neither. Add Bat and Owl, virtually derived, plus the dynamic_cast_ref the cast now needs - `requires_dynamic_cast` is true exactly when static_cast is ill-formed, so these overriders reach the policy hook. Confirmed by sabotaging dynamic_cast_ref and watching the test fail. Name core.hpp, not <boost/openmethod.hpp>, as the point where the macro is read. initialize.hpp, inplace_vptr.hpp and every interop/*.hpp also include core.hpp, so a TU that includes one of those before the #define binds silently to default_registry. Four places still said otherwise. test_capture_errors.hpp now owns the whole recipe - declaration, #define, include and test_registry - instead of the seven tests repeating it. There is no ordering left for a caller to get wrong, which is what the previous version's comment had claimed without being able to guarantee it. Those tests no longer name the macro, so test/CMakeLists.txt would have handed them the shared PCH and silently defeated the override; teach its scan to follow the header too. Documentation fixes from the same review: - ref_headers.adoc claimed every stock policy has its own header; indirect_vptr, runtime_checks and deferred_static_rtti live in preamble.hpp. Say so, and note runtime_checks joins default_registry under BOOST_OPENMETHOD_ENABLE_RUNTIME_CHECKS. - custom_rtti.adoc pointed at a declaration "at the top of the file" that sits below the classes listing, and its Deferred RTTI section never showed the declaration step at all - example 2's setup and registry tags were unrendered. - error_handling.adoc's listing opened with five lines of registry boilerplate its prose never introduced. - shared_libraries.adoc's my_registry.hpp is a complete header now, so give it the include guard the animals.hpp it is compared to has. - CLAUDE.md cited a line number that had already moved, pointed at greps in a section that has none, miscounted the files carrying the macro, and described an unqualified `registry` as resolving silently when it is a hard error. Remove doc/modules/ROOT/examples/static_rtti.cpp: an orphan no page references, left marker-free by the previous commit, duplicating snippets/static_rtti.cpp - which exercises the same policy, classes and dispatch, is the copy the docs render, and asserts more. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * test: give the registration-error tests a shared registry header Finishes the deduplication started for the capture-diagnostics tests. The three test_class_registration_*.cpp files repeated the same six-line prologue and the same `default_registry::with<runtime_checks, throw_error_handler>` definition verbatim; test_checked_registry.hpp now owns the whole recipe, so including it first is all they do and there is no ordering left to get wrong. Teach the PCH scan in test/CMakeLists.txt about the new header, as with test_capture_errors.hpp: those files no longer name the macro, and a force-included PCH would precede the #define and silently bind the macros to default_registry. Verified from build.ninja that all three stay off the shared PCH, and that defeating the override makes the tests fail rather than pass quietly - they assert BOOST_CHECK_THROW(..., missing_class), which needs runtime_checks and throw_error_handler. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * ci: exclude test sources from coverage The codecov report counted test/ alongside include/, so the only red check on this PR was three unreachable lines in test_custom_rtti_deferred.cpp: the cast_aux null return and the Bat/Owl delegation-to-base branches, which exist to make the custom cast well-formed for every pair but that no call reaches. Test code is not the thing the coverage target is measuring. The exclusion was already in the file, commented out, but spelled `test/**/*` as the boost-ci sample ships it. That spelling does not do what it looks like: codecov compiles it to (?s:test/.*/[^\/]*)\Z, which requires a second slash, so it matches only the subdirectories of test/ and leaves every top-level test/test_*.cpp counted - which is where nearly all the test code is. `test/**` compiles to (?s:test/.*)\Z and matches at any depth. Verified against the paths codecov actually reports, which are repo-relative despite boost-ci building inside the superproject. Comment records the trap and the validate endpoint. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
1 parent b3bec3b commit dd2c86f

41 files changed

Lines changed: 506 additions & 488 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.codecov.yml

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,4 +20,9 @@ comment:
2020
# See https://docs.codecov.com/docs/ignoring-paths
2121
ignore:
2222
- extra/**/*
23-
# - test/**/*
23+
# `test/**`, not the sample's `test/**/*`: the latter compiles to
24+
# (?s:test/.*/[^\/]*)\Z, which needs a second slash and so matches only the
25+
# subdirectories of test/ - every top-level test/test_*.cpp stays counted.
26+
# Check a pattern with:
27+
# curl -X POST --data-binary @.codecov.yml https://codecov.io/validate
28+
- test/**

CLAUDE.md

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,9 @@ The library is structured in three conceptual layers:
145145
- Registry and policy framework
146146
- Error types: `not_initialized`, `bad_call`, `no_overrider`, `ambiguous_call`, etc.
147147
- No executable dispatch code
148+
- An internal foundation, *not* an entry point: every other header pulls it in, and
149+
nothing outside `include/` includes it directly. See *Overriding the default registry*
150+
below.
148151

149152
2. **Core API** ([core.hpp](include/boost/openmethod/core.hpp))
150153
- `method<Id, ReturnType(Parameters...), Registry>` - Method implementation
@@ -364,6 +367,65 @@ One self-contained example per subdirectory of `doc/modules/ROOT/examples/shared
364367
`test/dynamic_loading/` (whose `registry_state_id()` is compared across modules to prove the state
365368
is a single symbol) and `test/implicit_shared_libraries/`.
366369

370+
### Overriding the default registry
371+
372+
The registry is **forward-declared** before `core.hpp` - or any header that includes it,
373+
`<boost/openmethod.hpp>` among them - and **defined after**. Do not reintroduce the old
374+
"include a pre-core header, define the registry, `#define`, then include" idiom:
375+
`preamble.hpp` and `default_registry.hpp` should appear nowhere outside `include/`. Nothing
376+
enforces that automatically; check it by hand when touching this area:
377+
378+
```bash
379+
git grep -n "openmethod/preamble.hpp\|openmethod/default_registry.hpp" -- doc test
380+
```
381+
382+
```cpp
383+
struct my_registry;
384+
#define BOOST_OPENMETHOD_DEFAULT_REGISTRY my_registry
385+
386+
#include <boost/openmethod.hpp>
387+
#include <boost/openmethod/policies/vptr_map.hpp> // extra policies, any order
388+
#include <boost/openmethod/initialize.hpp> // the TU that calls initialize()
389+
390+
struct my_registry
391+
: boost::openmethod::default_registry::with<
392+
boost::openmethod::policies::vptr_map<>> {};
393+
```
394+
395+
A registry the library provides needs no declaration at all - `default_registry`,
396+
`indirect_registry` and the five stock policies are complete as soon as
397+
`<boost/openmethod.hpp>` has been included, because `core.hpp` includes
398+
`default_registry.hpp` *before* it tests the macro:
399+
400+
```cpp
401+
#define BOOST_OPENMETHOD_DEFAULT_REGISTRY boost::openmethod::indirect_registry
402+
#include <boost/openmethod.hpp>
403+
```
404+
405+
This works because every use of the macro in the headers is a name-only context - a default
406+
template argument, an alias, a deduction guide, or a member typedef inside a template. Three
407+
rules:
408+
409+
- The registry must be **complete** before the first construct that instantiates it: a
410+
`BOOST_OPENMETHOD*` macro, `use_classes`, `method`, `virtual_ptr`, `inplace_vptr_base`,
411+
`initialize()`, or `BOOST_OPENMETHOD_{IMPORT,EXPORT,INSTANTIATE}_REGISTRY`. Violating this is
412+
a hard error (`incomplete type ... used in nested name specifier`, from `use_class_aux` in
413+
`core.hpp`) - never silent.
414+
- It must name a **class**, declared with the same class-key (`struct`) as the definition. A
415+
`class`/`struct` mismatch is MSVC C4099, an error under the suite's `/W4 /WX`.
416+
- **Qualify** the name if it could also be found in `namespace boost::openmethod`. The macro
417+
is expanded inside that namespace, so `#define BOOST_OPENMETHOD_DEFAULT_REGISTRY registry`
418+
binds to `boost::openmethod::registry` rather than the global one. That one is loud -
419+
`missing template arguments` - but a name that *does* resolve would bind to the wrong type
420+
silently. `::registry` works.
421+
422+
`test/CMakeLists.txt` withholds the shared PCH from any `test_*.cpp` that overrides the
423+
registry - a force-included PCH would still precede the `#define`. It detects them by scanning
424+
for the token `BOOST_OPENMETHOD_DEFAULT_REGISTRY` **or** for an include of a header that
425+
carries the override on the file's behalf (`test_capture_errors.hpp`). Add another such header
426+
and the scan has to learn about it: miss one and the file still compiles, binds to
427+
`default_registry`, and fails at run time.
428+
367429
### Custom RTTI
368430
When `<typeinfo>` is unavailable or insufficient, use static_rtti or implement custom RTTI. See `doc/modules/ROOT/examples/custom_rtti/` and policies in `include/boost/openmethod/policies/`.
369431

doc/modules/ROOT/examples/custom_rtti/1/custom_rtti.cpp

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -39,10 +39,15 @@ struct Times : Node {
3939
};
4040
// end::classes[]
4141

42-
// tag::policy[]
43-
#include <boost/openmethod/preamble.hpp>
44-
#include <boost/openmethod/policies/vptr_vector.hpp>
42+
// tag::setup[]
43+
struct custom_registry;
44+
#define BOOST_OPENMETHOD_DEFAULT_REGISTRY custom_registry
45+
46+
#include <boost/openmethod.hpp>
47+
#include <boost/openmethod/initialize.hpp>
48+
// end::setup[]
4549

50+
// tag::policy[]
4651
struct custom_rtti : boost::openmethod::policies::rtti {
4752
template<class Registry>
4853
struct fn : defaults {
@@ -75,12 +80,8 @@ struct custom_rtti : boost::openmethod::policies::rtti {
7580
// tag::registry[]
7681
struct custom_registry : boost::openmethod::registry<
7782
custom_rtti, boost::openmethod::policies::vptr_vector> {};
78-
79-
#define BOOST_OPENMETHOD_DEFAULT_REGISTRY custom_registry
8083
// end::registry[]
8184

82-
#include <boost/openmethod.hpp>
83-
#include <boost/openmethod/initialize.hpp>
8485
#include <iostream>
8586

8687
using boost::openmethod::virtual_ptr;
@@ -117,7 +118,6 @@ int main() {
117118
postfix(e, std::cout);
118119
std::cout << " = " << e.value() << "\n"; // 2 3 + 4 * = 20
119120
}
120-
// end::content[]
121121

122122
void call_via_ref(const Node& node, std::ostream& os) {
123123
postfix(node, os);

doc/modules/ROOT/examples/custom_rtti/2/custom_rtti.cpp

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -34,10 +34,15 @@ struct Times : Node {
3434
};
3535
// end::classes[]
3636

37-
// tag::policy[]
38-
#include <boost/openmethod/preamble.hpp>
39-
#include <boost/openmethod/policies/vptr_vector.hpp>
37+
// tag::setup[]
38+
struct custom_registry;
39+
#define BOOST_OPENMETHOD_DEFAULT_REGISTRY custom_registry
40+
41+
#include <boost/openmethod.hpp>
42+
#include <boost/openmethod/initialize.hpp>
43+
// end::setup[]
4044

45+
// tag::policy[]
4146
// note: vvvvvvvvvvvvvvvv
4247
struct custom_rtti : boost::openmethod::policies::deferred_static_rtti {
4348
template<class Registry>
@@ -71,12 +76,8 @@ struct custom_rtti : boost::openmethod::policies::deferred_static_rtti {
7176
// tag::registry[]
7277
struct custom_registry : boost::openmethod::registry<
7378
custom_rtti, boost::openmethod::policies::vptr_vector> {};
74-
75-
#define BOOST_OPENMETHOD_DEFAULT_REGISTRY custom_registry
7679
// end::registry[]
7780

78-
#include <boost/openmethod.hpp>
79-
#include <boost/openmethod/initialize.hpp>
8081
#include <iostream>
8182

8283
using boost::openmethod::virtual_ptr;
@@ -111,7 +112,6 @@ int main() {
111112
Plus d{a, b}; Times e{d, c};
112113
std::cout << value(e) << "\n"; // 2 3 + 4 * = 20
113114
}
114-
// end::content[]
115115

116116
auto call_via_ref(const Node& node, std::ostream& os) {
117117
return value(node);

doc/modules/ROOT/examples/deferred_custom_rtti.cpp

Lines changed: 0 additions & 165 deletions
This file was deleted.

doc/modules/ROOT/examples/static_rtti.cpp

Lines changed: 0 additions & 56 deletions
This file was deleted.

0 commit comments

Comments
 (0)