Initial implementation of zero-cost unique_ptr with exploding weak references.
authorRobert Sesek <rsesek@bluestatic.org>
Sat, 8 Oct 2016 18:16:46 +0000 (14:16 -0400)
committerRobert Sesek <rsesek@bluestatic.org>
Sat, 8 Oct 2016 18:16:46 +0000 (14:16 -0400)
test.cc [new file with mode: 0644]
zcpointer.h [new file with mode: 0644]

diff --git a/test.cc b/test.cc
new file mode 100644 (file)
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 <iostream>
+
+#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> c(new C());
+  zc::ref<C> owned = c.get();
+  zc::ref<C> owned2 = owned;
+  c.reset();
+  owned2->DoThing();
+}
+
+template <typename T>
+void TestUnwrap() {
+  zc::owned<T> t(new T());
+  T* unwrap = t.get();
+}
+
+void TestMove() {
+  zc::owned<C> c(new C());
+  zc::ref<C> owned = c.get();
+
+  zc::owned<C> 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 (file)
index 0000000..2edf05f
--- /dev/null
@@ -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 <memory>
+#include <forward_list>
+#include <stdexcept>
+
+namespace zc {
+
+#if defined(ZCPOINTER_TRACK_REFS) && ZCPOINTER_TRACK_REFS
+
+template <typename T> class ref;
+
+namespace internal {
+
+template <typename T>
+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<T>;
+
+  void AddRef(ref<T>* ref) {
+    refs_.push_front(ref);
+  }
+
+  void RemoveRef(ref<T>* ref) {
+    refs_.remove(ref);
+  }
+
+ private:
+  std::forward_list<ref<T>*> refs_;
+};
+
+}  // namespace internal
+
+using UseAfterFreeException = std::logic_error;
+
+template <typename T>
+class owned : public std::unique_ptr<T, internal::OwnedPtrDeleter<T>> {
+ private:
+  using Deleter = internal::OwnedPtrDeleter<T>;
+
+ public:
+  using std::unique_ptr<T, Deleter>::unique_ptr;
+
+  ref<T> get() {
+    return ref<T>(*this);
+  }
+
+ protected:
+  friend class ref<T>;
+
+  T* GetRawPointer() const {
+    return get();
+  }
+
+ private:
+  T* get() const {
+    return this->std::unique_ptr<T, Deleter>::get();
+  }
+};
+
+template <typename T>
+class ref {
+ public:
+  ref(owned<T>& o) : ptr_(o.GetRawPointer()), deleter_(o.get_deleter()) {
+    deleter_.AddRef(this);
+  }
+
+  ref(const ref<T>& o) : ptr_(o.ptr_), deleter_(o.deleter_), deleted_(o.deleted_) {
+    if (!deleted_) {
+      deleter_.AddRef(this);
+    }
+  }
+
+  ref<T>& operator=(ref<T> 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<T>;
+
+  void MarkDeleted() {
+    deleted_ = true;
+  }
+
+ private:
+  void CheckDeleted() const {
+    if (deleted_) {
+      throw UseAfterFreeException("attempt to access deleted pointer");
+    }
+  }
+
+  T* ptr_;
+  internal::OwnedPtrDeleter<T>& deleter_;
+  bool deleted_ = false;
+};
+
+#else
+
+template <typename T>
+using owned = std::unique_ptr<T>;
+
+template <typename T>
+using ref = T*;
+
+#endif
+
+}  // namespace zc