Saturday, July 15, 2006

After a few frantic nights of debugging the debugger, I think I have a stable version again... (there are a few minor issues still, but no show stopper that I know of). Sheesh.

As I was cleaning up some code, I came upon a few warnings, where signed values are compared against unsigned. The types are well chosen IMHO, for example in one case the unsigned is a memory address, and the signed is a relative offset (which may be negative). The assertions are more informative than anything else. The code is prepared to deal with the case when the assertion fails, I am just curious to see whether my assumption that a given condition should never happen is correct (and it's not the end of the world if it isn't). I would call this a soft assert in case the term is not coined already.

Here's the code I came up with, that silences down the compiler warnings about signed vs. unsigned, and throws an exception rather than calling abort on failure.



#include <assert.h>
#include <limits>
#include <stdexcept>
#include <boost/format.hpp>


class assert_error : public std::logic_error
{
public:
assert_error(const char* file, size_t line)
: std::logic_error((boost::format(
"Assertion failed at: %1%:%2%") % file % line).str())
{ }
};



// Assert functions that avoid the signed vs. unsigned compiler warnings.
namespace detail
{
template<typename T, typename U, int>
struct numeric_assert
{
static bool gt(T lhs, U rhs) { return(lhs > rhs); }
static bool eq(T lhs, U rhs) { return(lhs == rhs); }
};
template<typename T, typename U>
struct numeric_assert<T, U, 1>
{
static bool gt(T lhs, U rhs)
{ return((lhs >= 0) && (static_cast<U>(lhs) > rhs)); }

static bool eq(T lhs, U rhs)
{ return((rhs >= 0) && (rhs == static_cast<U>(lhs))); }
};
template<typename T, typename U>
struct numeric_assert<T, U, -1>
{
static bool gt(T lhs, U rhs)
{ return((rhs < 0) || (lhs > static_cast<T>(rhs))); }

static bool eq(T lhs, U rhs)
{ return((rhs >= 0) && (lhs == static_cast<T>(rhs))); }
};
}
template<typename T, typename U>
void __assert_gt(T lhs, U rhs, const char* file, size_t line)
{
typedef detail::numeric_assert<T, U,
std::numeric_limits<T>::is_signed -
std::numeric_limits<U>::is_signed> Assert;
if (!Assert::gt(lhs, rhs))
{
throw assert_error(file, line);
}
}


template<typename T, typename U>
void __assert_eq(T lhs, U rhs, const char* file, size_t line)
{
typedef detail::numeric_assert<T, U,
std::numeric_limits<T>::is_signed -
std::numeric_limits<U>::is_signed> Assert;
if (!Assert::eq(lhs, rhs))
{
throw assert_error(file, line);
}
}
#define assert_gt(x,y) __assert_gt((x), (y), __FILE__, __LINE__)
#define assert_eq(x,y) __assert_eq((x), (y), __FILE__, __LINE__)




Oh, and the sed command I was talking about the other day is:

sed -e 's/</\&lt;/g; s/>/\&gt;/g'


Specializing the templates for double and float is left as an exercise for the reader.

No comments: