support DLLs on Windows#75
Draft
jll63 wants to merge 148 commits into
Draft
Conversation
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>
The GCC 13 stages bundled multiple C++ standards into one pipeline (17,20,2b x 32,64 for the base job; 17,20 for the UBSAN/ASAN jobs), doing 2-3x the work of their per-standard GCC 14 siblings. After the branch added the dynamic_loading DLL test targets, these three stages exceeded Drone's 60-minute step timeout and were SIGKILLed (exit 137) mid-compile, with no actual build error. Every other toolset/arch passed on the same commit. Split them one standard per stage, mirroring the GCC 14 jobs, so each stays well under the timeout while keeping identical coverage: - GCC 13 32/64 -> C++17 / C++20 / C++2b - GCC 13 32/64 UBSAN -> C++17 / C++20 (C++2b stage already separate) - GCC 13 32 ASAN -> C++17 / C++20 (C++2b stage already separate) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Fixes several issues from a max-effort review of the DLL state-sharing
branch:
- augment_methods() deduped overriders by pointer identity, so the same
overrider registered by two state-sharing modules produced a false
ambiguous_call; now dedups by function type + virtual-parameter types.
- augment_classes() deduped class_info by type_id alone, dropping a
module's static_vptr under hidden ELF visibility (type_id unifies,
static_vptr does not); now keys on (type, static_vptr).
- registry_state_type's policy-state tuple had no uniqueness check, so two
colliding policy state types produced a cryptic "duplicate base type"
error instead of a readable one.
- vptr_vector's policy-ordering static_assert located itself by exact type,
silently passing for a vptr policy derived from vptr_vector.
- initialize() never reset st.initialized on entry, so a failed
re-initialize left dispatch looking valid.
- missing_base's base/derived fields were swapped in the diagnostic.
- fast_perfect_hash's collision check treated re-inserting an identical
type_id as a collision, which the class-dedup fix above now legitimately
triggers under hidden visibility.
- Stale VptrFn/TypeHashFn blueprint docs, sentinel-type_id hazard in both
custom_rtti examples (plus a defensive arity check), test bugs
(os.clear() vs os.str(""), a missing assertion), CMake silently skipping
dynamic_loading tests by default, three shared_libraries.adoc gaps, a
non-inline compat header in announce.md, and a stale CLAUDE.md test
description.
- Added 5 files that were referenced by tracked build/doc files but never
committed.
- Removed .claude/settings.json and notes.txt from tracking (personal
scratch files, kept on disk).
Extends test/dynamic_loading with a Cat class and shared_overrider.hpp
(the same overrider registered by two modules) plus a hidden-visibility
CMake variant, regression-testing the two dispatch fixes above; verified
each independently by reverting its fix and confirming the new test fails.
Verified: full ctest suite (112/112) under CMake+gcc and CMake+clang, plus
the complete b2 test suite, all passing.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…NDEBUG The consistency-check loop added in the previous commit only uses its loop variable inside BOOST_ASSERT, which compiles out under NDEBUG (release builds), leaving it genuinely unused. This broke the macOS/Xcode UBSAN CI job (-Werror,-Wunused-variable), which builds in release mode: https://drone.cpp.al/boostorg/openmethod/253/30/2 Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…ders augment_methods()'s cross-module overrider dedup (keyed on function type + virtual-parameter types) correctly merges the same overrider duplicated across state-sharing modules, but was also merging two genuinely different overriders that happen to share a signature within a SINGLE module - picking one arbitrarily instead of correctly reporting ambiguous_call, a regression from the previous behavior (pointer-identity dedup, which never merged same-module entries). Fix: only compare a module's specs against specs already collected from EARLIER modules, never against other specs from its own module. Two overriders registered within the same module are always genuinely distinct by construction (BOOST_OPENMETHOD_OVERRIDE keys one explicit specialization per signature - writing it twice is a redefinition error; method<...>::override<Fn> is keyed on the Fn non-type template argument - two different functions are two different registrations), so same-module entries must never be merged with each other, even when they share a signature. Adds test_runtime_errors_duplicate_overrider.cpp: registers two distinct functions with an identical signature directly via method<...>::override<Fn> (the primitive BOOST_OPENMETHOD_OVERRIDE itself expands to, which the macro can't use twice with the same signature in one TU) and asserts the call is reported as ambiguous_call, not silently resolved. Verified: reproduced the bug standalone before the fix (report.ambiguous==0, call silently returned one overrider's result), confirmed the old pointer-identity dedup handled it correctly, confirmed the fix restores that behavior while leaving the cross-module dedup (dynamic_loading Cat test, including the hidden-visibility variant) intact. Full suite green under CMake+gcc, CMake+clang, and b2. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…euristic augment_methods()'s cross-module overrider dedup previously merged two same-signature overrider_info entries whenever they came from different method.infos entries (an "earlier modules only" heuristic), to avoid merging two genuinely different overriders registered within the same module. That heuristic is replaced with a flag tied to an actual C++ invariant: BOOST_OPENMETHOD_INLINE_OVERRIDE marks its overrider_info as inline_ = true; BOOST_OPENMETHOD_OVERRIDE never does. Only an overrider declared `inline` can legally have an identical definition appear in more than one translation unit/module (ODR requires it for a non-inline function to be defined exactly once in the program), so the dedup now merges two specs only when *both* are inline_, dropping the module-provenance bookkeeping entirely - simpler, and tied to why merging is actually safe rather than where the specs happened to come from. The inline_ flag has to be a compile-time non-type template parameter (Inline) on override_impl/override_aux, set inside override_impl's own constructor alongside method/pf/etc, not a runtime constructor argument on class override as first attempted: override_aux::impl is a static template data member whose dynamic initialization is not guaranteed to happen before class override's own constructor body runs (confirmed empirically by instrumenting both constructors - taking impl's address doesn't force its construction), so a runtime assignment made there could run *before* override_impl's constructor, which then overwrites it via the implicit default-construction of the overrider_info base. A new class inline_override<Fn...> (using override_aux<Fn, decltype(Fn), true>) provides the inline path; plain override<Fn...> is unchanged (Inline always false). BOOST_OPENMETHOD_REGISTER/BOOST_OPENMETHOD_GENSYM (both public, documented, and used directly throughout tests and examples) are untouched; BOOST_OPENMETHOD_INLINE_OVERRIDE just registers via method<...>::inline_override<Fn> instead of ::override<Fn>, both routed through the same, already fully-variadic BOOST_OPENMETHOD_REGISTER. test/dynamic_loading/shared_overrider.hpp switches to BOOST_OPENMETHOD_INLINE_OVERRIDE, which is also the technically-correct ODR-legal choice for an overrider defined identically in a header included by two modules. Verified: full suite green under CMake+gcc, CMake+clang, and b2, including test_runtime_errors_duplicate_overrider and all five dynamic_loading variants (default/indirect/exereg_default/exereg_indirect/hidden_vis) - hidden_vis specifically exercises real cross-module merging of two independently-constructed (non-folded, hidden-visibility) overrider_info objects, which the default-visibility variants don't, since weak-symbol folding under RTLD_GLOBAL there means only one physical registration ever survives regardless of the dedup logic. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…ng test
classes.hpp declares these with internal (static) linkage since it's
included identically by multiple TUs; method.cpp and registry.cpp only
call make_dog(), never make_cat(), which MSVC's /W4 /WX build reports as
error C2220/C4505 ("unreferenced function with internal linkage has been
removed"). gcc/clang don't warn on this by default, so it only surfaced on
the Windows CI runners. Mark both [[maybe_unused]].
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
… review Fixes the items the earlier review verified but cut for its 15-finding cap: - Remove the dead has_id SFINAE detector (zero users anywhere); update the CLAUDE.md line that described it as load-bearing. - Consolidate the two near-identical MSVC>19.51 has_initialize/has_finalize workaround blocks into one BOOST_OPENMETHOD_DETAIL_HAS_STATIC_FN_MSVC_SAFE macro; the maintainer #pragma message now fires once per build instead of twice. - Drop registry::policy's unused Default template parameter and the get_policy_aux specialization it fed - undocumented, and unreachable since no caller ever supplied one. Rewrite has_policy_state as mp11::mp_valid. - fast_perfect_hash: reset min_value/max_value per attempt instead of per pass. A failed attempt's stale range was leaking into the eventually- successful one, contaminating hash_range() and oversizing the vptr vector (a real correctness bug, not just cosmetic). Also fix search_error.buckets reporting double the largest table actually tried (off-by-one from the for-loop's post-loop ++M). - Remove dead registry_make_dog/registry_initialize scaffolding from test/dynamic_loading/registry.cpp (confirmed zero call sites, including via dlopen). - Replace augment_methods()'s check-then-act find()/operator[] double lookup with a single try_emplace. - extensions.cpp doc snippet: move the 3 BOOST_ASSERT self-checks out of the rendered tag::content region (still compiled and run, just not displayed), and move `using namespace boost::openmethod;` inside the snippet so the displayed code actually compiles as shown. - Remove the stale "gcc-8 doesn't like CTAD" comment in registry::finalize: gcc-8/9 can't build this library at all any more (missing full <charconv> support required elsewhere), so the justification was moot. The harmless explicit-type workaround itself is kept. - Correct CMakeLists.txt comments claiming the _exereg_* dynamic_loading variants exercise lib_registry at runtime: it's built and link-configured for reverse linkage but never actually dlopened by main.cpp, only lib_method/lib_overrider are. Deliberately left alone: the duplicated test-file preambles across test_runtime_errors_*.cpp and the custom-RTTI tests carry genuine, easy-to- miss deliberate divergences (guard conditions, array-index offsets, registry configs) that a mechanical dedup would risk silently erasing; actually wiring the _exereg_* registry lib into the runtime test (rather than just correcting the comment) is a design decision, not a mechanical fix. Verified: full ctest suite (113/113) under CMake+gcc and CMake+clang, plus the complete b2 test suite, all passing. Co-Authored-By: Claude Sonnet 5 <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.