Преглед на файлове

refactor: replace Constraint variant with a tagged union

Sam Jaffe преди 1 месец
родител
ревизия
c6b4152e75
променени са 4 файла, в които са добавени 66 реда и са изтрити 7 реда
  1. 2 0
      include/jvalidate/_macro.h
  2. 59 0
      include/jvalidate/constraint.h
  3. 1 1
      include/jvalidate/forward.h
  4. 4 6
      include/jvalidate/validation_visitor.h

+ 2 - 0
include/jvalidate/_macro.h

@@ -1,5 +1,7 @@
 #pragma once
 
+#define JVALIDATE_FWD(X) std::forward<decltype(X)>(X)
+
 #define JVALIDATE_CONCAT2(A, B) A##B
 #define JVALIDATE_CONCAT(A, B) JVALIDATE_CONCAT2(A, B)
 

+ 59 - 0
include/jvalidate/constraint.h

@@ -1,5 +1,6 @@
 #pragma once
 // NOLINTBEGIN(readability-identifier-naming)
+#include <jvalidate/_macro.h>
 
 #include <cstdint>
 #include <cstdlib>
@@ -33,6 +34,64 @@
 #include <jvalidate/forward.h>
 #include <jvalidate/vocabulary.h>
 
+#define DECLARE_TYPE_ENUM(X) X,
+#define SIZEOF_STRUCT(X) sizeof(X),
+#define SWITCH_DELETE(X)                                                                           \
+  case Type::X:                                                                                    \
+    reinterpret_cast<X const *>(data_.data())->~X();                                               \
+    break;
+
+#define SWITCH_VISIT(X)                                                                            \
+  case Type::X:                                                                                    \
+    return JVALIDATE_FWD(visitor).visit(*reinterpret_cast<X const *>(data_.data()),                \
+                                        JVALIDATE_FWD(args)...);
+#define IMPLICIT_CONSTRUCTOR(X)                                                                    \
+  explicit(false) Constraint(X cons) : type_(Type::X) { new (data_.data()) X(std::move(cons)); }
+
+namespace jvalidate::constraint {
+
+class Constraint {
+public:
+  CONSTRAINT_IMPLEMENTATION_LIST(IMPLICIT_CONSTRUCTOR)
+  Constraint(Constraint const &) = delete;
+  Constraint & operator=(Constraint const &) = delete;
+
+  Constraint(Constraint && other) noexcept : type_(other.type_), data_(other.data_) {
+    other.type_ = Type::None;
+  }
+
+  Constraint & operator=(Constraint && other) noexcept {
+    std::swap(type_, other.type_);
+    std::swap(data_, other.data_);
+    return *this;
+  }
+
+  ~Constraint() {
+    switch (type_) {
+    case Type::None:
+      break;
+      // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
+      CONSTRAINT_IMPLEMENTATION_LIST(SWITCH_DELETE)
+    }
+  }
+
+  decltype(auto) visit(auto && visitor, auto &&... args) const {
+    switch (type_) {
+    case Type::None:
+      JVALIDATE_THROW(std::runtime_error, "calling visit on an uninitialized Constraint");
+      // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
+      CONSTRAINT_IMPLEMENTATION_LIST(SWITCH_VISIT)
+    }
+  }
+
+private:
+  enum class Type : uint8_t { None, CONSTRAINT_IMPLEMENTATION_LIST(DECLARE_TYPE_ENUM) };
+
+  Type type_ = Type::None;
+  std::array<std::byte, std::max({CONSTRAINT_IMPLEMENTATION_LIST(SIZEOF_STRUCT)})> data_;
+};
+}
+
 namespace jvalidate {
 /**
  * @brief A factory object for the generation of constraints in JSON Schema

+ 1 - 1
include/jvalidate/forward.h

@@ -92,7 +92,7 @@ namespace jvalidate::constraint {
 
 CONSTRAINT_IMPLEMENTATION_LIST(FORWARD_DECLARE_STRUCT);
 
-using Constraint = std::variant<DISCARD1(CONSTRAINT_IMPLEMENTATION_LIST(COMMA_NAME))>;
+class Constraint;
 using SubConstraint = std::variant<schema::Node const *, std::unique_ptr<Constraint>>;
 }
 

+ 4 - 6
include/jvalidate/validation_visitor.h

@@ -76,6 +76,7 @@ template <Adapter Root, RegexEngine RE, typename ExtensionVisitor> class Validat
 private:
   using VisitedAnnotation = std::tuple<std::unordered_set<size_t>, std::unordered_set<std::string>>;
   friend class ValidationVisitorTest;
+  friend class constraint::Constraint;
 
 private:
   detail::Pointer where_;
@@ -708,8 +709,7 @@ public:
     for (auto const & [key, p_constraint] : schema_->constraints()) {
       BREAK_EARLY_IF_NO_RESULT_TREE();
       schema_path_ = current_schema / key;
-      rval &= std::visit([this, &document](auto & cons) { return this->visit(cons, document); },
-                         *p_constraint);
+      rval &= p_constraint->visit(*this, document);
     }
 
     // Post Constraints represent the unevaluatedItems and unevaluatedProperties
@@ -717,8 +717,7 @@ public:
     for (auto const & [key, p_constraint] : schema_->post_constraints()) {
       BREAK_EARLY_IF_NO_RESULT_TREE();
       schema_path_ = current_schema / key;
-      rval &= std::visit([this, &document](auto & cons) { return this->visit(cons, document); },
-                         *p_constraint);
+      rval &= p_constraint->visit(*this, document);
     }
 
     (result_ ? result_->valid(where_, current_schema, static_cast<bool>(rval)) : void());
@@ -792,8 +791,7 @@ public:
     if (schema::Node const * const * ppschema = std::get_if<0>(&subschema)) {
       return validate_subschema(*ppschema, document, keys...);
     }
-    return std::visit([this, &document](auto & cons) { return this->visit(cons, document); },
-                      *std::get<1>(subschema));
+    return std::get<1>(subschema)->visit(*this, document);
   }
 
   /**