compile_time_tests.cpp
Go to the documentation of this file.
1 /*
2  * Created on: 27/02/2018
3  * Author: ricab
4  */
5 
6 #include "scope_guard.hpp"
7 #include <functional>
8 #include <stdexcept>
9 #include <tuple>
10 #include <utility>
11 
12 using namespace sg;
13 
14 /* --- first some test helpers --- */
15 
17 namespace
18 {
19  constexpr auto EMSG = "message in a bottle";
20  void non_throwing() noexcept { }
21  [[noreturn]] void throwing() { throw std::runtime_error{EMSG}; }
22  void meh() { }
23 
24  int returning() noexcept { return 42; }
25 
26 
27  using StdFun = std::function<void()>;
28  const StdFun throwing_stdfun{throwing};
29  const StdFun non_throwing_stdfun{non_throwing}; // drops noexcept
30  const StdFun meh_stdfun{meh};
31 
32  const std::function<int()> returning_stdfun{returning}; // drops noexcept
33 
34 
35  const auto throwing_lambda = []{ throwing(); };
36  const auto non_throwing_lambda = []() noexcept { non_throwing(); };
37  const auto meh_lambda = []{ meh(); };
38 
39  const auto returning_lambda = []() noexcept { return returning(); };
40 
41 
42  const auto throwing_bound = std::bind(throwing);
43  const auto non_throwing_bound = std::bind(non_throwing); // drops noexcept
44  const auto meh_bound = std::bind(meh);
45 
46  const auto returning_bound = std::bind(returning); // drops noexcept
47 
48 
49  struct throwing_struct
50  {
51  [[noreturn]] void operator()() { throwing(); }
52  } throwing_functor;
53  struct non_throwing_struct
54  {
55  void operator()() const noexcept { non_throwing(); }
56  } non_throwing_functor;
57  struct meh_struct
58  {
59  void operator()() const { meh(); }
60  } meh_functor;
61 
62  struct returning_struct
63  {
64  int operator()() const noexcept { return returning(); }
65  } returning_functor;
66 
67  struct nocopy_nomove // non-copyable and non-movable
68  {
69  void operator()() const noexcept { non_throwing(); }
70 
71  nocopy_nomove() noexcept = default;
72  ~nocopy_nomove() noexcept = default;
73 
74  nocopy_nomove(const nocopy_nomove&) = delete;
75  nocopy_nomove& operator=(const nocopy_nomove&) = delete;
76  nocopy_nomove(nocopy_nomove&&) = delete;
77  nocopy_nomove& operator=(nocopy_nomove&&) = delete;
78  };
79 
80  struct potentially_throwing_dtor
81  {
82  void operator()() const noexcept { non_throwing(); }
83 
84  ~potentially_throwing_dtor() noexcept(false) {}
85 
86  potentially_throwing_dtor() noexcept = default;
87  potentially_throwing_dtor(const potentially_throwing_dtor&) noexcept
88  = default;
89  potentially_throwing_dtor(potentially_throwing_dtor&&) noexcept = default;
90  };
91 
92  struct throwing_copy
93  {
94  void operator()() const noexcept { non_throwing(); }
95 
96  throwing_copy(const throwing_copy&) noexcept(false) {}
97 
98  ~throwing_copy() noexcept = default;
99  throwing_copy() noexcept = default;
100  throwing_copy(throwing_copy&&) noexcept = default;
101  };
102 
103  struct throwing_move
104  {
105  void operator()() const noexcept { non_throwing(); }
106 
107  throwing_move(throwing_move&&) noexcept(false) {}
108 
109  ~throwing_move() noexcept = default;
110  throwing_move() noexcept = default;
111  throwing_move(const throwing_move&) noexcept = default;
112  };
113 
114  struct nomove_throwing_copy
115  {
116  void operator()() const noexcept { non_throwing(); }
117 
118  nomove_throwing_copy(const nomove_throwing_copy&) noexcept(false) {}
119 
120  /*
121  * nomove_throwing_copy(nomove_throwing_copy&&) noexcept;
122  *
123  * not declared! move ctor absent but not deleted - does not participate in
124  * overload resolution, so copy ctor can still be selected
125  */
126 
127  ~nomove_throwing_copy() noexcept = default;
128  nomove_throwing_copy() noexcept = default;
129  };
130 
131  struct nothrow
132  {
133  void operator()() const noexcept { non_throwing(); }
134 
135  ~nothrow() noexcept = default;
136  nothrow() noexcept = default;
137  nothrow(const nothrow&) noexcept = default;
138  nothrow(nothrow&&) noexcept = default;
139  };
140 
141 
142  /* --- tests that always succeed --- */
143 
144 #ifdef test_1
145  static_assert(noexcept(make_scope_guard(std::declval<void(*)()noexcept>())),
146  "make_scope_guard not noexcept");
147 #endif
148 
149 #ifdef test_2
150  static_assert(noexcept(make_scope_guard(std::declval<void(*)()noexcept>())
151  .~scope_guard()),
152  "scope_guard dtor not noexcept");
153 #endif
154 
159  void test_throwing_dtor_throw_spec_good()
160  {
161 #ifdef test_3
162  potentially_throwing_dtor x;
163  static_assert(noexcept(make_scope_guard(x)),
164  "make_scope_guard not declared noexcept when instanced "
165  "with an lvalue object whose dtor throws (should deduce "
166  "reference and avoid destruction entirely)");
167 #endif
168 #ifdef test_4
169  potentially_throwing_dtor x;
170  auto& r = x;
171  static_assert(noexcept(make_scope_guard(r)),
172  "make_scope_guard not declared noexcept when instanced "
173  "with an lvalue reference to an object whose dtor throws "
174  "(should deduce reference and avoid destruction entirely)");
175 #endif
176 #ifdef test_5
177  potentially_throwing_dtor x;
178  const auto& cr = x;
179  static_assert(noexcept(make_scope_guard(cr)),
180  "make_scope_guard not declared noexcept when instanced "
181  "with an lvalue reference to a const object whose dtor "
182  "throws (should deduce reference and avoid destruction "
183  "entirely)");
184 #endif
185  }
186 
191  void test_throwing_copy_throw_spec()
192  {
193 #ifdef test_6
194  static_assert(noexcept(make_scope_guard(throwing_copy{})),
195  "make_scope_guard not declared noexcept when instanced "
196  "with an rvalue object whose copy ctor throws");
197 #endif
198 #ifdef test_7
199  throwing_copy x;
200  static_assert(noexcept(make_scope_guard(x)),
201  "make_scope_guard not declared noexcept when instanced "
202  "with an lvalue object whose copy ctor throws (should "
203  "deduce reference and avoid copy entirely)");
204 #endif
205 #ifdef test_8
206  throwing_copy x;
207  static_assert(noexcept(make_scope_guard(std::move(x))),
208  "make_scope_guard not declared noexcept when instanced "
209  "with an rvalue reference to an object whose copy ctor "
210  "throws");
211 #endif
212 #ifdef test_9
213  throwing_copy x;
214  auto& r = x;
215  static_assert(noexcept(make_scope_guard(r)),
216  "make_scope_guard not declared noexcept when instanced "
217  "with an lvalue reference to an object whose copy ctor "
218  "throws (should deduce reference and avoid copy entirely)");
219 #endif
220 #ifdef test_10
221  throwing_copy x;
222  const auto& cr = x;
223  static_assert(noexcept(make_scope_guard(cr)),
224  "make_scope_guard not declared noexcept when instanced "
225  "with an lvalue reference to a const object whose copy "
226  "ctor throws (should deduce reference and avoid copy "
227  "entirely)");
228 #endif
229  }
230 
235  void test_throwing_move_throw_spec()
236  {
237 #ifdef test_11
238  static_assert(!noexcept(make_scope_guard(throwing_move{})),
239  "make_scope_guard wrongly declared noexcept when instanced "
240  "with an rvalue object whose move ctor throws");
241 #endif
242 #ifdef test_12
243  throwing_move x;
244  static_assert(noexcept(make_scope_guard(x)),
245  "make_scope_guard not declared noexcept when instanced "
246  "with an lvalue object whose move ctor throws");
247 #endif
248 #ifdef test_13
249  throwing_move x;
250  static_assert(!noexcept(make_scope_guard(std::move(x))),
251  "make_scope_guard wrongly declared noexcept when instanced "
252  "with an rvalue reference to an object whose move ctor "
253  "throws");
254 #endif
255 #ifdef test_14
256  throwing_move x;
257  auto& r = x;
258  static_assert(noexcept(make_scope_guard(r)),
259  "make_scope_guard not declared noexcept when instanced "
260  "with an lvalue reference to an object whose move ctor "
261  "throws");
262 #endif
263 #ifdef test_15
264  throwing_move x;
265  const auto& cr = x;
266  static_assert(noexcept(make_scope_guard(cr)),
267  "make_scope_guard not declared noexcept when instanced "
268  "with an lvalue reference to a const object whose move "
269  "ctor throws");
270 #endif
271  }
272 
277  void test_nomove_throwing_copy_throw_spec()
278  {
279 #ifdef test_16
280  static_assert(!noexcept(make_scope_guard(nomove_throwing_copy{})),
281  "make_scope_guard wrongly declared noexcept when instanced "
282  "with an rvalue object whose copy ctor throws and without "
283  "a move ctor");
284 #endif
285 #ifdef test_17
286  nomove_throwing_copy x;
287  static_assert(noexcept(make_scope_guard(x)),
288  "make_scope_guard not declared noexcept when instanced "
289  "with an lvalue object whose copy ctor throws and without "
290  "a move ctor (should deduce reference and avoid copy "
291  "entirely)");
292 #endif
293 #ifdef test_18
294  nomove_throwing_copy x;
295  static_assert(!noexcept(make_scope_guard(std::move(x))),
296  "make_scope_guard wrongly declared noexcept when instanced "
297  "with an rvalue reference to an object whose copy ctor "
298  "throws and without a move ctor");
299 #endif
300 #ifdef test_19
301  nomove_throwing_copy x;
302  auto& r = x;
303  static_assert(noexcept(make_scope_guard(r)),
304  "make_scope_guard not declared noexcept when instanced "
305  "with an lvalue reference to an object whose copy ctor "
306  "throws (should deduce reference and avoid copy entirely)");
307 #endif
308 #ifdef test_20
309  nomove_throwing_copy x;
310  const auto& cr = x;
311  static_assert(noexcept(make_scope_guard(cr)),
312  "make_scope_guard not declared noexcept when instanced "
313  "with an lvalue reference to a const object whose copy "
314  "ctor throws (should deduce reference and avoid copy "
315  "entirely)");
316 #endif
317  }
318 
323  void test_nothrow_throw_spec()
324  {
325 #ifdef test_21
326  static_assert(noexcept(make_scope_guard(nothrow{})),
327  "make_scope_guard not declared noexcept when instanced "
328  "with an rvalue object whose ctors and dtor do not throw");
329 #endif
330 #ifdef test_22
331  nothrow x;
332  static_assert(noexcept(make_scope_guard(x)),
333  "make_scope_guard not declared noexcept when instanced "
334  "with an lvalue object whose ctors and dtor do not throw");
335 #endif
336 #ifdef test_23
337  nothrow x;
338  static_assert(noexcept(make_scope_guard(std::move(x))),
339  "make_scope_guard not declared noexcept when instanced "
340  "with an rvalue reference to an object whose ctors and "
341  "dtor do not throw");
342 #endif
343 #ifdef test_24
344  nothrow x;
345  auto& r = x;
346  static_assert(noexcept(make_scope_guard(r)),
347  "make_scope_guard not declared noexcept when instanced "
348  "with an lvalue reference to an object whose ctors and "
349  "dtor do not throw");
350 #endif
351 #ifdef test_25
352  nothrow x;
353  const auto& cr = x;
354  static_assert(noexcept(make_scope_guard(cr)),
355  "make_scope_guard not declared noexcept when instanced "
356  "with an lvalue reference to a const object whose ctors "
357  "dtor do not throw");
358 #endif
359  }
360 
365  void test_noncopyable_nonmovable_good()
366  {
367 #ifdef test_26
368  nocopy_nomove ncnm{};
369  std::ignore = make_scope_guard(ncnm);
370 #endif
371 #ifdef test_27
372  nocopy_nomove ncnm{};
373  auto& ncnmr = ncnm;
374  std::ignore = make_scope_guard(ncnmr);
375 #endif
376 #ifdef test_28
377  nocopy_nomove ncnm{};
378  const auto& ncnmcr = ncnm;
379  std::ignore = make_scope_guard(ncnmcr);
380 #endif
381  }
382 
386  void test_dismiss_is_noexcept()
387  {
388 #ifdef test_29
389  static_assert(noexcept(make_scope_guard(non_throwing).dismiss()),
390  "scope_guard::dismiss not noexcept");
391 #endif
392 #ifdef test_30
393  static_assert(noexcept(make_scope_guard(non_throwing_lambda).dismiss()),
394  "scope_guard::dismiss not noexcept");
395 #endif
396 #ifdef test_31
397  static_assert(noexcept(make_scope_guard(non_throwing_functor).dismiss()),
398  "scope_guard::dismiss not noexcept");
399 #endif
400  }
401 
405  void test_noexcept_good()
406  {
407 #ifdef test_32
408  std::ignore = make_scope_guard(non_throwing);
409 #endif
410 #ifdef test_33
411  std::ignore = make_scope_guard(non_throwing_lambda);
412 #endif
413 #ifdef test_34
414  std::ignore = make_scope_guard(non_throwing_functor);
415 #endif
416  }
417 
418  /* --- tests that fail iff nothrow_invocable is required --- */
419 
425  void test_noexcept_bad()
426  {
427 #ifdef test_35
428  std::ignore = make_scope_guard(throwing);
429 #endif
430 #ifdef test_36
431  std::ignore = make_scope_guard(throwing_stdfun);
432 #endif
433 #ifdef test_37
434  std::ignore = make_scope_guard(throwing_lambda);
435 #endif
436 #ifdef test_38
437  std::ignore = make_scope_guard(throwing_bound);
438 #endif
439 #ifdef test_39
440  std::ignore = make_scope_guard(throwing_functor);
441 #endif
442  }
443 
449  void test_noexcept_fixable()
450  {
451 #ifdef test_40
452  std::ignore = make_scope_guard(meh);
453 #endif
454 #ifdef test_41
455  std::ignore = make_scope_guard(meh_stdfun);
456 #endif
457 #ifdef test_42
458  std::ignore = make_scope_guard(meh_lambda);
459 #endif
460 #ifdef test_43
461  std::ignore = make_scope_guard(meh_bound);
462 #endif
463 #ifdef test_44
464  std::ignore = make_scope_guard(meh_functor);
465 #endif
466  }
467 
474  void test_noexcept_unfortunate()
475  {
476 #ifdef test_45
477  std::ignore = make_scope_guard(non_throwing_stdfun);
478 #endif
479 #ifdef test_46
480  std::ignore = make_scope_guard(non_throwing_bound);
481 #endif
482  }
483 
484  void test_dismiss_is_noexcept_even_if_bad_callable()
485  {
486 #ifdef test_47
487  static_assert(noexcept(make_scope_guard(throwing).dismiss()),
488  "scope_guard::dismiss not noexcept");
489 #endif
490 #ifdef test_48
491  static_assert(noexcept(make_scope_guard(throwing_stdfun).dismiss()),
492  "scope_guard::dismiss not noexcept");
493 #endif
494 #ifdef test_49
495  static_assert(noexcept(make_scope_guard(throwing_lambda).dismiss()),
496  "scope_guard::dismiss not noexcept");
497 #endif
498 #ifdef test_50
499  static_assert(noexcept(make_scope_guard(throwing_bound).dismiss()),
500  "scope_guard::dismiss not noexcept");
501 #endif
502 #ifdef test_51
503  static_assert(noexcept(make_scope_guard(throwing_functor).dismiss()),
504  "scope_guard::dismiss not noexcept");
505 #endif
506  }
507 
508 
509  /* --- tests that always fail --- */
510 
511  void test_throwing_dtor_throw_spec_bad()
512  {
513 #ifdef test_52
514  std::ignore = make_scope_guard(potentially_throwing_dtor{});
515 #endif
516 #ifdef test_53
517  potentially_throwing_dtor x;
518  std::ignore = make_scope_guard(std::move(x));
519 #endif
520  }
521 
525  void test_disallowed_copy_construction()
526  {
527  const auto guard1 = make_scope_guard(non_throwing);
528 #ifdef test_54
529  const auto guard2 = guard1;
530 #endif
531  }
532 
536  void test_disallowed_copy_assignment()
537  {
538  const auto guard1 = make_scope_guard(non_throwing_lambda);
539  auto guard2 = make_scope_guard(non_throwing_functor);
540 #ifdef test_55
541  guard2 = guard1;
542 #endif
543  }
544 
548  void test_disallowed_move_assignment()
549  {
550  auto guard = make_scope_guard(non_throwing);
551 #ifdef test_56
552  guard = make_scope_guard(non_throwing_lambda);
553 #endif
554  }
555 
560  void test_disallowed_return()
561  {
562 #ifdef test_57
563  std::ignore = make_scope_guard(returning);
564 #endif
565 #ifdef test_58
566  std::ignore = make_scope_guard(returning_stdfun);
567 #endif
568 #ifdef test_59
569  std::ignore = make_scope_guard(returning_lambda);
570 #endif
571 #ifdef test_60
572  std::ignore = make_scope_guard(returning_bound);
573 #endif
574 #ifdef test_61
575  std::ignore = make_scope_guard(returning_functor);
576 #endif
577  }
578 
583  void test_noncopyable_nonmovable_bad()
584  {
585 #ifdef test_62
586  std::ignore = make_scope_guard(nocopy_nomove{});
587 #endif
588 #ifdef test_63
589  nocopy_nomove ncnm{};
590  std::ignore = make_scope_guard(std::move(ncnm));
591 #endif
592  }
593 
598  void test_direct_construction_forbidden()
599  {
600 #ifdef test_64
601  detail::scope_guard{non_throwing};
602 #endif
603 #ifdef test_65
604  auto x = detail::scope_guard(non_throwing);
605 #endif
606 #ifdef test_66
607  auto x = detail::scope_guard<void(&)()noexcept>{non_throwing};
608 #endif
609 #ifdef test_67
610  auto lambda = []() noexcept {};
611  detail::scope_guard<decltype(lambda)>{std::move(lambda)};
612 #endif
613  }
614 
619  void test_const_guard_cannot_do_everything()
620  {
621  const auto guard = make_scope_guard(non_throwing);
622 #ifdef test_68
623  guard.dismiss();
624 #endif
625 #ifdef test_69
626  auto another = std::move(guard);
627 #endif
628  }
629 
633 #ifdef test_70
634  struct concrete_specialized_guard
635  : detail::scope_guard<void(*)()noexcept>
636  {};
637 #endif
638 #ifdef test_71
639  template<typename T>
640  struct specialized_guard : detail::scope_guard<T>
641  {
642  explicit specialized_guard(T&& t)
643  : detail::scope_guard<T>{std::forward<T>(t)}
644  {}
645  };
646 
647  template<typename T>
648  specialized_guard<T> make_specialized_guard(T&& t)
649  {
650  return specialized_guard<T>{std::forward<T>(t)};
651  }
652 
653  auto special = make_specialized_guard([]()noexcept{});
654 #endif
655 
656 }
657 
658 int main()
659 {
660  test_throwing_dtor_throw_spec_good();
661  test_throwing_copy_throw_spec();
662  test_throwing_move_throw_spec();
663  test_nomove_throwing_copy_throw_spec();
664  test_nothrow_throw_spec();
665  test_noncopyable_nonmovable_good();
666  test_dismiss_is_noexcept();
667  test_noexcept_good();
668 
669  test_noexcept_bad(); // this results in a call to std::terminate
670  test_noexcept_fixable();
671  test_noexcept_unfortunate();
672  test_dismiss_is_noexcept_even_if_bad_callable();
673 
674  test_throwing_dtor_throw_spec_bad();
675  test_disallowed_copy_construction();
676  test_disallowed_copy_assignment();
677  test_disallowed_move_assignment();
678  test_disallowed_return();
679  test_noncopyable_nonmovable_bad();
680  test_direct_construction_forbidden();
681 
682  return 0;
683 }
r
r
detail
Definition: cost_queue.h:131
x
x
main
int main()
Definition: compile_time_tests.cpp:658
cr
cr


core
Author(s):
autogenerated on Sat May 3 2025 02:40:11