tfrun

An easy-to-use C++ wrapper over the stable C API of TensorFlow
git clone https://0xff.ir/g/tfrun.git
Log | Files | Refs | README | LICENSE

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() + [&]()