6 #include "scope_guard.hpp"
19 constexpr
auto EMSG =
"message in a bottle";
20 void non_throwing() noexcept { }
21 [[noreturn]]
void throwing() {
throw std::runtime_error{EMSG}; }
24 int returning() noexcept {
return 42; }
27 using StdFun = std::function<void()>;
28 const StdFun throwing_stdfun{throwing};
29 const StdFun non_throwing_stdfun{non_throwing};
30 const StdFun meh_stdfun{meh};
32 const std::function<int()> returning_stdfun{returning};
35 const auto throwing_lambda = []{ throwing(); };
36 const auto non_throwing_lambda = []() noexcept { non_throwing(); };
37 const auto meh_lambda = []{ meh(); };
39 const auto returning_lambda = []() noexcept {
return returning(); };
42 const auto throwing_bound = std::bind(throwing);
43 const auto non_throwing_bound = std::bind(non_throwing);
44 const auto meh_bound = std::bind(meh);
46 const auto returning_bound = std::bind(returning);
49 struct throwing_struct
51 [[noreturn]]
void operator()() { throwing(); }
53 struct non_throwing_struct
55 void operator()() const noexcept { non_throwing(); }
56 } non_throwing_functor;
59 void operator()()
const { meh(); }
62 struct returning_struct
64 int operator()() const noexcept {
return returning(); }
69 void operator()() const noexcept { non_throwing(); }
71 nocopy_nomove() noexcept = default;
72 ~nocopy_nomove() noexcept = default;
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;
80 struct potentially_throwing_dtor
82 void operator()() const noexcept { non_throwing(); }
84 ~potentially_throwing_dtor() noexcept(false) {}
86 potentially_throwing_dtor() noexcept = default;
87 potentially_throwing_dtor(const potentially_throwing_dtor&) noexcept
89 potentially_throwing_dtor(potentially_throwing_dtor&&) noexcept = default;
94 void operator()() const noexcept { non_throwing(); }
96 throwing_copy(
const throwing_copy&) noexcept(
false) {}
98 ~throwing_copy() noexcept = default;
99 throwing_copy() noexcept = default;
100 throwing_copy(throwing_copy&&) noexcept = default;
105 void operator()() const noexcept { non_throwing(); }
107 throwing_move(throwing_move&&) noexcept(false) {}
109 ~throwing_move() noexcept = default;
110 throwing_move() noexcept = default;
111 throwing_move(const throwing_move&) noexcept = default;
114 struct nomove_throwing_copy
116 void operator()() const noexcept { non_throwing(); }
118 nomove_throwing_copy(
const nomove_throwing_copy&) noexcept(
false) {}
127 ~nomove_throwing_copy() noexcept = default;
128 nomove_throwing_copy() noexcept = default;
133 void operator()() const noexcept { non_throwing(); }
135 ~nothrow() noexcept = default;
136 nothrow() noexcept = default;
137 nothrow(const nothrow&) noexcept = default;
138 nothrow(nothrow&&) noexcept = default;
145 static_assert(noexcept(make_scope_guard(std::declval<
void(*)()noexcept>())),
146 "make_scope_guard not noexcept");
150 static_assert(noexcept(make_scope_guard(std::declval<
void(*)()noexcept>())
152 "scope_guard dtor not noexcept");
159 void test_throwing_dtor_throw_spec_good()
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)");
169 potentially_throwing_dtor
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)");
177 potentially_throwing_dtor
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 "
191 void test_throwing_copy_throw_spec()
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");
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)");
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 "
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)");
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 "
235 void test_throwing_move_throw_spec()
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");
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");
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 "
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 "
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 "
277 void test_nomove_throwing_copy_throw_spec()
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 "
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 "
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");
301 nomove_throwing_copy
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)");
309 nomove_throwing_copy
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 "
323 void test_nothrow_throw_spec()
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");
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");
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");
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");
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");
365 void test_noncopyable_nonmovable_good()
368 nocopy_nomove ncnm{};
369 std::ignore = make_scope_guard(ncnm);
372 nocopy_nomove ncnm{};
374 std::ignore = make_scope_guard(ncnmr);
377 nocopy_nomove ncnm{};
378 const auto& ncnmcr = ncnm;
379 std::ignore = make_scope_guard(ncnmcr);
386 void test_dismiss_is_noexcept()
389 static_assert(noexcept(make_scope_guard(non_throwing).dismiss()),
390 "scope_guard::dismiss not noexcept");
393 static_assert(noexcept(make_scope_guard(non_throwing_lambda).dismiss()),
394 "scope_guard::dismiss not noexcept");
397 static_assert(noexcept(make_scope_guard(non_throwing_functor).dismiss()),
398 "scope_guard::dismiss not noexcept");
405 void test_noexcept_good()
408 std::ignore = make_scope_guard(non_throwing);
411 std::ignore = make_scope_guard(non_throwing_lambda);
414 std::ignore = make_scope_guard(non_throwing_functor);
425 void test_noexcept_bad()
428 std::ignore = make_scope_guard(throwing);
431 std::ignore = make_scope_guard(throwing_stdfun);
434 std::ignore = make_scope_guard(throwing_lambda);
437 std::ignore = make_scope_guard(throwing_bound);
440 std::ignore = make_scope_guard(throwing_functor);
449 void test_noexcept_fixable()
452 std::ignore = make_scope_guard(meh);
455 std::ignore = make_scope_guard(meh_stdfun);
458 std::ignore = make_scope_guard(meh_lambda);
461 std::ignore = make_scope_guard(meh_bound);
464 std::ignore = make_scope_guard(meh_functor);
474 void test_noexcept_unfortunate()
477 std::ignore = make_scope_guard(non_throwing_stdfun);
480 std::ignore = make_scope_guard(non_throwing_bound);
484 void test_dismiss_is_noexcept_even_if_bad_callable()
487 static_assert(noexcept(make_scope_guard(throwing).dismiss()),
488 "scope_guard::dismiss not noexcept");
491 static_assert(noexcept(make_scope_guard(throwing_stdfun).dismiss()),
492 "scope_guard::dismiss not noexcept");
495 static_assert(noexcept(make_scope_guard(throwing_lambda).dismiss()),
496 "scope_guard::dismiss not noexcept");
499 static_assert(noexcept(make_scope_guard(throwing_bound).dismiss()),
500 "scope_guard::dismiss not noexcept");
503 static_assert(noexcept(make_scope_guard(throwing_functor).dismiss()),
504 "scope_guard::dismiss not noexcept");
511 void test_throwing_dtor_throw_spec_bad()
514 std::ignore = make_scope_guard(potentially_throwing_dtor{});
517 potentially_throwing_dtor
x;
518 std::ignore = make_scope_guard(std::move(x));
525 void test_disallowed_copy_construction()
527 const auto guard1 = make_scope_guard(non_throwing);
529 const auto guard2 = guard1;
536 void test_disallowed_copy_assignment()
538 const auto guard1 = make_scope_guard(non_throwing_lambda);
539 auto guard2 = make_scope_guard(non_throwing_functor);
548 void test_disallowed_move_assignment()
550 auto guard = make_scope_guard(non_throwing);
552 guard = make_scope_guard(non_throwing_lambda);
560 void test_disallowed_return()
563 std::ignore = make_scope_guard(returning);
566 std::ignore = make_scope_guard(returning_stdfun);
569 std::ignore = make_scope_guard(returning_lambda);
572 std::ignore = make_scope_guard(returning_bound);
575 std::ignore = make_scope_guard(returning_functor);
583 void test_noncopyable_nonmovable_bad()
586 std::ignore = make_scope_guard(nocopy_nomove{});
589 nocopy_nomove ncnm{};
590 std::ignore = make_scope_guard(std::move(ncnm));
598 void test_direct_construction_forbidden()
601 detail::scope_guard{non_throwing};
604 auto x = detail::scope_guard(non_throwing);
607 auto x = detail::scope_guard<void(&)()noexcept>{non_throwing};
610 auto lambda = []() noexcept {};
611 detail::scope_guard<decltype(lambda)>{std::move(lambda)};
619 void test_const_guard_cannot_do_everything()
621 const auto guard = make_scope_guard(non_throwing);
626 auto another = std::move(guard);
634 struct concrete_specialized_guard
635 : detail::scope_guard<void(*)()noexcept>
640 struct specialized_guard : detail::scope_guard<T>
642 explicit specialized_guard(T&& t)
643 :
detail::scope_guard<T>{std::forward<T>(t)}
648 specialized_guard<T> make_specialized_guard(T&& t)
650 return specialized_guard<T>{std::forward<T>(t)};
653 auto special = make_specialized_guard([]()noexcept{});
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();
670 test_noexcept_fixable();
671 test_noexcept_unfortunate();
672 test_dismiss_is_noexcept_even_if_bad_callable();
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();