|
3 | 3 | #include <cstdint> |
4 | 4 | #include <iosfwd> |
5 | 5 | #include <sstream> |
| 6 | +#include <stdexcept> |
6 | 7 | #include <string> |
7 | 8 |
|
8 | 9 | #include "tc/core/utils/type_traits.h" |
|
38 | 39 | * If bar is a function/constructor call and throws then the program will |
39 | 40 | * std::terminate (because when Checker's destructor runs it will throw a |
40 | 41 | * second exception). |
| 42 | + * |
| 43 | + * |
| 44 | + * Exception type: |
| 45 | + * The default exception type is std::runtime_error, a different type can be |
| 46 | + * specified by passing an extra argument, e.g.: |
| 47 | + * TC_CHECK(i, whatever.size(), std::out_of_range); |
| 48 | + * |
| 49 | + * |
| 50 | + * List of check macros: |
| 51 | + * TC_CHECK(condition) //checks if condition is true |
| 52 | + * TC_CHECK_EQ(x,y) //checks if x == y |
| 53 | + * TC_CHECK_NE(x,y) //checks if x != y |
| 54 | + * TC_CHECK_LT(x,y) //checks if x < y |
| 55 | + * TC_CHECK_GT(x,y) //checks if x > y |
| 56 | + * TC_CHECK_LE(x,y) //checks if x <= y |
| 57 | + * TC_CHECK_GE(x,y) //checks if x >= y |
41 | 58 | */ |
42 | 59 |
|
43 | 60 | // condition should either be a bool or convertible to bool |
44 | | -#define TC_CHECK(condition) \ |
45 | | - tc::detail::tc_check(static_cast<bool>(condition), __FILE__, __LINE__) |
46 | | - |
| 61 | +#define TC_CHECK_IMPL(condition, exception_type) \ |
| 62 | + tc::detail::tc_check<exception_type>( \ |
| 63 | + static_cast<bool>(condition), __FILE__, __LINE__) |
47 | 64 | // checks if x == y |
48 | | -#define TC_CHECK_EQ(x, y) tc::detail::tc_check_eq(x, y, __FILE__, __LINE__) |
| 65 | +#define TC_CHECK_EQ_IMPL(x, y, exception_type) \ |
| 66 | + tc::detail::tc_check_eq<exception_type>(x, y, __FILE__, __LINE__) |
49 | 67 | // checks if x != y |
50 | | -#define TC_CHECK_NE(x, y) tc::detail::tc_check_ne(x, y, __FILE__, __LINE__) |
| 68 | +#define TC_CHECK_NE_IMPL(x, y, exception_type) \ |
| 69 | + tc::detail::tc_check_ne<exception_type>(x, y, __FILE__, __LINE__) |
51 | 70 | // checks if x < y |
52 | | -#define TC_CHECK_LT(x, y) tc::detail::tc_check_lt(x, y, __FILE__, __LINE__) |
| 71 | +#define TC_CHECK_LT_IMPL(x, y, exception_type) \ |
| 72 | + tc::detail::tc_check_lt<exception_type>(x, y, __FILE__, __LINE__) |
53 | 73 | // checks if x > y |
54 | | -#define TC_CHECK_GT(x, y) tc::detail::tc_check_gt(x, y, __FILE__, __LINE__) |
| 74 | +#define TC_CHECK_GT_IMPL(x, y, exception_type) \ |
| 75 | + tc::detail::tc_check_gt<exception_type>(x, y, __FILE__, __LINE__) |
55 | 76 | // checks if x <= y |
56 | | -#define TC_CHECK_LE(x, y) tc::detail::tc_check_le(x, y, __FILE__, __LINE__) |
| 77 | +#define TC_CHECK_LE_IMPL(x, y, exception_type) \ |
| 78 | + tc::detail::tc_check_le<exception_type>(x, y, __FILE__, __LINE__) |
57 | 79 | // checks if x >= y |
58 | | -#define TC_CHECK_GE(x, y) tc::detail::tc_check_ge(x, y, __FILE__, __LINE__) |
| 80 | +#define TC_CHECK_GE_IMPL(x, y, exception_type) \ |
| 81 | + tc::detail::tc_check_ge<exception_type>(x, y, __FILE__, __LINE__) |
| 82 | + |
| 83 | +#define TC_CHECK_DEFAULT(condition) TC_CHECK_IMPL(condition, std::runtime_error) |
| 84 | +#define TC_CHECK_EQ_DEFAULT(x, y, ...) \ |
| 85 | + TC_CHECK_EQ_IMPL(x, y, std::runtime_error) |
| 86 | +#define TC_CHECK_NE_DEFAULT(x, y, ...) \ |
| 87 | + TC_CHECK_NE_IMPL(x, y, std::runtime_error) |
| 88 | +#define TC_CHECK_LT_DEFAULT(x, y, ...) \ |
| 89 | + TC_CHECK_LT_IMPL(x, y, std::runtime_error) |
| 90 | +#define TC_CHECK_GT_DEFAULT(x, y, ...) \ |
| 91 | + TC_CHECK_GT_IMPL(x, y, std::runtime_error) |
| 92 | +#define TC_CHECK_LE_DEFAULT(x, y, ...) \ |
| 93 | + TC_CHECK_LE_IMPL(x, y, std::runtime_error) |
| 94 | +#define TC_CHECK_GE_DEFAULT(x, y, ...) \ |
| 95 | + TC_CHECK_GE_IMPL(x, y, std::runtime_error) |
| 96 | + |
| 97 | +#define TC_GET_MACRO12(_1, _2, NAME, ...) NAME |
| 98 | +#define TC_GET_MACRO23(_1, _2, _3, NAME, ...) NAME |
| 99 | + |
| 100 | +#define TC_CHECK(...) \ |
| 101 | + TC_GET_MACRO12(__VA_ARGS__, TC_CHECK_IMPL, TC_CHECK_DEFAULT) \ |
| 102 | + (__VA_ARGS__) |
| 103 | + |
| 104 | +#define TC_CHECK_EQ(...) \ |
| 105 | + TC_GET_MACRO23(__VA_ARGS__, TC_CHECK_EQ_IMPL, TC_CHECK_EQ_DEFAULT) \ |
| 106 | + (__VA_ARGS__) |
| 107 | + |
| 108 | +#define TC_CHECK_NE(...) \ |
| 109 | + TC_GET_MACRO23(__VA_ARGS__, TC_CHECK_NE_IMPL, TC_CHECK_NE_DEFAULT) \ |
| 110 | + (__VA_ARGS__) |
| 111 | + |
| 112 | +#define TC_CHECK_LT(...) \ |
| 113 | + TC_GET_MACRO23(__VA_ARGS__, TC_CHECK_LT_IMPL, TC_CHECK_LT_DEFAULT) \ |
| 114 | + (__VA_ARGS__) |
| 115 | + |
| 116 | +#define TC_CHECK_GT(...) \ |
| 117 | + TC_GET_MACRO23(__VA_ARGS__, TC_CHECK_GT_IMPL, TC_CHECK_GT_DEFAULT) \ |
| 118 | + (__VA_ARGS__) |
| 119 | + |
| 120 | +#define TC_CHECK_LE(...) \ |
| 121 | + TC_GET_MACRO23(__VA_ARGS__, TC_CHECK_LE_IMPL, TC_CHECK_LE_DEFAULT) \ |
| 122 | + (__VA_ARGS__) |
| 123 | + |
| 124 | +#define TC_CHECK_GE(...) \ |
| 125 | + TC_GET_MACRO23(__VA_ARGS__, TC_CHECK_GE_IMPL, TC_CHECK_GE_DEFAULT) \ |
| 126 | + (__VA_ARGS__) |
59 | 127 |
|
60 | 128 | namespace tc { |
61 | 129 |
|
62 | 130 | namespace detail { |
| 131 | +template <typename ExceptionType> |
63 | 132 | class Checker { |
64 | 133 | public: |
65 | | - Checker(bool condition, std::string location, std::string baseErrorMsg); |
66 | | - ~Checker() noexcept(false); |
| 134 | + Checker(bool condition, std::string location, std::string baseErrorMsg) |
| 135 | + : condition_(condition), |
| 136 | + location_(location), |
| 137 | + baseErrorMsg_(baseErrorMsg){}; |
| 138 | + ~Checker() noexcept(false) { |
| 139 | + if (condition_) { |
| 140 | + return; |
| 141 | + } |
| 142 | + std::stringstream ss; |
| 143 | + ss << "Check failed [" << location_ << ']'; |
| 144 | + |
| 145 | + if (not baseErrorMsg_.empty()) { |
| 146 | + ss << ' ' << baseErrorMsg_; |
| 147 | + } |
| 148 | + |
| 149 | + if (not additionalMsg_.empty()) { |
| 150 | + ss << ": " << additionalMsg_; |
| 151 | + } |
| 152 | + throw ExceptionType(ss.str()); |
| 153 | + } |
67 | 154 |
|
68 | 155 | template <typename T> |
69 | 156 | typename std::enable_if<!tc::is_std_container<T>::value, Checker&>::type |
@@ -106,56 +193,70 @@ class Checker { |
106 | 193 | std::string additionalMsg_; |
107 | 194 | }; // namespace detail |
108 | 195 |
|
109 | | -std::string makeLocation(const char* filename, uint64_t lineno); |
| 196 | +inline std::string makeLocation(const char* filename, uint64_t lineno) { |
| 197 | + std::stringstream ss; |
| 198 | + ss << filename << ':' << lineno; |
| 199 | + return ss.str(); |
| 200 | +} |
110 | 201 |
|
111 | | -Checker tc_check(bool condition, const char* filename, uint64_t lineno); |
| 202 | +template <typename ExceptionType> |
| 203 | +Checker<ExceptionType> |
| 204 | +tc_check(bool condition, const char* filename, uint64_t lineno) { |
| 205 | + return Checker<ExceptionType>(condition, makeLocation(filename, lineno), {}); |
| 206 | +} |
112 | 207 |
|
113 | | -template <typename X, typename Y> |
114 | | -Checker |
| 208 | +template <typename ExceptionType, typename X, typename Y> |
| 209 | +Checker<ExceptionType> |
115 | 210 | tc_check_eq(const X& x, const Y& y, const char* filename, uint64_t lineno) { |
116 | 211 | std::stringstream ss; |
117 | 212 | ss << x << " not equal to " << y; |
118 | | - return Checker(x == y, makeLocation(filename, lineno), ss.str()); |
| 213 | + return Checker<ExceptionType>( |
| 214 | + x == y, makeLocation(filename, lineno), ss.str()); |
119 | 215 | } |
120 | 216 |
|
121 | | -template <typename X, typename Y> |
122 | | -Checker |
| 217 | +template <typename ExceptionType, typename X, typename Y> |
| 218 | +Checker<ExceptionType> |
123 | 219 | tc_check_ne(const X& x, const Y& y, const char* filename, uint64_t lineno) { |
124 | 220 | std::stringstream ss; |
125 | 221 | ss << x << " equal to " << y; |
126 | | - return Checker(x != y, makeLocation(filename, lineno), ss.str()); |
| 222 | + return Checker<ExceptionType>( |
| 223 | + x != y, makeLocation(filename, lineno), ss.str()); |
127 | 224 | } |
128 | 225 |
|
129 | | -template <typename X, typename Y> |
130 | | -Checker |
| 226 | +template <typename ExceptionType, typename X, typename Y> |
| 227 | +Checker<ExceptionType> |
131 | 228 | tc_check_lt(const X& x, const Y& y, const char* filename, uint64_t lineno) { |
132 | 229 | std::stringstream ss; |
133 | 230 | ss << x << " not less than " << y; |
134 | | - return Checker(x < y, makeLocation(filename, lineno), ss.str()); |
| 231 | + return Checker<ExceptionType>( |
| 232 | + x < y, makeLocation(filename, lineno), ss.str()); |
135 | 233 | } |
136 | 234 |
|
137 | | -template <typename X, typename Y> |
138 | | -Checker |
| 235 | +template <typename ExceptionType, typename X, typename Y> |
| 236 | +Checker<ExceptionType> |
139 | 237 | tc_check_gt(const X& x, const Y& y, const char* filename, uint64_t lineno) { |
140 | 238 | std::stringstream ss; |
141 | 239 | ss << x << " not greater than " << y; |
142 | | - return Checker(x > y, makeLocation(filename, lineno), ss.str()); |
| 240 | + return Checker<ExceptionType>( |
| 241 | + x > y, makeLocation(filename, lineno), ss.str()); |
143 | 242 | } |
144 | 243 |
|
145 | | -template <typename X, typename Y> |
146 | | -Checker |
| 244 | +template <typename ExceptionType, typename X, typename Y> |
| 245 | +Checker<ExceptionType> |
147 | 246 | tc_check_le(const X& x, const Y& y, const char* filename, uint64_t lineno) { |
148 | 247 | std::stringstream ss; |
149 | 248 | ss << x << " not less than or equal to " << y; |
150 | | - return Checker(x <= y, makeLocation(filename, lineno), ss.str()); |
| 249 | + return Checker<ExceptionType>( |
| 250 | + x <= y, makeLocation(filename, lineno), ss.str()); |
151 | 251 | } |
152 | 252 |
|
153 | | -template <typename X, typename Y> |
154 | | -Checker |
| 253 | +template <typename ExceptionType, typename X, typename Y> |
| 254 | +Checker<ExceptionType> |
155 | 255 | tc_check_ge(const X& x, const Y& y, const char* filename, uint64_t lineno) { |
156 | 256 | std::stringstream ss; |
157 | 257 | ss << x << " not greater than or equal to " << y; |
158 | | - return Checker(x >= y, makeLocation(filename, lineno), ss.str()); |
| 258 | + return Checker<ExceptionType>( |
| 259 | + x >= y, makeLocation(filename, lineno), ss.str()); |
159 | 260 | } |
160 | 261 |
|
161 | 262 | } // namespace detail |
|
0 commit comments