algorithms  2.2.0
Framework-independent algorithms for the EIC
Loading...
Searching...
No Matches
property.h
Go to the documentation of this file.
1// SPDX-License-Identifier: LGPL-3.0-or-later
2// Copyright (C) 2022 Wouter Deconinck, Sylvester Joosten
3//
4// Defines the Configurable base class and related PropertyMixin
5// These base classes provide access to the Property<T> object, a self-registering
6// configurable property that acts as a bare object T from a performance point-of-view
7//
8#pragma once
9
10#include <concepts>
11#include <cstdint>
12#include <map>
13#include <string>
14#include <variant>
15#include <vector>
16
17#include <fmt/format.h>
18#include <fmt/ranges.h>
19
21#include <algorithms/error.h>
22#include <algorithms/name.h>
23
24namespace algorithms {
25
26class PropertyError : public Error {
27public:
28 PropertyError(std::string_view msg) : Error{msg, "algorithms::PropertyError"} {}
29};
30
31// Data types supported for Properties, defined as std::variant. This allows for
32// automatic Property registration with the calling framework, and enables compile-time
33// type errors.
34using PropertyValue = std::variant<bool, uint32_t, int32_t, uint64_t, int64_t, double, std::string,
35 std::vector<bool>, std::vector<uint32_t>, std::vector<int32_t>,
36 std::vector<uint64_t>, std::vector<int64_t>, std::vector<double>,
37 std::vector<std::string>>;
38
39// Configuration/property handling
41public:
42 class PropertyBase;
43 using PropertyMap = std::map<std::string_view, PropertyBase&>;
44
45 template <typename T> void setProperty(std::string_view name, T&& value) {
46 m_props.at(name).set(static_cast<detail::upcast_type_t<T>>(value));
47 }
48 template <typename T> T getProperty(std::string_view name) const {
49 return std::get<T>(m_props.at(name).get());
50 }
51 const PropertyMap& getProperties() const { return m_props; }
52 bool hasProperty(std::string_view name) const {
53 return m_props.count(name) && m_props.at(name).hasValue();
54 }
55 // get a vector of the names of all missing (unset) properties
56 auto missingProperties() const {
57 std::vector<std::string_view> missing;
58 for (const auto& [name, prop] : m_props) {
59 if (!prop.hasValue()) {
60 missing.push_back(name);
61 }
62 }
63 return missing;
64 }
65 // Throw an exception if any properties are not set
66 void validate() const {
67 const auto missing = missingProperties();
68 if (!missing.empty()) {
69 throw PropertyError(fmt::format("Missing properties: {}", missing));
70 }
71 }
72
73private:
74 void registerProperty(PropertyBase& prop) {
75 if (m_props.count(prop.name())) {
76 throw PropertyError(fmt::format("Duplicate property name: {}", prop.name()));
77 }
78 m_props.emplace(prop.name(), prop);
79 }
80
81 PropertyMap m_props;
82
83public:
84 class PropertyBase : public NameMixin {
85 public:
86 PropertyBase(std::string_view name, std::string_view description)
88 virtual void set(const PropertyValue& v) = 0;
89 virtual PropertyValue get() const = 0;
90 bool hasValue() const { return m_has_value; }
91
92 protected:
93 bool m_has_value = false;
94 };
95
96 // A property type that auto-registers itself with the property handler
97 // Essentially a simplified and const-like version of Gaudi::Property
98 template <class T> class Property : public PropertyBase {
99 public:
100 using value_type = T;
102
103 Property(Configurable* owner, std::string_view name, std::string_view description)
105 if (owner) {
106 owner->registerProperty(*this);
107 } else {
108 throw PropertyError(
109 fmt::format("Attempting to create Property '{}' without valid owner", name));
110 }
111 }
112 Property(Configurable* owner, std::string_view name, const value_type& v,
113 std::string_view description)
114 : Property(owner, name, description) {
115 set(static_cast<impl_type>(v));
116 }
117
118 Property() = delete;
119 Property(const Property&) = default;
120 Property& operator=(const Property&) = default;
121
122 // Only settable by explicitly calling the ::set() member function
123 // as we want the Property to mostly act as if it is constant
124 virtual void set(const PropertyValue& v) {
125 m_value = static_cast<value_type>(std::get<impl_type>(v));
126 m_has_value = true;
127 }
128 // virtual getter for use from PropertyBase - use ::value() instead for direct member
129 // access
130 virtual PropertyValue get() const { return static_cast<impl_type>(m_value); }
131
132 // Direct access to the value. Use this one whenever possible (or go through the
133 // automatic casting)
134 const value_type& value() const { return m_value; }
135
136 // automatically cast to T
137 operator T() const { return m_value; }
138
139 // act as if this is a const T
140 template <typename U> bool operator==(const U& rhs) const { return m_value == rhs; }
141 template <typename U> bool operator!=(const U& rhs) const { return m_value != rhs; }
142 template <typename U> bool operator>(const U& rhs) const { return m_value > rhs; }
143 template <typename U> bool operator>=(const U& rhs) const { return m_value >= rhs; }
144 template <typename U> bool operator<(const U& rhs) const { return m_value < rhs; }
145 template <typename U> bool operator<=(const U& rhs) const { return m_value <= rhs; }
146 template <typename U> decltype(auto) operator+(const U& rhs) const { return m_value + rhs; }
147 template <typename U> decltype(auto) operator-(const U& rhs) const { return m_value - rhs; }
148 template <typename U> decltype(auto) operator*(const U& rhs) const { return m_value * rhs; }
149 template <typename U> decltype(auto) operator/(const U& rhs) const { return m_value / rhs; }
150
151 // stl collection helpers if needed
152 // constrained so they only exist when the value type provides them, so generic code
153 // (e.g. range detection in fmt) does not mistake every Property for a collection
154 // (no mutating helpers, as the Property acts as if it is constant)
155 decltype(auto) size() const
156 requires requires(const T& v) { v.size(); }
157 {
158 return value().size();
159 }
160 decltype(auto) length() const
161 requires requires(const T& v) { v.length(); }
162 {
163 return value().length();
164 }
165 decltype(auto) empty() const
166 requires requires(const T& v) { v.empty(); }
167 {
168 return value().empty();
169 }
170 decltype(auto) begin() const
171 requires requires(const T& v) { v.begin(); }
172 {
173 return value().begin();
174 }
175 decltype(auto) end() const
176 requires requires(const T& v) { v.end(); }
177 {
178 return value().end();
179 }
180 template <class Arg>
181 requires requires(const T& v, const Arg& arg) { v[arg]; }
182 decltype(auto) operator[](const Arg& arg) const {
183 return value()[arg];
184 }
185 template <class Key>
186 requires requires(const T& v, const Key& key) { v.find(key); }
187 decltype(auto) find(const Key& key) const {
188 return value().find(key);
189 }
190
191 // In case our property has operator (), delegate the operator()
192 template <class... Args>
193 requires std::invocable<const T&, Args&&...>
194 decltype(auto) operator()(Args&&... args) const {
195 return m_value(std::forward<Args>(args)...);
196 }
197
198 private:
199 T m_value;
200 };
201}; // namespace algorithms
202
203// Property mixin, provides all the configuration functionality for
204// our algorithms and services
205// Currently an alias to Configurable
207
208} // namespace algorithms
209
210// operator== overload not needed in C++20 as it will call the member version
211#if __cpp_impl_three_way_comparison < 201711
212template <class T, class U>
214 return p == rhs;
215}
216#endif
217template <class T>
218 requires requires(std::ostream& os, const T& v) { os << v; }
219std::ostream& operator<<(std::ostream& os, const algorithms::Configurable::Property<T>& p) {
220 return os << p.value();
221}
222
223// Make Property formattable, as its value (also for collections, which are not formatted
224// as a range of the Property itself)
225template <class T>
226struct fmt::formatter<algorithms::Configurable::Property<T>> : fmt::formatter<T> {
227 template <class FormatContext>
228 auto format(const algorithms::Configurable::Property<T>& p, FormatContext& ctx) const {
229 return fmt::formatter<T>::format(p.value(), ctx);
230 }
231};
232template <class T, class Char>
233struct fmt::is_range<algorithms::Configurable::Property<T>, Char> : std::false_type {};
234// others needed??? TODO
virtual void set(const PropertyValue &v)=0
virtual PropertyValue get() const =0
PropertyBase(std::string_view name, std::string_view description)
Definition property.h:86
bool operator>(const U &rhs) const
Definition property.h:142
detail::upcast_type_t< T > impl_type
Definition property.h:101
const value_type & value() const
Definition property.h:134
bool operator>=(const U &rhs) const
Definition property.h:143
Property(Configurable *owner, std::string_view name, std::string_view description)
Definition property.h:103
Property(const Property &)=default
Property & operator=(const Property &)=default
decltype(auto) size() const
Definition property.h:155
bool operator!=(const U &rhs) const
Definition property.h:141
decltype(auto) begin() const
Definition property.h:170
bool operator<=(const U &rhs) const
Definition property.h:145
bool operator==(const U &rhs) const
Definition property.h:140
virtual PropertyValue get() const
Definition property.h:130
decltype(auto) length() const
Definition property.h:160
decltype(auto) find(const Key &key) const
Definition property.h:187
bool operator<(const U &rhs) const
Definition property.h:144
Property(Configurable *owner, std::string_view name, const value_type &v, std::string_view description)
Definition property.h:112
decltype(auto) empty() const
Definition property.h:165
decltype(auto) end() const
Definition property.h:175
virtual void set(const PropertyValue &v)
Definition property.h:124
void setProperty(std::string_view name, T &&value)
Definition property.h:45
void validate() const
Definition property.h:66
const PropertyMap & getProperties() const
Definition property.h:51
bool hasProperty(std::string_view name) const
Definition property.h:52
auto missingProperties() const
Definition property.h:56
std::map< std::string_view, PropertyBase & > PropertyMap
Definition property.h:43
T getProperty(std::string_view name) const
Definition property.h:48
Error(std::string_view msg, std::string_view type="algorithms::Error")
Definition error.h:15
std::string_view description() const
Definition name.h:18
NameMixin(std::string_view name, std::string_view description)
Definition name.h:15
std::string_view name() const
Definition name.h:17
PropertyError(std::string_view msg)
Definition property.h:28
typename upcast_type< std::remove_cvref_t< T > >::type upcast_type_t
Definition upcast.h:35
Configurable PropertyMixin
Definition property.h:206
std::variant< bool, uint32_t, int32_t, uint64_t, int64_t, double, std::string, std::vector< bool >, std::vector< uint32_t >, std::vector< int32_t >, std::vector< uint64_t >, std::vector< int64_t >, std::vector< double >, std::vector< std::string > > PropertyValue
Definition property.h:34
bool operator==(const U &rhs, const algorithms::Configurable::Property< T > &p)
Definition property.h:213
std::ostream & operator<<(std::ostream &os, const algorithms::Configurable::Property< T > &p)
Definition property.h:219
auto format(const algorithms::Configurable::Property< T > &p, FormatContext &ctx) const
Definition property.h:228