1// vi:set ft=cpp: -*- Mode: C++ -*-
2/*
3 * Copyright (C) 2015 Kernkonzept GmbH.
4 * Author(s): Sarah Hoffmann <sarah.hoffmann@kernkonzept.com>
5 *            Alwexander Warg <alexander.warg@kernkonzept.com>
6 *
7 * This file is distributed under the terms of the GNU General Public
8 * License, version 2.  Please see the COPYING-GPL-2 file for details.
9 */
10#pragma once
11
12#include "hlist"
13
14namespace cxx {
15
16/**
17 * Generic (base) weak reference to some object.
18 *
19 * A weak reference is a reference that gets reset to NULL when the object
20 * shall be deleted.  All weak references to the same object are kept in a
21 * linked list of weak references.
22 *
23 * For typed weak references see `cxx::Weak_ref`.
24 */
25class Weak_ref_base : public H_list_item_t<Weak_ref_base>
26{
27protected:
28  Weak_ref_base(void const *ptr = nullptr) : _obj(ptr) {}
29  void reset_hard() { _obj = nullptr; }
30  void const *_obj;
31
32public:
33  struct List : H_list_t<Weak_ref_base>
34  {
35    void reset()
36    {
37      while (!empty())
38        pop_front()->reset_hard();
39    }
40
41    ~List()
42    { reset(); }
43  };
44
45  explicit operator bool () const
46  { return _obj ? true : false; }
47};
48
49
50/**
51 * Typed weak reference to an object of type `T`.
52 *
53 * \tparam T  The type of the referenced object.
54 *
55 * A weak reference is a reference that is invalidated when the referenced
56 * object is about to be deleted.  All weak references to an object are kept in
57 * a linked list and all the weak references are iterated and reset by the
58 * Weak_ref_base::List destructor or Weak_ref_base::reset().
59 *
60 * The type `T` must provide two methods that handle the housekeeping of weak
61 * references: remove_weak_ref(Weak_ref_base *) and add_weak_ref(Weak_ref_base *).
62 * These functions must handle the insertion and removal of the weak reference
63 * into the respective Weak_ref_base::List object.  For convenience one can use the
64 * cxx::Weak_ref_obj as a base class that handles weak references for you.
65 */
66template <typename T>
67class Weak_ref : public Weak_ref_base
68{
69public:
70  T *get() const
71  { return reinterpret_cast<T*>(const_cast<void *>(_obj)); }
72
73  T *reset(T *n)
74  {
75    T *r = get();
76    if (r)
77      r->remove_weak_ref(this);
78
79    _obj = n;
80    if (n)
81      n->add_weak_ref(this);
82
83    return r;
84  }
85
86  Weak_ref(T *s = nullptr) : Weak_ref_base(s)
87  {
88    if (s)
89      s->add_weak_ref(this);
90  }
91
92  ~Weak_ref() { reset(0); }
93
94  void operator = (T *n)
95  { reset(n); }
96
97  Weak_ref(Weak_ref const &o) : Weak_ref_base(o._obj)
98  {
99    if (T *x = get())
100      x->add_weak_ref(this);
101  }
102
103  Weak_ref &operator = (Weak_ref const &o)
104  {
105    if (&o == this)
106      return *this;
107
108    reset(o.get());
109    return *this;
110  }
111
112  T &operator * () const { return get(); }
113  T *operator -> () const { return get(); }
114};
115
116class Weak_ref_obj
117{
118protected:
119  template <typename T> friend class Weak_ref;
120  mutable Weak_ref_base::List weak_references;
121
122  void add_weak_ref(Weak_ref_base *ref) const
123  { weak_references.push_front(ref); }
124
125  void remove_weak_ref(Weak_ref_base *ref) const
126  { weak_references.remove(ref); }
127};
128
129}
130