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  section_ = section?section:"";
181  if (fallbackConfig_) {
182  fallbackConfig_->resetSection(section, false);
183  }
184  return *this;
185  }
186 
187  /**
188  * @brief Gets the child from the config.
189  * @details check the section for it, if not found, try use fallback provided
190  * if still missing, search using the default user config values set by
191  * setDefaultUserConfig. Throw exception ptree_bad_path if all fail
192  *
193  * @param[in] param config parameter name
194  *
195  * @return ptree reference to the child.
196  */
197  ptree const& getChildExt(const path_type& param) {
198  auto sec = section_;
199  auto res = get_child_optional(sec/=param);
200  if (!res) {
201  res = get_child_optional(param);
202  if (!res) {
203  if (fallbackConfig_) {
204  return fallbackConfig_->getChildExt(param);
205  } else {
206  throw ptree_bad_path("invalid param and no default user Config set", param);
207  }
208  }
209  }
210  return *res;
211  }
212 
213  /**
214  * @brief get a value from the config
215  * @details check the section for it, if not found, try use fallback provided
216  * if still missing, search using the default user config values set by
217  * setDefaultUserConfig. Throw exception ptree_bad_path if all fail
218  *
219  * @param param config parameter name
220  * @param throwIfMissing - true if no config found, throw an exception; otherwise return empty
221  * @tparam T type of the value
222  * @return result
223  */
224  template <typename T>
225  T getExt(const path_type& param, bool throwIfMissing = true) const {
226  auto sec = section_;
227  auto res = get_optional<T>(sec/=param);
228  if (!res) {
229  res = get_optional<T>(param);
230  if (!res) {
231  if (fallbackConfig_) {
232  return fallbackConfig_->getExt<T>(param, throwIfMissing);
233  } else if (throwIfMissing) {
234  throw ptree_bad_path("invalid param and no default user Config set", param);
235  } else {
236  return T{};
237  }
238  }
239  }
240  return *res;
241  }
242 
243  /**
244  * @brief get a number value in hex format
245  * @details check the section for it, if not found, try use fallback provided
246  * if still missing, search using the default user config values set by
247  * setDefaultUserConfig. Throw exception ptree_bad_path if all fail
248  *
249  * @param param config parameter name
250  * @tparam T numeric type of the value: int , uint64_t ...
251  * @return result
252  */
253  template <typename T>
254  T getHex(ptree::path_type const& param) const {
255  istringstream iss(getExt<string>(param));
256  T res;
257  iss >> hex >> res;
258  return res;
259  }
260 
261  /**
262  * @brief fill in a variable with a configured value retrieved using getExt
263  * @details example cfg(abc, "abc")(def, "def");
264  *
265  * @param to destination
266  * @param param config parameter
267  *
268  * @return the Config object itself
269  */
270  template <typename T>
271  Config const& operator()(T& to, const path_type& param) const {
272  to = getExt<T>(param);
273  return *this;
274  }
275 
276  /**
277  * @brief fill an unordered_set with a configured value retrieved using getExt
278  * @details the value in the Config is a space separated string
279  *
280  * @param to destination
281  * @param param config parameter
282  *
283  * @return the Config object itself
284  */
285  template <typename T>
286  Config const& operator()(std::unordered_set<T>& to, const path_type& param) const {
287  // auto toStr = getExt<string>(param);
288  auto s = getExt<string>(param);
289  istringstream iss(s);
290 
291  for (auto iit = istream_iterator<T>(iss)
292  ; iit != istream_iterator<T>()
293  ; iit ++) {
294  to.insert(*iit);
295  }
296  if (!iss.eof()) {
297  throw (ptree_bad_data("not space separated items in Config ", param));
298  }
299 
300  return *this;
301  }
302 
303  /**
304  * @brief fill an list with a configured value retrieved using getExt
305  * @details the value in the Config is a json array
306  *
307  * @param to destination
308  * @param param config parameter
309  *
310  * @return the Config object itself
311  */
312  template <typename T>
313  Config const& operator()(std::vector<T>& to, const path_type& param) const {
314  // auto toStr = getExt<string>(param);
315  auto s = getExt<string>(param);
316  istringstream iss(s);
317 
318  for (auto iit = istream_iterator<T>(iss)
319  ; iit != istream_iterator<T>()
320  ; iit ++) {
321  to.emplace_back(*iit);
322  }
323  if (!iss.eof()) {
324  throw (ptree_bad_data("not space separated items in Config ", param));
325  }
326 
327  return *this;
328  }
329 
330  /**
331  * @brief get contents of all the effective configure in the form of list of string pairs
332  * @details only effective ones are shown
333  *
334  * @param skipThese skip those config params
335  * @return list of string pairs in the original order of ptree nodes
336  */
337  list<pair<string, string>> content(
338  unordered_set<string> const& skipThese = unordered_set<string>()) const {
339  list<pair<string, string>> res;
340  unordered_set<string> history(skipThese);
341  auto secTree = get_child_optional(section_);
342  if (secTree) {
343  for (auto& p : *secTree) {
344  if (history.find(p.first) == history.end()) {
345  history.insert(p.first);
346  if (p.second.empty()) { //leaf
347  res.push_back(make_pair(p.first, p.second.get_value<string>()));
348  }
349  // else { //or array
350  // auto arrayText = getArrayText(p.second);
351  // if (arrayText.size()) {
352  // res.push_back(make_pair(p.first, arrayText));
353  // }
354  // }
355  }
356  }
357  }
358  for (auto& p : *this) {
359  if (history.find(p.first) == history.end()) {
360  history.insert(p.first);
361  if (p.second.empty()) { //leaf
362  res.push_back(make_pair(p.first, p.second.get_value<string>()));
363  }
364  // else { // or array
365  // auto arrayText = getArrayText(p.second);
366  // if (arrayText.size()) {
367  // res.push_back(make_pair(p.first, arrayText));
368  // }
369  // }
370  }
371  }
372  if (fallbackConfig_) {
373  auto more = fallbackConfig_->content(history);
374  res.insert(res.end(), more.begin(), more.end());
375  }
376  return res;
377  }
378 
379  /**
380  * @brief stream out the effective settings
381  *
382  * @param os ostream
383  * @param cfg The configuration
384  *
385  * @return os
386  */
387  friend ostream& operator << (ostream& os, Config const& cfg) {
388  for (auto& r : cfg.content()) {
389  os << r.first << '=' << r.second << endl;
390  }
391  if (cfg.fallbackConfig_) {
392  os << "next in line fallback" << endl;
393  os << *cfg.fallbackConfig_ << endl;
394  }
395 
396  return os;
397  }
398 
399 private:
400  // string getArrayText(ptree const& pt) const {
401  // bool isArray = true;
402  // string arrayText("[");
403  // for (auto& q : pt) {
404  // if (!q.second.empty() || q.first.size() != 0) {
405  // isArray = false;
406  // break;
407  // }
408  // arrayText += "\"" + q.second.get_value<string>() + "\",";
409  // }
410  // *arrayText.rbegin() = ']';
411  // if (isArray) {
412  // return arrayText;
413  // } else {
414  // return string();
415  // }
416  // }
417  path_type section_;
418  std::unique_ptr<Config> fallbackConfig_;
419 };
420 } //config_detail
421 
423 }}
424 
T getExt(const path_type &param, bool throwIfMissing=true) const
get a value from the config
Definition: Config.hpp:225
Config const & operator()(std::unordered_set< T > &to, const path_type &param) const
fill an unordered_set with a configured value retrieved using getExt
Definition: Config.hpp:286
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: TypedString.hpp:76
Config & resetSection(char const *section, bool sectionExists=true)
change section name
Definition: Config.hpp:178
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:254
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
Config const & operator()(T &to, const path_type &param) const
fill in a variable with a configured value retrieved using getExt
Definition: Config.hpp:271
ptree const & getChildExt(const path_type &param)
Gets the child from the config.
Definition: Config.hpp:197
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:337
Config const & operator()(std::vector< T > &to, const path_type &param) const
fill an list with a configured value retrieved using getExt
Definition: Config.hpp:313