Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
74 commits
Select commit Hold shift + click to select a range
c7c94dd
BOOST_OPENMETHOD_DETAIL_HAS_STATIC_FN
jll63 Jan 26, 2026
cbc4ca2
support dynamic loading on Windows
jll63 Jan 26, 2026
189a37d
support dynamic loading on Windows
jll63 Mar 8, 2026
55afef5
support dynamic loading on Windows
jll63 Mar 14, 2026
c001105
support dynamic loading on Windows
jll63 Mar 14, 2026
2676934
test
jll63 Mar 14, 2026
4ba9bc7
test _WIN32, not _MSC_VER
jll63 Mar 14, 2026
b10fee9
detail::unspecified
jll63 Mar 14, 2026
00ba04f
find dlls
jll63 Mar 14, 2026
9d751c4
find dlls
jll63 Mar 14, 2026
fa68acc
update doc (claude)
jll63 Mar 14, 2026
27f2cc3
simplify file names
jll63 Mar 14, 2026
2329655
remove useless compile definitions
jll63 Mar 14, 2026
bdd644e
mingw
jll63 Mar 14, 2026
b4e7754
prefix DLL file names
jll63 Mar 14, 2026
031adbf
'main' static links with 'registry'
jll63 Mar 14, 2026
d257c66
improve dl tests
jll63 Mar 14, 2026
d27f3e2
mingw
jll63 Mar 14, 2026
1c0d683
with b2
jll63 Mar 14, 2026
0d1adb0
mingw
jll63 Mar 15, 2026
7f114d1
minor reformatting
jll63 Mar 17, 2026
0b11d2e
work with b2 on posix (claude)
jll63 Mar 17, 2026
23deeec
Revert "work with b2 on posix (claude)"
jll63 Mar 22, 2026
676ccd8
Revert "minor reformatting"
jll63 Mar 22, 2026
11dc8c2
Revert "mingw"
jll63 Mar 22, 2026
41d6802
Revert "with b2"
jll63 Mar 22, 2026
e8d05fa
Revert "mingw"
jll63 Mar 22, 2026
ad50ae8
mingw
jll63 Mar 28, 2026
43912c3
mingw
jll63 Mar 28, 2026
6161186
simpler?
jll63 Mar 28, 2026
7d3859d
get_attributes -> get_declspec
jll63 Mar 28, 2026
422266c
b2
jll63 Mar 28, 2026
c6a4a4b
ok cmake
jll63 Mar 28, 2026
fbdebd8
also check fn
jll63 Mar 28, 2026
d206a8b
alleluiah!
jll63 Mar 29, 2026
d9e88cf
cmake
jll63 Mar 29, 2026
d60e264
cmake
jll63 Mar 29, 2026
7d9e325
b2: add deps
jll63 Mar 29, 2026
f0e522e
initialize.hpp need to include <tuple>
jll63 Mar 29, 2026
dca2b90
cml
jll63 Mar 29, 2026
b89a530
cml
jll63 Mar 29, 2026
ea2b7bf
Jamfile: move tag stuff one level
jll63 Mar 29, 2026
2e20cb6
cml on win
jll63 Mar 29, 2026
d30a1d1
cml on win
jll63 Mar 29, 2026
441e635
kill warning
jll63 Mar 29, 2026
4c7e4cf
b2: create symlinks/copies instead
jll63 Mar 29, 2026
df6f341
disable error=unused-function
jll63 Mar 29, 2026
fa71b8e
add guards
jll63 Mar 29, 2026
504f9db
remove INCLUDED_FROM
jll63 Mar 29, 2026
d22988f
fast_perfect_hash.hpp: #include <tuple>
jll63 Mar 29, 2026
a957382
win+b2
jll63 Mar 29, 2026
a901988
win+b2
jll63 Mar 29, 2026
1b36962
claude
jll63 Apr 14, 2026
7b337b0
mingw
jll63 Apr 18, 2026
062ea07
clang-win
jll63 Apr 18, 2026
a65d667
clang-win
jll63 Apr 19, 2026
6f98241
posix, mac: only when visibility = global
jll63 Apr 19, 2026
e3d1fc3
no alias
jll63 Apr 19, 2026
a96a84a
always use find_lib
jll63 Apr 19, 2026
07e94ab
cml
jll63 Apr 19, 2026
6795e57
wsl
jll63 Apr 19, 2026
04576b2
win
jll63 Apr 19, 2026
f7db988
cml
jll63 Apr 19, 2026
0657adb
wip
jll63 Apr 19, 2026
2903cdb
shift OK
jll63 Apr 20, 2026
85831c3
cygwin
jll63 Apr 20, 2026
4394cfc
cygwin
jll63 Apr 20, 2026
ea7df81
wip
jll63 Apr 20, 2026
2132d39
DLL in same dir as exe
jll63 Apr 20, 2026
ddac200
posix?
jll63 Apr 21, 2026
3fc41a2
b2: add boost_filesystem
jll63 Apr 26, 2026
4342495
CI: skip incapable compilers
jll63 Apr 26, 2026
abaa3a0
dynamic_loading test: suppress warnings for deps
jll63 Apr 26, 2026
d2f96c8
move almost everything to method_base
jll63 May 10, 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
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ jobs:
uses: boostorg/boost-ci/.github/workflows/reusable.yml@master
with:
exclude_cxxstd: '98,03,0x,11,14'
exclude_compiler: gcc-4.9,gcc-5,gcc-6,gcc-7,clang-3.9,clang-4.0,clang-5.0,clang-6.0,clang-7,clang-16
exclude_compiler: gcc-4.9,gcc-5,gcc-6,gcc-7,gcc-8,gcc-9,clang-3.9,clang-4.0,clang-5.0,clang-6.0,clang-7,clang-16
# exclude clang-16 because it seems to use the c++14 standard library on ubuntu-24.04:
# include/c++/14/bits/stl_pair.h:410:35: error: no matching function for call to 'get'
# maybe similar to this: https://github.com/actions/runner-images/issues/9679
Expand Down
356 changes: 356 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,356 @@
# CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

