فهرست منبع

refactor: convert Constraint from a virtual base class to a variant

Sam Jaffe 3 ماه پیش
والد
کامیت
36272657d0

+ 61 - 56
include/jvalidate/constraint.h

@@ -157,6 +157,10 @@ private:
 public:
   ConstraintFactory() = default;
 
+  static pConstraint ptr(auto && in) {
+    return std::make_unique<constraint::Constraint>(std::move(in));
+  }
+
   /**
    * @brief Construct a new ConstraintFactory, with a pre-defined list of user
    * keywords to be injected into the vocabulary. Does not override any keywords
@@ -249,7 +253,7 @@ public:
 
     adapter::Type const type = context.schema.type();
     if (type == adapter::Type::String) {
-      return std::make_unique<constraint::TypeConstraint>(to_type(context.schema.as_string()));
+      return ptr(constraint::TypeConstraint{{to_type(context.schema.as_string())}});
     }
 
     EXPECT(type == adapter::Type::Array);
@@ -257,7 +261,7 @@ public:
     for (auto subschema : context.schema.as_array()) {
       types.insert(to_type(subschema.as_string()));
     }
-    return std::make_unique<constraint::TypeConstraint>(types);
+    return ptr(constraint::TypeConstraint{types});
   }
 
   /**
@@ -305,11 +309,11 @@ public:
       if (context.schema.as_string() == "any") {
         return nullptr; // nullptr is a synonym for "always accept"
       }
-      return std::make_unique<constraint::TypeConstraint>(to_type(context.schema.as_string()));
+      return ptr(constraint::TypeConstraint{{to_type(context.schema.as_string())}});
     }
 
     EXPECT(type == adapter::Type::Array);
-    std::vector<schema::Node const *> children;
+    std::vector<constraint::SubConstraint> children;
 
     std::set<adapter::Type> types;
     for (auto const & [index, subschema] : detail::enumerate(context.schema.as_array())) {
@@ -322,9 +326,8 @@ public:
       }
     }
 
-    auto rval = std::make_unique<constraint::AnyOfConstraint>(children);
-    rval->children.push_back(std::make_unique<constraint::TypeConstraint>(types));
-    return rval;
+    children.push_back(ptr(constraint::TypeConstraint{types}));
+    return ptr(constraint::AnyOfConstraint{std::move(children)});
   }
 
   /**
@@ -344,7 +347,7 @@ public:
    * @throws {@see ConstraintFactory::typeDraft3}
    */
   static pConstraint disallowDraft3(detail::ParserContext<A> const & context) {
-    return std::make_unique<constraint::NotConstraint>(typeDraft3(context));
+    return ptr(constraint::NotConstraint{typeDraft3(context)});
   }
 
   /**
@@ -371,7 +374,7 @@ public:
    * @throws any std::exception if precondition #2 is broken
    */
   static pConstraint extendsDraft3(detail::ParserContext<A> const & context) {
-    std::vector<schema::Node const *> children;
+    std::vector<constraint::SubConstraint> children;
     switch (context.schema.type()) {
     case adapter::Type::Object:
       children.push_back(context.node());
@@ -386,7 +389,7 @@ public:
       JVALIDATE_THROW(std::runtime_error, "extends must be a schema of array-of-schemas");
     }
 
-    return std::make_unique<constraint::AllOfConstraint>(children);
+    return ptr(constraint::AllOfConstraint{std::move(children)});
   }
 
   /**
@@ -415,7 +418,7 @@ public:
       else_ = context.neighbor("else").node();
     }
 
-    return std::make_unique<constraint::ConditionalConstraint>(context.node(), then_, else_);
+    return ptr(constraint::ConditionalConstraint{context.node(), then_, else_});
   }
 
   /**
@@ -435,7 +438,7 @@ public:
       rval.push_back(subschema.freeze());
     }
 
-    return std::make_unique<constraint::EnumConstraint>(std::move(rval));
+    return ptr(constraint::EnumConstraint{std::move(rval)});
   }
 
   /**
@@ -448,7 +451,9 @@ public:
    * @returns A constraint that checks equality against a single value.
    */
   static auto isConstant(detail::ParserContext<A> const & context) {
-    return std::make_unique<constraint::EnumConstraint>(context.schema.freeze());
+    constraint::EnumConstraint rval;
+    rval.enumeration.push_back(context.schema.freeze());
+    return ptr(rval);
   }
 
   /**
@@ -466,12 +471,12 @@ public:
   static auto allOf(detail::ParserContext<A> const & context) {
     EXPECT(context.schema.type() == adapter::Type::Array);
 
-    std::vector<schema::Node const *> rval;
+    std::vector<constraint::SubConstraint> rval;
     for (auto const & [index, subschema] : detail::enumerate(context.schema.as_array())) {
       rval.push_back(context.child(subschema, index).node());
     }
 
-    return std::make_unique<constraint::AllOfConstraint>(rval);
+    return ptr(constraint::AllOfConstraint{std::move(rval)});
   }
 
   /**
@@ -489,12 +494,12 @@ public:
   static auto anyOf(detail::ParserContext<A> const & context) {
     EXPECT(context.schema.type() == adapter::Type::Array);
 
-    std::vector<schema::Node const *> rval;
+    std::vector<constraint::SubConstraint> rval;
     for (auto const & [index, subschema] : detail::enumerate(context.schema.as_array())) {
       rval.push_back(context.child(subschema, index).node());
     }
 
-    return std::make_unique<constraint::AnyOfConstraint>(rval);
+    return ptr(constraint::AnyOfConstraint{std::move(rval)});
   }
 
   /**
@@ -517,7 +522,7 @@ public:
       rval.push_back(context.child(subschema, index).node());
     }
 
-    return std::make_unique<constraint::OneOfConstraint>(rval);
+    return ptr(constraint::OneOfConstraint{rval});
   }
 
   /**
@@ -529,7 +534,7 @@ public:
    * @returns A NotConstraint
    */
   static auto isNot(detail::ParserContext<A> const & context) {
-    return std::make_unique<constraint::NotConstraint>(context.node());
+    return ptr(constraint::NotConstraint{context.node()});
   }
 
   // SECTION: Numeric Constraints
