GCC Code Coverage Report


Directory: ./
File: src/iguana/algorithms/Algorithm.h
Date: 2025-06-06 22:09:53
Exec Total Coverage
Lines: 14 15 93.3%
Functions: 7 8 87.5%
Branches: 14 52 26.9%

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