Skip to content

overriders: BOOST_OPENMETHOD_MEM family for methods/overriders as static members (#53) - #122

Open
jll63 wants to merge 6 commits into
boostorg:developfrom
jll63:mem-alt
Open

jll63 wants to merge 6 commits into
boostorg:developfrom
jll63:mem-alt

Conversation

@jll63

@jll63 jll63 commented Sep 19, 2026

Copy link
Copy Markdown
Collaborator

Summary

Alternative design for #53, exploring a different API shape than #120
(feature/member-overriders). Independent branches - neither depends on the
other, and this is deliberately a separate PR to compare approaches, not a
replacement.

Six new macros mirroring BOOST_OPENMETHOD and its overrider macros, but
declaring static member functions instead of free ones. No implicit
this, no receiver binding - dispatch is still entirely by the method's own
virtual parameters, exactly as for a free method.

  • BOOST_OPENMETHOD_MEM declares a method as a static member function,
    overloadable exactly like a free method (its tag is generated per
    expansion, not ID-derived, which is what makes the overload possible).
  • BOOST_OPENMETHOD_TYPE_MEM names a member method's core method<> type,
    since BOOST_OPENMETHOD_TYPE can't.
  • BOOST_OPENMETHOD_OVERRIDE_MEM / _DECLARE_OVERRIDER_MEM /
    _DEFINE_OVERRIDER_MEM add an overrider, as a static member function, to
    either a free method or a member method (Class::method). Being a member
    gives it the same access to its class's private state as any other member,
    with no friend declaration.
  • BOOST_OPENMETHOD_OVERRIDER_MEM finds a member overrider's key (fn,
    method_type) from outside, for explicit calls and for next/has_next
    via the core API.

No core.hpp changes: each overrider registers through a per-overrider
"key" struct holding a BOOST_FORCEINLINE trampoline, so
override_aux/thunk/validate_overrider_parameter all see an ordinary
function pointer, unchanged.

Known limitation: a _MEM overrider's body and key-accessor are each
named once, overloaded purely on (return type, parameters) - never on
which method they override, since a qualified ID can't be pasted into a
new declaration. At most one overrider of a given exact signature per class,
regardless of method. Pinned by
test/compile_fail_member_overrider_signature_collision.cpp.

doc/modules/ROOT/pages/friends.adoc is retitled "Members and Friends" in
the nav and gains a _MEM tutorial before the existing friend content,
which it supersedes for classes under the caller's control.

Test plan

  • ctest - 203/203 passing (197 baseline + test_member_method.cpp (2
    cases) + 3 new compile-fail tests)
  • Verified on gcc 13, clang 18, and MSVC 19.51/BuildTools v18 (the
    design leans on nested-class complete-class-context bodies and
    function-template address deduction, both flagged as MSVC risk during
    design)
  • doc/build_antora.sh - full site build, no new warnings; verified no
    stray backticks and no MrDocs artifact leakage in the touched/new
    pages and six new reference pages
  • rolex/8 example and member.cpp snippet run manually, output
    matches expected ($5000 / $10000 / $985000, bark/hiss)

🤖 Generated with Claude Code

…tic members (boostorg#53)

Alternative design to boostorg#120: six macros mirroring BOOST_OPENMETHOD and its
overrider macros, but declaring static member functions instead of free
ones. No implicit `this`, no receiver binding - dispatch is still entirely
by the method's own virtual parameters.

- BOOST_OPENMETHOD_MEM declares a method as a static member function of the
  class it's used in, overloadable exactly like a free method. Its tag is
  generated per-expansion rather than ID-derived, which is what makes the
  overload possible.
- BOOST_OPENMETHOD_TYPE_MEM names a member method's core `method<>` type,
  since BOOST_OPENMETHOD_TYPE can't (it reconstructs the tag from ID alone).
- BOOST_OPENMETHOD_OVERRIDE_MEM / _DECLARE_OVERRIDER_MEM / _DEFINE_OVERRIDER_MEM
  add an overrider, as a static member function, to either a free method or
  a member method (qualified as Class::method). Being a member gives it the
  same access to its class's private state as any other member, with no
  `friend` declaration.
- BOOST_OPENMETHOD_OVERRIDER_MEM finds a member overrider's key (`fn`,
  `method_type`) from outside, for explicit calls and for `next`/`has_next`
  via the core API - not available by name inside a _MEM body the way they
  are in a free one's.

