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