Phase 1b of the pluggable solver system. Converts KCSolve from a header-only INTERFACE target to a SHARED library and implements the SolverRegistry with dynamic plugin discovery. Changes: - Add KCSolveGlobal.h export macro header (KCSolveExport) - Move SolverRegistry method bodies from header to SolverRegistry.cpp - Implement scan() with dlopen/LoadLibrary plugin loading - Add scan_default_paths() for KCSOLVE_PLUGIN_PATH + system paths - Plugin entry points: kcsolve_api_version() + kcsolve_create() - API version checking (major version compatibility) - Convert CMakeLists.txt from INTERFACE to SHARED library - Link FreeCADBase (PRIVATE) for Console logging - Link dl on POSIX for dynamic loading - Fix -Wmissing-field-initializers warnings in IKCSolver.h defaults The registry discovers plugins by scanning directories for shared libraries that export the kcsolve C entry points. Plugins are validated for API version compatibility before registration. Manual registration via register_solver() remains available for built-in solvers (e.g. OndselAdapter in Phase 1c).
125 lines
5.0 KiB
C++
125 lines
5.0 KiB
C++
// SPDX-License-Identifier: LGPL-2.1-or-later
|
|
/****************************************************************************
|
|
* *
|
|
* Copyright (c) 2025 Kindred Systems <development@kindred-systems.com> *
|
|
* *
|
|
* This file is part of FreeCAD. *
|
|
* *
|
|
* FreeCAD is free software: you can redistribute it and/or modify it *
|
|
* under the terms of the GNU Lesser General Public License as *
|
|
* published by the Free Software Foundation, either version 2.1 of the *
|
|
* License, or (at your option) any later version. *
|
|
* *
|
|
* FreeCAD is distributed in the hope that it will be useful, but *
|
|
* WITHOUT ANY WARRANTY; without even the implied warranty of *
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
|
|
* Lesser General Public License for more details. *
|
|
* *
|
|
* You should have received a copy of the GNU Lesser General Public *
|
|
* License along with FreeCAD. If not, see *
|
|
* <https://www.gnu.org/licenses/>. *
|
|
* *
|
|
***************************************************************************/
|
|
|
|
#ifndef KCSOLVE_SOLVERREGISTRY_H
|
|
#define KCSOLVE_SOLVERREGISTRY_H
|
|
|
|
#include <functional>
|
|
#include <memory>
|
|
#include <mutex>
|
|
#include <string>
|
|
#include <unordered_map>
|
|
#include <vector>
|
|
|
|
#include "IKCSolver.h"
|
|
#include "KCSolveGlobal.h"
|
|
|
|
namespace KCSolve
|
|
{
|
|
|
|
/// Factory function that creates a solver instance.
|
|
using CreateSolverFn = std::function<std::unique_ptr<IKCSolver>()>;
|
|
|
|
/// Current KCSolve API major version. Plugins must match this to load.
|
|
constexpr int API_VERSION_MAJOR = 1;
|
|
|
|
/// Singleton registry for pluggable solver backends.
|
|
///
|
|
/// Solver plugins register themselves at module load time via
|
|
/// register_solver(). The Assembly module retrieves solvers via get().
|
|
///
|
|
/// Thread safety: all public methods are internally synchronized.
|
|
///
|
|
/// Usage:
|
|
/// // Registration (at module init):
|
|
/// KCSolve::SolverRegistry::instance().register_solver(
|
|
/// "ondsel", []() { return std::make_unique<OndselAdapter>(); });
|
|
///
|
|
/// // Retrieval:
|
|
/// auto solver = KCSolve::SolverRegistry::instance().get(); // default
|
|
/// auto solver = KCSolve::SolverRegistry::instance().get("ondsel");
|
|
|
|
class KCSolveExport SolverRegistry
|
|
{
|
|
public:
|
|
/// Access the singleton instance.
|
|
static SolverRegistry& instance();
|
|
|
|
~SolverRegistry();
|
|
|
|
/// Register a solver backend.
|
|
/// @param name Unique solver name (e.g. "ondsel").
|
|
/// @param factory Factory function that creates solver instances.
|
|
/// @return true if registration succeeded, false if name taken.
|
|
bool register_solver(const std::string& name, CreateSolverFn factory);
|
|
|
|
/// Create an instance of the named solver.
|
|
/// @param name Solver name. If empty, uses the default solver.
|
|
/// @return Solver instance, or nullptr if not found.
|
|
std::unique_ptr<IKCSolver> get(const std::string& name = {}) const;
|
|
|
|
/// Return the names of all registered solvers.
|
|
std::vector<std::string> available() const;
|
|
|
|
/// Query which BaseJointKind values a named solver supports.
|
|
/// Creates a temporary instance to call supported_joints().
|
|
std::vector<BaseJointKind> joints_for(const std::string& name) const;
|
|
|
|
/// Set the default solver name.
|
|
/// @return true if the name is registered, false otherwise.
|
|
bool set_default(const std::string& name);
|
|
|
|
/// Get the default solver name.
|
|
std::string get_default() const;
|
|
|
|
/// Scan a directory for solver plugin shared libraries.
|
|
/// Each plugin must export kcsolve_api_version() and kcsolve_create().
|
|
/// Non-existent or empty directories are handled gracefully.
|
|
void scan(const std::string& directory);
|
|
|
|
/// Scan all default plugin discovery paths:
|
|
/// 1. KCSOLVE_PLUGIN_PATH env var (colon-separated, semicolon on Windows)
|
|
/// 2. <install_prefix>/lib/kcsolve/
|
|
void scan_default_paths();
|
|
|
|
private:
|
|
SolverRegistry();
|
|
|
|
SolverRegistry(const SolverRegistry&) = delete;
|
|
SolverRegistry& operator=(const SolverRegistry&) = delete;
|
|
SolverRegistry(SolverRegistry&&) = delete;
|
|
SolverRegistry& operator=(SolverRegistry&&) = delete;
|
|
|
|
/// Close a single plugin handle (platform-specific).
|
|
static void close_handle(void* handle);
|
|
|
|
mutable std::mutex mutex_;
|
|
std::unordered_map<std::string, CreateSolverFn> factories_;
|
|
std::string default_name_;
|
|
std::vector<void*> handles_; // loaded plugin library handles
|
|
};
|
|
|
|
} // namespace KCSolve
|
|
|
|
#endif // KCSOLVE_SOLVERREGISTRY_H
|