Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2026-09-27 09:14:57

0001 // lAger: General Purpose l/A-event Generator
0002 // Copyright (C) 2016-2021 Sylvester Joosten <sjoosten@anl.gov>
0003 // 
0004 // This file is part of lAger.
0005 // 
0006 // lAger is free software: you can redistribute it and/or modify
0007 // it under the terms of the GNU General Public License as published by
0008 // the Free Shoftware Foundation, either version 3 of the License, or
0009 // (at your option) any later version.
0010 // 
0011 // lAger is distributed in the hope that it will be useful,
0012 // but WITHOUT ANY WARRANTY; without even the implied warranty of
0013 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
0014 // GNU General Public License for more details.
0015 // 
0016 // You should have received a copy of the GNU General Public License
0017 // along with lAger.  If not, see <https://www.gnu.org/licenses/>.
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 // necessary type aliases
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 // error prototypes
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 } // namespace lager
0049 
0050 // =============================================================================
0051 // Consistent way to format object paths/names/titles
0052 //
0053 // Note: * for optimal compatiblity with ROOT, a '/' path separator should be
0054 //         used.
0055 //       * a format_path() is not provided, as this is performed more
0056 //         transparently by the string_path class.
0057 // =============================================================================
0058 namespace lager {
0059 // path/name/title separators.
0060 constexpr const char PATH_SEPARATOR{'/'};
0061 constexpr const char NAME_SEPARATOR{'_'};
0062 constexpr const char* const TITLE_SEPARATOR{" "};
0063 
0064 // utility functions to consistently format object names and titles
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 // Utility class to store string paths (based on ptree::path_type),
0069 // automatically taking care of the prefered PATH_SEPARATOR.
0070 // Casts to std::string to make dumping the path contents less cumbersome.
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 } // namespace lager
0088 
0089 // =============================================================================
0090 // configuration handler
0091 //
0092 // The configuration constructor takes an identifier string
0093 // as argument. This string should match the settings path
0094 // in the associated settings ptree.
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   // load the settings from a given ptree
0112   void load(const ptree& in_conf);
0113 
0114   // store the settings in the give ptree
0115   // The defaults are only exported if not yet present.
0116   void save(ptree& out_conf) const;
0117 
0118   // HACK: expose raw ptree node, because we need this right now to directly
0119   //       loop over an array of settings
0120   //       the fact we need to do this means IMO that this part of the library
0121   //       using boost ptree is end-of-life, and a better json library should be
0122   //       used
0123   ptree& raw_node(const string_path& key);
0124 
0125   // get the type info
0126   std::string type() const { return get<std::string>(TYPE_KEY); }
0127 
0128   // add a value to the configuration
0129   template <class T> void set(const string_path& key, const T& value) {
0130     settings_.put(key, value);
0131   }
0132 
0133   // Universal getter
0134   // Use these instead of the get<T>() member functions below for more readible
0135   // code
0136   configuration_impl::value_proxy value(const string_path& key) const;
0137   configuration_impl::value_proxy operator[](const string_path& key) const;
0138 
0139   // Three pairs of functions to get a setting by its key.
0140   //
0141   // In each pair, the translation_map version will lookup the
0142   // configuration value in the map, and throw a
0143   // configuration_translation_error if the lookup failed.
0144   //
0145   // 1. optional version
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   // 2. Throwing version
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   // 3. default-value version
0155   //      if default_value is needed
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   // same getters, but in (STL) vector version
0162   // 1. optional version
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   // 2. Throwing version
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   // special physics vector versions
0176   // 1. optional versions
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   // 2. throwing versions
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   // special version to create bit pattern of a vector of
0200   // bit patterns
0201   // 1. optional versions
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   // 2. throwing versions
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   // special version to get an interval from a "range" vector
0212   // 1. optional versions
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   // 2. throwing versions
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   // Helper functions to construct exceptions
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   // settings
0245   string_path settings_path_;
0246   ptree settings_;
0247   // defaults
0248   string_path defaults_path_;
0249   ptree defaults_;
0250 };
0251 } // namespace lager
0252 
0253 // =============================================================================
0254 // configurable mixin
0255 //
0256 // Derive classes that should be configurable from the configurable mixin to
0257 // automatically take care of configuration storage/loading/saving.
0258 //
0259 // The path member function returns the configuration path of the object, making
0260 // it easier to create path hierarchies for nested objects.
0261 // =============================================================================
0262 namespace lager {
0263 class configurable {
0264 public:
0265   // ptree-constructor is deprecated, configuration constructor prefered
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 } // namespace lager
0279 
0280 // =============================================================================
0281 // Definition: exceptions
0282 // =============================================================================
0283 namespace lager {
0284 // exceptions
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 // Implementation: configuration value proxies with auto casting
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 } // namespace configuration_impl
0345 
0346 // =============================================================================
0347 // Implementation: configuration getters
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 // and vector versions
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 // and 3-Vector version
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 // and 4-Vector version
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 // and bitpattern versiosn
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 // and "range" (interval) version
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 // HACK: expose raw ptree node, because we need this right now to directly
0623 //       loop over an array of settings
0624 //       the fact we need to do this means IMO that this part of the library
0625 //       using boost ptree is end-of-life, and a better json library should be
0626 //       used
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 // Implementation: configuration errors
0637 // =============================================================================
0638 
0639 // configuration_translation_error<T> impl
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 // "manual" translation (private)
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 // further configuration_translation_error implementation
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 } // namespace lager
0677 
0678 #endif