Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-01-30 09:32:42

0001 // SPDX-License-Identifier: LGPL-3.0-or-later
0002 // Copyright (C) 2022 Wouter Deconinck, Sylvester Joosten
0003 //
0004 // Defines the Configurable base class and related PropertyMixin
0005 // These base classes provide access to the Property<T> object, a self-registering
0006 // configurable property that acts as a bare object T from a performance point-of-view
0007 //
0008 #pragma once
0009 
0010 #include <cstdint>
0011 #include <map>
0012 #include <string>
0013 #include <variant>
0014 #include <vector>
0015 
0016 #include <fmt/format.h>
0017 #include <fmt/ranges.h>
0018 
0019 #include <algorithms/detail/upcast.h>
0020 #include <algorithms/error.h>
0021 #include <algorithms/name.h>
0022 
0023 namespace algorithms {
0024 
0025 class PropertyError : public Error {
0026 public:
0027   PropertyError(std::string_view msg) : Error{msg, "algorithms::PropertyError"} {}
0028 };
0029 
0030 // Data types supported for Properties, defined as std::variant. This allows for
0031 // automatic Property registration with the calling framework, and enables compile-time
0032 // type errors.
0033 using PropertyValue = std::variant<bool, uint32_t, int32_t, uint64_t, int64_t, double, std::string,
0034                                    std::vector<bool>, std::vector<uint32_t>, std::vector<int32_t>,
0035                                    std::vector<uint64_t>, std::vector<int64_t>, std::vector<double>,
0036                                    std::vector<std::string>>;
0037 
0038 // Configuration/property handling
0039 class Configurable {
0040 public:
0041   class PropertyBase;
0042   using PropertyMap = std::map<std::string_view, PropertyBase&>;
0043 
0044   template <typename T> void setProperty(std::string_view name, T&& value) {
0045     m_props.at(name).set(static_cast<detail::upcast_type_t<T>>(value));
0046   }
0047   template <typename T> T getProperty(std::string_view name) const {
0048     return std::get<T>(m_props.at(name).get());
0049   }
0050   const PropertyMap& getProperties() const { return m_props; }
0051   bool hasProperty(std::string_view name) const {
0052     return m_props.count(name) && m_props.at(name).hasValue();
0053   }
0054   // get a vector of the names of all missing (unset) properties
0055   auto missingProperties() const {
0056     std::vector<std::string_view> missing;
0057     for (const auto& [name, prop] : m_props) {
0058       if (!prop.hasValue()) {
0059         missing.push_back(name);
0060       }
0061     }
0062     return missing;
0063   }
0064   // Throw an exception if any properties are not set
0065   void validate() const {
0066     const auto missing = missingProperties();
0067     if (!missing.empty()) {
0068       throw PropertyError(fmt::format("Missing properties: {}", missing));
0069     }
0070   }
0071 
0072 private:
0073   void registerProperty(PropertyBase& prop) {
0074     if (m_props.count(prop.name())) {
0075       throw PropertyError(fmt::format("Duplicate property name: {}", prop.name()));
0076     }
0077     m_props.emplace(prop.name(), prop);
0078   }
0079 
0080   PropertyMap m_props;
0081 
0082 public:
0083   class PropertyBase : public NameMixin {
0084   public:
0085     PropertyBase(std::string_view name, std::string_view description)
0086         : NameMixin{name, description} {}
0087     virtual void set(const PropertyValue& v) = 0;
0088     virtual PropertyValue get() const        = 0;
0089     bool hasValue() const { return m_has_value; }
0090 
0091   protected:
0092     bool m_has_value = false;
0093   };
0094 
0095   // A property type that auto-registers itself with the property handler
0096   // Essentially a simplified and const-like version of Gaudi::Property
0097   template <class T> class Property : public PropertyBase {
0098   public:
0099     using value_type = T;
0100     using impl_type  = detail::upcast_type_t<T>;
0101 
0102     Property(Configurable* owner, std::string_view name, std::string_view description)
0103         : PropertyBase{name, description} {
0104       if (owner) {
0105         owner->registerProperty(*this);
0106       } else {
0107         throw PropertyError(
0108             fmt::format("Attempting to create Property '{}' without valid owner", name));
0109       }
0110     }
0111     Property(Configurable* owner, std::string_view name, const value_type& v,
0112              std::string_view description)
0113         : Property(owner, name, description) {
0114       set(static_cast<impl_type>(v));
0115     }
0116 
0117     Property()                = delete;
0118     Property(const Property&) = default;
0119     Property& operator=(const Property&) = default;
0120 
0121     // Only settable by explicitly calling the ::set() member function
0122     // as we want the Property to mostly act as if it is constant
0123     virtual void set(const PropertyValue& v) {
0124       m_value     = static_cast<value_type>(std::get<impl_type>(v));
0125       m_has_value = true;
0126     }
0127     // virtual getter for use from PropertyBase - use ::value() instead for direct member
0128     // access
0129     virtual PropertyValue get() const { return static_cast<impl_type>(m_value); }
0130 
0131     // Direct access to the value. Use this one whenever possible (or go through the
0132     // automatic casting)
0133     const value_type& value() const { return m_value; }
0134 
0135     // automatically cast to T
0136     operator T() const { return m_value; }
0137 
0138     // act as if this is a const T
0139     template <typename U> bool operator==(const U& rhs) const { return m_value == rhs; }
0140     template <typename U> bool operator!=(const U& rhs) const { return m_value != rhs; }
0141     template <typename U> bool operator>(const U& rhs) const { return m_value > rhs; }
0142     template <typename U> bool operator>=(const U& rhs) const { return m_value >= rhs; }
0143     template <typename U> bool operator<(const U& rhs) const { return m_value < rhs; }
0144     template <typename U> bool operator<=(const U& rhs) const { return m_value <= rhs; }
0145     template <typename U> decltype(auto) operator+(const U& rhs) const { return m_value + rhs; }
0146     template <typename U> decltype(auto) operator-(const U& rhs) const { return m_value - rhs; }
0147     template <typename U> decltype(auto) operator*(const U& rhs) const { return m_value * rhs; }
0148     template <typename U> decltype(auto) operator/(const U& rhs) const { return m_value / rhs; }
0149 
0150     // stl collection helpers if needed
0151     // forced to be templated so we only declare them when used
0152     template <class U = const value_type> decltype(auto) size() const { return value().size(); }
0153     template <class U = const value_type> decltype(auto) length() const { return value().length(); }
0154     template <class U = const value_type> decltype(auto) empty() const { return value().empty(); }
0155     template <class U = value_type> decltype(auto) clear() { value().clear(); }
0156     template <class U = const value_type> decltype(auto) begin() const { return value().begin(); }
0157     template <class U = const value_type> decltype(auto) end() const { return value().end(); }
0158     template <class U = value_type> decltype(auto) begin() { return value().begin(); }
0159     template <class U = value_type> decltype(auto) end() { return value().end(); }
0160     template <class Arg> decltype(auto) operator[](const Arg& arg) const { return value()[arg]; }
0161     template <class Arg> decltype(auto) operator[](const Arg& arg) { return value()[arg]; }
0162     template <class U = const value_type>
0163     decltype(auto) find(const typename U::key_type& key) const {
0164       return value().find(key);
0165     }
0166     template <class U = value_type> decltype(auto) find(const typename U::key_type& key) {
0167       return value().find(key);
0168     }
0169     template <class Arg> decltype(auto) erase(const Arg& arg) { return value().erase(arg); }
0170 
0171     // In case our property has operator (), delegate the operator()
0172     template <class... Args>
0173     decltype(std::declval<value_type>()(std::declval<Args&&>()...))
0174     operator()(Args&&... args) const {
0175       return m_value()(std::forward<Args>(args)...);
0176     }
0177 
0178   private:
0179     T m_value;
0180   };
0181 }; // namespace algorithms
0182 
0183 // Property mixin, provides all the configuration functionality for
0184 // our algorithms and services
0185 // Currently an alias to Configurable
0186 using PropertyMixin = Configurable;
0187 
0188 } // namespace algorithms
0189 
0190 // operator== overload not needed in C++20 as it will call the member version
0191 #if __cpp_impl_three_way_comparison < 201711
0192 template <class T, class U>
0193 bool operator==(const U& rhs, const algorithms::Configurable::Property<T>& p) {
0194   return p == rhs;
0195 }
0196 #endif
0197 template <class T>
0198 std::ostream& operator<<(std::ostream& os, const algorithms::Configurable::Property<T>& p) {
0199   return os << p.value();
0200 }
0201 
0202 // Make Property formateble
0203 template <class T>
0204 struct fmt::formatter<algorithms::Configurable::Property<T>> : fmt::formatter<T> {};
0205 // others needed??? TODO