File indexing completed on 2025-10-30 08:10:18
0001 
0002 
0003 
0004 
0005 
0006 
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 
0031 
0032 
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 
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   
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   
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   
0096   
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     
0122     
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     
0128     
0129     virtual PropertyValue get() const { return static_cast<impl_type>(m_value); }
0130 
0131     
0132     
0133     const value_type& value() const { return m_value; }
0134 
0135     
0136     operator T() const { return m_value; }
0137 
0138     
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     
0151     
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     
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 }; 
0182 
0183 
0184 
0185 
0186 using PropertyMixin = Configurable;
0187 
0188 } 
0189 
0190 
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 
0203 template <class T>
0204 struct fmt::formatter<algorithms::Configurable::Property<T>> : fmt::formatter<T> {};
0205