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<RegisterFinalizeable, Derived> { 49 public: 50 using derived_type = Derived; 51 52 PETSC_NODISCARD bool registered() const noexcept; 53 template <typename... Args> 54 PETSC_NODISCARD PetscErrorCode finalize(Args &&...) noexcept; 55 template <typename... Args> 56 PETSC_NODISCARD PetscErrorCode register_finalize(Args &&...) noexcept; 57 58 private: 59 RegisterFinalizeable() = default; 60 friend derived_type; 61 62 // default implementations if the derived class does not want to implement them 63 template <typename... Args> 64 PETSC_NODISCARD static PetscErrorCode finalize_(Args &&...) noexcept; 65 template <typename... Args> 66 PETSC_NODISCARD static PetscErrorCode register_finalize_(Args &&...) noexcept; 67 68 bool registered_ = false; 69 }; 70 71 template <typename D> 72 template <typename... Args> 73 inline PetscErrorCode RegisterFinalizeable<D>::finalize_(Args &&...) noexcept 74 { 75 return 0; 76 } 77 78 template <typename D> 79 template <typename... Args> 80 inline PetscErrorCode RegisterFinalizeable<D>::register_finalize_(Args &&...) noexcept 81 { 82 return 0; 83 } 84 85 /* 86 RegisterFinalizeable::registered - Determine if the class instance is registered 87 88 Notes: 89 Returns true if class is registered, false otherwise. 90 */ 91 template <typename D> 92 inline bool RegisterFinalizeable<D>::registered() const noexcept 93 { 94 return registered_; 95 } 96 97 /* 98 RegisterFinalizeable::finalize - Run the finalizer for a class 99 100 Input Parameters: 101 102 . ...args - A set of arguments to pass to the finalizer 103 104 Notes: 105 It is not necessary to implement finalize_() in the derived class (though pretty much 106 pointless), a default (no-op) implementation is provided. 107 108 Runs the member function finalize_() with args forwarded. 109 110 "Unregisters" the class from PetscFinalize(). However, it is safe for finalize_() to 111 re-register itself (via register_finalize()). registered() is guaranteed to return false 112 inside finalize_(). 113 */ 114 template <typename D> 115 template <typename... Args> 116 inline PetscErrorCode RegisterFinalizeable<D>::finalize(Args &&...args) noexcept 117 { 118 PetscFunctionBegin; 119 // order of setting registered_ to false matters here, if the finalizer wants to re-register 120 // it should be able to 121 if (this->underlying().registered()) { 122 registered_ = false; 123 PetscCall(this->underlying().finalize_(std::forward<Args>(args)...)); 124 } 125 PetscFunctionReturn(0); 126 } 127 128 /* 129 RegisterFinalizeable::register_finalize - Register a finalizer to run during PetscFinalize() 130 131 Input Parameters: 132 . ...args - Additional arguments to pass to the register_finalize_() hook 133 134 Notes: 135 It is not necessary to implement register_finalize_() in the derived class. A default (no-op) 136 implementation is provided. 137 138 Before registering the class, the register_finalize_() hook function is run. This is useful 139 for running any one-time setup code before registering. Subsequent invocations of this 140 function (as long as registered() returns true) will not run register_finalize_() again. 141 142 The class is considered registered before calling the hook, that is registered() will always 143 return true inside register_finalize_(). register_finalize_() is allowed to immediately 144 un-register the class (via finalize()). In this case the finalizer does not run at 145 PetscFinalize(), and registered() returns false after this routine returns. 146 */ 147 template <typename D> 148 template <typename... Args> 149 inline PetscErrorCode RegisterFinalizeable<D>::register_finalize(Args &&...args) noexcept 150 { 151 PetscFunctionBegin; 152 if (PetscLikely(this->underlying().registered())) PetscFunctionReturn(0); 153 registered_ = true; 154 PetscCall(this->underlying().register_finalize_(std::forward<Args>(args)...)); 155 // Check if registered before we commit to actually register-finalizing. register_finalize_() 156 // is allowed to run its finalizer immediately 157 if (this->underlying().registered()) PetscCall(PetscCxxObjectRegisterFinalize(this)); 158 PetscFunctionReturn(0); 159 } 160 161 } // namespace Petsc 162 163 #endif // __cplusplus 164 165 #endif // PETSC_CPP_REGISTER_FINALIZE_HPP 166