Skip to content
This repository was archived by the owner on Apr 28, 2023. It is now read-only.

Commit 3f4bef4

Browse files
author
Theodoros Theodoridis
committed
Implement TC_CHECKs with custom exception types
The type of the exception thrown on failed checks can now be specified. E.g., TC_CHECK(i, v.size(), std::out_of_range)
1 parent f526d22 commit 3f4bef4

File tree

4 files changed

+169
-71
lines changed

4 files changed

+169
-71
lines changed

tc/core/CMakeLists.txt

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ add_library(
1010
SHARED
1111

1212
flags.cc
13-
check.cc
1413
mapping_options.cc
1514
mapping_options_cpp_printer.cc
1615
islpp.cc

tc/core/check.cc

Lines changed: 0 additions & 39 deletions
This file was deleted.

tc/core/check.h

Lines changed: 132 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
#include <cstdint>
44
#include <iosfwd>
55
#include <sstream>
6+
#include <stdexcept>
67
#include <string>
78

89
#include "tc/core/utils/type_traits.h"
@@ -38,32 +39,118 @@
3839
* If bar is a function/constructor call and throws then the program will
3940
* std::terminate (because when Checker's destructor runs it will throw a
4041
* 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
4158
*/
4259

4360
// 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__)
4764
// 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__)
4967
// 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__)
5170
// 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__)
5373
// 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__)
5576
// 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__)
5779
// 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__)
59127

60128
namespace tc {
61129

62130
namespace detail {
131+
template <typename ExceptionType>
63132
class Checker {
64133
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+
}
67154

68155
template <typename T>
69156
typename std::enable_if<!tc::is_std_container<T>::value, Checker&>::type
@@ -106,56 +193,70 @@ class Checker {
106193
std::string additionalMsg_;
107194
}; // namespace detail
108195

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+
}
110201

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+
}
112207

113-
template <typename X, typename Y>
114-
Checker
208+
template <typename ExceptionType, typename X, typename Y>
209+
Checker<ExceptionType>
115210
tc_check_eq(const X& x, const Y& y, const char* filename, uint64_t lineno) {
116211
std::stringstream ss;
117212
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());
119215
}
120216

121-
template <typename X, typename Y>
122-
Checker
217+
template <typename ExceptionType, typename X, typename Y>
218+
Checker<ExceptionType>
123219
tc_check_ne(const X& x, const Y& y, const char* filename, uint64_t lineno) {
124220
std::stringstream ss;
125221
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());
127224
}
128225

129-
template <typename X, typename Y>
130-
Checker
226+
template <typename ExceptionType, typename X, typename Y>
227+
Checker<ExceptionType>
131228
tc_check_lt(const X& x, const Y& y, const char* filename, uint64_t lineno) {
132229
std::stringstream ss;
133230
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());
135233
}
136234

137-
template <typename X, typename Y>
138-
Checker
235+
template <typename ExceptionType, typename X, typename Y>
236+
Checker<ExceptionType>
139237
tc_check_gt(const X& x, const Y& y, const char* filename, uint64_t lineno) {
140238
std::stringstream ss;
141239
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());
143242
}
144243

145-
template <typename X, typename Y>
146-
Checker
244+
template <typename ExceptionType, typename X, typename Y>
245+
Checker<ExceptionType>
147246
tc_check_le(const X& x, const Y& y, const char* filename, uint64_t lineno) {
148247
std::stringstream ss;
149248
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());
151251
}
152252

153-
template <typename X, typename Y>
154-
Checker
253+
template <typename ExceptionType, typename X, typename Y>
254+
Checker<ExceptionType>
155255
tc_check_ge(const X& x, const Y& y, const char* filename, uint64_t lineno) {
156256
std::stringstream ss;
157257
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());
159260
}
160261

161262
} // namespace detail

test/test_check.cc

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,43 @@ TEST(CHECK, GE) {
166166
}
167167
}
168168

169+
TEST(CHECK, CustomException) {
170+
ASSERT_THROW(TC_CHECK(false, std::out_of_range), std::out_of_range);
171+
ASSERT_THROW(TC_CHECK(false, std::out_of_range) << "aa", std::out_of_range);
172+
ASSERT_NO_THROW(TC_CHECK(true, std::out_of_range));
173+
ASSERT_NO_THROW(TC_CHECK(true, std::out_of_range) << "aa");
174+
175+
ASSERT_THROW(TC_CHECK_EQ(1, 2, std::out_of_range), std::out_of_range);
176+
ASSERT_THROW(TC_CHECK_EQ(1, 2, std::out_of_range) << "aa", std::out_of_range);
177+
ASSERT_NO_THROW(TC_CHECK_EQ(1, 1, std::out_of_range));
178+
ASSERT_NO_THROW(TC_CHECK_EQ(1, 1, std::out_of_range) << "aa");
179+
180+
ASSERT_THROW(TC_CHECK_NE(1, 1, std::out_of_range), std::out_of_range);
181+
ASSERT_THROW(TC_CHECK_NE(1, 1, std::out_of_range) << "aa", std::out_of_range);
182+
ASSERT_NO_THROW(TC_CHECK_NE(1, 2, std::out_of_range));
183+
ASSERT_NO_THROW(TC_CHECK_NE(1, 2, std::out_of_range) << "aa");
184+
185+
ASSERT_THROW(TC_CHECK_LT(1, 1, std::out_of_range), std::out_of_range);
186+
ASSERT_THROW(TC_CHECK_LT(1, 1, std::out_of_range) << "aa", std::out_of_range);
187+
ASSERT_NO_THROW(TC_CHECK_LT(1, 2, std::out_of_range));
188+
ASSERT_NO_THROW(TC_CHECK_LT(1, 2, std::out_of_range) << "aa");
189+
190+
ASSERT_THROW(TC_CHECK_GT(1, 1, std::out_of_range), std::out_of_range);
191+
ASSERT_THROW(TC_CHECK_GT(1, 1, std::out_of_range) << "aa", std::out_of_range);
192+
ASSERT_NO_THROW(TC_CHECK_GT(2, 1, std::out_of_range));
193+
ASSERT_NO_THROW(TC_CHECK_GT(2, 1, std::out_of_range) << "aa");
194+
195+
ASSERT_THROW(TC_CHECK_LE(2, 1, std::out_of_range), std::out_of_range);
196+
ASSERT_THROW(TC_CHECK_LE(2, 1, std::out_of_range) << "aa", std::out_of_range);
197+
ASSERT_NO_THROW(TC_CHECK_LE(1, 2, std::out_of_range));
198+
ASSERT_NO_THROW(TC_CHECK_LE(1, 2, std::out_of_range) << "aa");
199+
200+
ASSERT_THROW(TC_CHECK_GE(1, 2, std::out_of_range), std::out_of_range);
201+
ASSERT_THROW(TC_CHECK_GE(1, 2, std::out_of_range) << "aa", std::out_of_range);
202+
ASSERT_NO_THROW(TC_CHECK_GE(2, 1, std::out_of_range));
203+
ASSERT_NO_THROW(TC_CHECK_GE(2, 1, std::out_of_range) << "aa");
204+
}
205+
169206
int main(int argc, char** argv) {
170207
::testing::InitGoogleTest(&argc, argv);
171208
return RUN_ALL_TESTS();

0 commit comments

Comments
 (0)