support DLLs on Windows#75
Draft
jll63 wants to merge 138 commits into
Draft
Conversation
Switch the has_initialize/has_finalize SFINAE workaround guards to BOOST_MSVC (Boost.Config's alias for _MSC_VER), matching Boost's own convention. Add a #pragma message, gated on BOOST_MSVC > 1951 (the last version confirmed to need the workaround), reminding maintainers to re-check the underlying compiler bug on any newer MSVC before removing the workaround. Silent on all currently-known-bad versions; verified it fires when the guard condition is met and stays silent on the real 19.51.36248 compiler. Also documents measured compile-time findings for detail::tuple (kept per discussion): a real but modest, size-dependent win over std::tuple (~11% faster MSVC front-end time at 40 classes in a BOOST_OPENMETHOD_CLASSES list, ~7% at 100), well below the noise floor for this library's own test suite (2-5 classes per list), and not the cause of the C1060/CI-OOM issue that prompted introducing it - that was the SFINAE probe bug fixed above. Verified: full CMake suite (105/105) on MSVC 19.51.36248, b2 test//quick on Cygwin gcc-13. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
The RESOURCE_LOCK that serialized the compile-fail tests exists to avoid the MSBuild ZERO_CHECK race, which only happens with the Visual Studio generator. Guard it on CMAKE_GENERATOR so Ninja/Makefile builds run these tests in parallel with each other. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The dynamic_loading test forced CXX_VISIBILITY_PRESET=default on its modules to
keep the shared registry-state symbol (registry_state<...>::st) exported. Under
-fvisibility=hidden (e.g. the super-project's BoostRoot.cmake) each module
implicitly instantiates its own COMDAT copy of st, which GCC internalizes to a
per-module local symbol, so cross-DSO state sharing breaks and no symbol-level
attribute alone can fix it.
Extend the existing explicit-instantiation export/import mechanism from _WIN32
to ELF: BOOST_SYMBOL_EXPORT is visibility("default") and BOOST_SYMBOL_IMPORT is
empty there, so the same EXPORT/IMPORT macros emit a single strong,
default-visibility explicit instantiation that other modules import. This makes
the state shareable under hidden visibility, so the test no longer needs to
force visibility. Behavior is unchanged on Windows and for ELF users who don't
use the macros (non-hidden builds).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Extending the explicit-instantiation import of the registry state to ELF/Mach-O made the overrider reference registry_state<...>::st as an undefined (imported) symbol instead of instantiating its own copy. The b2 Jamfile linked lib_registry into the overrider only on Windows/Cygwin; lib_method only imports the symbol too, so it cannot satisfy the reference. On ELF the undefined symbol resolves lazily at load time, but Mach-O's two-level namespace requires it to be provided by a directly linked module at link time, so the macOS build failed with "Undefined symbols: registry_state<...>::st, referenced from overrider.o". Link lib_registry into the overrider unconditionally. The CMake build already gets this transitively (lib_method links lib_registry PUBLIC). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…warning b2's scanner treats ':' as special and warned "Unescaped special character in argument" for the <define>BOOST_OPENMETHOD_DEFAULT_REGISTRY=::boost::... value. Escape each colon as \: ; the value passed to the compiler is unchanged. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Change meet to return a pair {own_word, next_word}, where next_word is
next(a, b).first when a next overrider exists, else "n/a". The dynamically
loaded Dog,Dog overrider's next now resolves — through the multi-dispatch
table — to the Animal,Animal overrider in the separately compiled method
library, exercising next across a shared-library boundary:
meet(dog, dog) -> {"wag tails", "ignore"} (Dog,Dog, with cross-module next)
meet(animal..) -> {"ignore", "n/a"} (Animal,Animal, no next)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The `method_call_meet` / `overrider_call_meet` extern "C" entry points
returned `greeting` (a std::pair<std::string,std::string>) by value.
Under clang -Werror this trips -Wreturn-type-c-linkage ("returns
user-defined type ... incompatible with C"), which broke the macOS
sanitizer CI job.
Return the result through an out-parameter instead, matching the
existing method_make_dog entry point, and update main.cpp's meet_fn
signature and call sites accordingly.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Fix -Wreturn-type-c-linkage in dynamic_loading test
…_vector Policies are initialized in policy_list order (left to right) and, since vptr_vector::initialize now reads the type_hash policy's state instead of driving its initialize(), a type_hash policy must appear before vptr_vector. This contract was undocumented and unchecked. - Document the left-to-right initialize / reverse finalize order on the free initialize()/finalize() functions, detail::initialize_policies, and the registries_and_policies reference page; fix that page's stale default_registry example (it showed vptr_vector before fast_perfect_hash). - Make registry::finalize actually iterate in reverse policy_list order (it was forward), mirroring initialize, and fix the missed .st rename (static_::st.dispatch_data / static_::st.initialized) that prevented finalize from compiling at all. - static_assert in vptr_vector::fn that, when a type_hash policy is present, it precedes vptr_vector in policy_list, so mis-ordered custom registries fail to compile instead of corrupting the vptr table at runtime. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Re-add the finalize test dropped when test_dispatch.cpp was split: initialize a registry, assert the vptr_vector state is populated, call finalize(), assert it was cleared. Adapted to the new API — the vptr vector is reached via Registry::state<policies::vptr_vector>().vptrs, and the has_finalize probe uses the portable detail::has_finalize (has_finalize_aux is MSVC-only). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Add test_registries_do_not_share_vptr_state: two distinct registries, both using vptr_vector, have separate vptr_vector state. Asserts the two state vectors are distinct objects and that finalizing one clears its vector while the other's is untouched. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
… address same_ids() never compared the registry-state address and its per-policy loop compiled to nothing (no policy defines id() anymore), so it always returned true — the test that exists to prove the registry state is a single shared symbol across modules could not fail. Collapse the vestigial id-array machinery to the one meaningful value: get_ids() now returns the state address directly (const void*), and same_ids() compares the two addresses. Verified by building/running the dll-owner default variant (passes) and a negative build forcing the comparison false (both same_ids checks then fail), confirming the assertion is now load-bearing. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
A full URL-encoded Cygwin download mirror (setup.ini + ~157 binary tarballs) and a scratch t.txt were committed to the repo root by mistake. They do not belong in the library. notes.txt is kept. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Brings in: init/finalize ordering docs + reverse finalize + type_hash-before- vptr_vector static_assert, restored finalize test, two-registry no-share test, same_ids shared-state fix, stderr stream() doc, and removal of the accidentally- committed Cygwin mirror + t.txt.
…ze_policies - vptr_vector.hpp: collapse a leftover identical if constexpr(has_indirect_vptr) block (both arms set max_index = st().vptrs.size()); extract the type_hash- before-vptr_vector static_assert into named type_hash_pos/vptr_vector_pos intermediates for readability (same compile-time computation). - initialize.hpp: add detail::finalize_policies (mirror of initialize_policies) so finalize() delegates instead of open-coding the reverse mp_for_each loop; reverse order now has one named home. - test_compiler.cpp: drop the m1/m2 named refs + (void) casts for plain (void)method<...>::fn; discard statements. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…id family) get_ids() now returns a single registry-state address, so the plural name was a misnomer. Rename the helper to registry_state_id() and the exported wrappers to method_state_id / overrider_state_id (with matching Boost.DLL symbol lookups in main.cpp). Drop the dead, unused registry_get_ids export from registry.cpp (it would collide with the helper's new name and nothing references it). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Unused, unincluded leftover with the old const void** get_ids() signature; the live helper is registry_state_id() in registry.hpp. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
test_finalize_clears_vptr_vector used ADD_METHOD(A), which binds `auto& m_A` that the test never references, tripping -Werror=unused-variable on the b2 CI jobs (MinGW/posix build with -Wall -Wextra -Werror). Register the method with a discard-value expression instead, matching test_registries_do_not_share_vptr_state. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Removing the dead registry_get_ids export left registry_state_id() unreferenced in registry.cpp (the DLLs call it via their own wrappers, not this TU). As a static (internal-linkage) function that tripped MSVC C4505 under /WX; GCC's -Wunused-function was already suppressed by a pragma in registry.cpp but that doesn't cover MSVC. Make the header helper inline — unused inline functions don't warn, and a header function should be inline anyway. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…BSAN
The dll/test/filesystem dependencies were pulled in via the propagated
<link>shared and built as shared libraries. That fails under sanitizers:
* Clang UBSAN (Linux): Clang links the static UBSAN runtime, leaving the
DSO with undefined __ubsan_*_abort symbols; Boost.Filesystem links with
-Wl,--no-undefined, so libboost_filesystem.so fails to link. GCC links
libubsan into the DSO and is unaffected.
* macOS UBSAN: a shared Boost.Test built with hidden visibility gives each
DSO its own typeinfo for lazy_ostream, tripping -fsanitize=vptr at run.
Pin these dependencies to <link>static so only our registry/method/overrider
libraries remain shared objects. Verified on Clang 18 UBSAN, Clang release,
and GCC 13 UBSAN.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
After linking the Boost deps statically, macOS UBSAN got past the earlier
link/lazy_ostream failures but then aborted at runtime: Clang's vptr check
fires on the open-method downcast (core.hpp optimal_cast) of objects created
inside dlopen'd libraries. All 12 failures were vptr-category ("downcast of
address ..." / "member call on address ... which does not point to an object
of type 'Dog'"). The objects really are of the expected type; the check is a
false positive because macOS/Mach-O does not deduplicate a class's typeinfo
across DSOs the way ELF does (Linux passes thanks to <local-visibility>global).
Disable just the vptr sub-check, gated on the UBSAN feature. No-op for GCC
(no vptr sanitizer) and when UBSAN is off; ASAN is unaffected. Verified on
Clang 18 UBSAN.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
registry_state::st is a public static data member (the only way to expose the DLL-shared state as a whole-class instantiation), but it is an implementation detail that should not appear in the reference. It cannot be made protected/private since it is read across the library via the registry::static_ alias rather than through inheritance, so exclude it in mrdocs.yml instead. Also drop a duplicate registry::compiler entry. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The antora-cpp-reference-extension caches the downloaded MrDocs binary under a directory keyed by tag name (.../mrdocs/<os>/develop/), so with no pinned version a local Antora build reuses the first nightly it ever fetched and never picks up newer develop-release builds. CI is unaffected because runners start with an empty cache. This helper compares the upstream develop-release asset's publish time to a marker recording what was last synced, and clears the cache only when upstream has moved. It is a safe no-op when offline, rate-limited, or when a concrete version is pinned. Run it before build_antora.sh (e.g. as a VS Code task dependency) to keep local docs on the same MrDocs CI would use. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
No description provided.