ifw-daq  2.1.0-pre1
IFW Data Acquisition modules
manager.hpp
Go to the documentation of this file.
1 /**
2  * @file
3  * @ingroup daq_config
4  * @copyright (c) Copyright ESO 2022
5  * All Rights Reserved
6  * ESO (eso.org) is an Intergovernmental Organisation, and therefore special legal conditions apply.
7  *
8  * @brief daq::config::Manager and associated types.
9  */
10 #ifndef DAQ_CONFIG_MANAGER_HPP
11 #define DAQ_CONFIG_MANAGER_HPP
12 
13 #include <iosfwd>
14 #include <string>
15 #include <unordered_map>
16 
17 #include <fmt/format.h>
18 #include <log4cplus/logger.h>
19 #include <log4cplus/loggingmacros.h>
20 
21 namespace daq::config {
22 
23 /**
24  * Configuration origins in descending priority.
25  *
26  * This is used to indicate from where a configuration parameter originates as well as its
27  * configuration priority. Higher or equal priority (lower number) replace lower or equal priority
28  * origins.
29  */
30 enum class Origin {
31  /**
32  * Runtime change via e.g. request/reply command.
33  */
34  Runtime = 0,
35  /**
36  * Command line argument.
37  */
38  CommandLine,
39  /**
40  * Configuration file.
41  */
42  Configuration,
43  /**
44  * Environment variable.
45  */
46  EnvironmentVariable,
47  /**
48  * Built-in default value.
49  */
50  Default,
51 };
52 
53 /**
54  * Format Origin
55  *
56  * @param os output stream.
57  * @param origin Origin to format.
58  */
59 std::ostream& operator<<(std::ostream& os, Origin origin);
60 
61 /**
62  * Immutable information about a configuration attribute.
63  */
64 struct Metadata {
65  std::string canonical_name;
66  std::string description;
67 };
68 
69 /**
70  * Mutable metadata about a configuration attribute that describes where a value comes from.
71  */
72 struct OriginInfo {
74  /**
75  * May include additional information like which configuration file was used.
76  */
77  std::string description = "";
78 };
79 
80 /**
81  * Format OriginInfo
82  *
83  * @param os output stream.
84  * @param info OriginInfo to format.
85  */
86 std::ostream& operator<<(std::ostream& os, OriginInfo const& info);
87 
88 /**
89  * Maintains the associativity of configuration attributes with metadata and value origin/priority.
90  *
91  * Main responsibilities:
92  * - Maintain associativity between configuration attribute and metaata.
93  * - Implement update logic in a way that configuration can only be overwritten if origin has a
94  * higher priority.
95  *
96  * Outside the scope:
97  * - Implement parsing of various configuration origins.
98  *
99  * Manager works on the following assumptions and principles:
100  *
101  * - There is a 1:1 relationship between configuration class and manager.
102  * - Configuration point identity is the member pointer to configuration class member variable.
103  */
104 template <class C>
105 class Manager {
106 public:
107  /**
108  * Constructor
109  *
110  * @param config Configuration instance to manage. Object must outlive Manager.
111  * @param logger Where to log to.
112  */
113  Manager(C* config, log4cplus::Logger const& logger) : m_config(config), m_logger(logger) {
114  }
115 
116  /**
117  * Registers a configuration parameter/attribute.
118  *
119  * The pointer address of C acts as the identifer and that object must outlive Manager.
120  *
121  * @param ptr Configuration member pointer.
122  * @param meta Metadata associated with attribute.
123  * @throws std::exception if `ptr` has already been registered before or if allocation fails.
124  */
125  template <class AttrType>
126  void Register(AttrType C::*ptr, Metadata const& meta);
127 
128  /**
129  * Update configuration value taking into account the origin of the change.
130  *
131  * @param ptr Configuration member pointer.
132  * @param value new configuration value.
133  * @param origin Specifies origin of configuration value.
134  * @returns true if configuration was changed false otherwise.
135  * @throws std::exception if configuration member has not been registered before.
136  */
137  template <class AttrType, class T>
138  bool Update(AttrType C::*ptr, T const& value, OriginInfo const& origin);
139 
140  /**
141  * Describes current value.
142  */
143  template <class AttrType>
144  struct CurrentValue {
145  AttrType const& value;
148  };
149 
150  /**
151  * Get current configuration value and associated metadata and origin
152  * @param ptr Configuration member pointer.
153  * @throws std::exception if configuration is unknown.
154  */
155  template <class AttrType>
156  CurrentValue<AttrType> Get(AttrType C::*ptr) const;
157 
158 private:
159  struct State {
160  Metadata metadata;
161  OriginInfo origin;
162  };
163  struct Key {};
164 
165  C* m_config;
166  log4cplus::Logger m_logger;
167  // Type erased registered attributes
168  std::unordered_map<void*, State> m_update;
169 };
170 
171 template <class C>
172 template <class AttrType>
173 void Manager<C>::Register(AttrType C::*ptr, Metadata const& m) {
174  // Note pointer to member cannot be converted to void*, so we store the actual data pointer to
175  // the instance provided in constructor. This works equally well as we only care about using
176  // &C::member syntax as an identity.
177  void* vptr = &((*m_config).*ptr);
178  if (auto it = m_update.find(vptr); it != m_update.end()) {
179  throw std::invalid_argument("Attribute already registered");
180  }
181  m_update.insert(std::make_pair(vptr, State{m}));
182 }
183 
184 template <class C>
185 template <class AttrType>
186 typename Manager<C>::template CurrentValue<AttrType> Manager<C>::Get(AttrType C::*ptr) const {
187  // Note pointer to member cannot be converted to void*, so we store the actual data pointer to
188  // the instance provided in constructor. This works equally well as we only care about using
189  // &C::member syntax as an identity.
190  void* vptr = &((*m_config).*ptr);
191  if (auto it = m_update.find(vptr); it == m_update.end()) {
192  throw std::invalid_argument("Attribute not previously registered");
193  } else {
194  return CurrentValue<AttrType>{(*m_config).*ptr, it->second.origin, it->second.metadata};
195  }
196 }
197 
198 template <class C>
199 template <class AttrType, class T>
200 bool Manager<C>::Update(AttrType C::*ptr, T const& value, OriginInfo const& new_origin) {
201  void* vptr = &((*m_config).*ptr);
202  if (auto it = m_update.find(vptr); it == m_update.end()) {
203  throw std::invalid_argument("Attribute not previously registered");
204  } else {
205  // Keep old value and assign new before logging as T may not be the same as AttrType
206  auto old_value = (*m_config).*ptr;
207  ((*m_config).*ptr) = value;
208  auto const& new_value = (*m_config).*ptr;
209  auto const& meta = it->second.metadata;
210  auto const& old_origin = it->second.origin;
211 
212  if (new_origin.origin <= it->second.origin.origin) {
213  // Same or higher priority origin -> update!
214  LOG4CPLUS_INFO(m_logger,
215  fmt::format("Updating configuration value for <{}> from "
216  "old value {} ({}) -> new value {} ({})",
217  meta.canonical_name,
218  old_value,
219  old_origin,
220  new_value,
221  new_origin));
222  it->second.origin = new_origin;
223  return true;
224  } else {
225  // Current value came from higher priority origin!
226  LOG4CPLUS_DEBUG(m_logger,
227  fmt::format("Will _not_ update configuration value for <{}> from "
228  "old value {} (origin {}) -> new value {} (origin {}) as "
229  "current origin has higher priority",
230  meta.canonical_name,
231  old_value,
232  old_origin,
233  new_value,
234  new_origin));
235  return false;
236  }
237  }
238 }
239 
240 } // namespace daq::config
241 #endif // DAQ_CONFIG_MANAGER_HPP
daq::State
State
Observable states of the data acquisition process.
Definition: state.hpp:39
daq::config::Metadata::description
std::string description
Definition: manager.hpp:66
daq::config::Metadata::canonical_name
std::string canonical_name
Definition: manager.hpp:65
daq::config::OriginInfo
Mutable metadata about a configuration attribute that describes where a value comes from.
Definition: manager.hpp:72
daq::config::Manager::Register
void Register(AttrType C::*ptr, Metadata const &meta)
Registers a configuration parameter/attribute.
Definition: manager.hpp:173
daq::config::Manager::CurrentValue::origin
OriginInfo const & origin
Definition: manager.hpp:146
daq::config::Manager::CurrentValue
Describes current value.
Definition: manager.hpp:144
daq::config::Manager::CurrentValue::metadata
Metadata const & metadata
Definition: manager.hpp:147
daq::config::operator<<
std::ostream & operator<<(std::ostream &os, Origin origin)
Format Origin.
Definition: manager.cpp:16
daq::config::Manager::Update
bool Update(AttrType C::*ptr, T const &value, OriginInfo const &origin)
Update configuration value taking into account the origin of the change.
Definition: manager.hpp:200
daq::config::Manager::Manager
Manager(C *config, log4cplus::Logger const &logger)
Constructor.
Definition: manager.hpp:113
daq::config::Manager::Get
CurrentValue< AttrType > Get(AttrType C::*ptr) const
Get current configuration value and associated metadata and origin.
daq::config::Origin::Runtime
@ Runtime
Runtime change via e.g.
daq::config::Origin
Origin
Configuration origins in descending priority.
Definition: manager.hpp:30
daq::config::Manager::CurrentValue::value
AttrType const & value
Definition: manager.hpp:145
daq::Origin
Describes parsed origin string into its components "host" and "path".
Definition: dpSpec.hpp:77
daq::config::OriginInfo::description
std::string description
May include additional information like which configuration file was used.
Definition: manager.hpp:77
daq::config::Manager
Maintains the associativity of configuration attributes with metadata and value origin/priority.
Definition: manager.hpp:105
daq::config::Metadata
Immutable information about a configuration attribute.
Definition: manager.hpp:64
daq::config::OriginInfo::origin
Origin origin
Definition: manager.hpp:73
daq::config
Definition: manager.cpp:14