GCC Code Coverage Report


Directory: ./
File: src/iguana/algorithms/Algorithm.h
Date: 2025-01-05 09:03:17
Exec Total Coverage
Lines: 13 14 92.9%
Functions: 7 8 87.5%
Branches: 6 12 50.0%

Line Branch Exec Source
1 #pragma once
2
3 #include <mutex>
4 #include <optional>
5 #include <set>
6
7 #include <hipo4/bank.h>
8
9
10 #include "iguana/algorithms/AlgorithmBoilerplate.h"
11 #include "iguana/services/YAMLReader.h"
12 #include <iguana/services/GlobalParam.h>
13
14 namespace iguana {
15
16 /// Option value variant type
17 /* NOTE: if you modify this, you also must modify:
18 * - [ ] `PrintOptionValue`
19 * - [ ] Template specializations in this class
20 * - [ ] Template specializations in `YAMLReader` or `ConfigFileReader`, and `ConcurrentParam`
21 * - [ ] Add new tests, if you added new types
22 */
23 using option_t = std::variant<
24 int,
25 double,
26 std::string,
27 std::vector<int>,
28 std::vector<double>,
29 std::vector<std::string>>;
30
31 /// @brief Base class for all algorithms to inherit from
32 ///
33 /// This is the base class for all algorithms. It provides common members, such as
34 /// a logger instance and options data structure. Algorithm implementations must:
35 /// - inherit from this base class
36 /// - override the methods `Algorithm::Start`, `Algorithm::Run` and `Algorithm::Stop`
37 ///
38 /// See existing algorithms for examples.
39 class Algorithm : public Object
40 {
41
42 public:
43
44 /// @param name the unique name for a derived class instance
45 72 Algorithm(std::string_view name)
46 72 : Object(name)
47 72 , m_rows_only(false)
48
1/2
✓ Branch 0 taken 72 times.
✗ Branch 1 not taken.
72 , m_default_config_file("")
49
1/2
✓ Branch 0 taken 72 times.
✗ Branch 1 not taken.
72 , o_user_config_file("")
50
2/4
✓ Branch 0 taken 72 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 72 times.
✗ Branch 3 not taken.
144 , o_user_config_dir("")
51 72 {}
52 144 virtual ~Algorithm() {}
53
54 /// @brief Initialize this algorithm before any events are processed, with the intent to process _banks_
55 ///
56 /// use this method if you intend to use `Algorithm::Run`.
57 /// @param banks the list of banks this algorithm will use, so that `Algorithm::Run` can cache the indices
58 /// of the banks that it needs
59 virtual void Start(hipo::banklist& banks) = 0;
60
61 /// @brief Initialize this algorithm before any events are processed, with the intent to process _bank rows_ rather than full banks;
62 ///
63 /// use this method if you intend to use "action functions" instead of `Algorithm::Run`.
64 void Start();
65
66 /// @brief Run this algorithm for an event.
67 /// @param banks the list of banks to process
68 virtual void Run(hipo::banklist& banks) const = 0;
69
70 /// @brief Finalize this algorithm after all events are processed.
71 virtual void Stop() = 0;
72
73 /// Set an option specified by the user. If the option name is `"log"`, the log level of the `Logger`
74 /// owned by this algorithm will be changed to the specified value.
75 /// @param key the name of the option
76 /// @param val the value to set
77 /// @returns the value that has been set (if needed, _e.g._, when `val` is an rvalue)
78 template <typename OPTION_TYPE>
79 55 OPTION_TYPE SetOption(std::string const& key, const OPTION_TYPE val)
80 {
81 // FIXME: this template is not specialized, to be friendlier to python `cppyy` bindings
82
2/2
✓ Branch 0 taken 29 times.
✓ Branch 1 taken 26 times.
55 if(key == "log") {
83 if constexpr(std::disjunction<
84 std::is_same<OPTION_TYPE, std::string>,
85 std::is_same<OPTION_TYPE, char const*>,
86 std::is_same<OPTION_TYPE, Logger::Level>>::value)
87 29 m_log->SetLevel(val);
88 else
89 m_log->Error("Option '{}' must be a string or a Logger::Level", key);
90 }
91 52 m_option_cache[key] = val;
92 55 return val;
93 }
94
95 /// Get the value of a scalar option
96 /// @param key the unique key name of this option, for caching; if empty, the option will not be cached
97 /// @param node_path the `YAML::Node` identifier path to search for this option in the config files; if empty, it will just use `key`
98 /// @returns the scalar option
99 template <typename OPTION_TYPE>
100 OPTION_TYPE GetOptionScalar(std::string const& key, YAMLReader::node_path_t node_path = {}) const;
101
102 /// Get the value of a vector option
103 /// @param key the unique key name of this option, for caching; if empty, the option will not be cached
104 /// @param node_path the `YAML::Node` identifier path to search for this option in the config files; if empty, it will just use `key`
105 /// @returns the vector option
106 template <typename OPTION_TYPE>
107 std::vector<OPTION_TYPE> GetOptionVector(std::string const& key, YAMLReader::node_path_t node_path = {}) const;
108
109 /// Get the value of a vector option, and convert it to `std::set`
110 /// @param key the unique key name of this option
111 /// @param node_path the `YAML::Node` identifier path to search for this option in the config files; if empty, it will just use `key`
112 /// @returns the vector option converted to `std::set`
113 template <typename OPTION_TYPE>
114 std::set<OPTION_TYPE> GetOptionSet(std::string const& key, YAMLReader::node_path_t node_path = {}) const;
115
116 /// Set the name of this algorithm
117 /// @param name the new name
118 void SetName(std::string_view name);
119
120 /// Get a reference to this algorithm's configuration (`YAMLReader`)
121 /// @returns the configuration
122 std::unique_ptr<YAMLReader> const& GetConfig() const;
123
124 /// Set a custom `YAMLReader` to use for this algorithm
125 /// @param yaml_config the custom `YAMLReader` instance
126 void SetConfig(std::unique_ptr<YAMLReader>&& yaml_config);
127
128 /// Set a custom configuration file for this algorithm
129 /// @see `Algorithm::SetConfigDirectory`
130 /// @param name the configuration file name
131 void SetConfigFile(std::string const& name);
132
133 /// Set a custom configuration file directory for this algorithm
134 /// @see `Algorithm::SetConfigFile`
135 /// @param name the directory name
136 void SetConfigDirectory(std::string const& name);
137
138 protected: // methods
139
140 /// Parse YAML configuration files. Sets `m_yaml_config`.
141 void ParseYAMLConfig();
142
143 /// Get the reference to a bank from a `hipo::banklist`; optionally checks if the bank name matches the expectation
144 /// @param banks the `hipo::banklist` from which to get the specified bank
145 /// @param idx the index of `banks` of the specified bank
146 /// @param expected_bank_name if specified, checks that the specified bank has this name
147 /// @return a reference to the bank
148 hipo::bank& GetBank(hipo::banklist& banks, hipo::banklist::size_type const idx, std::string const& expected_bank_name = "") const noexcept(false);
149
150 /// Get the index of a bank in a `hipo::banklist`; throws an exception if the bank is not found
151 /// @param banks the list of banks this algorithm will use
152 /// @param bank_name the name of the bank
153 /// returns the `hipo::banklist` index of the bank
154 hipo::banklist::size_type GetBankIndex(hipo::banklist& banks, std::string const& bank_name) const noexcept(false);
155
156 /// Create a new bank and push it to the bank list
157 /// @param [out] banks the `hipo::banklist` onto which the new bank will be pushed
158 /// @param [out] bank_idx will be set to the `hipo::banklist` index of the new bank
159 /// @param [in] bank_name the new bank name
160 /// @param [in] schema_def a list of variables for the schema
161 /// @param [in] group_id the group ID for the schema
162 /// @param [in] item_id the item ID for the schema
163 /// @returns the bank's schema
164 hipo::schema CreateBank(
165 hipo::banklist& banks,
166 hipo::banklist::size_type& bank_idx,
167 std::string const& bank_name,
168 std::vector<std::string> schema_def,
169 int group_id, // FIXME: generalize group_id and item_id setting
170 int item_id) const noexcept(false);
171
172 /// Dump all banks in a `hipo::banklist`
173 /// @param banks the banks to show
174 /// @param message if specified, print a header message
175 /// @param level the log level
176 void ShowBanks(hipo::banklist& banks, std::string_view message = "", Logger::Level const level = Logger::trace) const;
177
178 /// Dump a single bank
179 /// @param bank the bank to show
180 /// @param message if specified, print a header message
181 /// @param level the log level
182 void ShowBank(hipo::bank& bank, std::string_view message = "", Logger::Level const level = Logger::trace) const;
183
184 /// Get an option from the option cache
185 /// @param key the key name associated with this option
186 /// @returns the option value, if found (using `std::optional`)
187 template <typename OPTION_TYPE>
188 std::optional<OPTION_TYPE> GetCachedOption(std::string const& key) const;
189
190 private: // methods
191
192 /// Prepend `node_path` with the full algorithm name. If `node_path` is empty, set it to `{key}`.
193 /// @param key the key name for this option
194 /// @param node_path the `YAMLReader::node_path_t` to prepend
195 void CompleteOptionNodePath(std::string const& key, YAMLReader::node_path_t& node_path) const;
196
197 // PrintOptionValue: overloaded for different value types
198 void PrintOptionValue(std::string const& key, int const& val, Logger::Level const level = Logger::debug, std::string_view prefix = "OPTION") const;
199 void PrintOptionValue(std::string const& key, double const& val, Logger::Level const level = Logger::debug, std::string_view prefix = "OPTION") const;
200 void PrintOptionValue(std::string const& key, std::string const& val, Logger::Level const level = Logger::debug, std::string_view prefix = "OPTION") const;
201 void PrintOptionValue(std::string const& key, std::vector<int> const& val, Logger::Level const level = Logger::debug, std::string_view prefix = "OPTION") const;
202 void PrintOptionValue(std::string const& key, std::vector<double> const& val, Logger::Level const level = Logger::debug, std::string_view prefix = "OPTION") const;
203 void PrintOptionValue(std::string const& key, std::vector<std::string> const& val, Logger::Level const level = Logger::debug, std::string_view prefix = "OPTION") const;
204
205 protected: // members
206
207 /// Class name of this algorithm
208 std::string m_class_name;
209
210 /// If true, algorithm can only operate on bank _rows_; `Algorithm::GetBank`, and therefore `Algorithm::Run`, cannot be called
211 bool m_rows_only;
212
213 /// Default configuration file name
214 std::string m_default_config_file;
215
216 /// User's configuration file name, which may override the default configuration file, `m_default_config_file`.
217 /// Set it with `Algorithm::SetConfigFile`
218 std::string o_user_config_file;
219
220 /// User's configuration file directory.
221 /// Set it with `Algorithm::SetConfigDirectory`
222 std::string o_user_config_dir;
223
224 /// A mutex for this algorithm
225 mutable std::mutex m_mutex;
226
227 private: // members
228
229 /// YAML reader
230 std::unique_ptr<YAMLReader> m_yaml_config;
231
232 /// Data structure to hold configuration options set by `Algorithm::SetOption`
233 std::unordered_map<std::string, option_t> m_option_cache;
234
235 };
236
237 //////////////////////////////////////////////////////////////////////////////
238
239 /// Algorithm pointer type
240 using algo_t = std::unique_ptr<Algorithm>;
241
242 /// @brief Factory to create an algorithm.
243 class AlgorithmFactory
244 {
245
246 public:
247
248 /// Algorithm creator function type
249 using algo_creator_t = std::function<algo_t()>;
250
251 AlgorithmFactory() = delete;
252
253 /// Register an algorithm with a unique name. Algorithms register themselves by calling this function.
254 /// @param name the name of the algorithm (not equivalent to `Object::m_name`)
255 /// @param creator the creator function
256 /// @param new_banks if this algorithm creates *new* banks, list them here
257 /// @returns true if the algorithm has not yet been registered
258 static bool Register(std::string const& name, algo_creator_t creator, std::vector<std::string> const new_banks = {}) noexcept;
259
260 /// Create an algorithm. Throws an exception if the algorithm cannot be created
261 /// @param name the name of the algorithm, which was used as an argument in the `AlgorithmFactory::Register` call
262 /// @returns the algorithm instance
263 static algo_t Create(std::string const& name) noexcept(false);
264
265 /// Check if a bank is created by an algorithm
266 /// @param bank_name the name of the bank
267 /// @returns the list of algorithms which create it, if any
268 static std::optional<std::vector<std::string>> QueryNewBank(std::string const& bank_name) noexcept;
269
270 private:
271
272 /// Association between the algorithm names and their creators
273 static std::unordered_map<std::string, algo_creator_t> s_creators;
274
275 /// Association between a created bank and its creator algorithms
276 static std::unordered_map<std::string, std::vector<std::string>> s_created_banks;
277 };
278 }
279