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

Commit 5d7cb96

Browse files
Theodoros TheodoridisSven Verdoolaege
authored andcommitted
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 5d7cb96

File tree

6 files changed

+400
-0
lines changed

6 files changed

+400
-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: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
#include "tc/core/check.h"
2+
3+
#include <stdexcept>
4+
5+
namespace tc {
6+
namespace detail {
7+
8+
Checker::Checker(bool condition, std::string location, std::string baseErrorMsg)
9+
: condition_(condition), location_(location), baseErrorMsg_(baseErrorMsg){};
10+
11+
Checker::~Checker() noexcept(false) {
12+
if (condition_) {
13+
return;
14+
}
15+
std::stringstream ss;
16+
ss << "Check failed [" << location_ << ']';
17+
18+
if (not baseErrorMsg_.empty()) {
19+
ss << ' ' << baseErrorMsg_;
20+
}
21+
22+
if (not additionalMsg_.empty()) {
23+
ss << ": " << additionalMsg_;
24+
}
25+
throw std::runtime_error(ss.str());
26+
}
27+
28+
Checker tc_check(bool condition, const char* filename, uint64_t lineno) {
29+
return Checker(condition, makeLocation(filename, lineno), {});
30+
}
31+
32+
std::string makeLocation(const char* filename, uint64_t lineno) {
33+
std::stringstream ss;
34+
ss << filename << ':' << lineno;
35+
return ss.str();
36+
}
37+
38+
} // namespace detail
39+
} // 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: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
#include <type_traits>
2+
3+
namespace tc {
4+
// WG21 N3911 2.3 Implementation workaround
5+
// (Make sure template argument is always used.)
6+
template <class...>
7+
struct voider {
8+
using type = void;
9+
};
10+
template <class... T0toN>
11+
using void_t = typename voider<T0toN...>::type;
12+
13+
template <typename T, typename = void>
14+
struct is_std_container : std::false_type {};
15+
16+
template <typename T>
17+
struct is_std_container<
18+
T,
19+
void_t<
20+
decltype(std::declval<T&>().begin()),
21+
decltype(std::declval<T&>().end()),
22+
typename T::value_type>> : std::true_type {};
23+
} // 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)