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 <unordered_set>
8 #include <sstream>
9 #include <boost/property_tree/ptree.hpp>
10 #include <boost/property_tree/json_parser.hpp>
11 
12 
13 /**
14  * @namespace hmbdc::app
15  * hmbdc's application layer where API resides
16  */
17 namespace hmbdc { namespace app {
18 
19 namespace config_detail {
20 
21 using namespace std;
22 using namespace boost::property_tree;
23 /**
24  * @brief class to hold an hmbdc configuration
25  * @details it is based on a two level (fallback and section) json.
26  * top level for the fallback values and lower level for the section specific values.
27  * a Config instance is always (constructed to be) associated to 0 or a specific section.
28  * shown below:
29  *
30  * {
31  * "parameter_1": "top level, value used as a fallback",
32  * "parameter_2": "fallback is used when not configured in a section",
33  * "section_A": {
34  * "parameter_2": "lower level, a specific value effective in section_A",
35  * "parameter_1": "a specific value effective in section_A"
36  * },
37  * "another_section": {
38  * "parameter_2": "a specific value effective in another_section"
39  * }
40  * }
41  *
42  */
43 struct Config
44 : ptree {
45  using ptree::ptree;
46  /**
47  * @brief empty config
48  */
49  Config(){}
50 
51  /**
52  * @brief construct using stream, optionally specifying the section name
53  * @details if the section is nullptr, just use the fallback values. if
54  * the section name cannot be found, throw an exception
55  *
56  * @param is stream as input providing a json stream
57  * @param section pointing to the effective section in the json above
58  */
59  explicit
60  Config(istream&& is, char const* section = nullptr) {
61  if (section) {
62  read_json(is, fallback_);
63  (ptree&)*this = fallback_.get_child(section);
64  } else {
65  read_json(is, (ptree&)*this);
66  }
67  }
68  /**
69  * @brief construct using stream, optionally specifying the section name
70  * @details if the section is nullptr, just use the fallback values. if
71  * the section name cannot be found, throw an exception
72  *
73  * @param is stream as input providing a json stream
74  * @param section pointing to the effective section in the json above
75  */
76  explicit
77  Config(istream& is, char const* section = nullptr) {
78  if (section) {
79  read_json(is, fallback_);
80  (ptree&)*this = fallback_.get_child(section);
81  } else {
82  read_json(is, (ptree&)*this);
83  }
84  }
85 
86  /**
87  * @brief construct using a string, optionally specifying the section name
88  * @details if the section is nullptr, just use the fallback values. if
89  * the section name cannot be found, throw an exception
90  *
91  * @param json string as input providing a json text
92  * @param section pointing to the effective section in the json above
93  */
94  explicit
95  Config(char const* json, char const* section = nullptr)
96  : Config(istringstream(json), section)
97  {}
98 
99  /**
100  * @brief construct using another ptree as fallbacks, optionally specifying the section name
101  * @details if the section is nullptr, just use the fallback values. if
102  * the section name cannot be found, throw an exception
103  *
104  * @param t ptree as input providing fallbacks (and sections)
105  * @param section pointing to the effective section in the ptree above
106  */
107  Config(ptree const& t, char const* section = nullptr) {
108  if (section) {
109  fallback_ = t;
110  (ptree&)*this = fallback_.get_child(section);
111  } else {
112  (ptree&)*this = t;
113  }
114  }
115 
116  /**
117  * @brief construct using a fallback ptree, and specify the section ptree
118  * @details if the section is nullptr, just use the fallback values. if
119  * the section name cannot be found, throw an exception
120  *
121  * @param t ptree as input providing fallbacks
122  * @param section ptree providing section specific values
123  */
124  explicit
125  Config(ptree const& t, ptree const& section) {
126  fallback_ = t;
127  (ptree&)*this = section;
128  }
129 
130  /**
131  * @brief internal use
132  * @details this is to set the default user config values (not the fallback values)
133  *
134  * @param c a config holding configuration values
135  */
136  void setDefaultUserConfig(Config const& c) {
137  defaultUserConfig_.reset(new Config(c));
138  }
139 
140  /**
141  * @brief get a value from the config
142  * @details check the section for it, if not found, try use fallback provided
143  * if still missing, search using the default user config values set by
144  * setDefaultUserConfig. Throw exception ptree_bad_path if all fail
145  *
146  * @param param config parameter name
147  * @tparam T type of the value
148  * @return result
149  */
150  template <typename T>
151  T getExt(const path_type& param) const {
152  auto res = get_optional<T>(param);
153  if (!res) {
154  res = fallback_.get_optional<T>(param);
155  if (!res) {
156  if (defaultUserConfig_) {
157  return defaultUserConfig_->getExt<T>(param);
158  } else {
159  throw ptree_bad_path("invalid param and no default user Config set", param);
160  }
161  }
162  }
163  return *res;
164  }
165 
166  /**
167  * @brief get a number value in hex format
168  * @details check the section for it, if not found, try use fallback provided
169  * if still missing, search using the default user config values set by
170  * setDefaultUserConfig. Throw exception ptree_bad_path if all fail
171  *
172  * @param param config parameter name
173  * @tparam T numeric type of the value: int , uint64_t ...
174  * @return result
175  */
176  template <typename T>
177  T getHex(ptree::path_type const& param) {
178  istringstream iss(getExt<string>(param));
179  T res;
180  iss >> hex >> res;
181  return res;
182  }
183 
184  /**
185  * @brief fill in a variable with a configured value retrieved using getExt
186  * @details example cfg(abc, "abc")(def, "def");
187  *
188  * @param to destination
189  * @param param config parameter
190  *
191  * @return the Config object itself
192  */
193  template <typename T>
194  Config const& operator()(T& to, const path_type& param) const {
195  to = getExt<T>(param);
196  return *this;
197  }
198 
199  /**
200  * @brief fill an unordered_set with a configured value retrieved using getExt
201  * @details the value in the Config is a space separated string
202  *
203  * @param to destination
204  * @param param config parameter
205  *
206  * @return the Config object itself
207  */
208  template <typename T>
209  Config const& operator()(std::unordered_set<T>& to, const path_type& param) const {
210  // auto toStr = getExt<string>(param);
211  auto s = getExt<string>(param);
212  istringstream iss(s);
213 
214  for (auto iit = istream_iterator<T>(iss)
215  ; iit != istream_iterator<T>()
216  ; iit ++) {
217  to.insert(*iit);
218  }
219  if (!iss.eof()) {
220  throw (ptree_bad_data("not space separated uint16_t's in Config", param));
221  }
222 
223  return *this;
224  }
225 
226  /**
227  * @brief get contents of all the effective configure in the form of list of string pairs
228  * @details only effective ones are shown
229  *
230  * @param skipThese skip those config params
231  * @return list of string pairs in the original order of ptree nodes
232  */
233  list<pair<string, string>> content(
234  unordered_set<string> const& skipThese = unordered_set<string>()) const {
235  list<pair<string, string>> res;
236  unordered_set<string> history(skipThese);
237  for (auto& p : *this) {
238  if (history.find(p.first) == history.end()) {
239  history.insert(p.first);
240  if (p.second.empty()) {
241  res.push_back(make_pair(p.first, p.second.get_value<string>()));
242  }
243  }
244  }
245  for (auto& p : fallback_) {
246  if (history.find(p.first) == history.end()) {
247  history.insert(p.first);
248  if (p.second.empty()) {
249  res.push_back(make_pair(p.first, p.second.get_value<string>()));
250  }
251  }
252  }
253  if (defaultUserConfig_) {
254  auto more = defaultUserConfig_->content(history);
255  res.insert(res.end(), more.begin(), more.end());
256  }
257  return res;
258  }
259 
260 private:
261  ptree fallback_;
262  std::shared_ptr<Config> defaultUserConfig_;
263 };
264 } //config_detail
265 
267 }}
268 
void setDefaultUserConfig(Config const &c)
internal use
Definition: Config.hpp:136
class to hold an hmbdc configuration
Definition: Config.hpp:43
T getExt(const path_type &param) const
get a value from the config
Definition: Config.hpp:151
Definition: TypedString.hpp:74
Config(istream &&is, char const *section=nullptr)
construct using stream, optionally specifying the section name
Definition: Config.hpp:60
Config(char const *json, char const *section=nullptr)
construct using a string, optionally specifying the section name
Definition: Config.hpp:95
Config(ptree const &t, ptree const &section)
construct using a fallback ptree, and specify the section ptree
Definition: Config.hpp:125
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:209
Config(istream &is, char const *section=nullptr)
construct using stream, optionally specifying the section name
Definition: Config.hpp:77
Config()
empty config
Definition: Config.hpp:49
Config(ptree const &t, char const *section=nullptr)
construct using another ptree as fallbacks, optionally specifying the section name ...
Definition: Config.hpp:107
T getHex(ptree::path_type const &param)
get a number value in hex format
Definition: Config.hpp:177
Config const & operator()(T &to, const path_type &param) const
fill in a variable with a configured value retrieved using getExt
Definition: Config.hpp:194
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:233
Definition: Base.hpp:12