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