From f4fc118fd754fe04e3fe5ed774ad8110a9e399e8 Mon Sep 17 00:00:00 2001 From: Jean-Louis Leroy Date: Tue, 15 Sep 2026 20:52:09 -0400 Subject: [PATCH] example: register the shared classes in both modules, as Windows requires The implicit_linking example has never run on Windows. Its library registers Animal, Herbivore, Cow, Carnivore and Wolf; the program creates a Wolf with `new` and dispatches on it, and MSVC aborts: unknown class struct Wolf Windows keeps one `type_info` object per module, so the program's `Wolf` and the library's `Wolf` are two different type ids, and only the library's was ever registered. ELF and Mach-O merge the two, which is why it passes everywhere else. `use_classes` already documents the rule - "a class must be registered in as many translation units as necessary ... The only such case known to the author is when using Windows DLLs" - and the example was the one place that did not follow it. The program now registers the library's classes as well. On ELF that is redundant and harmless; on Windows it is what makes the example work. The page says why, next to the example. Verified with MSVC v18 (Ninja, Release): the test fails on develop and at 7eb56f5, the commit that introduced the example, and passes with this change. Nothing in CI caught it because the doc examples are built only when this project is configured as the root with tests enabled, which no CI job does - the Boost.CI jobs build it inside the super-project, and b2's test/Jamfile globs test_*.cpp and compile_fail_*.cpp only. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01RQG6CbE4o2agseE7bDVzHS --- .../shared_libs/implicit_linking/main.cpp | 1 + doc/modules/ROOT/pages/shared_libraries.adoc | 15 ++++++++++++++- 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/doc/modules/ROOT/examples/shared_libs/implicit_linking/main.cpp b/doc/modules/ROOT/examples/shared_libs/implicit_linking/main.cpp index c872dbe8..1f844b81 100644 --- a/doc/modules/ROOT/examples/shared_libs/implicit_linking/main.cpp +++ b/doc/modules/ROOT/examples/shared_libs/implicit_linking/main.cpp @@ -16,6 +16,7 @@ using namespace boost::openmethod::aliases; struct Tiger : Carnivore {}; BOOST_OPENMETHOD_CLASSES(Tiger, Carnivore); +BOOST_OPENMETHOD_CLASSES(Animal, Herbivore, Cow, Carnivore, Wolf); BOOST_OPENMETHOD_OVERRIDE( meet, (virtual_ptr a, virtual_ptr b), std::string) { diff --git a/doc/modules/ROOT/pages/shared_libraries.adoc b/doc/modules/ROOT/pages/shared_libraries.adoc index 716a6fd2..81ff0008 100644 --- a/doc/modules/ROOT/pages/shared_libraries.adoc +++ b/doc/modules/ROOT/pages/shared_libraries.adoc @@ -119,7 +119,8 @@ include::{shared}/implicit_linking/extensions.cpp[tag=content] ---- The program extends it. It adds a class the library has never heard of, two -overriders that specialise `meet`, and imports the state through the header: +overriders that specialise `meet`, and imports the state through the header. It +also registers the library's classes a second time - see below: [source,c++] ---- @@ -131,6 +132,18 @@ the library's `Animal, Animal` overrider, so control crosses the module boundary in the middle of a single dispatch, and `cow meets wolf` prints `do not greet, run`. +The program registers `Animal`, `Herbivore`, `Cow`, `Carnivore` and `Wolf`, +although the library has registered them already. That is the rule +cpp:use_classes[] states: a class must be registered in as many translation +units as it has type ids. On ELF and Mach-O it has one - the dynamic linker +merges the `type_info` objects, and the second registration is redundant but +harmless. Windows keeps one `type_info` per module, so the program's `Wolf` and +the library's `Wolf` are two different type ids, and the program creates its +`Wolf` with `new`. Without the second registration the program's id is unknown +to the registry, and the first dispatch on it fails with `unknown class Wolf`. +Registering in both modules is what makes the example portable, and costs +nothing where it is not needed. + ## Dynamic Linking By "dynamic linking", we mean a program loading a shared library after it has