1 #ifndef PETSC_CPP_REGISTER_FINALIZE_HPP 2 #define PETSC_CPP_REGISTER_FINALIZE_HPP 3 4 #include <petscsys.h> 5 6 #if defined(__cplusplus) 7 #include <petsc/private/cpp/crtp.hpp> 8 9 namespace 10 { 11 12 template <typename T> 13 PETSC_NODISCARD inline PetscErrorCode PetscCxxObjectRegisterFinalize(T *obj, MPI_Comm comm = PETSC_COMM_SELF) noexcept 14 { 15 PetscContainer contain = nullptr; 16 const auto finalizer = [](void *ptr) { 17 PetscFunctionBegin; 18 PetscCall(static_cast<T *>(ptr)->finalize()); 19 PetscFunctionReturn(0); 20 }; 21 22 PetscFunctionBegin; 23 PetscValidPointer(obj, 1); 24 PetscCall(PetscContainerCreate(comm, &contain)); 25 PetscCall(PetscContainerSetPointer(contain, obj)); 26 PetscCall(PetscContainerSetUserDestroy(contain, std::move(finalizer))); 27 PetscCall(PetscObjectRegisterDestroy(reinterpret_cast<PetscObject>(contain))); 28 PetscFunctionReturn(0); 29 } 30 31 } // anonymous namespace 32 33 namespace Petsc 34 { 35 36 // ========================================================================================== 37 // RegisterFinalizeable 38 // 39 // A mixin class that enables registering a finalizer for a class instance to run during 40 // PetscFinalize(). Enables 3 public methods: 41 // 42 // 1. register_finalize() - Register the calling instance to run the member function 43 // finalize_() during PetscFinalize(). It only registers the class once. 44 // 2. finalize() - Run the member function finalize_() immediately. 45 // 3. registered() - Query whether you are registered. 46 // ========================================================================================== 47 template <typename Derived> 48 class RegisterFinalizeable : public util::crtp<Derived, RegisterFinalizeable> { 49 public: 50 using derived_type = Derived; 51 using crtp_type = util::crtp<Derived, RegisterFinalizeable>; 52 53 PETSC_NODISCARD bool registered() const noexcept; 54 template <typename... Args> 55 PETSC_NODISCARD PetscErrorCode finalize(Args &&...) noexcept; 56 template <typename... Args> 57 PETSC_NODISCARD PetscErrorCode register_finalize(Args &&...) noexcept; 58 59 private: 60 RegisterFinalizeable() = default; 61 friend derived_type; 62 63 // default implementations if the derived class does not want to implement them 64 template <typename... Args> 65 PETSC_NODISCARD static PetscErrorCode finalize_(Args &&...) noexcept; 66 template <typename... Args> 67 PETSC_NODISCARD static PetscErrorCode register_finalize_(Args &&...) noexcept; 68 69 bool registered_ = false; 70 }; 71 72 template <typename D> 73 template <typename... Args> 74 inline PetscErrorCode RegisterFinalizeable<D>::finalize_(Args &&...) noexcept 75 { 76 return 0; 77 } 78 79 template <typename D> 80 template <typename... Args> 81 inline PetscErrorCode RegisterFinalizeable<D>::register_finalize_(Args &&...) noexcept 82 { 83 return 0; 84 } 85 86 /* 87 RegisterFinalizeable::registered - Determine if the class instance is registered 88 89 Notes: 90 Returns true if class is registered, false otherwise. 91 */ 92 template <typename D> 93 inline bool RegisterFinalizeable<D>::registered() const noexcept 94 { 95 return registered_; 96 } 97 98 /* 99 RegisterFinalizeable::finalize - Run the finalizer for a class 100 101 Input Parameters: 102 103 . ...args - A set of arguments to pass to the finalizer 104 105 Notes: 106 It is not necessary to implement finalize_() in the derived class (though pretty much 107 pointless), a default (no-op) implementation is provided. 108 109 Runs the member function finalize_() with args forwarded. 110 111 "Unregisters" the class from PetscFinalize(). However, it is safe for finalize_() to 112 re-register itself (via register_finalize()). registered() is guaranteed to return false 113 inside finalize_(). 114 */ 115 template <typename D> 116 template <typename... Args> 117 inline PetscErrorCode RegisterFinalizeable<D>::finalize(Args &&...args) noexcept 118 { 119 PetscFunctionBegin; 120 // order of setting registered_ to false matters here, if the finalizer wants to re-register 121 // it should be able to 122 if (this->underlying().registered()) { 123 registered_ = false; 124 PetscCall(this->underlying().finalize_(std::forward<Args>(args)...)); 125 } 126 PetscFunctionReturn(0); 127 } 128 129 /* 130 RegisterFinalizeable::register_finalize - Register a finalizer to run during PetscFinalize() 131 132 Input Parameters: 133 . ...args - Additional arguments to pass to the register_finalize_() hook 134 135 Notes: 136 It is not necessary to implement register_finalize_() in the derived class. A default (no-op) 137 implementation is provided. 138 139 Before registering the class, the register_finalize_() hook function is run. This is useful 140 for running any one-time setup code before registering. Subsequent invocations of this 141 function (as long as registered() returns true) will not run register_finalize_() again. 142 143 The class is considered registered before calling the hook, that is registered() will always 144 return true inside register_finalize_(). register_finalize_() is allowed to immediately 145 un-register the class (via finalize()). In this case the finalizer does not run at 146 PetscFinalize(), and registered() returns false after this routine returns. 147 */ 148 template <typename D> 149 template <typename... Args> 150 inline PetscErrorCode RegisterFinalizeable<D>::register_finalize(Args &&...args) noexcept 151 { 152 PetscFunctionBegin; 153 if (PetscLikely(this->underlying().registered())) PetscFunctionReturn(0); 154 registered_ = true; 155 PetscCall(this->underlying().register_finalize_(std::forward<Args>(args)...)); 156 // Check if registered before we commit to actually register-finalizing. register_finalize_() 157 // is allowed to run its finalizer immediately 158 if (this->underlying().registered()) PetscCall(PetscCxxObjectRegisterFinalize(this)); 159 PetscFunctionReturn(0); 160 } 161 162 } // namespace Petsc 163 164 #endif // __cplusplus 165 166 #endif // PETSC_CPP_REGISTER_FINALIZE_HPP 167