Commit 5f2172c6 authored by Kenton Varda's avatar Kenton Varda

Add support for Own<void>.

Any Own<T> can be converted to Own<void> (or Own<const void>). The main purpose of this is to give someone a way to invoke an object's disposer without them having to know anything about the type.
parent 4f5ebd93
......@@ -136,6 +136,92 @@ TEST(Memory, AttachNested) {
KJ_EXPECT(destroyed3 == 3, destroyed3);
}
struct StaticType {
int i;
};
struct DynamicType1 {
virtual void foo() {}
int j;
DynamicType1(int j): j(j) {}
};
struct DynamicType2 {
virtual void bar() {}
int k;
DynamicType2(int k): k(k) {}
};
struct DerivedDynamic: public DynamicType1, public DynamicType2 {
DerivedDynamic(int j, int k, bool& destructorCalled)
: DynamicType1(j), DynamicType2(k), destructorCalled(destructorCalled) {}
~DerivedDynamic() {
destructorCalled = true;
}
KJ_DISALLOW_COPY(DerivedDynamic);
bool& destructorCalled;
};
TEST(Memory, OwnVoid) {
{
Own<StaticType> ptr = heap<StaticType>({123});
StaticType* addr = ptr.get();
Own<void> voidPtr = kj::mv(ptr);
KJ_EXPECT(voidPtr.get() == implicitCast<void*>(addr));
}
{
Own<DynamicType1> ptr = heap<DynamicType1>(123);
DynamicType1* addr = ptr.get();
Own<void> voidPtr = kj::mv(ptr);
KJ_EXPECT(voidPtr.get() == implicitCast<void*>(addr));
}
{
bool destructorCalled = false;
Own<DerivedDynamic> ptr = heap<DerivedDynamic>(123, 456, destructorCalled);
DerivedDynamic* addr = ptr.get();
Own<void> voidPtr = kj::mv(ptr);
KJ_EXPECT(voidPtr.get() == implicitCast<void*>(addr));
KJ_EXPECT(!destructorCalled);
voidPtr = nullptr;
KJ_EXPECT(destructorCalled);
}
{
bool destructorCalled = false;
Own<DerivedDynamic> ptr = heap<DerivedDynamic>(123, 456, destructorCalled);
DerivedDynamic* addr = ptr.get();
Own<DynamicType2> basePtr = kj::mv(ptr);
DynamicType2* baseAddr = basePtr.get();
// On most (all?) C++ ABIs, the second base class in a multiply-inherited class is offset from
// the beginning of the object (assuming the first base class has non-zero size). We use this
// fact here to verify that then casting to Own<void> does in fact result in a pointer that
// points to the start of the overall object, not the base class. We assert that the pointers
// are different here to prove that the test below is non-trivial.
//
// If there is some other ABI where these pointers are the same, and thus this assert fails,
// then it's no problem to #ifdef out the assert on that platform.
KJ_ASSERT(static_cast<void*>(baseAddr) != static_cast<void*>(addr));
Own<void> voidPtr = kj::mv(basePtr);
KJ_EXPECT(voidPtr.get() == static_cast<void*>(addr));
KJ_EXPECT(!destructorCalled);
voidPtr = nullptr;
KJ_EXPECT(destructorCalled);
}
}
// TODO(test): More tests.
} // namespace
......
......@@ -30,6 +30,55 @@
namespace kj {
namespace _ { // private
template <typename T> struct RefOrVoid_ { typedef T& Type; };
template <> struct RefOrVoid_<void> { typedef void Type; };
template <> struct RefOrVoid_<const void> { typedef void Type; };
template <typename T>
using RefOrVoid = typename RefOrVoid_<T>::Type;
// Evaluates to T&, unless T is `void`, in which case evaluates to `void`.
//
// This is a hack needed to avoid defining Own<void> as a totally separate class.
template <typename T, bool isPolymorphic = __is_polymorphic(T)>
struct CastToVoid_;
template <typename T>
struct CastToVoid_<T, false> {
static void* apply(T* ptr) {
return static_cast<void*>(ptr);
}
static const void* applyConst(T* ptr) {
const T* cptr = ptr;
return static_cast<const void*>(cptr);
}
};
template <typename T>
struct CastToVoid_<T, true> {
static void* apply(T* ptr) {
return dynamic_cast<void*>(ptr);
}
static const void* applyConst(T* ptr) {
const T* cptr = ptr;
return dynamic_cast<const void*>(cptr);
}
};
template <typename T>
void* castToVoid(T* ptr) {
return CastToVoid_<T>::apply(ptr);
}
template <typename T>
void* castToConstVoid(T* ptr) {
return CastToVoid_<T>::applyConst(ptr);
}
} // namespace _ (private)
// =======================================================================================
// Disposer -- Implementation details.
......@@ -177,8 +226,8 @@ public:
#define NULLCHECK KJ_IREQUIRE(ptr != nullptr, "null Own<> dereference")
inline T* operator->() { NULLCHECK; return ptr; }
inline const T* operator->() const { NULLCHECK; return ptr; }
inline T& operator*() { NULLCHECK; return *ptr; }
inline const T& operator*() const { NULLCHECK; return *ptr; }
inline _::RefOrVoid<T> operator*() { NULLCHECK; return *ptr; }
inline _::RefOrVoid<const T> operator*() const { NULLCHECK; return *ptr; }
#undef NULLCHECK
inline T* get() { return ptr; }
inline const T* get() const { return ptr; }
......@@ -210,6 +259,22 @@ private:
friend class Maybe<Own<T>>;
};
template <>
template <typename U, typename>
inline Own<void>::Own(Own<U>&& other) noexcept
: disposer(other.disposer),
ptr(_::castToVoid(other.ptr)) {
other.ptr = nullptr;
}
template <>
template <typename U, typename>
inline Own<const void>::Own(Own<U>&& other) noexcept
: disposer(other.disposer),
ptr(_::castToConstVoid(other.ptr)) {
other.ptr = nullptr;
}
namespace _ { // private
template <typename T>
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment