1 /*---------------------------------------------------------------------------*
2   Project:  Horizon
3   File:     util_AutoObject.h
4 
5   Copyright (C)2009-2012 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: 46347 $
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 #ifndef 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 private:
203 
204     Object* m_P;
205 
DecrementAndDelete()206     void DecrementAndDelete()
207     {
208         if (m_P && !detail::AutoObjectCoreAccess::DecrementRefCount(*m_P))
209         {
210             delete m_P;
211         }
212     }
213 
214 #ifndef NN_SWITCH_DISABLE_ASSERT_WARNING
215 
AssertNotUpdating()216     void AssertNotUpdating() const
217     {
218         NN_TASSERT_((reinterpret_cast<const uptr&>(m_P) & 1) == 0);
219     }
220 
SetUpdating()221     void SetUpdating()
222     {
223         AssertNotUpdating();
224         reinterpret_cast<uptr&>(m_P) |= 1;
225     }
226 
UnsetUpdating()227     void UnsetUpdating()
228     {
229         reinterpret_cast<uptr&>(m_P) &= ~1;
230     }
231 
232 #else
233 
AssertNotUpdating()234     void AssertNotUpdating() const {}
SetUpdating()235     void SetUpdating() {}
UnsetUpdating()236     void UnsetUpdating() {}
237 
238 #endif
239 
240 };
241 
242 }
243 
244 }}
245 
246 #define NN_UTIL_AUTOOBJECT_BEGIN_DEFINE_DECL_T0(name, ...) \
247     class name : public ::nn::util::detail::AutoObject<name, __VA_ARGS__ >
248 
249 #define NN_UTIL_AUTOOBJECT_BEGIN_DEFINE_DECL_T1(name, ...) \
250     template <typename NN_UTIL_AUTOOBJECT_T0> \
251     class name : public ::nn::util::detail::AutoObject<name<NN_UTIL_AUTOOBJECT_T0>, __VA_ARGS__ >
252 
253 #define NN_UTIL_AUTOOBJECT_BEGIN_DEFINE_BODY(name, ...) \
254     { \
255     private: \
256         typedef ::nn::util::detail::AutoObject<name, __VA_ARGS__ > Base; \
257         friend class ::nn::util::detail::AutoObject<name, __VA_ARGS__ >; \
258         name(__VA_ARGS__* p) : Base(p) {} \
259     public: \
260         name(const name& other) : Base(static_cast<const Base&>(other)) {} \
261         friend name Move(name& _this) { return _this.Release(); } \
262         name& operator=(const name& rhs) \
263         { \
264             return Base::Assign(rhs); \
265         } \
266     private:
267 
268 #define NN_UTIL_AUTOOBJECT_BEGIN_DEFINE(name, ...) \
269     NN_UTIL_AUTOOBJECT_BEGIN_DEFINE_DECL_T0(name, __VA_ARGS__) \
270     NN_UTIL_AUTOOBJECT_BEGIN_DEFINE_BODY(name, __VA_ARGS__)
271 
272 #define NN_UTIL_AUTOOBJECT_BEGIN_DEFINE_T1(name, ...) \
273     NN_UTIL_AUTOOBJECT_BEGIN_DEFINE_DECL_T1(name, __VA_ARGS__) \
274     NN_UTIL_AUTOOBJECT_BEGIN_DEFINE_BODY(name, __VA_ARGS__)
275 
276 #define NN_UTIL_AUTOOBJECT_END_DEFINE \
277     };
278 
279 #endif
280