Cantera 2.6.0
Delegator.h
Go to the documentation of this file.
1//! @file Delegator.h
2
3// This file is part of Cantera. See License.txt in the top-level directory or
4// at https://cantera.org/license.txt for license and copyright information.
5
6#ifndef CT_DELEGATOR_H
7#define CT_DELEGATOR_H
8
11#include <array>
12
13namespace Cantera
14{
15
16//! Delegate member functions of a C++ class to externally-specified functions
17/*!
18 * This base class provides functions for setting delegates for the member
19 * functions of a C++ class at runtime. The purpose of this capability is to
20 * allow the class to be extended using functions defined in any programming
21 * language that provides a C API for calling functions in that language.
22 *
23 * Delegates are specified as `std::function` objects that are responsible for
24 * encapsulating the data specific to the target language and calling the
25 * appropriate function in the target language. The `std::function` has a
26 * modified function signature compared to the method that it is replacing or
27 * augmenting:
28 * - Methods with no return value and scalar arguments are treated the same
29 * - Methods with a return value have that value as the first reference argument
30 * of their delegate function, and return an `int`. The delegate should return
31 * zero if it does not set the arguments value, and a non-zero value if it
32 * does.
33 * - Methods with pointers to arrays as arguments have an additional argument
34 * introduced to indicate the length of each array argument. This argument
35 * occurs either first, or after the return value reference, and is a
36 * `std::array<size_t, N>` where N is the number of array arguments.
37 *
38 * Delegated methods can be specified to either "replace" the original class's
39 * method, or to run "before" or "after" the original method, using the `when`
40 * parameter of the `setDelegate` method. There are two special cases for delegates of
41 * methods with return values:
42 * - If the "before" delegate specifies a value for the return parameter (and then
43 * returns with a non-zero status value), this value will be returned and the original
44 * method will not be called. Otherwise, the original method will be called and its
45 * value will be returned.
46 * - If an "after" delegate specifies a return value (and returns with a non-zero
47 * status value), this value will be added to the value returned by the original
48 * method, and this combined result will be then be returned. The meaning of "added"
49 * is determined by the `+` operator for the return type, for example addition for
50 * numeric types or concatenation for strings.
51 *
52 * ## Implementation for each delegated function type
53 *
54 * Several functions and member variables are defined in this base class for each
55 * distinct function signature that can be delegated by a derived class such as
56 * ReactorDelegator. These are:
57 * - The `install` function stores the address of a function that will be called by
58 * a derived delegator class in the corresponding `m_funcs_...` map, and sets the
59 * default implementation of this method (which should be to call the base class
60 * implementation). For delegates with a return type, a copy of this default
61 * implementation is also stored in the corresponding `m_base_...` map
62 * - The `setDelegate` function wraps the user-provided delegate in a function that
63 * handles whether the delegate is to be called before, after, or instead of the base
64 * class's implementation. This function is then stored at the address specified in
65 * the corresponding `m_funcs_...` map.
66 * - `m_funcs_...` is a mapping between member function names and the addresses
67 * of the functions that will be called to implement these functions in the derived
68 * delegator class.
69 * - `m_base_...` is a mapping between member function names and the default
70 * implementations of these member functions, for delegates with return values
71 *
72 * Additional implementation for each function type is specific to the programming
73 * language that the delegate is written in. For the Python delegates, see additional
74 * documentation in `delegator.pyx`.
75 *
76 * ## Implementation for specific delegated functions
77 *
78 * Beyond the implementation of particular function signatures, there are no elements
79 * of the Delegator class that are specific to individual delegated functions, which
80 * are handled by derived classes such as ReactorDelegator, which will also inherit from
81 * another base class such as Reactor.
82 *
83 * Delegation of a member function (for example, `Reactor::eval`) is handled by several
84 * elements:
85 * - A `std::function` member variable to hold a delegate function, or its default
86 * implementation. This function type includes the additional `std::array` argument
87 * of array sizes for functions with array arguments.
88 * - An override of the member function whose implementation calls the stored delegate.
89 * For delegates that need an array of array sizes, this function first calculates the
90 * necessary values and passes them as an additional argument to the delegate.
91 * - A call to `install` from the constructor of the derived delegator class, which
92 * takes the member function name, a reference to the `std::function` member variable
93 * described above, and a lambda function that implements the default behavior, that
94 * is, calling the equivalent base class method.
95 *
96 * Additional implementation for each function is specific to the programming language
97 * that the delegate is written in. For Python delegates, see additional documentation
98 * in `delegator.pyx`.
99 */
101{
102public:
103 //! Set delegates for member functions with the signature `void()`.
104 void setDelegate(const std::string& name, const std::function<void()>& func,
105 const std::string& when)
106 {
107 if (!m_funcs_v.count(name)) {
108 throw NotImplementedError("Delegator::setDelegate",
109 "for function named '{}' with signature 'void()'.", name);
110 }
111 *m_funcs_v[name] = makeDelegate(func, when, *m_funcs_v[name]);
112 }
113
114 //! set delegates for member functions with the signature `void(bool)`
115 void setDelegate(const std::string& name, const std::function<void(bool)>& func,
116 const std::string& when)
117 {
118 if (!m_funcs_v_b.count(name)) {
119 throw NotImplementedError("Delegator::setDelegate",
120 "for function named '{}' with signature 'void(bool)'.", name);
121 }
122 *m_funcs_v_b[name] = makeDelegate(func, when, *m_funcs_v_b[name]);
123 }
124
125 //! set delegates for member functions with the signature `void(double)`
126 void setDelegate(const std::string& name, const std::function<void(double)>& func,
127 const std::string& when)
128 {
129 if (!m_funcs_v_d.count(name)) {
130 throw NotImplementedError("Delegator::setDelegate",
131 "for function named '{}' with signature 'void(double)'.", name);
132 }
133 *m_funcs_v_d[name] = makeDelegate(func, when, *m_funcs_v_d[name]);
134 }
135
136 //! Set delegates for member functions with the signature `void(double*)`
137 void setDelegate(const std::string& name,
138 const std::function<void(std::array<size_t, 1>, double*)>& func,
139 const std::string& when)
140 {
141 if (!m_funcs_v_dp.count(name)) {
142 throw NotImplementedError("Delegator::setDelegate",
143 "for function named '{}' with signature 'void(double*)'.", name);
144 }
145 *m_funcs_v_dp[name] = makeDelegate(func, when, *m_funcs_v_dp[name]);
146 }
147
148 //! Set delegates for member functions with the signature `void(double, double*)`
150 const std::string& name,
151 const std::function<void(std::array<size_t, 1>, double, double*)>& func,
152 const std::string& when)
153 {
154 if (!m_funcs_v_d_dp.count(name)) {
155 throw NotImplementedError("Delegator::setDelegate",
156 "for function named '{}' with signature 'void(double, double*)'.",
157 name);
158 }
159 *m_funcs_v_d_dp[name] = makeDelegate(func, when, *m_funcs_v_d_dp[name]);
160 }
161
162 //! Set delegates for member functions with the signature
163 //! `void(double, double*, double*)`
165 const std::string& name,
166 const std::function<void(std::array <size_t, 2>, double, double*, double*)>& func,
167 const std::string& when)
168 {
169 if (!m_funcs_v_d_dp_dp.count(name)) {
170 throw NotImplementedError("Delegator::setDelegate",
171 "for function named '{}' with signature "
172 "'void(double, double*, double*)'.", name);
173 }
174 *m_funcs_v_d_dp_dp[name] = makeDelegate(func, when, *m_funcs_v_d_dp_dp[name]);
175 }
176
177 //! Set delegates for member functions with the signature
178 //! `void(double*, double*, double*)`
180 const std::string& name,
181 const std::function<void(std::array<size_t, 3>, double*, double*, double*)>& func,
182 const std::string& when)
183 {
184 if (!m_funcs_v_dp_dp_dp.count(name)) {
185 throw NotImplementedError("Delegator::setDelegate",
186 "for function named '{}' with signature "
187 "'void(double*, double*, double*)'.", name);
188 }
189 *m_funcs_v_dp_dp_dp[name] = makeDelegate(func, when, *m_funcs_v_dp_dp_dp[name]);
190 }
191
192 //! Set delegates for member functions with the signature `string(size_t)`
193 void setDelegate(const std::string& name,
194 const std::function<int(std::string&, size_t)>& func,
195 const std::string& when)
196 {
197 if (!m_funcs_s_sz.count(name)) {
198 throw NotImplementedError("Delegator::setDelegate",
199 "for function named '{}' with signature "
200 "'string(size_t)'.", name);
201 }
202 *m_funcs_s_sz[name] = makeDelegate(func, when, m_base_s_sz[name]);
203 }
204
205 //! Set delegates for member functions with the signature `size_t(string)`
206 void setDelegate(const std::string& name,
207 const std::function<int(size_t&, const std::string&)>& func,
208 const std::string& when)
209 {
210 if (!m_funcs_sz_csr.count(name)) {
211 throw NotImplementedError("Delegator::setDelegate",
212 "for function named '{}' with signature "
213 "'size_t(const string&)'.", name);
214 }
215 *m_funcs_sz_csr[name] = makeDelegate(func, when, m_base_sz_csr[name]);
216 }
217
218protected:
219 //! Install a function with the signature `void()` as being delegatable
220 void install(const std::string& name, std::function<void()>& target,
221 const std::function<void()>& func)
222 {
223 target = func;
224 m_funcs_v[name] = &target;
225 }
226
227 //! Install a function with the signature `void(bool)` as being delegatable
228 void install(const std::string& name, std::function<void(bool)>& target,
229 const std::function<void(bool)>& func)
230 {
231 target = func;
232 m_funcs_v_b[name] = &target;
233 }
234
235 //! Install a function with the signature `void(double)` as being delegatable
236 void install(const std::string& name, std::function<void(double)>& target,
237 const std::function<void(double)>& func)
238 {
239 target = func;
240 m_funcs_v_d[name] = &target;
241 }
242
243 //! Install a function with the signature `void(double*)` as being delegatable
244 void install(const std::string& name,
245 std::function<void(std::array<size_t, 1>, double*)>& target,
246 const std::function<void(std::array<size_t, 1>, double*)>& func)
247 {
248 target = func;
249 m_funcs_v_dp[name] = &target;
250 }
251
252 //! Install a function with the signature `void(double, double*)` as being delegatable
253 void install(const std::string& name,
254 std::function<void(std::array<size_t, 1>, double, double*)>& target,
255 const std::function<void(std::array<size_t, 1>, double, double*)>& func)
256 {
257 target = func;
258 m_funcs_v_d_dp[name] = &target;
259 }
260
261 //! Install a function with the signature `void(double, double*, double*)` as being
262 //! delegatable
263 void install(const std::string& name,
264 std::function<void(std::array<size_t, 2>, double, double*, double*)>& target,
265 const std::function<void(std::array<size_t, 2>, double, double*, double*)>& func)
266 {
267 target = func;
268 m_funcs_v_d_dp_dp[name] = &target;
269 }
270
271 //! Install a function with the signature
272 //! `void(double*, double*, double*)` as being delegatable
273 void install(const std::string& name,
274 std::function<void(std::array<size_t, 3>, double*, double*, double*)>& target,
275 const std::function<void(std::array<size_t, 3>, double*, double*, double*)>& base)
276 {
277 target = base;
278 m_funcs_v_dp_dp_dp[name] = &target;
279 }
280
281 //! Install a function with the signature `string(size_t)` as being delegatable
282 void install(const std::string& name,
283 std::function<std::string(size_t)>& target,
284 const std::function<std::string(size_t)>& base)
285 {
286 target = base;
287 m_funcs_s_sz[name] = &target;
288 m_base_s_sz[name] = base;
289 }
290
291 //! Install a function with the signature `size_t(string)` as being delegatable
292 void install(const std::string& name,
293 std::function<size_t(const std::string&)>& target,
294 const std::function<size_t(const std::string&)>& base)
295 {
296 target = base;
297 m_funcs_sz_csr[name] = &target;
298 m_base_sz_csr[name] = base;
299 }
300
301 //! Create a delegate for a function with no return value
302 template <typename BaseFunc, class ... Args>
303 std::function<void(Args ...)> makeDelegate(
304 const std::function<void(Args ...)>& func,
305 const std::string& when,
306 BaseFunc base)
307 {
308 if (when == "before") {
309 return [base, func](Args ... args) {
310 func(args ...);
311 base(args ...);
312 };
313 } else if (when == "after") {
314 return [base, func](Args ... args) {
315 base(args ...);
316 func(args ...);
317 };
318 } else if (when == "replace") {
319 return [func](Args ... args) {
320 func(args ...);
321 };
322 } else {
323 throw CanteraError("Delegator::makeDelegate",
324 "'when' must be one of 'before', 'after', or 'replace';"
325 " not '{}", when);
326 }
327 }
328
329 //! Create a delegate for a function with a return value
330 template <typename ReturnType, class ... Args>
331 std::function<ReturnType(Args ...)> makeDelegate(
332 const std::function<int(ReturnType&, Args ...)>& func,
333 const std::string& when,
334 const std::function<ReturnType(Args ...)>& base)
335 {
336 if (when == "before") {
337 return [base, func](Args ... args) {
338 // Call the provided delegate first. If it sets the return
339 // value, return that, otherwise return the value from the
340 // original method
341 ReturnType ret;
342 int done = func(ret, args ...);
343 if (done) {
344 return ret;
345 } else {
346 return base(args ...);
347 }
348 };
349 } else if (when == "after") {
350 return [base, func](Args ... args) {
351 // Add the value returned by the original method and the
352 // provided delegate
353 ReturnType ret1 = base(args ...);
354 ReturnType ret2;
355 int done = func(ret2, args ...);
356 if (done) {
357 return ret1 + ret2;
358 } else {
359 return ret1;
360 }
361 };
362 } else if (when == "replace") {
363 return [base, func](Args ... args) {
364 ReturnType ret;
365 int has_ret = func(ret, args ...);
366 if (!has_ret) {
367 throw CanteraError("Lambda generated by Delegator::makeDelegate",
368 "Delegate for function of type '{}'\ndid not return a value",
369 demangle(typeid(base)));
370 }
371 return ret;
372 };
373 } else {
374 throw CanteraError("Delegator::makeDelegate",
375 "'when' must be one of 'before', 'after', or 'replace';"
376 " not '{}", when);
377 }
378 }
379
380 //! @name Containers for delegates and base class implementations.
381 //!
382 //! Maps named `m_funcs_*` hold the current delegate. For functions with a return
383 //! value, `m_base_*` holds a pointer to the base class's implementation of the
384 //! function.
385 //!
386 //! These containers use a naming scheme based on the signature of the corresponding
387 //! member functions. Following the prefix `_m_funcs_` is a notation of the return
388 //! type, followed by an underscore, then the notations for each argument, separated
389 //! by underscores. The following shorthand is used for different return / argument
390 //! types:
391 //!
392 //! - `v` for `void`
393 //! - `b` for `bool`
394 //! - `d` for `double`
395 //! - `s` for `std::string`
396 //! - `sz` for `size_t`
397 //! - prefix `c` for `const` arguments
398 //! - suffix `r` for reference arguments
399 //! - suffix `p` for pointer arguments
400 //! @{
401
402 // Delegates with no return value
403 std::map<std::string, std::function<void()>*> m_funcs_v;
404 std::map<std::string, std::function<void(bool)>*> m_funcs_v_b;
405 std::map<std::string, std::function<void(double)>*> m_funcs_v_d;
406 std::map<std::string,
407 std::function<void(std::array<size_t, 1>, double*)>*> m_funcs_v_dp;
408 std::map<std::string,
409 std::function<void(std::array<size_t, 1>, double, double*)>*> m_funcs_v_d_dp;
410 std::map<std::string,
411 std::function<void(std::array<size_t, 2>, double, double*, double*)>*> m_funcs_v_d_dp_dp;
412 std::map<std::string,
413 std::function<void(std::array<size_t, 3>, double*, double*, double*)>*> m_funcs_v_dp_dp_dp;
414
415 // Delegates with a return value
416 std::map<std::string,
417 std::function<std::string(size_t)>> m_base_s_sz;
418 std::map<std::string,
419 std::function<std::string(size_t)>*> m_funcs_s_sz;
420
421 std::map<std::string,
422 std::function<size_t(const std::string&)>> m_base_sz_csr;
423 std::map<std::string,
424 std::function<size_t(const std::string&)>*> m_funcs_sz_csr;
425 //! @}
426};
427
428}
429
430#endif
Base class for exceptions thrown by Cantera classes.
Definition: ctexceptions.h:61
Delegate member functions of a C++ class to externally-specified functions.
Definition: Delegator.h:101
void install(const std::string &name, std::function< void(std::array< size_t, 3 >, double *, double *, double *)> &target, const std::function< void(std::array< size_t, 3 >, double *, double *, double *)> &base)
Install a function with the signature void(double*, double*, double*) as being delegatable.
Definition: Delegator.h:273
void setDelegate(const std::string &name, const std::function< void(std::array< size_t, 2 >, double, double *, double *)> &func, const std::string &when)
Set delegates for member functions with the signature void(double, double*, double*)
Definition: Delegator.h:164
void install(const std::string &name, std::function< void()> &target, const std::function< void()> &func)
Install a function with the signature void() as being delegatable.
Definition: Delegator.h:220
void setDelegate(const std::string &name, const std::function< int(size_t &, const std::string &)> &func, const std::string &when)
Set delegates for member functions with the signature size_t(string)
Definition: Delegator.h:206
void setDelegate(const std::string &name, const std::function< void(double)> &func, const std::string &when)
set delegates for member functions with the signature void(double)
Definition: Delegator.h:126
void install(const std::string &name, std::function< void(double)> &target, const std::function< void(double)> &func)
Install a function with the signature void(double) as being delegatable.
Definition: Delegator.h:236
void install(const std::string &name, std::function< size_t(const std::string &)> &target, const std::function< size_t(const std::string &)> &base)
Install a function with the signature size_t(string) as being delegatable.
Definition: Delegator.h:292
void setDelegate(const std::string &name, const std::function< void()> &func, const std::string &when)
Set delegates for member functions with the signature void().
Definition: Delegator.h:104
std::function< void(Args ...)> makeDelegate(const std::function< void(Args ...)> &func, const std::string &when, BaseFunc base)
Create a delegate for a function with no return value.
Definition: Delegator.h:303
void install(const std::string &name, std::function< void(std::array< size_t, 2 >, double, double *, double *)> &target, const std::function< void(std::array< size_t, 2 >, double, double *, double *)> &func)
Install a function with the signature void(double, double*, double*) as being delegatable.
Definition: Delegator.h:263
void setDelegate(const std::string &name, const std::function< void(std::array< size_t, 1 >, double, double *)> &func, const std::string &when)
Set delegates for member functions with the signature void(double, double*)
Definition: Delegator.h:149
void setDelegate(const std::string &name, const std::function< void(bool)> &func, const std::string &when)
set delegates for member functions with the signature void(bool)
Definition: Delegator.h:115
void install(const std::string &name, std::function< void(std::array< size_t, 1 >, double *)> &target, const std::function< void(std::array< size_t, 1 >, double *)> &func)
Install a function with the signature void(double*) as being delegatable.
Definition: Delegator.h:244
void install(const std::string &name, std::function< void(std::array< size_t, 1 >, double, double *)> &target, const std::function< void(std::array< size_t, 1 >, double, double *)> &func)
Install a function with the signature void(double, double*) as being delegatable.
Definition: Delegator.h:253
void setDelegate(const std::string &name, const std::function< void(std::array< size_t, 3 >, double *, double *, double *)> &func, const std::string &when)
Set delegates for member functions with the signature void(double*, double*, double*)
Definition: Delegator.h:179
void install(const std::string &name, std::function< std::string(size_t)> &target, const std::function< std::string(size_t)> &base)
Install a function with the signature string(size_t) as being delegatable.
Definition: Delegator.h:282
void install(const std::string &name, std::function< void(bool)> &target, const std::function< void(bool)> &func)
Install a function with the signature void(bool) as being delegatable.
Definition: Delegator.h:228
void setDelegate(const std::string &name, const std::function< void(std::array< size_t, 1 >, double *)> &func, const std::string &when)
Set delegates for member functions with the signature void(double*)
Definition: Delegator.h:137
std::function< ReturnType(Args ...)> makeDelegate(const std::function< int(ReturnType &, Args ...)> &func, const std::string &when, const std::function< ReturnType(Args ...)> &base)
Create a delegate for a function with a return value.
Definition: Delegator.h:331
void setDelegate(const std::string &name, const std::function< int(std::string &, size_t)> &func, const std::string &when)
Set delegates for member functions with the signature string(size_t)
Definition: Delegator.h:193
An error indicating that an unimplemented function has been called.
Definition: ctexceptions.h:187
Definitions for the classes that are thrown when Cantera experiences an error condition (also contain...
This file contains definitions for utility functions and text for modules, inputfiles,...
Namespace for the Cantera kernel.
Definition: AnyMap.h:29
std::string demangle(const std::type_info &type)
Convert a type name to a human readable string, using boost::core::demangle if available.
Definition: global.cpp:297