summaryrefslogtreecommitdiff
path: root/zen/scope_guard.h
blob: 853f51b9bed6ec3782536b77f3481cad327b1d66 (plain)
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
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
// *****************************************************************************
// * This file is part of the FreeFileSync project. It is distributed under    *
// * GNU General Public License: http://www.gnu.org/licenses/gpl-3.0           *
// * Copyright (C) Zenju (zenju AT freefilesync DOT org) - All Rights Reserved *
// *****************************************************************************

#ifndef SCOPE_GUARD_H_8971632487321434
#define SCOPE_GUARD_H_8971632487321434

#include <cassert>
#include <exception>
#include "type_tools.h"


#ifdef ZEN_WIN
inline int getUncaughtExceptionCount() { return std::uncaught_exceptions(); }

#elif defined ZEN_LINUX || defined ZEN_MAC
//std::uncaught_exceptions() currently unsupported on GCC and Clang => clean up ASAP
#ifdef ZEN_LINUX
    static_assert(__GNUC__ < 6 || (__GNUC__ == 6 && (__GNUC_MINOR__ < 2 || (__GNUC_MINOR__ == 2 && __GNUC_PATCHLEVEL__ <= 1))), "check std::uncaught_exceptions support");
#else //std::uncaught_exceptions() requires "mmacosx-version-min=10.12"
    static_assert(__clang_major__ < 8 || (__clang_major__ == 8 && __clang_minor__ <= 0), "check std::uncaught_exceptions support");
#endif

namespace __cxxabiv1
{
struct __cxa_eh_globals;
extern "C" __cxa_eh_globals* __cxa_get_globals() noexcept;
}

inline int getUncaughtExceptionCount()
{
    return *(reinterpret_cast<unsigned int*>(static_cast<char*>(static_cast<void*>(__cxxabiv1::__cxa_get_globals())) + sizeof(void*)));
}
#endif

//best of Zen, Loki and C++17

namespace zen
{
//Scope Guard
/*
    auto guardAio = zen::makeGuard<ScopeGuardRunMode::ON_EXIT>([&] { ::CloseHandle(hDir); });
        ...
    guardAio.dismiss();

Scope Exit:
    ZEN_ON_SCOPE_EXIT(::CloseHandle(hDir));
    ZEN_ON_SCOPE_FAIL(UndoPreviousWork());
    ZEN_ON_SCOPE_SUCCESS(NotifySuccess());
*/

enum class ScopeGuardRunMode
{
    ON_EXIT,
    ON_SUCCESS,
    ON_FAIL
};


//partially specialize scope guard destructor code and get rid of those pesky MSVC "4127 conditional expression is constant"
template <typename F> inline
void runScopeGuardDestructor(F& fun, int /*exeptionCountOld*/, StaticEnum<ScopeGuardRunMode, ScopeGuardRunMode::ON_EXIT>)
{
    try { fun(); }
    catch (...) { assert(false); } //consistency: don't expect exceptions for ON_EXIT even if "!failed"!
}


template <typename F> inline
void runScopeGuardDestructor(F& fun, int exeptionCountOld, StaticEnum<ScopeGuardRunMode, ScopeGuardRunMode::ON_SUCCESS>)
{
    const bool failed = getUncaughtExceptionCount() > exeptionCountOld;
    if (!failed)
        fun(); //throw X
}


template <typename F> inline
void runScopeGuardDestructor(F& fun, int exeptionCountOld, StaticEnum<ScopeGuardRunMode, ScopeGuardRunMode::ON_FAIL>)
{
    const bool failed = getUncaughtExceptionCount() > exeptionCountOld;
    if (failed)
        try { fun(); }
        catch (...) { assert(false); }
}


template <ScopeGuardRunMode runMode, typename F>
class ScopeGuard
{
public:
    explicit ScopeGuard(const F&  fun) : fun_(fun) {}
    explicit ScopeGuard(      F&& fun) : fun_(std::move(fun)) {}

    ScopeGuard(ScopeGuard&& other) : fun_(std::move(other.fun_)),
        exeptionCount_(other.exeptionCount_),
        dismissed_(other.dismissed_) { other.dismissed_ = true; }

    ~ScopeGuard() noexcept(runMode != ScopeGuardRunMode::ON_SUCCESS)
    {
        if (!dismissed_)
            runScopeGuardDestructor(fun_, exeptionCount_, StaticEnum<ScopeGuardRunMode, runMode>());
    }

    void dismiss() { dismissed_ = true; }

private:
    ScopeGuard           (const ScopeGuard&) = delete;
    ScopeGuard& operator=(const ScopeGuard&) = delete;

    F fun_;
    const int exeptionCount_ = getUncaughtExceptionCount();
    bool dismissed_ = false;
};


template <ScopeGuardRunMode runMode, class F> inline
auto makeGuard(F&& fun) { return ScopeGuard<runMode, std::decay_t<F>>(std::forward<F>(fun)); }
}

#define ZEN_CONCAT_SUB(X, Y) X ## Y
#define ZEN_CONCAT(X, Y) ZEN_CONCAT_SUB(X, Y)

#define ZEN_ON_SCOPE_EXIT(X)    auto ZEN_CONCAT(dummy, __LINE__) = zen::makeGuard<zen::ScopeGuardRunMode::ON_EXIT   >([&]{ X; }); (void)ZEN_CONCAT(dummy, __LINE__);
#define ZEN_ON_SCOPE_FAIL(X)    auto ZEN_CONCAT(dummy, __LINE__) = zen::makeGuard<zen::ScopeGuardRunMode::ON_FAIL   >([&]{ X; }); (void)ZEN_CONCAT(dummy, __LINE__);
#define ZEN_ON_SCOPE_SUCCESS(X) auto ZEN_CONCAT(dummy, __LINE__) = zen::makeGuard<zen::ScopeGuardRunMode::ON_SUCCESS>([&]{ X; }); (void)ZEN_CONCAT(dummy, __LINE__);

#endif //SCOPE_GUARD_H_8971632487321434
bgstack15