OpenSWMM Engine  6.0.0-alpha.4
Data-oriented, plugin-extensible SWMM Engine (6.0.0-alpha.4)
Loading...
Searching...
No Matches
PluginFactory.hpp
Go to the documentation of this file.
1// SPDX-License-Identifier: Apache-2.0
2//
3// Copyright 2026 Caleb Buahin
4//
5// Licensed under the Apache License, Version 2.0 (the "License");
6// you may not use this file except in compliance with the License.
7// You may obtain a copy of the License at
8//
9// http://www.apache.org/licenses/LICENSE-2.0
10//
11// Unless required by applicable law or agreed to in writing, software
12// distributed under the License is distributed on an "AS IS" BASIS,
13// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14// See the License for the specific language governing permissions and
15// limitations under the License.
16
58
59#ifndef OPENSWMM_ENGINE_PLUGIN_FACTORY_HPP
60#define OPENSWMM_ENGINE_PLUGIN_FACTORY_HPP
61
62#include <string>
63#include <vector>
64#include <unordered_map>
65#include <functional>
66
67// Forward declarations
68namespace openswmm {
70class IInputPlugin;
71class IOutputPlugin;
72class IReportPlugin;
73class IStateIOPlugin;
76struct PluginSpec;
77enum class PluginType;
78}
79
80namespace openswmm {
81
92public:
102
103 // Non-copyable, movable
105 PluginFactory(const PluginFactory&) = delete;
112 PluginFactory(PluginFactory&&) noexcept = default;
120 PluginFactory& operator=(PluginFactory&&) noexcept = default;
121
122 // -----------------------------------------------------------------------
123 // Discovery and resolution
124 // -----------------------------------------------------------------------
125
131 void discover(std::function<void(const std::string&)> warn_cb = {});
132
144 IPluginComponentInfo* load_library(const std::string& path,
145 std::function<void(const std::string&)> warn_cb = {});
146
153 IPluginComponentInfo* find_component(const std::string& id_or_path,
154 std::function<void(const std::string&)> warn_cb = {});
155
163 std::string id;
164 std::string version;
165 bool has_input = false;
166 bool has_output = false;
167 bool has_report = false;
168 bool has_state_io = false;
170
177 bool is_builtin = false;
178 };
179
183 std::vector<ComponentEntry> discovered_components() const;
184
185 // -----------------------------------------------------------------------
186 // D2 (program plan §B.2.2) — HydroCouple component libraries
187 // -----------------------------------------------------------------------
188 //
189 // A library that exports no `openswmm_plugin_info` is offered to this
190 // catalogue before it is unloaded: if it carries the HydroCouple component
191 // ABI (`hydrocouple_component_abi_v1`, HydroCouple's
192 // `hydrocouplecomponentabi.h`) its stamp is compared against the engine's
193 // own, and only on agreement is `hydrocouple_component_info_v1` called.
194 //
195 // PROCESS-GLOBAL, unlike the rest of this class. The plan's C API for it
196 // takes no engine handle (§B.2.5 `swmm_component_library_count(void)`),
197 // the same as the built-in `swmm_process_component_known_*` catalogue: a
198 // component library is a fact about the installation, not about one
199 // model. Every engine's `load_library` feeds the same catalogue, which
200 // de-duplicates by path.
201 //
202 // Libraries stay loaded for the life of the process. The component-info
203 // object is a function-local static INSIDE the library, and component
204 // instances are host-owned objects whose code lives there too, so
205 // unloading while anything could still refer to either is undefined
206 // behaviour. Never unloading is the simple correct answer for a discovery
207 // catalogue; only `component_catalog_reset_for_testing` closes handles.
208 //
209 // Without OPENSWMM_WITH_HYDROCOUPLE this compiles and answers "nothing
210 // discovered"; the header exposes no HydroCouple type either way.
211
216 std::string path;
217 std::string id;
218 std::string caption;
219 std::string version;
220 std::string kind;
221 std::string stamp;
222 std::string load_error;
223 void* info = nullptr;
224 };
225
228 static bool hydrocouple_enabled() noexcept;
229
232 static std::string component_abi_stamp();
233
241 static int component_search_path_add(const std::string& dir);
242
247 static std::vector<ComponentLibraryRecord> component_libraries();
248
254
255 // -----------------------------------------------------------------------
256 // Loading (from specs or explicit paths)
257 // -----------------------------------------------------------------------
258
267 int load_plugins(const std::vector<PluginSpec>& specs,
268 std::function<void(const std::string&)> warn_cb = {},
269 const SimulationContext* io = nullptr);
270
271 // -----------------------------------------------------------------------
272 // Lifecycle dispatch (main thread — call in order)
273 // -----------------------------------------------------------------------
274
301 int update_all(const SimulationSnapshot& snapshot);
312
313 // -----------------------------------------------------------------------
314 // Introspection
315 // -----------------------------------------------------------------------
316
321 const std::vector<IInputPlugin*>& input_plugins() const noexcept { return input_plugins_; }
326 const std::vector<IOutputPlugin*>& output_plugins() const noexcept { return output_plugins_; }
331 const std::vector<IReportPlugin*>& report_plugins() const noexcept { return report_plugins_; }
336 const std::vector<IStateIOPlugin*>& state_io_plugins() const noexcept {
337 return state_io_plugins_;
338 }
339
342 int plugin_count() const noexcept {
343 return static_cast<int>(input_plugins_.size() + output_plugins_.size() +
344 report_plugins_.size() + state_io_plugins_.size());
345 }
346
349 bool empty() const noexcept { return plugin_count() == 0; }
350
355 void unload_all();
356
357 // -----------------------------------------------------------------------
358 // Plugin injection (for built-in / programmatic plugins)
359 // -----------------------------------------------------------------------
360
366 void add_output_plugin(IOutputPlugin* plugin, std::vector<std::string> args = {},
367 IPluginComponentInfo* info = nullptr);
371 void add_report_plugin(IReportPlugin* plugin, std::vector<std::string> args = {},
372 IPluginComponentInfo* info = nullptr);
376 void add_input_plugin(IInputPlugin* plugin);
380 void add_state_io_plugin(IStateIOPlugin* plugin);
381
382private:
383 // -----------------------------------------------------------------------
384 // Internal types
385 // -----------------------------------------------------------------------
386
387 struct LibEntry {
388 void* handle = nullptr;
389 IPluginComponentInfo* info = nullptr;
390 std::string path;
391 };
392
393 // -----------------------------------------------------------------------
394 // Component registry
395 // -----------------------------------------------------------------------
396
398 std::unordered_map<std::string, std::size_t> registry_;
399
400 // -----------------------------------------------------------------------
401 // Plugin storage
402 // -----------------------------------------------------------------------
403
404 std::vector<LibEntry> libs_;
405 std::vector<IInputPlugin*> input_plugins_;
406 std::vector<IOutputPlugin*> output_plugins_;
407 std::vector<IReportPlugin*> report_plugins_;
408 std::vector<IStateIOPlugin*> state_io_plugins_;
409 std::vector<std::vector<std::string>> init_args_;
410 std::unordered_map<void*, std::vector<std::string>> instance_args_;
411 std::unordered_map<void*, IPluginComponentInfo*> instance_info_;
412
413 // -----------------------------------------------------------------------
414 // Helpers
415 // -----------------------------------------------------------------------
416
423 void scan_directory(const std::string& dir_path,
424 std::function<void(const std::string&)> warn_cb);
425
429 void register_builtin_infos();
430
437 void validate_filter_invariant(IPluginComponentInfo* info, const std::string& source_path,
438 std::function<void(const std::string&)> warn_cb);
439
444 static bool is_file_path(const std::string& str);
449 static bool is_shared_library(const std::string& filename);
453 static std::string get_library_directory();
454
459 static void* platform_load(const std::string& path);
463 static void platform_unload(void* handle) noexcept;
468 static void* platform_sym(void* handle, const char* sym) noexcept;
472 static std::string platform_error() noexcept;
473
474 // D2: the component catalogue's internals. Private statics because they
475 // use platform_* above; the catalogue state itself is file-local in
476 // PluginFactory.cpp. Callers must hold the catalogue mutex.
487 static bool catalog_adopt_locked(const std::string& path, void* handle);
492 static int catalog_scan_locked(const std::string& dir);
497 static void catalog_defaults_locked();
498};
499
500} /* namespace openswmm */
501
502#endif /* OPENSWMM_ENGINE_PLUGIN_FACTORY_HPP */
Interface for input file reader/writer plugins.
Definition IInputPlugin.hpp:61
Interface for output-writing plugins.
Definition IOutputPlugin.hpp:77
Describes a plugin component: metadata, capabilities, and factory methods.
Definition IPluginComponentInfo.hpp:201
Interface for report-writing plugins.
Definition IReportPlugin.hpp:63
Interface for state / hot-start IO plugins.
Definition IStateIOPlugin.hpp:71
IPluginComponentInfo * find_component(const std::string &id_or_path, std::function< void(const std::string &)> warn_cb={})
Resolve an exact registered ID/version or load an apparent platform library path.
Definition PluginFactory.cpp:384
int prepare_all(SimulationContext &ctx)
Prepare VALIDATED output and report plugins.
Definition PluginFactory.cpp:537
int validate_all(SimulationContext &ctx)
Validate INITIALIZED output/report/state-IO plugins in list order.
Definition PluginFactory.cpp:507
~PluginFactory()
Delete owned plugin instances before unloading per-instance IO libraries. Requires no outstanding upd...
Definition PluginFactory.cpp:138
IPluginComponentInfo * load_library(const std::string &path, std::function< void(const std::string &)> warn_cb={})
Load/register an IO plugin or offer a non-IO library to the global component catalogue.
Definition PluginFactory.cpp:309
static std::vector< ComponentLibraryRecord > component_libraries()
Snapshot process-global loaded/refused component records under its mutex.
Definition PluginFactory.cpp:959
PluginFactory(PluginFactory &&) noexcept=default
Move the registry, raw-pointer ownership lists and library entries.
void add_input_plugin(IInputPlugin *plugin)
Adopt an input plugin without lifecycle calls or validation.
Definition PluginFactory.cpp:637
void add_report_plugin(IReportPlugin *plugin, std::vector< std::string > args={}, IPluginComponentInfo *info=nullptr)
Adopt a report plugin without lifecycle calls or validation.
Definition PluginFactory.cpp:630
int write_summary_all(SimulationContext &ctx)
Ask every report plugin to write a summary without a state gate.
Definition PluginFactory.cpp:609
void discover(std::function< void(const std::string &)> warn_cb={})
Rescan the host library directory and its plugins/components subdirectories.
Definition PluginFactory.cpp:261
const std::vector< IReportPlugin * > & report_plugins() const noexcept
Borrow the retained capability list without transferring ownership.
Definition PluginFactory.hpp:331
bool empty() const noexcept
Test whether any capability instance is retained.
Definition PluginFactory.hpp:349
int load_plugins(const std::vector< PluginSpec > &specs, std::function< void(const std::string &)> warn_cb={}, const SimulationContext *io=nullptr)
Resolve specs and create every advertised input/output/report/state-IO capability.
Definition PluginFactory.cpp:439
void unload_all()
Delete all owned capability instances, clear registries/args and release IO library handles....
Definition PluginFactory.cpp:649
static int component_search_path_add(const std::string &dir)
Scan an explicit directory into the process-global component catalogue.
Definition PluginFactory.cpp:945
const std::vector< IStateIOPlugin * > & state_io_plugins() const noexcept
Borrow the retained capability list without transferring ownership.
Definition PluginFactory.hpp:336
void add_output_plugin(IOutputPlugin *plugin, std::vector< std::string > args={}, IPluginComponentInfo *info=nullptr)
Adopt a programmatically constructed output plugin without lifecycle calls.
Definition PluginFactory.cpp:623
PluginFactory(const PluginFactory &)=delete
Disable copying owned plugin pointers and library references.
int initialize_all(SimulationContext &ctx)
Initialize output, report and state-IO instances in that order.
Definition PluginFactory.cpp:489
int finalize_all(SimulationContext &ctx)
Finalize every output/report/state-IO plugin without a state gate.
Definition PluginFactory.cpp:585
const std::vector< IInputPlugin * > & input_plugins() const noexcept
Borrow the retained capability list without transferring ownership.
Definition PluginFactory.hpp:321
void add_state_io_plugin(IStateIOPlugin *plugin)
Adopt a state-IO plugin without lifecycle calls or validation.
Definition PluginFactory.cpp:641
const std::vector< IOutputPlugin * > & output_plugins() const noexcept
Borrow the retained capability list without transferring ownership.
Definition PluginFactory.hpp:326
static void component_catalog_reset_for_testing()
Close globally retained component handles and reset records/default-discovery state....
Definition PluginFactory.cpp:966
int update_all(const SimulationSnapshot &snapshot)
Deliver a snapshot to PREPARED/UPDATING output and report plugins.
Definition PluginFactory.cpp:560
static bool hydrocouple_enabled() noexcept
Report this build's optional HydroCouple component-loader support.
Definition PluginFactory.cpp:769
PluginFactory & operator=(const PluginFactory &)=delete
Disable copy assignment of plugin/library ownership.
int plugin_count() const noexcept
Count all injected/created capability instances.
Definition PluginFactory.hpp:342
std::vector< ComponentEntry > discovered_components() const
Snapshot registered metadata/capabilities in retained library order.
Definition PluginFactory.cpp:410
static std::string component_abi_stamp()
Copy the engine's HydroCouple component ABI fingerprint.
Definition PluginFactory.cpp:777
PluginFactory()
Register builtin metadata and scan the engine library directory plus plugins/components....
Definition PluginFactory.cpp:133
PluginType
Identifies a plugin capability.
Definition IPluginComponentInfo.hpp:77
Definition AtomicOutputFile.hpp:25
Definition ExchangeLaw.hpp:60
List all discovered component info entries.
Definition PluginFactory.hpp:162
std::string id
Definition PluginFactory.hpp:163
bool has_state_io
Definition PluginFactory.hpp:168
bool has_output
Definition PluginFactory.hpp:166
bool is_builtin
Definition PluginFactory.hpp:177
std::string version
Definition PluginFactory.hpp:164
bool has_input
Definition PluginFactory.hpp:165
IPluginComponentInfo * info
Definition PluginFactory.hpp:169
bool has_report
Definition PluginFactory.hpp:167
Definition PluginFactory.hpp:215
std::string version
Definition PluginFactory.hpp:219
std::string path
the file, as found
Definition PluginFactory.hpp:216
std::string kind
"model" | "other"; empty if refused
Definition PluginFactory.hpp:220
std::string id
IComponentInfo::id(); empty if refused
Definition PluginFactory.hpp:217
std::string load_error
empty on success; names both stamps on refusal
Definition PluginFactory.hpp:222
void * info
HydroCouple::IComponentInfo*, opaque here.
Definition PluginFactory.hpp:223
std::string caption
Definition PluginFactory.hpp:218
std::string stamp
the library's own stamp, or "unstamped(…)"
Definition PluginFactory.hpp:221
One plugin entry from the [PLUGINS] section.
Definition SimulationContext.hpp:136
Central, reentrant simulation context.
Definition SimulationContext.hpp:390
Complete simulation state snapshot at one output time step.
Definition SimulationSnapshot.hpp:123