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 construct using stream, optionally specifying the section name
58  * @details if the section is nullptr, just use the fallback values. if
59  * the section name cannot be found, throw an exception
60  *
61  * @param is stream as input providing a json stream
62  * @param section pointing to the effective section in the json above
63  */
64  explicit
65  Config(istream&& is, char const* section = nullptr) {
66  if (section) {
67  read_json(is, fallback_);
68  (ptree&)*this = fallback_.get_child(section);
69  } else {
70  read_json(is, (ptree&)*this);
71  }
72  }
73  /**
74  * @brief construct using stream, optionally specifying the section name
75  * @details if the section is nullptr, just use the fallback values. if
76  * the section name cannot be found, throw an exception
77  *
78  * @param is stream as input providing a json stream
79  * @param section pointing to the effective section in the json above
80  */
81  explicit
82  Config(istream& is, char const* section = nullptr) {
83  if (section) {
84  read_json(is, fallback_);
85  (ptree&)*this = fallback_.get_child(section);
86  } else {
87  read_json(is, (ptree&)*this);
88  }
89  }
90 
91  /**
92  * @brief construct using a string, optionally specifying the section name
93  * @details if the section is nullptr, just use the fallback values. if
94  * the section name cannot be found, throw an exception
95  *
96  * @param json string as input providing a json text
97  * @param section pointing to the effective section in the json above
98  */
99  explicit
100  Config(char const* json, char const* section = nullptr)
101  : Config(istringstream(json), section)
102  {}
103 
104  /**
105  * @brief construct using another ptree as fallbacks, optionally specifying the section name
106  * @details if the section is nullptr, just use the fallback values. if
107  * the section name cannot be found, throw an exception
108  *
109  * @param t ptree as input providing fallbacks (and sections)
110  * @param section pointing to the effective section in the ptree above
111  */
112  Config(ptree const& t, char const* section = nullptr) {
113  if (section) {
114  fallback_ = t;
115  (ptree&)*this = fallback_.get_child(section);
116  } else {
117  (ptree&)*this = t;
118  }
119  }
120 
121  /**
122  * @brief construct using a fallback ptree, and specify the section ptree
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
127  * @param section ptree providing section specific values
128  */
129  Config(ptree const& t, ptree const& section) {
130  fallback_ = t;
131  (ptree&)*this = section;
132  }
133 
134  /**
135  * @brief internal use
136  * @details this is to set the default user config values (not the fallback values)
137  *
138  * @param c a config holding configuration values
139  */
140  void setDefaultUserConfig(Config const& c) {
141  defaultUserConfig_.reset(new Config(c));
142  }
143 
144  /**
145  * @brief Gets the child from the config.
146  * @details check the section for it, if not found, try use fallback provided
147  * if still missing, search using the default user config values set by
148  * setDefaultUserConfig. Throw exception ptree_bad_path if all fail
149  *
150  * @param[in] param config parameter name
151  *
152  * @return ptree reference to the child.
153  */
154  ptree const& getChildExt(const path_type& param) {
155  auto res = get_child_optional(param);
156  if (!res) {
157  res = fallback_.get_child_optional(param);
158  if (!res) {
159  if (defaultUserConfig_) {
160  return defaultUserConfig_->getChildExt(param);
161  } else {
162  throw ptree_bad_path("invalid param and no default user Config set", param);
163  }
164  }
165  }
166  return *res;
167  }
168 
169  /**
170  * @brief get a value from the config
171  * @details check the section for it, if not found, try use fallback provided
172  * if still missing, search using the default user config values set by
173  * setDefaultUserConfig. Throw exception ptree_bad_path if all fail
174  *
175  * @param param config parameter name
176  * @tparam T type of the value
177  * @return result
178  */
179  template <typename T>
180  T getExt(const path_type& param) const {
181  auto res = get_optional<T>(param);
182  if (!res) {
183  res = fallback_.get_optional<T>(param);
184  if (!res) {
185  if (defaultUserConfig_) {
186  return defaultUserConfig_->getExt<T>(param);
187  } else {
188  throw ptree_bad_path("invalid param and no default user Config set", param);
189  }
190  }
191  }
192  return *res;
193  }
194 
195  /**
196  * @brief get a number value in hex format
197  * @details check the section for it, if not found, try use fallback provided
198  * if still missing, search using the default user config values set by
199  * setDefaultUserConfig. Throw exception ptree_bad_path if all fail
200  *
201  * @param param config parameter name
202  * @tparam T numeric type of the value: int , uint64_t ...
203  * @return result
204  */
205  template <typename T>
206  T getHex(ptree::path_type const& param) const {
207  istringstream iss(getExt<string>(param));
208  T res;
209  iss >> hex >> res;
210  return res;
211  }
212 
213  /**
214  * @brief fill in a variable with a configured value retrieved using getExt
215  * @details example cfg(abc, "abc")(def, "def");
216  *
217  * @param to destination
218  * @param param config parameter
219  *
220  * @return the Config object itself
221  */
222  template <typename T>
223  Config const& operator()(T& to, const path_type& param) const {
224  to = getExt<T>(param);
225  return *this;
226  }
227 
228  /**
229  * @brief fill an unordered_set with a configured value retrieved using getExt
230  * @details the value in the Config is a space separated string
231  *
232  * @param to destination
233  * @param param config parameter
234  *
235  * @return the Config object itself
236  */
237  template <typename T>
238  Config const& operator()(std::unordered_set<T>& to, const path_type& param) const {
239  // auto toStr = getExt<string>(param);
240  auto s = getExt<string>(param);
241  istringstream iss(s);
242 
243  for (auto iit = istream_iterator<T>(iss)
244  ; iit != istream_iterator<T>()
245  ; iit ++) {
246  to.insert(*iit);
247  }
248  if (!iss.eof()) {
249  throw (ptree_bad_data("not space separated items in Config ", param));
250  }
251 
252  return *this;
253  }
254 
255  /**
256  * @brief fill an list with a configured value retrieved using getExt
257  * @details the value in the Config is a json array
258  *
259  * @param to destination
260  * @param param config parameter
261  *
262  * @return the Config object itself
263  */
264  template <typename T>
265  Config const& operator()(std::vector<T>& to, const path_type& param) const {
266  // auto toStr = getExt<string>(param);
267  auto s = getExt<string>(param);
268  istringstream iss(s);
269 
270  for (auto iit = istream_iterator<T>(iss)
271  ; iit != istream_iterator<T>()
272  ; iit ++) {
273  to.emplace_back(*iit);
274  }
275  if (!iss.eof()) {
276  throw (ptree_bad_data("not space separated items in Config ", param));
277  }
278 
279  return *this;
280  }
281 
282  /**
283  * @brief get contents of all the effective configure in the form of list of string pairs
284  * @details only effective ones are shown
285  *
286  * @param skipThese skip those config params
287  * @return list of string pairs in the original order of ptree nodes
288  */
289  list<pair<string, string>> content(
290  unordered_set<string> const& skipThese = unordered_set<string>()) const {
291  list<pair<string, string>> res;
292  unordered_set<string> history(skipThese);
293  for (auto& p : *this) {
294  if (history.find(p.first) == history.end()) {
295  history.insert(p.first);
296  if (p.second.empty()) { //leaf
297  res.push_back(make_pair(p.first, p.second.get_value<string>()));
298  }
299  // else { //or array
300  // auto arrayText = getArrayText(p.second);
301  // if (arrayText.size()) {
302  // res.push_back(make_pair(p.first, arrayText));
303  // }
304  // }
305  }
306  }
307  for (auto& p : fallback_) {
308  if (history.find(p.first) == history.end()) {
309  history.insert(p.first);
310  if (p.second.empty()) { //leaf
311  res.push_back(make_pair(p.first, p.second.get_value<string>()));
312  }
313  // else { // or array
314  // auto arrayText = getArrayText(p.second);
315  // if (arrayText.size()) {
316  // res.push_back(make_pair(p.first, arrayText));
317  // }
318  // }
319  }
320  }
321  if (defaultUserConfig_) {
322  auto more = defaultUserConfig_->content(history);
323  res.insert(res.end(), more.begin(), more.end());
324  }
325  return res;
326  }
327 
328  /**
329  * @brief stream out the effective settings
330  *
331  * @param os ostream
332  * @param cfg The configuration
333  *
334  * @return os
335  */
336  friend ostream& operator << (ostream& os, Config const& cfg) {
337  for (auto& r : cfg.content()) {
338  os << r.first << '=' << r.second << endl;
339  }
340  return os;
341  }
342 
343 private:
344  // string getArrayText(ptree const& pt) const {
345  // bool isArray = true;
346  // string arrayText("[");
347  // for (auto& q : pt) {
348  // if (!q.second.empty() || q.first.size() != 0) {
349  // isArray = false;
350  // break;
351  // }
352  // arrayText += "\"" + q.second.get_value<string>() + "\",";
353  // }
354  // *arrayText.rbegin() = ']';
355  // if (isArray) {
356  // return arrayText;
357  // } else {
358  // return string();
359  // }
360  // }
361 
362  ptree fallback_;
363  std::shared_ptr<Config> defaultUserConfig_;
364 };
365 } //config_detail
366 
368 }}
369 
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:238
void setDefaultUserConfig(Config const &c)
internal use
Definition: Config.hpp:140
class to hold an hmbdc configuration
Definition: Config.hpp:46
T getExt(const path_type &param) const
get a value from the config
Definition: Config.hpp:180
Definition: TypedString.hpp:74
Config(istream &&is, char const *section=nullptr)
construct using stream, optionally specifying the section name
Definition: Config.hpp:65
T getHex(ptree::path_type const &param) const
get a number value in hex format
Definition: Config.hpp:206
Config(char const *json, char const *section=nullptr)
construct using a string, optionally specifying the section name
Definition: Config.hpp:100
Config const & operator()(T &to, const path_type &param) const
fill in a variable with a configured value retrieved using getExt
Definition: Config.hpp:223
ptree const & getChildExt(const path_type &param)
Gets the child from the config.
Definition: Config.hpp:154
Config(ptree const &t, ptree const &section)
construct using a fallback ptree, and specify the section ptree
Definition: Config.hpp:129
Config(istream &is, char const *section=nullptr)
construct using stream, optionally specifying the section name
Definition: Config.hpp:82
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:112
Definition: Base.hpp:12
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:289
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:265