1 /*---------------------------------------------------------------------------*
2   Project:  Horizon
3   File:     util_AutoObject.h
4 
5   Copyright (C)2009 Nintendo Co., Ltd.  All rights reserved.
6 
7   These coded instructions, statements, and computer programs contain
8   proprietary information of Nintendo of America Inc. and/or Nintendo
9   Company Ltd., and are protected by Federal copyright law.  They may
10   not be disclosed to third parties or copied or duplicated in any form,
11   in whole or in part, without the prior written consent of Nintendo.
12 
13   $Rev: 28252 $
14  *---------------------------------------------------------------------------*/
15 
16 #ifndef NN_UTIL_UTIL_AUTOOBJECT_H_
17 #define NN_UTIL_UTIL_AUTOOBJECT_H_
18 
19 #include <nn/util/util_NonCopyable.h>
20 #include <nn/WithInitialize.h>
21 #include <memory>
22 
23 namespace nn { namespace util {
24 
25 namespace detail
26 {
27     struct AutoObjectCoreAccess;
28 }
29 
30 template <typename Int>
31 class ReferenceCounter
32 {
33     friend struct detail::AutoObjectCoreAccess;
34 private:
35     Int m_Value;
36 protected:
ReferenceCounter()37     ReferenceCounter() : m_Value(1) { NN_LOG_DEBUG("ctor0\n"); }
ReferenceCounter(const ReferenceCounter &)38     ReferenceCounter(const ReferenceCounter&) : m_Value(1) { NN_LOG_DEBUG("copy\n"); }
~ReferenceCounter()39     ~ReferenceCounter() { NN_TASSERT_(m_Value == 0);  NN_LOG_DEBUG("dtor\n"); }
40     ReferenceCounter& operator=(const ReferenceCounter& rhs)
41     {
42         if (this != &rhs)
43         {
44             NN_TASSERT_(m_Value == 0);
45             m_Value = 1;
46         };
47         return *this;
48     }
49 private:
IncrementRefCount()50     void IncrementRefCount() { ++m_Value; NN_LOG_DEBUG("inc\n"); }
DecrementRefCount()51     bool DecrementRefCount() { NN_LOG_DEBUG("dec\n"); return --m_Value > 0; }
TryDecrementRefCount()52     bool TryDecrementRefCount() { return (m_Value > 1) && (--m_Value, NN_LOG_DEBUG("trydec\n"), true); }
53 public:
GetReferenceCounter()54     Int GetReferenceCounter() const { return m_Value; }
55 };
56 
57 namespace detail
58 {
59 
60 struct AutoObjectCoreAccess
61 {
62     template <typename ReferenceCounter>
IncrementRefCountAutoObjectCoreAccess63     static void IncrementRefCount(ReferenceCounter& r) { r.IncrementRefCount(); }
64     template <typename ReferenceCounter>
DecrementRefCountAutoObjectCoreAccess65     static bool DecrementRefCount(ReferenceCounter& r) { return r.DecrementRefCount(); }
66     template <typename ReferenceCounter>
TryDecrementRefCountAutoObjectCoreAccess67     static bool TryDecrementRefCount(ReferenceCounter& r) { return r.TryDecrementRefCount(); }
68 };
69 
70 template <class Self, class TObject>
71 class AutoObject
72 {
73 protected:
74 
75     typedef TObject Object;
76 
77 
AutoObject()78     AutoObject() : m_P(0) {}
79 
AutoObject(const nn::WithInitialize &)80     AutoObject(const nn::WithInitialize&) : m_P(new Object()) {}
81 
82     template <class T>
AutoObject(const T & x)83     AutoObject(const T& x) : m_P(new Object(x)) {}
84 
AutoObject(const AutoObject & other)85     AutoObject(const AutoObject& other) : m_P(other.m_P)
86     {
87         other.AssertNotUpdating();
88         if (m_P)
89         {
90             detail::AutoObjectCoreAccess::IncrementRefCount(*m_P);
91         }
92     }
93 
~AutoObject()94     ~AutoObject()
95     {
96         AssertNotUpdating();
97         DecrementAndDelete();
98     }
99 
Assign(const Self & rhs)100     Self& Assign(const Self& rhs)
101     {
102         AssertNotUpdating();
103         rhs.AssertNotUpdating();
104         if (this != &rhs && m_P != rhs.m_P)
105         {
106             DecrementAndDelete();
107             this->m_P = rhs.m_P;
108             if (m_P)
109             {
110                 detail::AutoObjectCoreAccess::IncrementRefCount(*m_P);
111             }
112         }
113         return static_cast<Self&>(*this);
114     }
115 
Clear()116     void Clear()
117     {
118         AssertNotUpdating();
119         DecrementAndDelete();
120         this->m_P = 0;
121     }
122 
123     const Object& operator*() const
124     {
125         NN_TASSERT_(IsValid());
126         AssertNotUpdating();
127         return *m_P;
128     }
129 
130     const Object* operator->() const
131     {
132         NN_TASSERT_(IsValid());
133         AssertNotUpdating();
134         return m_P;
135     }
136 
IsValid()137     bool IsValid() const
138     {
139         return m_P != 0;
140     }
141 
142     friend class Updater;
143 
144     class Updater
145     {
146     private:
147 
148         AutoObject& m_AutoObject;
149 
150 #ifdef NN_SWITCH_DISABLE_ASSERT_WARNING
GetObject()151         Object* GetObject() { return reinterpret_cast<Object*>(reinterpret_cast<uptr>(m_AutoObject.m_P) & ~1); }
152 #else
GetObject()153         Object* GetObject() { return m_AutoObject.m_P; }
154 #endif
155 
156     public:
157 
Updater(AutoObject & autoObject)158         Updater(AutoObject& autoObject) : m_AutoObject(autoObject)
159         {
160             NN_TASSERT_(autoObject.IsValid());
161             if (detail::AutoObjectCoreAccess::DecrementRefCount(*autoObject.m_P))
162             {
163                 autoObject.m_P = new Object(*autoObject);
164             }
165             else
166             {
167                 detail::AutoObjectCoreAccess::IncrementRefCount(*autoObject.m_P);
168             }
169             autoObject.SetUpdating();
170         }
171 
~Updater()172         ~Updater() { m_AutoObject.UnsetUpdating(); }
173 
174         Object& operator*()
175         {
176             return *GetObject();
177         }
178 
179         Object* operator->()
180         {
181             return GetObject();
182         }
183     };
184 
AutoObject(Object * p)185     AutoObject(Object* p) : m_P(p) {}
186 
Release()187     Object* Release()
188     {
189         AssertNotUpdating();
190         Object* p = m_P;
191         this->m_P = 0;
192         if (!detail::AutoObjectCoreAccess::TryDecrementRefCount(*p))
193         {
194             return p;
195         }
196         else
197         {
198             return new Object(*p);
199         }
200     }
201 
202 public:
GetObject()203     Object* GetObject() { return m_P; }
204 
205 private:
206 
207     Object* m_P;
208 
DecrementAndDelete()209     void DecrementAndDelete()
210     {
211         if (m_P && !detail::AutoObjectCoreAccess::DecrementRefCount(*m_P))
212         {
213             delete m_P;
214         }
215     }
216 
217 #ifdef NN_SWITCH_DISABLE_ASSERT_WARNING
218 
AssertNotUpdating()219     void AssertNotUpdating() const
220     {
221         NN_TASSERT_((reinterpret_cast<const uptr&>(m_P) & 1) == 0);
222     }
223 
SetUpdating()224     void SetUpdating()
225     {
226         AssertNotUpdating();
227         reinterpret_cast<uptr&>(m_P) |= 1;
228     }
229 
UnsetUpdating()230     void UnsetUpdating()
231     {
232         reinterpret_cast<uptr&>(m_P) &= ~1;
233     }
234 
235 #else
236 
AssertNotUpdating()237     void AssertNotUpdating() const {}
SetUpdating()238     void SetUpdating() {}
UnsetUpdating()239     void UnsetUpdating() {}
240 
241 #endif
242 
243 };
244 
245 }
246 
247 }}
248 
249 #define NN_UTIL_AUTOOBJECT_BEGIN_DEFINE_DECL_T0(name, ...) \
250     class name : public ::nn::util::detail::AutoObject<name, __VA_ARGS__ >
251 
252 #define NN_UTIL_AUTOOBJECT_BEGIN_DEFINE_DECL_T1(name, ...) \
253     template <typename NN_UTIL_AUTOOBJECT_T0> \
254     class name : public ::nn::util::detail::AutoObject<name<NN_UTIL_AUTOOBJECT_T0>, __VA_ARGS__ >
255 
256 #define NN_UTIL_AUTOOBJECT_BEGIN_DEFINE_BODY(name, ...) \
257     { \
258     private: \
259         typedef ::nn::util::detail::AutoObject<name, __VA_ARGS__ > Base; \
260         friend class ::nn::util::detail::AutoObject<name, __VA_ARGS__ >; \
261         name(__VA_ARGS__* p) : Base(p) {} \
262     public: \
263         name(const name& other) : Base(static_cast<const Base&>(other)) {} \
264         friend name Move(name& _this) { return _this.Release(); } \
265         name& operator=(const name& rhs) \
266         { \
267             return Base::Assign(rhs); \
268         } \
269     private:
270 
271 #define NN_UTIL_AUTOOBJECT_BEGIN_DEFINE(name, ...) \
272     NN_UTIL_AUTOOBJECT_BEGIN_DEFINE_DECL_T0(name, __VA_ARGS__) \
273     NN_UTIL_AUTOOBJECT_BEGIN_DEFINE_BODY(name, __VA_ARGS__)
274 
275 #define NN_UTIL_AUTOOBJECT_BEGIN_DEFINE_T1(name, ...) \
276     NN_UTIL_AUTOOBJECT_BEGIN_DEFINE_DECL_T1(name, __VA_ARGS__) \
277     NN_UTIL_AUTOOBJECT_BEGIN_DEFINE_BODY(name, __VA_ARGS__)
278 
279 #define NN_UTIL_AUTOOBJECT_END_DEFINE \
280     };
281 
282 #endif
283