|
11 | 11 | #define CPROVER_UTIL_ARITH_TOOLS_H
|
12 | 12 |
|
13 | 13 | #include "mp_arith.h"
|
| 14 | +#include "optional.h" |
| 15 | +#include "invariant.h" |
14 | 16 |
|
15 | 17 | class exprt;
|
16 | 18 | class constant_exprt;
|
17 | 19 | class typet;
|
18 | 20 |
|
19 | 21 | // this one will go away
|
20 | 22 | // returns 'true' on error
|
| 23 | +/// \deprecated: use the constant_exprt version instead |
21 | 24 | bool to_integer(const exprt &expr, mp_integer &int_value);
|
22 | 25 |
|
23 | 26 | // returns 'true' on error
|
| 27 | +/// \deprecated: use numeric_cast<mp_integer> instead |
24 | 28 | bool to_integer(const constant_exprt &expr, mp_integer &int_value);
|
25 | 29 |
|
26 | 30 | // returns 'true' on error
|
27 | 31 | bool to_unsigned_integer(const constant_exprt &expr, unsigned &uint_value);
|
28 | 32 |
|
| 33 | +/// Numerical cast provides a unified way of converting from one numerical type |
| 34 | +/// to another. |
| 35 | +/// Generic case doesn't exist, this has to be specialized for different types. |
| 36 | +template <typename Target, typename = void> |
| 37 | +struct numeric_castt final |
| 38 | +{ |
| 39 | +}; |
| 40 | + |
| 41 | +/// Convert expression to mp_integer |
| 42 | +template <> |
| 43 | +struct numeric_castt<mp_integer> final |
| 44 | +{ |
| 45 | + optionalt<mp_integer> operator()(const exprt &expr) const |
| 46 | + { |
| 47 | + mp_integer out; |
| 48 | + if(to_integer(expr, out)) |
| 49 | + return {}; |
| 50 | + return out; |
| 51 | + } |
| 52 | +}; |
| 53 | + |
| 54 | +/// Convert mp_integer or expr to any integral type |
| 55 | +template <typename T> |
| 56 | +struct numeric_castt<T, |
| 57 | + typename std::enable_if<std::is_integral<T>::value>::type> |
| 58 | +{ |
| 59 | +private: |
| 60 | + // Unchecked conversion from mp_integer when T is signed |
| 61 | + template <typename U = T, |
| 62 | + typename std::enable_if<std::is_signed<U>::value, int>::type = 0> |
| 63 | + static auto get_val(const mp_integer &mpi) -> decltype(mpi.to_long()) |
| 64 | + { |
| 65 | + return mpi.to_long(); |
| 66 | + } |
| 67 | + |
| 68 | + // Unchecked conversion from mp_integer when T is unsigned |
| 69 | + template <typename U = T, |
| 70 | + typename std::enable_if<!std::is_signed<U>::value, int>::type = 0> |
| 71 | + static auto get_val(const mp_integer &mpi) -> decltype(mpi.to_ulong()) |
| 72 | + { |
| 73 | + return mpi.to_ulong(); |
| 74 | + } |
| 75 | + |
| 76 | +public: |
| 77 | + // Conversion from mp_integer to integral type T |
| 78 | + optionalt<T> operator()(const mp_integer &mpi) const |
| 79 | + { |
| 80 | +#if !defined(_MSC_VER) || _MSC_VER >= 1900 |
| 81 | + static_assert( |
| 82 | + std::numeric_limits<T>::max() <= |
| 83 | + std::numeric_limits<decltype(get_val(mpi))>::max() && |
| 84 | + std::numeric_limits<T>::min() >= |
| 85 | + std::numeric_limits<decltype(get_val(mpi))>::min(), |
| 86 | + "Numeric cast only works for types smaller than long long"); |
| 87 | +#else |
| 88 | + // std::numeric_limits<> methods are not declared constexpr in old versions |
| 89 | + // of VS |
| 90 | + PRECONDITION( |
| 91 | + std::numeric_limits<T>::max() <= |
| 92 | + std::numeric_limits<decltype(get_val(mpi))>::max() && |
| 93 | + std::numeric_limits<T>::min() >= |
| 94 | + std::numeric_limits<decltype(get_val(mpi))>::min()); |
| 95 | +#endif |
| 96 | + if( |
| 97 | + mpi <= std::numeric_limits<T>::max() && |
| 98 | + mpi >= std::numeric_limits<T>::min()) |
| 99 | + // to_long converts to long long which is the largest signed numeric type |
| 100 | + return static_cast<T>(get_val(mpi)); |
| 101 | + else |
| 102 | + return {}; |
| 103 | + } |
| 104 | + |
| 105 | + // Conversion from expression |
| 106 | + optionalt<T> operator()(const exprt &expr) const |
| 107 | + { |
| 108 | + if(auto mpi_opt = numeric_castt<mp_integer>{}(expr)) |
| 109 | + return numeric_castt<T>{}(*mpi_opt); |
| 110 | + else |
| 111 | + return {}; |
| 112 | + } |
| 113 | +}; |
| 114 | + |
| 115 | +/// Converts an expression to any integral type |
| 116 | +/// \tparam Target: type to convert to |
| 117 | +/// \param arg: expression to convert |
| 118 | +/// \return optional integer of type Target if conversion is possible, |
| 119 | +/// empty optional otherwise. |
| 120 | +template <typename Target> |
| 121 | +optionalt<Target> numeric_cast(const exprt &arg) |
| 122 | +{ |
| 123 | + return numeric_castt<Target>{}(arg); |
| 124 | +} |
| 125 | + |
| 126 | +/// Convert an mp_integer to integral type Target |
| 127 | +/// An invariant with fail with message "Bad conversion" if conversion |
| 128 | +/// is not possible. |
| 129 | +/// \tparam Target: type to convert to |
| 130 | +/// \param arg: mp_integer |
| 131 | +/// \return value of type Target |
| 132 | +template <typename Target> |
| 133 | +Target numeric_cast_v(const mp_integer &arg) |
| 134 | +{ |
| 135 | + const auto maybe = numeric_castt<Target>{}(arg); |
| 136 | + INVARIANT(maybe, "Bad conversion"); |
| 137 | + return *maybe; |
| 138 | +} |
| 139 | + |
| 140 | +/// Convert an expression to integral type Target |
| 141 | +/// An invariant with fail with message "Bad conversion" if conversion |
| 142 | +/// is not possible. |
| 143 | +/// \tparam Target: type to convert to |
| 144 | +/// \param arg: constant expression |
| 145 | +/// \return value of type Target |
| 146 | +template <typename Target> |
| 147 | +Target numeric_cast_v(const exprt &arg) |
| 148 | +{ |
| 149 | + const auto maybe = numeric_castt<Target>{}(arg); |
| 150 | + INVARIANT(maybe, "Bad conversion"); |
| 151 | + return *maybe; |
| 152 | +} |
| 153 | + |
29 | 154 | // PRECONDITION(false) in case of unsupported type
|
30 | 155 | constant_exprt from_integer(const mp_integer &int_value, const typet &type);
|
31 | 156 |
|
|
0 commit comments