aboutsummaryrefslogtreecommitdiffstats
path: root/util/generic/scope.h
blob: a54ffc26447be6f05e6bad3dc3e1acd32dfc9f3b (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
#pragma once

#include <util/system/compiler.h>
#include <util/system/defaults.h>

#include <utility>

namespace NPrivate {
    template <typename F>
    class TScopeGuard {
    public:
        TScopeGuard(const F& function)
            : Function_{function}
        {
        }

        TScopeGuard(F&& function)
            : Function_{std::move(function)}
        {
        }

        TScopeGuard(TScopeGuard&&) = default;
        TScopeGuard(const TScopeGuard&) = default;

        ~TScopeGuard() {
            Function_();
        }

    private:
        F Function_;
    };

    struct TMakeGuardHelper {
        template <class F>
        TScopeGuard<F> operator|(F&& function) const {
            return std::forward<F>(function);
        }
    };
}

// \brief `Y_SCOPE_EXIT(captures) { body };` 
// 
// General implementaion of RAII idiom (resource acquisition is initialization). Executes 
// function upon return from the current scope. 
// 
// @note expects `body` to provide no-throw guarantee, otherwise whenever an exception
// is thrown and leaves the outermost block of `body`, the function `std::terminate` is called.
// @see http://drdobbs.com/184403758 for detailed motivation. 
#define Y_SCOPE_EXIT(...) const auto Y_GENERATE_UNIQUE_ID(scopeGuard) Y_DECLARE_UNUSED = ::NPrivate::TMakeGuardHelper{} | [__VA_ARGS__]() mutable -> void
 
// \brief `Y_DEFER { body };` 
// 
// Same as `Y_SCOPE_EXIT` but doesn't require user to provide capture-list explicitly (it 
// implicitly uses `[&]` capture). Have same requirements for `body`.
// 
// Inspired by `defer` statement in languages like Swift and Go. 
// 
// \code 
// auto item = s.pop(); 
// bool ok = false; 
// Y_DEFER { if (!ok) { s.push(std::move(item)); } }; 
// ... try handle `item` ... 
// ok = true; 
// \endcode 
#define Y_DEFER Y_SCOPE_EXIT(&)