@@ -133,6 +133,245 @@ class comstr {
133133 BSTR str;
134134};
135135
136+ class last_error_context {
137+ bool m_dismissed = false ;
138+ DWORD m_error = 0 ;
139+
140+ public:
141+ last_error_context () noexcept : last_error_context(::GetLastError()) {}
142+
143+ explicit last_error_context (DWORD error) noexcept : m_error(error) {}
144+
145+ last_error_context (last_error_context &&other) noexcept { operator =(std::move (other)); }
146+
147+ last_error_context &operator =(last_error_context &&other) noexcept {
148+ m_dismissed = std::exchange (other.m_dismissed , true );
149+ m_error = other.m_error ;
150+
151+ return *this ;
152+ }
153+
154+ ~last_error_context () noexcept {
155+ if (!m_dismissed) {
156+ ::SetLastError (m_error);
157+ }
158+ }
159+
160+ // ! last_error_context doesn't own a concrete resource, so therefore
161+ // ! it just disarms its destructor and returns void.
162+ void release () noexcept {
163+ BELA_ASSERT (!m_dismissed);
164+ m_dismissed = true ;
165+ }
166+
167+ [[nodiscard]] auto value () const noexcept { return m_error; }
168+ };
169+
170+ namespace details {
171+ typedef std::integral_constant<size_t , 0 > pointer_access_all; // get(), release(), addressof(), and '&' are available
172+ typedef std::integral_constant<size_t , 1 > pointer_access_noaddress; // get() and release() are available
173+ typedef std::integral_constant<size_t , 2 > pointer_access_none; // the raw pointer is not available
174+
175+ template <bool is_fn_ptr, typename close_fn_t , close_fn_t close_fn, typename pointer_storage_t >
176+ struct close_invoke_helper {
177+ BELA_FORCE_INLINE static void close (pointer_storage_t value) noexcept { std::invoke (close_fn, value); }
178+ inline static void close_reset (pointer_storage_t value) noexcept {
179+ auto preserveError = last_error_context ();
180+ std::invoke (close_fn, value);
181+ }
182+ };
183+
184+ template <typename close_fn_t , close_fn_t close_fn, typename pointer_storage_t >
185+ struct close_invoke_helper <true , close_fn_t , close_fn, pointer_storage_t > {
186+ BELA_FORCE_INLINE static void close (pointer_storage_t value) noexcept { close_fn (value); }
187+ inline static void close_reset (pointer_storage_t value) noexcept {
188+ auto preserveError = last_error_context ();
189+ close_fn (value);
190+ }
191+ };
192+
193+ template <typename close_fn_t , close_fn_t close_fn, typename pointer_storage_t >
194+ using close_invoker =
195+ close_invoke_helper<std::is_pointer_v<close_fn_t > ? std::is_function_v<std::remove_pointer_t <close_fn_t >> : false ,
196+ close_fn_t , close_fn, pointer_storage_t >;
197+
198+ template <typename pointer_t , // The handle type
199+ typename close_fn_t , // The handle close function type
200+ close_fn_t close_fn, // * and function pointer
201+ typename pointer_access_t = pointer_access_all, // all, noaddress or none to control pointer method access
202+ typename pointer_storage_t =
203+ pointer_t , // The type used to store the handle (usually the same as the handle itself)
204+ typename invalid_t = pointer_t , // The invalid handle value type
205+ invalid_t invalid = invalid_t {}, // * and its value (default ZERO value)
206+ typename pointer_invalid_t =
207+ std::nullptr_t > // nullptr_t if the invalid handle value is compatible with nullptr, otherwise pointer
208+ struct resource_policy : close_invoker<close_fn_t , close_fn, pointer_storage_t > {
209+ typedef pointer_storage_t pointer_storage;
210+ typedef pointer_t pointer;
211+ typedef pointer_invalid_t pointer_invalid;
212+ typedef pointer_access_t pointer_access;
213+ BELA_FORCE_INLINE static pointer_storage invalid_value () { return (pointer)invalid; }
214+ BELA_FORCE_INLINE static bool is_valid (pointer_storage value) noexcept {
215+ return (static_cast <pointer>(value) != (pointer)invalid);
216+ }
217+ };
218+
219+ // This class provides the pointer storage behind the implementation of unique_any_t utilizing the given
220+ // resource_policy. It is separate from unique_any_t to allow a type-specific specialization class to plug
221+ // into the inheritance chain between unique_any_t and unique_storage. This allows classes like unique_event
222+ // to be a unique_any formed class, but also expose methods like SetEvent directly.
223+
224+ template <typename Policy> class unique_storage {
225+ protected:
226+ typedef Policy policy;
227+ typedef typename policy::pointer_storage pointer_storage;
228+ typedef typename policy::pointer pointer;
229+ typedef unique_storage<policy> base_storage;
230+
231+ public:
232+ unique_storage () noexcept : m_ptr(policy::invalid_value()) {}
233+
234+ explicit unique_storage (pointer_storage ptr) noexcept : m_ptr(ptr) {}
235+
236+ unique_storage (unique_storage &&other) noexcept : m_ptr(std::move(other.m_ptr)) {
237+ other.m_ptr = policy::invalid_value ();
238+ }
239+
240+ ~unique_storage () noexcept {
241+ if (policy::is_valid (m_ptr)) {
242+ policy::close (m_ptr);
243+ }
244+ }
245+
246+ [[nodiscard]] bool is_valid () const noexcept { return policy::is_valid (m_ptr); }
247+
248+ void reset (pointer_storage ptr = policy::invalid_value()) noexcept {
249+ if (policy::is_valid (m_ptr)) {
250+ policy::close_reset (m_ptr);
251+ }
252+ m_ptr = ptr;
253+ }
254+
255+ void reset (std::nullptr_t ) noexcept {
256+ static_assert (std::is_same<typename policy::pointer_invalid, std::nullptr_t >::value,
257+ " reset(nullptr): valid only for handle types using nullptr as the invalid value" );
258+ reset ();
259+ }
260+
261+ [[nodiscard]] pointer get () const noexcept { return static_cast <pointer>(m_ptr); }
262+
263+ pointer_storage release () noexcept {
264+ static_assert (!std::is_same<typename policy::pointer_access, pointer_access_none>::value,
265+ " release(): the raw handle value is not available for this resource class" );
266+ auto ptr = m_ptr;
267+ m_ptr = policy::invalid_value ();
268+ return ptr;
269+ }
270+
271+ pointer_storage *addressof () noexcept {
272+ static_assert (std::is_same<typename policy::pointer_access, pointer_access_all>::value,
273+ " addressof(): the address of the raw handle is not available for this resource class" );
274+ return &m_ptr;
275+ }
276+
277+ protected:
278+ void replace (unique_storage &&other) noexcept {
279+ reset (other.m_ptr );
280+ other.m_ptr = policy::invalid_value ();
281+ }
282+
283+ private:
284+ pointer_storage m_ptr;
285+ };
286+ } // namespace details
287+
288+ template <typename struct_t , typename close_fn_t , close_fn_t close_fn, typename init_fn_t = std::nullptr_t ,
289+ init_fn_t init_fn = std::nullptr_t ()>
290+ class unique_struct : public struct_t {
291+ using closer = details::close_invoker<close_fn_t , close_fn, struct_t *>;
292+
293+ public:
294+ // ! Initializes the managed struct using the user-provided initialization function, or ZeroMemory if no function is
295+ // ! specified
296+ unique_struct () { call_init (use_default_init_fn ()); }
297+
298+ // ! Takes ownership of the struct by doing a shallow copy. Must explicitly be type struct_t
299+ explicit unique_struct (const struct_t &other) noexcept : struct_t(other) {}
300+
301+ // ! Initializes the managed struct by taking the ownership of the other managed struct
302+ // ! Then resets the other managed struct by calling the custom close function
303+ unique_struct (unique_struct &&other) noexcept : struct_t (other.release()) {}
304+
305+ // ! Resets this managed struct by calling the custom close function and takes ownership of the other managed struct
306+ // ! Then resets the other managed struct by calling the custom close function
307+ unique_struct &operator =(unique_struct &&other) noexcept {
308+ if (this != std::addressof (other)) {
309+ reset (other.release ());
310+ }
311+ return *this ;
312+ }
313+
314+ // ! Calls the custom close function
315+ ~unique_struct () noexcept { closer::close (this ); }
316+
317+ void reset (const unique_struct &) = delete;
318+
319+ // ! Resets this managed struct by calling the custom close function and begins management of the other struct
320+ void reset (const struct_t &other) noexcept {
321+ closer::close_reset (this );
322+ struct_t ::operator =(other);
323+ }
324+
325+ // ! Resets this managed struct by calling the custom close function
326+ // ! Then initializes this managed struct using the user-provided initialization function, or ZeroMemory if no function
327+ // ! is specified
328+ void reset () noexcept {
329+ closer::close (this );
330+ call_init (use_default_init_fn ());
331+ }
332+
333+ void swap (struct_t &) = delete;
334+
335+ // ! Swaps the managed structs
336+ void swap (unique_struct &other) noexcept {
337+ struct_t self (*this );
338+ struct_t ::operator =(other);
339+ *(other.addressof ()) = self;
340+ }
341+
342+ // ! Returns the managed struct
343+ // ! Then initializes this managed struct using the user-provided initialization function, or ZeroMemory if no function
344+ // ! is specified
345+ struct_t release () noexcept {
346+ struct_t value (*this );
347+ call_init (use_default_init_fn ());
348+ return value;
349+ }
350+
351+ // ! Returns address of the managed struct
352+ struct_t *addressof () noexcept { return this ; }
353+
354+ // ! Resets this managed struct by calling the custom close function
355+ // ! Then initializes this managed struct using the user-provided initialization function, or ZeroMemory if no function
356+ // ! is specified Returns address of the managed struct
357+ struct_t *reset_and_addressof () noexcept {
358+ reset ();
359+ return this ;
360+ }
361+
362+ unique_struct (const unique_struct &) = delete ;
363+ unique_struct &operator =(const unique_struct &) = delete ;
364+ unique_struct &operator =(const struct_t &) = delete ;
365+
366+ private:
367+ typedef typename std::is_same<init_fn_t , std::nullptr_t >::type use_default_init_fn;
368+
369+ void call_init (std::true_type) { RtlZeroMemory (this , sizeof (*this )); }
370+
371+ void call_init (std::false_type) { init_fn (this ); }
372+ };
373+ using unique_variant =
374+ unique_struct<VARIANT, decltype (&::VariantClear), ::VariantClear, decltype (&::VariantInit), ::VariantInit>;
136375} // namespace bela
137376
138377#endif
0 commit comments