ZeroErr
Loading...
Searching...
No Matches
decomposition.h
Go to the documentation of this file.
1#pragma once
2
4
5#include "zeroerr/dbg.h"
6#include "zeroerr/print.h"
7
10
11#ifndef ZEROERR_DISABLE_COMPLEX_AND_OR
20#define AND &&zeroerr::ExpressionDecomposer() <<
21#define OR || zeroerr::ExpressionDecomposer() <<
22#endif
23
24namespace zeroerr {
25
26
27// SFINAE helper used to check L op R is supported, but the result type is `ret`
28#define ZEROERR_SFINAE_OP(ret, op) \
29 typename std::decay<decltype(std::declval<L>() op std::declval<R>(), std::declval<ret>())>::type
30
31template <typename T>
33 static const bool value = false;
34};
35
36#define ZEROERR_EXPRESSION_COMPARISON(op, op_name) \
37 template <typename R> \
38 ZEROERR_DIAGNOSTIC_NOINLINE ZEROERR_SFINAE_OP(Expression<R>, op) operator op(R&& rhs) { \
39 zeroerr::String text = \
40 detail::comparisonText(decomp, detail::makeDiagnosticValue(lhs), " " #op " ", \
41 detail::makeDiagnosticValue(rhs), res); \
42 return Expression<R>(static_cast<R&&>(rhs), res && (lhs op rhs), std::move(text)); \
43 } \
44 template <typename R, \
45 typename std::enable_if<!std::is_rvalue_reference<R>::value, void>::type* = nullptr> \
46 ZEROERR_DIAGNOSTIC_NOINLINE ZEROERR_SFINAE_OP(Expression<const R&>, op) operator op( \
47 const R& rhs) { \
48 zeroerr::String text = \
49 detail::comparisonText(decomp, detail::makeDiagnosticValue(lhs), " " #op " ", \
50 detail::makeDiagnosticValue(rhs), res); \
51 return Expression<const R&>(rhs, res && (lhs op rhs), std::move(text)); \
52 }
53
54
55#define ZEROERR_EXPRESSION_ANDOR(op, op_name) \
56 ExprResult operator op(ExprResult rhs) { \
57 return ExprResult(res op rhs.res, \
58 detail::joinDiagnostic(decomp, " " #op " ", rhs.decomp)); \
59 }
60
61
62#define ZEROERR_FORBIT_EXPRESSION(rt, op) \
63 template <typename R> \
64 rt& operator op(const R&) { \
65 static_assert(deferred_false<R>::value, \
66 "Please Rewrite Expression As Binary Comparison!"); \
67 return *this; \
68 }
69
99
100namespace detail {
101
102// Bind operands for one full expression without copying or re-evaluating them.
103// Unlike Expression, a comparison does not first convert its left operand to bool.
104template <class L>
106 const L& lhs;
108
109#define ZEROERR_DIRECT_COMPARISON(op) \
110 template <class R> \
111 ExprResult operator op(const R& rhs) const { \
112 return {lhs op rhs, printer(lhs, #op, rhs)}; \
113 }
120#undef ZEROERR_DIRECT_COMPARISON
121};
122
123template <class L>
125 return {lhs, printer};
126}
127
128} // namespace detail
129
130namespace details {
131// Detect whether a type can be explicitly converted to bool. This covers
132// explicit operator bool (std::unique_ptr/std::shared_ptr), which
133// std::is_convertible would miss. Scoped enums are excluded so that a bare
134// CHECK(enum) does not silently turn into "value != 0"; unscoped enums keep
135// their historical implicit-conversion behavior.
136template <typename T, typename = void>
137struct has_bool_conversion : std::false_type {};
138
139template <typename T>
140struct has_bool_conversion<T, decltype(static_cast<bool>(std::declval<T>()), void())>
141 : std::integral_constant<
142 bool, !(std::is_enum<typename std::decay<T>::type>::value &&
143 !std::is_convertible<typename std::decay<T>::type, int>::value)> {};
144
145template <typename T>
146typename std::enable_if<has_bool_conversion<T>::value, bool>::type getBool(T&& lhs) {
147 return static_cast<bool>(lhs);
148}
149
150template <typename T>
151typename std::enable_if<!has_bool_conversion<T>::value, bool>::type getBool(T&&) {
152 return true;
153}
154} // namespace details
155
156template <typename L>
159 bool res = true;
161
162 explicit Expression(L&& in) : lhs(static_cast<L&&>(in)) { res = details::getBool(lhs); }
163 explicit Expression(L&& in, bool res, zeroerr::String&& decomp)
164 : lhs(static_cast<L&&>(in)), res(res), decomp(static_cast<zeroerr::String&&>(decomp)) {}
165
166 operator ExprResult() {
167 if (decomp.empty()) {
169 }
170 return ExprResult(res, decomp);
171 }
172
173 operator L() const { return lhs; }
174
181
184
201};
202
203#undef ZEROERR_EXPRESSION_COMPARISON
204#undef ZEROERR_EXPRESSION_ANDOR
205#undef ZEROERR_FORBIT_EXPRESSION
206
208 // The right operator for capturing expressions is "<=" instead of "<<" (based on the
209 // operator precedence table) but then there will be warnings from GCC about "-Wparentheses"
210 // and since "_Pragma()" is problematic this will stay for now...
211 // https://github.com/catchorg/Catch2/issues/870
212 // https://github.com/catchorg/Catch2/issues/565
213
214 // For temporary objects, we need to use rvalue reference to avoid copy
215 template <typename L>
216 Expression<L> operator<<(L&& operand) {
217 return Expression<L>(static_cast<L&&>(operand));
218 }
219
220 // For other objects, we will store the reference
221 template <typename L,
222 typename std::enable_if<!std::is_rvalue_reference<L>::value, void>::type* = nullptr>
223 Expression<const L&> operator<<(const L& operand) {
224 return Expression<const L&>(operand);
225 }
226};
227
228
229template <typename T>
230class IMatcher {
231public:
232 virtual ~IMatcher() = default;
233
234
235 virtual bool match(const T&) const = 0;
236};
237
238template <typename T>
240public:
241 IMatcherRef(const IMatcher<T>* ptr) : p(ptr) {}
242 IMatcherRef(const IMatcherRef&) = delete;
243
245 p = std::move(other.p);
246 other.p = nullptr;
247 }
248 void operator=(IMatcherRef&& other) {
249 p = std::move(other.p);
250 other.p = nullptr;
251 }
254 if (p) delete p;
255 }
256
257 IMatcherRef operator&&(IMatcherRef&& other);
258 IMatcherRef operator||(IMatcherRef&& other);
259 IMatcherRef operator!();
260
261 const IMatcher<T>* operator->() const { return p; }
262
263protected:
264 const IMatcher<T>* p = nullptr;
265};
266
267
268template <typename T>
269class CombinedMatcher : public IMatcher<T> {
270public:
273
276 bool is_and;
277
278 virtual bool match(const T& t) const override {
279 if (is_and) {
280 return lhs->match(t) && rhs->match(t);
281 } else {
282 return lhs->match(t) || rhs->match(t);
283 }
284 }
285};
286
287template <typename T>
288class NotMatcher : public IMatcher<T> {
289public:
292
293 virtual bool match(const T& t) const override { return !matcher->match(t); }
294};
295
296template <typename T>
298 return new CombinedMatcher<T>(std::move(*this), std::move(other), true);
299}
300
301template <typename T>
303 return new CombinedMatcher<T>(std::move(*this), std::move(other), false);
304}
305
306template <typename T>
308 return new NotMatcher<T>(std::move(*this));
309}
310
311
312template <typename T>
313struct StartWithMatcher : public IMatcher<T> {
314 StartWithMatcher(const T& s) : start(s) {}
316
317 virtual bool match(const T& t) const override {
318 bool result = true;
319 for (auto i = start.begin(), j = t.begin(); i != start.end(); ++i, ++j) {
320 if (j == t.end() || *i != *j) {
321 result = false;
322 break;
323 }
324 }
325 return result;
326 }
327};
328
329template <typename T>
330typename std::enable_if<std::is_constructible<zeroerr::String, T>::value,
331 IMatcherRef<zeroerr::String>>::type
335
336} // namespace zeroerr
337
Definition decomposition.h:269
IMatcherRef< T > lhs
Definition decomposition.h:274
IMatcherRef< T > rhs
Definition decomposition.h:275
virtual bool match(const T &t) const override
Definition decomposition.h:278
bool is_and
Definition decomposition.h:276
CombinedMatcher(IMatcherRef< T > &&lhs, IMatcherRef< T > &&rhs, bool is_and)
Definition decomposition.h:271
Definition decomposition.h:239
IMatcherRef(IMatcherRef &&other)
Definition decomposition.h:244
IMatcherRef operator||(IMatcherRef &&other)
Definition decomposition.h:302
IMatcherRef(const IMatcher< T > *ptr)
Definition decomposition.h:241
void operator=(IMatcherRef &&other)
Definition decomposition.h:248
IMatcherRef(const IMatcherRef &)=delete
const IMatcher< T > * p
Definition decomposition.h:264
IMatcherRef operator&&(IMatcherRef &&other)
Definition decomposition.h:297
IMatcherRef & operator=(const IMatcherRef &)=delete
const IMatcher< T > * operator->() const
Definition decomposition.h:261
IMatcherRef operator!()
Definition decomposition.h:307
~IMatcherRef()
Definition decomposition.h:253
Definition decomposition.h:230
virtual bool match(const T &) const =0
virtual ~IMatcher()=default
Definition decomposition.h:288
IMatcherRef< T > matcher
Definition decomposition.h:291
NotMatcher(IMatcherRef< T > &&matcher)
Definition decomposition.h:290
virtual bool match(const T &t) const override
Definition decomposition.h:293
Definition string.h:41
#define ZEROERR_SUPPRESS_COMPARE
Definition config.h:318
#define ZEROERR_SUPPRESS_COMMON_WARNINGS_POP
Definition config.h:283
#define ZEROERR_SUPPRESS_COMPARE_POP
Definition config.h:331
#define ZEROERR_SUPPRESS_COMMON_WARNINGS_PUSH
Definition config.h:235
#define ZEROERR_FORBIT_EXPRESSION(rt, op)
Definition decomposition.h:62
#define ZEROERR_EXPRESSION_ANDOR(op, op_name)
Definition decomposition.h:55
#define ZEROERR_EXPRESSION_COMPARISON(op, op_name)
Definition decomposition.h:36
#define ZEROERR_DIRECT_COMPARISON(op)
Definition decomposition.h:109
STL namespace.
Definition assert_text.cpp:6
zeroerr::String valueText(DiagnosticValue value)
Definition text.cpp:63
ComparisonLhs< L > makeComparisonLhs(const L &lhs, Printer &printer)
Definition decomposition.h:124
DiagnosticValue makeDiagnosticValue(T &value)
Definition print.h:364
Definition decomposition.h:130
std::enable_if< has_bool_conversion< T >::value, bool >::type getBool(T &&lhs)
Definition decomposition.h:146
Definition assert_text.cpp:5
std::enable_if< std::is_constructible< zeroerr::String, T >::value, IMatcherRef< zeroerr::String > >::type start_with(T &&s)
Definition decomposition.h:332
Definition decomposition.h:70
bool res
Definition decomposition.h:71
zeroerr::String decomp
Definition decomposition.h:72
ExprResult(bool res, zeroerr::String decomposition="")
Definition decomposition.h:74
Definition decomposition.h:207
Expression< L > operator<<(L &&operand)
Definition decomposition.h:216
Definition decomposition.h:157
bool res
Definition decomposition.h:159
zeroerr::String decomp
Definition decomposition.h:160
L lhs
Definition decomposition.h:158
Expression(L &&in)
Definition decomposition.h:162
Expression(L &&in, bool res, zeroerr::String &&decomp)
Definition decomposition.h:163
A functor class Printer for printing a value of any type.
Definition print.h:87
Definition decomposition.h:313
StartWithMatcher(const T &s)
Definition decomposition.h:314
virtual bool match(const T &t) const override
Definition decomposition.h:317
T start
Definition decomposition.h:315
Definition decomposition.h:32
static const bool value
Definition decomposition.h:33
Definition decomposition.h:105
Printer & printer
Definition decomposition.h:107
const L & lhs
Definition decomposition.h:106
Definition decomposition.h:137