include/pops/runtime/program/module_metadata.hpp Source FileΒΆ

adc_cpp: include/pops/runtime/program/module_metadata.hpp Source File
adc_cpp 0.3.0
Model-free C++23 core for coupled hyperbolic-elliptic systems on adaptive (AMR) meshes, with MPI and GPU (Kokkos) backends
module_metadata.hpp
Go to the documentation of this file.
1#ifndef POPS_RUNTIME_PROGRAM_MODULE_METADATA_HPP
2#define POPS_RUNTIME_PROGRAM_MODULE_METADATA_HPP
3
4// GeneratedModule metadata (Spec 2 / ADC-442). A combined model+program ``problem.so`` carries,
5// alongside ``GeneratedProgram`` (the installed step), a ``GeneratedModule`` descriptor: the typed
6// operator registry the Python codegen emits (pops.time.Program._emit_module_metadata) as a set of
7// ``extern "C"`` accessors. This header reads that descriptor from an already-dlopen'd handle, for
8// INTROSPECTION and install-time requirement validation. It is read ONCE at install; the step body
9// never touches it, so operators stay inlined and there is NO string lookup in any hot kernel.
10//
11// Backward compatible: a .so generated before Spec 2 exports no ``pops_module_*`` symbols, so
12// read_module_metadata returns ``present == false`` and the caller simply skips module introspection.
13#include <cstdint>
14#include <string>
15#include <utility>
16#include <vector>
17
18#include <dlfcn.h>
19
20namespace pops {
21namespace runtime {
22namespace program {
23
27using OperatorId = std::uint32_t;
28
30using SpaceId = std::uint32_t;
31
34 OperatorId id = 0;
35 std::string name;
36 std::string kind;
37 std::string signature;
38 std::string requirements;
39};
40
44 bool present = false;
45 std::vector<OperatorMetadata> operators;
46 std::vector<std::string> state_spaces;
47 std::vector<std::string> field_spaces;
48
50 const OperatorMetadata* find(const std::string& name) const {
51 for (const auto& op : operators) {
52 if (op.name == name) {
53 return &op;
54 }
55 }
56 return nullptr;
57 }
58};
59
60namespace detail {
61
63inline std::string module_str(void* handle, const char* symbol, int i) {
64 using Fn = const char* (*)(int);
65 // dlsym yields a void*; the cast to a function pointer is the standard (and only) idiom.
66 auto* fn = reinterpret_cast<Fn>(dlsym(handle, symbol)); // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast)
67 if (fn == nullptr) {
68 return std::string();
69 }
70 const char* s = fn(i);
71 return s != nullptr ? std::string(s) : std::string();
72}
73
75inline std::vector<std::string> module_names(void* handle, const char* count_symbol,
76 const char* name_symbol) {
77 std::vector<std::string> out;
78 using CountFn = int (*)();
79 auto* count = reinterpret_cast<CountFn>(dlsym(handle, count_symbol)); // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast)
80 if (count == nullptr) {
81 return out;
82 }
83 const int n = count();
84 for (int i = 0; i < n; ++i) {
85 out.push_back(module_str(handle, name_symbol, i));
86 }
87 return out;
88}
89
90} // namespace detail
91
95inline ModuleMetadata read_module_metadata(void* dl_handle) {
96 ModuleMetadata meta;
97 if (dl_handle == nullptr) {
98 return meta;
99 }
100 using CountFn = int (*)();
101 auto* count = reinterpret_cast<CountFn>( // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast)
102 dlsym(dl_handle, "pops_module_operator_count"));
103 if (count == nullptr) {
104 return meta; // pre-Spec-2 .so: no GeneratedModule descriptor
105 }
106 meta.present = true;
107 const int n = count();
108 if (n > 0) {
109 meta.operators.reserve(static_cast<std::size_t>(n));
110 }
111 for (int i = 0; i < n; ++i) {
113 op.id = static_cast<OperatorId>(i);
114 op.name = detail::module_str(dl_handle, "pops_module_operator_name", i);
115 op.kind = detail::module_str(dl_handle, "pops_module_operator_kind", i);
116 op.signature = detail::module_str(dl_handle, "pops_module_operator_signature", i);
117 op.requirements = detail::module_str(dl_handle, "pops_module_operator_requirements", i);
118 meta.operators.push_back(std::move(op));
119 }
120 meta.state_spaces =
121 detail::module_names(dl_handle, "pops_module_state_space_count", "pops_module_state_space_name");
122 meta.field_spaces =
123 detail::module_names(dl_handle, "pops_module_field_space_count", "pops_module_field_space_name");
124 return meta;
125}
126
134inline std::vector<std::string> required_string_list(const std::string& requirements_json,
135 const std::string& key) {
136 std::vector<std::string> out;
137 const std::size_t k = requirements_json.find(key);
138 if (k == std::string::npos) {
139 return out;
140 }
141 const std::size_t lb = requirements_json.find('[', k + key.size());
142 if (lb == std::string::npos) {
143 return out;
144 }
145 const std::size_t rb = requirements_json.find(']', lb);
146 if (rb == std::string::npos) {
147 return out;
148 }
149 std::size_t p = lb + 1;
150 while (p < rb) {
151 const std::size_t q1 = requirements_json.find('"', p);
152 if (q1 == std::string::npos || q1 >= rb) {
153 break;
154 }
155 const std::size_t q2 = requirements_json.find('"', q1 + 1);
156 if (q2 == std::string::npos || q2 > rb) {
157 break;
158 }
159 out.push_back(requirements_json.substr(q1 + 1, q2 - q1 - 1));
160 p = q2 + 1;
161 }
162 return out;
163}
164
170inline std::string requirement_string(const std::string& requirements_json, const std::string& key) {
171 auto is_space = [](char c) { return c == ' ' || c == '\t' || c == '\n' || c == '\r'; };
172 // @p key is the quoted JSON key (e.g. "\"solver\""). Match it as a genuine KEY, not as an array
173 // element or a value substring: the first non-space char before it must be '{' or ',', and the
174 // first non-space char after it must be ':'. (Without this, an aux field literally named "solver"
175 // -- {"aux":["solver"],...} -- or any value equal to the key would yield a bogus requirement and
176 // wrongly reject a valid install.) Scan all occurrences until one is a real key.
177 std::size_t k = requirements_json.find(key);
178 while (k != std::string::npos) {
179 std::size_t before = k;
180 while (before > 0 && is_space(requirements_json[before - 1])) {
181 --before;
182 }
183 const bool key_start =
184 before == 0 || requirements_json[before - 1] == '{' || requirements_json[before - 1] == ',';
185 std::size_t after = k + key.size();
186 while (after < requirements_json.size() && is_space(requirements_json[after])) {
187 ++after;
188 }
189 if (key_start && after < requirements_json.size() && requirements_json[after] == ':') {
190 const std::size_t q1 = requirements_json.find('"', after + 1);
191 if (q1 == std::string::npos) {
192 return std::string();
193 }
194 const std::size_t q2 = requirements_json.find('"', q1 + 1);
195 if (q2 == std::string::npos) {
196 return std::string();
197 }
198 return requirements_json.substr(q1 + 1, q2 - q1 - 1);
199 }
200 k = requirements_json.find(key, k + 1);
201 }
202 return std::string();
203}
204
207inline std::vector<std::string> required_aux(const std::string& requirements_json) {
208 return required_string_list(requirements_json, "\"aux\"");
209}
210
214inline std::vector<std::string> required_blocks(const std::string& requirements_json) {
215 return required_string_list(requirements_json, "\"block\"");
216}
217
222inline std::string required_solver(const std::string& requirements_json) {
223 return requirement_string(requirements_json, "\"solver\"");
224}
225
226} // namespace program
227} // namespace runtime
228} // namespace pops
229
230#endif // POPS_RUNTIME_PROGRAM_MODULE_METADATA_HPP
std::string module_str(void *handle, const char *symbol, int i)
Call a const char* (int) accessor at index i; empty string if the symbol is absent.
Definition module_metadata.hpp:63
std::vector< std::string > module_names(void *handle, const char *count_symbol, const char *name_symbol)
Read a (count, name) string table (state/field spaces) from the handle.
Definition module_metadata.hpp:75
std::uint32_t OperatorId
Integer id of an operator within a module: its registration index.
Definition module_metadata.hpp:27
std::string required_solver(const std::string &requirements_json)
Solver name a field operator requires (the scalar "solver" value), e.g.
Definition module_metadata.hpp:222
std::vector< std::string > required_aux(const std::string &requirements_json)
Aux-field names an operator requires (the "aux" array).
Definition module_metadata.hpp:207
ModuleMetadata read_module_metadata(void *dl_handle)
Read the GeneratedModule metadata from an already-dlopen'd problem.so dl_handle.
Definition module_metadata.hpp:95
std::string requirement_string(const std::string &requirements_json, const std::string &key)
Read a single quoted JSON string value keyed by key inside the operator's flat requirements JSON,...
Definition module_metadata.hpp:170
std::vector< std::string > required_string_list(const std::string &requirements_json, const std::string &key)
Collect the quoted tokens of a JSON string array keyed by key inside the operator's flat requirements...
Definition module_metadata.hpp:134
std::uint32_t SpaceId
Integer id of a state or field space within a module.
Definition module_metadata.hpp:30
std::vector< std::string > required_blocks(const std::string &requirements_json)
Block-instance names an operator requires (the "block" array), e.g.
Definition module_metadata.hpp:214
Definition amr_hierarchy.hpp:29
The GeneratedModule descriptor read from a problem.so.
Definition module_metadata.hpp:43
bool present
Definition module_metadata.hpp:44
std::vector< std::string > field_spaces
Definition module_metadata.hpp:47
std::vector< std::string > state_spaces
Definition module_metadata.hpp:46
std::vector< OperatorMetadata > operators
Definition module_metadata.hpp:45
const OperatorMetadata * find(const std::string &name) const
The operator with this name, or nullptr if none.
Definition module_metadata.hpp:50
One operator's metadata, as exported by the .so.
Definition module_metadata.hpp:33
std::string requirements
JSON, e.g. {"kind":"local_source","aux":["grad_x","grad_y"]}.
Definition module_metadata.hpp:38
std::string name
Definition module_metadata.hpp:35
std::string signature
human-readable typed signature
Definition module_metadata.hpp:37
OperatorId id
Definition module_metadata.hpp:34
std::string kind
one of the Spec-2 operator kinds (local_rate, field_operator, ...)
Definition module_metadata.hpp:36