## Project Overview

Boost.OpenMethod is a C++17 header-only library implementing open multi-methods (multiple dispatch). Unlike traditional virtual functions where dispatch occurs only on the first (`this`) parameter, open methods dispatch based on the runtime types of multiple arguments.

**Key Characteristics:**
- C++17 required
- Header-only library
- Part of the Boost ecosystem
- Supports both CMake and Boost.Build (b2)

## Build System

### CMake Build

**Basic build:**
```bash
mkdir build && cd build
cmake .. -DBOOST_SRC_DIR=/path/to/boost
cmake --build .
```

**Build with tests:**
```bash
cmake .. -DBOOST_OPENMETHOD_BUILD_TESTS=ON
cmake --build . --target tests
ctest
```

**Build with examples:**
```bash
cmake .. -DBOOST_OPENMETHOD_BUILD_TESTS=ON -DBOOST_OPENMETHOD_BUILD_EXAMPLES=ON
cmake --build .
```

**Important CMake options:**
- `BOOST_OPENMETHOD_BUILD_TESTS` - Enable tests (default: ON if root project)
- `BOOST_OPENMETHOD_BUILD_EXAMPLES` - Enable examples (requires tests enabled)
- `BOOST_OPENMETHOD_WARNINGS_AS_ERRORS` - Treat warnings as errors
- `BOOST_SRC_DIR` - Path to Boost source directory (default: `../..` or `$BOOST_SRC_DIR` env var)

### Boost.Build (b2)

**Build and test:**
```bash
b2 test
```

**Quick test (for CI):**
```bash
b2 test//quick
```

## Testing

### Running All Tests (CMake)
```bash
cd build
ctest
```

### Running a Single Test (CMake)
```bash
cd build
ctest -R test_dispatch # Run specific test by name
# or directly
./boost_openmethod-test_dispatch
```

### Test Structure
- Test files: `test/test_*.cpp` - Standard unit tests using Boost.Test
- Compile-fail tests: `test/compile_fail_*.cpp` - Tests that should fail to compile
- Mixed build test: `test/mix_release_debug/` - Tests mixing debug/release builds
- Dynamic loading test: `test/dynamic_loading/` - Tests shared library support (requires Boost.DLL)
- 21+ test files covering dispatch, policies, virtual_ptr, RTTI, errors, etc.

### Debug Mode Features
When building in Debug mode (`CMAKE_BUILD_TYPE=Debug`), runtime checks are automatically enabled via `BOOST_OPENMETHOD_ENABLE_RUNTIME_CHECKS`.

