Unverified Commit 3b834806 authored by Kenton Varda's avatar Kenton Varda Committed by GitHub

Merge pull request #600 from capnproto/own-void

Add support for Own<void>.
parents 9f5b8a85 e338d636
......@@ -136,6 +136,159 @@ 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 SingularDerivedDynamic final: public DynamicType1 {
SingularDerivedDynamic(int j, bool& destructorCalled)
: DynamicType1(j), destructorCalled(destructorCalled) {}
~SingularDerivedDynamic() {
destructorCalled = true;
}
KJ_DISALLOW_COPY(SingularDerivedDynamic);
bool& destructorCalled;
};
struct MultipleDerivedDynamic final: public DynamicType1, public DynamicType2 {
MultipleDerivedDynamic(int j, int k, bool& destructorCalled)
: DynamicType1(j), DynamicType2(k), destructorCalled(destructorCalled) {}
~MultipleDerivedDynamic() {
destructorCalled = true;
}
KJ_DISALLOW_COPY(MultipleDerivedDynamic);
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));
}
{
bool destructorCalled = false;
Own<SingularDerivedDynamic> ptr = heap<SingularDerivedDynamic>(123, destructorCalled);
SingularDerivedDynamic* addr = ptr.get();
Own<void> voidPtr = kj::mv(ptr);
KJ_EXPECT(voidPtr.get() == implicitCast<void*>(addr));
}
{
bool destructorCalled = false;
Own<MultipleDerivedDynamic> ptr = heap<MultipleDerivedDynamic>(123, 456, destructorCalled);
MultipleDerivedDynamic* 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<MultipleDerivedDynamic> ptr = heap<MultipleDerivedDynamic>(123, 456, destructorCalled);
MultipleDerivedDynamic* 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 expect 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 expectation
// fails, then it's no problem to #ifdef out the expectation on that platform.
KJ_EXPECT(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);
}
}
TEST(Memory, OwnConstVoid) {
{
Own<StaticType> ptr = heap<StaticType>({123});
StaticType* addr = ptr.get();
Own<const void> voidPtr = kj::mv(ptr);
KJ_EXPECT(voidPtr.get() == implicitCast<void*>(addr));
}
{
bool destructorCalled = false;
Own<SingularDerivedDynamic> ptr = heap<SingularDerivedDynamic>(123, destructorCalled);
SingularDerivedDynamic* addr = ptr.get();
Own<const void> voidPtr = kj::mv(ptr);
KJ_EXPECT(voidPtr.get() == implicitCast<void*>(addr));
}
{
bool destructorCalled = false;
Own<MultipleDerivedDynamic> ptr = heap<MultipleDerivedDynamic>(123, 456, destructorCalled);
MultipleDerivedDynamic* addr = ptr.get();
Own<const void> voidPtr = kj::mv(ptr);
KJ_EXPECT(voidPtr.get() == implicitCast<void*>(addr));
KJ_EXPECT(!destructorCalled);
voidPtr = nullptr;
KJ_EXPECT(destructorCalled);
}
{
bool destructorCalled = false;
Own<MultipleDerivedDynamic> ptr = heap<MultipleDerivedDynamic>(123, 456, destructorCalled);
MultipleDerivedDynamic* 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 expect 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 expectation
// fails, then it's no problem to #ifdef out the expectation on that platform.
KJ_EXPECT(static_cast<void*>(baseAddr) != static_cast<void*>(addr));
Own<const 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>
const void* castToConstVoid(T* ptr) {
return CastToVoid_<T>::applyConst(ptr);
}
} // namespace _ (private)
// =======================================================================================
// Disposer -- Implementation details.
......@@ -120,9 +169,7 @@ public:
: disposer(other.disposer), ptr(other.ptr) { other.ptr = nullptr; }
template <typename U, typename = EnableIf<canConvert<U*, T*>()>>
inline Own(Own<U>&& other) noexcept
: disposer(other.disposer), ptr(other.ptr) {
static_assert(__is_polymorphic(T),
"Casting owned pointers requires that the target type is polymorphic.");
: disposer(other.disposer), ptr(cast(other.ptr)) {
other.ptr = nullptr;
}
inline Own(T* ptr, const Disposer& disposer) noexcept: disposer(&disposer), ptr(ptr) {}
......@@ -177,8 +224,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; }
......@@ -205,11 +252,30 @@ private:
}
}
template <typename U>
static inline T* cast(U* ptr) {
static_assert(__is_polymorphic(T),
"Casting owned pointers requires that the target type is polymorphic.");
return ptr;
}
template <typename U>
friend class Own;
friend class Maybe<Own<T>>;
};
template <>
template <typename U>
inline void* Own<void>::cast(U* ptr) {
return _::castToVoid(ptr);
}
template <>
template <typename U>
inline const void* Own<const void>::cast(U* ptr) {
return _::castToConstVoid(ptr);
}
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