#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 . */ #include #include #include #include // std::is_base_of<> #include // 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, * and whose reference() method is noexcept. * In glibmm and gtkmm, that is anything derived from Glib::ObjectBase. * * Unlike a RefPtr, a WeakRef does not contribute to the reference counting of * the underlying object. * * @newin{2,46} */ template class WeakRef { static_assert(std::is_base_of::value, "Glib::WeakRef can be used only for classes derived from Glib::ObjectBase."); public: /** Default constructor. * * Create an empty weak reference. */ inline WeakRef() noexcept; /// Copy constructor. inline WeakRef(const WeakRef& src) noexcept; /// Move constructor. inline WeakRef(WeakRef&& src) noexcept; /// Copy constructor from different, but castable type. template inline WeakRef(const WeakRef& src) noexcept; /// Move constructor from different, but castable type. template inline WeakRef(WeakRef&& src) noexcept; /** 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 inline WeakRef(const RefPtr& src) noexcept; /// Destructor. inline ~WeakRef() noexcept; /// Swap the contents of two WeakRef<>. inline void swap(WeakRef& other) noexcept; /// Copy assignment operator. inline WeakRef& operator=(const WeakRef& src) noexcept; /// Move assignment operator. inline WeakRef& operator=(WeakRef&& src) noexcept; /// Copy assignment from different, but castable type. template inline WeakRef& operator=(const WeakRef& src) noexcept; /// Move assignment from different, but castable type. template inline WeakRef& operator=(WeakRef&& src) noexcept; /// Assignment from a RefPtr of the same or a castable type. template inline WeakRef& operator=(const RefPtr& src) noexcept; /** 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 true 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 noexcept; /** 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 get() const noexcept; /// Make this WeakRef empty. inline void reset() noexcept; /** 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::cast_dynamic(ptr_base); * @endcode */ template static inline WeakRef cast_dynamic(const WeakRef& src) noexcept; /** 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::cast_static(ptr_base); * @endcode */ template static inline WeakRef cast_static(const WeakRef& src) noexcept; /** Cast to non-const. * * The WeakRef can't be cast with the usual notation so instead you can use * @code * ptr_nonconst = Glib::WeakRef::cast_const(ptr_const); * @endcode */ template static inline WeakRef cast_const(const WeakRef& src) noexcept; private: // Let all instantiations of WeakRef access private data. template friend class WeakRef; // If pCppObject != nullptr && gobject == nullptr, // then the caller holds a strong reference. void set(T_CppObject* pCppObject, GWeakRef* gobject) noexcept; // 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 WeakRef::WeakRef() noexcept : pCppObject_(nullptr), gobject_(nullptr) { } template WeakRef::WeakRef(const WeakRef& src) noexcept : 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 WeakRef::WeakRef(WeakRef&& src) noexcept : 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 template WeakRef::WeakRef(const WeakRef& src) noexcept : 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 template WeakRef::WeakRef(WeakRef&& src) noexcept : pCppObject_(src.pCppObject_), gobject_(src.gobject_) { src.pCppObject_ = nullptr; src.gobject_ = nullptr; } template template WeakRef::WeakRef(const RefPtr& src) noexcept : pCppObject_(src.operator->()), gobject_(nullptr) { if (pCppObject_) { gobject_ = new GWeakRef; g_weak_ref_init(gobject_, pCppObject_->gobj()); } } template WeakRef::~WeakRef() noexcept { if (gobject_) { g_weak_ref_clear(gobject_); delete gobject_; } } template void WeakRef::swap(WeakRef& other) noexcept { std::swap(pCppObject_, other.pCppObject_); std::swap(gobject_, other.gobject_); } template WeakRef& WeakRef::operator=(const WeakRef& src) noexcept { set(src.pCppObject_, src.gobject_); return *this; } template WeakRef& WeakRef::operator=(WeakRef&& src) noexcept { // 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 temp(std::forward>(src)); this->swap(temp); return *this; } template template WeakRef& WeakRef::operator=(const WeakRef& src) noexcept { set(src.pCppObject_, src.gobject_); return *this; } template template WeakRef& WeakRef::operator=(WeakRef&& src) noexcept { WeakRef temp(std::forward>(src)); this->swap(temp); return *this; } template template WeakRef& WeakRef::operator=(const RefPtr& src) noexcept { T_CppObject* pCppObject = src.operator->(); set(pCppObject, nullptr); return *this; } template WeakRef::operator bool() const noexcept { if (!pCppObject_) return false; gpointer ptr = g_weak_ref_get(gobject_); if (!ptr) return false; g_object_unref(ptr); return true; } template RefPtr WeakRef::get() const noexcept { RefPtr 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(pCppObject_); g_object_unref(ptr); return ret; } template void WeakRef::reset() noexcept { set(nullptr, nullptr); } template template WeakRef WeakRef::cast_dynamic(const WeakRef& src) noexcept { WeakRef 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(src.pCppObject_); ret.set(pCppObject, nullptr); g_object_unref(ptr); return ret; } template template WeakRef WeakRef::cast_static(const WeakRef& src) noexcept { T_CppObject* const pCppObject = static_cast(src.pCppObject_); WeakRef ret; ret.set(pCppObject, src.gobject_); return ret; } template template WeakRef WeakRef::cast_const(const WeakRef& src) noexcept { T_CppObject* const pCppObject = const_cast(src.pCppObject_); WeakRef ret; ret.set(pCppObject, src.gobject_); return ret; } template void WeakRef::set(T_CppObject* pCppObject, GWeakRef* gobject) noexcept { // 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 inline void swap(WeakRef& lhs, WeakRef& rhs) noexcept { lhs.swap(rhs); } } // namespace Glib #endif // _GLIBMM_WEAKREF_H