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

Commit af1eeaa

Browse files
author
Theodoros Theodoridis
committed
Implement TC_CHECK(_*) macros
The glog CHECK macros terminate on failure. Instead of termination the TC_CHECK macros throw exceptions. The available macros are: TC_CHECK(condition) (boolean argument) TC_CHECK_EQ(x, y) (checks if x == y) TC_CHECK_NE(x, y) (checks if x != y) TC_CHECK_LT(x, y) (checks if x < y) TC_CHECK_GT(x, y) (checks if x > y) TC_CHECK_GE(x, y) (checks if x <= y) TC_CHECK_LE(x, y) (checks if x >= y) Additional information can be passed through operator<< and is included in the exception's error message, e.g.: TC_CHECK_EQ(x, 42) << "x is not the answer"; Implementation detail: Information passed with calls to operator<< is maintained in a separate string. The reason is that a separator is printed between the base message and the additional info if it is present (e.g., 1 is not equal to 42: x is not the answer). Maintaining two strings helps determining the correct location for the separator.
1 parent 0f3f64b commit af1eeaa

File tree

6 files changed

+391
-0
lines changed

6 files changed

+391
-0
lines changed

tc/core/CMakeLists.txt

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

1212
flags.cc
13+
check.cc
1314
mapping_options.cc
1415
mapping_options_cpp_printer.cc
1516
islpp.cc

tc/core/check.cc

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
#include "tc/core/check.h"
2+
namespace tc {
3+
namespace detail {
4+
5+
Checker::Checker(bool condition, std::string location, std::string baseErrorMsg)
6+
: condition_(condition), location_(location), baseErrorMsg_(baseErrorMsg){};
7+
8+
Checker::~Checker() noexcept(false) {
9+
if (condition_) {
10+
return;
11+
}
12+
std::stringstream ss;
13+
ss << "Check failed [" << location_ << ']';
14+
15+
if (not baseErrorMsg_.empty()) {
16+
ss << ' ' << baseErrorMsg_;
17+
}
18+
19+
if (not additionalMsg_.empty()) {
20+
ss << ": " << additionalMsg_;
21+
}
22+
throw std::runtime_error(ss.str());
23+
}
24+
25+
Checker tc_check(bool condition, const char* filename, uint64_t lineno) {
26+
return Checker(condition, makeLocation(filename, lineno), {});
27+
}
28+
29+
std::string makeLocation(const char* filename, uint64_t lineno) {
30+
std::stringstream ss;
31+
ss << filename << ':' << lineno;
32+
return ss.str();
33+
}
34+
35+
} // namespace detail
36+
} // namespace tc

