File indexing completed on 2026-09-27 09:14:57
0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013
0014
0015
0016
0017
0018
0019
0020 #ifndef LAGER_CORE_CONFIGURATION_LOADED
0021 #define LAGER_CORE_CONFIGURATION_LOADED
0022
0023 #include <boost/lexical_cast.hpp>
0024 #include <boost/optional.hpp>
0025 #include <boost/property_tree/exceptions.hpp>
0026 #include <boost/property_tree/json_parser.hpp>
0027 #include <boost/property_tree/ptree.hpp>
0028 #include <map>
0029 #include <lager/core/exception.hh>
0030 #include <lager/core/interval.hh>
0031 #include <lager/core/stringify.hh>
0032 #include <string>
0033
0034 namespace lager {
0035
0036 using ptree = boost::property_tree::ptree;
0037 using boost::property_tree::read_json;
0038 using boost::property_tree::write_json;
0039 template <class T> using optional = boost::optional<T>;
0040 template <class T> using translation_map = std::map<std::string, T>;
0041
0042
0043 class configuration_error;
0044 class configuration_path_error;
0045 class configuration_key_error;
0046 class configuration_value_error;
0047 class configuration_translation_error;
0048 }
0049
0050
0051
0052
0053
0054
0055
0056
0057
0058 namespace lager {
0059
0060 constexpr const char PATH_SEPARATOR{'/'};
0061 constexpr const char NAME_SEPARATOR{'_'};
0062 constexpr const char* const TITLE_SEPARATOR{" "};
0063
0064
0065 std::string format_name(std::string context, const std::string& name);
0066 std::string format_title(const std::string& context, std::string title);
0067
0068
0069
0070
0071 class string_path : public ptree::path_type {
0072 public:
0073 using base_type = ptree::path_type;
0074 explicit string_path(const char separator = PATH_SEPARATOR)
0075 : base_type{separator} {}
0076 string_path(const std::string& path, const char separator = PATH_SEPARATOR)
0077 : base_type{path, separator} {}
0078 string_path(const char* path, const char separator = PATH_SEPARATOR)
0079 : string_path(std::string{path}, separator) {}
0080 string_path(const base_type& rhs) : base_type{rhs} {}
0081 string_path& operator=(const string_path& rhs) {
0082 static_cast<base_type>(*this) = rhs;
0083 return *this;
0084 }
0085 std::string str() const { return dump(); }
0086 };
0087 }
0088
0089
0090
0091
0092
0093
0094
0095
0096 namespace lager {
0097
0098 namespace configuration_impl {
0099 class value_proxy;
0100 }
0101
0102 class configuration {
0103 public:
0104 constexpr static const char* DEFAULTS{"defaults"};
0105 constexpr static const char* TYPE_KEY{"type"};
0106
0107 configuration(const ptree& settings, const string_path& path);
0108 configuration(const configuration& conf, const string_path& path)
0109 : configuration{conf.settings_, path} {}
0110
0111
0112 void load(const ptree& in_conf);
0113
0114
0115
0116 void save(ptree& out_conf) const;
0117
0118
0119
0120
0121
0122
0123 ptree& raw_node(const string_path& key);
0124
0125
0126 std::string type() const { return get<std::string>(TYPE_KEY); }
0127
0128
0129 template <class T> void set(const string_path& key, const T& value) {
0130 settings_.put(key, value);
0131 }
0132
0133
0134
0135
0136 configuration_impl::value_proxy value(const string_path& key) const;
0137 configuration_impl::value_proxy operator[](const string_path& key) const;
0138
0139
0140
0141
0142
0143
0144
0145
0146 template <class T> optional<T> get_optional(const string_path& key) const;
0147 template <class T>
0148 optional<T> get_optional(const string_path& key,
0149 const translation_map<T>& tr) const;
0150
0151 template <class T> T get(const string_path& key) const;
0152 template <class T>
0153 T get(const string_path& key, const translation_map<T>& tr) const;
0154
0155
0156 template <class T, class = typename std::enable_if<!is_map<T>::value>::type>
0157 T get(const string_path& key, const T& default_value) const;
0158 template <class T>
0159 T get(const string_path& key, const T& default_value,
0160 const translation_map<T>& tr) const;
0161
0162
0163 template <class T>
0164 optional<std::vector<T>> get_optional_vector(const string_path& key) const;
0165 template <class T>
0166 optional<std::vector<T>>
0167 get_optional_vector(const string_path& key,
0168 const translation_map<T>& tr) const;
0169
0170 template <class T> std::vector<T> get_vector(const string_path& key) const;
0171 template <class T>
0172 std::vector<T> get_vector(const string_path& key,
0173 const translation_map<T>& tr) const;
0174
0175
0176
0177 template <class Vector3>
0178 optional<Vector3> get_optional_vector3(const string_path& key) const;
0179 template <class Vector3>
0180 optional<Vector3>
0181 get_optional_vector3(const string_path& key,
0182 const translation_map<double>& tr) const;
0183 template <class Vector4>
0184 optional<Vector4> get_optional_vector4(const string_path& key) const;
0185 template <class Vector4>
0186 optional<Vector4>
0187 get_optional_vector4(const string_path& key,
0188 const translation_map<double>& tr) const;
0189
0190 template <class Vector3> Vector3 get_vector3(const string_path& key) const;
0191 template <class Vector3>
0192 Vector3 get_vector3(const string_path& key,
0193 const translation_map<double>& tr) const;
0194 template <class Vector4> Vector4 get_vector4(const string_path& key) const;
0195 template <class Vector4>
0196 Vector4 get_vector4(const string_path& key,
0197 const translation_map<double>& tr) const;
0198
0199
0200
0201
0202 template <class T>
0203 optional<T> get_optional_bitpattern(const string_path& key) const;
0204 template <class T>
0205 optional<T> get_optional_bitpattern(const string_path& key,
0206 const translation_map<T>& tr) const;
0207
0208 template <class T> T get_bitpattern(const string_path& key) const;
0209 template <class T>
0210 T get_bitpattern(const string_path& key, const translation_map<T>& tr) const;
0211
0212
0213 template <class T>
0214 optional<interval<T>> get_optional_range(const string_path& key) const;
0215 template <class T>
0216 optional<interval<T>> get_optional_range(const string_path& key,
0217 const translation_map<T>& tr) const;
0218
0219 template <class T> interval<T> get_range(const string_path& key) const;
0220 template <class T>
0221 interval<T> get_range(const string_path& key,
0222 const translation_map<T>& tr) const;
0223
0224
0225 configuration_path_error path_error(const string_path& path) const;
0226 configuration_key_error key_error(const string_path& key) const;
0227 configuration_value_error value_error(const string_path& key,
0228 const std::string& value) const;
0229 configuration_value_error value_error(const string_path& key) const;
0230 configuration_translation_error
0231 translation_error(const string_path& key, const std::string& value) const;
0232 configuration_translation_error
0233 translation_error(const string_path& key) const;
0234 template <class T>
0235 configuration_translation_error
0236 translation_error(const string_path& key, const std::string& value,
0237 const translation_map<T>& tr) const;
0238
0239 private:
0240 template <class T>
0241 T translate(const string_path& key, const std::string& val,
0242 const translation_map<T>& tr) const;
0243
0244
0245 string_path settings_path_;
0246 ptree settings_;
0247
0248 string_path defaults_path_;
0249 ptree defaults_;
0250 };
0251 }
0252
0253
0254
0255
0256
0257
0258
0259
0260
0261
0262 namespace lager {
0263 class configurable {
0264 public:
0265
0266 configurable(const ptree& settings, const string_path& path)
0267 : conf_{settings, path}, path_{path} {}
0268 configurable(const configuration& conf, const string_path& path)
0269 : conf_{conf, path}, path_{path} {}
0270 const string_path& path() const { return path_; }
0271 const configuration& conf() const { return conf_; }
0272 configuration& conf() { return conf_; }
0273
0274 private:
0275 configuration conf_;
0276 const string_path path_;
0277 };
0278 }
0279
0280
0281
0282
0283 namespace lager {
0284
0285 class configuration_error : public lager::exception {
0286 public:
0287 configuration_error(const std::string& msg,
0288 const std::string& type = "configuration_error")
0289 : lager::exception{msg, type} {}
0290 };
0291
0292 class configuration_path_error : public configuration_error {
0293 public:
0294 configuration_path_error(const string_path& path);
0295 };
0296 class configuration_key_error : public configuration_error {
0297 public:
0298 configuration_key_error(const string_path& key,
0299 const string_path& settings_path,
0300 const string_path& defaults_path);
0301 };
0302
0303 class configuration_value_error : public configuration_error {
0304 public:
0305 configuration_value_error(const string_path& key, const std::string& value,
0306 const string_path& settings_path,
0307 const string_path& defaults_path);
0308 };
0309 class configuration_translation_error : public configuration_error {
0310 public:
0311 configuration_translation_error(const string_path& key,
0312 const std::string& value,
0313 const string_path& settings_path,
0314 const string_path& defaults_path);
0315 template <class T>
0316 configuration_translation_error(const string_path& key,
0317 const std::string& value,
0318 const translation_map<T>& tr,
0319 const string_path& settings_path,
0320 const string_path& defaults_path);
0321 };
0322
0323
0324
0325
0326 namespace configuration_impl {
0327 class value_proxy {
0328 public:
0329 value_proxy(const configuration& conf, string_path key)
0330 : conf_{conf}, key_{std::move(key)} {}
0331
0332 template <class T> operator T() const { return conf_.get<T>(key_); }
0333 template <class T> operator std::vector<T>() const {
0334 return conf_.get_vector<T>(key_);
0335 }
0336 template <class T> operator interval<T>() const {
0337 return conf_.get_range<T>(key_);
0338 }
0339
0340 private:
0341 const configuration& conf_;
0342 const string_path key_;
0343 };
0344 }
0345
0346
0347
0348
0349 template <class T>
0350 optional<T> configuration::get_optional(const string_path& key) const {
0351 try {
0352 auto s = settings_.get_optional<T>(key);
0353 if (!s) {
0354 s = defaults_.get_optional<T>(key);
0355 }
0356 return s;
0357 } catch (boost::property_tree::ptree_bad_data& e) {
0358 throw translation_error(key, e.data<std::string>());
0359 }
0360 }
0361 template <class T>
0362 optional<T> configuration::get_optional(const string_path& key,
0363 const translation_map<T>& tr) const {
0364 auto s = get_optional<std::string>(key);
0365 if (!s) {
0366 return {};
0367 }
0368 return {translate(key, *s, tr)};
0369 }
0370 template <class T> T configuration::get(const string_path& key) const {
0371 auto s = get_optional<T>(key);
0372 if (!s) {
0373 throw key_error(key);
0374 }
0375 return *s;
0376 }
0377 template <class T>
0378 T configuration::get(const string_path& key,
0379 const translation_map<T>& tr) const {
0380 std::string val{get<std::string>(key)};
0381 return translate(key, val, tr);
0382 }
0383 template <class T, class>
0384 T configuration::get(const string_path& key, const T& default_value) const {
0385 auto s = get_optional<T>(key);
0386 if (!s) {
0387 return default_value;
0388 }
0389 return *s;
0390 }
0391 template <class T>
0392 T configuration::get(const string_path& key, const T& default_value,
0393 const translation_map<T>& tr) const {
0394 std::string val{get(key, default_value)};
0395 return translate(key, val, tr);
0396 }
0397
0398 template <class T>
0399 optional<std::vector<T>>
0400 configuration::get_optional_vector(const string_path& key) const {
0401 optional<std::vector<T>> vec;
0402 auto node = settings_.get_child_optional(key);
0403 if (!node) {
0404 node = defaults_.get_child_optional(key);
0405 }
0406 if (node) {
0407 vec.reset(std::vector<T>());
0408 for (const auto& child : *node) {
0409 auto val = child.second.get_value_optional<T>();
0410 if (val) {
0411 vec->push_back(*val);
0412 }
0413 }
0414 }
0415 return vec;
0416 }
0417 template <class T>
0418 optional<std::vector<T>>
0419 configuration::get_optional_vector(const string_path& key,
0420 const translation_map<T>& tr) const {
0421 optional<std::vector<T>> vec;
0422 auto vec_str = get_optional_vector<std::string>(key);
0423 if (vec_str) {
0424 vec.reset(std::vector<T>());
0425 for (const auto& el : *vec_str) {
0426 vec->push_back(translate(key, el, tr));
0427 }
0428 }
0429 return vec;
0430 }
0431 template <class T>
0432 std::vector<T> configuration::get_vector(const string_path& key) const {
0433 auto s = get_optional_vector<T>(key);
0434 if (!s) {
0435 throw key_error(key);
0436 }
0437 return *s;
0438 }
0439 template <class T>
0440 std::vector<T> configuration::get_vector(const string_path& key,
0441 const translation_map<T>& tr) const {
0442 auto s = get_optional_vector<T>(key, tr);
0443 if (!s) {
0444 throw key_error(key);
0445 }
0446 return *s;
0447 }
0448
0449 template <class Vector3>
0450 optional<Vector3>
0451 configuration::get_optional_vector3(const string_path& key) const {
0452 auto range = get_optional_vector<double>(key);
0453 if (range) {
0454 if (range->size() != 3) {
0455 throw translation_error(key, stringify(*range));
0456 }
0457 return {{(*range)[0], (*range)[1], (*range)[2]}};
0458 }
0459 return {};
0460 }
0461 template <class Vector3>
0462 optional<Vector3>
0463 configuration::get_optional_vector3(const string_path& key,
0464 const translation_map<double>& tr) const {
0465 auto range = get_optional_vector(key, tr);
0466 if (range) {
0467 if (range->size() != 3) {
0468 throw translation_error(key, stringify(*range));
0469 }
0470 return {{(*range)[0], (*range)[1], (*range)[2]}};
0471 }
0472 return {};
0473 }
0474 template <class Vector3>
0475 Vector3 configuration::get_vector3(const string_path& key) const {
0476 auto range = get_optional_vector3<Vector3>(key);
0477 if (!range) {
0478 throw key_error(key);
0479 }
0480 return *range;
0481 }
0482 template <class Vector3>
0483 Vector3 configuration::get_vector3(const string_path& key,
0484 const translation_map<double>& tr) const {
0485 auto range = get_optional_vector3<Vector3>(key, tr);
0486 if (!range) {
0487 throw key_error(key);
0488 }
0489 return *range;
0490 }
0491
0492 template <class Vector4>
0493 optional<Vector4>
0494 configuration::get_optional_vector4(const string_path& key) const {
0495 auto range = get_optional_vector<double>(key);
0496 if (range) {
0497 if (range->size() != 4) {
0498 throw translation_error(key, stringify(*range));
0499 }
0500 return {{(*range)[0], (*range)[1], (*range)[2], (*range)[3]}};
0501 }
0502 return {};
0503 }
0504 template <class Vector4>
0505 optional<Vector4>
0506 configuration::get_optional_vector4(const string_path& key,
0507 const translation_map<double>& tr) const {
0508 auto range = get_optional_vector(key, tr);
0509 if (range) {
0510 if (range->size() != 4) {
0511 throw translation_error(key, stringify(*range));
0512 }
0513 return {{(*range)[0], (*range)[1], (*range)[2], (*range)[3]}};
0514 }
0515 return {};
0516 }
0517 template <class Vector4>
0518 Vector4 configuration::get_vector4(const string_path& key) const {
0519 auto range = get_optional_vector4<Vector4>(key);
0520 if (!range) {
0521 throw key_error(key);
0522 }
0523 return *range;
0524 }
0525 template <class Vector4>
0526 Vector4 configuration::get_vector4(const string_path& key,
0527 const translation_map<double>& tr) const {
0528 auto range = get_optional_vector4<Vector4>(key, tr);
0529 if (!range) {
0530 throw key_error(key);
0531 }
0532 return *range;
0533 }
0534
0535 template <class T>
0536 optional<T>
0537 configuration::get_optional_bitpattern(const string_path& key) const {
0538 optional<std::vector<T>> vec{get_optional_vector<T>(key)};
0539 optional<T> pattern;
0540 if (vec) {
0541 pattern.reset(static_cast<T>(0));
0542 for (const auto& val : *vec) {
0543 *pattern = static_cast<T>(*pattern | val);
0544 }
0545 }
0546 return pattern;
0547 }
0548 template <class T>
0549 optional<T>
0550 configuration::get_optional_bitpattern(const string_path& key,
0551 const translation_map<T>& tr) const {
0552 optional<std::vector<T>> vec{get_optional_vector(key, tr)};
0553 optional<T> pattern;
0554 if (vec) {
0555 pattern.reset(static_cast<T>(0));
0556 for (const auto& val : *vec) {
0557 *pattern = static_cast<T>(*pattern | val);
0558 }
0559 }
0560 return pattern;
0561 }
0562 template <class T>
0563 T configuration::get_bitpattern(const string_path& key) const {
0564 auto s = get_optional_bitpattern<T>(key);
0565 if (!s) {
0566 throw key_error(key);
0567 }
0568 return *s;
0569 }
0570 template <class T>
0571 T configuration::get_bitpattern(const string_path& key,
0572 const translation_map<T>& tr) const {
0573 auto s = get_optional_bitpattern<T>(key, tr);
0574 if (!s) {
0575 throw key_error(key);
0576 }
0577 return *s;
0578 }
0579
0580 template <class T>
0581 optional<interval<T>>
0582 configuration::get_optional_range(const string_path& key) const {
0583 auto range = get_optional_vector<T>(key);
0584 if (range) {
0585 if (range->size() != 2) {
0586 throw translation_error(key, stringify(*range));
0587 }
0588 return {{(*range)[0], (*range)[1]}};
0589 }
0590 return {};
0591 }
0592 template <class T>
0593 optional<interval<T>>
0594 configuration::get_optional_range(const string_path& key,
0595 const translation_map<T>& tr) const {
0596 auto range = get_optional_vector(key, tr);
0597 if (range) {
0598 if (range->size() != 2) {
0599 throw translation_error(key, stringify(*range));
0600 }
0601 return {{(*range)[0], (*range)[1]}};
0602 }
0603 return {};
0604 }
0605 template <class T>
0606 interval<T> configuration::get_range(const string_path& key) const {
0607 auto range = get_optional_range<T>(key);
0608 if (!range) {
0609 throw key_error(key);
0610 }
0611 return *range;
0612 }
0613 template <class T>
0614 interval<T> configuration::get_range(const string_path& key,
0615 const translation_map<T>& tr) const {
0616 auto range = get_optional_range(key, tr);
0617 if (!range) {
0618 throw key_error(key);
0619 }
0620 return *range;
0621 }
0622
0623
0624
0625
0626
0627 inline ptree& configuration::raw_node(const string_path& key) {
0628 auto node = settings_.get_child_optional(key);
0629 if (!node) {
0630 throw key_error(key);
0631 }
0632 return *node;
0633 }
0634
0635
0636
0637
0638
0639
0640 template <class T>
0641 configuration_translation_error
0642 configuration::translation_error(const string_path& key,
0643 const std::string& value,
0644 const translation_map<T>& tr) const {
0645 return {key, value, tr, settings_path_, defaults_path_};
0646 }
0647
0648
0649 template <class T>
0650 T configuration::translate(const string_path& key, const std::string& val,
0651 const translation_map<T>& tr) const {
0652 try {
0653 return tr.at(val);
0654 } catch (std::out_of_range) {
0655 throw translation_error(key, val, tr);
0656 }
0657 }
0658
0659
0660 template <class T>
0661 configuration_translation_error::configuration_translation_error(
0662 const string_path& key, const std::string& value,
0663 const translation_map<T>& tr, const string_path& settings_path,
0664 const string_path& defaults_path)
0665 : configuration_error(
0666 "Unable to translate value '" + value + "' for key '" + key.str() +
0667 "' (in '" + settings_path.str() + "' or '" + defaults_path.str() +
0668 "' -- allowed values: '" +
0669 stringify(tr, "', '",
0670 [](const typename translation_map<T>::value_type& el) {
0671 return el.first;
0672 }) +
0673 "')",
0674 "configuration_translation_error") {}
0675
0676 }
0677
0678 #endif