|
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