138 lines
8.1 KiB
C++
138 lines
8.1 KiB
C++
/***
|
|
Copyright (C) 2022 J Reece Wilson (a/k/a "Reece"). All rights reserved.
|
|
|
|
File: SafeDestroy.hpp
|
|
Date: 2022-07-19
|
|
Author: Reece
|
|
***/
|
|
#pragma once
|
|
|
|
namespace AuUtil
|
|
{
|
|
namespace Detail
|
|
{
|
|
template <class T>
|
|
struct AuHasDestroy
|
|
{
|
|
template <class C> static constexpr AuTrueType Test(decltype(&C::DestroyPrivate));
|
|
template <class C> static constexpr AuFalseType Test(...);
|
|
using type = decltype(Test<T>(0));
|
|
};
|
|
|
|
template <class T>
|
|
struct AuHasDeconstruct
|
|
{
|
|
template <class C> static constexpr AuTrueType Test(decltype(&C::DeconstructPrivate));
|
|
template <class C> static constexpr AuFalseType Test(...);
|
|
using type = decltype(Test<T>(0));
|
|
};
|
|
|
|
template <class T>
|
|
struct AuHasPreDeconstruct
|
|
{
|
|
template <class C> static constexpr AuTrueType Test(decltype(&C::PreDeconstructPrivate));
|
|
template <class C> static constexpr AuFalseType Test(...);
|
|
using type = decltype(Test<T>(0));
|
|
};
|
|
|
|
template <class T>
|
|
constexpr inline bool AuHasDestroy_v = AuHasDestroy<T>::type::value;
|
|
|
|
template <class T>
|
|
constexpr inline bool AuHasDesconstruct_v = AuHasDeconstruct<T>::type::value;
|
|
|
|
template <class T>
|
|
constexpr inline bool AuHasPreDesconstruct_v = AuHasPreDeconstruct<T>::type::value;
|
|
}
|
|
|
|
#define AU_SAFE_DESTROY(type) \
|
|
public: \
|
|
virtual ~type() \
|
|
{ \
|
|
if (AuAtomicTestAndSet(&this->uCurrentState_, 2)) \
|
|
{ \
|
|
return; \
|
|
} \
|
|
\
|
|
type :: _TryPreDeconstruct<type>(this); \
|
|
\
|
|
this->Destroy(); \
|
|
\
|
|
type :: _TryDeconstruct<type>(this); \
|
|
\
|
|
} \
|
|
\
|
|
inline void Destroy() \
|
|
{ \
|
|
if (AuAtomicTestAndSet(&this->uCurrentState_, 1)) \
|
|
{ \
|
|
return; \
|
|
} \
|
|
\
|
|
type :: _TryDeconstructPrivate<type>(this); \
|
|
} \
|
|
\
|
|
friend AuUtil::Detail::AuHasDestroy<type>; \
|
|
friend AuUtil::Detail::AuHasDeconstruct<type>; \
|
|
friend AuUtil::Detail::AuHasPreDeconstruct<type>; \
|
|
\
|
|
private: \
|
|
AuUInt32 uCurrentState_{}; \
|
|
\
|
|
template<typename T> \
|
|
static void _TryPreDeconstruct(T *that) \
|
|
{ \
|
|
if constexpr (AuUtil::Detail::AuHasPreDesconstruct_v<T>) \
|
|
{ \
|
|
that->PreDeconstructPrivate(); \
|
|
} \
|
|
} \
|
|
\
|
|
template<typename T> \
|
|
static void _TryDeconstruct(T *that) \
|
|
{ \
|
|
if constexpr (AuUtil::Detail::AuHasDesconstruct_v<T>) \
|
|
{ \
|
|
that->DeconstructPrivate(); \
|
|
} \
|
|
} \
|
|
\
|
|
template<typename T> \
|
|
static void _TryDeconstructPrivate(T *that) \
|
|
{ \
|
|
if constexpr (AuUtil::Detail::AuHasDestroy_v<T>) \
|
|
{ \
|
|
that->DestroyPrivate(); \
|
|
} \
|
|
} \
|
|
\
|
|
public:
|
|
}
|
|
|
|
/**
|
|
Safe destroy objects:
|
|
* implement a builtin ::Destroy function for objects whose ownership is expectantly shared instead of explicitly owned (eg: overlapped io resources over simplier utility class objects with explicit an release function)
|
|
|
|
* allow the user to define ::DestroyPrivate, ::DeconstructPrivate, and ::*Pre*DeconstructPrivate methods in their class
|
|
|
|
* forces virtual destruction to prevent hidden leaks
|
|
note: The real reason we do this is bc "some projects" expect the destruction implementation itself to be a virtual destructor (an implicitly overloaded virtual, if it were a regular method).
|
|
That way API objects can call a `virtual ~IInterface()` and nuke safely nuke away the underlying safe-destroy object by using the builtin "everyone has this destruct method" destructor;
|
|
bypassing the need for a `virtual Destroy() = 0;` and alternative an AU_SAFE_DESTROY.
|
|
|
|
* enable safe explicit double ~Type() support; however, some objects in your STL may not support this behaviour (eg: AuFunction)
|
|
workaround: implement explicit ::Deconstruct() with AuResetMember calls to fixup the UB
|
|
|
|
* allows for an arbitrary amount of Type::Destroy() and Type::~Type() calls across multiple threads
|
|
|
|
Usage:
|
|
Defines #define AU_SAFE_DESTROY(type)
|
|
|
|
Simply add AU_SAFE_DESTROY to the types body add arbitrarily add the following callback methods as needed
|
|
|
|
void DestroyPrivate(void) - Generic called once object destroy
|
|
void DeconstructPrivate(void) - Object destruction hook. Called once. Called After DestroyPrivate.
|
|
void PreDeconstructPrivate(void) - Object destruction hook. Called once. Called before DestroyPrivate.
|
|
|
|
These user defined functions may be private and/or virtual, if implemented at all.
|
|
*/ |