scope_guard.hpp (9758B)
1 // Adapted from `ScopeGaurd.h` of [folly](https://github.com/facebook/folly) 2 3 /* 4 * Copyright 2017 Facebook, Inc. 5 * Copyright 2019-2021 Mohammad-Reza Nabipoor 6 * 7 * Licensed under the Apache License, Version 2.0 (the "License"); 8 * you may not use this file except in compliance with the License. 9 * You may obtain a copy of the License at 10 * 11 * http://www.apache.org/licenses/LICENSE-2.0 12 * 13 * Unless required by applicable law or agreed to in writing, software 14 * distributed under the License is distributed on an "AS IS" BASIS, 15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 * See the License for the specific language governing permissions and 17 * limitations under the License. 18 */ 19 20 #pragma once 21 22 #include <cstddef> 23 #include <exception> 24 #include <functional> 25 #include <new> 26 #include <type_traits> 27 #include <utility> 28 29 // folly/CPortability.h 30 31 // always inline 32 #ifdef _MSC_VER 33 #define FOLLY_ALWAYS_INLINE __forceinline 34 #elif defined(__clang__) || defined(__GNUC__) 35 #define FOLLY_ALWAYS_INLINE inline __attribute__((__always_inline__)) 36 #else 37 #define FOLLY_ALWAYS_INLINE inline 38 #endif 39 40 // folly/Preprocessor.h 41 42 /** 43 * FB_ANONYMOUS_VARIABLE(str) introduces an identifier starting with 44 * str and ending with a number that varies with the line. 45 */ 46 #ifndef FB_ANONYMOUS_VARIABLE 47 #define FB_CONCATENATE_IMPL(s1, s2) s1##s2 48 #define FB_CONCATENATE(s1, s2) FB_CONCATENATE_IMPL(s1, s2) 49 #ifdef __COUNTER__ 50 #define FB_ANONYMOUS_VARIABLE(str) FB_CONCATENATE(str, __COUNTER__) 51 #else 52 #define FB_ANONYMOUS_VARIABLE(str) FB_CONCATENATE(str, __LINE__) 53 #endif 54 #endif 55 56 // changed from `folly` to `myfolly` 57 namespace myfolly { 58 59 /** 60 * ScopeGuard is a general implementation of the "Initialization is 61 * Resource Acquisition" idiom. Basically, it guarantees that a function 62 * is executed upon leaving the currrent scope unless otherwise told. 63 * 64 * The makeGuard() function is used to create a new ScopeGuard object. 65 * It can be instantiated with a lambda function, a std::function<void()>, 66 * a functor, or a void(*)() function pointer. 67 * 68 * 69 * Usage example: Add a friend to memory if and only if it is also added 70 * to the db. 71 * 72 * void User::addFriend(User& newFriend) { 73 * // add the friend to memory 74 * friends_.push_back(&newFriend); 75 * 76 * // If the db insertion that follows fails, we should 77 * // remove it from memory. 78 * // (You could also declare this as "auto guard = makeGuard(...)") 79 * ScopeGuard guard = makeGuard([&] { friends_.pop_back(); }); 80 * 81 * // this will throw an exception upon error, which 82 * // makes the ScopeGuard execute UserCont::pop_back() 83 * // once the Guard's destructor is called. 84 * db_->addFriend(GetName(), newFriend.GetName()); 85 * 86 * // an exception was not thrown, so don't execute 87 * // the Guard. 88 * guard.dismiss(); 89 * } 90 * 91 * Examine ScopeGuardTest.cpp for some more sample usage. 92 * 93 * Stolen from: 94 * Andrei's and Petru Marginean's CUJ article: 95 * http://drdobbs.com/184403758 96 * and the loki library: 97 * http://loki-lib.sourceforge.net/index.php?n=Idioms.ScopeGuardPointer 98 * and triendl.kj article: 99 * http://www.codeproject.com/KB/cpp/scope_guard.aspx 100 */ 101 class ScopeGuardImplBase 102 { 103 public: 104 void dismiss() noexcept { dismissed_ = true; } 105 106 template<typename T> 107 FOLLY_ALWAYS_INLINE static void runAndWarnAboutToCrashOnException( 108 T& function) noexcept 109 { 110 try { 111 function(); 112 } catch (...) { 113 warnAboutToCrash(); 114 std::terminate(); 115 } 116 } 117 118 protected: 119 ScopeGuardImplBase() noexcept 120 : dismissed_(false) 121 {} 122 123 static ScopeGuardImplBase makeEmptyScopeGuard() noexcept 124 { 125 return ScopeGuardImplBase{}; 126 } 127 128 template<typename T> 129 static const T& asConst(const T& t) noexcept 130 { 131 return t; 132 } 133 134 bool dismissed_; 135 136 private: 137 static void warnAboutToCrash() noexcept {}; // FIXME(mrn): {} 138 }; 139 140 template<typename FunctionType> 141 class ScopeGuardImpl : public ScopeGuardImplBase 142 { 143 public: 144 explicit ScopeGuardImpl(FunctionType& fn) noexcept( 145 std::is_nothrow_copy_constructible<FunctionType>::value) 146 : ScopeGuardImpl( 147 asConst(fn), 148 makeFailsafe(std::is_nothrow_copy_constructible<FunctionType>{}, &fn)) 149 {} 150 151 explicit ScopeGuardImpl(const FunctionType& fn) noexcept( 152 std::is_nothrow_copy_constructible<FunctionType>::value) 153 : ScopeGuardImpl( 154 fn, 155 makeFailsafe(std::is_nothrow_copy_constructible<FunctionType>{}, &fn)) 156 {} 157 158 explicit ScopeGuardImpl(FunctionType&& fn) noexcept( 159 std::is_nothrow_move_constructible<FunctionType>::value) 160 : ScopeGuardImpl( 161 std::move_if_noexcept(fn), 162 makeFailsafe(std::is_nothrow_move_constructible<FunctionType>{}, &fn)) 163 {} 164 165 ScopeGuardImpl(ScopeGuardImpl&& other) noexcept( 166 std::is_nothrow_move_constructible<FunctionType>::value) 167 : function_(std::move_if_noexcept(other.function_)) 168 { 169 // If the above line attempts a copy and the copy throws, other is 170 // left owning the cleanup action and will execute it (or not) depending 171 // on the value of other.dismissed_. The following lines only execute 172 // if the move/copy succeeded, in which case *this assumes ownership of 173 // the cleanup action and dismisses other. 174 dismissed_ = other.dismissed_; 175 other.dismissed_ = true; 176 } 177 178 ~ScopeGuardImpl() noexcept 179 { 180 if (!dismissed_) { 181 execute(); 182 } 183 } 184 185 private: 186 static ScopeGuardImplBase makeFailsafe(std::true_type, const void*) noexcept 187 { 188 return makeEmptyScopeGuard(); 189 } 190 191 template<typename Fn> 192 static auto makeFailsafe(std::false_type, Fn* fn) noexcept 193 -> ScopeGuardImpl<decltype(std::ref(*fn))> 194 { 195 return ScopeGuardImpl<decltype(std::ref(*fn))>{ std::ref(*fn) }; 196 } 197 198 template<typename Fn> 199 explicit ScopeGuardImpl(Fn&& fn, ScopeGuardImplBase&& failsafe) 200 : ScopeGuardImplBase{} 201 , function_(std::forward<Fn>(fn)) 202 { 203 failsafe.dismiss(); 204 } 205 206 void* operator new(std::size_t) = delete; 207 208 void execute() noexcept { runAndWarnAboutToCrashOnException(function_); } 209 210 FunctionType function_; 211 }; 212 213 template<typename FunctionType> 214 ScopeGuardImpl<typename std::decay<FunctionType>::type> 215 makeGuard(FunctionType&& fn) noexcept( 216 std::is_nothrow_constructible<typename std::decay<FunctionType>::type, 217 FunctionType>::value) 218 { 219 return ScopeGuardImpl<typename std::decay<FunctionType>::type>( 220 std::forward<FunctionType>(fn)); 221 } 222 223 /** 224 * This is largely unneeded if you just use auto for your guards. 225 */ 226 typedef ScopeGuardImplBase&& ScopeGuard; 227 228 namespace detail { 229 230 /** 231 * ScopeGuard used for executing a function when leaving the current scope 232 * depending on the presence of a new uncaught exception. 233 * 234 * If the executeOnException template parameter is true, the function is 235 * executed if a new uncaught exception is present at the end of the scope. 236 * If the parameter is false, then the function is executed if no new uncaught 237 * exceptions are present at the end of the scope. 238 * 239 * Used to implement SCOPE_FAIL and SCOPE_SUCCESS below. 240 */ 241 template<typename FunctionType, bool ExecuteOnException> 242 class ScopeGuardForNewException 243 { 244 public: 245 explicit ScopeGuardForNewException(const FunctionType& fn) 246 : function_(fn) 247 {} 248 249 explicit ScopeGuardForNewException(FunctionType&& fn) 250 : function_(std::move(fn)) 251 {} 252 253 ScopeGuardForNewException(ScopeGuardForNewException&& other) = default; 254 255 ~ScopeGuardForNewException() noexcept(ExecuteOnException) 256 { 257 if (ExecuteOnException == 258 (exceptionCounter_ < std::uncaught_exceptions())) { 259 if (ExecuteOnException) { 260 ScopeGuardImplBase::runAndWarnAboutToCrashOnException(function_); 261 } else { 262 function_(); 263 } 264 } 265 } 266 267 private: 268 ScopeGuardForNewException(const ScopeGuardForNewException& other) = delete; 269 270 void* operator new(std::size_t) = delete; 271 272 FunctionType function_; 273 int exceptionCounter_{ std::uncaught_exceptions() }; 274 }; 275 276 /** 277 * Internal use for the macro SCOPE_FAIL below 278 */ 279 enum class ScopeGuardOnFail 280 { 281 }; 282 283 template<typename FunctionType> 284 ScopeGuardForNewException<typename std::decay<FunctionType>::type, true> 285 operator+(detail::ScopeGuardOnFail, FunctionType&& fn) 286 { 287 return ScopeGuardForNewException<typename std::decay<FunctionType>::type, 288 true>(std::forward<FunctionType>(fn)); 289 } 290 291 /** 292 * Internal use for the macro SCOPE_SUCCESS below 293 */ 294 enum class ScopeGuardOnSuccess 295 { 296 }; 297 298 template<typename FunctionType> 299 ScopeGuardForNewException<typename std::decay<FunctionType>::type, false> 300 operator+(ScopeGuardOnSuccess, FunctionType&& fn) 301 { 302 return ScopeGuardForNewException<typename std::decay<FunctionType>::type, 303 false>(std::forward<FunctionType>(fn)); 304 } 305 306 /** 307 * Internal use for the macro SCOPE_EXIT below 308 */ 309 enum class ScopeGuardOnExit 310 { 311 }; 312 313 template<typename FunctionType> 314 ScopeGuardImpl<typename std::decay<FunctionType>::type> 315 operator+(detail::ScopeGuardOnExit, FunctionType&& fn) 316 { 317 return ScopeGuardImpl<typename std::decay<FunctionType>::type>( 318 std::forward<FunctionType>(fn)); 319 } 320 } // namespace detail 321 322 } // namespace myfolly 323 324 #define SCOPE_EXIT \ 325 auto FB_ANONYMOUS_VARIABLE(SCOPE_EXIT_STATE) = \ 326 ::myfolly::detail::ScopeGuardOnExit() + [&]() noexcept 327 328 #define SCOPE_FAIL \ 329 auto FB_ANONYMOUS_VARIABLE(SCOPE_FAIL_STATE) = \ 330 ::myfolly::detail::ScopeGuardOnFail() + [&]() noexcept 331 332 #define SCOPE_SUCCESS \ 333 auto FB_ANONYMOUS_VARIABLE(SCOPE_SUCCESS_STATE) = \ 334 ::myfolly::detail::ScopeGuardOnSuccess() + [&]()