Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 47 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -288,6 +288,40 @@ 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/<page>.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_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
`BOOST_OPENMETHOD_UNLESS_MRDOCS(detail::) Trait<T> == false`, never `!... Trait<T>`.
- **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.

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<Parameters>::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
Expand Down Expand Up @@ -379,6 +413,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
Expand Down
1 change: 1 addition & 0 deletions doc/mrdocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ include-macros:
- 'BOOST_OPENMETHOD*'
exclude-macros:
- 'BOOST_OPENMETHOD_DETAIL_*'
- 'BOOST_OPENMETHOD_*UNLESS_MRDOCS'
- 'BOOST_OPENMETHOD_GENSYM'
- 'BOOST_OPENMETHOD_GUIDE'

Expand Down
123 changes: 64 additions & 59 deletions include/boost/openmethod/core.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -66,14 +66,39 @@
//! 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 - 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_UNLESS_MRDOCS(detail::)` must be the first thing in the
// condition. Spell a negation as `Trait<T> == false`, not `!Trait<T>`.
// - 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
#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 {
Expand Down Expand Up @@ -854,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<Other, Registry> &&
std::is_constructible_v<Class*, Other*>>>
virtual_ptr(Other& other)
Expand Down Expand Up @@ -893,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<Class, Registry> &&
std::is_constructible_v<Class*, Other*>>>
virtual_ptr(Other* other)
Expand Down Expand Up @@ -964,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<Class, Registry> &&
std::is_assignable_v<Class*&, Other*>>>
virtual_ptr& operator=(Other& other) {
Expand Down Expand Up @@ -1002,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<Class, Registry> &&
std::is_assignable_v<Class*&, Other*>>>
virtual_ptr& operator=(Other* other) {
Expand Down Expand Up @@ -1137,8 +1162,8 @@ class virtual_ptr {
template<class SmartPtr, class Registry>
class virtual_ptr<
SmartPtr, Registry,
std::enable_if_t<
BOOST_OPENMETHOD_DETAIL_UNLESS_MRDOCS IsSmartPtr<SmartPtr, Registry>>> {
std::enable_if_t<BOOST_OPENMETHOD_UNLESS_MRDOCS(detail::)
IsSmartPtr<SmartPtr, Registry>>> {

#ifndef __MRDOCS__
template<class, class, typename>
Expand Down Expand Up @@ -1202,7 +1227,7 @@ class virtual_ptr<
detail::box_vptr<use_indirect_vptrs>(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
Expand All @@ -1228,25 +1253,18 @@ class virtual_ptr<
template<
class Other,
typename = std::enable_if_t<
SameSmartPtr<SmartPtr, Other, Registry> &&
IsPolymorphic<typename Other::element_type, Registry> &&
std::is_constructible_v<SmartPtr, const Other&>>>
#else
template<
class Other,
typename = std::enable_if_t<
detail::SameSmartPtr<SmartPtr, Other, Registry> &&
detail::IsPolymorphic<typename Other::element_type, Registry> &&
std::is_constructible_v<SmartPtr, const Other&>>>
#endif
BOOST_OPENMETHOD_UNLESS_MRDOCS(detail::)
SameSmartPtr<SmartPtr, Other, Registry> &&
std::is_constructible_v<SmartPtr, const Other&>>,
typename = std::enable_if_t<BOOST_OPENMETHOD_UNLESS_MRDOCS(
detail::) IsPolymorphic<typename Other::element_type, Registry>>>
virtual_ptr(const Other& other)
: vp(detail::box_vptr<use_indirect_vptrs>(
other ? detail::acquire_vptr<Registry>(*other)
: detail::null_vptr)),
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
Expand All @@ -1264,25 +1282,18 @@ class virtual_ptr<
template<
class Other,
typename = std::enable_if_t<
SameSmartPtr<SmartPtr, Other, Registry> &&
IsPolymorphic<typename Other::element_type, Registry> &&
std::is_constructible_v<SmartPtr, Other&>>>
#else
template<
class Other,
typename = std::enable_if_t<
detail::SameSmartPtr<SmartPtr, Other, Registry> &&
detail::IsPolymorphic<typename Other::element_type, Registry> &&
std::is_constructible_v<SmartPtr, Other&>>>
#endif
BOOST_OPENMETHOD_UNLESS_MRDOCS(detail::)
SameSmartPtr<SmartPtr, Other, Registry> &&
std::is_constructible_v<SmartPtr, Other&>>,
typename = std::enable_if_t<BOOST_OPENMETHOD_UNLESS_MRDOCS(
detail::) IsPolymorphic<typename Other::element_type, Registry>>>
virtual_ptr(Other& other)
: vp(detail::box_vptr<use_indirect_vptrs>(
other ? detail::acquire_vptr<Registry>(*other)
: detail::null_vptr)),
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
Expand All @@ -1307,17 +1318,11 @@ class virtual_ptr<
template<
class Other,
typename = std::enable_if_t<
SameSmartPtr<SmartPtr, Other, Registry> &&
IsPolymorphic<typename Other::element_type, Registry> &&
std::is_constructible_v<SmartPtr, Other&&>>>
#else
template<
class Other,
typename = std::enable_if_t<
detail::SameSmartPtr<SmartPtr, Other, Registry> &&
detail::IsPolymorphic<typename Other::element_type, Registry> &&
std::is_constructible_v<SmartPtr, Other&&>>>
#endif
BOOST_OPENMETHOD_UNLESS_MRDOCS(detail::)
SameSmartPtr<SmartPtr, Other, Registry> &&
std::is_constructible_v<SmartPtr, Other&&>>,
typename = std::enable_if_t<BOOST_OPENMETHOD_UNLESS_MRDOCS(
detail::) IsPolymorphic<typename Other::element_type, Registry>>>
virtual_ptr(Other&& other)
: vp(detail::box_vptr<use_indirect_vptrs>(
other ? detail::acquire_vptr<Registry>(*other)
Expand All @@ -1343,7 +1348,7 @@ class virtual_ptr<
template<
class Other,
typename = std::enable_if_t<
BOOST_OPENMETHOD_DETAIL_UNLESS_MRDOCS
BOOST_OPENMETHOD_UNLESS_MRDOCS(detail::)
SameSmartPtr<SmartPtr, Other, Registry> &&
std::is_constructible_v<SmartPtr, const Other&>>>
virtual_ptr(const virtual_ptr<Other, Registry>& other)
Expand Down Expand Up @@ -1376,7 +1381,7 @@ class virtual_ptr<
template<
class Other,
typename = std::enable_if_t<
BOOST_OPENMETHOD_DETAIL_UNLESS_MRDOCS
BOOST_OPENMETHOD_UNLESS_MRDOCS(detail::)
SameSmartPtr<SmartPtr, Other, Registry> &&
std::is_constructible_v<SmartPtr, Other&&>>>
virtual_ptr(virtual_ptr<Other, Registry>&& other)
Expand Down Expand Up @@ -1418,11 +1423,11 @@ class virtual_ptr<
template<
class Other,
typename = std::enable_if_t<
BOOST_OPENMETHOD_DETAIL_UNLESS_MRDOCS
BOOST_OPENMETHOD_UNLESS_MRDOCS(detail::)
SameSmartPtr<SmartPtr, Other, Registry> &&
std::is_assignable_v<SmartPtr, const Other&> &&
BOOST_OPENMETHOD_DETAIL_UNLESS_MRDOCS
IsPolymorphic<typename Other::element_type, Registry>>>
std::is_assignable_v<SmartPtr, const Other&>>,
typename = std::enable_if_t<BOOST_OPENMETHOD_UNLESS_MRDOCS(
detail::) IsPolymorphic<typename Other::element_type, Registry>>>
virtual_ptr& operator=(const Other& other) {
obj = other;
vp = detail::box_vptr<use_indirect_vptrs>(
Expand Down Expand Up @@ -1454,11 +1459,11 @@ class virtual_ptr<
template<
class Other,
typename = std::enable_if_t<
BOOST_OPENMETHOD_DETAIL_UNLESS_MRDOCS
BOOST_OPENMETHOD_UNLESS_MRDOCS(detail::)
SameSmartPtr<SmartPtr, Other, Registry> &&
std::is_assignable_v<SmartPtr, Other&&> &&
BOOST_OPENMETHOD_DETAIL_UNLESS_MRDOCS
IsPolymorphic<typename Other::element_type, Registry>>>
std::is_assignable_v<SmartPtr, Other&&>>,
typename = std::enable_if_t<BOOST_OPENMETHOD_UNLESS_MRDOCS(
detail::) IsPolymorphic<typename Other::element_type, Registry>>>
virtual_ptr& operator=(Other&& other) {
vp = detail::box_vptr<use_indirect_vptrs>(
other ? detail::acquire_vptr<Registry>(*other) : detail::null_vptr);
Expand All @@ -1484,7 +1489,7 @@ class virtual_ptr<
template<
class Other,
typename = std::enable_if_t<
BOOST_OPENMETHOD_DETAIL_UNLESS_MRDOCS
BOOST_OPENMETHOD_UNLESS_MRDOCS(detail::)
SameSmartPtr<SmartPtr, Other, Registry> &&
std::is_assignable_v<SmartPtr, Other&>>>
virtual_ptr& operator=(virtual_ptr<Other, Registry>& other) {
Expand Down Expand Up @@ -1513,7 +1518,7 @@ class virtual_ptr<
template<
class Other,
typename = std::enable_if_t<
BOOST_OPENMETHOD_DETAIL_UNLESS_MRDOCS
BOOST_OPENMETHOD_UNLESS_MRDOCS(detail::)
SameSmartPtr<SmartPtr, Other, Registry> &&
std::is_assignable_v<SmartPtr, const Other&>>>
virtual_ptr& operator=(const virtual_ptr<Other, Registry>& other) {
Expand Down Expand Up @@ -1548,7 +1553,7 @@ class virtual_ptr<
template<
class Other,
typename = std::enable_if_t<
BOOST_OPENMETHOD_DETAIL_UNLESS_MRDOCS
BOOST_OPENMETHOD_UNLESS_MRDOCS(detail::)
SameSmartPtr<SmartPtr, Other, Registry> &&
std::is_assignable_v<SmartPtr, Other&&>>>
virtual_ptr& operator=(virtual_ptr<Other, Registry>&& other) {
Expand Down Expand Up @@ -2074,7 +2079,7 @@ class method<Id, ReturnType(Parameters...), Registry>
//! @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<Parameters>::type... args) const
-> ReturnType;

Expand Down Expand Up @@ -2349,7 +2354,7 @@ template<
typename Id, typename... Parameters, typename ReturnType, class Registry>
BOOST_FORCEINLINE auto
method<Id, ReturnType(Parameters...), Registry>::operator()(
typename BOOST_OPENMETHOD_DETAIL_UNLESS_MRDOCS
typename BOOST_OPENMETHOD_UNLESS_MRDOCS(detail::)
StripVirtualDecorator<Parameters>::type... args) const -> ReturnType {
using namespace detail;
auto pf = resolve(args...);
Expand Down
12 changes: 7 additions & 5 deletions include/boost/openmethod/interop/virtual_any.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -180,8 +180,9 @@ class virtual_any {
template<
typename T,
typename = std::enable_if_t<
!BOOST_OPENMETHOD_DETAIL_UNLESS_MRDOCS
IsVirtualAny<std::decay_t<T>> &&
BOOST_OPENMETHOD_UNLESS_MRDOCS(detail::)
IsVirtualAny<std::decay_t<T>> == false>,
typename = std::enable_if_t<
!std::is_same_v<std::decay_t<T>, Any> &&
std::is_constructible_v<Any, T&&>>>
virtual_any(T&& value)
Expand Down Expand Up @@ -259,8 +260,9 @@ class virtual_any {
template<
typename T,
typename = std::enable_if_t<
!BOOST_OPENMETHOD_DETAIL_UNLESS_MRDOCS
IsVirtualAny<std::decay_t<T>> &&
BOOST_OPENMETHOD_UNLESS_MRDOCS(detail::)
IsVirtualAny<std::decay_t<T>> == false>,
typename = std::enable_if_t<
!std::is_same_v<std::decay_t<T>, Any> &&
std::is_constructible_v<Any, T&&>>>
auto operator=(T&& value) -> virtual_any& {
Expand Down Expand Up @@ -500,7 +502,7 @@ struct virtual_traits<virtual_any<Any, Registry>&&, Registry> {
template<class Class, class Registry>
class virtual_ptr<
Class, Registry,
std::enable_if_t<BOOST_OPENMETHOD_DETAIL_UNLESS_MRDOCS
std::enable_if_t<BOOST_OPENMETHOD_UNLESS_MRDOCS(detail::)
IsVirtualAny<std::remove_cv_t<Class>>>> {
static_assert(
detail::false_t<Class>,
Expand Down
Loading