• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

bemanproject / scope / 22527042229

28 Feb 2026 07:03PM UTC coverage: 96.296% (-3.7%) from 100.0%
22527042229

Pull #53

github

web-flow
Merge 6b5e4008c into 307ce9544
Pull Request #53: Feature/add windows ci

26 of 27 new or added lines in 1 file covered. (96.3%)

26 of 27 relevant lines covered (96.3%)

1.93 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

96.3
/include/beman/scope/scope_impl.hpp
1
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
2

3
#ifndef SCOPE_IMPL_HPP
4
#define SCOPE_IMPL_HPP
5

6
#define BEMAN_SCOPE_USE_FALLBACK
7

8
#ifndef BEMAN_SCOPE_IMPORT_STD
9
    #include <version>
10

11
    #if defined(__cpp_concepts) && __cpp_concepts >= 201907L
12
        // C++20 concepts supported
13
    #elif __cplusplus < 202002L
14
        #error "C++20 or later is required"
15
    #endif
16

17
    // detect standard header first, then experimental, otherwise use local implementation
18
    #ifndef BEMAN_SCOPE_USE_FALLBACK
19
        #ifdef __has_include
20
            #if __has_include(<scope>)
21
                #include <scope>
22
                #define BEMAN_SCOPE_USE_STD
23
            // XXX #warning "Set BEMAN_SCOPE_USE_STD"
24
            #elif __has_include(<experimental/scope>)
25
                #include <experimental/scope>
26
                #define BEMAN_SCOPE_USE_STD_EXPERIMENTAL
27
            // XXX #warning "Set BEMAN_SCOPE_USE_STD_EXPERIMENTAL"
28
            #else
29
                #define BEMAN_SCOPE_USE_FALLBACK
30
            #endif
31
        #else
32
            #define BEMAN_SCOPE_USE_FALLBACK
33
        #endif
34
    #endif
35

36
    #ifdef BEMAN_SCOPE_USE_STD
37
        #if !defined(__cpp_lib_scope_exit)
38
            #error "Standard <scope> present but __cpp_lib_scope_exit not defined"
39
        #endif
40
    #endif
41
#endif
42

43
#ifdef BEMAN_SCOPE_USE_FALLBACK
44
    #if __has_include("beman/scope/modules_export.hpp")
45
        #include "beman/scope/modules_export.hpp"
46
    #else
47
        #define BEMAN_SCOPE_EXPORT
48
    #endif
49

50
    #ifndef BEMAN_SCOPE_IMPORT_STD
51
        #include <exception>
52
        #include <type_traits>
53
        #include <utility>
54
    #endif
55

