$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]] std::vector<std::string> get_factory_keys() const {
230  std::vector<std::string> keys;
231  for (const auto& [key, value] : available_) {
232  keys.emplace_back(key);
233  }
234  return keys;
235  }
236 
240  [[nodiscard]] bool has_factory(const std::string& key) const { return available_.count(key); }
241 
248  bool add_factory(const std::string& key, Box&& s, MakeFunc f) {
249  if (!available_.count(key)) {
250  available_.insert(std::make_pair(key, TypeFactory{std::move(s), std::move(f)}));
251  reset_schema();
252  return true;
253  }
254  return false;
255  }
256 
260  void set_factory(const std::string& key, Box&& s, MakeFunc f) {
261  if (!available_.count(key)) {
262  available_.erase(key);
263  }
264  available_.insert(std::make_pair(key, TypeFactory{std::move(s), std::move(f)}));
265  reset_schema();
266  }
267 
281  template <typename F,
282  std::enable_if_t<(std::is_default_constructible_v<F> &&
283  std::is_convertible_v<std::unique_ptr<F>, T>),
284  int> = 0>
285  void add_default_factory(const std::string& key) {
286  add_factory(key, make_prototype<F>().get_confable_schema(), [](const Conf& c) -> T {
287  auto ptr = std::make_unique<F>();
288  ptr->from_conf(c);
289  return ptr;
290  });
291  }
292 
293  public: // Overrides
294  [[nodiscard]] Json json_schema() const override {
295  Json j;
296  if (available_.empty()) {
297  j["not"] = Json{
298  {"description", "no variants available"},
299  };
300  } else {
301  j["oneOf"] = factory_json_schemas();
302  }
303  this->augment_schema(j);
304  return j;
305  }
306 
307  bool validate(const Conf& c, std::optional<SchemaError>& err) const override {
308  assert(schema_ != nullptr);
309  auto factory = c.get<std::string>(factory_key_);
310  if (!available_.count(factory)) {
311  return this->set_error(err, c, "unknown factory: {}", factory);
312  }
313 
314  return schema_->validate(c, err);
315  }
316 
317  [[nodiscard]] Type make(const Conf& c) const { return deserialize(c); }
318 
319  [[nodiscard]] Type deserialize(const Conf& c) const {
320  assert(schema_ != nullptr);
321  auto factory = c.get<std::string>(factory_key_);
322  if (!available_.count(factory)) {
323  throw this->error(c, "unknown factory: {}", factory);
324  }
325 
326  Conf args;
327  if (args_subset_) {
328  if (!args_key_.empty()) {
329  if (c.has(args_key_)) {
330  args = c.at(args_key_);
331  }
332  } else {
333  args = c;
334  args.erase(factory_key_);
335  }
336  } else {
337  args = c;
338  }
339 
340  return available_.at(factory).func(args);
341  }
342 
343  [[nodiscard]] Json serialize(const Type& x) const { return x; }
344 
345  void serialize_into(Json& j, const Type& x) const { j = serialize(x); }
346 
347  void deserialize_into(const Conf& c, Type& x) const { x = deserialize(c); }
348 
349  void from_conf(const Conf& /* unused */) override {
350  throw std::logic_error("FactoryBase::from_conf() should not be used");
351  }
352 
353  using Interface::to_json;
354  void to_json(Json& /* unused */) const override {
355  throw std::logic_error("FactoryBase::to_json() should not be used");
356  }
357 
358  void reset_ptr() override {
359  // No pointer, so nothing to do here.
360  }
361 
362  protected:
363  void reset_schema() {
364  if (available_.size() == 0) {
365  return;
366  }
367  schema_ = std::make_unique<Variant>(factory_schemas());
368  }
369 
370  [[nodiscard]] std::vector<Box> factory_schemas() const {
371  std::vector<Box> out;
372  out.reserve(available_.size());
373  for (auto& kv : available_) {
374  Struct base{
375  {factory_key_, make_const_schema(kv.first, "name of factory").require()},
376  };
377  if (args_key_.empty()) {
378  base.set_properties_from(kv.second.schema);
379  } else {
380  base.set_property(args_key_, kv.second.schema.clone());
381  }
382  base.reset_ptr();
383 
384  if (transform_func_) {
385  out.emplace_back(transform_func_(std::move(base)));
386  } else {
387  out.emplace_back(std::move(base));
388  }
389  }
390  return out;
391  }
392 
393  [[nodiscard]] std::vector<Json> factory_json_schemas() const {
394  auto schemas = factory_schemas();
395  std::vector<Json> out;
396  out.reserve(schemas.size());
397  for (auto& s : schemas) {
398  out.emplace_back(s.json_schema());
399  }
400  return out;
401  }
402 
403  protected:
404  std::unique_ptr<Variant> schema_;
405  TransformFunc transform_func_;
406  FactoryMap available_;
407  std::string factory_key_{"factory"};
408  std::string args_key_{"args"};
409  bool args_subset_{true};
410 };
411 
416 template <typename T>
418  public:
420  FactoryPointerless() = default;
421  FactoryPointerless(const FactoryPointerless<T>& other) = default;
422  FactoryPointerless(FactoryPointerless<T>&& other) noexcept = default;
423  FactoryPointerless<T>& operator=(const FactoryPointerless<T>& other) = default;
424  FactoryPointerless<T>& operator=(FactoryPointerless<T>&& other) noexcept = default;
425  ~FactoryPointerless() override = default;
426 };
427 
435 template <typename T>
436 class Factory : public FactoryBase<T, Factory<T>> {
437  public: // Types
438  using Type = typename FactoryBase<T, Factory<T>>::Type;
439  using MakeFunc = typename FactoryBase<T, Factory<T>>::MakeFunc;
440  using TypeFactory = typename FactoryBase<T, Factory<T>>::TypeFactory;
441  using FactoryMap = typename FactoryBase<T, Factory<T>>::FactoryMap;
442  using FactoryPairList = typename FactoryBase<T, Factory<T>>::FactoryPairList;
443 
444  public: // Constructors
446  Factory(const Factory<T>& other) = default;
447  Factory(Factory<T>&& other) noexcept = default;
448  Factory<T>& operator=(const Factory<T>& other) = default;
449  Factory<T>& operator=(Factory<T>&& other) noexcept = default;
450  ~Factory() override = default;
451 
452  Factory(Type* ptr, std::string desc) : FactoryBase<T, Factory<T>>(std::move(desc)), ptr_(ptr) {}
453 
454  Factory(Type* ptr, std::string desc, FactoryMap&& fs)
455  : FactoryBase<T, Factory<T>>(std::move(desc)), ptr_(ptr) {
456  for (auto&& f : fs) {
457  this->available_.insert(f);
458  }
459  this->reset_schema();
460  }
461 
462  Factory(Type* ptr, std::string desc, FactoryPairList fs)
463  : FactoryBase<T, Factory<T>>(std::move(desc)), ptr_(ptr) {
464  for (auto&& f : fs) {
465  this->available_.insert(f);
466  }
467  this->reset_schema();
468  }
469 
470  public: // Overrides
471  void from_conf(const Conf& c) override {
472  assert(ptr_ != nullptr);
473  *ptr_ = this->deserialize(c);
474  }
475 
476  void to_json(Json& j) const override {
477  assert(ptr_ != nullptr);
478  j = this->serialize(*ptr_);
479  }
480 
481  void reset_ptr() override { ptr_ = nullptr; }
482 
483  private:
484  Type* ptr_{nullptr};
485 };
486 
487 } // namespace fable::schema
Definition: conf.hpp:82
bool has(const std::string &key) const
Definition: conf.hpp:166
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:298
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:285
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:294
void from_conf(const Conf &) override
Definition: factory.hpp:349
bool has_factory(const std::string &key) const
Definition: factory.hpp:240
void reset_ptr() override
Definition: factory.hpp:358
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:260
std::vector< std::string > get_factory_keys() const
Definition: factory.hpp:229
CRTP args_key(const std::string &keyword) &&
Definition: factory.hpp:145
void to_json(Json &) const override
Definition: factory.hpp:354
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:307
bool add_factory(const std::string &key, Box &&s, MakeFunc f)
Definition: factory.hpp:248
Definition: factory.hpp:417
Definition: factory.hpp:436
void from_conf(const Conf &c) override
Definition: factory.hpp:471
void to_json(Json &j) const override
Definition: factory.hpp:476
void reset_ptr() override
Definition: factory.hpp:481
virtual Json to_json() const
Definition: interface.hpp:254
Definition: struct.hpp:70
nlohmann::json Json
Definition: fable_fwd.hpp:35
nlohmann::json::value_t JsonType
Definition: json.hpp:78
Definition: factory.hpp:64