## Architecture

### Layered Design

The library is structured in three conceptual layers:

1. **Preamble Layer** ([preamble.hpp](include/boost/openmethod/preamble.hpp))
- Foundational types: `type_id`, `vptr_type`, `virtual_<T>`
- Registry and policy framework
- Error types: `not_initialized`, `bad_call`, `no_overrider`, `ambiguous_call`, etc.
- No executable dispatch code

2. **Core API** ([core.hpp](include/boost/openmethod/core.hpp))
- `method<Id, ReturnType(Parameters...), Registry>` - Method implementation
- `virtual_ptr<Class, Registry>` - "Wide pointer" combining object pointer + v-table pointer
- Dispatch algorithms: `resolve_uni()` (single dispatch), `resolve_multi_*()` (multiple dispatch)
- Override registration via `override_impl<>`
- Class registration via `use_classes<>`

3. **Macro Layer** ([macros.hpp](include/boost/openmethod/macros.hpp))
- `BOOST_OPENMETHOD(name, params, return_type)` - Declare method
- `BOOST_OPENMETHOD_OVERRIDE(name, params, return_type)` - Declare overrider
- `BOOST_OPENMETHOD_CLASSES(classes...)` - Register class hierarchy
- Generates static registrar objects for automatic registration

### Key Concepts

**Open Methods**: Functions where dispatch depends on runtime types of multiple parameters, not just the first.

**Virtual Parameters**: Parameters marked with `virtual_<T>` or `virtual_ptr<T>` that participate in dispatch.

**Registries**: Template-parameterized contexts holding classes, methods, and policies. Default: `boost::openmethod::default_registry`.

**Policies**: Pluggable components controlling behavior:
- `rtti` - Type identification (std_rtti, static_rtti, custom)
- `vptr` - V-table storage (vptr_vector, vptr_map)
- `type_hash` - Type ID hashing (fast_perfect_hash with hash_fn function object)
- `error_handler` - Error handling strategy (default_error_handler, throw_error_handler)
- `output` - Diagnostic output destination (stderr_output)
- `attributes` - Visibility/DLL decoration (dllexport, dllimport, local)

**Dispatch Mechanisms**:
- Single dispatch: Direct v-table lookup `vtbl[slot]`
- Multi-dispatch: Stride-based indexing through multi-dimensional dispatch tables

**virtual_ptr**: A "wide pointer" combining object pointer with v-table pointer
for efficient dispatch. Key for enabling dispatch on non-polymorphic or smart
pointer types.

### Component Interaction

```
User Code → Macros → Core API → Preamble → Policies
Static Registration
```

Static initializers generated by macros call core API functions to register
classes, methods, and overriders. The `initialize()` function builds dispatch
tables before first use.

## Code Conventions

### Formatting
The project uses clang-format with an LLVM-based style:
- `AlignAfterOpenBracket: AlwaysBreak`
- `AllowShortFunctionsOnASingleLine: false`
- No short blocks, if statements, or loops on single lines

### Compiler Requirements
Tests require these C++17 features (checked by Boost.Build):
- auto nontype template params
- deduction guides
- fold expressions
- if constexpr
- inline variables
- structured bindings
- `<charconv>`, `<string_view>`, `<variant>` headers

## Common Development Patterns

### Working with Shared Libraries / DLL Support

**Overview**: The library supports shared library usage on Windows with proper dllexport/dllimport decoration.

**Key Pattern - Decoratable Static Variables**:
All policy static variables use `BOOST_OPENMETHOD_DETAIL_MAKE_STATICS(name)` macro (in `preamble.hpp`) to enable DLL decoration. This generates three specializations of `static_<name>`:
- Default (no attributes)
- `BOOST_SYMBOL_EXPORT` when registry has dllexport attributes
- `BOOST_SYMBOL_IMPORT` when registry has dllimport attributes

The attributes are selected via `get_attributes<Guide>` which uses ADL to call `boost_openmethod_declspec(Guide)`.

