$darkmode
interface.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  */
24 #pragma once
25 #ifndef FABLE_SCHEMA_INTERFACE_HPP_
26 #define FABLE_SCHEMA_INTERFACE_HPP_
27 
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 
33 #include <fable/conf.hpp> // for Conf
34 #include <fable/error.hpp> // for SchemaError
35 #include <fable/json.hpp> // for Json
36 
37 namespace fable {
38 
39 // Forward declarations:
40 class Confable;
41 
42 namespace schema {
43 
72 class Interface {
73  public:
74  Interface() = default;
75  virtual ~Interface() = default;
76 
83  virtual Interface* clone() const = 0;
84 
91  virtual bool is_variant() const { return false; }
92 
101  virtual JsonType type() const = 0;
102 
119  virtual std::string type_string() const = 0;
120 
124  virtual bool is_required() const = 0;
125 
129  virtual const std::string& description() const = 0;
130 
134  virtual void set_description(const std::string& s) = 0;
135 
160  virtual Json usage() const = 0;
161 
188  virtual Json json_schema() const = 0;
189 
200  virtual void validate(const Conf& c) const = 0;
201 
205  virtual bool is_valid(const Conf& c) const {
206  try {
207  validate(c);
208  } catch (...) {
209  return false;
210  }
211  return true;
212  }
213 
220  virtual Json to_json() const {
221  Json j;
222  to_json(j);
223  return j;
224  }
225 
232  virtual void to_json(Json&) const = 0;
233 
239  virtual void from_conf(const Conf&) = 0;
240 
247  virtual void reset_ptr() = 0;
248 };
249 
258 template <typename S>
259 using enable_if_schema_t = std::enable_if_t<std::is_base_of<Interface, S>::value>;
260 
261 // ------------------------------------------------------------------------- //
262 
263 class Box : public Interface {
264  public: // Constructors
265  Box() = default;
266  Box(const Box&) = default;
267  Box(Box&&) = default;
268  Box& operator=(const Box&) = default;
269 
270  Box(Interface* i) : impl_(i) { assert(impl_); } // NOLINT
271  Box(std::shared_ptr<Interface> i) : impl_(std::move(i)) { assert(impl_); } // NOLINT
272 
273  public: // Special
277  std::shared_ptr<Interface> get() { return impl_; }
278 
288  template <typename T>
289  std::shared_ptr<T> as() const {
290  assert(impl_ != nullptr);
291  auto downcast_ptr = std::dynamic_pointer_cast<T>(impl_);
292  if (downcast_ptr == nullptr) {
293  // If you can't figure out the type of this even from this error, set
294  // a breakpoint here in gdb and run:
295  //
296  // p dynamic_cast<Interface*>(impl_.get())
297  //
298  // That will give you something like:
299  //
300  // $8 = (fable::schema::Interface *) 0x5555559dfd30 <
301  // vtable for fable::schema::FromConfable<
302  // (anonymous namespace)::NormalDistribution<double>, 0
303  // >
304  // +16>
305  //
306  // Which is much more specific, since the output of FromConfable and
307  // Struct can be equivalent, for example.
308  throw SchemaError{Conf{}, this->json_schema(),
309  "cannot dynamic_pointer_cast to type T, got nullptr"};
310  }
311  return downcast_ptr;
312  }
313 
320  template <typename T>
321  std::shared_ptr<T> as_unsafe() const {
322  return std::dynamic_pointer_cast<T>(impl_);
323  }
324 
325  Box reset_pointer() && {
326  reset_ptr();
327  return std::move(*this);
328  }
329 
330  public: // Overrides
331  using Interface::to_json;
332  Interface* clone() const override { return impl_->clone(); }
333  JsonType type() const override { return impl_->type(); }
334  std::string type_string() const override { return impl_->type_string(); }
335  bool is_required() const override { return impl_->is_required(); }
336  const std::string& description() const override { return impl_->description(); }
337  void set_description(const std::string& s) override { return impl_->set_description(s); }
338  Json usage() const override { return impl_->usage(); }
339  Json json_schema() const override { return impl_->json_schema(); };
340  void validate(const Conf& c) const override { impl_->validate(c); }
341  void to_json(Json& j) const override { impl_->to_json(j); }
342  void from_conf(const Conf& c) override { impl_->from_conf(c); }
343  void reset_ptr() override { impl_->reset_ptr(); }
344 
345  friend void to_json(Json& j, const Box& b) { b.impl_->to_json(j); }
346 
347  private:
348  std::shared_ptr<Interface> impl_{nullptr};
349 };
350 
351 // ------------------------------------------------------------------------- //
352 
353 template <typename CRTP>
354 class Base : public Interface {
355  public:
356  Base() = default;
357  Base(JsonType t, std::string&& desc) : type_(t), desc_(std::move(desc)) {}
358  explicit Base(JsonType t) : type_(t) {}
359  explicit Base(std::string&& desc) : desc_(std::move(desc)) {}
360  virtual ~Base() = default;
361 
362  Interface* clone() const override { return new CRTP(static_cast<CRTP const&>(*this)); }
363  operator Box() const { return Box{this->clone()}; }
364 
365  JsonType type() const override { return type_; }
366  std::string type_string() const override { return to_string(type_); }
367 
368  Json usage() const override {
369  auto required = required_ ? "!" : "";
370  if (desc_.empty()) {
371  return type_string() + required;
372  } else {
373  return fmt::format("{}{} :: {}", type_string(), required, desc_);
374  }
375  }
376 
377  bool is_required() const override { return required_; }
378  CRTP require() && {
379  required_ = true;
380  return std::move(*dynamic_cast<CRTP*>(this));
381  }
382  CRTP required(bool value) && {
383  required_ = value;
384  return std::move(*dynamic_cast<CRTP*>(this));
385  }
386 
387  CRTP reset_pointer() && {
388  reset_ptr();
389  return std::move(*dynamic_cast<CRTP*>(this));
390  }
391 
392  bool has_description() const { return !desc_.empty(); }
393  void set_description(const std::string& s) override { desc_ = s; }
394  void set_description(std::string&& s) { desc_ = std::move(s); }
395  const std::string& description() const override { return desc_; }
396  CRTP description(std::string&& desc) && {
397  desc_ = std::move(desc);
398  return std::move(*dynamic_cast<CRTP*>(this));
399  }
400 
401  protected:
402  void validate_type(const Conf& c) const {
403  if (c->type() != type_) {
404  if (c->type() == JsonType::number_unsigned && type_ == JsonType::number_integer) {
405  return;
406  }
407 
408  throw SchemaError{c, this->json_schema(), "require type {}, got {}", type_string(),
409  to_string(c->type())};
410  }
411  }
412 
413  template <typename... Args>
414  [[noreturn]] void throw_error(const Conf& c, const char* format, Args... args) const {
415  throw SchemaError{c, this->json_schema(), format, args...};
416  }
417 
418  [[noreturn]] void throw_error(const ConfError& e) const {
419  throw SchemaError{e, this->json_schema()};
420  }
421 
422  [[noreturn]] void throw_wrong_type(const Conf& c) const {
423  throw_error(error::WrongType(c, type_));
424  }
425 
426  void augment_schema(Json& j) const {
427  if (!desc_.empty()) {
428  j["description"] = desc_;
429  }
430  }
431 
432  protected:
433  JsonType type_{JsonType::null};
434  bool required_{false};
435  std::string desc_{};
436 };
437 
438 // ------------------------------------------------------------------------- //
439 
448 template <typename T>
449 using enable_if_confable_t = std::enable_if_t<std::is_base_of<Confable, T>::value>;
450 
459 template <typename T>
460 using enable_if_not_confable_t = std::enable_if_t<!std::is_base_of<Confable, T>::value>;
461 
462 template <typename T, std::enable_if_t<std::is_base_of<Confable, T>::value, int> = 0>
463 auto make_prototype(std::string&& desc = "");
464 
465 template <typename T, std::enable_if_t<!std::is_base_of<Confable, T>::value, int> = 0>
466 auto make_prototype(std::string&& desc = "");
467 
468 } // namespace schema
469 } // namespace fable
470 
471 #endif // FABLE_SCHEMA_INTERFACE_HPP_
std::shared_ptr< T > as() const
Definition: interface.hpp:289
Definition: conf.hpp:74
virtual void reset_ptr()=0
Json usage() const override
Definition: interface.hpp:338
virtual void from_conf(const Conf &)=0
Definition: error.hpp:56
std::string type_string() const override
Definition: interface.hpp:334
JsonType type() const override
Definition: interface.hpp:365
virtual std::string type_string() const =0
bool is_required() const override
Definition: interface.hpp:377
Definition: interface.hpp:263
virtual void validate(const Conf &c) const =0
Interface * clone() const override
Definition: interface.hpp:362
void set_description(const std::string &s) override
Definition: interface.hpp:337
const std::string & description() const override
Definition: interface.hpp:395
nlohmann::json Json
Definition: json.hpp:62
void reset_ptr() override
Definition: interface.hpp:343
bool is_required() const override
Definition: interface.hpp:335
Definition: error.hpp:120
Json::value_t JsonType
Definition: json.hpp:78
Definition: conf.hpp:70
Definition: interface.hpp:72
void from_conf(const Conf &c) override
Definition: interface.hpp:342
std::shared_ptr< T > as_unsafe() const
Definition: interface.hpp:321
virtual Interface * clone() const =0
virtual bool is_valid(const Conf &c) const
Definition: interface.hpp:205
virtual Json usage() const =0
virtual Json to_json() const
Definition: interface.hpp:220
Interface * clone() const override
Definition: interface.hpp:332
JsonType type() const override
Definition: interface.hpp:333
virtual const std::string & description() const =0
void validate(const Conf &c) const override
Definition: interface.hpp:340
virtual bool is_required() const =0
const std::string & description() const override
Definition: interface.hpp:336
virtual void set_description(const std::string &s)=0
void to_json(Json &j) const override
Definition: interface.hpp:341
Definition: interface.hpp:354
std::string type_string() const override
Definition: interface.hpp:366
Json json_schema() const override
Definition: interface.hpp:339
virtual JsonType type() const =0
Json usage() const override
Definition: interface.hpp:368
virtual Json json_schema() const =0
void set_description(const std::string &s) override
Definition: interface.hpp:393
virtual bool is_variant() const
Definition: interface.hpp:91