56
namespace beman::scope {
57

58
// TODO(CK): make a std::experimental::scope_exit<EF>::scope_exit conform
59
// implementation
60
template <class F>
61
class [[nodiscard]] BEMAN_SCOPE_EXPORT scope_exit {
62
    F    f;
63
    bool active = true;
64

65
  public:
66
    constexpr explicit scope_exit(F func) noexcept(std::is_nothrow_move_constructible_v<F>) : f(std::move(func)) {}
2✔
67

68
    // Move constructor
69
    constexpr scope_exit(scope_exit&& other) noexcept(std::is_nothrow_move_constructible_v<F>)
70
        : f(std::move(other.f)), active(other.active) {
71
        other.active = false;
72
    }
73

74
    // Deleted copy
75
    auto operator=(const scope_exit&) -> scope_exit& = delete;
76
    scope_exit(const scope_exit&)                    = delete;
77

78
    // Deleted move assignment
79
    // Does scope_exit need to be move-assignable? LEWG: NO!
80
    constexpr auto operator=(scope_exit&& other) noexcept(std::is_nothrow_move_assignable_v<F>)
81
        -> scope_exit& = delete;
82

83
    // Destructor: call only if scope is exiting normally
84
    ~scope_exit() noexcept(noexcept(f())) {
2✔
85
        if (active) {
2✔
86
            f();
2✔
87
        }
88
    }
2✔
89

90
    // Release to prevent execution
91
    constexpr auto release() -> void { active = false; }
92

93
    // Helper to tests if active
94
    constexpr auto is_active() -> bool { return active; }
95
};
96

97
// Factory helper
98
// NOLINTNEXTLINE(misc-use-anonymous-namespace)
99
template <class F>
100
auto make_scope_exit(F f) -> scope_exit<F> {
101
    return scope_exit<F>(std::move(f));
102
}
103

104
// TODO(CK): make a std::experimental::scope_fail<EF>::scope_fail conform
105
// implementation
106
template <typename F>
107
class [[nodiscard]] BEMAN_SCOPE_EXPORT scope_fail {
108
    F    f;
109
    bool active = true;
110
    int  exception_count{};
111

112
  public:
113
    // Constructor: capture current uncaught exceptions
114
    constexpr explicit scope_fail(F func) noexcept(std::is_nothrow_move_constructible_v<F>)
2✔
115
        : f(std::move(func)), exception_count(std::uncaught_exceptions()) {}
2✔
116

117
    // Move constructor
118
    constexpr scope_fail(scope_fail&& other) noexcept(std::is_nothrow_move_constructible_v<F>)
119
        : f(std::move(other.f)), active(other.active), exception_count(other.exception_count) {
120
        other.active = false;
121
    }
122

123
    // Deleted copy
124
    scope_fail(const scope_fail&)                    = delete;
125
    auto operator=(const scope_fail&) -> scope_fail& = delete;
126

127
    // Deleted move assignment
128
    // Move assignment
129
    constexpr auto operator=(scope_fail&& other) noexcept(std::is_nothrow_move_assignable_v<F>)
130
        -> scope_fail& = delete;
131
    #if MOVE_ASSIGNMENT_NEEDED
132
    G {
133
        if (this != &other) {
134
            f               = std::move(other.f);
135
            active          = other.active;
136
            exception_count = other.exception_count;
137
            other.active    = false;
138
        }
139
        return *this;
140
    }
141
    #endif
142

143
    // Destructor: call if scope is exiting due to an exception
144
    ~scope_fail() noexcept(noexcept(f())) {
2✔
145
        if (active && std::uncaught_exceptions() > exception_count) {
2✔
NEW
146
            f();
×
147
        }
148
    }
2✔
149

150
    // Release to prevent execution
151
    constexpr auto release() -> void { active = false; }
152

153
    // Helper to tests if active
154
    constexpr auto is_active() -> bool { return active; }
155
};
156

157
// Factory helper
158
// NOLINTNEXTLINE(misc-use-anonymous-namespace)
159
template <typename F>
160
constexpr auto make_scope_fail(F&& f) -> scope_fail<std::decay_t<F>> {
161
    return scope_fail<std::decay_t<F>>(std::forward<F>(f));
162
}
163

164
// TODO(CK): make a std::experimental::scope_success<EF>::scope_success conform
165
// implementation
166
template <typename F>
167
class [[nodiscard]] BEMAN_SCOPE_EXPORT scope_success {
168
    F    f;
169
    bool active = true;
170
    int  exception_count{};
171

172
  public:
173
    // Constructor: capture current uncaught exceptions
174
    constexpr explicit scope_success(F func) noexcept(std::is_nothrow_move_constructible_v<F>)
2✔
175
        : f(std::move(func)), exception_count(std::uncaught_exceptions()) {}
2✔
176

177
    // Move constructor
178
    constexpr scope_success(scope_success&& other) noexcept(std::is_nothrow_move_constructible_v<F>)
179
        : f(std::move(other.f)), active(other.active), exception_count(other.exception_count) {
180
        other.active = false;
181
    }
182

183
    // Deleted copy
184
    scope_success(const scope_success&)                    = delete;
185
    auto operator=(const scope_success&) -> scope_success& = delete;
186

187
    // Deleted move assignment
188
    // Move assignment
189
    constexpr auto operator=(scope_success&& other) noexcept(std::is_nothrow_move_assignable_v<F>)
190
        -> scope_success& = delete;
191
    #if MOVE_ASSIGNMENT_NEEDED
192
    {
193
        if (this != &other) {
194
            f               = std::move(other.f);
195
            active          = other.active;
196
            exception_count = other.exception_count;
197
            other.active    = false;
198
        }
199
        return *this;
200
    }
201
    #endif
202

203
    // Destructor: call only if scope is exiting normally
204
    ~scope_success() noexcept(noexcept(f())) {
2✔
205
        if (active && std::uncaught_exceptions() == exception_count) {
2✔
206
            f();
2✔
207
        }
208
    }
2✔
209

210
    // Release to prevent execution
211
    constexpr auto release() -> void { active = false; }
212

213
    // Helper to tests if active
214
    constexpr auto is_active() -> bool { return active; }
215
};
216

217
// Factory helper
218
// NOLINTNEXTLINE(misc-use-anonymous-namespace)
219
template <typename F>
220
constexpr auto make_scope_success(F&& f) -> scope_success<std::decay_t<F>> {
221
    return scope_success<std::decay_t<F>>(std::forward<F>(f));
222
}
223

224
template <typename Resource, typename Deleter>
225
class [[nodiscard]] BEMAN_SCOPE_EXPORT unique_resource {
226
    Resource resource;
227
    Deleter  deleter;
228
    bool     active = true;
229

230
  public:
231
    // Constructor
232
    constexpr unique_resource(Resource r, Deleter d) noexcept(std::is_nothrow_move_constructible_v<Deleter>)
2✔
233
        : resource(std::move(r)), deleter(std::move(d)) {}
2✔
234

235
    // Move constructor
236
    constexpr unique_resource(unique_resource&& other) noexcept(std::is_nothrow_move_constructible_v<Deleter>)
237
        : resource(std::move(other.resource)), deleter(std::move(other.deleter)) {
238
        active = std::exchange(other.active, false);
239
    }
240

241
    // Move assignment
242
    constexpr auto operator=(unique_resource&& other) noexcept(std::is_nothrow_move_assignable_v<Deleter>)
243
        -> unique_resource& {
244
        if (this != &other) {
245
            reset(std::move(other.resource));
246
            deleter = std::move(other.deleter);
247
            active  = std::exchange(other.active, false);
248
        }
249
        return *this;
250
    }
251

252
    // Deleted copy operations
253
    unique_resource(const unique_resource&)                    = delete;
254
    auto operator=(const unique_resource&) -> unique_resource& = delete;
255

256
    // Destructor
257
    ~unique_resource() noexcept(noexcept(deleter(resource))) { reset(); }
2✔
258

259
    // Release ownership
260
    constexpr void release() noexcept { active = false; }
261

262
    // Reset resource
263
    constexpr void reset() noexcept(noexcept(deleter(resource))) {
2✔
264
        if (active) {
2✔
265
            active = false;
2✔
266
            deleter(resource);
2✔
267
        }
268
    }
2✔
269

270
    // Reset the resource and call deleter if engaged
271
    constexpr void reset(Resource new_resource) noexcept(noexcept(deleter(resource))) {
272
        if (active) {
273
            deleter(resource);
274
        }
275
        resource = std::move(new_resource);
276
        active   = true;
277
    }
278

279
    // Accessors
280
    constexpr auto get() const -> const Resource& { return resource; }
281
    constexpr auto get() -> Resource& { return resource; }
282

283
    // operator* — only for non-void pointer resources
284
    constexpr auto operator*() const noexcept -> std::add_lvalue_reference_t<std::remove_pointer_t<Resource>>
285
        requires(std::is_pointer_v<Resource> && !std::is_void_v<std::remove_pointer_t<Resource>>)
286
    {
287
        return *resource;
288
    }
289

290
    // Optional pointer convenience
291
    constexpr auto operator->() const noexcept -> Resource
2✔
292
        requires std::is_pointer_v<Resource>
293
    {
294
        return resource;
2✔
295
    }
296

297
    // TODO(CK): missing usecase?
298
    constexpr auto get_deleter() const noexcept -> Deleter;
299

300
    // Helper to tests is_active()
301
    // NOTE: check if active; not required from LWG?
302
    constexpr explicit operator bool() const noexcept { return active; }
303
};
304

305
// Deduction guide
306
template <typename Resource, typename Deleter>
307
unique_resource(Resource&&, Deleter&&) -> unique_resource<std::decay_t<Resource>, std::decay_t<Deleter>>;
308

309
// Factory: conditionally engaged
310
// NOLINTNEXTLINE(misc-use-anonymous-namespace)
311
template <class R, class Invalid, class D>
312
constexpr auto make_unique_resource_checked(R&& r, const Invalid& invalid, D&& d) {
313
    using resource_type = std::decay_t<R>;
314
    using deleter_type  = std::decay_t<D>;
315

316
    unique_resource<resource_type, deleter_type> ur(resource_type{}, std::forward<D>(d));
317
    if (r == invalid) {
318
        ur.release(); // disengage immediately
319
    }
320
    return ur;
321
}
322

323
} // namespace beman::scope
324

325
#elifdef BEMAN_SCOPE_USE_STD_EXPERIMENTAL
326

327
namespace beman::scope {
328
using ::std::experimental::scope_exit;
329
using ::std::experimental::scope_fail;
330
using ::std::experimental::scope_success;
331
using ::std::experimental::unique_resource;
332
} // namespace beman::scope
333
  //
334
#endif // BEMAN_SCOPE_USE_FALLBACK
335

336
#endif // SCOPE_IMPL_HPP
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc