Skip to content

support DLLs on Windows#75

Draft
jll63 wants to merge 91 commits into
boostorg:developfrom
jll63:feature/windll3
Draft

support DLLs on Windows#75
jll63 wants to merge 91 commits into
boostorg:developfrom
jll63:feature/windll3

Conversation

@jll63

@jll63 jll63 commented Jun 13, 2026

Copy link
Copy Markdown
Collaborator

No description provided.

jll63 and others added 22 commits May 24, 2026 09:50
Replace declspec(import/export) approach with method consolidation:
- Remove detail::static_fn infrastructure
- Revert fn to simple static method member (not via MAKE_STATICS)
- In augment_methods(), group methods by type_id and collect
  overriders from all module copies into canonical method
- Build dispatch tables once per unique method, not once per copy

This avoids intrusive list manipulation during initialization while
enabling correct dispatch table construction with shared method state
across DLL boundaries.

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
- Resolve deferred type IDs before consolidation (deferred_static_rtti)
- Update test_custom_rtti to assign unique IDs to non-polymorphic types
  via non_polymorphic_static_type<T>() function-local counter, so method
  tag classes don't collapse to a single sentinel and trigger spurious
  ambiguous dispatch
- Remove get_fn test from dynamic_loading: each module now has its own
  fn instance, so address equality no longer holds (the state is
  synchronized via consolidation at initialize time instead)

All 77 tests pass.

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
- Add `meet` multi-method to test_dynamic_loading so the test exercises
  multiple methods and non-zero slots/strides — previously the test
  accidentally passed because the lone `speak` method got slot 0
  (matching zero-initialized arrays in non-local module fns)
- Rename `canonical_methods` → `local_methods` in augment_methods: the
  selected method_info isn't canonical in any global sense, it's just
  the one that happens to live in the module performing initialize()
- After dispatch table build, copy slots_strides values from each
  local method_info to all other module copies. Each module's `fn`
  has its own slots_strides[] array and dispatch reads it directly,
  so every copy must hold the same values.

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
Iterating methods directly and skipping the self pointer is clearer than
building a side map keyed by type_index.

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
Revert from signed int back to std::size_t. To distinguish polymorphic
from non-polymorphic types, set the high bit (1 << (sizeof(size_t)*8-1))
in the non_polymorphic_static_type counter values. Polymorphic Animal/
Dog/Cat keep small positive values 1/2/3; method tag classes get values
like 0x800...001, 0x800...002 — comfortably outside the 1..3 range used
by type_name. Avoids the size mismatch warnings (C4312) that b2's W4/WX
flags treat as errors on the int->const-void* reinterpret_cast path.

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
Policy state moved into registry_state, so stderr_output::os is now a deprecated alias and stream() returns the registry-backed stream. The library's own diagnostics still wrote to os, which broke warnings-as-errors builds (e.g. b2 with /WX). Route them through stream() and give the test's capture_output policy a stream() accessor.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
boost::dll resolves the relative library name against the working directory. Without WORKING_DIRECTORY, ctest ran the test from the binary tree where a stale boost_openmethod-shared.dll could be picked up, failing at load with ERROR_PROC_NOT_FOUND. Point it at $<TARGET_FILE_DIR:boost_openmethod-dynamic>, where the freshly-built DLL sits co-located with the exe.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@cppalliance-bot

cppalliance-bot commented Jun 13, 2026

Copy link
Copy Markdown

An automated preview of the documentation is available at https://75.openmethod.prtest3.cppalliance.org/libs/openmethod/doc/html/index.html

If more commits are pushed to the pull request, the docs will rebuild at the same URL.

2026-06-28 16:06:00 UTC

@jll63 jll63 force-pushed the feature/windll3 branch from e9e5c26 to 01127a4 Compare June 13, 2026 21:28
jll63 and others added 6 commits June 14, 2026 10:39
registry_state::policy<P>() uses the type-indexed std::get<T>(tuple),
declared in <tuple>. Newer toolchains pulled the header in transitively,
but gcc-12's libstdc++ did not, so only the std::pair overloads of
std::get were visible and the call failed to compile. Include <tuple>
explicitly in the headers that use std::tuple/std::get.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
main.cpp loads and calls method_get_ids/overrider_get_ids through
policy_ids_fn = const void**(), but the exported functions returned
const void*. The pointer reinterpretation is harmless at the ABI level,
so the test passes everywhere except the clang UBSan job, where
-fsanitize=function traps the call through the mismatched function-pointer
type and aborts. Declare the exports to return const void** to match.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Add a second axis to the dynamic_loading test variants: which module owns
and exports the single shared registry-state symbol. Selected at compile
time via the REGISTRY_IN_EXE macro -- registry.cpp exports the state unless
REGISTRY_IN_EXE is set, in which case main.cpp (the executable) exports it
and the shared libraries import it.

CMake builds all four variants ({dll,exe}-owned x {default,indirect}); the
exe-owned ones link the libraries against the executable's import library
(ENABLE_EXPORTS). b2 builds only the two dll-owned variants: it does not
expose an executable's import library to dependent DLLs, so the reverse
linkage cannot be expressed on Windows.

Also rename the default-registry variant suffix from "" to "_default".

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…DR violation with dynamic loading

When a project uses <visibility>hidden (which adds -fvisibility-inlines-hidden),
template static variable initializers like odr_check::inc<R> get hidden symbol
visibility. When multiple shared libraries are loaded via RTLD_GLOBAL on ELF
systems (e.g. s390x), hidden symbols are not deduplicated by the dynamic linker.
Each DSO initializes its own copy of inc<R>, each time incrementing the shared
count variable. When count > 1, initialize() falsely fires the ODR violation check.

Fix: add BOOST_SYMBOL_VISIBLE to odr_check struct, which expands to
__attribute__((__visibility__("default"))) on GCC/Clang (no-op on MSVC/Windows).
This overrides -fvisibility-inlines-hidden for all struct members including the
inc<R> template static, ensuring proper RTLD_GLOBAL symbol deduplication.

Also add the required #include <boost/config.hpp> for BOOST_SYMBOL_VISIBLE.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants