1 // *****************************************************************************
2 // * This file is part of the FreeFileSync project. It is distributed under    *
3 // * GNU General Public License: http://www.gnu.org/licenses/gpl-3.0           *
4 // * Copyright (C) Zenju (zenju AT freefilesync DOT org) - All Rights Reserved *
5 // *****************************************************************************
6 
7 #ifndef SCOPE_GUARD_H_8971632487321434
8 #define SCOPE_GUARD_H_8971632487321434
9 
10 #include <cassert>
11 #include <exception>
12 #include "type_tools.h"
13 
14 
15 #ifdef ZEN_WIN
getUncaughtExceptionCount()16 inline int getUncaughtExceptionCount() { return std::uncaught_exceptions(); }
17 
18 #elif defined ZEN_LINUX || defined ZEN_MAC
19 //std::uncaught_exceptions() currently unsupported on GCC and Clang => clean up ASAP
20 #ifdef ZEN_LINUX
21     static_assert(__GNUC__ < 6 || (__GNUC__ == 6 && (__GNUC_MINOR__ < 2 || (__GNUC_MINOR__ == 2 && __GNUC_PATCHLEVEL__ <= 1))), "check std::uncaught_exceptions support");
22 #else //std::uncaught_exceptions() requires "mmacosx-version-min=10.12"
23     static_assert(__clang_major__ < 8 || (__clang_major__ == 8 && __clang_minor__ <= 0), "check std::uncaught_exceptions support");
24 #endif
25 
26 namespace __cxxabiv1
27 {
28 struct __cxa_eh_globals;
29 extern "C" __cxa_eh_globals* __cxa_get_globals() noexcept;
30 }
31 
getUncaughtExceptionCount()32 inline int getUncaughtExceptionCount()
33 {
34     return *(reinterpret_cast<unsigned int*>(static_cast<char*>(static_cast<void*>(__cxxabiv1::__cxa_get_globals())) + sizeof(void*)));
35 }
36 #endif
37 
38 //best of Zen, Loki and C++17
39 
40 namespace zen
41 {
42 //Scope Guard
43 /*
44     auto guardAio = zen::makeGuard<ScopeGuardRunMode::ON_EXIT>([&] { ::CloseHandle(hDir); });
45         ...
46     guardAio.dismiss();
47 
48 Scope Exit:
49     ZEN_ON_SCOPE_EXIT(::CloseHandle(hDir));
50     ZEN_ON_SCOPE_FAIL(UndoPreviousWork());
51     ZEN_ON_SCOPE_SUCCESS(NotifySuccess());
52 */
53 
54 enum class ScopeGuardRunMode
55 {
56     ON_EXIT,
57     ON_SUCCESS,
58     ON_FAIL
59 };
60 
61 
62 //partially specialize scope guard destructor code and get rid of those pesky MSVC "4127 conditional expression is constant"
63 template <typename F> inline
runScopeGuardDestructor(F & fun,int,StaticEnum<ScopeGuardRunMode,ScopeGuardRunMode::ON_EXIT>)64 void runScopeGuardDestructor(F& fun, int /*exeptionCountOld*/, StaticEnum<ScopeGuardRunMode, ScopeGuardRunMode::ON_EXIT>)
65 {
66     try { fun(); }
67     catch (...) { assert(false); } //consistency: don't expect exceptions for ON_EXIT even if "!failed"!
68 }
69 
70 
71 template <typename F> inline
runScopeGuardDestructor(F & fun,int exeptionCountOld,StaticEnum<ScopeGuardRunMode,ScopeGuardRunMode::ON_SUCCESS>)72 void runScopeGuardDestructor(F& fun, int exeptionCountOld, StaticEnum<ScopeGuardRunMode, ScopeGuardRunMode::ON_SUCCESS>)
73 {
74     const bool failed = getUncaughtExceptionCount() > exeptionCountOld;
75     if (!failed)
76         fun(); //throw X
77 }
78 
79 
80 template <typename F> inline
runScopeGuardDestructor(F & fun,int exeptionCountOld,StaticEnum<ScopeGuardRunMode,ScopeGuardRunMode::ON_FAIL>)81 void runScopeGuardDestructor(F& fun, int exeptionCountOld, StaticEnum<ScopeGuardRunMode, ScopeGuardRunMode::ON_FAIL>)
82 {
83     const bool failed = getUncaughtExceptionCount() > exeptionCountOld;
84     if (failed)
85         try { fun(); }
86         catch (...) { assert(false); }
87 }
88 
89 
90 template <ScopeGuardRunMode runMode, typename F>
91 class ScopeGuard
92 {
93 public:
ScopeGuard(const F & fun)94     explicit ScopeGuard(const F&  fun) : fun_(fun) {}
ScopeGuard(F && fun)95     explicit ScopeGuard(      F&& fun) : fun_(std::move(fun)) {}
96 
ScopeGuard(ScopeGuard && other)97     ScopeGuard(ScopeGuard&& other) : fun_(std::move(other.fun_)),
98         exeptionCount_(other.exeptionCount_),
99         dismissed_(other.dismissed_) { other.dismissed_ = true; }
100 
101     ~ScopeGuard() noexcept(runMode != ScopeGuardRunMode::ON_SUCCESS)
102     {
103         if (!dismissed_)
104             runScopeGuardDestructor(fun_, exeptionCount_, StaticEnum<ScopeGuardRunMode, runMode>());
105     }
106 
dismiss()107     void dismiss() { dismissed_ = true; }
108 
109 private:
110     ScopeGuard           (const ScopeGuard&) = delete;
111     ScopeGuard& operator=(const ScopeGuard&) = delete;
112 
113     F fun_;
114     const int exeptionCount_ = getUncaughtExceptionCount();
115     bool dismissed_ = false;
116 };
117 
118 
119 template <ScopeGuardRunMode runMode, class F> inline
makeGuard(F && fun)120 auto makeGuard(F&& fun) { return ScopeGuard<runMode, std::decay_t<F>>(std::forward<F>(fun)); }
121 }
122 
123 #define ZEN_CONCAT_SUB(X, Y) X ## Y
124 #define ZEN_CONCAT(X, Y) ZEN_CONCAT_SUB(X, Y)
125 
126 #define ZEN_ON_SCOPE_EXIT(X)    auto ZEN_CONCAT(dummy, __LINE__) = zen::makeGuard<zen::ScopeGuardRunMode::ON_EXIT   >([&]{ X; }); (void)ZEN_CONCAT(dummy, __LINE__);
127 #define ZEN_ON_SCOPE_FAIL(X)    auto ZEN_CONCAT(dummy, __LINE__) = zen::makeGuard<zen::ScopeGuardRunMode::ON_FAIL   >([&]{ X; }); (void)ZEN_CONCAT(dummy, __LINE__);
128 #define ZEN_ON_SCOPE_SUCCESS(X) auto ZEN_CONCAT(dummy, __LINE__) = zen::makeGuard<zen::ScopeGuardRunMode::ON_SUCCESS>([&]{ X; }); (void)ZEN_CONCAT(dummy, __LINE__);
129 
130 #endif //SCOPE_GUARD_H_8971632487321434
131