initialize: correct the class range's iterator category, drop a homonym - #107
Open
jll63 wants to merge 2 commits into
Open
initialize: correct the class range's iterator category, drop a homonym#107jll63 wants to merge 2 commits into
jll63 wants to merge 2 commits into
Conversation
`const_class_iterator` declared `forward_iterator_tag` while its `reference` is `class_view` - a prvalue built on the fly by `operator*`. A forward iterator owes a real reference and a multipass guarantee; this one can give neither: two iterators at the same position hand out views at different addresses. libstdc++ says so outright under `_GLIBCXX_CONCEPT_CHECKS`, which rejects an algorithm asserting `_ForwardIteratorConcept` over the range with "reference type of a forward iterator must be a real reference". Nothing in the tree acted on the promise - `fast_perfect_hash`, `vptr_vector` and `vptr_map` only ever `++`, `!=` and dereference, and `std::distance` needs no more - but `classes_begin()`/`classes_end()` are public API, so an out-of-tree policy author is entitled to. Say input, and say in the blueprint why. Not a regression: before the class range was reshaped, the iterator advertised the same tag with `reference` spelled `const class_info*&` while `operator*` returned a prvalue, so even `std::iterator_traits<It>::reference r = *it;` did not compile. This is a long-standing mis-tag, now corrected. The `type_id_begin()`/`type_id_end()` blueprint still says forward, correctly: those return a pointer. test_initialize_context.cpp pins the category, and the reason for it - that `reference` and `value_type` are the same type. It fails the first assertion if the tag goes back to forward. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JQa4fuiwcfsheZYTCyfPPr
`detail::class_info::vptr()` returned `*static_vptr` - the v-table pointer from the *previous* initialize(), by reference. `class_view::vptr()`, which is what a policy sees, returns the one being *staged*, by value. The two differ for every class on every pass, and differ precisely while the policies run, which is when a reader of initialize.hpp is most likely to reach for the wrong one. One caller was left, a trace line that took its address - so `&cr.vptr()` was a two-step spelling of `cr.static_vptr`, correct but roundabout. Say `cr.static_vptr` and delete the accessor. No behaviour change: the address of `*static_vptr` is `static_vptr`. Nothing outside include/ ever used it, and `class_info` is never handed to a policy - the InitializeContext blueprint exposes only `class_view` - so the confusion was confined to future edits inside the library. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JQa4fuiwcfsheZYTCyfPPr
|
An automated preview of the documentation is available at https://107.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-12 15:07:04 UTC |
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.)
Two small corrections to the API a policy's
initializesees. Neither changes behaviour;both remove a statement that is not true.
1. The class range is an input range, not a forward one
const_class_iteratordeclaredforward_iterator_tagwhile itsreferenceisclass_view— a prvalue built on the fly byoperator*. A forward iterator owes a realreference and a multipass guarantee, and this one can give neither: two iterators at the same
position hand out views at different addresses. libstdc++ says so outright under
_GLIBCXX_CONCEPT_CHECKS, which rejects any algorithm asserting_ForwardIteratorConceptover the range:
Nothing in the tree acted on the promise —
fast_perfect_hash,vptr_vectorandvptr_maponly ever
++,!=and dereference, andstd::distanceneeds no more — butclasses_begin()/classes_end()are public API, so an out-of-tree policy author is entitledto. The tag becomes
input_iterator_tag, and the blueprint says why.Not a regression. Before the class range was reshaped, the iterator advertised the same
tag with
referencespelledconst class_info*&whileoperator*returned a prvalue — soeven
std::iterator_traits<It>::reference r = *it;failed to compile. This is along-standing mis-tag, not something a recent change broke.
type_id_begin()/type_id_end()still say forward in the blueprint, correctly: thosereturn a pointer.
Worth knowing for context: under C++20,
std::forward_iteratoris satisfied by both theold and the new iterator, so
std::rangesaccepts the range either way. Only the C++17taxonomy — and libstdc++'s concept checks, which implement it — is affected.
2.
class_info::vptr()was a misleading homonymIt returned
*static_vptr, the v-table pointer from the previousinitialize(), byreference.
class_view::vptr(), which is what a policy sees, returns the one being staged,by value. The two differ for every class on every pass — and differ precisely while the
policies run, which is when a reader of
initialize.hppis most likely to reach for thewrong one.
One caller was left, a trace line that took its address, so
&cr.vptr()was a two-stepspelling of
cr.static_vptr: correct, but roundabout. That becomescr.static_vptrand theaccessor goes. No behaviour change — the address of
*static_vptrisstatic_vptr. Nothingoutside
include/ever used it, andclass_infois never handed to a policy (theInitializeContextblueprint exposes onlyclass_view), so the confusion was confined tofuture edits inside the library.
Test
test/test_initialize_context.cppis new — nothing in the suite looked at the class range'scategory, or at the range at all from a policy's point of view. It rides a policy along on a
normal registry to get a
Context, then pins the category and the reason for it (thatreferenceandvalue_typeare the same type), counts the classes throughstd::distance, and checks that single-pass does not mean unstable: two iterators at thesame position describe the same class.
Confirmed it catches a regression: with the tag put back to
forward_iterator_tag, it failson the first
static_assert.Verification
ctestpass.toolset=gcc: no failures.clang-format-22applied.🤖 Generated with Claude Code
https://claude.ai/code/session_01JQa4fuiwcfsheZYTCyfPPr