tc/core/check.h

Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
1+
#pragma once
2+
3+
#include <cstdint>
4+
#include <iosfwd>
5+
#include <sstream>
6+
#include <string>
7+
8+
#include "tc/core/utils/type_traits.h"
9+
10+
/*
11+
* Each TC_CHECK(_*) macro checks for a condition and throws an exception if
12+
* the condition does not hold
13+
*
14+
*
15+
*Additional information can be passed through operator<< and is
16+
*included in the exception's error message, e.g.:
17+
*TC_CHECK_EQ(x, 42) << "x is not the answer";
18+
*
19+
*
20+
* The message in the throw exception is:
21+
* Check failed [filename:line_number] error(: info)
22+
*
23+
* filename: the name of the file where TC_CHECK(_*) was used
24+
* lineno: the number of the line in which TC_CHECK(_*) was used
25+
* error: this shows what failed, (e.g, "1 is not equal to 42")
26+
* info: if operator<< was called then the information passed to it is info
27+
*
28+
*
29+
*
30+
* WARNING/CORNER CASE:
31+
*
32+
* Checker's destructor throws. This means that if another exception is thrown
33+
* before a fully constructed Checker object is destroyed then the program will
34+
* std::terminate. here is one unavoidable corner case:
35+
*
36+
* TC_CHECK(foo) << bar;
37+
*
38+
* If bar is a function/constructor call and throws then the program will
39+
* std::terminate (because when Checker's destructor runs it will throw a
40+
* second exception).
41+
*/
42+
43+
// 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+
47+
// checks if x == y
48+
#define TC_CHECK_EQ(x, y) tc::detail::tc_check_eq(x, y, __FILE__, __LINE__)
49+
// checks if x != y
50+
#define TC_CHECK_NE(x, y) tc::detail::tc_check_ne(x, y, __FILE__, __LINE__)
51+
// checks if x < y
52+
#define TC_CHECK_LT(x, y) tc::detail::tc_check_lt(x, y, __FILE__, __LINE__)
53+
// checks if x > y
54+
#define TC_CHECK_GT(x, y) tc::detail::tc_check_gt(x, y, __FILE__, __LINE__)
55+
// checks if x <= y
56+
#define TC_CHECK_LE(x, y) tc::detail::tc_check_le(x, y, __FILE__, __LINE__)
57+
// checks if x >= y
58+
#define TC_CHECK_GE(x, y) tc::detail::tc_check_ge(x, y, __FILE__, __LINE__)
59+
60+
namespace tc {
61+
62+
namespace detail {
63+
class Checker {
64+
public:
65+
Checker(bool condition, std::string location, std::string baseErrorMsg);
66+
~Checker() noexcept(false);
67+
68+
template <typename T>
69+
typename std::enable_if<!tc::is_std_container<T>::value, Checker&>::type
70+
operator<<(const T& msg) {
71+
try {
72+
std::stringstream ss;
73+
ss << additionalMsg_ << msg;
74+
additionalMsg_ = ss.str();
75+
} catch (...) {
76+
// If the above throws and we don't catch the exception then the
77+
// destructor will throw a second one and the program will terminate.
78+
}
79+
return *this;
80+
}
81+
82+
template <typename C>
83+
typename std::enable_if<tc::is_std_container<C>::value, Checker&>::type
84+
operator<<(const C& msg) {
85+
try {
86+
std::stringstream ss;
87+
ss << additionalMsg_;
88+
for (const auto& x : msg) {
89+
ss << x << ',';
90+
}
91+
additionalMsg_ = ss.str();
92+
if (msg.begin() != msg.end()) {
93+
additionalMsg_.pop_back();
94+
}
95+
} catch (...) {
96+
// If the above throws and we don't catch the exception then the
97+
// destructor will throw a second one and the program will terminate.
98+
}
99+
return *this;
100+
}
101+
102+
private:
103+
bool condition_;
104+
std::string location_;
105+
std::string baseErrorMsg_;
106+
std::string additionalMsg_;
107+
}; // namespace detail
108+
109+
std::string makeLocation(const char* filename, uint64_t lineno);
110+
111+
Checker tc_check(bool condition, const char* filename, uint64_t lineno);
112+
113+
template <typename X, typename Y>
114+
Checker
115+
tc_check_eq(const X& x, const Y& y, const char* filename, uint64_t lineno) {
116+
std::stringstream ss;
117+
ss << x << " not equal to " << y;
118+
return Checker(x == y, makeLocation(filename, lineno), ss.str());
119+
}
120+
121+
template <typename X, typename Y>
122+
Checker
123+
tc_check_ne(const X& x, const Y& y, const char* filename, uint64_t lineno) {
124+
std::stringstream ss;
125+
ss << x << " equal to " << y;
126+
return Checker(x != y, makeLocation(filename, lineno), ss.str());
127+
}
128+
129+
template <typename X, typename Y>
130+
Checker
131+
tc_check_lt(const X& x, const Y& y, const char* filename, uint64_t lineno) {
132+
std::stringstream ss;
133+
ss << x << " not less than " << y;
134+
return Checker(x < y, makeLocation(filename, lineno), ss.str());
135+
}
136+
137+
template <typename X, typename Y>
138+
Checker
139+
tc_check_gt(const X& x, const Y& y, const char* filename, uint64_t lineno) {
140+
std::stringstream ss;
141+
ss << x << " not greater than " << y;
142+
return Checker(x > y, makeLocation(filename, lineno), ss.str());
143+
}
144+
145+
template <typename X, typename Y>
146+
Checker
147+
tc_check_le(const X& x, const Y& y, const char* filename, uint64_t lineno) {
148+
std::stringstream ss;
149+
ss << x << " not less than or equal to " << y;
150+
return Checker(x <= y, makeLocation(filename, lineno), ss.str());
151+
}
152+
153+
template <typename X, typename Y>
154+
Checker
155+
tc_check_ge(const X& x, const Y& y, const char* filename, uint64_t lineno) {
156+
std::stringstream ss;
157+
ss << x << " not greater than or equal to " << y;
158+
return Checker(x >= y, makeLocation(filename, lineno), ss.str());
159+
}
160+
161+
} // namespace detail
162+
} // namespace tc

tc/core/utils/type_traits.h

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
#include <type_traits>
2+
3+
namespace tc {
4+
template <class...>
5+
using void_t = void;
6+
7+
template <typename T, typename = void>
8+
struct is_std_container : std::false_type {};
9+
10+
template <typename T>
11+
struct is_std_container<
12+
T,
13+
void_t<
14+
decltype(std::declval<T&>().begin()),
15+
decltype(std::declval<T&>().end()),
16+
typename T::value_type>> : std::true_type {};
17+
} // namespace tc

test/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ set(CORE_TEST_FILES
2929
test_core
3030
test_inference
3131
test_tc2halide
32+
test_check
3233
)
3334

3435
foreach(i ${CORE_TEST_FILES})

0 commit comments

Comments
 (0)