No core.hpp changes: each overrider is registered through a per-overrider
"key" struct holding a BOOST_FORCEINLINE trampoline, so override_aux/thunk/
validate_overrider_parameter all see an ordinary function pointer, unchanged.
Verified on gcc, clang and MSVC, including private access, overloaded member
methods, and next<> from a DECLARE/DEFINE-split body (self-referencing,
easy to get backwards - documented prominently).

Known limitation: a _MEM overrider's body and key-accessor are each named
once, overloaded purely on (return type, parameters) - never on which method
they override, since a qualified ID can't be pasted into a new declaration.
At most one overrider of a given exact signature per class, regardless of
method.

doc/modules/ROOT/pages/friends.adoc is retitled "Members and Friends" in the
nav and gains a _MEM tutorial before the existing `friend` content, which it
supersedes for classes under the caller's control.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E53cDWKgiva4cfH48EtvMP
@cppalliance-bot

Copy link
Copy Markdown

An automated preview of the documentation is available at https://122.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-19 16:37:01 UTC

jll63 and others added 4 commits September 20, 2026 12:29
…rence

The page named the macros in plain code spans while every other page links
them, so a reader had no way from the tutorial to the reference page that
describes the arguments.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E53cDWKgiva4cfH48EtvMP
The `pay` example declared its first parameter `Employee&` and made Payroll
derive from Employee so that each overrider could cast it back - which reads
as if a payroll were a kind of employee, and put a `static_cast<Payroll&>` in
front of every call to the private member the example exists to demonstrate.

Declare the parameter `Payroll&` instead, over a forward declaration, exactly
as the `friend` example further down the same page already does: only a
reference appears in the parameter list, and Payroll is not dispatched on.
The inheritance and all three casts go away, and the page's claim that the
overriders "call it as an ordinary same-class private call" becomes literally
true.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E53cDWKgiva4cfH48EtvMP
The page now covers both ways an overrider reaches a class's private
state - being a member of it, and `friend` - so the file name names the
subject rather than one of the two answers. The nav label and the `@see`
link text are unchanged; only the path moves.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E53cDWKgiva4cfH48EtvMP
The tutorial referred to `ID`, and to `(Class, ID, PARAMETERS, RETURN)`,
which are the reference pages' formal parameter names and are never
introduced on the tutorial page itself. Describe the arguments by what
they are instead.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E53cDWKgiva4cfH48EtvMP
…acros

BOOST_OPENMETHOD and BOOST_OPENMETHOD_MEM take an optional registry after
the return type. The macros that name an existing member method, or that
declare an overrider, cannot use one: a member method's registry is fixed
by the declaration that created it, and an overrider takes the registry of
the method it overrides, which LOCATE_METHOD finds through a guide lookup
that enable_guide_ignoring_registry makes registry-agnostic.

Passing one anyway produced two different failures, neither legible.
BOOST_OPENMETHOD_TYPE_MEM accepted it and silently ignored it, because
va_args<...>::return_type takes the first argument and drops the rest.
The four overrider macros pasted __VA_ARGS__ raw into a function pointer
type, which gave three cascading parse errors on the user's own line with
nothing to suggest the cause.

**This makes a previously tolerated input an error**: TYPE_MEM with a
trailing registry no longer compiles. There are no call sites.

Route all five through a new detail::va_args_no_registry, whose variadic
specialization is a static_assert naming the reason. Its return_type lives
in a base class: a failed static_assert marks the record invalid on clang,
so a member declared alongside it is not found and the cascade returns.
With the base, gcc reports exactly one error and clang leads with the
message. A return type containing a top-level comma still arrives as
several macro arguments and reassembles inside the template argument list,
which is what tells the two cases apart - the same discrimination va_args
already relies on, and the reason DEFINE_OVERRIDER_MEM can stop using
mp_back for it.

DETAIL_OVERRIDE_MEM forwards the return type to a new _AUX helper, which
is variadic rather than taking a named RET: a comma-bearing return type
would otherwise be split into several arguments before _AUX could receive
it, and the expansion would fail on arity.

Tests: a comma-bearing return type through all three _MEM overrider shapes
plus TYPE_MEM and OVERRIDER_MEM, which nothing covered before and which
va_args_no_registry is now solely responsible for; and a compile-fail test
for the rejected registry. 204/204.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E53cDWKgiva4cfH48EtvMP
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants