Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
83 commits
Select commit Hold shift + click to select a range
f9aacc6
BOOST_OPENMETHOD_DETAIL_HAS_STATIC_FN
jll63 Jan 26, 2026
1eb22d8
inter-operate with 'any'
jll63 Feb 28, 2026
f0aafd3
inter-operate with 'any'
jll63 Mar 7, 2026
4b00b38
Merge branch 'develop' into feature/any
jll63 Jul 28, 2026
3a1fbc1
Merge branch 'develop' into feature/any
jll63 Jul 30, 2026
4914053
support std::any by mutable and xvalue reference
jll63 Jul 31, 2026
810a981
support boost::any
jll63 Jul 31, 2026
f086985
type_vptr -> vptr
jll63 Aug 2, 2026
7ecd96c
dynamic_vptr -> vptr
jll63 Aug 8, 2026
65cd1b5
fix leftovers of the dynamic_vptr -> vptr rename
jll63 Aug 8, 2026
57b6c32
add virtual_any
jll63 Aug 8, 2026
bfe8537
Merge branch 'feature/any' into feature/type_erasure
jll63 Aug 8, 2026
475b05b
probe virtual_traits::vptr with the argument type, not type_id
jll63 Aug 8, 2026
a117a28
any traits: pass the any through to catch-all overriders
jll63 Aug 8, 2026
a6d56d2
use_*_any_types: do not register a trailing registry as a class
jll63 Aug 8, 2026
495abc2
support boost::type_erasure (#21)
jll63 Aug 8, 2026
78143c8
fix infinite recursion in virtual_any's vptr friend on MSVC
jll63 Aug 8, 2026
01c863d
Merge branch 'feature/mrdocs-macros' into feature/any
jll63 Aug 8, 2026
9dcba28
doc: an Interoperation page, and reference examples for the `any`s
jll63 Aug 8, 2026
8bcb1a3
alias use_std_any_types and use_boost_any_types
jll63 Aug 8, 2026
50d5ec9
support catch-all overriders on plain `any` virtual parameters
jll63 Aug 8, 2026
ff11651
doc: lead the Interoperation page with `virtual_`, not `virtual_any`
jll63 Aug 8, 2026
acdd8e0
test: make the virtual_any-by-value compile-fail test actually fail o…
jll63 Aug 8, 2026
e946dd2
doc: fix the broken `any` header links on the Headers page
jll63 Aug 8, 2026
f1d3cd8
doc: narrow the Interoperation page to `any`
jll63 Aug 8, 2026
0474536
Merge branch 'feature/any' into feature/type_erasure
jll63 Aug 8, 2026
9663974
doc: an Interoperation with Boost.TypeErasure page, and reference exa…
jll63 Aug 8, 2026
15ddee5
doc: fix the broken type_erasure header link on the Headers page
jll63 Aug 8, 2026
3bedb21
doc: document `vptr` in VirtualTraits and VptrFn
jll63 Aug 9, 2026
608f1b6
Merge branch 'feature/mrdocs-macros' into feature/any
jll63 Aug 9, 2026
a735c45
doc: make the `any` header links follow the deployment too
jll63 Aug 9, 2026
65904e3
doc: inline the final_virtual_ptr example instead of linking to it
jll63 Aug 9, 2026
1fc8ea3
doc: hide the deleted `final_virtual_ptr` overloads from the reference
jll63 Aug 9, 2026
597f03b
add the openmethod_vptr concept (#21)
jll63 Aug 9, 2026
6c6cb44
doc: hide the deleted final_virtual_ptr overloads from the reference
jll63 Aug 9, 2026
8737e86
doc: tighten the boost_type_erasure entry on the Headers page
jll63 Aug 9, 2026
437c666
reject virtual_ptr over classes with a boost_openmethod_vptr overload
jll63 Aug 9, 2026
4704d43
Merge branch 'feature/any' into feature/type_erasure
jll63 Aug 9, 2026
e3a2537
doc: openmethod_vptr anys are not wrapped in virtual_any
jll63 Aug 9, 2026
b1d78b8
doc: make the type_erasure header link follow the deployment
jll63 Aug 9, 2026
dbdd951
doc: fix the plural any's rendering with literal backslashes and back…
jll63 Aug 9, 2026
516606c
doc: trim the any-interop entries on the Headers page
jll63 Aug 9, 2026
3e98203
Merge branch 'feature/any' into feature/type_erasure
jll63 Aug 9, 2026
c42b1bd
doc: trim the type_erasure entry on the Headers page to match
jll63 Aug 9, 2026
9da7fd9
doc: give the example Dog a ctor so make_any_virtual forwards its arg…
jll63 Aug 9, 2026
b7706b3
add a make_any_virtual overload that deduces the type of the value
jll63 Aug 9, 2026
4b0c30a
Revert the value-deducing make_any_virtual overload and the example ctor
jll63 Aug 9, 2026
69b5d43
remove the make_*_virtual factories
jll63 Aug 9, 2026
df3c0b7
add virtual_any_ref, a non-owning counterpart of virtual_any
jll63 Aug 9, 2026
1109d69
Merge branch 'feature/any' into feature/type_erasure
jll63 Aug 9, 2026
c9807ab
doc: stop advertising virtual_any for type_erasure anys
jll63 Aug 9, 2026
cb44af6
test: exercise virtual_any_ref with boost::any
jll63 Aug 10, 2026
e0797c1
Merge branch 'feature/any' into feature/type_erasure
jll63 Aug 10, 2026
8b21152
Merge branch 'feature/mrdocs-macros' into feature/any
jll63 Aug 10, 2026
9221d15
doc: point the `any` header links at GitHub too
jll63 Aug 10, 2026
0ac37a3
Merge branch 'feature/any' into feature/type_erasure
jll63 Aug 10, 2026
32e064c
doc: point the type_erasure header link at GitHub too
jll63 Aug 10, 2026
a131c7f
fix MSVC: constrain the type_erasure boost_openmethod_vptr friend
jll63 Aug 10, 2026
47c80a8
test: make the type_erasure by-value guard fire on every compiler
jll63 Aug 10, 2026
338108e
Merge branch 'feature/any' into feature/type_erasure
jll63 Aug 10, 2026
b45feb4
Merge branch 'feature/mrdocs-macros' into feature/any
jll63 Aug 10, 2026
f3440db
ci: split the macOS 10.15 Drone stages by C++ standard
jll63 Aug 12, 2026
1788457
doc, test: cover virtual_any with type_erasure::any
jll63 Aug 15, 2026
d579766
doc: give the acknowledgment its own section on both interop pages
jll63 Aug 15, 2026
408fe46
doc: credit the interop design in the headers, matching the pages
jll63 Aug 15, 2026
235344f
doc: touch up `any` doc
jll63 Aug 15, 2026
926574c
Merge branch 'feature/mrdocs-macros' into feature/any
jll63 Aug 15, 2026
8183c83
Merge branch 'feature/any' into feature/type_erasure
jll63 Aug 15, 2026
8f2384b
doc: touch up TypeErasure doc
jll63 Aug 15, 2026
1f8ed77
doc: add the virtual_traits::vptr step to the dispatch lists
jll63 Aug 15, 2026
5c033b5
Merge branch 'feature/any' into feature/type_erasure
jll63 Aug 15, 2026
161fdf8
doc: fix an article
jll63 Aug 15, 2026
eb92a95
Merge branch 'develop' into feature/any
jll63 Aug 16, 2026
a01efed
doc: more `any` touch-up
jll63 Aug 16, 2026
57116d5
Merge branch 'feature/any' into feature/type_erasure
jll63 Aug 16, 2026
156b343
doc: drop "borrow" from the type_erasure interop prose
jll63 Aug 16, 2026
49bfbf5
require std_rtti in the any interop
jll63 Aug 16, 2026
f2d7f23
Merge branch 'feature/any' into feature/type_erasure
jll63 Aug 16, 2026
8027dd9
require std_rtti in the type_erasure interop
jll63 Aug 16, 2026
6730af4
doc: state the any interop's rtti requirement as what, not how
jll63 Aug 16, 2026
eaf01f5
Merge branch 'feature/any' into feature/type_erasure
jll63 Aug 16, 2026
f298c6f
doc: state the type_erasure rtti requirement as what, not how
jll63 Aug 16, 2026
20a2a96
doc, test: openmethod_vptr lifts the std_rtti requirement
jll63 Aug 16, 2026
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
21 changes: 17 additions & 4 deletions .drone.jsonnet
Original file line number Diff line number Diff line change
Expand Up @@ -297,14 +297,27 @@ local windows_pipeline(name, image, environment, arch = "amd64") =
"clang-18",
),

# One C++ standard per stage: the Catalina runner is the slowest in the
# fleet, and 17,2a in a single stage was killed at Drone's 60-minute step
# timeout, halfway into the second standard.
macos_pipeline(
"MacOS 10.15 Xcode 12.2 UBSAN",
{ TOOLSET: 'clang', COMPILER: 'clang++', CXXSTD: '17,2a' } + ubsan,
"MacOS 10.15 Xcode 12.2 UBSAN C++17",
{ TOOLSET: 'clang', COMPILER: 'clang++', CXXSTD: '17' } + ubsan,
),

macos_pipeline(
"MacOS 10.15 Xcode 12.2 ASAN",
{ TOOLSET: 'clang', COMPILER: 'clang++', CXXSTD: '17,2a' } + asan,
"MacOS 10.15 Xcode 12.2 UBSAN C++2a",
{ TOOLSET: 'clang', COMPILER: 'clang++', CXXSTD: '2a' } + ubsan,
),

macos_pipeline(
"MacOS 10.15 Xcode 12.2 ASAN C++17",
{ TOOLSET: 'clang', COMPILER: 'clang++', CXXSTD: '17' } + asan,
),

macos_pipeline(
"MacOS 10.15 Xcode 12.2 ASAN C++2a",
{ TOOLSET: 'clang', COMPILER: 'clang++', CXXSTD: '2a' } + asan,
),

macos_pipeline(
Expand Down
33 changes: 33 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,39 @@ Tests require these C++17 features (checked by Boost.Build):
- structured bindings
- `<charconv>`, `<string_view>`, `<variant>` headers

### Documentation (AsciiDoc)

Prose lives in `doc/modules/ROOT/pages/*.adoc`; explanations belong there, not in comments inside
the example sources under `doc/modules/ROOT/examples/`, which are pulled into the rendered page
verbatim through `include::example$file.cpp[tag=content]`. Pages hard-wrap at ~79 columns and use
`cpp:name[]` for API names that have a reference page.

**Render the docs; do not just eyeball the `.adoc`.** `doc/build_antora.sh` (~2 min, writes the
gitignored `doc/html/`) is the only way to catch markup that is silently mis-parsed — asciidoctor
emits no warning for it.

**The backtick-apostrophe trap**: never write a possessive right after a code span. Asciidoctor
parses ``` `any`'s ``` as `` ` `` + `any` + the **`` `' `` curly-apostrophe shorthand**, which
consumes the *closing* backtick; the opening one is then left unmatched and pairs with the next
backtick in the same paragraph. Two things break at once — a literal `` ` `` appears in the output,
and the following code span loses its `<code>` formatting:

```
source: is part of the `any`'s type - whereas the `typeid_of`-based dispatch above
rendered: is part of the any's type - whereas the `typeid_of-based dispatch above
```

Reword instead: "the reference types of the `any`", "separate from that of `default_registry`".
``{apos}`` also works and matches the house style (`shared_libraries.adoc` uses ``{empty}`` for
plurals: ``` `virtual_ptr`{empty}s ```), but rewording is safer and reads better. Before building:

```bash
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.

## Common Development Patterns

### Working with Shared Libraries / DLL Support
Expand Down
2 changes: 2 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,8 @@ set(

if (BOOST_OPENMETHOD_BUILD_TESTS OR BOOST_OPENMETHOD_MRDOCS_BUILD)
list(APPEND BOOST_OPENMETHOD_DEPENDENCIES Boost::smart_ptr)
list(APPEND BOOST_OPENMETHOD_DEPENDENCIES Boost::any)
list(APPEND BOOST_OPENMETHOD_DEPENDENCIES Boost::type_erasure)
endif()

foreach (BOOST_OPENMETHOD_DEPENDENCY ${BOOST_OPENMETHOD_DEPENDENCIES})
Expand Down
67 changes: 67 additions & 0 deletions doc/modules/ROOT/examples/type_erasure.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
// Copyright (c) 2018-2026 Jean-Louis Leroy
// Distributed under the Boost Software License, Version 1.0.
// See accompanying file LICENSE_1_0.txt
// or copy at http://www.boost.org/LICENSE_1_0.txt)

// clang-format off

// tag::content[]
#include <iostream>
#include <string>

#include <boost/mpl/vector.hpp>
#include <boost/type_erasure/any.hpp>
#include <boost/type_erasure/builtin.hpp>
#include <boost/type_erasure/is_empty.hpp>

#include <boost/openmethod.hpp>
#include <boost/openmethod/interop/boost_type_erasure.hpp>

namespace te = boost::type_erasure;
using namespace boost::openmethod;

// `relaxed` implies `typeid_<>`, which dispatch relies on.
using Concept = boost::mpl::vector<te::copy_constructible<>, te::relaxed>;
using erased = te::any<Concept>;

struct Dog {
std::string name;
};

// The owning `any`, `any<Concept>`, becomes the common base of the types
// the `any` may bind.
BOOST_OPENMETHOD_REGISTER(use_type_erasure_types<erased, Dog, std::string, int>);

BOOST_OPENMETHOD(name, (virtual_<const erased&>), std::string);

// An overrider takes the bound value...
BOOST_OPENMETHOD_OVERRIDE(name, (const Dog& dog), std::string) {
return dog.name + " the dog";
}

BOOST_OPENMETHOD_OVERRIDE(name, (const std::string& name), std::string) {
return name;
}

// ...or the `any` itself, which makes it a catch-all.
BOOST_OPENMETHOD_OVERRIDE(name, (const erased& value), std::string) {
return te::is_empty(value) ? "nothing" : "something else";
}

#include <boost/openmethod/initialize.hpp>

int main() {
initialize();

const erased spot(Dog{"Spot"});
const erased felix(std::string("Felix the cat"));
const erased answer(42);

std::cout << name(spot) << "\n"; // Spot the dog
std::cout << name(felix) << "\n"; // Felix the cat

// `int` is registered, but has no overrider of its own, so the
// catch-all applies.
std::cout << name(answer) << "\n"; // something else
}
// end::content[]
60 changes: 60 additions & 0 deletions doc/modules/ROOT/examples/type_erasure_concept.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
// Copyright (c) 2018-2026 Jean-Louis Leroy
// Distributed under the Boost Software License, Version 1.0.
// See accompanying file LICENSE_1_0.txt
// or copy at http://www.boost.org/LICENSE_1_0.txt)

// clang-format off

// tag::content[]
#include <iostream>
#include <string>

#include <boost/mpl/vector.hpp>
#include <boost/type_erasure/any.hpp>
#include <boost/type_erasure/builtin.hpp>
#include <boost/type_erasure/is_empty.hpp>

#include <boost/openmethod.hpp>
#include <boost/openmethod/interop/boost_type_erasure.hpp>

namespace te = boost::type_erasure;
using namespace boost::openmethod;

struct Dog {
std::string name;
};

// The concept must name the Concept it is part of, so the Concept is
// defined as a struct.
struct Dispatchable
: boost::mpl::vector<
te::copy_constructible<>, te::relaxed,
openmethod_vptr<Dispatchable>> {};

using erased = te::any<Dispatchable>;

// No use_type_erasure_types: binding a value to the `any` registers its
// type.

BOOST_OPENMETHOD(name, (virtual_<const erased&>), std::string);

BOOST_OPENMETHOD_OVERRIDE(name, (const Dog& dog), std::string) {
return dog.name + " the dog";
}

BOOST_OPENMETHOD_OVERRIDE(name, (const erased& value), std::string) {
return te::is_empty(value) ? "nothing" : "something else";
}

#include <boost/openmethod/initialize.hpp>

int main() {
initialize();

const erased spot(Dog{"Spot"});
const erased answer(42);

std::cout << name(spot) << "\n"; // Spot the dog
std::cout << name(answer) << "\n"; // something else
}
// end::content[]
59 changes: 59 additions & 0 deletions doc/modules/ROOT/examples/type_erasure_ref.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
// Copyright (c) 2018-2026 Jean-Louis Leroy
// Distributed under the Boost Software License, Version 1.0.
// See accompanying file LICENSE_1_0.txt
// or copy at http://www.boost.org/LICENSE_1_0.txt)

// clang-format off

// tag::content[]
#include <iostream>
#include <string>

#include <boost/mpl/vector.hpp>
#include <boost/type_erasure/any.hpp>
#include <boost/type_erasure/builtin.hpp>

#include <boost/openmethod.hpp>
#include <boost/openmethod/interop/boost_type_erasure.hpp>

namespace te = boost::type_erasure;
using namespace boost::openmethod;

using Concept = boost::mpl::vector<te::copy_constructible<>, te::relaxed>;
using erased_ref = te::any<Concept, te::_self&>;

struct Dog {
std::string name;
};

BOOST_OPENMETHOD_REGISTER(use_type_erasure_types<erased_ref, Dog, int>);

// An any reference is a cheap handle; it is passed by value.
BOOST_OPENMETHOD(poke, (virtual_<erased_ref>), std::string);

BOOST_OPENMETHOD_OVERRIDE(poke, (Dog& dog), std::string) {
dog.name += "!";
return dog.name;
}

BOOST_OPENMETHOD_OVERRIDE(poke, (int& value), std::string) {
++value;
return "poked";
}

#include <boost/openmethod/initialize.hpp>

int main() {
initialize();

Dog snoopy{"Snoopy"};
int count = 41;

// mutations reach the referents
std::cout << poke(erased_ref(snoopy)) << "\n"; // Snoopy!
std::cout << snoopy.name << "\n"; // Snoopy!

std::cout << poke(erased_ref(count)) << "\n"; // poked
std::cout << count << "\n"; // 42
}
// end::content[]
60 changes: 60 additions & 0 deletions doc/modules/ROOT/examples/type_erasure_virtual_any.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
// Copyright (c) 2018-2026 Jean-Louis Leroy
// Distributed under the Boost Software License, Version 1.0.
// See accompanying file LICENSE_1_0.txt
// or copy at http://www.boost.org/LICENSE_1_0.txt)

// clang-format off

// tag::content[]
#include <iostream>
#include <string>

#include <boost/mpl/vector.hpp>
#include <boost/type_erasure/any.hpp>
#include <boost/type_erasure/builtin.hpp>

#include <boost/openmethod.hpp>
#include <boost/openmethod/interop/boost_type_erasure.hpp>

namespace te = boost::type_erasure;
using namespace boost::openmethod;

using Concept = boost::mpl::vector<te::copy_constructible<>, te::relaxed>;
using erased = te::any<Concept>;

struct Dog {
std::string name;
};

BOOST_OPENMETHOD_REGISTER(use_type_erasure_types<erased, Dog, std::string>);

BOOST_OPENMETHOD(name, (const virtual_any<erased>&), std::string);

BOOST_OPENMETHOD_OVERRIDE(name, (const Dog& dog), std::string) {
return dog.name + " the dog";
}

BOOST_OPENMETHOD_OVERRIDE(name, (const std::string& name), std::string) {
return name;
}

#include <boost/openmethod/initialize.hpp>

int main() {
initialize();

// from a value: the v-table pointer is set statically
virtual_any<erased> rex = Dog{"Rex"};
std::cout << (rex.vptr() == default_registry::static_vptr<Dog>) << "\n"; // 1

// from an `any`: one lookup, here, and none in the calls below
erased spot_any(Dog{"Spot"});
virtual_any<erased> spot = spot_any;

std::cout << name(rex) << "\n"; // Rex the dog
std::cout << name(spot) << "\n"; // Spot the dog

spot = std::string("Felix the cat");
std::cout << name(spot) << "\n"; // Felix the cat
}
// end::content[]
63 changes: 63 additions & 0 deletions doc/modules/ROOT/examples/virtual_any.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
// Copyright (c) 2018-2026 Jean-Louis Leroy
// Distributed under the Boost Software License, Version 1.0.
// See accompanying file LICENSE_1_0.txt
// or copy at http://www.boost.org/LICENSE_1_0.txt)

// clang-format off

// tag::content[]
#include <any>
#include <iostream>
#include <string>

#include <boost/openmethod.hpp>
#include <boost/openmethod/interop/std_any.hpp>

using namespace boost::openmethod;

struct Dog {
std::string name;
};

// `std::any` becomes the common base of the types it may contain.
BOOST_OPENMETHOD_REGISTER(use_std_any_types<Dog, std::string, int, float>);

BOOST_OPENMETHOD(name, (virtual_<const std::any&>), std::string);

// An overrider takes the contained value...
BOOST_OPENMETHOD_OVERRIDE(name, (const Dog& dog), std::string) {
return dog.name + " the dog";
}

BOOST_OPENMETHOD_OVERRIDE(name, (const std::string& name), std::string) {
return name;
}

BOOST_OPENMETHOD_OVERRIDE(name, (const int& value), std::string) {
return std::to_string(value) + " the integer";
}

// ...or the `any` itself, which makes it a catch-all.
BOOST_OPENMETHOD_OVERRIDE(name, (const std::any&), std::string) {
return "something else";
}

#include <boost/openmethod/initialize.hpp>

int main() {
initialize();

std::any spot = Dog{"Spot"};
std::any felix = std::string("Felix the cat");
std::any answer = 42;
std::any pi = 3.14f;

std::cout << name(spot) << "\n"; // Spot the dog
std::cout << name(felix) << "\n"; // Felix the cat
std::cout << name(answer) << "\n"; // 42 the integer

// `float` is registered, but has no overrider of its own, so the
// catch-all applies.
std::cout << name(pi) << "\n"; // something else
}
// end::content[]
Loading
Loading