diff options
author | Kjell Ahlstedt <kjell.ahlstedt@bredband.net> | 2015-08-28 16:34:56 +0200 |
---|---|---|
committer | Murray Cumming <murrayc@murrayc.com> | 2015-09-15 09:08:05 +0200 |
commit | bd40d0acec53de33ce6257366613c6e68e40ca57 (patch) | |
tree | 2a28c597070b25ec5cd74dc68adcfb9940b32f8c | |
parent | df4aff9ee1cdb698f1657c96485c84620dfe53e6 (diff) | |
download | glibmm-bd40d0acec53de33ce6257366613c6e68e40ca57.tar.gz |
Add Glib::WeakRef<>
* glib/glibmm/filelist.am: Add weakref.h.
* glib/glibmm/weakref.h: New file.
* glib/glibmm.h: Add weakref.h. (Not yet. I'll do it before I push weakref.h.)
* tests/Makefile.am: Add test case glibmm_weakref.
* tests/glibmm_weakref/main.cc: New test case. Bug #583399.
Thanks to worknesday, who attached a first version of WeakRef to
the bug report. Bug #583399
-rw-r--r-- | glib/glibmm/filelist.am | 1 | ||||
-rw-r--r-- | glib/glibmm/weakref.h | 447 | ||||
-rw-r--r-- | tests/Makefile.am | 3 | ||||
-rw-r--r-- | tests/glibmm_weakref/main.cc | 174 |
4 files changed, 625 insertions, 0 deletions
diff --git a/glib/glibmm/filelist.am b/glib/glibmm/filelist.am index 4de7bad6..352a1321 100644 --- a/glib/glibmm/filelist.am +++ b/glib/glibmm/filelist.am @@ -81,6 +81,7 @@ glibmm_files_extra_h = \ value.h \ value_custom.h \ vectorutils.h \ + weakref.h \ wrap.h \ wrap_init.h diff --git a/glib/glibmm/weakref.h b/glib/glibmm/weakref.h new file mode 100644 index 00000000..cfdf4937 --- /dev/null +++ b/glib/glibmm/weakref.h @@ -0,0 +1,447 @@ +#ifndef _GLIBMM_WEAKREF_H +#define _GLIBMM_WEAKREF_H + +/* Copyright (C) 2015 The glibmm Development Team + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <glib-object.h> +#include <glibmm/refptr.h> +#include <glibmm/objectbase.h> +#include <type_traits> // std::is_base_of<> +#include <utility> // std::swap<>, std::forward<> + +namespace Glib +{ + +/** WeakRef<> is a weak reference smartpointer. + * + * WeakRef can store a pointer to any class that is derived from Glib::ObjectBase. + * + * Unlike a RefPtr, a WeakRef does not contribute to the reference counting of + * the underlying object. + * + * @newin{2,46} + */ +template <typename T_CppObject> +class WeakRef +{ + static_assert(std::is_base_of<Glib::ObjectBase,T_CppObject>::value, + "Glib::WeakRef can be used only for classes derived from Glib::ObjectBase."); + +public: + /** Default constructor. + * + * Create an empty weak reference. + */ + inline WeakRef(); + + /// Copy constructor. + inline WeakRef(const WeakRef& src); + + /// Move constructor. + inline WeakRef(WeakRef&& src); + + /// Copy constructor from different, but castable type. + template <typename T_CastFrom> + inline WeakRef(const WeakRef<T_CastFrom>& src); + + /// Move constructor from different, but castable type. + template <typename T_CastFrom> + inline WeakRef(WeakRef<T_CastFrom>&& src); + + /** Constructor from a RefPtr of the same or a castable type. + * + * Create a weak reference from a RefPtr of the same or a castable type. + * If the RefPtr references nothing, an empty weak reference will be constructed. + */ + template <typename T_CastFrom> + inline WeakRef(const RefPtr<T_CastFrom>& src); + + /// Destructor. + inline ~WeakRef(); + + /// Swap the contents of two WeakRef<>. + inline void swap(WeakRef& other); + + /// Copy assignment operator. + inline WeakRef& operator=(const WeakRef& src); + + /// Move assignment operator. + inline WeakRef& operator=(WeakRef&& src); + + /// Copy assignment from different, but castable type. + template <typename T_CastFrom> + inline WeakRef& operator=(const WeakRef<T_CastFrom>& src); + + /// Move assignment from different, but castable type. + template <typename T_CastFrom> + inline WeakRef& operator=(WeakRef<T_CastFrom>&& src); + + /// Assignment from a RefPtr of the same or a castable type. + template <typename T_CastFrom> + inline WeakRef& operator=(const RefPtr<T_CastFrom>& src); + + /** Test whether the WeakRef<> points to any underlying instance. + * + * Mimics usage of ordinary pointers: + * @code + * if (ptr) + * do_something(); + * @endcode + * + * In a multi-threaded program a <tt>true</tt> return value can become + * obsolete at any time, even before the caller has a chance to test it, + * because the underlying instance may lose its last reference in another + * thread. Use get() if this is not acceptable. + */ + inline explicit operator bool() const; + + /** Create a strong reference to the underlying object. + * + * This is a thread-safe way to acquire a strong reference to the underlying + * object. If the WeakRef is empty, the returned RefPtr will reference nothing. + */ + inline RefPtr<T_CppObject> get() const; + + /// Make this WeakRef empty. + inline void reset(); + + /** Dynamic cast to derived class. + * + * The WeakRef can't be cast with the usual notation so instead you can use + * @code + * ptr_derived = Glib::WeakRef<Derived>::cast_dynamic(ptr_base); + * @endcode + */ + template <typename T_CastFrom> + static inline WeakRef cast_dynamic(const WeakRef<T_CastFrom>& src); + + /** Static cast to derived class. + * + * The WeakRef can't be cast with the usual notation so instead you can use + * @code + * ptr_derived = Glib::WeakRef<Derived>::cast_static(ptr_base); + * @endcode + */ + template <typename T_CastFrom> + static inline WeakRef cast_static(const WeakRef<T_CastFrom>& src); + + /** Cast to non-const. + * + * The WeakRef can't be cast with the usual notation so instead you can use + * @code + * ptr_nonconst = Glib::WeakRef<NonConstType>::cast_const(ptr_const); + * @endcode + */ + template <typename T_CastFrom> + static inline WeakRef cast_const(const WeakRef<T_CastFrom>& src); + +private: + // Let all instantiations of WeakRef access private data. + template <typename T_CastFrom> + friend class WeakRef; + + // If pCppObject != nullptr && gobject == nullptr, + // then the caller holds a strong reference. + inline void set(T_CppObject* pCppObject, GWeakRef* gobject); + + // WeakRef owns *gobject_, but it does not own *pCppObject_. + // Invariant: (!pCppObject_ || gobject_), + // i.e. if pCppObject_ != nullptr then also gobject_ != nullptr. + T_CppObject* pCppObject_; + GWeakRef* gobject_; + + // Some methods would be simpler if gobject_ were a GWeakRef instead of + // a GWeakRef*, but then the move constructor and the move assignment + // operation would not be efficient. + +}; // end class WeakRef + + +#ifndef DOXYGEN_SHOULD_SKIP_THIS + +template <typename T_CppObject> +WeakRef<T_CppObject>::WeakRef() +: +pCppObject_(nullptr), gobject_(nullptr) +{ +} + +template <typename T_CppObject> +WeakRef<T_CppObject>::WeakRef(const WeakRef& src) +: +pCppObject_(src.pCppObject_), gobject_(nullptr) +{ + if (pCppObject_) + { + // We must own a strong reference to the underlying GObject while + // calling g_weak_ref_init(). + gpointer ptr = g_weak_ref_get(src.gobject_); + if (ptr) + { + gobject_ = new GWeakRef; + g_weak_ref_init(gobject_, pCppObject_->gobj()); + g_object_unref(ptr); + } + else + pCppObject_ = nullptr; + } +} + +template <typename T_CppObject> +WeakRef<T_CppObject>::WeakRef(WeakRef&& src) +: +pCppObject_(src.pCppObject_), gobject_(src.gobject_) +{ + src.pCppObject_ = nullptr; + src.gobject_ = nullptr; +} + +// The templated ctor allows copy construction from any object that's +// castable. Thus, it does downcasts: +// base_ref = derived_ref +template <typename T_CppObject> template <typename T_CastFrom> +WeakRef<T_CppObject>::WeakRef(const WeakRef<T_CastFrom>& src) +: +pCppObject_(src.pCppObject_), gobject_(nullptr) +{ + if (pCppObject_) + { + // We must own a strong reference to the underlying GObject while + // calling g_weak_ref_init(). + gpointer ptr = g_weak_ref_get(src.gobject_); + if (ptr) + { + gobject_ = new GWeakRef; + g_weak_ref_init(gobject_, pCppObject_->gobj()); + g_object_unref(ptr); + } + else + pCppObject_ = nullptr; + } +} + +// The templated ctor allows move construction from any object that's +// castable. Thus, it does downcasts: +// base_ref = std::move(derived_ref) +template <typename T_CppObject> template <typename T_CastFrom> +WeakRef<T_CppObject>::WeakRef(WeakRef<T_CastFrom>&& src) +: +pCppObject_(src.pCppObject_), gobject_(src.gobject_) +{ + src.pCppObject_ = nullptr; + src.gobject_ = nullptr; +} + +template <typename T_CppObject> template <typename T_CastFrom> +WeakRef<T_CppObject>::WeakRef(const RefPtr<T_CastFrom>& src) +: +pCppObject_(src.operator->()), gobject_(nullptr) +{ + if (pCppObject_) + { + gobject_ = new GWeakRef; + g_weak_ref_init(gobject_, pCppObject_->gobj()); + } +} + +template <typename T_CppObject> +WeakRef<T_CppObject>::~WeakRef() +{ + if (gobject_) + { + g_weak_ref_clear(gobject_); + delete gobject_; + } +} + +template <class T_CppObject> +void WeakRef<T_CppObject>::swap(WeakRef& other) +{ + std::swap(pCppObject_, other.pCppObject_); + std::swap(gobject_, other.gobject_); +} + +template <typename T_CppObject> +WeakRef<T_CppObject>& WeakRef<T_CppObject>::operator=(const WeakRef& src) +{ + set(src.pCppObject_, src.gobject_); + return *this; +} + +template <typename T_CppObject> +WeakRef<T_CppObject>& WeakRef<T_CppObject>::operator=(WeakRef&& src) +{ + // See RefPtr for an explanation of the swap() technique to implement + // copy assignment and move assignment. + // This technique is inefficient for copy assignment of WeakRef, + // because it involves copy construction + destruction, i.e. in a typical + // case g_weak_ref_init() + g_weak_ref_clear(), when a g_weak_ref_set() + // would be enough. For move assignment, the swap technique is fine. + WeakRef<T_CppObject> temp(std::forward<WeakRef<T_CppObject>>(src)); + this->swap(temp); + return *this; +} + +template <typename T_CppObject> template <typename T_CastFrom> +WeakRef<T_CppObject>& WeakRef<T_CppObject>::operator=(const WeakRef<T_CastFrom>& src) +{ + set(src.pCppObject_, src.gobject_); + return *this; +} + +template <typename T_CppObject> template <typename T_CastFrom> +WeakRef<T_CppObject>& WeakRef<T_CppObject>::operator=(WeakRef<T_CastFrom>&& src) +{ + WeakRef<T_CppObject> temp(std::forward<WeakRef<T_CastFrom>>(src)); + this->swap(temp); + return *this; +} + +template <typename T_CppObject> template <typename T_CastFrom> +WeakRef<T_CppObject>& WeakRef<T_CppObject>::operator=(const RefPtr<T_CastFrom>& src) +{ + T_CppObject* pCppObject = src.operator->(); + set(pCppObject, nullptr); + return *this; +} + +template <class T_CppObject> +WeakRef<T_CppObject>::operator bool() const +{ + if (!pCppObject_) + return false; + + gpointer ptr = g_weak_ref_get(gobject_); + if (!ptr) + return false; + + g_object_unref(ptr); + return true; +} + +template <typename T_CppObject> +RefPtr<T_CppObject> WeakRef<T_CppObject>::get() const +{ + RefPtr<T_CppObject> ret; + + if (!pCppObject_) + return ret; + + gpointer ptr = g_weak_ref_get(gobject_); + if (!ptr) + return ret; + + // A RefPtr constructed from pointer expects reference to be done externally. + pCppObject_->reference(); + ret = RefPtr<T_CppObject>(pCppObject_); + + g_object_unref(ptr); + + return ret; +} + +template <typename T_CppObject> +void WeakRef<T_CppObject>::reset() +{ + set(nullptr, nullptr); +} + +template <typename T_CppObject> template <typename T_CastFrom> +WeakRef<T_CppObject> WeakRef<T_CppObject>::cast_dynamic(const WeakRef<T_CastFrom>& src) +{ + WeakRef<T_CppObject> ret; + + if (!src.pCppObject_) + return ret; + + gpointer ptr = g_weak_ref_get(src.gobject_); + if (!ptr) + return ret; + + // Don't call dynamic_cast<>() unless we know that the referenced object + // still exists. + T_CppObject *const pCppObject = dynamic_cast<T_CppObject*>(src.pCppObject_); + ret.set(pCppObject, nullptr); + g_object_unref(ptr); + + return ret; +} + +template <typename T_CppObject> template <typename T_CastFrom> +WeakRef<T_CppObject> WeakRef<T_CppObject>::cast_static(const WeakRef<T_CastFrom>& src) +{ + T_CppObject *const pCppObject = static_cast<T_CppObject*>(src.pCppObject_); + + WeakRef<T_CppObject> ret; + ret.set(pCppObject, src.gobject_); + return ret; +} + +template <typename T_CppObject> template <typename T_CastFrom> +WeakRef<T_CppObject> WeakRef<T_CppObject>::cast_const(const WeakRef<T_CastFrom>& src) +{ + T_CppObject *const pCppObject = const_cast<T_CppObject*>(src.pCppObject_); + + WeakRef<T_CppObject> ret; + ret.set(pCppObject, src.gobject_); + return ret; +} + +template <typename T_CppObject> +void WeakRef<T_CppObject>::set(T_CppObject* pCppObject, GWeakRef* gobject) +{ + // We must own a strong reference to the underlying GObject while + // calling g_weak_ref_init() or g_weak_ref_set(). + // If pCppObject != nullptr && gobject == nullptr, + // then the caller holds a strong reference. + + // An aim with this moderately complicated method is to keep the same + // GWeakRef, calling g_weak_ref_set() when possible, instead of using swap(), + // which implies creating a new WeakRef, swapping with *this, and deleting + // the new WeakRef. + + gpointer ptr = nullptr; + if (pCppObject && gobject) + ptr = g_weak_ref_get(gobject); + + pCppObject_ = (ptr || !gobject) ? pCppObject : nullptr; + if (pCppObject_ && !gobject_) + { + gobject_ = new GWeakRef; + g_weak_ref_init(gobject_, pCppObject_->gobj()); + } + else if (gobject_) + g_weak_ref_set(gobject_, pCppObject_ ? pCppObject_->gobj() : nullptr); + + if (ptr) + g_object_unref(ptr); +} + +#endif // DOXYGEN_SHOULD_SKIP_THIS + +/** Swap the contents of two WeakRef<>. + * @relates Glib::WeakRef + */ +template <class T_CppObject> inline +void swap(WeakRef<T_CppObject>& lhs, WeakRef<T_CppObject>& rhs) +{ + lhs.swap(rhs); +} + +} // namespace Glib + +#endif // _GLIBMM_WEAKREF_H diff --git a/tests/Makefile.am b/tests/Makefile.am index e01f4182..b2dddc32 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -46,6 +46,7 @@ check_PROGRAMS = \ glibmm_null_containerhandle/test \ glibmm_refptr/test \ glibmm_refptr_sigc_bind/test \ + glibmm_weakref/test \ glibmm_bytearray/test TESTS = $(check_PROGRAMS) @@ -110,4 +111,6 @@ glibmm_null_containerhandle_test_SOURCES = glibmm_null_containerhandle/main.cc glibmm_null_containerhandle_test_LDADD = $(giomm_ldadd) glibmm_refptr_test_SOURCES = glibmm_refptr/main.cc glibmm_refptr_sigc_bind_test_SOURCES = glibmm_refptr_sigc_bind/main.cc +glibmm_weakref_test_SOURCES = glibmm_weakref/main.cc +glibmm_weakref_test_LDADD = $(giomm_ldadd) glibmm_bytearray_test_SOURCES = glibmm_bytearray/main.cc diff --git a/tests/glibmm_weakref/main.cc b/tests/glibmm_weakref/main.cc new file mode 100644 index 00000000..715ca1d7 --- /dev/null +++ b/tests/glibmm_weakref/main.cc @@ -0,0 +1,174 @@ +/* Copyright (C) 2015 The glibmm Development Team + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <glibmm.h> +#include <giomm.h> //There is no class derived from Glib::Object in glibmm +#include <iostream> +#include <cstring> +#include <utility> // std::move +#include <glibmm/weakref.h> //!! Until it's included in glibmm.h + +int main(int, char**) +{ + Glib::init(); + bool success = true; + + // A Glib::WeakRef cannot be created from a Glib::RefPtr<Glib::Bytes>, + // because Glib::Bytes is not derived from Glib::ObjectBase. + //const int bdata = 1234; + //Glib::RefPtr<Glib::Bytes> bytes = Glib::Bytes::create(&bdata, sizeof bdata); + //Glib::WeakRef<Glib::Bytes> weakbytes = bytes; // does not compile + + // Gio::MemoryInputStream + Glib::RefPtr<Gio::MemoryInputStream> memstream1 = Gio::MemoryInputStream::create(); + const char data[] = "Some arbitrary data"; + memstream1->add_data(data, sizeof data, Gio::MemoryInputStream::SlotDestroyData()); + + // Downcast copy, followed by upcast. + Glib::WeakRef<Gio::MemoryInputStream> weakmemstream1 = memstream1; + Glib::WeakRef<Gio::InputStream> weakstream1 = weakmemstream1; + Glib::WeakRef<Gio::MemoryInputStream> weakmemstream2 = + Glib::WeakRef<Gio::MemoryInputStream>::cast_dynamic(weakstream1); + Glib::RefPtr<Gio::MemoryInputStream> memstream2 = weakmemstream2.get(); + if (memstream2) + { + char buffer[200]; + gsize bytes_read = 0; + try + { + memstream2->read_all(buffer, sizeof buffer, bytes_read); + std::cout << buffer << std::endl; + success &= std::strcmp(buffer, data) == 0; + } + catch (const Glib::Error& ex) + { + std::cout << "Error reading from memory stream: " << ex.what() << std::endl; + success = false; + } + } + else + { + std::cout << "!memstream2" << std::endl; + success = false; + } + + // Move construction. + Glib::WeakRef<Gio::MemoryInputStream> weakmemstream3(std::move(weakmemstream1)); + if (weakmemstream1.get() || !weakmemstream3.get()) + { + success = false; + if (weakmemstream1.get()) + std::cout << "weakmemstream1 || !weakmemstream3: weakmemstream1" << std::endl; + if (!weakmemstream3.get()) + std::cout << "weakmemstream1 || !weakmemstream3: !weakmemstream3" << std::endl; + } + else + { + // Move assignment. + weakmemstream2 = std::move(weakmemstream3); + if (!weakmemstream2 || weakmemstream3) + { + success = false; + if (!weakmemstream2.get()) + std::cout << "!weakmemstream2 || weakmemstream3: !weakmemstream2" << std::endl; + if (weakmemstream3.get()) + std::cout << "!weakmemstream2 || weakmemstream3: weakmemstream3" << std::endl; + } + else + { + // Downcast move, followed by upcast. + weakstream1 = std::move(weakmemstream2); + weakmemstream1 = Glib::WeakRef<Gio::MemoryInputStream>::cast_dynamic(weakstream1); + if (weakmemstream2 || !weakmemstream1) + { + success = false; + if (weakmemstream2) + std::cout << "weakmemstream2 || !weakmemstream1: weakmemstream2" << std::endl; + if (!weakmemstream1) + std::cout << "weakmemstream2 || !weakmemstream1: !weakmemstream1" << std::endl; + } + } + } + + // Gio::SimpleAction + Glib::RefPtr<Gio::SimpleAction> action1 = Gio::SimpleAction::create("Action1"); + + Glib::ustring name = action1->get_name(); + std::cout << "The name is '" << name << "'." << std::endl; + success &= name == "Action1"; + + Glib::WeakRef<Gio::SimpleAction> weakaction1 = action1; + Glib::WeakRef<Gio::SimpleAction> weakaction2 = weakaction1; + + // A second RefPtr + Glib::RefPtr<Gio::SimpleAction> action2 = weakaction1.get(); + if (action2) + { + name = action2->get_name(); + std::cout << "The name is '" << name << "'." << std::endl; + success &= name == "Action1"; + } + else + { + std::cout << "!action2" << std::endl; + success = false; + } + + weakaction1.reset(); + if (weakaction1.get()) + { + std::cout << "weakaction1" << std::endl; + success = false; + } + + action2 = weakaction2.get(); + if (action2) + { + name = action2->get_name(); + std::cout << "The name is '" << name << "'." << std::endl; + success &= name == "Action1"; + } + else + { + std::cout << "!action2" << std::endl; + success = false; + } + + // Reset one of the RefPtrs. One remains. + action1.reset(); + action2 = weakaction2.get(); + if (action2) + { + name = action2->get_name(); + std::cout << "The name is '" << name << "'." << std::endl; + success &= name == "Action1"; + } + else + { + std::cout << "!action2" << std::endl; + success = false; + } + + // Reset the other RefPtr as well. + action2.reset(); + if (weakaction2.get()) + { + std::cout << "weakaction2" << std::endl; + success = false; + } + + return success ? EXIT_SUCCESS : EXIT_FAILURE; +} |