Falaise  4.0.1
SuperNEMO Software Toolkit
module.h
Go to the documentation of this file.
1 //! \file falaise/snemo/processing/module.h
2 #ifndef FALAISE_SNEMO_PROCESSING_MODULE_H
3 #define FALAISE_SNEMO_PROCESSING_MODULE_H
4 
5 #include <exception>
6 #include <type_traits>
7 
10 #include <bayeux/dpp/base_module.h>
11 
13 
14 namespace falaise {
15 namespace processing {
16 //! Enumeration for module to indicate processing success/failure/other
18 
19 //! Exception thrown if configuration overwrites reserved keys
20 class reserved_key_error : public std::logic_error {
21  using std::logic_error::logic_error;
22 };
23 
24 //! Exception thrown if module fails configuration
25 class configuration_error : public std::logic_error {
26  using std::logic_error::logic_error;
27 };
28 
29 //! \brief A DPP module wrapping a simple processing algorithm
30 /*!
31  * Modules in Falaise's DPP pipeline are concrete classes of @ref dpp::base_module.
32  * Authors of modules need to write a significant amount of boilerplate code
33  * for the initialize/reset/destruction member functions/states *required* by
34  * @ref dpp::base_module. This class template automatically generates these state transitions
35  * for the templated type `T`. This type only needs to implement
36  * constructors for initialization (via configuration properties) and a `process` member function
37  * in which the event data will be processed.
38  *
39  * The type to be wrapped must meet the requirements:
40  *
41  * - `DefaultConstructible`
42  * - `Construtible` with `T(falaise::config::property_set const&, datatools::service_manager&)
43  * - `CopyAssignable`
44  * - Has a member function with signature
45  * ```cpp
46  * falaise::processing::status process(datatools::things& d)
47  * ```
48  *
49  * For example:
50  *
51  * ```cpp
52  * class MyModule
53  * {
54  * MyModule(); // ideally this is =default
55  * MyModule(falaise::config::property_set const& config, datatools::service_manager& services);
56  *
57  * falaise::processing::status process(datatools::things& data);
58  * };
59  * ```
60  *
61  * The user defined constructor must throw if initialization fails.
62  *
63  * The class is wrapped and registered with Falaise's DPP plugin system
64  * by calling the macro #FALAISE_REGISTER_MODULE(T)
65  * in the module's implementation file, e.g.
66  *
67  * ```cpp
68  * // This is MyModule.cpp
69  * #include "falaise/snemo/processing/module.h"
70  * class MyModule
71  * {
72  * ... as above ...
73  * };
74  *
75  * FALAISE_REGISTER_MODULE(MyModule)
76  * ```
77  *
78  * The module will be registered with a string key equal to the typename
79  * so that use in pipeline scripts is transparent, e.g.
80  *
81  * ```ini
82  * [name="theLabel" type="MyModule"]
83  * ... config ...
84  * ```
85  *
86  * \sa dpp::base_module
87  */
88 template <typename T>
89 class module : public dpp::base_module {
90  static_assert(std::is_default_constructible<T>::value, "T must be default constructible");
91  static_assert(
92  std::is_constructible<T, falaise::config::property_set const&, datatools::service_manager&>::value,
93  "T must have a constructor T(datatools::properties const&, "
94  "datatools::services const&)");
95  // static_assert(has process member function)
96  public:
97  //! Destructor
98  /*!
99  * Moves state to uninitialized as required by dpp::base_module
100  */
101  virtual ~module() { reset(); }
102 
103  //! Change module state to unitialized
104  void reset() override { _set_initialized(false); }
105 
106  //! Initialize the module
107  /*!
108  * Constructs an instance of T using its user defined constructor,
109  * passing it the module configuration, and services parameters.
110  * \param config Parameters passed to configure the module
111  * \param services Reference to running service provider
112  */
114  dpp::module_handle_dict_type&) override {
115  // Inject required parameters, throwing if config sets these
116  if (config.has_key("module_label")) {
117  throw reserved_key_error("reserved key 'module_name' passed to module '" + get_name() + "'");
118  }
119  if (config.has_key("module_type")) {
120  throw reserved_key_error("reserved key 'module_type' passed to module '" + get_name() + "'");
121  }
122 
123  falaise::config::property_set module_config{config};
124  module_config.put("module_label", get_name());
125  module_config.put("module_type", factory.get_type_id());
126 
127  try {
128  wrappedModule = T(module_config, services);
129  }
130  catch (const falaise::config::missing_key_error& e) {
131  std::ostringstream oss{};
132  oss << "initialization of module '" << get_name() <<"' (type '" << factory.get_type_id() << "') failed with exception:\n"
133  << "- missing_key_error: " << e.what() << "\n";
134  config.tree_dump(oss, "- config:");
135  throw configuration_error{oss.str()};
136  }
137  catch (const falaise::config::wrong_type_error& e) {
138  std::ostringstream oss{};
139  oss << "initialization of module '" << get_name() <<"' (type '" << factory.get_type_id() << "') failed with exception:\n"
140  << "- wrong_type_error: " << e.what() << "\n";
141  config.tree_dump(oss, "- config:");
142  throw configuration_error{oss.str()};
143  }
144  catch (const std::exception& e) {
145  std::ostringstream oss{};
146  oss << "initialization of module '" << get_name() <<"' (type '" << factory.get_type_id() << "') failed with exception:\n"
147  << "- <unknown>: " << e.what() << "\n";
148  config.tree_dump(oss, "- config:");
149  throw configuration_error{oss.str()};
150  }
151 
152  _set_initialized(true);
153  }
154 
155  //! Process the input data
156  /*!
157  * The data is passed to the process member function of the wrapped type
158  * \param data Reference to the input data
159  * \return An enum reflecting the success/failure/other of the processing
160  */
161  status process(datatools::things& data) override { return wrappedModule.process(data); };
162 
163  private:
164  T wrappedModule{}; //! Implementation of the processing algorithm
165 
166  // T has to be a friend otherwise it cannot register itself
167  friend T;
169  static WrapperFactory factory;
170 };
171 
172 } // namespace processing
173 } // namespace falaise
174 
175 //! \def FALAISE_REGISTER_MODULE(T)
176 /*! Registers T with the module manager
177  * \param T Typename to register
178  */
179 #define FALAISE_REGISTER_MODULE(T) \
180  namespace falaise { \
181  namespace processing { \
182  template <> \
183  module<T>::WrapperFactory module<T>::factory{#T}; \
184  } \
185  }
186 
187 #endif // FALAISE_SNEMO_PROCESSING_MODULE_H
Exception thrown if module fails configuration.
Definition: module.h:25
Definition: property_set.h:28
Exception thrown when requesting a key that is not in the property_set.
Definition: property_set.h:17
virtual void tree_dump(std::ostream &out_=std::clog, const std::string &title_="", const std::string &indent_="", bool inherit_=false) const
void _set_initialized(bool initialized_)
A DPP module wrapping a simple processing algorithm.
Definition: module.h:89
const std::string & get_name() const
void reset() override
Change module state to unitialized.
Definition: module.h:104
bool has_key(const std::string &prop_key_) const
std::map< std::string, module_entry_type > module_handle_dict_type
Definition: metadata_utils.h:35
Exception thrown if configuration overwrites reserved keys.
Definition: module.h:20
Class holding a set of key-value properties.
Definition: property_set.h:189
status process(datatools::things &data) override
Process the input data.
Definition: module.h:161
const std::string & get_type_id() const
void initialize(datatools::properties const &config, datatools::service_manager &services, dpp::module_handle_dict_type &) override
Initialize the module.
Definition: module.h:113
virtual ~module()
Destructor.
Definition: module.h:101