In a
previous blog entry about unit-testing in C++ I wrote that instead of writing something like this...
expected = 42;
actual = expression();
assert_equals(expected, actual);
you could write something like this...
ARE_EQUAL(int)
{
...
expected = 42;
actual = 40+1
}
Admittedly this was not much of an improvement, if at all. However, on a flight to Oslo a few days ago I suddenly realized a way to improve it. The idea is to fold an expected == actual assertion down to this:
expected(42) == actual(40+1);
Where expected and actual can be two function templates that return objects wrapping their arguments. These objects can have an overloaded == operator that does the assertion for me. Instead of emphasizing the assertion I emphasize the roles instead. Here's some proof of concept code:
#include <cassert>
#include <iostream>
template<typename T>
struct asserted_role
{
asserted_role(const char * name, const T * ptr)
: name(name)
, ptr(ptr)
{
}
const char * name;
const T * ptr;
};
template<typename T>
void failed_assertion(const asserted_role<T> & lhs,
const char * op,
const asserted_role<T> & rhs)
{
std::cout << "FAILED ASSERTION" ...
<< " " << lhs.name << "(" << *lhs.ptr << ") "
<< op
<< " " << rhs.name << "(" << *rhs.ptr << ")"
<< endl;
}
template<typename T>
void operator==(const asserted_role<T> & lhs,
const asserted_role<T> & rhs)
{
if (*lhs.ptr == *rhs.ptr)
;
else
failed_assertion(lhs, "==", rhs);
}
template<typename T>
asserted_role<T> expected(const T & t)
{
return asserted_role<T>("expected", &t);
}
template<typename T>
asserted_role<T> actual(const T & t)
{
return asserted_role<T>("actual", &t);
}
int main()
{
expected(42) == actual(40+1);
}
There are a couple of things I like about this idea. The first is I no longer have to remember to get the expected and actual in the right order; if I want to I can write:
actual(40+1) == expected(42);
The second is that the idea can be extended. For example, I can create similar function templates called lesser and greater and add a < operator:
lesser(42) < greater(40+1);
I can create a new template function of any name I like to express the role I'm thinking of. For example, suppose I want to test that the value of one expression is equal to the value of another expression, but neither value is "expected" or "actual" they are just two values and I don't care what the values actually are, other than the two values are equal. I can create two function templates called lhs and rhs:
lhs(2 + 2) == rhs(4);