ZeroErr
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 
24 namespace 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 
31 template <typename T>
33  static const bool value = false;
34 };
35 
36 #define ZEROERR_EXPRESSION_COMPARISON(op, op_name) \
37  template <typename R> \
38  ZEROERR_SFINAE_OP(Expression<R>, op) \
39  operator op(R && rhs) { \
40  std::stringstream ss; \
41  Printer print(ss); \
42  print.isCompact = true; \
43  print.line_break = ""; \
44  if (decomp.empty()) { \
45  print(lhs); \
46  res = true; \
47  } else \
48  ss << decomp; \
49  ss << " " #op " "; \
50  print(rhs); \
51  return Expression<R>(static_cast<R&&>(rhs), res && (lhs op rhs), ss.str()); \
52  } \
53  template <typename R, \
54  typename std::enable_if<!std::is_rvalue_reference<R>::value, void>::type* = nullptr> \
55  ZEROERR_SFINAE_OP(Expression<const R&>, op) \
56  operator op(const R & rhs) { \
57  std::stringstream ss; \
58  Printer print(ss); \
59  print.isCompact = true; \
60  print.line_break = ""; \
61  if (decomp.empty()) { \
62  print(lhs); \
63  res = true; \
64  } else \
65  ss << decomp; \
66  ss << " " #op " "; \
67  print(rhs); \
68  return Expression<const R&>(rhs, res && (lhs op rhs), ss.str()); \
69  }
70 
71 #define ZEROERR_EXPRESSION_ANDOR(op, op_name) \
72  ExprResult operator op(ExprResult rhs) { \
73  std::stringstream ss; \
74  ss << decomp << " " #op " " << rhs.decomp; \
75  return ExprResult(res op rhs.res, ss.str()); \
76  }
77 
78 
79 #define ZEROERR_FORBIT_EXPRESSION(rt, op) \
80  template <typename R> \
81  rt& operator op(const R&) { \
82  static_assert(deferred_false<R>::value, \
83  "Please Rewrite Expression As Binary Comparison!"); \
84  return *this; \
85  }
86 
87 struct ExprResult {
88  bool res;
89  std::string decomp;
90 
91  ExprResult(bool res, std::string decomposition = "") : res(res), decomp(decomposition) {}
92 
95 
115 };
116 
117 namespace details {
118 template <typename T>
119 typename std::enable_if<std::is_convertible<T, bool>::value, bool>::type getBool(T&& lhs) {
120  return static_cast<bool>(lhs);
121 }
122 
123 template <typename T>
124 typename std::enable_if<!std::is_convertible<T, bool>::value, bool>::type getBool(T&&) {
125  return true;
126 }
127 } // namespace details
128 
129 template <typename L>
130 struct Expression {
131  L lhs;
132  bool res = true;
133  std::string decomp;
134 
135  explicit Expression(L&& in) : lhs(static_cast<L&&>(in)) { res = details::getBool(lhs); }
136  explicit Expression(L&& in, bool res, std::string&& decomp)
137  : lhs(static_cast<L&&>(in)), res(res), decomp(static_cast<std::string&&>(decomp)) {}
138 
139  operator ExprResult() {
140  if (decomp.empty()) {
141  Printer print;
142  print.isCompact = true;
143  print.line_break = "";
144  decomp = print(lhs).str();
145  }
146  return ExprResult(res, decomp);
147  }
148 
149  operator L() const { return lhs; }
150 
157 
158  ZEROERR_EXPRESSION_ANDOR(&&, and)
160 
177 };
178 
179 #undef ZEROERR_EXPRESSION_COMPARISON
180 #undef ZEROERR_EXPRESSION_ANDOR
181 #undef ZEROERR_FORBIT_EXPRESSION
182 
184  // The right operator for capturing expressions is "<=" instead of "<<" (based on the
185  // operator precedence table) but then there will be warnings from GCC about "-Wparentheses"
186  // and since "_Pragma()" is problematic this will stay for now...
187  // https://github.com/catchorg/Catch2/issues/870
188  // https://github.com/catchorg/Catch2/issues/565
189 
190  // For temporary objects, we need to use rvalue reference to avoid copy
191  template <typename L>
192  Expression<L> operator<<(L&& operand) {
193  return Expression<L>(static_cast<L&&>(operand));
194  }
195 
196  // For other objects, we will store the reference
197  template <typename L,
198  typename std::enable_if<!std::is_rvalue_reference<L>::value, void>::type* = nullptr>
199  Expression<const L&> operator<<(const L& operand) {
200  return Expression<const L&>(operand);
201  }
202 };
203 
204 
205 template <typename T>
206 class IMatcher {
207 public:
208  virtual ~IMatcher() = default;
209 
210 
211  virtual bool match(const T&) const = 0;
212 };
213 
214 template <typename T>
215 class IMatcherRef {
216 public:
217  IMatcherRef(const IMatcher<T>* ptr) : p(ptr) {}
218  IMatcherRef(const IMatcherRef&) = delete;
219 
221  p = std::move(other.p);
222  other.p = nullptr;
223  }
224  void operator=(IMatcherRef&& other) {
225  p = std::move(other.p);
226  other.p = nullptr;
227  }
228  IMatcherRef& operator=(const IMatcherRef&) = delete;
230  if (p) delete p;
231  }
232 
233  IMatcherRef operator&&(IMatcherRef&& other);
234  IMatcherRef operator||(IMatcherRef&& other);
235  IMatcherRef operator!();
236 
237  const IMatcher<T>* operator->() const { return p; }
238 
239 protected:
240  const IMatcher<T>* p = nullptr;
241 };
242 
243 
244 template <typename T>
245 class CombinedMatcher : public IMatcher<T> {
246 public:
247  CombinedMatcher(IMatcherRef<T>&& lhs, IMatcherRef<T>&& rhs, bool is_and)
248  : lhs(std::move(lhs)), rhs(std::move(rhs)), is_and(is_and) {}
249 
252  bool is_and;
253 
254  virtual bool match(const T& t) const override {
255  if (is_and) {
256  return lhs->match(t) && rhs->match(t);
257  } else {
258  return lhs->match(t) || rhs->match(t);
259  }
260  }
261 };
262 
263 template <typename T>
264 class NotMatcher : public IMatcher<T> {
265 public:
266  NotMatcher(IMatcherRef<T>&& matcher) : matcher(std::move(matcher)) {}
268 
269  virtual bool match(const T& t) const override { return !matcher->match(t); }
270 };
271 
272 template <typename T>
274  return new CombinedMatcher<T>(std::move(*this), std::move(other), true);
275 }
276 
277 template <typename T>
279  return new CombinedMatcher<T>(std::move(*this), std::move(other), false);
280 }
281 
282 template <typename T>
284  return new NotMatcher<T>(std::move(*this));
285 }
286 
287 
288 template <typename T>
289 struct StartWithMatcher : public IMatcher<T> {
290  StartWithMatcher(const T& s) : start(s) {}
291  T start;
292 
293  virtual bool match(const T& t) const override {
294  bool result = true;
295  for (auto i = start.begin(), j = t.begin(); i != start.end(); ++i, ++j) {
296  if (j == t.end() || *i != *j) {
297  result = false;
298  break;
299  }
300  }
301  return result;
302  }
303 };
304 
305 template <typename T>
306 typename std::enable_if<std::is_constructible<std::string, T>::value,
307  IMatcherRef<std::string>>::type
308 start_with(T&& s) {
309  return new StartWithMatcher<std::string>(std::string(s));
310 }
311 
312 } // namespace zeroerr
313 
Definition: decomposition.h:245
IMatcherRef< T > lhs
Definition: decomposition.h:250
IMatcherRef< T > rhs
Definition: decomposition.h:251
virtual bool match(const T &t) const override
Definition: decomposition.h:254
bool is_and
Definition: decomposition.h:252
CombinedMatcher(IMatcherRef< T > &&lhs, IMatcherRef< T > &&rhs, bool is_and)
Definition: decomposition.h:247
Definition: decomposition.h:215
IMatcherRef(IMatcherRef &&other)
Definition: decomposition.h:220
IMatcherRef operator||(IMatcherRef &&other)
Definition: decomposition.h:278
IMatcherRef(const IMatcher< T > *ptr)
Definition: decomposition.h:217
void operator=(IMatcherRef &&other)
Definition: decomposition.h:224
IMatcherRef(const IMatcherRef &)=delete
IMatcherRef operator&&(IMatcherRef &&other)
Definition: decomposition.h:273
const IMatcher< T > * operator->() const
Definition: decomposition.h:237
IMatcherRef operator!()
Definition: decomposition.h:283
IMatcherRef & operator=(const IMatcherRef &)=delete
~IMatcherRef()
Definition: decomposition.h:229
Definition: decomposition.h:206
virtual bool match(const T &) const =0
virtual ~IMatcher()=default
Definition: decomposition.h:264
IMatcherRef< T > matcher
Definition: decomposition.h:267
NotMatcher(IMatcherRef< T > &&matcher)
Definition: decomposition.h:266
virtual bool match(const T &t) const override
Definition: decomposition.h:269
#define ZEROERR_SUPPRESS_COMPARE
Definition: config.h:299
#define ZEROERR_SUPPRESS_COMMON_WARNINGS_POP
Definition: config.h:265
#define ZEROERR_SUPPRESS_COMPARE_POP
Definition: config.h:312
#define ZEROERR_SUPPRESS_COMMON_WARNINGS_PUSH
Definition: config.h:218
#define ZEROERR_FORBIT_EXPRESSION(rt, op)
Definition: decomposition.h:79
#define ZEROERR_EXPRESSION_ANDOR(op, op_name)
Definition: decomposition.h:71
#define ZEROERR_EXPRESSION_COMPARISON(op, op_name)
Definition: decomposition.h:36
std::ostream & operator<<(std::ostream &os, const XmlEncode &xmlEncode)
Definition: unittest.cpp:570
std::enable_if<!std::is_convertible< T, bool >::value, bool >::type getBool(T &&)
Definition: decomposition.h:124
std::enable_if< std::is_convertible< T, bool >::value, bool >::type getBool(T &&lhs)
Definition: decomposition.h:119
Definition: benchmark.cpp:17
std::enable_if< std::is_constructible< std::string, T >::value, IMatcherRef< std::string > >::type start_with(T &&s)
Definition: decomposition.h:308
Definition: decomposition.h:87
std::string decomp
Definition: decomposition.h:89
bool res
Definition: decomposition.h:88
ExprResult(bool res, std::string decomposition="")
Definition: decomposition.h:91
Definition: decomposition.h:183
Definition: decomposition.h:130
Expression(L &&in, bool res, std::string &&decomp)
Definition: decomposition.h:136
std::string decomp
Definition: decomposition.h:133
L lhs
Definition: decomposition.h:131
Expression(L &&in)
Definition: decomposition.h:135
A functor class Printer for printing a value of any type.
Definition: print.h:80
std::string str() const
Definition: print.h:270
bool isCompact
Definition: print.h:122
const char * line_break
Definition: print.h:125
Definition: decomposition.h:289
StartWithMatcher(const T &s)
Definition: decomposition.h:290
virtual bool match(const T &t) const override
Definition: decomposition.h:293
T start
Definition: decomposition.h:291
Definition: decomposition.h:32
static const bool value
Definition: decomposition.h:33