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
22template<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. */
31template<typename T>
32class DynamicScopedRef
33{
34 friend class DynamicScopedRefGuard<T>;
35
36public:
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
68private:
69 T* getset(T* ptr)
70 {
71 T* tmp = m_ptr;
72 m_ptr = ptr;
73 return tmp;
74 }
75
76private:
77 T* m_ptr;
78
79private:
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. */
88template<typename T>
89class DynamicScopedRefGuard
90{
91public:
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
105private:
106 DynamicScopedRef<T>& m_dynamic_scoped;
107 T* m_old_ptr;
108
109private:
110 DynamicScopedRefGuard(const DynamicScopedRefGuard&) = delete;
111 DynamicScopedRefGuard& operator=(const DynamicScopedRefGuard&) = delete;
112};
113
114#endif
115
116/* EOF */
117