$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 #ifndef FABLE_SCHEMA_FACTORY_HPP_
25 #define FABLE_SCHEMA_FACTORY_HPP_
26 
27 #include <functional> // for function<>
28 #include <limits> // for numeric_limits<>
29 #include <map> // for map<>
30 #include <memory> // for shared_ptr<>
31 #include <string> // for string
32 #include <type_traits> // for enable_if_t<>, is_base_of<>
33 #include <utility> // for move
34 #include <vector> // for vector<>
35 
36 #include <fable/schema/const.hpp> // for Const
37 #include <fable/schema/interface.hpp> // for Base<>, Box
38 #include <fable/schema/string.hpp> // for String
39 #include <fable/schema/struct.hpp> // for Struct
40 #include <fable/schema/variant.hpp> // for Variant
41 
42 namespace fable {
43 namespace schema {
44 
58 template <typename T, typename CRTP>
59 class FactoryBase : public Base<CRTP> {
60  public: // Types
61  using Type = T;
62  using MakeFunc = std::function<T(const Conf& c)>;
63  struct TypeFactory {
64  TypeFactory(const Box& s, MakeFunc f) : schema(s), func(f) { schema.reset_ptr(); }
65 
66  Box schema;
67  MakeFunc func;
68  };
69 
70  using TransformFunc = std::function<Box(Struct&&)>;
71  using FactoryMap = std::map<std::string, TypeFactory>;
72  using FactoryPairList = std::initializer_list<std::pair<std::string, TypeFactory>>;
73 
74  public: // Constructors
83  explicit FactoryBase(std::string&& desc = "") : Base<CRTP>(JsonType::object, std::move(desc)) {}
84 
85  FactoryBase(std::string&& desc, FactoryPairList fs)
86  : Base<CRTP>(JsonType::object, std::move(desc)), available_(std::move(fs)) {
87  reset_schema();
88  }
89 
90  FactoryBase(std::string&& desc, FactoryMap&& fs)
91  : Base<CRTP>(JsonType::object, std::move(desc)), available_(std::move(fs)) {
92  reset_schema();
93  }
94 
100  CRTP factory_key(const std::string& keyword) && {
101  set_factory_key(keyword);
102  return std::move(*dynamic_cast<CRTP*>(this));
103  }
104 
110  CRTP args_key(const std::string& keyword) && {
111  set_args_key(keyword);
112  return std::move(*dynamic_cast<CRTP*>(this));
113  }
114 
120  CRTP args_subset(bool value) && {
121  set_args_subset(value);
122  return std::move(*dynamic_cast<CRTP*>(this));
123  }
124 
130  CRTP transform_schema(TransformFunc f) && {
132  return std::move(*dynamic_cast<CRTP*>(this));
133  }
134 
135  public: // Special
142  void set_factory_key(const std::string& keyword) {
143  assert(keyword != "");
144  factory_key_ = keyword;
145  reset_schema();
146  }
147 
157  void set_args_key(const std::string& keyword) {
158  args_key_ = keyword;
159  reset_schema();
160  }
161 
173  void set_args_subset(bool value) { args_subset_ = value; }
174 
182  void set_transform_schema(TransformFunc f) { transform_func_ = f; }
183 
187  const TypeFactory& get_factory(const std::string& key) const { return available_.at(key); }
188 
192  bool has_factory(const std::string& key) const { return available_.count(key); }
193 
200  bool add_factory(const std::string& key, Box&& s, MakeFunc f) {
201  if (!available_.count(key)) {
202  available_.insert(std::make_pair(key, TypeFactory{std::move(s), f}));
203  reset_schema();
204  return true;
205  }
206  return false;
207  }
208 
212  void set_factory(const std::string& key, Box&& s, MakeFunc f) {
213  if (!available_.count(key)) {
214  available_.erase(key);
215  }
216  available_.insert(std::make_pair(key, TypeFactory{std::move(s), f}));
217  reset_schema();
218  }
219 
233  template <typename F,
234  std::enable_if_t<(std::is_default_constructible<F>::value &&
235  std::is_convertible<std::unique_ptr<F>, T>::value),
236  int> = 0>
237  void add_default_factory(const std::string& key) {
238  add_factory(key, make_prototype<F>().get_confable_schema(), [](const Conf& c) -> T {
239  auto ptr = std::make_unique<F>();
240  ptr->from_conf(c);
241  return ptr;
242  });
243  }
244 
245  public: // Overrides
246  Json json_schema() const override {
247  Json j;
248  if (available_.empty()) {
249  j["not"] = Json{
250  {"description", "no variants available"},
251  };
252  } else {
253  j["oneOf"] = factory_json_schemas();
254  }
255  this->augment_schema(j);
256  return j;
257  }
258 
259  void validate(const Conf& c) const override {
260  assert(schema_ != nullptr);
261  auto factory = c.get<std::string>(factory_key_);
262  if (!available_.count(factory)) {
263  this->throw_error(c, "unknown factory: {}", factory);
264  }
265 
266  schema_->validate(c);
267  }
268 
269  Type make(const Conf& c) const { return deserialize(c); }
270 
271  Type deserialize(const Conf& c) const {
272  assert(schema_ != nullptr);
273  auto factory = c.get<std::string>(factory_key_);
274  if (!available_.count(factory)) {
275  this->throw_error(c, "unknown factory: {}", factory);
276  }
277 
278  Conf args;
279  if (args_subset_) {
280  if (args_key_ != "") {
281  if (c.has(args_key_)) {
282  args = c.at(args_key_);
283  }
284  } else {
285  args = c;
286  args.erase(factory_key_);
287  }
288  } else {
289  args = c;
290  }
291 
292  return available_.at(factory).func(args);
293  }
294 
295  Json serialize(const Type& x) const { return x; }
296 
297  void from_conf(const Conf& c) override {
298  throw std::logic_error("FactoryBase::from_conf() should not be used");
299  }
300 
301  using Interface::to_json;
302  void to_json(Json& j) const override {
303  throw std::logic_error("FactoryBase::to_json() should not be used");
304  }
305 
306  void reset_ptr() override {
307  // No pointer, so nothing to do here.
308  }
309 
310  protected:
311  void reset_schema() {
312  if (available_.size() == 0) {
313  return;
314  }
315  schema_.reset(new Variant(factory_schemas()));
316  }
317 
318  std::vector<Box> factory_schemas() const {
319  std::vector<Box> out;
320  out.reserve(available_.size());
321  for (auto& kv : available_) {
322  Struct base{
323  {factory_key_, make_const_str(kv.first, "name of factory").require()},
324  };
325  if (args_key_ == "") {
326  base.set_properties_from(kv.second.schema);
327  } else {
328  base.set_property(args_key_, kv.second.schema.clone());
329  }
330  base.reset_ptr();
331 
332  if (transform_func_) {
333  out.emplace_back(transform_func_(std::move(base)));
334  } else {
335  out.emplace_back(std::move(base));
336  }
337  }
338  return out;
339  }
340 
341  std::vector<Json> factory_json_schemas() const {
342  auto schemas = factory_schemas();
343  std::vector<Json> out;
344  out.reserve(schemas.size());
345  for (auto& s : schemas) {
346  out.emplace_back(s.json_schema());
347  }
348  return out;
349  }
350 
351  protected:
352  std::shared_ptr<Variant> schema_;
353  TransformFunc transform_func_;
354  FactoryMap available_;
355  std::string factory_key_{"factory"};
356  std::string args_key_{"args"};
357  bool args_subset_{true};
358 };
359 
364 template <typename T>
365 class FactoryPointerless : public FactoryBase<T, FactoryPointerless<T>> {};
366 
374 template <typename T>
375 class Factory : public FactoryBase<T, Factory<T>> {
376  public: // Types
377  using Type = typename FactoryBase<T, Factory<T>>::Type;
378  using MakeFunc = typename FactoryBase<T, Factory<T>>::MakeFunc;
379  using TypeFactory = typename FactoryBase<T, Factory<T>>::TypeFactory;
380  using FactoryMap = typename FactoryBase<T, Factory<T>>::FactoryMap;
381  using FactoryPairList = typename FactoryBase<T, Factory<T>>::FactoryPairList;
382 
383  public: // Constructors
385 
386  Factory(Type* ptr, std::string&& desc) : FactoryBase<T, Factory<T>>(std::move(desc)), ptr_(ptr) {}
387 
388  Factory(Type* ptr, std::string&& desc, FactoryMap&& fs)
389  : FactoryBase<T, Factory<T>>(std::move(desc)), ptr_(ptr) {
390  for (auto&& f : fs) {
391  this->available_.insert(f);
392  }
393  this->reset_schema();
394  }
395 
396  Factory(Type* ptr, std::string&& desc, FactoryPairList fs)
397  : FactoryBase<T, Factory<T>>(std::move(desc)), ptr_(ptr) {
398  for (auto&& f : fs) {
399  this->available_.insert(f);
400  }
401  this->reset_schema();
402  }
403 
404  public: // Overrides
405  void from_conf(const Conf& c) override {
406  assert(ptr_ != nullptr);
407  *ptr_ = this->deserialize(c);
408  }
409 
410  void to_json(Json& j) const override {
411  assert(ptr_ != nullptr);
412  j = this->serialize(*ptr_);
413  }
414 
415  void reset_ptr() override { ptr_ = nullptr; }
416 
417  private:
418  Type* ptr_{nullptr};
419 };
420 
421 } // namespace schema
422 } // namespace fable
423 
424 #endif // FABLE_SCHEMA_FACTORY_HPP_
Definition: conf.hpp:74
void validate(const Conf &c) const override
Definition: factory.hpp:259
void from_conf(const Conf &c) override
Definition: factory.hpp:405
void add_default_factory(const std::string &key)
Definition: factory.hpp:237
void set_args_key(const std::string &keyword)
Definition: factory.hpp:157
CRTP args_key(const std::string &keyword) &&
Definition: factory.hpp:110
Definition: interface.hpp:263
CRTP args_subset(bool value) &&
Definition: factory.hpp:120
Definition: factory.hpp:59
void set_transform_schema(TransformFunc f)
Definition: factory.hpp:182
T get() const
Definition: conf.hpp:164
Definition: factory.hpp:365
CRTP transform_schema(TransformFunc f) &&
Definition: factory.hpp:130
void from_conf(const Conf &c) override
Definition: factory.hpp:297
bool has_factory(const std::string &key) const
Definition: factory.hpp:192
nlohmann::json Json
Definition: json.hpp:62
void reset_ptr() override
Definition: interface.hpp:343
bool has(const std::string &key) const
Definition: conf.hpp:130
size_t erase(const std::string &key)
Definition: conf.cpp:74
void reset_ptr() override
Definition: factory.hpp:306
Json::value_t JsonType
Definition: json.hpp:78
Definition: conf.hpp:70
void set_factory_key(const std::string &keyword)
Definition: factory.hpp:142
const TypeFactory & get_factory(const std::string &key) const
Definition: factory.hpp:187
void set_factory(const std::string &key, Box &&s, MakeFunc f)
Definition: factory.hpp:212
void set_args_subset(bool value)
Definition: factory.hpp:173
Json json_schema() const override
Definition: factory.hpp:246
virtual Json to_json() const
Definition: interface.hpp:220
Definition: conf.hpp:65
Conf at(const std::string &key) const
Definition: conf.cpp:58
void reset_ptr() override
Definition: factory.hpp:415
Definition: struct.hpp:73
bool add_factory(const std::string &key, Box &&s, MakeFunc f)
Definition: factory.hpp:200
Definition: variant.hpp:59
void to_json(Json &j) const override
Definition: factory.hpp:302
Definition: factory.hpp:63
Definition: interface.hpp:354
void to_json(Json &j) const override
Definition: factory.hpp:410
CRTP factory_key(const std::string &keyword) &&
Definition: factory.hpp:100
Definition: factory.hpp:375
FactoryBase(std::string &&desc="")
Definition: factory.hpp:83