$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 <map> // for map<>
27 #include <memory> // for shared_ptr<>
28 #include <string> // for string
29 #include <type_traits> // for enable_if_t<>, is_base_of<>
30 #include <utility> // for move
31 #include <vector> // for vector<>
32 
33 #include <fable/schema/const.hpp> // for Const
34 #include <fable/schema/interface.hpp> // for Base<>, Box
35 #include <fable/schema/string.hpp> // for String
36 #include <fable/schema/struct.hpp> // for Struct
37 #include <fable/schema/variant.hpp> // for Variant
38 
39 namespace fable::schema {
40 
54 template <typename T, typename CRTP>
55 class FactoryBase : public Base<CRTP> {
56  public: // Types
57  using Type = T;
58  using MakeFunc = std::function<T(const Conf& c)>;
59 
63  struct TypeFactory {
64  TypeFactory(Box s, MakeFunc f) : schema(std::move(s)), func(std::move(f)) {
65  schema.reset_ptr();
66  }
67 
68  Box schema; // NOLINT
69  MakeFunc func; // NOLINT
70  };
71 
72  using TransformFunc = std::function<Box(Struct&&)>;
73  using FactoryMap = std::map<std::string, TypeFactory>;
74  using FactoryPairList = std::initializer_list<std::pair<std::string, TypeFactory>>;
75 
76  public: // Constructors
77  ~FactoryBase() noexcept override = default;
78 
79  protected:
80  FactoryBase(const FactoryBase& other)
81  : Base<CRTP>(other)
82  , transform_func_(other.transform_func_)
83  , available_(other.available_)
84  , factory_key_(other.factory_key_)
85  , args_key_(other.args_key_)
86  , args_subset_(other.args_subset_) {
87  reset_schema();
88  }
89 
90  FactoryBase& operator=(const FactoryBase& other) {
91  if (&other == this) {
92  return *this;
93  }
94  Base<CRTP>::operator=(other);
95  transform_func_ = other.transform_func_;
96  available_ = other.available_;
97  factory_key_ = other.factory_key_;
98  args_key_ = other.args_key_;
99  args_subset_ = other.args_subset_;
100  reset_schema();
101  return *this;
102  }
103 
104  FactoryBase(FactoryBase&&) noexcept = default;
105 
106  FactoryBase& operator=(FactoryBase&&) noexcept = default;
107 
108  public:
117  explicit FactoryBase(std::string desc = "") : Base<CRTP>(JsonType::object, std::move(desc)) {}
118 
119  FactoryBase(std::string desc, FactoryPairList fs)
120  : Base<CRTP>(JsonType::object, std::move(desc)), available_(std::move(fs)) {
121  reset_schema();
122  }
123 
124  FactoryBase(std::string desc, FactoryMap&& fs)
125  : Base<CRTP>(JsonType::object, std::move(desc)), available_(std::move(fs)) {
126  reset_schema();
127  }
128 
134  CRTP factory_key(const std::string& keyword) && {
135  set_factory_key(keyword);
136  return std::move(*dynamic_cast<CRTP*>(this));
137  }
138 
144  CRTP args_key(const std::string& keyword) && {
145  set_args_key(keyword);
146  return std::move(*dynamic_cast<CRTP*>(this));
147  }
148 
154  CRTP args_subset(bool value) && {
155  set_args_subset(value);
156  return std::move(*dynamic_cast<CRTP*>(this));
157  }
158 
164  CRTP transform_schema(TransformFunc f) && {
166  return std::move(*dynamic_cast<CRTP*>(this));
167  }
168 
169  public: // Special
176  void set_factory_key(const std::string& keyword) {
177  assert(!keyword.empty());
178  factory_key_ = keyword;
179  reset_schema();
180  }
181 
191  void set_args_key(const std::string& keyword) {
192  args_key_ = keyword;
193  reset_schema();
194  }
195 
207  void set_args_subset(bool value) { args_subset_ = value; }
208 
216  void set_transform_schema(TransformFunc f) { transform_func_ = std::move(f); }
217 
221  [[nodiscard]] const TypeFactory& get_factory(const std::string& key) const {
222  return available_.at(key);
223  }
224 
228  [[nodiscard]] std::vector<std::string> get_factory_keys() const {
229  std::vector<std::string> keys;
230  for (const auto& [key, value] : available_) {
231  keys.emplace_back(key);
232  }
233  return keys;
234  }
235 
239  [[nodiscard]] bool has_factory(const std::string& key) const { return available_.count(key); }
240 
247  bool add_factory(const std::string& key, Box&& s, MakeFunc f) {
248  if (!available_.count(key)) {
249  available_.insert(std::make_pair(key, TypeFactory{std::move(s), std::move(f)}));
250  reset_schema();
251  return true;
252  }
253  return false;
254  }
255 
259  void set_factory(const std::string& key, Box&& s, MakeFunc f) {
260  if (!available_.count(key)) {
261  available_.erase(key);
262  }
263  available_.insert(std::make_pair(key, TypeFactory{std::move(s), std::move(f)}));
264  reset_schema();
265  }
266 
280  template <typename F,
281  std::enable_if_t<(std::is_default_constructible_v<F> &&
282  std::is_convertible_v<std::unique_ptr<F>, T>),
283  int> = 0>
284  void add_default_factory(const std::string& key) {
285  add_factory(key, make_prototype<F>().get_confable_schema(), [](const Conf& c) -> T {
286  auto ptr = std::make_unique<F>();
287  ptr->from_conf(c);
288  return ptr;
289  });
290  }
291 
292  public: // Overrides
293  [[nodiscard]] Json json_schema() const override {
294  Json j;
295  if (available_.empty()) {
296  j["not"] = Json{
297  {"description", "no variants available"},
298  };
299  } else {
300  j["oneOf"] = factory_json_schemas();
301  }
302  this->augment_schema(j);
303  return j;
304  }
305 
306  bool validate(const Conf& c, std::optional<SchemaError>& err) const override {
307  assert(schema_ != nullptr);
308  auto factory = c.get<std::string>(factory_key_);
309  if (!available_.count(factory)) {
310  return this->set_error(err, c, "unknown factory: {}", factory);
311  }
312 
313  return schema_->validate(c, err);
314  }
315 
316  [[nodiscard]] Type make(const Conf& c) const { return deserialize(c); }
317 
318  [[nodiscard]] Type deserialize(const Conf& c) const {
319  assert(schema_ != nullptr);
320  auto factory = c.get<std::string>(factory_key_);
321  if (!available_.count(factory)) {
322  throw this->error(c, "unknown factory: {}", factory);
323  }
324 
325  Conf args;
326  if (args_subset_) {
327  if (!args_key_.empty()) {
328  if (c.has(args_key_)) {
329  args = c.at(args_key_);
330  }
331  } else {
332  args = c;
333  args.erase(factory_key_);
334  }
335  } else {
336  args = c;
337  }
338 
339  return available_.at(factory).func(args);
340  }
341 
342  [[nodiscard]] Json serialize(const Type& x) const { return x; }
343 
344  void serialize_into(Json& j, const Type& x) const { j = serialize(x); }
345 
346  void deserialize_into(const Conf& c, Type& x) const { x = deserialize(c); }
347 
348  void from_conf(const Conf& /* unused */) override {
349  throw std::logic_error("FactoryBase::from_conf() should not be used");
350  }
351 
352  using Interface::to_json;
353  void to_json(Json& /* unused */) const override {
354  throw std::logic_error("FactoryBase::to_json() should not be used");
355  }
356 
357  void reset_ptr() override {
358  // No pointer, so nothing to do here.
359  }
360 
361  protected:
362  void reset_schema() {
363  if (available_.size() == 0) {
364  return;
365  }
366  schema_ = std::make_unique<Variant>(factory_schemas());
367  }
368 
369  [[nodiscard]] std::vector<Box> factory_schemas() const {
370  std::vector<Box> out;
371  out.reserve(available_.size());
372  for (auto& kv : available_) {
373  Struct base{
374  {factory_key_, make_const_schema(kv.first, "name of factory").require()},
375  };
376  if (args_key_.empty()) {
377  base.set_properties_from(kv.second.schema);
378  } else {
379  base.set_property(args_key_, kv.second.schema.clone());
380  }
381  base.reset_ptr();
382 
383  if (transform_func_) {
384  out.emplace_back(transform_func_(std::move(base)));
385  } else {
386  out.emplace_back(std::move(base));
387  }
388  }
389  return out;
390  }
391 
392  [[nodiscard]] std::vector<Json> factory_json_schemas() const {
393  auto schemas = factory_schemas();
394  std::vector<Json> out;
395  out.reserve(schemas.size());
396  for (auto& s : schemas) {
397  out.emplace_back(s.json_schema());
398  }
399  return out;
400  }
401 
402  protected:
403  std::unique_ptr<Variant> schema_;
404  TransformFunc transform_func_;
405  FactoryMap available_;
406  std::string factory_key_{"factory"};
407  std::string args_key_{"args"};
408  bool args_subset_{true};
409 };
410 
415 template <typename T>
417  public:
419  FactoryPointerless() = default;
420  FactoryPointerless(const FactoryPointerless<T>& other) = default;
421  FactoryPointerless(FactoryPointerless<T>&& other) noexcept = default;
422  FactoryPointerless<T>& operator=(const FactoryPointerless<T>& other) = default;
423  FactoryPointerless<T>& operator=(FactoryPointerless<T>&& other) noexcept = default;
424  ~FactoryPointerless() override = default;
425 };
426 
434 template <typename T>
435 class Factory : public FactoryBase<T, Factory<T>> {
436  public: // Types
437  using Type = typename FactoryBase<T, Factory<T>>::Type;
438  using MakeFunc = typename FactoryBase<T, Factory<T>>::MakeFunc;
439  using TypeFactory = typename FactoryBase<T, Factory<T>>::TypeFactory;
440  using FactoryMap = typename FactoryBase<T, Factory<T>>::FactoryMap;
441  using FactoryPairList = typename FactoryBase<T, Factory<T>>::FactoryPairList;
442 
443  public: // Constructors
445  Factory(const Factory<T>& other) = default;
446  Factory(Factory<T>&& other) noexcept = default;
447  Factory<T>& operator=(const Factory<T>& other) = default;
448  Factory<T>& operator=(Factory<T>&& other) noexcept = default;
449  ~Factory() override = default;
450 
451  Factory(Type* ptr, std::string desc) : FactoryBase<T, Factory<T>>(std::move(desc)), ptr_(ptr) {}
452 
453  Factory(Type* ptr, std::string desc, FactoryMap&& fs)
454  : FactoryBase<T, Factory<T>>(std::move(desc)), ptr_(ptr) {
455  for (auto&& f : fs) {
456  this->available_.insert(f);
457  }
458  this->reset_schema();
459  }
460 
461  Factory(Type* ptr, std::string desc, FactoryPairList fs)
462  : FactoryBase<T, Factory<T>>(std::move(desc)), ptr_(ptr) {
463  for (auto&& f : fs) {
464  this->available_.insert(f);
465  }
466  this->reset_schema();
467  }
468 
469  public: // Overrides
470  void from_conf(const Conf& c) override {
471  assert(ptr_ != nullptr);
472  *ptr_ = this->deserialize(c);
473  }
474 
475  void to_json(Json& j) const override {
476  assert(ptr_ != nullptr);
477  j = this->serialize(*ptr_);
478  }
479 
480  void reset_ptr() override { ptr_ = nullptr; }
481 
482  private:
483  Type* ptr_{nullptr};
484 };
485 
486 } // namespace fable::schema
Definition: conf.hpp:81
bool has(const std::string &key) const
Definition: conf.hpp:165
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:297
Definition: interface.hpp:398
Definition: interface.hpp:297
void reset_ptr() override
Definition: interface.hpp:381
Definition: factory.hpp:55
void add_default_factory(const std::string &key)
Definition: factory.hpp:284
void set_transform_schema(TransformFunc f)
Definition: factory.hpp:216
void set_args_subset(bool value)
Definition: factory.hpp:207
Json json_schema() const override
Definition: factory.hpp:293
void from_conf(const Conf &) override
Definition: factory.hpp:348
bool has_factory(const std::string &key) const
Definition: factory.hpp:239
void reset_ptr() override
Definition: factory.hpp:357
const TypeFactory & get_factory(const std::string &key) const
Definition: factory.hpp:221
void set_factory(const std::string &key, Box &&s, MakeFunc f)
Definition: factory.hpp:259
std::vector< std::string > get_factory_keys() const
Definition: factory.hpp:228
CRTP args_key(const std::string &keyword) &&
Definition: factory.hpp:144
void to_json(Json &) const override
Definition: factory.hpp:353
void set_factory_key(const std::string &keyword)
Definition: factory.hpp:176
CRTP factory_key(const std::string &keyword) &&
Definition: factory.hpp:134
CRTP args_subset(bool value) &&
Definition: factory.hpp:154
void set_args_key(const std::string &keyword)
Definition: factory.hpp:191
CRTP transform_schema(TransformFunc f) &&
Definition: factory.hpp:164
bool validate(const Conf &c, std::optional< SchemaError > &err) const override
Definition: factory.hpp:306
bool add_factory(const std::string &key, Box &&s, MakeFunc f)
Definition: factory.hpp:247
Definition: factory.hpp:416
Definition: factory.hpp:435
void from_conf(const Conf &c) override
Definition: factory.hpp:470
void to_json(Json &j) const override
Definition: factory.hpp:475
void reset_ptr() override
Definition: factory.hpp:480
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:63