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