Invariant checking and validation
Some classes or API specifications may admit a valdidity or consistency/invariant check. For uniformity in tests, these checks should conform to the API described below.
Validity checks
Validity tests confirm that members have valid values and that any class invariants have been preserved.
Classes which offer a validity test should provide a public method
X is_valid() const
with a return value that converts implicitly to bool
, giving false if and only if the validation check fails. The return value may contain additional information, depending on the type.
The check_valid_api
class
The check_valid_api
template class definied in check_valid.h
provides additional functionality. A class T
deriving from check_valid<T>
provides additional member functions:
void check_valid() const;
void check_valid(const std::string &message) const;
template <typename Exception,typename... Args>
void check_valid_ex(Args &&... args) const;
void assert_valid() const;
The first two will throw an exception of type rdmini::validation_failure
if is_valid()
returns a value that implicity converts to false; if no message is explicitly given, and the return type of is_valid
has a member function with signature R what() const
with return type R
convertible to std::string
, this message is used to construct the validation_failure
exception.
The third form will throw an exception of type specified in the template parameter, constructed from the supplied arguments.
The assert_valid()
member function will do nothing if NDEBUG
is defined, but otherwise will abort with a message if the validation check fails.
A helper class valid_info
is provided as a convenient return type for an is_valid
member function. valid_info
converts implicitly to and from bool
, but also may be constructed from a std::string
argument describing how a validation check fails.
Other functionality in check_valid.h
This header also defines two macros, SOURCE_LINE
(a string literal) and SOURCE_LINE_FUNC
(a temporary value of type const char *
) which expand to the current source file and line number, or file, line number and function name respectively.
In addition, two scoped validation check functions are provided, check_valid_guard
and assert_valid_guard
. These produce a guard object (of implementation-specific type) which will perform validity checks on the supplied object or object pointer at construction and also when the guard falls out of scope (i.e. is destroyed.)
check_valid_guard
will throw a rdmini::validation_failure
exception on check failure.
assert_valid_guard
will perform no checks if NDEBUG
is defined, but otherwise will print a message to stderr
and abort if the check fails.
Example usage:
using namespace rdmini;
struct my_class: check_valid_api<my_class> {
void foo() {
// abort if is_valid() returns false at beginning or end
// of the methos.
auto _(assert_valid_guard(this));
unsafe_operation();
// ...
}
void unsafe_operation() { /* ... */ }
valid_info is_valid() const {
if (a!=b) return "a not equal to b";
else return true;
}
int a,b; // invariant: a==b
};
void bar(my_class &x) {
// throw if x is invalid at beginning or end of bar()
auto _(check_valid_guard(x));
x.unsafe_operation();
}