$darkmode
factory.hpp
Go to the documentation of this file.
1 /*
2  * Copyright 2020 Robert Bosch GmbH
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  *
16  * SPDX-License-Identifier: Apache-2.0
17  */
23 #pragma once
24 
25 #include <functional> // for function<>
26 #include <limits> // for numeric_limits<>
27 #include <map> // for map<>
28 #include <memory> // for shared_ptr<>
29 #include <string> // for string
30 #include <type_traits> // for enable_if_t<>, is_base_of<>
31 #include <utility> // for move
32 #include <vector> // for vector<>
33 
34 #include <fable/schema/const.hpp> // for Const
35 #include <fable/schema/interface.hpp> // for Base<>, Box
36 #include <fable/schema/string.hpp> // for String
37 #include <fable/schema/struct.hpp> // for Struct
38 #include <fable/schema/variant.hpp> // for Variant
39 
40 namespace fable::schema {
41 
55 template <typename T, typename CRTP>
56 class FactoryBase : public Base<CRTP> {
57  public: // Types
58  using Type = T;
59  using MakeFunc = std::function<T(const Conf& c)>;
60 
64  struct TypeFactory {
65  TypeFactory(Box s, MakeFunc f) : schema(std::move(s)), func(std::move(f)) {
66  schema.reset_ptr();
67  }
68 
69  Box schema; // NOLINT
70  MakeFunc func; // NOLINT
71  };
72 
73  using TransformFunc = std::function<Box(Struct&&)>;
74  using FactoryMap = std::map<std::string, TypeFactory>;
75  using FactoryPairList = std::initializer_list<std::pair<std::string, TypeFactory>>;
76 
77  public: // Constructors
78  ~FactoryBase() noexcept override = default;
79 
80  protected:
81  FactoryBase(const FactoryBase& other)
82  : Base<CRTP>(other)
83  , transform_func_(other.transform_func_)
84  , available_(other.available_)
85  , factory_key_(other.factory_key_)
86  , args_key_(other.args_key_)
87  , args_subset_(other.args_subset_) {
88  reset_schema();
89  }
90 
91  FactoryBase& operator=(const FactoryBase& other) {
92  if (&other == this) {
93  return *this;
94  }
95  Base<CRTP>::operator=(other);
96  transform_func_ = other.transform_func_;
97  available_ = other.available_;
98  factory_key_ = other.factory_key_;
99  args_key_ = other.args_key_;
100  args_subset_ = other.args_subset_;
101  reset_schema();
102  return *this;
103  }
104 
105  FactoryBase(FactoryBase&&) noexcept = default;
106 
107  FactoryBase& operator=(FactoryBase&&) noexcept = default;
108 
109  public:
118  explicit FactoryBase(std::string desc = "") : Base<CRTP>(JsonType::object, std::move(desc)) {}
119 
120  FactoryBase(std::string desc, FactoryPairList fs)
121  : Base<CRTP>(JsonType::object, std::move(desc)), available_(std::move(fs)) {
122  reset_schema();
123  }
124 
125  FactoryBase(std::string desc, FactoryMap&& fs)
126  : Base<CRTP>(JsonType::object, std::move(desc)), available_(std::move(fs)) {
127  reset_schema();
128  }
129 
135  CRTP factory_key(const std::string& keyword) && {
136  set_factory_key(keyword);
137  return std::move(*dynamic_cast<CRTP*>(this));
138  }
139 
145  CRTP args_key(const std::string& keyword) && {
146  set_args_key(keyword);
147  return std::move(*dynamic_cast<CRTP*>(this));
148  }
149 
155  CRTP args_subset(bool value) && {
156  set_args_subset(value);
157  return std::move(*dynamic_cast<CRTP*>(this));
158  }
159 
165  CRTP transform_schema(TransformFunc f) && {
167  return std::move(*dynamic_cast<CRTP*>(this));
168  }
169 
170  public: // Special
177  void set_factory_key(const std::string& keyword) {
178  assert(!keyword.empty());
179  factory_key_ = keyword;
180  reset_schema();
181  }
182 
192  void set_args_key(const std::string& keyword) {
193  args_key_ = keyword;
194  reset_schema();
195  }
196 
208  void set_args_subset(bool value) { args_subset_ = value; }
209 
217  void set_transform_schema(TransformFunc f) { transform_func_ = std::move(f); }
218 
222  [[nodiscard]] const TypeFactory& get_factory(const std::string& key) const {
223  return available_.at(key);
224  }
225 
229  [[nodiscard]] bool has_factory(const std::string& key) const { return available_.count(key); }
230 
237  bool add_factory(const std::string& key, Box&& s, MakeFunc f) {
238  if (!available_.count(key)) {
239  available_.insert(std::make_pair(key, TypeFactory{std::move(s), std::move(f)}));
240  reset_schema();
241  return true;
242  }
243  return false;
244  }
245 
249  void set_factory(const std::string& key, Box&& s, MakeFunc f) {
250  if (!available_.count(key)) {
251  available_.erase(key);
252  }
253  available_.insert(std::make_pair(key, TypeFactory{std::move(s), std::move(f)}));
254  reset_schema();
255  }
256 
270  template <typename F,
271  std::enable_if_t<(std::is_default_constructible_v<F> &&
272  std::is_convertible_v<std::unique_ptr<F>, T>),
273  int> = 0>
274  void add_default_factory(const std::string& key) {
275  add_factory(key, make_prototype<F>().get_confable_schema(), [](const Conf& c) -> T {
276  auto ptr = std::make_unique<F>();
277  ptr->from_conf(c);
278  return ptr;
279  });
280  }
281 
282  public: // Overrides
283  [[nodiscard]] Json json_schema() const override {
284  Json j;
285  if (available_.empty()) {
286  j["not"] = Json{
287  {"description", "no variants available"},
288  };
289  } else {
290  j["oneOf"] = factory_json_schemas();
291  }
292  this->augment_schema(j);
293  return j;
294  }
295 
296  bool validate(const Conf& c, std::optional<SchemaError>& err) const override {
297  assert(schema_ != nullptr);
298  auto factory = c.get<std::string>(factory_key_);
299  if (!available_.count(factory)) {
300  return this->set_error(err, c, "unknown factory: {}", factory);
301  }
302 
303  return schema_->validate(c, err);
304  }
305 
306  [[nodiscard]] Type make(const Conf& c) const { return deserialize(c); }
307 
308  [[nodiscard]] Type deserialize(const Conf& c) const {
309  assert(schema_ != nullptr);
310  auto factory = c.get<std::string>(factory_key_);
311  if (!available_.count(factory)) {
312  throw this->error(c, "unknown factory: {}", factory);
313  }
314 
315  Conf args;
316  if (args_subset_) {
317  if (!args_key_.empty()) {
318  if (c.has(args_key_)) {
319  args = c.at(args_key_);
320  }
321  } else {
322  args = c;
323  args.erase(factory_key_);
324  }
325  } else {
326  args = c;
327  }
328 
329  return available_.at(factory).func(args);
330  }
331 
332  [[nodiscard]] Json serialize(const Type& x) const { return x; }
333 
334  void serialize_into(Json& j, const Type& x) const { j = serialize(x); }
335 
336  void deserialize_into(const Conf& c, Type& x) const { x = deserialize(c); }
337 
338  void from_conf(const Conf& /* unused */) override {
339  throw std::logic_error("FactoryBase::from_conf() should not be used");
340  }
341 
342  using Interface::to_json;
343  void to_json(Json& /* unused */) const override {
344  throw std::logic_error("FactoryBase::to_json() should not be used");
345  }
346 
347  void reset_ptr() override {
348  // No pointer, so nothing to do here.
349  }
350 
351  protected:
352  void reset_schema() {
353  if (available_.size() == 0) {
354  return;
355  }
356  schema_ = std::make_unique<Variant>(factory_schemas());
357  }
358 
359  [[nodiscard]] std::vector<Box> factory_schemas() const {
360  std::vector<Box> out;
361  out.reserve(available_.size());
362  for (auto& kv : available_) {
363  Struct base{
364  {factory_key_, make_const_schema(kv.first, "name of factory").require()},
365  };
366  if (args_key_.empty()) {
367  base.set_properties_from(kv.second.schema);
368  } else {
369  base.set_property(args_key_, kv.second.schema.clone());
370  }
371  base.reset_ptr();
372 
373  if (transform_func_) {
374  out.emplace_back(transform_func_(std::move(base)));
375  } else {
376  out.emplace_back(std::move(base));
377  }
378  }
379  return out;
380  }
381 
382  [[nodiscard]] std::vector<Json> factory_json_schemas() const {
383  auto schemas = factory_schemas();
384  std::vector<Json> out;
385  out.reserve(schemas.size());
386  for (auto& s : schemas) {
387  out.emplace_back(s.json_schema());
388  }
389  return out;
390  }
391 
392  protected:
393  std::unique_ptr<Variant> schema_;
394  TransformFunc transform_func_;
395  FactoryMap available_;
396  std::string factory_key_{"factory"};
397  std::string args_key_{"args"};
398  bool args_subset_{true};
399 };
400 
405 template <typename T>
407  public:
409  FactoryPointerless() = default;
410  FactoryPointerless(const FactoryPointerless<T>& other) = default;
411  FactoryPointerless(FactoryPointerless<T>&& other) noexcept = default;
412  FactoryPointerless<T>& operator=(const FactoryPointerless<T>& other) = default;
413  FactoryPointerless<T>& operator=(FactoryPointerless<T>&& other) noexcept = default;
414  ~FactoryPointerless() override = default;
415 };
416 
424 template <typename T>
425 class Factory : public FactoryBase<T, Factory<T>> {
426  public: // Types
427  using Type = typename FactoryBase<T, Factory<T>>::Type;
428  using MakeFunc = typename FactoryBase<T, Factory<T>>::MakeFunc;
429  using TypeFactory = typename FactoryBase<T, Factory<T>>::TypeFactory;
430  using FactoryMap = typename FactoryBase<T, Factory<T>>::FactoryMap;
431  using FactoryPairList = typename FactoryBase<T, Factory<T>>::FactoryPairList;
432 
433  public: // Constructors
435  Factory(const Factory<T>& other) = default;
436  Factory(Factory<T>&& other) noexcept = default;
437  Factory<T>& operator=(const Factory<T>& other) = default;
438  Factory<T>& operator=(Factory<T>&& other) noexcept = default;
439  ~Factory() override = default;
440 
441  Factory(Type* ptr, std::string desc) : FactoryBase<T, Factory<T>>(std::move(desc)), ptr_(ptr) {}
442 
443  Factory(Type* ptr, std::string desc, FactoryMap&& fs)
444  : FactoryBase<T, Factory<T>>(std::move(desc)), ptr_(ptr) {
445  for (auto&& f : fs) {
446  this->available_.insert(f);
447  }
448  this->reset_schema();
449  }
450 
451  Factory(Type* ptr, std::string desc, FactoryPairList fs)
452  : FactoryBase<T, Factory<T>>(std::move(desc)), ptr_(ptr) {
453  for (auto&& f : fs) {
454  this->available_.insert(f);
455  }
456  this->reset_schema();
457  }
458 
459  public: // Overrides
460  void from_conf(const Conf& c) override {
461  assert(ptr_ != nullptr);
462  *ptr_ = this->deserialize(c);
463  }
464 
465  void to_json(Json& j) const override {
466  assert(ptr_ != nullptr);
467  j = this->serialize(*ptr_);
468  }
469 
470  void reset_ptr() override { ptr_ = nullptr; }
471 
472  private:
473  Type* ptr_{nullptr};
474 };
475 
476 } // namespace fable::schema
Definition: conf.hpp:76
bool has(const std::string &key) const
Definition: conf.hpp:132
size_t erase(const std::string &key)
Definition: conf.cpp:74
Conf at(const std::string &key) const
Definition: conf.cpp:58
T get() const
Definition: conf.hpp:166
Definition: interface.hpp:398
Definition: interface.hpp:297
void reset_ptr() override
Definition: interface.hpp:381
Definition: factory.hpp:56
void add_default_factory(const std::string &key)
Definition: factory.hpp:274
void set_transform_schema(TransformFunc f)
Definition: factory.hpp:217
void set_args_subset(bool value)
Definition: factory.hpp:208
Json json_schema() const override
Definition: factory.hpp:283
void from_conf(const Conf &) override
Definition: factory.hpp:338
bool has_factory(const std::string &key) const
Definition: factory.hpp:229
void reset_ptr() override
Definition: factory.hpp:347
const TypeFactory & get_factory(const std::string &key) const
Definition: factory.hpp:222
void set_factory(const std::string &key, Box &&s, MakeFunc f)
Definition: factory.hpp:249
CRTP args_key(const std::string &keyword) &&
Definition: factory.hpp:145
void to_json(Json &) const override
Definition: factory.hpp:343
void set_factory_key(const std::string &keyword)
Definition: factory.hpp:177
CRTP factory_key(const std::string &keyword) &&
Definition: factory.hpp:135
CRTP args_subset(bool value) &&
Definition: factory.hpp:155
void set_args_key(const std::string &keyword)
Definition: factory.hpp:192
CRTP transform_schema(TransformFunc f) &&
Definition: factory.hpp:165
bool validate(const Conf &c, std::optional< SchemaError > &err) const override
Definition: factory.hpp:296
bool add_factory(const std::string &key, Box &&s, MakeFunc f)
Definition: factory.hpp:237
Definition: factory.hpp:406
Definition: factory.hpp:425
void from_conf(const Conf &c) override
Definition: factory.hpp:460
void to_json(Json &j) const override
Definition: factory.hpp:465
void reset_ptr() override
Definition: factory.hpp:470
virtual Json to_json() const
Definition: interface.hpp:254
Definition: struct.hpp:70
nlohmann::json Json
Definition: fable_fwd.hpp:35
nlohmann::detail::value_t JsonType
Definition: fable_fwd.hpp:37
Definition: factory.hpp:64