The photos don't do it justice: the components are in glorious 3D in real life.
There's probably something profound about making an old-fashioned pen from new-fangled technology. Probably.
The personal blog of Ian Taylor
ASSERT(a < b);
if (!VERIFY(a != b)) { ... }If you're running without a debugger attached or with optimisations fully on, you'll get the usual error report, but no idea what the underlying values are or were:
test.cpp(1): Failed VERIFY(a != b)Wouldn't it be wonderful if it did? Up until recently, I thought the only way of achieving that was to use ugly macros of the forms:
ASSERT_OP(a, <, b);which, if you're lucky, will produce output similar to:
ASSERT_LT(a, b);
test.cpp(2): Failed ASSERT(a < b)The only problem is that it's extremely ugly. There must be a better way, surely? But it's C++: there's always an alternative, no matter how convoluted internally. Consider operator precedence:
'a' is 321
'b' is 123
something << a < b >> somethingThis will evaluate 'something << a' first, then 'b >> something', and then apply the less-than operation to the results. Now consider:
namespace Verify
{
struct Outer;
template<typename T>
struct Inner
{
const T& value;
explicit Inner(const T& inner) :
value(inner) {}
};
template<typename T>
Inner<T> operator<<(const Outer&,
const T& inner)
{
return Inner<T>(inner);
}
template<typename T>
Inner<T> operator>>(const T& inner,
const Outer&)
{
return Inner<T>(inner);
}
}
#define CHILLIANT_OUTER \
(*(Verify::Outer*)nullptr)
#define CHILLIANT_VERIFY(expr) /
CHILLIANT_BLAH(CHILLIANT_OUTER << expr >>\
CHILLIANT_OUTER)
#define CHILLIANT_VERIFY(expr) /
(Verify::Report(__FILE__,__LINE__,#expr, \
(CHILLIANT_OUTER << expr >>\
CHILLIANT_OUTER)))
namespace Verify
{
template<typename L,typename R>
bool Report(/*blah*/,
const Binary<L,R>& result)
{
if (!result.condition)
{
/*blah*/
}
return result.condition;
}
template<typename T>
bool Report(/*blah*/,
const Unary<T>& result)
{
if (!result.condition)
{
/*blah*/
}
return result.condition;
}
bool Report(/*blah*/,
bool result)
{
if (!result)
{
/*blah*/
}
return result;
}
}
namespace Verify
{
template<typename L,typename R>
struct Binary
{
bool condition;
const char* operation;
const Inner<L>& lhs;
const Inner<R>& rhs;
Binary(bool result,
const char* op,
const Inner<L>& left,
const Inner<R>& right) :
condition(result),
operation(op),
lhs(left),
rhs(right) {}
};
template<typename L,typename R>
Binary<L,R> operator<(const Inner<L>& lhs,
const Inner<R>& rhs)
{
return Binary<L,R>(
lhs.value < rhs.value,
"<", lhs, rhs);
}
/* Similarly for <=, ==, !=, > and >= */
}
namespace Verify
{
template<typename T>
struct Unary
{
bool condition;
const Inner<T>& value;
Unary(bool result,
const Inner<T>& inner) :
condition(result),
value(inner) {}
};
template<typename T>
Unary<T> operator>>(const Inner<T>& inner,
const Outer&)
{
return Unary<T>(inner.value, inner);
}
}
namespace Verify
{
template<typename T>
struct Inner
{
const T& value;
explicit Inner(const T& inner) :
value(inner) {}
operator T() const { return value; }
};
}
namespace Verify
{
void Output(const char* fmt, ...);
struct Outer;
template<typename T>
struct Inner
{
const T& value;
explicit Inner(const T& inner) :
value(inner) {}
operator T() const { return value; }
void Output(const char* label) const;
};
template<typename T>
Inner<T> operator<<(const Outer&,
const T& inner)
{
return Inner<T>(inner);
}
template<typename T>
Inner<T> operator>>(const T& inner,
const Outer&)
{
return Inner<T>(inner);
}
template<typename T>
struct Unary
{
bool condition;
const Inner<T>& value;
Unary(bool result,
const Inner<T>& inner) :
condition(result),
value(inner) {}
};
template<typename T>
Unary<T> operator>>(const Inner<T>& inner,
const Outer&)
{
return Unary<T>(inner.value, inner);
}
template<typename L,typename R>
struct Binary
{
bool condition;
const char* operation;
const Inner<L>& lhs;
const Inner<R>& rhs;
Binary(bool result, const char* op,
const Inner<L>& left,
const Inner<R>& right) :
condition(result),
operation(op),
lhs(left),
rhs(right) {}
};
template<typename L,typename R>
Binary<L,R> operator<(
const Inner<L>& lhs,
const Inner<R>& rhs)
{
return Binary<L,R>(
lhs.value < rhs.value,
"<", lhs, rhs);
}
template<typename L,typename R>
Binary<L,R> operator<=(
const Inner<L>& lhs,
const Inner<R>& rhs)
{
return Binary<L,R>(
lhs.value <= rhs.value,
"<=", lhs, rhs);
}
template<typename L,typename R>
Binary<L,R> operator>(
const Inner<L>& lhs,
const Inner<R>& rhs)
{
return Binary<L,R>(
lhs.value > rhs.value,
">", lhs, rhs);
}
template<typename L,typename R>
Binary<L,R> operator>=(
const Inner<L>& lhs,
const Inner<R>& rhs)
{
return Binary<L,R>(
lhs.value >= rhs.value,
">=", lhs, rhs);
}
template<typename L,typename R>
Binary<L,R> operator==(
const Inner<L>& lhs,
const Inner<R>& rhs)
{
return Binary<L,R>(
lhs.value == rhs.value,
"==", lhs, rhs);
}
template<typename L,typename R>
Binary<L,R> operator!=(
const Inner<L>& lhs,
const Inner<R>& rhs)
{
return Binary<L,R>(
lhs.value != rhs.value,
"!=", lhs, rhs);
}
bool Report(const char* file, long line,
const char* expression,
bool result)
{
if (!result)
{
Output("%s(%ld): Failed "
"CHILLIANT_VERIFY(%s)\n",
file, line, expression);
Inner<bool> value(false);
value.Output("value");
}
return result;
}
template<typename T>
bool Report(const char* file, long line,
const char* expression,
const Unary<T>& result)
{
if (!result.condition)
{
Output("%s(%ld): Failed "
"CHILLIANT_VERIFY(%s)\n",
file, line, expression);
result.value.Output("value");
}
return result.condition;
}
template<typename L,typename R>
bool Report(const char* file, long line,
const char* expression,
const Binary<L,R>& result)
{
if (!result.condition)
{
Output("%s(%ld): Failed "
"CHILLIANT_VERIFY(%s)\n",
file, line, expression);
Output(" for "
"(LHS %s RHS)"
" where\n",
result.operation);
result.lhs.Output("LHS");
result.rhs.Output("RHS");
}
return result.condition;
}
}
#define CHILLIANT_VERIFY_OUTER \
(*(Verify::Outer*)nullptr)
#define CHILLIANT_VERIFY(expr) \
(Verify::Report(__FILE__,__LINE__,#expr,\
(CHILLIANT_VERIFY_OUTER << expr >>\
CHILLIANT_VERIFY_OUTER)))
template<>
void Verify::Inner<bool>::
Output(const char* label) const
{
Verify::Output(" %s is %s (bool)\n",
label,
value ? "true" : "false");
}
template<>
void Verify::Inner<int> :: Output(const char* label) const
{
Verify::Output(" %s is %d (int)\n",
label,
value);
}
template<>
void Verify::Inner<float> :: Output(const char* label) const
{
Verify::Output(" %s is %g (float)\n",
label,
value);
}
void TestVerify(void)
{
int i = 3;
float f = 3.14159f;
if (!CHILLIANT_VERIFY(false)) ...
if (!CHILLIANT_VERIFY(i > 0 && i < 2)) ...
if (!CHILLIANT_VERIFY(12 * 6 < i + 7)) ...
if (!CHILLIANT_VERIFY(12 * 6 > f * 1000)) ...
}
test.cpp(101): Failed CHILLIANT_VERIFY(false)
value is false (bool)
test.cpp(102): Failed CHILLIANT_VERIFY(i > 0 && i < 2)
value is false (bool)
test.cpp(103): Failed CHILLIANT_VERIFY(12 * 6 < i + 7)
for CHILLIANT_VERIFY(LHS < RHS) where
LHS is 72 (int)
RHS is 10 (int)
test.cpp(104): Failed CHILLIANT_VERIFY(12 * 6 > f * 1000)
for CHILLIANT_VERIFY(LHS > RHS) where
LHS is 72 (int)
RHS is 3141.59 (float)
#define CHILLIANT_VERIFY(expr) (expr)