**Affected Policies** (actual symbol names from `nm -D`):
- `stderr_output::fn::os` - Output stream (via `static_os`)
- `default_error_handler::fn::handler` - Error handler function (via `static_handler`)
- `fast_perfect_hash::fn::hash_fn` - Hash factors struct (via `static_hash_fn`)
- `vptr_map::fn::vptrs` - V-table pointer map (via `static_vptrs`)
- `vptr_vector::fn::vptr_vector_vptrs` / `vptr_vector_indirect_vptrs` - V-table vectors
- Registry state itself: `static_st<registry_state<Registry>>::st` - class/method/overrider lists

**`declspec` types** (in `preamble.hpp`):
- `dllexport` — marks the owning library (exports static variables)
- `dllimport` — marks client libraries (imports from the owning library)
- `declspec_none` — no-op; use on non-Windows where dllexport/dllimport are unnecessary

**Example Usage**:
```cpp
// In header shared between library and client
#if !defined(_MSC_VER)
#define MY_API boost::openmethod::declspec_none
#elif defined(MY_LIBRARY_EXPORTS)
#define MY_API boost::openmethod::dllexport
#else
#define MY_API boost::openmethod::dllimport
#endif

namespace boost::openmethod {
MY_API boost_openmethod_declspec(default_registry_attributes);
}

BOOST_OPENMETHOD(my_method, (virtual_ptr<MyClass>), void, MY_API);
```

**Critical: Exporting the Registry State**:
`static_st<registry_state<...>>::st` (the list of registered classes/methods/overriders) is only
instantiated and exported from a shared library when that library compiles code that actually
registers classes or methods (i.e., `BOOST_OPENMETHOD_CLASSES` or `BOOST_OPENMETHOD` macros).
If a library is meant to "own" the registry state, it must include a header that triggers these
registrations — it is not enough to just declare `boost_openmethod_declspec`.

See `doc/modules/ROOT/examples/shared_libs/` and `test/dynamic_loading/` for complete examples.

**Dynamic Loading Test** (`test/dynamic_loading/`):
Verifies shared state across libraries. Each library exports a single `dl_XXX_get_policy_ids() -> const void**` (null-terminated array of policy state addresses) plus `dl_XXX_get_method_fn()` where applicable. The array is built at first call using:
```cpp
namespace mp11 = boost::mp11; // alias, not 'using namespace' — avoids detail:: ambiguity
// ...
mp11::mp_for_each<default_registry::policy_list>([&](auto p) {
using P = decltype(p);
if constexpr (detail::has_id<default_registry::policy<P>>) {
ids[i++] = default_registry::policy<P>::id();
}
});
```
Files:
- `registry.hpp` — sets up `REGISTRY_API` macro and `boost_openmethod_declspec` declaration
- `method.hpp` — sets up `METHOD_API` macro and declares the `speak` method
- `classes.hpp` — class definitions + `BOOST_OPENMETHOD_CLASSES` (included by lib_registry with dllexport to force `st` export)
- `lib_registry.cpp` — compiled with `REGISTRY_API=dllexport`, exports all registry statics
- `lib_method.cpp` — compiled with `METHOD_API=dllexport`, imports registry from lib_registry
- `lib_overrider.cpp` — dynamically loaded at runtime, adds a Dog overrider
- `main.cpp` — loads lib_overrider, compares all `id()` arrays element-by-element, calls `initialize()`, tests dispatch

### Custom RTTI
When `<typeinfo>` is unavailable or insufficient, use static_rtti or implement custom RTTI. See `doc/modules/ROOT/examples/custom_rtti/` and policies in `include/boost/openmethod/policies/`.

### Multiple Registries
Registries are completely independent. Use separate registries to:
- Isolate method sets
- Apply different policies to different method families
- Enable coexistence of incompatible configurations

Registry type must be specified consistently across related methods and classes.

## File Organization

- `include/boost/openmethod/` - Public headers
- `core.hpp`, `macros.hpp`, `preamble.hpp` - Main headers
- `initialize.hpp` - Dispatch table construction
- `default_registry.hpp` - Default policy configuration
- `detail/` - Internal implementation details
- `policies/` - Policy implementations
- `interop/` - Interoperability with other systems
- `test/` - Unit tests and compile-fail tests
- `doc/modules/ROOT/examples/` - Example programs
- `doc/modules/ROOT/pages/` - AsciiDoc documentation

## Dependencies (Boost Libraries)

