Skip to content

Commit 0d2a527

Browse files
Add an alternative/example scope_guard implementation (not inline with synopsis)
1 parent 37abed8 commit 0d2a527

File tree

2 files changed

+192
-10
lines changed

2 files changed

+192
-10
lines changed

examples/scope_example.cpp

Lines changed: 93 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,29 @@
11
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
22

3-
#include <beman/scope/scope.hpp>
3+
#include <beman/scope/scope_guard_demo.hpp>
44

55
#include <cstdlib>
66
#include <iostream>
77
#include <string_view>
88

9-
namespace scope = beman::scope;
9+
namespace scope = beman::scope::demo;
1010

1111
void print_exit_status(std::string_view name, bool exit_status, bool did_throw) {
1212
std::cout << name << ":\n";
13-
std::cout << " Throwed exception " << (did_throw ? "yes" : "no") << "\n";
14-
std::cout << " Exit status " << (exit_status ? "finished" : "pending") << "\n\n";
13+
std::cout << " Exception thrown: " << (did_throw ? "yes" : "no") << "\n";
14+
std::cout << " Exit status: " << (exit_status ? "handled" : "no handling") << "\n\n";
1515
}
1616

1717
// Randomly throw an exception (50% chance)
1818
void maybe_throw() {
19-
if (std::rand() >= RAND_MAX / 2)
19+
if (std::rand() >= RAND_MAX / 2) {
2020
throw std::exception{};
21+
}
2122
}
2223