@@ -556,9 +561,9 @@ public:
         context.parent->contains("exclusiveMinimum")) {
       auto exclusive = (*context.parent)["exclusiveMinimum"];
       EXPECT(exclusive.type() == adapter::Type::Boolean);
-      return std::make_unique<constraint::MinimumConstraint>(value, exclusive.as_boolean());
+      return ptr(constraint::MinimumConstraint{value, exclusive.as_boolean()});
     }
-    return std::make_unique<constraint::MinimumConstraint>(value, false);
+    return ptr(constraint::MinimumConstraint{value, false});
   }
 
   /**
@@ -574,7 +579,7 @@ public:
    */
   static pConstraint exclusiveMinimum(detail::ParserContext<A> const & context) {
     double value = context.schema.as_number();
-    return std::make_unique<constraint::MinimumConstraint>(value, true);
+    return ptr(constraint::MinimumConstraint{value, true});
   }
 
   /**
@@ -599,9 +604,9 @@ public:
         context.parent->contains("exclusiveMaximum")) {
       auto exclusive = (*context.parent)["exclusiveMaximum"];
       EXPECT(exclusive.type() == adapter::Type::Boolean);
-      return std::make_unique<constraint::MaximumConstraint>(value, exclusive.as_boolean());
+      return ptr(constraint::MaximumConstraint{value, exclusive.as_boolean()});
     }
-    return std::make_unique<constraint::MaximumConstraint>(value, false);
+    return ptr(constraint::MaximumConstraint{value, false});
   }
 
   /**
@@ -617,7 +622,7 @@ public:
    */
   static pConstraint exclusiveMaximum(detail::ParserContext<A> const & context) {
     double value = context.schema.as_number();
-    return std::make_unique<constraint::MaximumConstraint>(value, true);
+    return ptr(constraint::MaximumConstraint{value, true});
   }
 
   /**
@@ -639,7 +644,7 @@ public:
    */
   static auto multipleOf(detail::ParserContext<A> const & context) {
     double value = context.schema.as_number();
-    return std::make_unique<constraint::MultipleOfConstraint>(value);
+    return ptr(constraint::MultipleOfConstraint{value});
   }
 
   // SECTION: String Constraints
@@ -661,7 +666,7 @@ public:
   static auto minLength(detail::ParserContext<A> const & context) {
     EXPECT(context.schema.type() == adapter::Type::Integer ||
            context.schema.type() == adapter::Type::Number);
-    return std::make_unique<constraint::MinLengthConstraint>(context.schema.as_integer());
+    return ptr(constraint::MinLengthConstraint{context.schema.as_integer()});
   }
 
   /**
@@ -681,7 +686,7 @@ public:
   static auto maxLength(detail::ParserContext<A> const & context) {
     EXPECT(context.schema.type() == adapter::Type::Integer ||
            context.schema.type() == adapter::Type::Number);
-    return std::make_unique<constraint::MaxLengthConstraint>(context.schema.as_integer());
+    return ptr(constraint::MaxLengthConstraint{context.schema.as_integer()});
   }
 
   /**
@@ -702,7 +707,7 @@ public:
    * @throws If the contained value is not interpretable as a string
    */
   static auto pattern(detail::ParserContext<A> const & context) {
-    return std::make_unique<constraint::PatternConstraint>(context.schema.as_string());
+    return ptr(constraint::PatternConstraint{context.schema.as_string()});
   }
 
   /**
@@ -723,8 +728,8 @@ public:
    * @throws If the contained value is not interpretable as a string
    */
   static auto format(detail::ParserContext<A> const & context) {
-    return std::make_unique<constraint::FormatConstraint>(context.schema.as_string(),
-                                                          context.vocab->is_format_assertion());
+    return ptr(constraint::FormatConstraint{context.schema.as_string(),
+                                            context.vocab->is_format_assertion()});
   }
 
   // SECTION: Array Constraints
