1 //  SuperTux
2 //  Copyright (C) 2018 Ingo Ruhnke <grumbel@gmail.com>
3 //
4 //  This program is free software: you can redistribute it and/or modify
5 //  it under the terms of the GNU General Public License as published by
6 //  the Free Software Foundation, either version 3 of the License, or
7 //  (at your option) any later version.
8 //
9 //  This program is distributed in the hope that it will be useful,
10 //  but WITHOUT ANY WARRANTY; without even the implied warranty of
11 //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 //  GNU General Public License for more details.
13 //
14 //  You should have received a copy of the GNU General Public License
15 //  along with this program.  If not, see <http://www.gnu.org/licenses/>.
16 
17 #ifndef HEADER_SUPERTUX_UTIL_DYNAMIC_SCOPED_REF_HPP
18 #define HEADER_SUPERTUX_UTIL_DYNAMIC_SCOPED_REF_HPP
19 
20 #include <assert.h>
21 
22 template<typename T> class DynamicScopedRefGuard;
23 
24 /** The DynamicScopedRef class emulates dynamic scoping in C++ by
25     providing a rebindable reference to a value. Binding a new value
26     to the DynamicScopedRef pushes it's previous value on the stack
27     and restores it again once the guard object goes out of scope.
28     Thus it provides a safer alternative to normal global variables as
29     functions deeper down the stack can't manipulate the value
30     persistantly. */
31 template<typename T>
32 class DynamicScopedRef
33 {
34   friend class DynamicScopedRefGuard<T>;
35 
36 public:
DynamicScopedRef()37   DynamicScopedRef() :
38     m_ptr(nullptr)
39   {}
40 
get() const41   T* get() const {
42     return m_ptr;
43   }
44 
operator ->() const45   T* operator->() const {
46     assert(m_ptr != nullptr);
47     return m_ptr;
48   }
49 
operator *() const50   T& operator*() const {
51     assert(m_ptr != nullptr);
52     return *m_ptr;
53   }
54 
55   /** Set the DynamicScopedRef to a new value, the lifetime of the
56       returned guard object decides when the ref gets reset to it's
57       previous value */
bind(T & value)58   DynamicScopedRefGuard<T> bind(T& value)
59   {
60     return DynamicScopedRefGuard<T>(*this, &value);
61   }
62 
63   /** Check if the ref contains a valid value */
operator bool() const64   explicit operator bool() const {
65     return m_ptr != nullptr;
66   }
67 
68 private:
getset(T * ptr)69   T* getset(T* ptr)
70   {
71     T* tmp = m_ptr;
72     m_ptr = ptr;
73     return tmp;
74   }
75 
76 private:
77   T* m_ptr;
78 
79 private:
80   DynamicScopedRef(const DynamicScopedRef&) = delete;
81   DynamicScopedRef& operator=(const DynamicScopedRef&) = delete;
82 };
83 
84 /** The DynamicScopedRefGuard class is returned by
85     DynamicScopedRef::bind() and ensures that the DynamicScopedRef
86     gets reset to it's previous value once the guard goes out of
87     scope. */
88 template<typename T>
89 class DynamicScopedRefGuard
90 {
91 public:
DynamicScopedRefGuard(DynamicScopedRef<T> & dynamic_scoped,T * ptr)92   DynamicScopedRefGuard(DynamicScopedRef<T>& dynamic_scoped, T* ptr) :
93     m_dynamic_scoped(dynamic_scoped),
94     m_old_ptr(m_dynamic_scoped.getset(ptr))
95   {
96   }
97 
98   DynamicScopedRefGuard(DynamicScopedRefGuard<T>&&) noexcept = default;
99 
~DynamicScopedRefGuard()100   ~DynamicScopedRefGuard()
101   {
102     m_dynamic_scoped.getset(m_old_ptr);
103   }
104 
105 private:
106   DynamicScopedRef<T>& m_dynamic_scoped;
107   T* m_old_ptr;
108 
109 private:
110   DynamicScopedRefGuard(const DynamicScopedRefGuard&) = delete;
111   DynamicScopedRefGuard& operator=(const DynamicScopedRefGuard&) = delete;
112 };
113 
114 #endif
115 
116 /* EOF */
117