Add documentation to the documentation and verifier classes

Add static const member variable to the DocumentationEntry class for Wildcards
This commit is contained in:
Alexander Bock
2016-09-18 20:47:41 +02:00
parent 4e7035a18d
commit 29c989ab82
10 changed files with 952 additions and 142 deletions
+171 -11
View File
@@ -42,60 +42,220 @@ using Exhaustive = ghoul::Boolean;
/**
* The TestResult structure returns the information from the #testSpecification method. It
* contains the information whether test specification test was successful
* (TestResult::success) and a list of Offence%s
* (TestResult::success) and a list of TestResult::Offense%s (TestResult::offenses). If
* TestResult::success is true, TestResult::offenses is guaranteed to be empty.
*/
struct TestResult {
/**
* An Offense is a violation against a specific verifier. The Offense::offender is the
* key that caused the offense (in the case of nested tables, it will be fully
* qualified identifier) and the Offense::Reason is the reason that caused the
* offense.
*/
struct Offense {
/**
* The Reason for the offense
*/
enum class Reason {
MissingKey,
ExtraKey,
WrongType,
Verification
MissingKey, ///< The offending key that was requested was not found
ExtraKey, ///< The exhaustive documentation contained an extra key
WrongType, ///< The key's value was not of the expected type
Verification ///< The value did not pass a necessary non-type verifier
};
/// The offending key that caused the Offense. In the case of a nested table,
/// this value will be the fully qualified name of the key
std::string offender;
/// The Reason that caused this offense
Reason reason;
};
/// Is \c true if the TestResult is positive, \c false otherwise
bool success;
/// Contains a list of offenses that were found in the test. Is empty if
/// TestResult::Success is \c true
std::vector<Offense> offenses;
};
/**
* This exception is thrown by the #testSpecificationAndThrow method if the test detected
* a specification violation. This class contains the TestResult that would have otherwise
* be returned in a call to #testSpecification.
*/
struct SpecificationError : public ghoul::RuntimeError {
/**
* Creates the SpecificationError exception instance.
* \param result The offending TestResult that is passed on
* \param component The component that initiated the specification test
* \pre \p result%'s TestResult::success must be \c false
*/
SpecificationError(TestResult result, std::string component);
/// The TestResult that caused the SpecificationError to be thrown
TestResult result;
};
struct Verifier;
/**
* A DocumentationEntry provides the specification for a single key, which is tested using
* the provided Verifier. Each DocumentationEntry can contain a textual documentation that
* describes the entry and is printed when the documentation for a Documentation is
* requested. Lastly, each DocumentationEntry can be Optional. If the provided key is the
* DocumentationEntry::Wildcard, any key in the containing Documentation will be tested
* against the provided verifier. The most convenient way of creating DocumentationEntry%s
* is by using an inline initializer list such as:
*\verbatim
DocumentationEntry e = { "key", new IntVerifier, "Documentation text", Optional::Yes };
\endverbatim
* Furthermore, these initializer lists can be crated all at once for a Documentation.
* Even if the Verifier%s are specified using the \c new operators, they will not leak
* memory as the DocumentationEntry takes ownership of them in the constructor.
*/
struct DocumentationEntry {
DocumentationEntry(std::string key, Verifier* t, std::string doc = "",
/// The wildcard character that will match against every key in a Documentation
static const std::string Wildcard;
/**
* The constructor for a DocumentationEntry describing a \p key in a Documentation.
* The value for the key (or each value in the case of the
* DocumentationEntry::Wildcard) is tested using the \p verifier, that specifies the
* conditions that the \p key%'s value has to fulfill. The textual documentation
* \p doc shall describe the usage of the key-value pair and will be printed for human
* consumption for example in the DocumentationEngine. Each DocumentationEntry can
* further be \p optional.
* \param key The key for which this DocumentationEntry is valid. If this valid is
* equal to DocumentationEntry::Wildcard, each entry in the Documentation that
* contains this DocumentationEntry will be matched
* \param verifier The Verifier that is used to test the \p key%'s value to determine
* if it is a valid value
* \param doc The textual documentation that describes the DocumentationEntry in a
* human readable format
* \param optional Determines whether the Documentation containing this
* DocumentationEntry must have a key \p key, or whether it is optional
* \pre \p key must not be empty
* \pre \p verifier must not be nullptr
*/
DocumentationEntry(std::string key, std::shared_ptr<Verifier> verifier,
std::string doc = "", Optional optional = Optional::No);
/**
* The constructor for a DocumentationEntry describing a \p key in a Documentation.
* The value for the key (or each value in the case of the
* DocumentationEntry::Wildcard) is tested using the \p verifier, that specifies the
* conditions that the \p key%'s value has to fulfill. The textual documentation
* \p doc shall describe the usage of the key-value pair and will be printed for human
* consumption for example in the DocumentationEngine. Each DocumentationEntry can
* further be \p optional.
* \param key The key for which this DocumentationEntry is valid. If this valid is
* equal to DocumentationEntry::Wildcard, each entry in the Documentation that
* contains this DocumentationEntry will be matched
* \param verifier The Verifier that is used to test the \p key%'s value to determine
* if it is a valid value. The DocumentationEntry will take ownership of the passed
* object
* \param doc The textual documentation that describes the DocumentationEntry in a
* human readable format
* \param optional Determines whether the Documentation containing this
* DocumentationEntry must have a key \p key, or whether it is optional
* \pre \p key must not be empty
* \pre \p verifier must not be nullptr
*/
DocumentationEntry(std::string key, Verifier* verifier, std::string doc = "",
Optional optional = Optional::No);
/// The key that is described by this DocumentationEntry
std::string key;
/// The Verifier that is used to test the key's value
std::shared_ptr<Verifier> verifier;
bool optional;
/// Determines whether the described DocumentationEntry is optional or not
Optional optional;
/// The textual description of this DocumentationEntry
std::string documentation;
};
using DocumentationEntries = std::vector<documentation::DocumentationEntry>;
/**
* This struct contains the documentation and specification for a ghoul::Dictionary. It is
* used to impose restrictions on keys and values and determine whether a given
* ghoul::Dictionary adheres to these specifications (see #testSpecification and
* #testSpecificationAndThrow methods). Each Documentation consists of a human-readable
* \c name, a list of DocumentationEntry%s that each describe a single key value, and a
* flag whether these entries are Exhaustive or not. If a Documentation is Exhaustive, a
* ghoul::Dictionary that contains additional keys will fail the specification, whereas a
* non-exhaustive Documentation allow for other (potentially non used) keys. The most
* convenient way of creating a Documentation is by using nested initializer lists:
*\verbatim
Documentation doc = {
"Documentation for an arbitrary dictionary",
{ // A list of DocumentationEntry%s; also specified using initializer lists
{ "key1", new IntVerifier, "Documentation key1", Optional::Yes },
{ "key2", new FloatVerifier, "Documentation key2" },
{ "key3", new StringVerifier }
},
Exhaustive::Yes
+;
\endverbatim
*
* If multiple DocumentationEntries cover the same key, they are all evaluated for that
* specific key. The same holds true if there is a DocumentationEntry with a
* DocumentationEntry::Wildcard and a more specialized DocumentationEntry. In this case,
* both the wildcard and the specialized entry will be evaluated.
*/
struct Documentation {
using DocumentationEntries = std::vector<documentation::DocumentationEntry>;
/**
* Creates a Documentation with a human-readable \p name and a list of \p entries.
* \param name The human-readable name of this Documentation
* \param entries A list of DocumentationEntry%s that describe the individual keys for
* this entrie Documentation
* \param exhaustive Determines whether the \p entries are an exhaustive specification
* of the object or whether additional, potentially unused, keys are allowed
*/
Documentation(std::string name = "", DocumentationEntries entries = {},
Exhaustive exhaustive = Exhaustive::No);
Documentation(DocumentationEntries entries);
/// The human-readable name of the Documentation
std::string name;
/// A list of specifications that are describing this Documentation
DocumentationEntries entries;
/// A flag to say wheter the DocumentationEntries are an exhaustive description
Exhaustive exhaustive;
};
/**
* This method tests whether a provided ghoul::Dictionary \p dictionary adheres to the
* specification \p documentation and returns its result as a TestResult. The TestResult
* will contain whether the \p dictionary adheres to the \p documentation and, in
* addition, the list of all offending keys together with the reason why they are
* offending.
* \param documentation The Documentation that the \p dictionary is tested against
* \param dictionary The ghoul::Dictionary that is to be tested against the
* \p documentation
* \return A TestResult that contains the results of the specification testing
*/
TestResult testSpecification(const Documentation& documentation,
const ghoul::Dictionary& dictionary);
TestResult testSpecification(const Documentation& d, const ghoul::Dictionary& dictionary);
void testSpecificationAndThrow(const Documentation& doc,
/**
* This method tests whether a provided ghoul::Dictionary \p dictionary adheres to the
* specification \p documentation. If the \p dictionary does not adhere to the
* specification a SpecificationError is thrown, and the exception contains the TestResult
* that contains more information about the offending keys. If the \p dictionary adheres to
* the \p documentation, the method returns normally.
* \param documentation The Documentation that the \p dictionary is tested against
* \param dictionary The ghoul::Dictionary that is to be tested against the
* \p documentation
* \param component The component that is using this method; this argument is passed to the
* SpecificationError that is thrown in case of not adhering to the \p documentation
* \throw SpecificationError If the \p dictionary does not adhere to the \p documentation
*/
void testSpecificationAndThrow(const Documentation& documentation,
const ghoul::Dictionary& dictionary, std::string component);
} // namespace documentation
// We want to make it easier for people to use it, so we pull the Documentation class into
// the openspace namespace
using documentation::Documentation;
} // namespace openspace
+458 -24
View File
@@ -32,31 +32,107 @@
namespace openspace {
namespace documentation {
/**
* The base class of all Verifier%s. Each object must have an Verifier::operator()
* overload, that performs the actual testing of the key inside the passed
* ghoul::Dictionary and return a TestResult. The Verifier::type method returns a
* human-readable representation of the type that is expected by the concret subclass of
* Verifier. Furthermore, the Verifier::documentation method returns a human-readable
* description of the Verifier subclass and what it tests for.
*/
struct Verifier {
virtual TestResult operator()(const ghoul::Dictionary& dict,
/**
* This method tests whether the \p key contained in the \p dictionary adheres to
* whatever the concrete Verifer needs to test. The actual testing depends on the
* concrete subclass and can range from type testing (for example IntVerifier or
* StringVerifier) to more complex testing (for example DoubleInRangeVerifier or
* TableVerifier).
* \param dictionary The dictionary that contains the \p key which is to be tested by
* this Verifier
* \param key The key inside the \p dictionary that is to be tested
* \return A TestResult struct that contains information about whether the key adheres
* to the demands of the specific Verifier. If it does not, TestResult::offenders will
* either contain \p key or, in the case of a TableVerifier, a list of all offending
* subkeys as fully qualified names.
* \post If the return values' TestResult::success is \c true, its
* TestResult::offenders is empty
*/
virtual TestResult operator()(const ghoul::Dictionary& dictionary,
const std::string& key) const = 0;
/**
* This method returns a human-readable string describing the type of object that is
* handled by the Verifier subclass. This is only used for generating a human-readable
* documentation and description of a Documenation object.
* \return A human-readable string describing the type of object for the Verifier
* \post The return value is not empty
*/
virtual std::string type() const = 0;
virtual std::string documentation() const;
/**
* This method returns a human-readable string describing the tests that the concrete
* Verifier subclass implements. This is only used for generating a human-readable
* documentation and description of a Documentation object.
* \return A human-readable string describing the tests that are performed by the
* Verifier
* \post The return value is not empty
*/
virtual std::string documentation() const = 0;
};
//----------------------------------------------------------------------------------------
// General verifiers
//----------------------------------------------------------------------------------------
/**
* The base class Verifier for all Verifier%s that have to test against a specific value
* type. This Verifier tests whether a given key exists and whether it has the same type
* as the template parameter \c T.
* \tparam T The type against which the key's value is tested
*/
template <typename T>
struct TemplateVerifier : public Verifier {
using Type = T;
TestResult operator()(const ghoul::Dictionary& dict,
/**
* Tests whether the \p key contained in the ghoul::Dictionary \p dictionary exists
* and has the same type as \c T.
* \param dictionary The ghoul::Dictionary that contains the \p key to be tested
* \param key The key inside the \p dictinoary that is to be tested
* \return A TestResult that contains the information whether the \p key exists in the
* \p dictionary and whether the key's value's type agrees with \c T.
* \post The return values' TestResult::success is either \c true and
* TestResult::offenders is empty, or it is \c false and TestResult::offenders
* contains \p key
*/
TestResult operator()(const ghoul::Dictionary& dictionary,
const std::string& key) const override;
std::string documentation() const override;
};
/**
* A Verifier that checks whether a given key inside a ghoul::Dictionary is of type
* \c bool. No implicit conversion is considered in this testing.
*/
struct BoolVerifier : public TemplateVerifier<bool> {
std::string type() const override;
};
/**
* A Verifier that checks whether a given key inside a ghoul::Dictionary is of type
* \c double. No implicit conversion is considered in this testing.
*/
struct DoubleVerifier : public TemplateVerifier<double> {
std::string type() const override;
};
/**
* A Verifier that checks whether a given key inside a ghoul::Dictionary is of type
* \c int. It will also return \c true if the key's value is of type \c double, but is a
* integer value (for example, <code>0.0</code>, <code>12.0</code>, but not
* <code>0.5</code>).
*/
struct IntVerifier : public TemplateVerifier<int> {
TestResult operator()(const ghoul::Dictionary& dict,
const std::string& key) const override;
@@ -64,51 +140,154 @@ struct IntVerifier : public TemplateVerifier<int> {
std::string type() const override;
};
/**
* A Verifier that checks whether a given key inside a ghoul::Dictionary is of type
* <code>std::string</code>. No implicit conversion is considered in this testing.
*/
struct StringVerifier : public TemplateVerifier<std::string> {
std::string type() const override;
};
/**
* A Verifier that checks whether a given key inside a ghoul::Dictionary is another
* ghoul::Dictionary. The constructor takes a list of DocumentationEntry%s, which are used
* recursively to check the contained table. If this list is empty, a simple type testing
* is performed instead. If the testing finds any offending keys, it will return those keys
* with fully qualified names, that is, the name of the table will be prepended to the
* offending keys. Example: If the key \c Table is tested and a passed DocumentationEntry
* checks for a nested key \c a and this does not comply, this Verifier will return
* <code>Table.a</code> as an offender.
*/
struct TableVerifier : public TemplateVerifier<ghoul::Dictionary> {
TableVerifier(std::vector<DocumentationEntry> d = {},
/**
* This constructor takes a list of DocumentationEntry%s that are used recursively to
* check the table (= ghoul::Dictionary) contained in the key's value. Similar to the
* Documentation, these DocumentationEntry%s can be Exhaustive or not.
* \param documentationEntries The DocumentationEntry%s that are used to recursively
* test the ghoul::Dictionary that is contained inside. If this list is empty, only a
* type check is performed
* \param exhaustive Whether the DocumentationEntry%s contained in
* \p documentationEntries completely describe the contained table or whether
* additional keys are allowed
*/
TableVerifier(std::vector<DocumentationEntry> documentationEntries = {},
Exhaustive exhaustive = Exhaustive::No);
TestResult operator()(const ghoul::Dictionary& dict,
/**
* Checks whether the \p key%'s value is a table (= ghoul::Dictionary) and (if
* provided) recursively checks whether the table adheres to the DocumentationEntry%s
* provided in the constructor. If the testing finds any offending keys, it will
* return those keys with fully qualified names, that is, the name of the table will
* be prepended to the offending keys.
* \param dictionary The ghoul::Dictionary that is to be tested for the \p key
* \param key The key for which the \p dictionary is tested
* \return A TestResult containing the results of the testing. If DocumentationEntry%s
* were specified in the constructor and one of those values find an offending key
* inside the table, it's name will be returned with a fully qualified name by
* prepending the name (= \key) of the table.
*/
TestResult operator()(const ghoul::Dictionary& dictionary,
const std::string& key) const override;
std::string type() const override;
std::vector<DocumentationEntry> doc;
/// The documentations passed in the constructor
std::vector<DocumentationEntry> documentations;
/// Flag that specifies whether the TableVerifier::documentation exhaustively
/// describes the table or whether additional keys are allowed
Exhaustive exhaustive;
};
//----------------------------------------------------------------------------------------
// Vector verifiers
//----------------------------------------------------------------------------------------
/**
* This struct is the base class for all Verifier%s that check for \c glm vector types.
* The template parameter for the subclasses is the containing type, not the full vector
* type. For example to check for <code>glm::dvec3</code>, one would create a
* <code>Vector3Verifier<double></code>.
*/
struct VectorVerifier {};
/**
* This Verifier checks whether the value is of type <code>glm::tvec2<T></code>
*/
template <typename T>
struct Vector2Verifier : public TemplateVerifier<glm::tvec2<T>>, public VectorVerifier {
std::string type() const override;
};
/**
* This Verifier checks whether the value is of type <code>glm::tvec3<T></code>
*/
template <typename T>
struct Vector3Verifier : public TemplateVerifier<glm::tvec3<T>>, public VectorVerifier {
std::string type() const override;
};
/**
* This Verifier checks whether the value is of type <code>glm::tvec4<T></code>
*/
template <typename T>
struct Vector4Verifier : public TemplateVerifier<glm::tvec4<T>>, public VectorVerifier {
std::string type() const override;
};
// Operator Verifiers
template <typename T, typename Op>
//----------------------------------------------------------------------------------------
// Operator verifiers
//----------------------------------------------------------------------------------------
/**
* This is the abstract base class of all binary operator-based verifiers. This class
* takes two template parameters. The first is the Verifier that one would use to only
* check for the type of the object, for example IntVerifier. The second argument is a
* function object that has its <code>operator()</code> function overloaded and returns a
* boolean value. In these cases, the \c std function objects <code>std::less</code>,
* <code>std::equal_to</code>, etc are used.
*
* This verifier will apply the \c Operator to the stored value and the incoming value
* (after type checking) and will check if the \c Operator returns \c true or \c false.
* The incoming value is used as the first argument and the stored value as the second
* argument to the \c Operator. If the type checking fails, the offense reason
* TestResult::Offense::Reason::WrongType is returned. If the \c Operator fails, the
* reason TestResult::Offense::Verification is returned instead.
*/
template <typename T, typename Operator>
struct OperatorVerifier : public T {
/**
* Constructor for an OperatorVerifier. As all operators need to compare the incoming
* value to a stored value, we require the comparison \p value to be passed in here.
* \param value The value against which the tested value is compared using the
* \c Operator
*/
OperatorVerifier(typename T::Type value);
TestResult operator()(const ghoul::Dictionary& dict,
/**
* First checks whether the \p dictionary contains the passed \p key and whether the
* \p key%'s value is correct using the template paramater \c T as a verifier. Then,
* the \p key%'s value is checked against the stored OperatorVerifier::value using the
* \c Operator.
* \param dictionary The ghoul::Dictionary that contains the \p key to be tested
* \param key The key inside the \p dictinoary that is to be tested
* \return A TestResult containing the results of the specification testing. If the
* \p key%'s value has the wrong type, it will be added to the TestResult's offense
* list with the reason TestResult::Offense::Reason::WrongType; if the \c Operator
* returns false, it will be added with the reason TestResult::Offense::Verification
* instead.
*/
TestResult operator()(const ghoul::Dictionary& dictionary,
const std::string& key) const override;
/// The stored value which is passed to the \c Operator as a second argument
typename T::Type value;
};
/**
* This Verifier checks whether the incoming value is strictly smaller than the stored
* value. Due to the operator type restrictions, \c T cannot be a subclass of (or the same
* as) BoolVerifier, StringVerifier, TableVerifier, or VectorVerifier.
*/
template <typename T>
struct LessVerifier : public OperatorVerifier<T, std::less<typename T::Type>> {
static_assert(!std::is_base_of_v<BoolVerifier, T>, "T cannot be BoolVerifier");
@@ -121,6 +300,11 @@ struct LessVerifier : public OperatorVerifier<T, std::less<typename T::Type>> {
std::string documentation() const;
};
/**
* This Verifier checks whether the incoming value is smaller than or equal to the stored
* value. Due to the operator type restrictions, \c T cannot be a subclass of (or the same
* as) BoolVerifier, StringVerifier, TableVerifier, or VectorVerifier.
*/
template <typename T>
struct LessEqualVerifier : public OperatorVerifier<T, std::less_equal<typename T::Type>> {
static_assert(!std::is_base_of_v<BoolVerifier, T>, "T cannot be BoolVerifier");
@@ -133,6 +317,11 @@ struct LessEqualVerifier : public OperatorVerifier<T, std::less_equal<typename T
std::string documentation() const override;
};
/**
* This Verifier checks whether the incoming value is strictly greater than the stored
* value. Due to the operator type restrictions, \c T cannot be a subclass of (or the same
* as) BoolVerifier, StringVerifier, TableVerifier, or VectorVerifier.
*/
template <typename T>
struct GreaterVerifier : public OperatorVerifier<T, std::greater<typename T::Type>> {
static_assert(!std::is_base_of_v<BoolVerifier, T>, "T cannot be BoolVerifier");
@@ -145,6 +334,11 @@ struct GreaterVerifier : public OperatorVerifier<T, std::greater<typename T::Typ
std::string documentation() const override;
};
/**
* This Verifier checks whether the incoming value is greater than or equal to the stored
* value. Due to the operator type restrictions, \c T cannot be a subclass of (or the same
* as) BoolVerifier, StringVerifier, TableVerifier, or VectorVerifier.
*/
template <typename T>
struct GreaterEqualVerifier : public OperatorVerifier<T, std::greater_equal<typename T::Type>> {
static_assert(!std::is_base_of_v<BoolVerifier, T>, "T cannot be BoolVerifier");
@@ -157,6 +351,10 @@ struct GreaterEqualVerifier : public OperatorVerifier<T, std::greater_equal<type
std::string documentation() const override;
};
/**
* This Verifier checks whether the incoming value is equal to the stored value. Due to the
* operator type restrictions, \c T cannot be a subclass of (or the same as) TableVerifier.
*/
template <typename T>
struct EqualVerifier : public OperatorVerifier<T, std::equal_to<typename T::Type>> {
static_assert(!std::is_base_of_v<TableVerifier, T>, "T cannot be TableVerifier");
@@ -166,6 +364,11 @@ struct EqualVerifier : public OperatorVerifier<T, std::equal_to<typename T::Type
std::string documentation() const override;
};
/**
* This Verifier checks whether the incoming value is unequal to the store value. Due to
* the operator type restrictions, \c T cannot be a subclass of (or the same as)
* TableVerifier.
*/
template <typename T>
struct UnequalVerifier : public OperatorVerifier<T, std::not_equal_to<typename T::Type>> {
static_assert(!std::is_base_of_v<TableVerifier, T>, "T cannot be TableVerifier");
@@ -175,29 +378,77 @@ struct UnequalVerifier : public OperatorVerifier<T, std::not_equal_to<typename T
std::string documentation() const override;
};
// List Verifiers
//----------------------------------------------------------------------------------------
// List verifiers
//----------------------------------------------------------------------------------------
/**
* This Verifier checks whether the incoming value is of the correct type, using the
* Verifier passed as a template parameter \c T and then checks whether it is part of a
* list that is passed to the constructor. To the missing equality operator, \c T cannot
* be a subclass of (or the same as) TableVerifier.
*/
template <typename T>
struct InListVerifier : public T {
static_assert(!std::is_base_of_v<TableVerifier, T>, "T cannot be TableVerifier");
/**
* Constructs an InListVerifier that checks whether the incoming value is of the
* correct type and whether the value is part of the list passed as \p values.
* \param values The list of values against which the incoming value is tested
*/
InListVerifier(std::vector<typename T::Type> values);
TestResult operator()(const ghoul::Dictionary& dict,
/**
* Tests whether the \p key exists in the \p dictionary, whether it has the correct
* type by invoking the template parameter \c T, and then tests if the \p key's value
* is part of the list passed to the constructor.
* \param dictionary The ghoul::Dictionary that contains the \p key
* \param key The key that is contained in the \p dictionary and whose value is tested
* \return A TestResult containing the results of the specification testing. If the
* \p key%'s value has the wrong type, it will be added to the TestResult's offense
* list with the reason TestResult::Offense::Reason::WrongType; if the value is not
* in the list, it will be added with the reason TestResult::Offense::Verification
* instead.
*/
TestResult operator()(const ghoul::Dictionary& dictionary,
const std::string& key) const override;
std::string documentation() const override;
/// The list of values against which the incoming value is tested
std::vector<typename T::Type> values;
};
/**
* This Verifier checks whether the incoming value is of the correct type, using the
* Verifier passed as a template parameter \c T and then checks whether it is not part of a
* list that is passed to the constructor. To the missing equality operator, \c T cannot
* be a subclass of (or the same as) TableVerifier.
*/
template <typename T>
struct NotInListVerifier : public T {
static_assert(!std::is_base_of_v<TableVerifier, T>, "T cannot be TableVerifier");
/**
* Constructs a NotInListVerifier that checks whether the incoming value is of the
* correct type and whether the value is not part of the list passed as \p values.
* \param values The list of values against which the incoming value is tested
*/
NotInListVerifier(std::vector<typename T::Type> values);
TestResult operator()(const ghoul::Dictionary& dict,
/**
* Tests whether the \p key exists in the \p dictionary, whether it has the correct
* type by invoking the template parameter \c T, and then tests if the \p key's value
* is not part of the list passed to the constructor.
* \param dictionary The ghoul::Dictionary that contains the \p key
* \param key The key that is contained in the \p dictionary and whose value is tested
* \return A TestResult containing the results of the specification testing. If the
* \p key%'s value has the wrong type, it will be added to the TestResult's offense
* list with the reason TestResult::Offense::Reason::WrongType; if the value is in the
* list, it will be added with the reason TestResult::Offense::Verification instead.
*/
TestResult operator()(const ghoul::Dictionary& dictionary,
const std::string& key) const override;
std::string documentation() const override;
@@ -205,7 +456,17 @@ struct NotInListVerifier : public T {
std::vector<typename T::Type> values;
};
// Range Verifiers
//----------------------------------------------------------------------------------------
// Range verifiers
//----------------------------------------------------------------------------------------
/**
* This Verifier checks whether the incoming value is of the correct type, using the
* Verifier passed as a template parameter \c T and then checks whether it is greater or
* equal to a lower limit and less or equal to a higher limit. To the missing comparison
* operators, \c T cannot be a subclass of (or the same as) BoolVerifier, StringVerifier,
* TableVerifier, or VectorVerifier. Both the lower and the higher limit are inclusive).
*/
template <typename T>
struct InRangeVerifier : public T {
static_assert(!std::is_base_of_v<BoolVerifier, T>, "T cannot be BoolVerifier");
@@ -213,9 +474,30 @@ struct InRangeVerifier : public T {
static_assert(!std::is_base_of_v<TableVerifier, T>, "T cannot be TableVerifier");
static_assert(!std::is_base_of_v<VectorVerifier, T>, "T cannot be VectorVerifier");
/**
* Constructs a InRangeVerifier that checks whether the incoming value is of the
* correct type and whether the value is greater or equal to \p lower and less or equal
* to \upper.
* \param lower The (inclusive) lower limit of the range
* \param upper The (inclusive) upper limit of the range
* \pre \p lower must be smaller or equal to \p upper
*/
InRangeVerifier(typename T::Type lower, typename T::Type upper);
TestResult operator()(const ghoul::Dictionary& dict,
/**
* Tests whether the \p key exists in the \p dictionary, whether it has the correct
* type by invoking the template parameter \c T, and then tests if the \p key's value
* is between the lower and upper limits (both inclusive) that were passed to the
* constructor.
* \param dictionary The ghoul::Dictionary that contains the \p key
* \param key The key that is contained in the \p dictionary and whose value is tested
* \return A TestResult containing the results of the specification testing. If the
* \p key%'s value has the wrong type, it will be added to the TestResult's offense
* list with the reason TestResult::Offense::Reason::WrongType; if the value is outside
* the range defined by the lower and upper limits passed to the constructor, it will
* be added with the reason TestResult::Offense::Verification instead.
*/
TestResult operator()(const ghoul::Dictionary& dictionary,
const std::string& key) const override;
std::string documentation() const override;
@@ -224,6 +506,13 @@ struct InRangeVerifier : public T {
typename T::Type upper;
};
/**
* This Verifier checks whether the incoming value is of the correct type, using the
* Verifier passed as a template parameter \c T and then checks whether it is outside the
* (exclusive) range defined by a lower and upper limit. To the missing comparison
* operators, \c T cannot be a subclass of (or the same as) BoolVerifier, StringVerifier,
* TableVerifier, or VectorVerifier. Both the lower and the higher limit are exclusive).
*/
template <typename T>
struct NotInRangeVerifier : public T {
static_assert(!std::is_base_of_v<BoolVerifier, T>, "T cannot be BoolVerifier");
@@ -231,9 +520,29 @@ struct NotInRangeVerifier : public T {
static_assert(!std::is_base_of_v<TableVerifier, T>, "T cannot be TableVerifier");
static_assert(!std::is_base_of_v<VectorVerifier, T>, "T cannot be VectorVerifier");
/**
* Constructs a InRangeVerifier that checks whether the incoming value is of the
* correct type and whether the value is less then \p lower and greater than \upper.
* \param lower The (exclusive) lower limit of the range
* \param upper The (exclusive) upper limit of the range
* \pre \p lower must be smaller or equal to \p upper
*/
NotInRangeVerifier(typename T::Type lower, typename T::Type upper);
TestResult operator()(const ghoul::Dictionary& dict,
/**
* Tests whether the \p key exists in the \p dictionary, whether it has the correct
* type by invoking the template parameter \c T, and then tests if the \p key's value
* is outside the lower and upper limits (both exclusive) that were passed to the
* constructor.
* \param dictionary The ghoul::Dictionary that contains the \p key
* \param key The key that is contained in the \p dictionary and whose value is tested
* \return A TestResult containing the results of the specification testing. If the
* \p key%'s value has the wrong type, it will be added to the TestResult's offense
* list with the reason TestResult::Offense::Reason::WrongType; if the value is greater
* or equal to the lower limit and less or equal to the upper limit, it will be added
* with the reason TestResult::Offense::Verification instead.
*/
TestResult operator()(const ghoul::Dictionary& dictionary,
const std::string& key) const override;
std::string documentation() const override;
@@ -242,92 +551,217 @@ struct NotInRangeVerifier : public T {
typename T::Type upper;
};
// Misc Verifiers
//----------------------------------------------------------------------------------------
// Misc verifiers
//----------------------------------------------------------------------------------------
/**
* This Verifier only checks for the correct type of the incoming value. If the
* documentation is requested, it will return an additional string that is the annotation.
* This can be used to specify further conditions that are hard (or impossible) to codify,
* but the user should be notified about. This, for example, can be that used to notify
* the user that the parameter should be a file of a specific type.
*/
template <typename T>
struct AnnotationVerifier : public T {
/**
* Constructs an AnnotationVerifier that contains the passed \p annotation which is
* passed to the user when a documentation is requested.
* \param annotation The annotation that is stored and returned to the user when it
* is requested.
* \pre annotation must not be empty
*/
AnnotationVerifier(std::string annotation);
std::string documentation() const override;
/// The annotation that is returned to the user in the documentation
std::string annotation;
};
// Boolean Verifiers
//----------------------------------------------------------------------------------------
// Misc verifiers
//----------------------------------------------------------------------------------------
/**
* This Verifier takes two Verifiers and performs a boolean \c and operation on their
* results. In essence, a value only passes this Verifier if it passes both Verifier%s
* that are passed in the constructor. Opposed to the <code>C++</code> <code>&&</code>
* operator, the AndVerifier does not perform any short-circut evaluation.
*/
struct AndVerifier : public Verifier {
AndVerifier(Verifier* a, Verifier* b);
/**
* Constructs an AndVerifier with two Verifiers which must be cleared by incoming
* values in order to pass this Verifier.
* \param lhs The first Verifier that is to be tested
* \param rhs The second Verifier that is to be tested
* \pre lhs must not be nullptr
* \pre rhs must not be nullptr
*/
AndVerifier(Verifier* lhs, Verifier* rhs);
TestResult operator()(const ghoul::Dictionary& dict,
/**
* Checks whether the \p dictionary contains the \p key and whether this key passes
* both Verifier%'s that were passed in the constructor. If the value fails either
* of the two Verifiers, it is only added once to the TestResult::offenses list with
* a reason of TestResult::Offense::Reason::Verification.
* \param dictionary The ghoul::Dictionary that is to be tested
* \param key The key contained in \p dictionary that is to be tested
* \return A TestResult object that contains the test results. If the value fails
* either of the two Verifiers, TestResult::success is \c false and the
* TestResult::offenses list contains \p with a reason of
* TestResult::Offense::Reason::Verification. If \p key%'s value passes both
* Verifier%s, the result's TestResult::success is \c true and the
* TestResult::offenses is empty.
*/
TestResult operator()(const ghoul::Dictionary& dictionary,
const std::string& key) const override;
std::string type() const override;
std::string documentation() const override;
std::shared_ptr<Verifier> a;
std::shared_ptr<Verifier> b;
/// The first Verifier that incoming values are tested against
std::shared_ptr<Verifier> lhs;
/// The second Verifier that incoming values are tested against
std::shared_ptr<Verifier> rhs;
};
/**
* This Verifier takes two Verifiers and performs a boolean \c or operation on their
* results. In essence, a value only passes this Verifier if it passes either of the two
* Verifier%s that are passed in the constructor. Opposed to the <code>C++</code>
* <code>||</code> operator, the OrVerifier does not perform any short-circut evaluation.
*/
struct OrVerifier : public Verifier {
OrVerifier(Verifier* a, Verifier* b);
/**
* Constructs an OrVerifier with two Verifiers, either of which must be cleared by
* incoming values in order to pass this Verifier.
* \param lhs The first Verifier that is to be tested
* \param rhs The second Verifier that is to be tested
* \pre lhs must not be nullptr
* \pre rhs must not be nullptr
*/
OrVerifier(Verifier* lhs, Verifier* rhs);
/**
* Checks whether the \p dictionary contains the \p key and whether this key passes
* either of the two Verifier%'s that were passed in the constructor. If the value
* fails both Verifiers, it is added to the TestResult::offenses list with a reason of
* TestResult::Offense::Reason::Verification.
* \param dictionary The ghoul::Dictionary that is to be tested
* \param key The key contained in \p dictionary that is to be tested
* \return A TestResult object that contains the test results. If the value fails
* both Verifiers, TestResult::success is \c false and the TestResult::offenses list
* contains \p with a reason of TestResult::Offense::Reason::Verification. If \p key%'s
* value passes either of the two Verifier%s, the result's TestResult::success is
* \c true and the TestResult::offenses is empty.
*/
TestResult operator()(const ghoul::Dictionary& dict,
const std::string& key) const override;
std::string type() const override;
std::string documentation() const override;
std::shared_ptr<Verifier> a;
std::shared_ptr<Verifier> b;
/// The first Verifier that incoming values are tested against
std::shared_ptr<Verifier> lhs;
/// The second Verifier that incoming values are tested against
std::shared_ptr<Verifier> rhs;
};
/// A short-hand definition for a Verifier checking for <code>glm::bvec2</code>
using BoolVector2Verifier = Vector2Verifier<bool>;
/// A short-hand definition for a Verifier checking for <code>glm::ivec2</code>
using IntVector2Verifier = Vector2Verifier<int>;
/// A short-hand definition for a Verifier checking for <code>glm::dvec2</code>
using DoubleVector2Verifier = Vector2Verifier<double>;
/// A short-hand definition for a Verifier checking for <code>glm::bvec3</code>
using BoolVector3Verifier = Vector3Verifier<bool>;
/// A short-hand definition for a Verifier checking for <code>glm::ivec3</code>
using IntVector3Verifier = Vector3Verifier<int>;
/// A short-hand definition for a Verifier checking for <code>glm::dvec3</code>
using DoubleVector3Verifier = Vector3Verifier<double>;
/// A short-hand definition for a Verifier checking for <code>glm::bvec4</code>
using BoolVector4Verifier = Vector4Verifier<bool>;
/// A short-hand definition for a Verifier checking for <code>glm::ivec4</code>
using IntVector4Verifier = Vector4Verifier<int>;
/// A short-hand definition for a Verifier checking for <code>glm::dvec4</code>
using DoubleVector4Verifier = Vector4Verifier<double>;
/// A short-hand definition for a LessVerifier with a type check for \c int
using IntLessVerifier = LessVerifier<IntVerifier>;
/// A short-hand definition for a LessVerifier with a type check for \c double
using DoubleLessVerifier = LessVerifier<DoubleVerifier>;
/// A short-hand definition for a LessEqualVerifier with a type check for \c int
using IntLessEqualVerifier = LessEqualVerifier<IntVerifier>;
/// A short-hand definition for a LessEqualVerifier with a type check for \c double
using DoubleLessEqualVerifier = LessEqualVerifier<DoubleVerifier>;
/// A short-hand definition for a GreaterVerifier with a type check for \c int
using IntGreaterVerifier = GreaterVerifier<IntVerifier>;
/// A short-hand definition for a GreaterVerifier with a type check for \c double
using DoubleGreaterVerifier = GreaterVerifier<DoubleVerifier>;
/// A short-hand definition for a GreaterEqualVerifier with a type check for \c int
using IntGreaterEqualVerifier = GreaterEqualVerifier<IntVerifier>;
/// A short-hand definition for a GreaterEqualVerifier with a type check for \c double
using DoubleGreaterEqualVerifier = GreaterEqualVerifier<DoubleVerifier>;
/// A short-hand definition for a EqualVerifier with a type check for \c bool
using BoolEqualVerifier = EqualVerifier<BoolVerifier>;
/// A short-hand definition for a EqualVerifier with a type check for \c int
using IntEqualVerifier = EqualVerifier<IntVerifier>;
/// A short-hand definition for a EqualVerifier with a type check for \c double
using DoubleEqualVerifier = EqualVerifier<DoubleVerifier>;
/// A short-hand definition for a EqualVerifier with a type check for \c string
using StringEqualVerifier = EqualVerifier<StringVerifier>;
/// A short-hand definition for a UnequalVerifier with a type check for \c bool
using BoolUnequalVerifier = UnequalVerifier<BoolVerifier>;
/// A short-hand definition for a UnequalVerifier with a type check for \c int
using IntUnequalVerifier = UnequalVerifier<IntVerifier>;
/// A short-hand definition for a UnequalVerifier with a type check for \c double
using DoubleUnequalVerifier = UnequalVerifier<DoubleVerifier>;
/// A short-hand definition for a UnequalVerifier with a type check for \c string
using StringUnequalVerifier = UnequalVerifier<StringVerifier>;
/// A short-hand definition for a InListVerifier with a type check for \c bool
using BoolInListVerifier = InListVerifier<BoolVerifier>;
/// A short-hand definition for a InListVerifier with a type check for \c int
using IntInListVerifier = InListVerifier<IntVerifier>;
/// A short-hand definition for a InListVerifier with a type check for \c double
using DoubleInListVerifier = InListVerifier<DoubleVerifier>;
/// A short-hand definition for a InListVerifier with a type check for \c string
using StringInListVerifier = InListVerifier<StringVerifier>;
/// A short-hand definition for a NotInListVerifier with a type check for \c bool
using BoolNotInListVerifier = NotInListVerifier<BoolVerifier>;
/// A short-hand definition for a NotInListVerifier with a type check for \c int
using IntNotInListVerifier = NotInListVerifier<IntVerifier>;
/// A short-hand definition for a NotInListVerifier with a type check for \c double
using DoubleNotInListVerifier = NotInListVerifier<DoubleVerifier>;
/// A short-hand definition for a NotInListVerifier with a type check for \c string
using StringNotInListVerifier = NotInListVerifier<StringVerifier>;
/// A short-hand definition for a InRangeVerifier with a type check for \c int
using IntInRangeVerifier = InRangeVerifier<IntVerifier>;
/// A short-hand definition for a InRangeVerifier with a type check for \c double
using DoubleInRangeVerifier = InRangeVerifier<DoubleVerifier>;
/// A short-hand definition for a NotInRangeVerifier with a type check for \c int
using IntNotInRangeVerifier = NotInRangeVerifier<IntVerifier>;
/// A short-hand definition for a NotInRangeVerifier with a type check for \c double
using DoubleNotInRangeVerifier = NotInRangeVerifier<DoubleVerifier>;
/// A short-hand definition for a AnnotationVerifier with a type check for \c bool
using BoolAnnotationVerifier = AnnotationVerifier<BoolVerifier>;
/// A short-hand definition for a AnnotationVerifier with a type check for \c int
using IntAnnotationVerifier = AnnotationVerifier<IntVerifier>;
/// A short-hand definition for a AnnotationVerifier with a type check for \c double
using DoubleAnnotationVerifier = AnnotationVerifier<DoubleVerifier>;
/// A short-hand definition for a AnnotationVerifier with a type check for \c string
using StringAnnotationVerifier = AnnotationVerifier<StringVerifier>;
/// A short-hand definition for a AnnotationVerifier with a type check for
/// <code>ghoul::Dictionary</code>
using TableAnnotationVerifier = AnnotationVerifier<TableVerifier>;
// Definitions of external templates that are instantiated in the cpp file
// This cuts down the compilation times as almost all of the possible template types do
// not need to be instantiated multiple times
extern template struct Vector2Verifier<bool>;
extern template struct Vector2Verifier<int>;
extern template struct Vector2Verifier<double>;
+33 -21
View File
@@ -25,7 +25,7 @@
#include <iterator>
namespace std {
std::string to_string(std::string value);
std::string to_string(std::string value);
}
namespace openspace {
@@ -35,17 +35,22 @@ template <typename T>
TestResult TemplateVerifier<T>::operator()(const ghoul::Dictionary& dict,
const std::string& key) const
{
if (dict.hasKey(key)) {
if (dict.hasValue<Type>(key)) {
return{ true, {} };
if (dict.hasKeyAndValue<Type>(key)) {
return { true, {} };
}
else {
if (dict.hasKey(key)) {
return { false, { { key, TestResult::Offense::Reason::MissingKey } } };
}
else {
return { false, { { key, TestResult::Offense::Reason::WrongType } } };
}
}
else {
return { false, { { key, TestResult::Offense::Reason::MissingKey } } };
}
}
template <typename T>
std::string TemplateVerifier<T>::documentation() const {
return "Type testing of '" + type() + "'";
}
template <typename T>
@@ -69,18 +74,18 @@ std::string Vector4Verifier<T>::type() const {
return "Vector4<"s + typeid(T).name() + ">";
}
template <typename T, typename Op>
OperatorVerifier<T, Op>::OperatorVerifier(typename T::Type value)
template <typename T, typename Operator>
OperatorVerifier<T, Operator>::OperatorVerifier(typename T::Type value)
: value(std::move(value))
{}
template <typename T, typename Op>
TestResult OperatorVerifier<T, Op>::operator()(const ghoul::Dictionary& dict,
const std::string& key) const
template <typename T, typename Operator>
TestResult OperatorVerifier<T, Operator>::operator()(const ghoul::Dictionary& dict,
const std::string& key) const
{
TestResult res = T::operator()(dict, key);
if (res.success) {
if (Op()(dict.value<Type>(key), value)) {
if (Operator()(dict.value<Type>(key), value)) {
return { true, {} };
}
else {
@@ -154,7 +159,11 @@ std::string InListVerifier<T>::documentation() const {
std::string result = "In list { ";
std::stringstream s;
std::copy(values.begin(), values.end(), std::ostream_iterator<typename T::Type>(s, ","));
std::copy(
values.begin(),
values.end(),
std::ostream_iterator<typename T::Type>(s, ",")
);
std::string joined = s.str();
// We need to remove a trailing ',' at the end of the string
@@ -196,13 +205,16 @@ std::string NotInListVerifier<T>::documentation() const {
std::string result = "Not in list { ";
std::stringstream s;
std::copy(values.begin(), values.end(), std::ostream_iterator<typename T::Type>(s, ","));
std::copy(
values.begin(),
values.end(),
std::ostream_iterator<typename T::Type>(s, ",")
);
std::string joined = s.str();
// We need to remove a trailing ',' at the end of the string
result += joined.substr(0, joined.size() - 1);
result += " }";
return result;
}
@@ -212,10 +224,9 @@ InRangeVerifier<T>::InRangeVerifier(typename T::Type lower, typename T::Type upp
: lower(std::move(lower))
, upper(std::move(upper))
{
ghoul_assert(lower <= upper, "Lower value must be smaller or equal to upper value");
ghoul_assert(lower <= upper, "lower must be smaller or equal to upper");
}
template <typename T>
TestResult InRangeVerifier<T>::operator()(const ghoul::Dictionary& dict,
const std::string& key) const
@@ -247,7 +258,7 @@ NotInRangeVerifier<T>::NotInRangeVerifier(typename T::Type lower, typename T::Ty
: lower(std::move(lower))
, upper(std::move(upper))
{
ghoul_assert(lower <= upper, "Lower value must be smaller or equal to upper value");
ghoul_assert(lower <= upper, "lower must be smaller or equal to upper");
}
template <typename T>
@@ -279,8 +290,9 @@ std::string NotInRangeVerifier<T>::documentation() const {
template <typename T>
AnnotationVerifier<T>::AnnotationVerifier(std::string annotation)
: annotation(std::move(annotation))
{}
{
ghoul_assert(!this->annotation.empty(), "Annotation must not be empty");
}
template <typename T>
std::string AnnotationVerifier<T>::documentation() const {