Skip to content

Commit 12c9213

Browse files
First implementation of scope_guard. Something to keep working on. Missing "release" functionality.
1 parent 37abed8 commit 12c9213

File tree

2 files changed

+155
-67
lines changed

2 files changed

+155
-67
lines changed

examples/scope_example.cpp

Lines changed: 20 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -10,55 +10,56 @@ namespace scope = beman::scope;
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 function: " << (exit_status ? "invoked" : "not invoked") << "\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

2324
int main() {
24-
bool exit_status{false}, did_throw{false};
25+
bool is_exit_func_invoked{false}, is_exception_thrown{false};
2526

2627
// Manual handling at "end of scope"
2728
try {
2829
maybe_throw();
29-
exit_status = true;
30+
is_exit_func_invoked = true;
3031
} catch (...) {
31-
did_throw = true;
32+
is_exception_thrown = true;
3233
}
33-
print_exit_status("Manual handling", exit_status, did_throw);
34+
print_exit_status("Manual handling", is_exit_func_invoked, is_exception_thrown);
3435

3536
// Using scope_exit: runs on scope exit (success or exception)
36-
exit_status = did_throw = false;
37+
is_exit_func_invoked = is_exception_thrown = false;
3738
try {
38-
auto guard = scope::scope_exit{[&] { exit_status = true; }};
39+
auto guard = scope::scope_exit{[&] { is_exit_func_invoked = true; }};
3940
maybe_throw();
4041
} catch (...) {
41-
did_throw = true;
42+
is_exception_thrown = true;
4243
}
43-
print_exit_status("scope_exit", exit_status, did_throw);
44+
print_exit_status("scope_exit", is_exit_func_invoked, is_exception_thrown);
4445

4546
// Using scope_fail: runs only if an exception occurs
46-
exit_status = did_throw = false;
47+
is_exit_func_invoked = is_exception_thrown = false;
4748
try {
48-
auto guard = scope::scope_fail{[&] { exit_status = true; }};
49+
auto guard = scope::scope_fail{[&] { is_exit_func_invoked = true; }};
4950
maybe_throw();
5051
} catch (...) {
51-
did_throw = true;
52+
is_exception_thrown = true;
5253
}
53-
print_exit_status("scope_fail", exit_status, did_throw);
54+
print_exit_status("scope_fail", is_exit_func_invoked, is_exception_thrown);
5455

5556
// Using scope_success: runs only if no exception occurs
56-
exit_status = did_throw = false;
57+
is_exit_func_invoked = is_exception_thrown = false;
5758
try {
58-
auto guard = scope::scope_success{[&] { exit_status = true; }};
59+
auto guard = scope::scope_success{[&] { is_exit_func_invoked = true; }};
5960
maybe_throw();
6061
} catch (...) {
61-
did_throw = true;
62+
is_exception_thrown = true;
6263
}
63-
print_exit_status("scope_success", exit_status, did_throw);
64+
print_exit_status("scope_success", is_exit_func_invoked, is_exception_thrown);
6465
}

include/beman/scope/scope.hpp

Lines changed: 135 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -3,34 +3,17 @@
33
#ifndef BEMAN_SCOPE_HPP
44
#define BEMAN_SCOPE_HPP
55