@@ -752,7 +757,7 @@ public:
    */
   static auto contains(detail::ParserContext<A> const & context) {
     if (context.vocab->version() < schema::Version::Draft2019_09) {
-      return std::make_unique<constraint::ContainsConstraint>(context.node());
+      return ptr(constraint::ContainsConstraint{context.node()});
     }
 
     std::optional<size_t> maximum;
@@ -764,7 +769,7 @@ public:
       minimum = (*context.parent)["minContains"].as_integer();
     }
 
-    return std::make_unique<constraint::ContainsConstraint>(context.node(), minimum, maximum);
+    return ptr(constraint::ContainsConstraint{context.node(), minimum, maximum});
   }
 
   /**
@@ -783,7 +788,7 @@ public:
   static auto minItems(detail::ParserContext<A> const & context) {
     EXPECT(context.schema.type() == adapter::Type::Integer ||
            context.schema.type() == adapter::Type::Number);
-    return std::make_unique<constraint::MinItemsConstraint>(context.schema.as_integer());
+    return ptr(constraint::MinItemsConstraint{context.schema.as_integer()});
   }
 
   /**
@@ -802,7 +807,7 @@ public:
   static auto maxItems(detail::ParserContext<A> const & context) {
     EXPECT(context.schema.type() == adapter::Type::Integer ||
            context.schema.type() == adapter::Type::Number);
-    return std::make_unique<constraint::MaxItemsConstraint>(context.schema.as_integer());
+    return ptr(constraint::MaxItemsConstraint{context.schema.as_integer()});
   }
 
   /**
@@ -829,7 +834,7 @@ public:
       rval.push_back(context.child(subschema, index).node());
     }
 
-    return std::make_unique<constraint::TupleConstraint>(rval);
+    return ptr(constraint::TupleConstraint{rval});
   }
 
   /**
@@ -869,10 +874,10 @@ public:
     // "additionalProperties" keywords.
     if (context.vocab->version() < schema::Version::Draft06 &&
         context.schema.type() == adapter::Type::Boolean) {
-      return std::make_unique<constraint::AdditionalItemsConstraint>(context.always(), n);
+      return ptr(constraint::AdditionalItemsConstraint{context.always(), n});
     }
 
-    return std::make_unique<constraint::AdditionalItemsConstraint>(context.node(), n);
+    return ptr(constraint::AdditionalItemsConstraint{context.node(), n});
   }
 
   /**
@@ -900,7 +905,7 @@ public:
       return prefixItems(context);
     }
 
-    return std::make_unique<constraint::AdditionalItemsConstraint>(context.node(), 0);
+    return ptr(constraint::AdditionalItemsConstraint{context.node(), 0});
   }
 
   /**
@@ -917,7 +922,7 @@ public:
    * @returns An AdditionalPropertiesConstraint
    */
   static auto unevaluatedItems(detail::ParserContext<A> const & context) {
-    return std::make_unique<constraint::UnevaluatedItemsConstraint>(context.node());
+    return ptr(constraint::UnevaluatedItemsConstraint{context.node()});
   }
 
   /**
@@ -938,7 +943,7 @@ public:
       return nullptr;
     }
 
-    return std::make_unique<constraint::UniqueItemsConstraint>();
+    return ptr(constraint::UniqueItemsConstraint{});
   }
 
   // SECTION: Object Constraints
@@ -967,7 +972,7 @@ public:
       rval.insert(subschema.as_string());
     }
 
-    return std::make_unique<constraint::RequiredConstraint>(rval);
+    return ptr(constraint::RequiredConstraint{rval});
   }
 
   /**
@@ -986,7 +991,7 @@ public:
   static auto minProperties(detail::ParserContext<A> const & context) {
     EXPECT(context.schema.type() == adapter::Type::Integer ||
            context.schema.type() == adapter::Type::Number);
-    return std::make_unique<constraint::MinPropertiesConstraint>(context.schema.as_integer());
+    return ptr(constraint::MinPropertiesConstraint{context.schema.as_integer()});
   }
 
   /**
@@ -1005,7 +1010,7 @@ public:
   static auto maxProperties(detail::ParserContext<A> const & context) {
     EXPECT(context.schema.type() == adapter::Type::Integer ||
            context.schema.type() == adapter::Type::Number);
-    return std::make_unique<constraint::MaxPropertiesConstraint>(context.schema.as_integer());
+    return ptr(constraint::MaxPropertiesConstraint{context.schema.as_integer()});
   }
 
   /**
@@ -1036,7 +1041,7 @@ public:
       rval.emplace_back(prop, context.child(subschema, prop).node());
     }
 
-    return std::make_unique<constraint::PatternPropertiesConstraint>(rval);
+    return ptr(constraint::PatternPropertiesConstraint{rval});
   }
 
   /**
@@ -1061,7 +1066,7 @@ public:
       rval.emplace(prop, context.child(subschema, prop).node());
     }
 
-    return std::make_unique<constraint::PropertiesConstraint>(rval);
+    return ptr(constraint::PropertiesConstraint{rval});
   }
 
   /**
@@ -1106,10 +1111,10 @@ public:
       return properties(context);
     }
 
-    std::vector<pConstraint> rval;
+    std::vector<constraint::SubConstraint> rval;
     rval.push_back(properties(context));
-    rval.push_back(std::make_unique<constraint::RequiredConstraint>(std::move(required)));
-    return std::make_unique<constraint::AllOfConstraint>(std::move(rval));
+    rval.push_back(ptr(constraint::RequiredConstraint{std::move(required)}));
+    return ptr(constraint::AllOfConstraint{std::move(rval)});
   }
 
   /**
@@ -1122,7 +1127,7 @@ public:
    * @returns A PropertyNamesConstraint
    */
   static auto propertyNames(detail::ParserContext<A> const & context) {
-    return std::make_unique<constraint::PropertyNamesConstraint>(context.node());
+    return ptr(constraint::PropertyNamesConstraint{context.node()});
   }
 
   /**
@@ -1139,7 +1144,7 @@ public:
    * @returns An AdditionalPropertiesConstraint
    */
   static auto unevaluatedProperties(detail::ParserContext<A> const & context) {
-    return std::make_unique<constraint::UnevaluatedPropertiesConstraint>(context.node());
+    return ptr(constraint::UnevaluatedPropertiesConstraint{context.node()});
   }
 
   /**
@@ -1177,10 +1182,10 @@ public:
     // "additionalProperties" keywords.
     if (context.vocab->version() < schema::Version::Draft06 &&
         context.schema.type() == adapter::Type::Boolean) {
-      return std::make_unique<C>(context.always(), properties, patterns);
+      return ptr(C{context.always(), properties, patterns});
     }
 
-    return std::make_unique<C>(context.node(), properties, patterns);
+    return ptr(C{context.node(), properties, patterns});
   }
 
   /**
@@ -1230,7 +1235,7 @@ public:
       }
     }
 
-    return std::make_unique<constraint::DependenciesConstraint>(schemas, required);
+    return ptr(constraint::DependenciesConstraint{schemas, required});
   }
 
   /**
@@ -1261,7 +1266,7 @@ public:
       rval.emplace(prop, context.child(subschema, prop).node());
     }
 
-    return std::make_unique<constraint::DependenciesConstraint>(rval);
+    return ptr(constraint::DependenciesConstraint{rval});
   }
 
   /**
@@ -1295,7 +1300,7 @@ public:
       }
     }
 
-    return std::make_unique<constraint::DependenciesConstraint>(rval);
+    return ptr(constraint::DependenciesConstraint{{}, rval});
   }
 };
 }

+ 9 - 42
include/jvalidate/constraint/array_constraint.h

@@ -1,71 +1,38 @@
 #pragma once
 
-#include <jvalidate/detail/expect.h>
 #include <optional>
 #include <vector>
 
 #include <jvalidate/adapter.h>
-#include <jvalidate/constraint/constraint.h>
 #include <jvalidate/forward.h>
 
 namespace jvalidate::constraint {
-class AdditionalItemsConstraint : public SimpleConstraint<AdditionalItemsConstraint> {
-public:
+struct AdditionalItemsConstraint {
   schema::Node const * subschema;
   size_t applies_after_nth;
-
-public:
-  AdditionalItemsConstraint(schema::Node const * subschema, size_t applies_after_nth)
-      : subschema(subschema), applies_after_nth(applies_after_nth) {}
 };
 
-class ContainsConstraint : public SimpleConstraint<ContainsConstraint> {
-public:
+struct ContainsConstraint {
   schema::Node const * subschema;
-  std::optional<size_t> minimum;
-  std::optional<size_t> maximum;
-
-public:
-  ContainsConstraint(schema::Node const * subschema) : subschema(subschema) {}
-
-  ContainsConstraint(schema::Node const * subschema, std::optional<size_t> minimum,
-                     std::optional<size_t> maximum)
-      : subschema(subschema), minimum(minimum), maximum(maximum) {}
+  std::optional<size_t> minimum = std::nullopt;
+  std::optional<size_t> maximum = std::nullopt;
 };
 
-class MaxItemsConstraint : public SimpleConstraint<MaxItemsConstraint> {
-public:
+struct MaxItemsConstraint {
   int64_t value;
-
-public:
-  MaxItemsConstraint(int64_t value) : value(value) {}
 };
 
-class MinItemsConstraint : public SimpleConstraint<MinItemsConstraint> {
-public:
+struct MinItemsConstraint {
   int64_t value;
-
-public:
-  MinItemsConstraint(int64_t value) : value(value) {}
 };
 
-class TupleConstraint : public SimpleConstraint<TupleConstraint> {
-public:
+struct TupleConstraint {
   std::vector<schema::Node const *> items;
-
-public:
-  TupleConstraint(std::vector<schema::Node const *> const & items) : items(items) {}
 };
 
-class UnevaluatedItemsConstraint : public SimpleConstraint<UnevaluatedItemsConstraint> {
-public:
+struct UnevaluatedItemsConstraint {
   schema::Node const * subschema;
-
-public:
-  UnevaluatedItemsConstraint(schema::Node const * subschema) : subschema(subschema) {}
 };
 
-class UniqueItemsConstraint : public SimpleConstraint<UniqueItemsConstraint> {
-public:
-};
+struct UniqueItemsConstraint {};
 }

+ 0 - 30
include/jvalidate/constraint/constraint.h

@@ -1,30 +0,0 @@
-#pragma once
-
-#include <jvalidate/constraint/visitor.h>
-#include <jvalidate/detail/pointer.h>
-#include <jvalidate/enum.h>
-#include <jvalidate/forward.h>
-#include <jvalidate/status.h>
-
-namespace jvalidate::constraint {
-class Constraint {
-public:
-  virtual ~Constraint() = default;
-  virtual Status accept(ConstraintVisitor const & visitor) const = 0;
-};
-
-template <typename CRTP> class SimpleConstraint : public Constraint {
-public:
-  Status accept(ConstraintVisitor const & visitor) const final {
-    return visitor.visit(*static_cast<CRTP const *>(this));
-  }
-};
-
-class ExtensionConstraint : public Constraint {
-public:
-  Status accept(ConstraintVisitor const & visitor) const final { return visitor.visit(*this); }
-
-  virtual Status validate(adapter::Adapter const & json, detail::Pointer const & where,
-                          ValidationResult * result) const = 0;
-};
-}

+ 9 - 61
include/jvalidate/constraint/general_constraint.h

@@ -2,93 +2,41 @@
 
 #include <memory>
 #include <set>
-#include <utility>
 #include <vector>
 
-#include <jvalidate/constraint/constraint.h>
 #include <jvalidate/forward.h>
 #include <jvalidate/status.h>
 
 namespace jvalidate::constraint {
-class AllOfConstraint : public SimpleConstraint<AllOfConstraint> {
-public:
+struct AllOfConstraint {
   std::vector<SubConstraint> children;
-
-public:
-  AllOfConstraint(std::vector<schema::Node const *> const & children)
-      : children(children.begin(), children.end()) {}
-
-  AllOfConstraint(std::vector<std::unique_ptr<Constraint>> && children) {
-    for (auto && child : children) {
-      this->children.push_back(std::move(child));
-    }
-  }
 };
 
-class AnyOfConstraint : public SimpleConstraint<AnyOfConstraint> {
-public:
+struct AnyOfConstraint {
   std::vector<SubConstraint> children;
-
-public:
-  AnyOfConstraint(std::vector<schema::Node const *> const & children)
-      : children(children.begin(), children.end()) {}
-
-  AnyOfConstraint(std::vector<std::unique_ptr<Constraint>> && children) {
-    for (auto && child : children) {
-      this->children.push_back(std::move(child));
-    }
-  }
 };
 
-class EnumConstraint : public SimpleConstraint<EnumConstraint> {
-public:
+struct EnumConstraint {
   std::vector<std::unique_ptr<adapter::Const const>> enumeration;
-
-public:
-  EnumConstraint(std::unique_ptr<adapter::Const const> && constant) {
-    enumeration.push_back(std::move(constant));
-  }
-
-  EnumConstraint(std::vector<std::unique_ptr<adapter::Const const>> && enums)
-      : enumeration(std::move(enums)) {}
 };
 
-class OneOfConstraint : public SimpleConstraint<OneOfConstraint> {
-public:
+struct OneOfConstraint {
   std::vector<schema::Node const *> children;
-
-public:
-  OneOfConstraint(std::vector<schema::Node const *> const & children) : children(children) {}
 };
 
-class ConditionalConstraint : public SimpleConstraint<ConditionalConstraint> {
-public:
+struct ConditionalConstraint {
   schema::Node const * if_constraint;
   schema::Node const * then_constraint;
   schema::Node const * else_constraint;
-
-public:
-  ConditionalConstraint(schema::Node const * if_constraint, schema::Node const * then_constraint,
-                        schema::Node const * else_constraint)
-      : if_constraint(if_constraint), then_constraint(then_constraint),
-        else_constraint(else_constraint) {}
 };
 
-class NotConstraint : public SimpleConstraint<NotConstraint> {
-public:
+struct NotConstraint {
   SubConstraint child;
-
-public:
-  NotConstraint(schema::Node const * child) : child(child) {}
-  NotConstraint(std::unique_ptr<Constraint> && child) : child(std::move(child)) {}
 };
 
-class TypeConstraint : public SimpleConstraint<TypeConstraint> {
-public:
+struct TypeConstraint {
   std::set<adapter::Type> types;
-
-public:
-  TypeConstraint(adapter::Type type) : types{type} {}
-  TypeConstraint(std::set<adapter::Type> const & types) : types(types) {}
 };
+
+struct ExtensionConstraint {};
 }

+ 4 - 19
include/jvalidate/constraint/number_constraint.h

@@ -1,44 +1,29 @@
 #pragma once
 
 #include <cmath>
-#include <iostream>
+#include <limits>
 
-#include <jvalidate/adapter.h>
-#include <jvalidate/constraint/constraint.h>
 #include <jvalidate/detail/number.h>
 #include <jvalidate/forward.h>
-#include <limits>
 
 namespace jvalidate::constraint {
-class MaximumConstraint : public SimpleConstraint<MaximumConstraint> {
-public:
+struct MaximumConstraint {
   double value;
   bool exclusive;
 
-public:
-  MaximumConstraint(double value, bool exclusive) : value(value), exclusive(exclusive) {}
-
   bool operator()(double arg) const { return exclusive ? arg < value : arg <= value; }
 };
 
-class MinimumConstraint : public SimpleConstraint<MinimumConstraint> {
-public:
+struct MinimumConstraint {
   double value;
   bool exclusive;
 
-public:
-  MinimumConstraint(double value, bool exclusive) : value(value), exclusive(exclusive) {}
-
   bool operator()(double arg) const { return exclusive ? arg > value : arg >= value; }
 };
 
-class MultipleOfConstraint : public SimpleConstraint<MultipleOfConstraint> {
-public:
+struct MultipleOfConstraint {
   double value;
 
-public:
-  MultipleOfConstraint(double value) : value(value) {}
-
   bool operator()(double arg) const {
     if (std::fabs(std::remainder(arg, value)) <= std::numeric_limits<double>::epsilon()) {
       return true;

+ 9 - 61
include/jvalidate/constraint/object_constraint.h

@@ -1,102 +1,50 @@
 #pragma once
 
 #include <map>
-#include <optional>
 #include <string>
 #include <unordered_set>
 #include <utility>
 #include <vector>
 
-#include <jvalidate/constraint/constraint.h>
 #include <jvalidate/forward.h>
 
 namespace jvalidate::constraint {
-class AdditionalPropertiesConstraint : public SimpleConstraint<AdditionalPropertiesConstraint> {
-public:
+struct AdditionalPropertiesConstraint {
   schema::Node const * subschema;
   std::unordered_set<std::string> properties;
   std::vector<std::string> patterns;
-
-public:
-  AdditionalPropertiesConstraint(schema::Node const * subschema,
-                                 std::unordered_set<std::string> const & properties,
-                                 std::vector<std::string> const & patterns)
-      : subschema(subschema), properties(properties), patterns(patterns) {}
 };
 
-class DependenciesConstraint : public SimpleConstraint<DependenciesConstraint> {
-public:
+struct DependenciesConstraint {
   std::map<std::string, schema::Node const *> subschemas;
   std::map<std::string, std::unordered_set<std::string>> required;
-
-public:
-  DependenciesConstraint(std::map<std::string, schema::Node const *> const & subschemas)
-      : subschemas(subschemas) {}
-
-  DependenciesConstraint(std::map<std::string, std::unordered_set<std::string>> const & required)
-      : required(required) {}
-
-  DependenciesConstraint(std::map<std::string, schema::Node const *> const & subschemas,
-                         std::map<std::string, std::unordered_set<std::string>> const & required)
-      : subschemas(subschemas), required(required) {}
 };
 
-class MaxPropertiesConstraint : public SimpleConstraint<MaxPropertiesConstraint> {
-public:
+struct MaxPropertiesConstraint {
   int64_t value;
-
-public:
-  MaxPropertiesConstraint(int64_t value) : value(value) {}
 };
 
-class MinPropertiesConstraint : public SimpleConstraint<MinPropertiesConstraint> {
-public:
+struct MinPropertiesConstraint {
   int64_t value;
-
-public:
-  MinPropertiesConstraint(int64_t value) : value(value) {}
 };
 
-class PatternPropertiesConstraint : public SimpleConstraint<PatternPropertiesConstraint> {
-public:
+struct PatternPropertiesConstraint {
   std::vector<std::pair<std::string, schema::Node const *>> properties;
-
-public:
-  PatternPropertiesConstraint(
-      std::vector<std::pair<std::string, schema::Node const *>> const & properties)
-      : properties(properties) {}
 };
 
-class PropertiesConstraint : public SimpleConstraint<PropertiesConstraint> {
-public:
+struct PropertiesConstraint {
   std::map<std::string, schema::Node const *> properties;
-
-public:
-  PropertiesConstraint(std::map<std::string, schema::Node const *> const & properties)
-      : properties(properties) {}
 };
 
-class PropertyNamesConstraint : public SimpleConstraint<PropertyNamesConstraint> {
-public:
+struct PropertyNamesConstraint {
   schema::Node const * key_schema;
-
-public:
-  PropertyNamesConstraint(schema::Node const * key_schema) : key_schema(key_schema) {}
 };
 
-class RequiredConstraint : public SimpleConstraint<RequiredConstraint> {
-public:
+struct RequiredConstraint {
   std::unordered_set<std::string> properties;
-
-public:
-  RequiredConstraint(std::unordered_set<std::string> const & properties) : properties(properties) {}
 };
 
-class UnevaluatedPropertiesConstraint : public SimpleConstraint<UnevaluatedPropertiesConstraint> {
-public:
+struct UnevaluatedPropertiesConstraint {
   schema::Node const * subschema;
-
-public:
-  UnevaluatedPropertiesConstraint(schema::Node const * subschema) : subschema(subschema) {}
 };
 }

+ 4 - 22
include/jvalidate/constraint/string_constraint.h

@@ -2,42 +2,24 @@
 
 #include <string>
 
-#include <jvalidate/constraint/constraint.h>
 #include <jvalidate/detail/string.h>
 #include <jvalidate/forward.h>
 
 namespace jvalidate::constraint {
-class MinLengthConstraint : public SimpleConstraint<MinLengthConstraint> {
-public:
+struct MinLengthConstraint {
   int64_t value;
-
-public:
-  MinLengthConstraint(int64_t value) : value(value) {}
 };
 
-class MaxLengthConstraint : public SimpleConstraint<MaxLengthConstraint> {
-public:
+struct MaxLengthConstraint {
   int64_t value;
-
-public:
-  MaxLengthConstraint(int64_t value) : value(value) {}
 };
 
-class PatternConstraint : public SimpleConstraint<PatternConstraint> {
-public:
+struct PatternConstraint {
   std::string regex;
-
-public:
-  PatternConstraint(std::string const & regex) : regex(regex) {}
 };
 
-class FormatConstraint : public SimpleConstraint<FormatConstraint> {
-public:
+struct FormatConstraint {
   std::string format;
   bool is_assertion;
-
-public:
-  FormatConstraint(std::string const & format, bool is_assertion)
-      : format(format), is_assertion(is_assertion) {}
 };
 }

+ 0 - 23
include/jvalidate/constraint/visitor.h

@@ -1,23 +0,0 @@
-#pragma once
-
-#include <jvalidate/forward.h>
-
-#define VISITOR_PURE_VIRTUAL(TYPE) virtual Status visit(TYPE const &) const = 0;
-
-namespace jvalidate::constraint {
-/**
- * @brief The base interface for visitors to be implemented off of.
- * Provides a visit function for every provided concrete type of Constraint,
- * as well as the ExtensionConstraint interface.
- */
-struct ConstraintVisitor {
-  virtual ~ConstraintVisitor() = default;
-  CONSTRAINT_IMPLEMENTATION_LIST(VISITOR_PURE_VIRTUAL);
-};
-
-template <typename Cons> struct ExtensionConstraintVisitor {
-  virtual Status visit(Cons const &) const = 0;
-};
-}
-
-#undef VISITOR_PURE_VIRTUAL

