extension.h 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129
  1. #pragma once
  2. #include <type_traits>
  3. #include <jvalidate/constraint/extension_constraint.h>
  4. #include <jvalidate/forward.h>
  5. #include <jvalidate/status.h>
  6. namespace jvalidate::extension {
  7. /**
  8. * @brief A stub visitor type for building the ExtensionConstraint processing
  9. * hierarchy. Exposed for use with {@see ConstraintBase::visit}.
  10. */
  11. struct VisitorBase {
  12. virtual ~VisitorBase() = default;
  13. };
  14. template <typename E>
  15. concept Constraint = std::is_base_of_v<constraint::ExtensionConstraint::Impl, E>;
  16. namespace detail {
  17. /**
  18. * @brief A stub visitor type for proxying the ExtensionConstraint concrete
  19. * implementation type into a visitor.
  20. * Exists as a base class so that {@see jvalidate::extension::ConstraintBase}
  21. * can cast a VisitorBase to this.
  22. *
  23. * The call graph (including abstract -> virtual resolution) is as follows:
  24. * ExtensionConstraint::Impl::visit(VisitorBase const &)
  25. * -> ConstraintBase<E>::visit(VisitorBase const &)
  26. * ---> TypedVisitor<E>::visit(E const &)
  27. * -----> TypedVisitorImpl<E, V>::visit(E const &)
  28. * -------> Visitor::Impl::dispatch(E const &)
  29. * ---------> Visitor::visit<Adapter, ValidationVisitor>(E const &, ...)
  30. *
  31. * @tparam E The ExtensionConstraint type that this visitor applies to
  32. */
  33. template <Constraint E> struct TypedVisitor : VisitorBase {
  34. virtual Status visit(E const & cons) const = 0;
  35. };
  36. /**
  37. * @brief The cleverness of this virtual class hierarchy.
  38. * Connects {@see jvalidate::extension::Visitor::Impl} to one of the extension
  39. * constraint types provided in its type signature.
  40. *
  41. * The call graph (including abstract -> virtual resolution) is as follows:
  42. * ExtensionConstraint::Impl::visit(VisitorBase const &)
  43. * -> ConstraintBase<E>::visit(VisitorBase const &)
  44. * ---> TypedVisitor<E>::visit(E const &)
  45. * -----> TypedVisitorImpl<E, V>::visit(E const &)
  46. * -------> Visitor::Impl::dispatch(E const &)
  47. * ---------> Visitor::visit<Adapter, ValidationVisitor>(E const &, ...)
  48. *
  49. * @tparam E The ExtensionConstraint type that this visitor applies to
  50. * @tparam CRTP A "Curiously Recurring Template Pattern" class (the actually
  51. * concrete class implementation of this Visitor).
  52. * Must implement a function "dispatch" which can accept at least E.
  53. */
  54. template <Constraint E, typename CRTP> struct TypedVisitorImpl : TypedVisitor<E> {
  55. Status visit(E const & cons) const final {
  56. return static_cast<CRTP const *>(this)->dispatch(cons);
  57. }
  58. };
  59. }
  60. /**
  61. * @brief The preferred base class of all user-defined constraints. Using the
  62. * "Curiously Recurring Template Pattern", it is able to unwrap RTTI (Run Time
  63. * Type Information) through {@see TypedVisitor}.
  64. *
  65. * The call graph (including abstract -> virtual resolution) is as follows:
  66. * ExtensionConstraint::Impl::visit(VisitorBase const &)
  67. * -> ConstraintBase<E>::visit(VisitorBase const &)
  68. * ---> TypedVisitor<E>::visit(E const &)
  69. * -----> TypedVisitorImpl<E, V>::visit(E const &)
  70. * -------> Visitor::Impl::dispatch(E const &)
  71. * ---------> Visitor::visit<Adapter, ValidationVisitor>(E const &, ...)
  72. *
  73. * @tparam CRTP A "Curiously Recurring Template Pattern" class (the actually
  74. * concrete class implementation of this ExtensionConstraint).
  75. */
  76. template <typename CRTP> struct ConstraintBase : constraint::ExtensionConstraint::Impl {
  77. Status visit(VisitorBase const & visitor) const final {
  78. return dynamic_cast<detail::TypedVisitor<CRTP> const &>(visitor).visit(
  79. static_cast<CRTP const &>(*this));
  80. }
  81. };
  82. /**
  83. * @brief The visitor class responsible for performing validation on some list
  84. * of ExtensionConstraints as desired by the user.
  85. *
  86. * @tparam CRTP A "Curiously Recurring Template Pattern" class (the actually
  87. * concrete class implementation of this Visitor).
  88. * This class must implement the visit function for each extension constraint
  89. * in the Es... type list, as described by Visitor::Impl::dispatch.
  90. *
  91. * @tparam Es... A number of ExtensionConstraint implementations. Each of these
  92. * must fulfill the {@see jvalidate::extension::Constraint} contract.
  93. */
  94. template <typename CRTP, typename... Es> class Visitor {
  95. private:
  96. template <Adapter A, typename V> class Impl : public detail::TypedVisitorImpl<Es, Impl<A, V>>... {
  97. public:
  98. Impl(Visitor const * self, A const & document, V const & visitor)
  99. : self_(self), document_(document), visitor_(visitor) {}
  100. using detail::TypedVisitorImpl<Es, Impl>::visit...;
  101. template <Constraint E> Status dispatch(E const & cons) const {
  102. // static_assert(Visitable<CRTP, E, A, V>, "Must implement all visitation functions");
  103. return static_cast<CRTP const *>(self_)->visit(cons, document_, visitor_);
  104. }
  105. private:
  106. Visitor const * self_;
  107. A const & document_;
  108. V const & visitor_;
  109. };
  110. public:
  111. template <Adapter A, typename V>
  112. Status operator()(constraint::ExtensionConstraint const & cons, A const & document,
  113. V const & visitor) const {
  114. return cons.pimpl->visit(Impl<A, V>{this, document, visitor});
  115. }
  116. };
  117. }