From 42cf1c19233d160b1607a406ee2ab7f9f5b82196 Mon Sep 17 00:00:00 2001 From: Robert Sesek Date: Sat, 8 Oct 2016 14:16:46 -0400 Subject: [PATCH 1/1] Initial implementation of zero-cost unique_ptr with exploding weak references. --- test.cc | 59 +++++++++++++++++++ zcpointer.h | 162 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 221 insertions(+) create mode 100644 test.cc create mode 100644 zcpointer.h diff --git a/test.cc b/test.cc new file mode 100644 index 0000000..3c1e152 --- /dev/null +++ b/test.cc @@ -0,0 +1,59 @@ +// Copyright 2016 Google Inc. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include + +#define ZCPOINTER_TRACK_REFS 1 + +#include "zcpointer.h" + +class C { + public: + ~C() { + std::cout << "~C" << std::endl; + } + + void DoThing() { + std::cout << "DoThing" << std::endl; + } +}; + +void TestReset() { + zc::owned c(new C()); + zc::ref owned = c.get(); + zc::ref owned2 = owned; + c.reset(); + owned2->DoThing(); +} + +template +void TestUnwrap() { + zc::owned t(new T()); + T* unwrap = t.get(); +} + +void TestMove() { + zc::owned c(new C()); + zc::ref owned = c.get(); + + zc::owned c2(std::move(c)); + owned->DoThing(); + + c2.reset(); + owned->DoThing(); +} + +int main() { + TestMove(); +} diff --git a/zcpointer.h b/zcpointer.h new file mode 100644 index 0000000..2edf05f --- /dev/null +++ b/zcpointer.h @@ -0,0 +1,162 @@ +// Copyright 2016 Google Inc. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include +#include + +namespace zc { + +#if defined(ZCPOINTER_TRACK_REFS) && ZCPOINTER_TRACK_REFS + +template class ref; + +namespace internal { + +template +class OwnedPtrDeleter { + public: + OwnedPtrDeleter() {} + ~OwnedPtrDeleter() {} + + OwnedPtrDeleter(OwnedPtrDeleter&& other) : refs_(std::move(other.refs_)) { + } + + void operator()(T* t) const { + for (auto& ref : refs_) { + ref->MarkDeleted(); + } + delete t; + } + + protected: + friend class ref; + + void AddRef(ref* ref) { + refs_.push_front(ref); + } + + void RemoveRef(ref* ref) { + refs_.remove(ref); + } + + private: + std::forward_list*> refs_; +}; + +} // namespace internal + +using UseAfterFreeException = std::logic_error; + +template +class owned : public std::unique_ptr> { + private: + using Deleter = internal::OwnedPtrDeleter; + + public: + using std::unique_ptr::unique_ptr; + + ref get() { + return ref(*this); + } + + protected: + friend class ref; + + T* GetRawPointer() const { + return get(); + } + + private: + T* get() const { + return this->std::unique_ptr::get(); + } +}; + +template +class ref { + public: + ref(owned& o) : ptr_(o.GetRawPointer()), deleter_(o.get_deleter()) { + deleter_.AddRef(this); + } + + ref(const ref& o) : ptr_(o.ptr_), deleter_(o.deleter_), deleted_(o.deleted_) { + if (!deleted_) { + deleter_.AddRef(this); + } + } + + ref& operator=(ref o) { + ptr_ = o.ptr_; + deleter_ = o.deleter_; + deleted_ = o.deleted_; + if (!deleted_) { + deleter_.AddRef(this); + } + return *this; + } + + ~ref() { + deleter_.RemoveRef(this); + } + +#if 0 + operator T*() const { + CheckDeleted(); + return ptr_; + } +#endif + + T* operator->() const { + CheckDeleted(); + return ptr_; + } + +#if 0 + T* get() { + CheckDeleted(); + return ptr_; + } +#endif + + protected: + friend class internal::OwnedPtrDeleter; + + void MarkDeleted() { + deleted_ = true; + } + + private: + void CheckDeleted() const { + if (deleted_) { + throw UseAfterFreeException("attempt to access deleted pointer"); + } + } + + T* ptr_; + internal::OwnedPtrDeleter& deleter_; + bool deleted_ = false; +}; + +#else + +template +using owned = std::unique_ptr; + +template +using ref = T*; + +#endif + +} // namespace zc -- 2.22.5