+ 15 - 8
include/jvalidate/forward.h

@@ -5,6 +5,13 @@
 #include <type_traits>
 #include <variant>
 
+#define DISCARD1_IMPL(_, ...) __VA_ARGS__
+#define DISCARD1(...) DISCARD1_IMPL(__VA_ARGS__)
+
+#define FORWARD_DECLARE_STRUCT(TYPE) struct TYPE;
+
+#define COMMA_NAME(X) , X
+
 namespace jvalidate {
 class Schema;
 class Status;
@@ -33,11 +40,6 @@ class Node;
 }
 
 namespace jvalidate::constraint {
-class ConstraintVisitor;
-class Constraint;
-using SubConstraint = std::variant<schema::Node const *, std::unique_ptr<Constraint>>;
-template <typename> class SimpleConstraint;
-
 #define CONSTRAINT_IMPLEMENTATION_LIST(X)                                                          \
   /* General Constraints - See jvalidate/constraint/general_constraint.h */                        \
   X(TypeConstraint)                                                                                \
@@ -77,10 +79,10 @@ template <typename> class SimpleConstraint;
   /* ExtensionConstraint - A special constraint for all user-defined constraints */                \
   X(ExtensionConstraint)
 
-#define FORWARD_DECLARE_CLASS(TYPE) class TYPE;
-CONSTRAINT_IMPLEMENTATION_LIST(FORWARD_DECLARE_CLASS);
-#undef FORWARD_DECLARE_CLASS
+CONSTRAINT_IMPLEMENTATION_LIST(FORWARD_DECLARE_STRUCT);
 
+using Constraint = std::variant<DISCARD1(CONSTRAINT_IMPLEMENTATION_LIST(COMMA_NAME))>;
+using SubConstraint = std::variant<schema::Node const *, std::unique_ptr<Constraint>>;
 }
 
 namespace jvalidate {
@@ -179,3 +181,8 @@ template <Adapter A> using URIResolver = bool (*)(URI const &, typename A::value
 template <typename T, typename S>
 concept Not = not std::is_convertible_v<std::decay_t<S>, T>;
 }
+
+#undef FORWARD_DECLARE_STRUCT
+#undef COMMA_NAME
+#undef DISCARD1_IMPL
+#undef DISCARD1

+ 6 - 8
include/jvalidate/validation_visitor.h

@@ -11,7 +11,6 @@
 #include <jvalidate/constraint/number_constraint.h>
 #include <jvalidate/constraint/object_constraint.h>
 #include <jvalidate/constraint/string_constraint.h>
-#include <jvalidate/constraint/visitor.h>
 #include <jvalidate/detail/expect.h>
 #include <jvalidate/detail/iostream.h>
 #include <jvalidate/detail/number.h>
@@ -45,8 +44,7 @@
   } while (false)
 
 namespace jvalidate {
-template <Adapter A, RegexEngine RE>
-class ValidationVisitor : public constraint::ConstraintVisitor {
+template <Adapter A, RegexEngine RE> class ValidationVisitor {
 private:
   using VisitedAnnotation = std::tuple<std::unordered_set<size_t>, std::unordered_set<std::string>>;
   JVALIDATE_TRIBOOL_TYPE(StoreResults, ForValid, ForInvalid, ForAnything);
@@ -101,7 +99,7 @@ public:
   }
 
   Status visit(constraint::ExtensionConstraint const & cons) const {
-    return cons.validate(document_, where_, result_);
+    // return cons.validate(document_, where_, result_);
   }
 
   Status visit(constraint::EnumConstraint const & cons) const {
@@ -609,7 +607,7 @@ public:
     for (auto const & [key, p_constraint] : schema_->constraints()) {
       BREAK_EARLY_IF_NO_RESULT_TREE();
       schema_path_ = current_schema / key;
-      rval &= p_constraint->accept(*this);
+      rval &= std::visit([this](auto & c) { return visit(c); }, *p_constraint);
     }
 
     // Post Constraints represent the unevaluatedItems and unevaluatedProperties
@@ -617,7 +615,7 @@ public:
     for (auto const & [key, p_constraint] : schema_->post_constraints()) {
       BREAK_EARLY_IF_NO_RESULT_TREE();
       schema_path_ = current_schema / key;
-      rval &= p_constraint->accept(*this);
+      rval &= std::visit([this](auto & c) { return visit(c); }, *p_constraint);
     }
 
     (result_ ? result_->valid(where_, current_schema, static_cast<bool>(rval)) : void());
@@ -650,7 +648,7 @@ private:
     if (not result_) {
       return false;
     }
-    switch (tracking_) {
+    switch (*tracking_) {
     case StoreResults::ForAnything:
       return stat != Status::Noop;
     case StoreResults::ForValid:
@@ -695,7 +693,7 @@ private:
     if (schema::Node const * const * ppschema = std::get_if<0>(&subschema)) {
       return validate_subschema(*ppschema, keys...);
     } else {
-      return std::get<1>(subschema)->accept(*this);
+      return std::visit([this](auto & c) { return visit(c); }, *std::get<1>(subschema));
     }
   }