hmbdc
simplify-high-performance-messaging-programming
Config.hpp
1 #include "hmbdc/Copyright.hpp"
2 #pragma once
3 
4 
5 #include <boost/property_tree/ptree.hpp>
6 #include <boost/property_tree/json_parser.hpp>
7 
8 #include <string>
9 #include <memory>
10 #include <list>
11 #include <vector>
12 #include <unordered_set>
13 #include <sstream>
14 
15 
16 /**
17  * @namespace hmbdc::app
18  * hmbdc's application layer where API resides
19  */
20 namespace hmbdc { namespace app {
21 
22 namespace config_detail {
23 
24 /**
25  * @brief class to hold an hmbdc configuration
26  * @details it is based on a two level (fallback and section) json.
27  * top level for the fallback values and lower level for the section specific values.
28  * a Config instance is always constructed to be associated to 0 or 1 specific section.
29  * shown below:
30  *
31  * {
32  * "parameter_1": "top level, value used as a fallback",
33  * "parameter_2": "fallback is used when not configured in a section",
34  * "section_A": {
35  * "parameter_2": "lower level, a specific value effective in section_A",
36  * "parameter_1": "a specific value effective in section_A"
37  * },
38  * "another_section": {
39  * "parameter_2": "a specific value effective in another_section"
40  * }
41  * }
42  * json array is not supported !!!
43  */
44 struct Config
45 : boost::property_tree::ptree {
46  // using ptree::ptree;
47  using Base = boost::property_tree::ptree;
48 
49  /**
50  * @brief empty config
51  */
52  Config(){}
53 
54  /**
55  * @brief copt ctor
56  */
57  Config(Config const& other)
58  : Base(other)
59  , section_(other.section_)
60  , fallbackConfig_(other.fallbackConfig_
61  ?new Config(*other.fallbackConfig_)
62  :nullptr)
63  {}
64 
65  /**
66  * @brief assignment
67  */
68  Config& operator = (Config const& other) {
69  (Base&)*this = other;
70  section_ = other.section_;
71  fallbackConfig_.reset(other.fallbackConfig_
72  ?new Config(*other.fallbackConfig_)
73  :nullptr);
74  return *this;
75  }
76 
77  /**
78  * @brief construct using stream, optionally specifying the section name
79  * @details if the section is nullptr, just use the fallback values. if
80  * the section name cannot be found, throw an exception
81  *
82  * @param is stream as input providing a json stream
83  * @param section pointing to the effective section in the json above
84  */
85  explicit
86  Config(std::istream&& is, char const* section = nullptr)
87  : section_(section?section:"") {
88  read_json(is, (Base&)*this);
89  get_child(section_);
90  }
91 
92  /**
93  * @brief construct using stream, optionally specifying the section name
94  * @details if the section is nullptr, just use the fallback values. if
95  * the section name cannot be found, throw an exception
96  *
97  * @param is stream as input providing a json stream
98  * @param section pointing to the effective section in the json above
99  */
100  explicit
101  Config(std::istream& is, char const* section = nullptr)
102  : section_(section?section:"") {
103  read_json(is, (Base&)*this);
104  get_child(section_);
105  }
106 
107  /**
108  * @brief construct using a string, optionally specifying the section name
109  * @details if the section is nullptr, just use the fallback values. if
110  * the section name cannot be found, throw an exception
111  *
112  * @param json string as input providing a json text
113  * @param section pointing to the effective section in the json above
114  */
115  explicit
116  Config(char const* json, char const* section = nullptr)
117  : Config(std::istringstream(json), section)
118  {}
119 
120  /**
121  * @brief construct using another ptree as fallbacks, optionally specifying the section name
122  * @details if the section is nullptr, just use the fallback values. if
123  * the section name cannot be found, throw an exception
124  *
125  * @param t ptree as input providing fallbacks (and sections)
126  * @param section pointing to the effective section in the ptree above
127  */
128  Config(boost::property_tree::ptree const& t, char const* section = nullptr)
129  : Base(t)
130  , section_(section?section:"") {
131  get_child(section_);
132  }
133 
134  /**
135  * @brief construct using a fallback ptree, and specify the section ptree
136  * @details if the section is nullptr, just use the fallback values. if
137  * the section name cannot be found, throw an exception
138  *
139  * @param dft ptree as input providing fallbacks
140  * @param section ptree providing section specific values
141  */
142  Config(boost::property_tree::ptree const& dft, boost::property_tree::ptree const& section)
143  : Base(section)
144  , fallbackConfig_(new Config(dft)) {
145  }
146 
147  /**
148  * @brief set additional defaults
149  * @details previously set defaults take precedence
150  *
151  * @param c a config holding configuration values
152  */
154  if (!fallbackConfig_) {
155  fallbackConfig_.reset(new Config(c));
156  } else {
157  fallbackConfig_->setAdditionalFallbackConfig(c);
158  }
159  }
160  /**
161  * @brief depracated
162  */
163  void setDefaultUserConfig(Config const& c) {
165  }
166 
167  /**
168  * @brief change section name
169  * @details the fallbacks also changes accordingly if possible
170  *
171  * @param section new section name
172  * @param sectionExists check if the section exist in current config
173  * - exlcuding fallbacks if set to true
174  * @return object itself
175  */
176  Config& resetSection(char const* section, bool sectionExists = true) {
177  if (sectionExists) get_child(section);
178  auto sec = section?section:"";
179  if (get_child_optional(sec)) section_ = sec; //else no change
180  if (fallbackConfig_) {
181  fallbackConfig_->resetSection(section, false);
182  }
183  return *this;
184  }
185 
186  /**
187  * @brief forward the call to ptree's put but return Configure
188  *
189  * @tparam Args whatever ptree put expect
190  * @param args whatever ptree put expect
191  * @return object itself
192  */
193  template <typename ...Args>
194  Config& put(Args&&... args) {
195  Base::put(std::forward<Args>(args)...);
196  return *this;
197  }
198 
199  /**
200  * @brief Gets the child from the config.
201  * @details check the section for it, if not found, try use fallback provided
202  * if still missing, search using the default user config values set by
203  * setDefaultUserConfig. Throw exception ptree_bad_path if all fail
204  *
205  * @param[in] param config parameter name
206  *
207  * @return ptree reference to the child.
208  */
209  boost::property_tree::ptree const& getChildExt(const path_type& param) {
210  auto sec = section_;
211  auto res = get_child_optional(sec/=param);
212  if (!res) {
213  res = get_child_optional(param);
214  if (!res) {
215  if (fallbackConfig_) {
216  return fallbackConfig_->getChildExt(param);
217  } else {
218  throw boost::property_tree::ptree_bad_path(
219  "invalid param and no default user Config set", param);
220  }
221  }
222  }
223  return *res;
224  }
225 
226  /**
227  * @brief get a value from the config
228  * @details check the section for it, if not found, try use fallback provided
229  * if still missing, search using the default user config values set by
230  * setDefaultUserConfig. If throwIfMissing, throw exception ptree_bad_path if all fail
231  *
232  * @param param config parameter name
233  * @param throwIfMissing - true if no config found, throw an exception; otherwise return empty
234  * @tparam T type of the value
235  * @return result
236  */
237  template <typename T>
238  T getExt(const path_type& param, bool throwIfMissing = true) const {
239  auto sec = section_;
240  auto res = get_optional<T>(sec/=param);
241  if (!res) {
242  res = get_optional<T>(param);
243  if (!res) {
244  if (fallbackConfig_) {
245  return fallbackConfig_->getExt<T>(param, throwIfMissing);
246  } else if (throwIfMissing) {
247  throw boost::property_tree::ptree_bad_path(
248  "invalid param and no default user Config set", param);
249  } else {
250  return T{};
251  }
252  }
253  }
254  return *res;
255  }
256 
257  /**
258  * @brief get a vector of value from the json array
259  * @details check the section for it, if not found, try use fallback provided
260  * if still missing, search using the default user config values set by
261  * setDefaultUserConfig.
262  *
263  * @param param config parameter name
264  * @param throwIfMissing - true if no config found, throw an exception; otherwise return empty
265  * @tparam T type of the value
266  * @return result
267  */
268  template <typename T>
269  std::vector<T> getArray(const path_type& param) const {
270  std::vector<T> res;
271  auto sec = section_;
272  auto children = get_child_optional(sec/=param);
273  if (!children) children = get_child_optional(param);
274  if (children) {
275  for (auto& v : *children) {
276  if (v.first.empty()) {
277  res.push_back(v.second.get_value<T>());
278  } else {
279  throw boost::property_tree::ptree_bad_data(
280  "param not pointing to array ", param);
281  }
282  }
283  return res;
284  } else if (fallbackConfig_) {
285  return fallbackConfig_->getArray<T>(param);
286  }
287  throw boost::property_tree::ptree_bad_path(
288  "invalid array param and no default user Config set", param);
289  }
290 
291  /**
292  * @brief get a number value in hex format
293  * @details check the section for it, if not found, try use fallback provided
294  * if still missing, search using the default user config values set by
295  * setDefaultUserConfig. Throw exception ptree_bad_path if all fail
296  *
297  * @param param config parameter name
298  * @tparam T numeric type of the value: int , uint64_t ...
299  * @return result
300  */
301  template <typename T>
302  T getHex(boost::property_tree::ptree::path_type const& param) const {
303  std::istringstream iss(getExt<std::string>(param));
304  T res;
305  iss >> std::hex >> res;
306  return res;
307  }
308 
309  /**
310  * @brief fill in a variable with a configured value retrieved using getExt
311  * @details example cfg(abc, "abc")(def, "def");
312  *
313  * @param to destination
314  * @param param config parameter
315  * @param throwIfMissing - true if no config found, throw an exception
316  *
317  * @return the Config object itself
318  */
319  template <typename T>
320  Config const& operator()(T& to, const boost::property_tree::ptree::path_type& param
321  , bool throwIfMissing = true) const {
322  to = getExt<T>(param, throwIfMissing);
323  return *this;
324  }
325 
326  /**
327  * @brief fill an unordered_set with a configured value retrieved using getExt
328  * @details the value in the Config is a space separated string
329  *
330  * @param to destination
331  * @param param config parameter
332  * @param throwIfMissing - true if no config found, throw an exception; otherwise ignored
333  *
334  * @return the Config object itself
335  */
336  template <typename T>
337  Config const& operator()(std::unordered_set<T>& to
338  , const boost::property_tree::ptree::path_type& param, bool throwIfMissing = true) const {
339  // auto toStr = getExt<string>(param);
340  auto s = getExt<std::string>(param, throwIfMissing);
341  std::istringstream iss(s);
342 
343  for (auto iit = std::istream_iterator<T>(iss)
344  ; iit != std::istream_iterator<T>()
345  ; iit ++) {
346  to.insert(*iit);
347  }
348  if (!iss.eof()) {
349  throw (boost::property_tree::ptree_bad_data(
350  "not space separated items in Config ", param));
351  }
352 
353  return *this;
354  }
355 
356  /**
357  * @brief fill an list with a configured value retrieved using getExt
358  * @details the value in the Config is a space separated string
359  *
360  * @param to destination
361  * @param param config parameter
362  * @param throwIfMissing - true if no config found, throw an exception; otherwise ignored
363  *
364  * @return the Config object itself
365  */
366  template <typename T>
367  Config const& operator()(std::vector<T>& to, const boost::property_tree::ptree::path_type& param
368  , bool throwIfMissing = true) const {
369  // auto toStr = getExt<string>(param);
370  auto s = getExt<std::string>(param, throwIfMissing);
371  std::istringstream iss(s);
372 
373  for (auto iit = std::istream_iterator<T>(iss)
374  ; iit != std::istream_iterator<T>()
375  ; iit ++) {
376  to.emplace_back(*iit);
377  }
378  if (!iss.eof()) {
379  throw (boost::property_tree::ptree_bad_data(
380  "not space separated items in Config ", param));
381  }
382 
383  return *this;
384  }
385 
386  /**
387  * @brief get contents of all the effective configure in the form of list of string pairs
388  * @details only effective ones are shown
389  *
390  * @param skipThese skip those config params
391  * @return list of string pairs in the original order of ptree nodes
392  */
393  std::list<std::pair<std::string, std::string>> content(
394  std::unordered_set<std::string> const& skipThese = std::unordered_set<std::string>()) const {
395  std::list<std::pair<std::string, std::string>> res;
396  std::unordered_set<std::string> history(skipThese);
397  auto secTree = get_child_optional(section_);
398  if (secTree) {
399  for (auto& p : *secTree) {
400  if (history.find(p.first) == history.end()) {
401  history.insert(p.first);
402  if (p.second.empty()) { //leaf
403  res.push_back(make_pair(p.first, p.second.get_value<std::string>()));
404  }
405  // else { //or array
406  // auto arrayText = getArrayText(p.second);
407  // if (arrayText.size()) {
408  // res.push_back(make_pair(p.first, arrayText));
409  // }
410  // }
411  }
412  }
413  }
414  for (auto& p : *this) {
415  if (history.find(p.first) == history.end()) {
416  history.insert(p.first);
417  if (p.second.empty()) { //leaf
418  res.push_back(make_pair(p.first, p.second.get_value<std::string>()));
419  }
420  // else { // or array
421  // auto arrayText = getArrayText(p.second);
422  // if (arrayText.size()) {
423  // res.push_back(make_pair(p.first, arrayText));
424  // }
425  // }
426  }
427  }
428  if (fallbackConfig_) {
429  auto more = fallbackConfig_->content(history);
430  res.insert(res.end(), more.begin(), more.end());
431  }
432  return res;
433  }
434 
435  /**
436  * @brief stream out the effective settings
437  *
438  * @param os ostream
439  * @param cfg The configuration
440  *
441  * @return os
442  */
443  friend std::ostream& operator << (std::ostream& os, Config const& cfg) {
444  for (auto& r : cfg.content()) {
445  os << r.first << '=' << r.second << std::endl;
446  }
447  if (cfg.fallbackConfig_) {
448  os << "next in line fallback" << std::endl;
449  os << *cfg.fallbackConfig_ << std::endl;
450  }
451 
452  return os;
453  }
454 
455 private:
456  // std::string getArrayText(ptree const& pt) const {
457  // bool isArray = true;
458  // std::string arrayText("[");
459  // for (auto& q : pt) {
460  // if (!q.second.empty() || q.first.size() != 0) {
461  // isArray = false;
462  // break;
463  // }
464  // arrayText += "\"" + q.second.get_value<std::string>() + "\",";
465  // }
466  // *arrayText.rbegin() = ']';
467  // if (isArray) {
468  // return arrayText;
469  // } else {
470  // return std::string();
471  // }
472  // }
473  boost::property_tree::ptree::path_type section_;
474  std::unique_ptr<Config> fallbackConfig_;
475 };
476 } //config_detail
477 
478 using Config = config_detail::Config;
479 }}
480 
std::vector< T > getArray(const path_type &param) const
get a vector of value from the json array
Definition: Config.hpp:269
T getExt(const path_type &param, bool throwIfMissing=true) const
get a value from the config
Definition: Config.hpp:238
Config(Config const &other)
copt ctor
Definition: Config.hpp:57
Config const & operator()(T &to, const boost::property_tree::ptree::path_type &param, bool throwIfMissing=true) const
fill in a variable with a configured value retrieved using getExt
Definition: Config.hpp:320
void setDefaultUserConfig(Config const &c)
depracated
Definition: Config.hpp:163
class to hold an hmbdc configuration
Definition: Config.hpp:44
void setAdditionalFallbackConfig(Config const &c)
set additional defaults
Definition: Config.hpp:153
Config(boost::property_tree::ptree const &t, char const *section=nullptr)
construct using another ptree as fallbacks, optionally specifying the section name ...
Definition: Config.hpp:128
Definition: TypedString.hpp:84
Config & resetSection(char const *section, bool sectionExists=true)
change section name
Definition: Config.hpp:176
Config const & operator()(std::vector< T > &to, const boost::property_tree::ptree::path_type &param, bool throwIfMissing=true) const
fill an list with a configured value retrieved using getExt
Definition: Config.hpp:367
Config const & operator()(std::unordered_set< T > &to, const boost::property_tree::ptree::path_type &param, bool throwIfMissing=true) const
fill an unordered_set with a configured value retrieved using getExt
Definition: Config.hpp:337
friend std::ostream & operator<<(std::ostream &os, Config const &cfg)
stream out the effective settings
Definition: Config.hpp:443
std::list< std::pair< std::string, std::string > > content(std::unordered_set< std::string > const &skipThese=std::unordered_set< std::string >()) const
get contents of all the effective configure in the form of list of string pairs
Definition: Config.hpp:393
Config(char const *json, char const *section=nullptr)
construct using a string, optionally specifying the section name
Definition: Config.hpp:116
Config(std::istream &&is, char const *section=nullptr)
construct using stream, optionally specifying the section name
Definition: Config.hpp:86
Config(std::istream &is, char const *section=nullptr)
construct using stream, optionally specifying the section name
Definition: Config.hpp:101
Config(boost::property_tree::ptree const &dft, boost::property_tree::ptree const &section)
construct using a fallback ptree, and specify the section ptree
Definition: Config.hpp:142
Config & operator=(Config const &other)
assignment
Definition: Config.hpp:68
Config & put(Args &&... args)
forward the call to ptree&#39;s put but return Configure
Definition: Config.hpp:194
Config()
empty config
Definition: Config.hpp:52
boost::property_tree::ptree const & getChildExt(const path_type &param)
Gets the child from the config.
Definition: Config.hpp:209
Definition: Base.hpp:12
T getHex(boost::property_tree::ptree::path_type const &param) const
get a number value in hex format
Definition: Config.hpp:302