Required:
- Boost.Assert
- Boost.Config
- Boost.Core
- Boost.DynamicBitset
- Boost.MP11 (metaprogramming)
- Boost.Preprocessor

For testing:
- Boost.Test
- Boost.SmartPtr

For examples:
- Boost.DLL (shared library examples)

## Development Workflow

1. Make changes to headers in `include/boost/openmethod/`
2. Build tests: `cmake --build build --target tests`
3. Run tests: `cd build && ctest`
4. For changes affecting examples: enable `BOOST_OPENMETHOD_BUILD_EXAMPLES`
5. Submit PRs against the `develop` branch

## Important Implementation Details

### Static Registration
Classes, methods, and overriders register automatically via static constructors. This happens before `main()`. The `initialize()` function must be called before first method invocation to build dispatch tables.

### Dispatch Table Construction
The `initialize()` function:
1. Collects registered classes and overriders
2. Builds class hierarchy using provided inheritance relationships
3. Constructs dispatch tables using perfect hashing
4. Validates configuration (in debug mode or with runtime_checks policy)

### Virtual Pointer Mechanics
`virtual_ptr<T>` stores both object pointer and v-table pointer. It can be constructed from:
- Raw pointers (requires prior `use_classes` registration)
- Smart pointers (std::unique_ptr, std::shared_ptr, boost::intrusive_ptr)
- References
- Other virtual_ptr instances

The v-table pointer enables O(1) method dispatch.

### Policy Static Variables Pattern

When adding static variables to policies:

1. **Declare the variable storage** in `detail` namespace using `BOOST_OPENMETHOD_DETAIL_MAKE_STATICS(variable_name)` (defined in `preamble.hpp`)
2. **Use type alias** in policy's `fn<Registry>` class: `using var_storage = detail::static_variable_name<Type, Registry>`
3. **Access via storage**: `var_storage::variable_name` instead of direct static member
4. **Definition is generated** by the macro: `template<class Registry> Type detail::static_variable_name<Type, Registry, Enable>::variable_name;`

This pattern ensures static variables can be properly decorated with dllexport/dllimport for shared library usage.

5. **Add an `id()` function** to the policy's `fn<Registry>` class returning the address of the first byte of the state. Its presence is detectable via `detail::has_id<P>` (a variable template generated by `BOOST_OPENMETHOD_DETAIL_HAS_STATIC_FN(has_id, id)` in `preamble.hpp`):
```cpp
static auto id() -> const void* {
return &static_::variable_name;
}
```
For policies with two possible state variables (e.g., `vptr_vector`), use `if constexpr` to select the active one:
```cpp
static auto id() -> const void* {
if constexpr (Registry::has_indirect_vptr) {
return &static_::vptr_vector_indirect_vptrs;
} else {
return &static_::vptr_vector_vptrs;
}
}
```
For `stderr_output`, which inherits its state, use the class name: `&fn::os`.

**Example from fast_perfect_hash**:
```cpp
// In detail namespace
struct hash_fn {
std::size_t mult, shift, min_value, max_value;
auto operator()(type_id type) const -> std::size_t {
return (mult * reinterpret_cast<uintptr>(type)) >> shift;
}
};
BOOST_OPENMETHOD_DETAIL_MAKE_STATICS(hash_fn); // generates static_hash_fn<Type, Registry, Enable>

// In policy
template<class Registry>
class fn {
using factors_storage = detail::static_hash_fn<detail::hash_fn, Registry>;
public:
static auto hash(type_id type) -> std::size_t {
return factors_storage::hash_fn(type); // Use via storage
}
};
```
6 changes: 2 additions & 4 deletions doc/modules/ROOT/examples/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ foreach (cpp ${cpp_files})
add_dependencies(tests ${test_target})
endforeach()

add_subdirectory(shared_libs)

function(boost_openmethod_add_step_by_step dir)
set(add_test "")
if(ARGC GREATER 1)
Expand Down Expand Up @@ -60,7 +62,3 @@ boost_openmethod_add_step_by_step(ambiguities OFF)
boost_openmethod_add_step_by_step(core_api)
boost_openmethod_add_step_by_step(custom_rtti)
boost_openmethod_add_step_by_step(virtual_ptr_alt)

if (NOT WIN32)
add_subdirectory(shared_libs)
endif()
Loading
Loading