hmbdc
simplify-high-performance-messaging-programming
Config.hpp
1 #include "hmbdc/Copyright.hpp"
2 #pragma once
3 
4 #include <string>
5 #include <memory>
6 #include <list>
7 #include <vector>
8 #include <unordered_set>
9 #include <sstream>
10 #include <boost/property_tree/ptree.hpp>
11 #include <boost/property_tree/json_parser.hpp>
12 
13 
14 /**
15  * @namespace hmbdc::app
16  * hmbdc's application layer where API resides
17  */
18 namespace hmbdc { namespace app {
19 
20 namespace config_detail {
21 
22 using namespace std;
23 using namespace boost::property_tree;
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  *
43  */
44 struct Config
45 : 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 construct using stream, optionally specifying the section name
56  * @details if the section is nullptr, just use the fallback values. if
57  * the section name cannot be found, throw an exception
58  *
59  * @param is stream as input providing a json stream
60  * @param section pointing to the effective section in the json above
61  */
62  explicit
63  Config(istream&& is, char const* section = nullptr) {
64  if (section) {
65  read_json(is, fallback_);
66  (ptree&)*this = fallback_.get_child(section);
67  } else {
68  read_json(is, (ptree&)*this);
69  }
70  }
71  /**
72  * @brief construct using stream, optionally specifying the section name
73  * @details if the section is nullptr, just use the fallback values. if
74  * the section name cannot be found, throw an exception
75  *
76  * @param is stream as input providing a json stream
77  * @param section pointing to the effective section in the json above
78  */
79  explicit
80  Config(istream& is, char const* section = nullptr) {
81  if (section) {
82  read_json(is, fallback_);
83  (ptree&)*this = fallback_.get_child(section);
84  } else {
85  read_json(is, (ptree&)*this);
86  }
87  }
88 
89  /**
90  * @brief construct using a string, optionally specifying the section name
91  * @details if the section is nullptr, just use the fallback values. if
92  * the section name cannot be found, throw an exception
93  *
94  * @param json string as input providing a json text
95  * @param section pointing to the effective section in the json above
96  */
97  explicit
98  Config(char const* json, char const* section = nullptr)
99  : Config(istringstream(json), section)
100  {}
101 
102  /**
103  * @brief construct using another ptree as fallbacks, optionally specifying the section name
104  * @details if the section is nullptr, just use the fallback values. if
105  * the section name cannot be found, throw an exception
106  *
107  * @param t ptree as input providing fallbacks (and sections)
108  * @param section pointing to the effective section in the ptree above
109  */
110  Config(ptree const& t, char const* section = nullptr) {
111  if (section) {
112  fallback_ = t;
113  (ptree&)*this = fallback_.get_child(section);
114  } else {
115  (ptree&)*this = t;
116  }
117  }
118 
119  /**
120  * @brief construct using a fallback ptree, and specify the section ptree
121  * @details if the section is nullptr, just use the fallback values. if
122  * the section name cannot be found, throw an exception
123  *
124  * @param t ptree as input providing fallbacks
125  * @param section ptree providing section specific values
126  */
127  explicit
128  Config(ptree const& t, ptree const& section) {
129  fallback_ = t;
130  (ptree&)*this = section;
131  }
132 
133  /**
134  * @brief internal use
135  * @details this is to set the default user config values (not the fallback values)
136  *
137  * @param c a config holding configuration values
138  */
139  void setDefaultUserConfig(Config const& c) {
140  defaultUserConfig_.reset(new Config(c));
141  }
142 
143  /**
144  * @brief get a value from the config
145  * @details check the section for it, if not found, try use fallback provided
146  * if still missing, search using the default user config values set by
147  * setDefaultUserConfig. Throw exception ptree_bad_path if all fail
148  *
149  * @param param config parameter name
150  * @tparam T type of the value
151  * @return result
152  */
153  template <typename T>
154  T getExt(const path_type& param) const {
155  auto res = get_optional<T>(param);
156  if (!res) {
157  res = fallback_.get_optional<T>(param);
158  if (!res) {
159  if (defaultUserConfig_) {
160  return defaultUserConfig_->getExt<T>(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 number value in hex format
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 numeric type of the value: int , uint64_t ...
177  * @return result
178  */
179  template <typename T>
180  T getHex(ptree::path_type const& param) {
181  istringstream iss(getExt<string>(param));
182  T res;
183  iss >> hex >> res;
184  return res;
185  }
186 
187  /**
188  * @brief fill in a variable with a configured value retrieved using getExt
189  * @details example cfg(abc, "abc")(def, "def");
190  *
191  * @param to destination
192  * @param param config parameter
193  *
194  * @return the Config object itself
195  */
196  template <typename T>
197  Config const& operator()(T& to, const path_type& param) const {
198  to = getExt<T>(param);
199  return *this;
200  }
201 
202  /**
203  * @brief fill an unordered_set with a configured value retrieved using getExt
204  * @details the value in the Config is a space separated string
205  *
206  * @param to destination
207  * @param param config parameter
208  *
209  * @return the Config object itself
210  */
211  template <typename T>
212  Config const& operator()(std::unordered_set<T>& to, const path_type& param) const {
213  // auto toStr = getExt<string>(param);
214  auto s = getExt<string>(param);
215  istringstream iss(s);
216 
217  for (auto iit = istream_iterator<T>(iss)
218  ; iit != istream_iterator<T>()
219  ; iit ++) {
220  to.insert(*iit);
221  }
222  if (!iss.eof()) {
223  throw (ptree_bad_data("not space separated uint16_t's in Config", param));
224  }
225 
226  return *this;
227  }
228 
229  /**
230  * @brief fill an list with a configured value retrieved using getExt
231  * @details the value in the Config is a json array
232  *
233  * @param to destination
234  * @param param config parameter
235  *
236  * @return the Config object itself
237  */
238  template <typename T>
239  Config const& operator()(std::vector<T>& to, const path_type& param) const {
240  auto a = this->get_child_optional(param);
241  if (!a) {
242  a = fallback_.get_child_optional(param);
243  }
244  if (!a) {
245  if (defaultUserConfig_) {
246  return (*defaultUserConfig_)(to, param);
247  } else {
248  throw ptree_bad_path("invalid param and no default user Config set", param);
249  }
250  }
251  for (auto const& v : *a) {
252  to.push_back(v.second.get_value<T>());
253  }
254  return *this;
255  }
256 
257  /**
258  * @brief get contents of all the effective configure in the form of list of string pairs
259  * @details only effective ones are shown
260  *
261  * @param skipThese skip those config params
262  * @return list of string pairs in the original order of ptree nodes
263  */
264  list<pair<string, string>> content(
265  unordered_set<string> const& skipThese = unordered_set<string>()) const {
266  list<pair<string, string>> res;
267  unordered_set<string> history(skipThese);
268  for (auto& p : *this) {
269  if (history.find(p.first) == history.end()) {
270  history.insert(p.first);
271  if (p.second.empty()) {
272  res.push_back(make_pair(p.first, p.second.get_value<string>()));
273  }
274  }
275  }
276  for (auto& p : fallback_) {
277  if (history.find(p.first) == history.end()) {
278  history.insert(p.first);
279  if (p.second.empty()) {
280  res.push_back(make_pair(p.first, p.second.get_value<string>()));
281  }
282  }
283  }
284  if (defaultUserConfig_) {
285  auto more = defaultUserConfig_->content(history);
286  res.insert(res.end(), more.begin(), more.end());
287  }
288  return res;
289  }
290 
291 private:
292  ptree fallback_;
293  std::shared_ptr<Config> defaultUserConfig_;
294 };
295 } //config_detail
296 
298 }}
299 
void setDefaultUserConfig(Config const &c)
internal use
Definition: Config.hpp:139
class to hold an hmbdc configuration
Definition: Config.hpp:44
T getExt(const path_type &param) const
get a value from the config
Definition: Config.hpp:154
Definition: TypedString.hpp:74
Config(istream &&is, char const *section=nullptr)
construct using stream, optionally specifying the section name
Definition: Config.hpp:63
Config(char const *json, char const *section=nullptr)
construct using a string, optionally specifying the section name
Definition: Config.hpp:98
Config(ptree const &t, ptree const &section)
construct using a fallback ptree, and specify the section ptree
Definition: Config.hpp:128
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:239
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:212
Config(istream &is, char const *section=nullptr)
construct using stream, optionally specifying the section name
Definition: Config.hpp:80
Config()
empty config
Definition: Config.hpp:52
Config(ptree const &t, char const *section=nullptr)
construct using another ptree as fallbacks, optionally specifying the section name ...
Definition: Config.hpp:110
T getHex(ptree::path_type const &param)
get a number value in hex format
Definition: Config.hpp:180
Config const & operator()(T &to, const path_type &param) const
fill in a variable with a configured value retrieved using getExt
Definition: Config.hpp:197
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:264
Definition: Base.hpp:12