23-
int main() {
24-
bool exit_status{false}, did_throw{false};
24+
void standard_exit() {
25+
bool exit_status{false};
26+
bool did_throw{false};
2527

2628
// Manual handling at "end of scope"
2729
try {
@@ -35,7 +37,7 @@ int main() {
3537
// Using scope_exit: runs on scope exit (success or exception)
3638
exit_status = did_throw = false;
3739
try {
38-
auto guard = scope::scope_exit{[&] { exit_status = true; }};
40+
auto guard = scope::scope_exit([&] { exit_status = true; });
3941
maybe_throw();
4042
} catch (...) {
4143
did_throw = true;
@@ -45,7 +47,7 @@ int main() {
4547
// Using scope_fail: runs only if an exception occurs
4648
exit_status = did_throw = false;
4749
try {
48-
auto guard = scope::scope_fail{[&] { exit_status = true; }};
50+
auto guard = scope::scope_fail([&] { exit_status = true; });
4951
maybe_throw();
5052
} catch (...) {
5153
did_throw = true;
@@ -55,10 +57,91 @@ int main() {
5557
// Using scope_success: runs only if no exception occurs
5658
exit_status = did_throw = false;
5759
try {
58-
auto guard = scope::scope_success{[&] { exit_status = true; }};
60+
auto guard = scope::scope_success([&] { exit_status = true; });
5961
maybe_throw();
6062
} catch (...) {
6163
did_throw = true;
6264
}
6365
print_exit_status("scope_success", exit_status, did_throw);
6466
}
67+
68+
void releasable_exit() {
69+
bool exit_status{false};
70+
bool did_throw{false};
71+
72+
// Manual handling at "end of scope"
73+
try {
74+
maybe_throw();
75+
exit_status = true;
76+
} catch (...) {
77+
did_throw = true;
78+
}
79+
print_exit_status("Manual handling", exit_status, did_throw);
80+
81+
// Using scope_exit: runs on scope exit (success or exception)
82+
exit_status = did_throw = false;
83+
try {
84+
auto guard = scope::scope_exit_releasable([&] { exit_status = true; });
85+
guard.release();
86+
maybe_throw();
87+
} catch (...) {
88+
did_throw = true;
89+
}
90+
print_exit_status("scope_exit", exit_status, did_throw);
91+
92+
// Using scope_fail: runs only if an exception occurs
93+
exit_status = did_throw = false;
94+
try {
95+
auto guard = scope::scope_fail_releasable([&] { exit_status = true; });
96+
guard.release();
97+
maybe_throw();
98+
} catch (...) {
99+
did_throw = true;
100+
}
101+
print_exit_status("scope_fail", exit_status, did_throw);
102+
103+
// Using scope_success: runs only if no exception occurs
104+
exit_status = did_throw = false;
105+
try {
106+
auto guard = scope::scope_success_releasable([&] { exit_status = true; });
107+
guard.release();
108+
maybe_throw();
109+
} catch (...) {
110+
did_throw = true;
111+
}
112+
print_exit_status("scope_success", exit_status, did_throw);
113+
}
114+
115+
class CancelableAction {
116+
bool m_can_invoke = true;
117+
118+
public:
119+
void cancel() { m_can_invoke = false; }
120+
121+
void uncancel() { m_can_invoke = true; }
122+
123+
bool can_invoke() const { return m_can_invoke; }
124+
};
125+
126+
void cancelable_exit() {
127+
128+
bool exit_status{false};
129+
bool did_throw{false};
130+
131+
try {
132+
auto guard = scope::scope_guard([&] { exit_status = true; }, CancelableAction{});
133+
guard.cancel();
134+
maybe_throw();
135+
} catch (...) {
136+
did_throw = true;
137+
}
138+
139+
print_exit_status("cancelable", exit_status, did_throw);
140+
}
141+
142+
int main() {
143+
standard_exit();
144+
releasable_exit();
145+
146+
cancelable_exit();
147+
}
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
2+
3+
#include <concepts>
4+
#include <exception> // needed for scope_fail & scope_success
5+
6+
namespace beman::scope::demo {
7+
8+
template <typename T>
9+
struct Wrapper {
10+
T val;
11+
};
12+
13+
template <std::invocable ExitCallable, typename... Checkers> //
14+
class scope_guard : public Checkers... {
15+
ExitCallable m_exit_func;
16+
17+
public:
18+
scope_guard(ExitCallable exit_func, Checkers&&... checkers)
19+
: Checkers{std::move(checkers)}..., //
20+
m_exit_func(std::move(exit_func)) //
21+
{}
22+
23+
~scope_guard() {
24+
if constexpr (sizeof...(Checkers) > 0) {
25+
if ((call_check<Checkers>(*this) && ...)) {
26+
m_exit_func();
27+
}
28+
} else {
29+
std::invoke(m_exit_func);
30+
}
31+
}
32+
33+
private:
34+
template <typename T>
35+
bool call_check(const T& obj) const {
36+
if constexpr (requires(T t) {
37+
{ t.can_invoke() } -> std::convertible_to<bool>;
38+
}) {
39+
return obj.can_invoke();
40+
} else if constexpr (requires {
41+
{ T::operator()() } -> std::convertible_to<bool>;
42+
}) {
43+
return T::operator()();
44+
} else if constexpr (requires(T t) {
45+
{ t.operator()() } -> std::convertible_to<bool>;
46+
}) {
47+
return obj();
48+
} else {
49+
return true; // Default behavior if no check function is available
50+
}
51+
}
52+
};
53+
54+
[[nodiscard]] inline auto scope_exit(auto&& exit_func) { //
55+
return scope_guard{exit_func};
56+
}
57+
58+
[[nodiscard]] inline auto scope_success(auto&& success_func) {
59+
return scope_guard{
60+
success_func, //
61+
[exception_count = std::uncaught_exceptions()] { return exception_count >= std::uncaught_exceptions(); }};
62+
}
63+
64+
[[nodiscard]] inline auto scope_fail(auto&& fail_func) {
65+
return scope_guard{
66+
fail_func, //
67+
[exception_count = std::uncaught_exceptions()] { return exception_count < std::uncaught_exceptions(); }};
68+
}
69+
70+
class ReleasableAction {
71+
bool m_can_invoke = true;
72+
73+
public:
74+
void release() { m_can_invoke = false; }
75+
76+
void unrelease() { m_can_invoke = true; }
77+
78+
bool can_invoke() const { return m_can_invoke; }
79+
};
80+
81+
[[nodiscard]] inline auto scope_exit_releasable(auto&& exit_func) { //
82+
return scope_guard{exit_func, ReleasableAction{}};
83+
}
84+
85+
[[nodiscard]] inline auto scope_success_releasable(auto&& success_func) {
86+
return scope_guard{
87+
success_func, //
88+
[exception_count = std::uncaught_exceptions()] { return exception_count >= std::uncaught_exceptions(); },
89+
ReleasableAction{}};
90+
}
91+
92+
[[nodiscard]] inline auto scope_fail_releasable(auto&& fail_func) {
93+
return scope_guard{
94+
fail_func, //
95+
[exception_count = std::uncaught_exceptions()] { return exception_count < std::uncaught_exceptions(); },
96+
ReleasableAction{}};
97+
}
98+
99+
} // namespace beman::scope::demo

0 commit comments

Comments
 (0)