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 |
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: |
37 | DynamicScopedRef() : |
38 | m_ptr(nullptr) |
39 | {} |
40 | |
41 | T* get() const { |
42 | return m_ptr; |
43 | } |
44 | |
45 | T* operator->() const { |
46 | assert(m_ptr != nullptr); |
47 | return m_ptr; |
48 | } |
49 | |
50 | 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 */ |
58 | DynamicScopedRefGuard<T> bind(T& value) |
59 | { |
60 | return DynamicScopedRefGuard<T>(*this, &value); |
61 | } |
62 | |
63 | /** Check if the ref contains a valid value */ |
64 | explicit operator bool() const { |
65 | return m_ptr != nullptr; |
66 | } |
67 | |
68 | private: |
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: |
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 | |
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 | |