6+
#include <concepts>
7+
#include <exception>
8+
69
namespace beman::scope {
710

811
// -- 7.6.7 Feature test macro --
912
//
1013
// __cpp_lib_scope
1114
//
1215

13-
// -- 7.5.1 Header <scope> synopsis [scope.syn] --
14-
//
15-
// namespace std {
16-
// template <class EF>
17-
// class scope_exit;
18-
//
19-
// template <class EF>
20-
// class scope_fail;
21-
//
22-
// template <class EF>
23-
// class scope_success;
24-
//
25-
// template <class R, class D>
26-
// class unique_resource;
27-
//
28-
// // factory function
29-
// template <class R, class D, class S = decay_t<R>>
30-
// unique_resource<decay_t<R>, decay_t<D>>
31-
// make_unique_resource_checked(R&& r, const S& invalid, D&& d) noexcept(see below);
32-
// } // namespace std
33-
//
16+
//=========================================================
3417

3518
// -- 7.5.2 Scope guard class templates [scope.scope_guard] --
3619
//
@@ -60,6 +43,137 @@ namespace beman::scope {
6043
// scope_guard(EF) -> scope_guard<EF>;
6144
//
6245

46+
//=========================================================
47+
48+
template <typename F, typename R, typename... Args>
49+
concept invocable_return = std::invocable<F, Args...> && std::same_as<std::invoke_result_t<F, Args...>, R>;
50+
51+
//=========================================================
52+
53+
struct ExecuteAlways;
54+
55+
//=========================================================
56+
57+
template <invocable_return<void> ExitFunc, invocable_return<bool> ExecuteCondition = ExecuteAlways>
58+
class scope_guard {
59+
public:
60+
explicit scope_guard(ExitFunc&& func) /*noexcept(see below)*/
61+
: m_exit_func(std::move(func)) //
62+
{}
63+
64+
scope_guard(scope_guard&& rhs) noexcept
65+
: m_exit_func{std::move(rhs)}, m_invoke_condition_func{std::move(rhs.m_invoke_condition_func)} {}
66+
67+
scope_guard(const scope_guard&) = delete;
68+
scope_guard& operator=(const scope_guard&) = delete;
69+
scope_guard& operator=(scope_guard&&) = delete;
70+
71+
~scope_guard() /*noexcept(see below)*/ {
72+
if (can_invoke_check(m_invoke_condition_func)) {
73+
m_exit_func();
74+
}
75+
}
76+
77+
void release() noexcept {
78+
// Needs implementation
79+
}
80+
81+
private:
82+
ExitFunc m_exit_func;
83+
[[no_unique_address]] ExecuteCondition m_invoke_condition_func;
84+
85+
template <typename T>
86+
bool can_invoke_check(const T& obj) const {
87+
if constexpr (requires(T /*t*/) {
88+
{ T::can_invoke() } -> std::convertible_to<bool>;
89+
}) {
90+
return T::can_invoke();
91+
} else if constexpr (requires(T t) {
92+
{ t.can_invoke() } -> std::convertible_to<bool>;
93+
}) {
94+
return obj.can_invoke();
95+
} else if constexpr (requires {
96+
{ T::operator()() } -> std::convertible_to<bool>;
97+
}) {
98+
return T::operator()();
99+
} else if constexpr (requires(T t) {
100+
{ t.operator()() } -> std::convertible_to<bool>;
101+
}) {
102+
return obj();
103+
} else {
104+
return true; // Default behavior if no check function is available
105+
}
106+
}
107+
};
108+
109+
//=========================================================
110+
111+
template <std::invocable ExitFunc>
112+
scope_guard(ExitFunc) -> scope_guard<ExitFunc>;
113+
114+
//=========================================================
115+
116+
// -- 7.5.1 Header <scope> synopsis [scope.syn] --
117+
//
118+
// namespace std {
119+
// template <class EF>
120+
// class scope_exit;
121+
//
122+
// template <class EF>
123+
// class scope_fail;
124+
//
125+
// template <class EF>
126+
// class scope_success;
127+
128+
//=========================================================
129+
130+
struct ExecuteAlways {
131+
[[nodiscard]] constexpr bool operator()() const { return true; }
132+
};
133+
134+
struct ExecuteWhenNoException {
135+
136+
[[nodiscard]] bool operator()() const { return uncaught_on_creation >= std::uncaught_exceptions(); }
137+
138+
private:
139+
int uncaught_on_creation = std::uncaught_exceptions();
140+
};
141+
142+
struct ExecuteOnlyWhenException {
143+
144+
[[nodiscard]] bool operator()() const { return uncaught_on_creation < std::uncaught_exceptions(); }
145+
146+
private:
147+
int uncaught_on_creation = std::uncaught_exceptions();
148+
};
149+
150+
//=========================================================
151+
152+
template <class ExitFunc>
153+
using scope_exit = scope_guard<ExitFunc>;
154+
155+
template <class ExitFunc>
156+
using scope_success = scope_guard<ExitFunc, ExecuteWhenNoException>;
157+
158+
template <class ExitFunc>
159+
using scope_fail = scope_guard<ExitFunc, ExecuteOnlyWhenException>;
160+
161+
//=========================================================
162+
163+
//
164+
// template <class R, class D>
165+
// class unique_resource;
166+
//
167+
// // factory function
168+
// template <class R, class D, class S = decay_t<R>>
169+
// unique_resource<decay_t<R>, decay_t<D>>
170+
// make_unique_resource_checked(R&& r, const S& invalid, D&& d) noexcept(see below);
171+
// } // namespace std
172+
//
173+
174+
//=========================================================
175+
176+
63177
// -- 7.6.1 Class template unique_resource [scope.unique_resource.class] --
64178
//
65179
// template <class R, class D>
@@ -93,33 +207,6 @@ namespace beman::scope {
93207
// template <typename R, typename D>
94208
// unique_resource(R, D) -> unique_resource<R, D>;
95209

96-
// TODO: Implement
97-
struct scope_exit {
98-
template <typename F>
99-
scope_exit(F) {}
100-
~scope_exit() {
101-
// TODO: Cleanup
102-
}
103-
};
104-
105-
// TODO: Implement
106-
struct scope_fail {
107-
template <typename F>
108-
scope_fail(F) {}
109-
~scope_fail() {
110-
// TODO: Cleanup
111-
}
112-
};
113-
114-
// TODO: Implement
115-
struct scope_success {
116-
template <typename F>
117-
scope_success(F) {}
118-
~scope_success() {
119-
// TODO: Cleanup
120-
}
121-
};
122-
123210
} // namespace beman::scope
124211

125212
#endif // BEMAN_SCOPE_HPP

0 commit comments

Comments
 (0)