// src/safe_int.hh // This file is part of libpbe; see http://svn.chezphil.org/libpbe/ // (C) 2007 Philip Endecott // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. // safe_int: provide an N-bit signed integer (N excludes the sign bit) with overflow // checking. An exception is thrown if the value overflows. // The implementation uses Boost.Integer to supply an integer with at least one more bit // than we need. This extra guard bit is tested to detect overflow. // This approach is reasonably efficient, except in the case where the need for the extra // bit means that we have to use a larger underlying value; for example, safe_int<31> needs // an int64_t. There is another approach that could work in that case, which is to compare // the operands before performing the operation; that could be added as a specialisation of // this template for safe_int<31>, safe_int<15> and safe_int<7>. // One of the better resources describing this subject is: // http://msdn2.microsoft.com/en-us/library/ms972705.aspx #ifndef libpbe_safe_int_hh #define libpbe_safe_int_hh #include "./integer.hpp" #include namespace pbe { template // NBITS excludes the sign bit class safe_int: boost::operators > { public: struct overflow: public std::exception { const char* what() const throw() { return "safe_int overflow"; } }; private: typedef typename boost::int_t::least val_t; val_t val; void check() { // If guard bits != sign bit, value has overflowed. val_t x = val>>NBITS; if (x==0 || x==-1) { return; } throw overflow(); } public: safe_int() {} safe_int(const safe_int& x): val(x.val) {} // This can convert from integer or other safe_int types. // It would be good if we could do a non-checking version when X_T is guaranteed to be // smaller than NBITS. Is there any template-magic that can do that? (enable_if?) template safe_int(X_T x): val(static_cast(x)) { check(); } // Implicitly convert to val_t. (Is this a safe thing to do?) operator val_t() { return val; } // These don't need any checking: bool operator<(const safe_int& x) const { return val>=(const safe_int& x) { val>>=x.val; return *this; } // These can be checked using the guard bit: safe_int& operator+=(const safe_int& x) { val+=x.val; check(); return *this; } safe_int& operator-=(const safe_int& x) { val-=x.val; check(); return *this; } // These need more complex checking: // This only works when val_t is <= int32_t. safe_int& operator*=(const safe_int& x) { // Do a multiplication that is certain to have enough bits for the result: typedef typename boost::int_t<2*NBITS+1>::least twice_val_t; twice_val_t tw = static_cast(val) * static_cast(x.val); // Overflow if any too-significant bits are set: twice_val_t r = tw >> NBITS; if (r!=0 && r!=-1) { throw overflow(); } val = static_cast(tw); return *this; } // The only case where division can overflow is -MAX/-1. // We could also detect division by zero here if we wanted to. safe_int& operator/=(const safe_int& x) { if (x.val==-1 && val==(1<>(NBITS-x.val); if (r!=0 && r!=-1) { throw overflow(); } val <<= x.val; return *this; } // These are implemented in terms of other operators, which do checking: safe_int& operator++() { return (*this) += 1; } safe_int& operator--() { return (*this) -= 1; } // (Does Boost.Operators provide unary operator- ?) // safe_int operator-() { ... } // It would probably be reasonable to allow a different type for the RHS of most of these // operators. Boost.Integer has some code to do this, but I have not investigated it. }; }; #endif