feat(solver): Phase 1b — SolverRegistry and plugin loading #293

Closed
opened 2026-02-19 21:26:40 +00:00 by forbes · 0 comments
Owner

Summary

Implement the SolverRegistry — runtime discovery, loading, and management of solver plugins.

Parent: #287 (Phase 1)
Depends on: #292 (1a — API types)
Blocks: #294 (1c), #295 (1d)

Scope

Files

src/Mod/Assembly/Solver/
├── include/KCSolve/
│   └── SolverRegistry.h    # from 1a
├── src/
│   └── SolverRegistry.cpp  # NEW
└── CMakeLists.txt           # upgrade from header-only to shared lib

SolverRegistry.cpp implementation

Plugin discovery via scan():

  • Iterate plugin_dir for shared libraries (.so / .dll / .dylib)
  • dlopen() / LoadLibrary() each candidate
  • Check kcsolve_api_version() symbol — verify major version compatibility
  • Call kcsolve_create() to instantiate the solver
  • Register the returned IKCSolver* by its id()
  • Log discovery results via Base::Console()

Plugin entry points (exported by each solver .so):

extern "C" KCSolve::IKCSolver* kcsolve_create();
extern "C" const char* kcsolve_api_version();  // "1.0"

Discovery paths (scanned in order):

  1. <install_prefix>/lib/kcsolve/ — system-installed solvers
  2. KCSOLVE_PLUGIN_PATH env var — development overrides
  3. Future: user plugin directory

Manual registration:

  • register_solver(std::unique_ptr<IKCSolver>) — for built-in solvers (OndselAdapter will use this)
  • Validates no duplicate id()

Lookup:

  • get(solver_id) — returns IKCSolver* or nullptr
  • available() — list of registered solver IDs
  • get_default() / set_default() — global default solver selection
  • joints_for(BaseJointKind) — which registered solvers support a given joint kind

Lifecycle:

  • SolverRegistry is a singleton owned by the Assembly module (or App-level if needed for server)
  • Plugins are loaded once at startup, unloaded at shutdown
  • Handles are kept alive for the process lifetime

CMakeLists.txt

Upgrade from header-only INTERFACE to a proper shared library target:

add_library(KCSolve SHARED
    src/SolverRegistry.cpp
)
target_include_directories(KCSolve PUBLIC include/)
target_link_libraries(KCSolve PRIVATE FreeCADBase)  # for Console logging

Platform-specific: link dl on Linux, nothing extra on Windows/macOS.

Acceptance criteria

  • SolverRegistry::scan() discovers and loads .so plugins from a directory
  • kcsolve_api_version() check rejects incompatible plugins with a clear error message
  • register_solver() works for manual (built-in) registration
  • get(), available(), get_default() work correctly
  • joints_for() aggregates supported joints across all registered solvers
  • No plugins found = no error (empty registry is valid)
  • Duplicate solver ID = warning + skip
  • dlopen failure = warning + skip (don't crash)
  • Builds as libKCSolve.so and links into Assembly module

References

  • docs/INTER_SOLVER.md §4.3 (SolverRegistry), §4.4 (Plugin Loading)
  • Existing plugin pattern: FreeCAD's module loading in src/App/Application.cpp
## Summary Implement the SolverRegistry — runtime discovery, loading, and management of solver plugins. **Parent:** #287 (Phase 1) **Depends on:** #292 (1a — API types) **Blocks:** #294 (1c), #295 (1d) ## Scope ### Files ``` src/Mod/Assembly/Solver/ ├── include/KCSolve/ │ └── SolverRegistry.h # from 1a ├── src/ │ └── SolverRegistry.cpp # NEW └── CMakeLists.txt # upgrade from header-only to shared lib ``` ### SolverRegistry.cpp implementation **Plugin discovery via `scan()`:** - Iterate `plugin_dir` for shared libraries (`.so` / `.dll` / `.dylib`) - `dlopen()` / `LoadLibrary()` each candidate - Check `kcsolve_api_version()` symbol — verify major version compatibility - Call `kcsolve_create()` to instantiate the solver - Register the returned `IKCSolver*` by its `id()` - Log discovery results via `Base::Console()` **Plugin entry points (exported by each solver .so):** ```cpp extern "C" KCSolve::IKCSolver* kcsolve_create(); extern "C" const char* kcsolve_api_version(); // "1.0" ``` **Discovery paths (scanned in order):** 1. `<install_prefix>/lib/kcsolve/` — system-installed solvers 2. `KCSOLVE_PLUGIN_PATH` env var — development overrides 3. Future: user plugin directory **Manual registration:** - `register_solver(std::unique_ptr<IKCSolver>)` — for built-in solvers (OndselAdapter will use this) - Validates no duplicate `id()` **Lookup:** - `get(solver_id)` — returns `IKCSolver*` or nullptr - `available()` — list of registered solver IDs - `get_default()` / `set_default()` — global default solver selection - `joints_for(BaseJointKind)` — which registered solvers support a given joint kind **Lifecycle:** - `SolverRegistry` is a singleton owned by the Assembly module (or App-level if needed for server) - Plugins are loaded once at startup, unloaded at shutdown - Handles are kept alive for the process lifetime ### CMakeLists.txt Upgrade from header-only INTERFACE to a proper shared library target: ```cmake add_library(KCSolve SHARED src/SolverRegistry.cpp ) target_include_directories(KCSolve PUBLIC include/) target_link_libraries(KCSolve PRIVATE FreeCADBase) # for Console logging ``` Platform-specific: link `dl` on Linux, nothing extra on Windows/macOS. ## Acceptance criteria - [ ] `SolverRegistry::scan()` discovers and loads `.so` plugins from a directory - [ ] `kcsolve_api_version()` check rejects incompatible plugins with a clear error message - [ ] `register_solver()` works for manual (built-in) registration - [ ] `get()`, `available()`, `get_default()` work correctly - [ ] `joints_for()` aggregates supported joints across all registered solvers - [ ] No plugins found = no error (empty registry is valid) - [ ] Duplicate solver ID = warning + skip - [ ] `dlopen` failure = warning + skip (don't crash) - [ ] Builds as `libKCSolve.so` and links into Assembly module ## References - `docs/INTER_SOLVER.md` §4.3 (SolverRegistry), §4.4 (Plugin Loading) - Existing plugin pattern: FreeCAD's module loading in `src/App/Application.cpp`
forbes added the enhancement label 2026-02-19 21:26:40 +00:00
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: kindred/create#293