From de126262bfbfe37fed6cdabacc19d6f946ef8324 Mon Sep 17 00:00:00 2001 From: Jean-Louis Leroy Date: Sun, 23 Aug 2026 10:44:40 -0400 Subject: [PATCH 1/4] doc: keep the detail-hiding macro out of the rendered constraints MrDocs renders the condition of an enable_if_t used as a defaulted template argument as a C++20 requires-clause by copying the raw source text spanning the condition expression - it does not walk the expression tree (cppalliance/mrdocs#1016). BOOST_OPENMETHOD_DETAIL_UNLESS_MRDOCS therefore disappears only when it sits before the first token of the condition, where it expands to nothing under __MRDOCS__ and so falls outside the copied range; anywhere inside the condition its name is printed verbatim, whatever shape the macro has. That is why a constraint naming two exposition-only traits rendered the first correctly and leaked the second. Two rules restore the reference, documented next to the macro definition and in CLAUDE.md: the macro must lead the condition, with a negation spelled `Trait == false`; and each trait gets its own defaulted template parameter, which MrDocs joins with && in source order, leaving the rendered clause unchanged. Splitting also makes substitution short-circuit, so `typename Other::element_type` is only formed once SameSmartPtr has passed. No reference page mentions the macro any more. Co-Authored-By: Claude Opus 5 --- CLAUDE.md | 27 +++++++++++++++++ include/boost/openmethod/core.hpp | 30 +++++++++++++++++-- .../boost/openmethod/interop/virtual_any.hpp | 10 ++++--- 3 files changed, 61 insertions(+), 6 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index 0e83b6e3..d05bf70d 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -288,6 +288,33 @@ grep -rn "\`'" doc/modules/ROOT/pages/*.adoc # must return nothing After building, no stray backticks should survive outside code blocks — `grep -n '\`' doc/html/openmethod/.html` should only hit backticks inside C++ comments. +### Reference (MrDocs) constraints + +MrDocs turns the condition of an `enable_if_t` used as a defaulted template argument into a C++20 +requires-clause by copying the **raw source text** spanning the condition expression - it does not +walk the expression tree (cppalliance/mrdocs#1016, which upstream cannot fix until MrDocs can +manipulate expression trees). `BOOST_OPENMETHOD_DETAIL_UNLESS_MRDOCS`, which hides the `detail::` +qualification of the exposition-only traits, therefore disappears only when it sits *before* the +first token of the condition: it expands to nothing under `__MRDOCS__`, so it falls outside the +copied range. Anywhere inside the condition - after a `!`, after a `&&`, inside parentheses - its +name is printed verbatim, whatever shape the macro has (object-like, function-like +`MACRO(detail::)`, or a bare `#ifndef __MRDOCS__` around `detail::`). That is why a constraint with +two occurrences renders the first one correctly and leaks the second. + +Two rules keep the reference clean; the long-form version lives next to the macro definition in +`core.hpp`: + +- **The macro must be the first thing in the condition.** Spell a negation + `MACRO Trait == false`, never `!MACRO Trait`. +- **One trait per condition.** When a constraint needs several, give each its own defaulted + template parameter. MrDocs joins them with `&&`, in order, so the rendered clause is unchanged - + and substitution short-circuits at the first failure, so a dependent type in a later condition + (`typename Other::element_type`) is only formed once the earlier ones pass. + +Only expressions are affected: types are printed from the AST, so the macro may appear anywhere in +one (`method::operator()` takes `typename MACRO StripVirtualDecorator::type...` and +renders correctly). After a doc build, `grep -rl MRDOCS doc/html/` must return nothing. + ## Common Development Patterns ### Working with Shared Libraries / DLL Support diff --git a/include/boost/openmethod/core.hpp b/include/boost/openmethod/core.hpp index cedd132a..8bdc93bd 100644 --- a/include/boost/openmethod/core.hpp +++ b/include/boost/openmethod/core.hpp @@ -66,6 +66,30 @@ //! Top namespace of the library. namespace boost::openmethod { +// Hide the `detail::` qualification of the exposition-only traits from MrDocs, +// which documents them as members of `boost::openmethod`. +// +// MrDocs renders the condition of an `enable_if_t` used as a defaulted template +// argument as a C++20 requires-clause, by copying the *raw source text* spanning +// the condition expression - it does not walk the expression tree +// (cppalliance/mrdocs#1016). A macro is thus elided only when it sits *before* +// the first token of the condition, where it expands to nothing under +// `__MRDOCS__` and so falls outside the copied range; anywhere inside the +// condition - after a `!`, after a `&&`, inside parentheses - its name is +// printed verbatim. That holds whatever shape the macro has: object-like, +// function-like, or a bare `#ifndef __MRDOCS__` around `detail::`. +// +// Hence the rule for a condition mentioning an exposition-only trait: +// +// - `BOOST_OPENMETHOD_DETAIL_UNLESS_MRDOCS` must be the first thing in the +// condition. Spell a negation as `Trait == false`, not `!Trait`. +// - One trait per condition. When a constraint needs several, give each its own +// defaulted template parameter - MrDocs joins them with `&&`, in order, so the +// rendered clause is unchanged, and substitution short-circuits at the first +// failure. +// +// Only expressions are affected. Types are printed from the AST, so the macro +// may appear anywhere in one (see `method::operator()`). #ifdef __MRDOCS__ #define BOOST_OPENMETHOD_OPEN_NAMESPACE_DETAIL_UNLESS_MRDOCS #define BOOST_OPENMETHOD_CLOSE_NAMESPACE_DETAIL_UNLESS_MRDOCS @@ -1420,7 +1444,8 @@ class virtual_ptr< typename = std::enable_if_t< BOOST_OPENMETHOD_DETAIL_UNLESS_MRDOCS SameSmartPtr && - std::is_assignable_v && + std::is_assignable_v>, + typename = std::enable_if_t< BOOST_OPENMETHOD_DETAIL_UNLESS_MRDOCS IsPolymorphic>> virtual_ptr& operator=(const Other& other) { @@ -1456,7 +1481,8 @@ class virtual_ptr< typename = std::enable_if_t< BOOST_OPENMETHOD_DETAIL_UNLESS_MRDOCS SameSmartPtr && - std::is_assignable_v && + std::is_assignable_v>, + typename = std::enable_if_t< BOOST_OPENMETHOD_DETAIL_UNLESS_MRDOCS IsPolymorphic>> virtual_ptr& operator=(Other&& other) { diff --git a/include/boost/openmethod/interop/virtual_any.hpp b/include/boost/openmethod/interop/virtual_any.hpp index 6a7e1b93..6b79a3f2 100644 --- a/include/boost/openmethod/interop/virtual_any.hpp +++ b/include/boost/openmethod/interop/virtual_any.hpp @@ -180,8 +180,9 @@ class virtual_any { template< typename T, typename = std::enable_if_t< - !BOOST_OPENMETHOD_DETAIL_UNLESS_MRDOCS - IsVirtualAny> && + BOOST_OPENMETHOD_DETAIL_UNLESS_MRDOCS + IsVirtualAny> == false>, + typename = std::enable_if_t< !std::is_same_v, Any> && std::is_constructible_v>> virtual_any(T&& value) @@ -259,8 +260,9 @@ class virtual_any { template< typename T, typename = std::enable_if_t< - !BOOST_OPENMETHOD_DETAIL_UNLESS_MRDOCS - IsVirtualAny> && + BOOST_OPENMETHOD_DETAIL_UNLESS_MRDOCS + IsVirtualAny> == false>, + typename = std::enable_if_t< !std::is_same_v, Any> && std::is_constructible_v>> auto operator=(T&& value) -> virtual_any& { From be8dd7f66a00cf1b075e5c7f0f85fde702ce3de6 Mon Sep 17 00:00:00 2001 From: Jean-Louis Leroy Date: Sun, 23 Aug 2026 10:48:33 -0400 Subject: [PATCH 2/4] doc: make the MrDocs escape hatch a general BOOST_OPENMETHOD_UNLESS_MRDOCS(...) Replace BOOST_OPENMETHOD_DETAIL_UNLESS_MRDOCS with a function-like BOOST_OPENMETHOD_UNLESS_MRDOCS(...), spelled at the call sites as BOOST_OPENMETHOD_UNLESS_MRDOCS(detail::). The macro now says what it does - drop this text under __MRDOCS__ - instead of encoding one payload in its name, and the payload is visible where it is used. The shape has no effect on the rendering: MrDocs copies the raw source text of a constraint, so the macro still has to lead the condition, whether it is object-like or function-like. The two rules recorded in the previous commit are unchanged. Also widen the mrdocs.yml exclude-macros pattern, which matched the old name through 'BOOST_OPENMETHOD_DETAIL_*'. 'BOOST_OPENMETHOD_*UNLESS_MRDOCS' covers the new name and the two namespace macros, which were never listed. Verified that MrDocs honours a glob star in the middle of a pattern. Document, in CLAUDE.md, that anything posted in public under the maintainer's account has to identify Claude as its author on the first line. Co-Authored-By: Claude Opus 5 --- CLAUDE.md | 32 ++++++++---- doc/mrdocs.yml | 1 + include/boost/openmethod/core.hpp | 51 +++++++++---------- .../boost/openmethod/interop/virtual_any.hpp | 6 +-- 4 files changed, 52 insertions(+), 38 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index d05bf70d..7c298a51 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -293,26 +293,27 @@ After building, no stray backticks should survive outside code blocks — MrDocs turns the condition of an `enable_if_t` used as a defaulted template argument into a C++20 requires-clause by copying the **raw source text** spanning the condition expression - it does not walk the expression tree (cppalliance/mrdocs#1016, which upstream cannot fix until MrDocs can -manipulate expression trees). `BOOST_OPENMETHOD_DETAIL_UNLESS_MRDOCS`, which hides the `detail::` -qualification of the exposition-only traits, therefore disappears only when it sits *before* the -first token of the condition: it expands to nothing under `__MRDOCS__`, so it falls outside the -copied range. Anywhere inside the condition - after a `!`, after a `&&`, inside parentheses - its -name is printed verbatim, whatever shape the macro has (object-like, function-like -`MACRO(detail::)`, or a bare `#ifndef __MRDOCS__` around `detail::`). That is why a constraint with -two occurrences renders the first one correctly and leaks the second. +manipulate expression trees). `BOOST_OPENMETHOD_UNLESS_MRDOCS(detail::)`, which hides the +`detail::` qualification of the exposition-only traits, therefore disappears only when it sits +*before* the first token of the condition: it expands to nothing under `__MRDOCS__`, so it falls +outside the copied range. Anywhere inside the condition - after a `!`, after a `&&`, inside +parentheses - its name is printed verbatim, whatever shape the macro has (function-like as here, +object-like, or a bare `#ifndef __MRDOCS__` around `detail::`). That is why a constraint with two +occurrences renders the first one correctly and leaks the second. Two rules keep the reference clean; the long-form version lives next to the macro definition in `core.hpp`: - **The macro must be the first thing in the condition.** Spell a negation - `MACRO Trait == false`, never `!MACRO Trait`. + `BOOST_OPENMETHOD_UNLESS_MRDOCS(detail::) Trait == false`, never `!... Trait`. - **One trait per condition.** When a constraint needs several, give each its own defaulted template parameter. MrDocs joins them with `&&`, in order, so the rendered clause is unchanged - and substitution short-circuits at the first failure, so a dependent type in a later condition (`typename Other::element_type`) is only formed once the earlier ones pass. Only expressions are affected: types are printed from the AST, so the macro may appear anywhere in -one (`method::operator()` takes `typename MACRO StripVirtualDecorator::type...` and +one (`method::operator()` takes +`typename BOOST_OPENMETHOD_UNLESS_MRDOCS(detail::) StripVirtualDecorator::type...` and renders correctly). After a doc build, `grep -rl MRDOCS doc/html/` must return nothing. ## Common Development Patterns @@ -406,6 +407,19 @@ For examples: 4. For changes affecting examples: enable `BOOST_OPENMETHOD_BUILD_EXAMPLES` 5. Submit PRs against the `develop` branch +### Posting in public on the maintainer's behalf + +Anything published under the maintainer's account - a GitHub issue or comment, a PR body, a +mailing-list or forum post - must **identify its author in the text itself**, on the first line: + +``` +*(Written by Claude Code, on behalf of @jll63.)* +``` + +The account is a person's, and readers reasonably assume a human wrote what it says; an unlabelled +post misrepresents who is speaking, and a signature in the tool call or the commit trailer is not +visible to them. Ask before posting anyway - the attribution line does not substitute for consent. + ## Important Implementation Details ### Static Registration diff --git a/doc/mrdocs.yml b/doc/mrdocs.yml index 32a92a73..3e4382f5 100644 --- a/doc/mrdocs.yml +++ b/doc/mrdocs.yml @@ -33,6 +33,7 @@ include-macros: - 'BOOST_OPENMETHOD*' exclude-macros: - 'BOOST_OPENMETHOD_DETAIL_*' + - 'BOOST_OPENMETHOD_*UNLESS_MRDOCS' - 'BOOST_OPENMETHOD_GENSYM' - 'BOOST_OPENMETHOD_GUIDE' diff --git a/include/boost/openmethod/core.hpp b/include/boost/openmethod/core.hpp index 8bdc93bd..fa7746cd 100644 --- a/include/boost/openmethod/core.hpp +++ b/include/boost/openmethod/core.hpp @@ -76,12 +76,13 @@ namespace boost::openmethod { // the first token of the condition, where it expands to nothing under // `__MRDOCS__` and so falls outside the copied range; anywhere inside the // condition - after a `!`, after a `&&`, inside parentheses - its name is -// printed verbatim. That holds whatever shape the macro has: object-like, -// function-like, or a bare `#ifndef __MRDOCS__` around `detail::`. +// printed verbatim. That holds whatever shape the macro has - the function-like +// form below, an object-like one, or a bare `#ifndef __MRDOCS__` around +// `detail::`. // // Hence the rule for a condition mentioning an exposition-only trait: // -// - `BOOST_OPENMETHOD_DETAIL_UNLESS_MRDOCS` must be the first thing in the +// - `BOOST_OPENMETHOD_UNLESS_MRDOCS(detail::)` must be the first thing in the // condition. Spell a negation as `Trait == false`, not `!Trait`. // - One trait per condition. When a constraint needs several, give each its own // defaulted template parameter - MrDocs joins them with `&&`, in order, so the @@ -93,11 +94,11 @@ namespace boost::openmethod { #ifdef __MRDOCS__ #define BOOST_OPENMETHOD_OPEN_NAMESPACE_DETAIL_UNLESS_MRDOCS #define BOOST_OPENMETHOD_CLOSE_NAMESPACE_DETAIL_UNLESS_MRDOCS -#define BOOST_OPENMETHOD_DETAIL_UNLESS_MRDOCS +#define BOOST_OPENMETHOD_UNLESS_MRDOCS(...) #else #define BOOST_OPENMETHOD_OPEN_NAMESPACE_DETAIL_UNLESS_MRDOCS namespace detail { #define BOOST_OPENMETHOD_CLOSE_NAMESPACE_DETAIL_UNLESS_MRDOCS } -#define BOOST_OPENMETHOD_DETAIL_UNLESS_MRDOCS detail:: +#define BOOST_OPENMETHOD_UNLESS_MRDOCS(...) __VA_ARGS__ #endif namespace detail { @@ -878,7 +879,7 @@ class virtual_ptr { template< class Other, typename = std::enable_if_t< - BOOST_OPENMETHOD_DETAIL_UNLESS_MRDOCS + BOOST_OPENMETHOD_UNLESS_MRDOCS(detail::) IsPolymorphic && std::is_constructible_v>> virtual_ptr(Other& other) @@ -917,7 +918,7 @@ class virtual_ptr { template< class Other, typename = std::enable_if_t< - BOOST_OPENMETHOD_DETAIL_UNLESS_MRDOCS + BOOST_OPENMETHOD_UNLESS_MRDOCS(detail::) IsPolymorphic && std::is_constructible_v>> virtual_ptr(Other* other) @@ -988,7 +989,7 @@ class virtual_ptr { template< class Other, typename = std::enable_if_t< - BOOST_OPENMETHOD_DETAIL_UNLESS_MRDOCS + BOOST_OPENMETHOD_UNLESS_MRDOCS(detail::) IsPolymorphic && std::is_assignable_v>> virtual_ptr& operator=(Other& other) { @@ -1026,7 +1027,7 @@ class virtual_ptr { template< class Other, typename = std::enable_if_t< - BOOST_OPENMETHOD_DETAIL_UNLESS_MRDOCS + BOOST_OPENMETHOD_UNLESS_MRDOCS(detail::) IsPolymorphic && std::is_assignable_v>> virtual_ptr& operator=(Other* other) { @@ -1161,8 +1162,8 @@ class virtual_ptr { template class virtual_ptr< SmartPtr, Registry, - std::enable_if_t< - BOOST_OPENMETHOD_DETAIL_UNLESS_MRDOCS IsSmartPtr>> { + std::enable_if_t>> { #ifndef __MRDOCS__ template @@ -1367,7 +1368,7 @@ class virtual_ptr< template< class Other, typename = std::enable_if_t< - BOOST_OPENMETHOD_DETAIL_UNLESS_MRDOCS + BOOST_OPENMETHOD_UNLESS_MRDOCS(detail::) SameSmartPtr && std::is_constructible_v>> virtual_ptr(const virtual_ptr& other) @@ -1400,7 +1401,7 @@ class virtual_ptr< template< class Other, typename = std::enable_if_t< - BOOST_OPENMETHOD_DETAIL_UNLESS_MRDOCS + BOOST_OPENMETHOD_UNLESS_MRDOCS(detail::) SameSmartPtr && std::is_constructible_v>> virtual_ptr(virtual_ptr&& other) @@ -1442,12 +1443,11 @@ class virtual_ptr< template< class Other, typename = std::enable_if_t< - BOOST_OPENMETHOD_DETAIL_UNLESS_MRDOCS + BOOST_OPENMETHOD_UNLESS_MRDOCS(detail::) SameSmartPtr && std::is_assignable_v>, - typename = std::enable_if_t< - BOOST_OPENMETHOD_DETAIL_UNLESS_MRDOCS - IsPolymorphic>> + typename = std::enable_if_t>> virtual_ptr& operator=(const Other& other) { obj = other; vp = detail::box_vptr( @@ -1479,12 +1479,11 @@ class virtual_ptr< template< class Other, typename = std::enable_if_t< - BOOST_OPENMETHOD_DETAIL_UNLESS_MRDOCS + BOOST_OPENMETHOD_UNLESS_MRDOCS(detail::) SameSmartPtr && std::is_assignable_v>, - typename = std::enable_if_t< - BOOST_OPENMETHOD_DETAIL_UNLESS_MRDOCS - IsPolymorphic>> + typename = std::enable_if_t>> virtual_ptr& operator=(Other&& other) { vp = detail::box_vptr( other ? detail::acquire_vptr(*other) : detail::null_vptr); @@ -1510,7 +1509,7 @@ class virtual_ptr< template< class Other, typename = std::enable_if_t< - BOOST_OPENMETHOD_DETAIL_UNLESS_MRDOCS + BOOST_OPENMETHOD_UNLESS_MRDOCS(detail::) SameSmartPtr && std::is_assignable_v>> virtual_ptr& operator=(virtual_ptr& other) { @@ -1539,7 +1538,7 @@ class virtual_ptr< template< class Other, typename = std::enable_if_t< - BOOST_OPENMETHOD_DETAIL_UNLESS_MRDOCS + BOOST_OPENMETHOD_UNLESS_MRDOCS(detail::) SameSmartPtr && std::is_assignable_v>> virtual_ptr& operator=(const virtual_ptr& other) { @@ -1574,7 +1573,7 @@ class virtual_ptr< template< class Other, typename = std::enable_if_t< - BOOST_OPENMETHOD_DETAIL_UNLESS_MRDOCS + BOOST_OPENMETHOD_UNLESS_MRDOCS(detail::) SameSmartPtr && std::is_assignable_v>> virtual_ptr& operator=(virtual_ptr&& other) { @@ -2100,7 +2099,7 @@ class method //! @li @ref ambiguous_call: More than one overrider is applicable, and //! none is more specialized than all the others. //! - auto operator()(typename BOOST_OPENMETHOD_DETAIL_UNLESS_MRDOCS + auto operator()(typename BOOST_OPENMETHOD_UNLESS_MRDOCS(detail::) StripVirtualDecorator::type... args) const -> ReturnType; @@ -2375,7 +2374,7 @@ template< typename Id, typename... Parameters, typename ReturnType, class Registry> BOOST_FORCEINLINE auto method::operator()( - typename BOOST_OPENMETHOD_DETAIL_UNLESS_MRDOCS + typename BOOST_OPENMETHOD_UNLESS_MRDOCS(detail::) StripVirtualDecorator::type... args) const -> ReturnType { using namespace detail; auto pf = resolve(args...); diff --git a/include/boost/openmethod/interop/virtual_any.hpp b/include/boost/openmethod/interop/virtual_any.hpp index 6b79a3f2..11bdf57c 100644 --- a/include/boost/openmethod/interop/virtual_any.hpp +++ b/include/boost/openmethod/interop/virtual_any.hpp @@ -180,7 +180,7 @@ class virtual_any { template< typename T, typename = std::enable_if_t< - BOOST_OPENMETHOD_DETAIL_UNLESS_MRDOCS + BOOST_OPENMETHOD_UNLESS_MRDOCS(detail::) IsVirtualAny> == false>, typename = std::enable_if_t< !std::is_same_v, Any> && @@ -260,7 +260,7 @@ class virtual_any { template< typename T, typename = std::enable_if_t< - BOOST_OPENMETHOD_DETAIL_UNLESS_MRDOCS + BOOST_OPENMETHOD_UNLESS_MRDOCS(detail::) IsVirtualAny> == false>, typename = std::enable_if_t< !std::is_same_v, Any> && @@ -502,7 +502,7 @@ struct virtual_traits&&, Registry> { template class virtual_ptr< Class, Registry, - std::enable_if_t>>> { static_assert( detail::false_t, From 009a95e83e5155627125c2e970918f76103cc147 Mon Sep 17 00:00:00 2001 From: Jean-Louis Leroy Date: Sun, 23 Aug 2026 10:51:30 -0400 Subject: [PATCH 3/4] core: declare the smart-pointer converting constructors once The three constructors taking a smart pointer to a derived class carried two copies of their template parameter list: an unqualified one under `#ifdef __MRDOCS__`, for the reference, and the real `detail::`-qualified one. Only MrDocs ever compiles the first, so the two could drift apart without anything failing, and the reference would document a constraint the library does not have. The rules recorded in the previous commits cover this case, so state each constraint once, with SameSmartPtr and IsPolymorphic in a defaulted template parameter each. The rendered requires-clause is byte-for-byte what the duplicated declarations produced. The `#ifdef __MRDOCS__` blocks that remain remove declarations from the reference - friends, deleted overloads, the VirtualTraits blueprint - rather than restate them. Co-Authored-By: Claude Opus 5 --- CLAUDE.md | 6 ++++ include/boost/openmethod/core.hpp | 46 +++++++++---------------------- 2 files changed, 19 insertions(+), 33 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index 7c298a51..02026d76 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -311,6 +311,12 @@ Two rules keep the reference clean; the long-form version lives next to the macr and substitution short-circuits at the first failure, so a dependent type in a later condition (`typename Other::element_type`) is only formed once the earlier ones pass. +Between them these cover every constraint in the library, so **do not declare a member twice**, an +unqualified copy under `#ifdef __MRDOCS__` beside the real one. Only MrDocs ever compiles that +copy, so the two drift apart silently and the reference then documents a constraint the library +does not have. The `#ifdef __MRDOCS__` blocks that remain remove declarations from the reference +(friends, deleted overloads, the `VirtualTraits` blueprint) rather than restate them. + Only expressions are affected: types are printed from the AST, so the macro may appear anywhere in one (`method::operator()` takes `typename BOOST_OPENMETHOD_UNLESS_MRDOCS(detail::) StripVirtualDecorator::type...` and diff --git a/include/boost/openmethod/core.hpp b/include/boost/openmethod/core.hpp index fa7746cd..fac88504 100644 --- a/include/boost/openmethod/core.hpp +++ b/include/boost/openmethod/core.hpp @@ -1227,7 +1227,7 @@ class virtual_ptr< detail::box_vptr(detail::null_vptr))), obj(std::move(other.obj)) { } -#ifdef __MRDOCS__ + //! Construct from a (const) smart pointer to a derived class //! //! Set the object pointer with a copy of `other`. Set the v-table pointer @@ -1252,18 +1252,12 @@ class virtual_ptr< //! @li @c SmartPtr must be constructible from @c const @c Other&. template< class Other, + typename = std::enable_if_t>, typename = std::enable_if_t< - SameSmartPtr && - IsPolymorphic && - std::is_constructible_v>> -#else - template< - class Other, - typename = std::enable_if_t< - detail::SameSmartPtr && - detail::IsPolymorphic && + BOOST_OPENMETHOD_UNLESS_MRDOCS(detail::) + IsPolymorphic && std::is_constructible_v>> -#endif virtual_ptr(const Other& other) : vp(detail::box_vptr( other ? detail::acquire_vptr(*other) @@ -1271,7 +1265,6 @@ class virtual_ptr< obj(other) { } -#if __MRDOCS__ //! Construct from a smart pointer to a derived class //! //! Copy object pointer from `other` to `this`. Set the v-table pointer @@ -1288,18 +1281,12 @@ class virtual_ptr< //! @li @c SmartPtr must be constructible from @c Other&. template< class Other, + typename = std::enable_if_t>, typename = std::enable_if_t< - SameSmartPtr && - IsPolymorphic && - std::is_constructible_v>> -#else - template< - class Other, - typename = std::enable_if_t< - detail::SameSmartPtr && - detail::IsPolymorphic && + BOOST_OPENMETHOD_UNLESS_MRDOCS(detail::) + IsPolymorphic && std::is_constructible_v>> -#endif virtual_ptr(Other& other) : vp(detail::box_vptr( other ? detail::acquire_vptr(*other) @@ -1307,7 +1294,6 @@ class virtual_ptr< obj(other) { } -#ifdef __MRDOCS__ //! Move-construct from a smart pointer to a derived class //! //! Move object pointer from `other` to `this`. Set the v-table pointer @@ -1331,18 +1317,12 @@ class virtual_ptr< //! @li @c SmartPtr must be constructible from @c Other&&. template< class Other, + typename = std::enable_if_t>, typename = std::enable_if_t< - SameSmartPtr && - IsPolymorphic && - std::is_constructible_v>> -#else - template< - class Other, - typename = std::enable_if_t< - detail::SameSmartPtr && - detail::IsPolymorphic && + BOOST_OPENMETHOD_UNLESS_MRDOCS(detail::) + IsPolymorphic && std::is_constructible_v>> -#endif virtual_ptr(Other&& other) : vp(detail::box_vptr( other ? detail::acquire_vptr(*other) From 752b3eb831d6c2e091cd1757337fbf736a916826 Mon Sep 17 00:00:00 2001 From: Jean-Louis Leroy Date: Sun, 23 Aug 2026 10:56:31 -0400 Subject: [PATCH 4/4] core: align the smart-pointer constructor constraints with the assignments The three converting constructors listed IsPolymorphic before the constructibility test, the two converting assignments after it. Put the exposition-only traits at the ends in all five, so the rendered clause reads SameSmartPtr && && IsPolymorphic throughout, and the five template parameter lists are spelled and formatted identically. Substitution still short-circuits on SameSmartPtr, so `typename Other::element_type` is only formed for an actual smart pointer. Co-Authored-By: Claude Opus 5 --- include/boost/openmethod/core.hpp | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/include/boost/openmethod/core.hpp b/include/boost/openmethod/core.hpp index fac88504..a7b795a1 100644 --- a/include/boost/openmethod/core.hpp +++ b/include/boost/openmethod/core.hpp @@ -1252,12 +1252,12 @@ class virtual_ptr< //! @li @c SmartPtr must be constructible from @c const @c Other&. template< class Other, - typename = std::enable_if_t>, typename = std::enable_if_t< BOOST_OPENMETHOD_UNLESS_MRDOCS(detail::) - IsPolymorphic && - std::is_constructible_v>> + SameSmartPtr && + std::is_constructible_v>, + typename = std::enable_if_t>> virtual_ptr(const Other& other) : vp(detail::box_vptr( other ? detail::acquire_vptr(*other) @@ -1281,12 +1281,12 @@ class virtual_ptr< //! @li @c SmartPtr must be constructible from @c Other&. template< class Other, - typename = std::enable_if_t>, typename = std::enable_if_t< BOOST_OPENMETHOD_UNLESS_MRDOCS(detail::) - IsPolymorphic && - std::is_constructible_v>> + SameSmartPtr && + std::is_constructible_v>, + typename = std::enable_if_t>> virtual_ptr(Other& other) : vp(detail::box_vptr( other ? detail::acquire_vptr(*other) @@ -1317,12 +1317,12 @@ class virtual_ptr< //! @li @c SmartPtr must be constructible from @c Other&&. template< class Other, - typename = std::enable_if_t>, typename = std::enable_if_t< BOOST_OPENMETHOD_UNLESS_MRDOCS(detail::) - IsPolymorphic && - std::is_constructible_v>> + SameSmartPtr && + std::is_constructible_v>, + typename = std::enable_if_t>> virtual_ptr(Other&& other) : vp(detail::box_vptr( other ? detail::acquire_vptr(*other)