Automatic forward-decl fixup
[hiphop-php.git] / hphp / runtime / base / req-ptr.h
blob391c583f26c5e81c91377f3d113f51af8b5530cd
1 /*
2 +----------------------------------------------------------------------+
3 | HipHop for PHP |
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2010-2016 Facebook, Inc. (http://www.facebook.com) |
6 +----------------------------------------------------------------------+
7 | This source file is subject to version 3.01 of the PHP license, |
8 | that is bundled with this package in the file LICENSE, and is |
9 | available through the world-wide-web at the following url: |
10 | http://www.php.net/license/3_01.txt |
11 | If you did not receive a copy of the PHP license and are unable to |
12 | obtain it through the world-wide-web, please send a note to |
13 | license@php.net so we can mail you a copy immediately. |
14 +----------------------------------------------------------------------+
17 #ifndef incl_HPHP_REQ_PTR_H_
18 #define incl_HPHP_REQ_PTR_H_
20 #include "hphp/runtime/base/countable.h"
21 #include "hphp/util/portability.h"
22 #include "hphp/util/compilation-flags.h"
23 #include <algorithm>
25 namespace HPHP {
26 ///////////////////////////////////////////////////////////////////////////////
29 * Because of type punning with the *NR classes (ArrNR, StrNR, etc),
30 * we want to assert that offsetof(T, m_px) is a specific value in all
31 * these classes.
33 const std::size_t kExpectedMPxOffset = 0;
35 namespace req {
37 template<typename T> struct ptr final {
38 ptr() : m_px(nullptr) {}
39 /* implicit */ ptr(std::nullptr_t) : m_px(nullptr) { }
40 explicit ptr(T* px) : m_px(px) {
41 if (LIKELY(m_px != nullptr)) m_px->incRefCount();
43 ptr(const ptr<T>& src) : m_px(src.get()) {
44 if (LIKELY(m_px != nullptr)) m_px->incRefCount();
47 enum class NoIncRef {};
48 explicit ptr(T* px, NoIncRef) : m_px(px) {}
50 enum class NonNull {};
51 explicit ptr(T* px, NonNull) : m_px(px) {
52 assert(px);
53 m_px->incRefCount();
56 // Move ctor
57 ptr(ptr&& src) noexcept : m_px(src.get()) {
58 src.m_px = nullptr;
61 template<class Y>
62 ptr(const ptr<Y>& src) : m_px(src.get()) {
63 if (LIKELY(m_px != nullptr)) m_px->incRefCount();
65 // Move ctor for derived types
66 template<class Y>
67 ptr(ptr<Y>&& src) : m_px(src.get()) {
68 src.m_px = nullptr;
71 ~ptr() {
72 decRefPtr(m_px);
73 if (debug) {
74 m_px = reinterpret_cast<T*>(0xdeadbeeffaceb004);
78 /**
79 * Assignments.
81 ptr& operator=(const ptr<T>& src) {
82 return operator=(src.m_px);
84 // Move assignment
85 ptr& operator=(ptr&& src) {
86 std::swap(m_px, src.m_px);
87 return *this;
89 template<class Y>
90 ptr& operator=(const ptr<Y>& src) {
91 return operator=(src.get());
93 // Move assignment for derived types
94 template<class Y>
95 ptr& operator=(ptr<Y>&& src) {
96 assert((void*)this != (void*)&src);
97 // Update m_px before releasing the goner
98 auto goner = m_px;
99 m_px = src.m_px;
100 src.m_px = nullptr;
101 decRefPtr(goner);
102 return *this;
104 ptr& operator=(T* px) {
105 // Works with self-assignment because incRefCount is
106 // called before decRefCount.
107 if (LIKELY(px != nullptr)) px->incRefCount();
108 auto goner = m_px;
109 m_px = px;
110 decRefPtr(goner);
111 return *this;
115 * Swap two pointers
117 void swap(ptr& rhs) {
118 std::swap(m_px, rhs.m_px);
122 * Safe bool cast.
124 explicit operator bool() const { return m_px != nullptr; }
127 * Magic delegation.
129 T* operator->() const {
130 return m_px; // intentionally skip nullptr check.
134 * Get the raw pointer.
136 T* get() const {
137 return m_px;
141 * Reset the raw pointer. This will increment the ref count on ptr
142 * (when it is non-null) and decrement the ref count on the old pointer
143 * being replaced.
145 void reset(T* ptr = nullptr) {
146 operator=(ptr);
150 * Release the raw pointer.
151 * Note: be careful when using this. It does not decrement the
152 * ref count on m_px.
154 T* detach() {
155 T* ptr = m_px;
156 m_px = nullptr;
157 return ptr;
161 * Take ownership of a pointer without touching the ref count.
163 template <typename Y>
164 static ptr<Y> attach(Y* y) {
165 ptr<Y> py;
166 py.m_px = y;
167 return py;
170 private:
171 // For templatized ptr<Y> move constructor.
172 template <typename Y> friend class ptr;
174 static ALWAYS_INLINE void decRefPtr(T* ptr) {
175 if (LIKELY(ptr != nullptr)) ptr->decRefAndRelease();
177 static void compileTimeAssertions() {
178 static_assert(offsetof(ptr, m_px) == kExpectedMPxOffset, "");
181 T* m_px; // raw pointer
184 ///////////////////////////////////////////////////////////////////////////////
185 // req::ptr helper functions
187 // Note: don't define the other relational (<,>,<=,=>) operators in
188 // terms of the underlying pointer. If the underlying pointer is
189 // moved (e.g. by GC) the relation may no longer hold.
191 template <typename T>
192 struct isValidPtrRelopArg {
193 enum { value = false };
196 template <>
197 struct isValidPtrRelopArg<std::nullptr_t> {
198 enum { value = true };
201 template <typename T>
202 struct isValidPtrRelopArg<req::ptr<T>> {
203 enum { value = true };
206 template <typename T>
207 struct isValidPtrRelopArg<T*> {
208 enum { value = true };
211 template <typename T>
212 struct isValidPtrRelopArg<const T*> {
213 enum { value = true };
216 } // namespace req
218 template <typename T>
219 inline T* deref(const req::ptr<T>& p) { return p.get(); }
221 template <typename T>
222 inline const T* deref(const T* p) { return p; }
224 inline std::nullptr_t deref(std::nullptr_t) { return nullptr; }
226 template <typename A, typename B>
227 inline
228 typename std::enable_if<
229 (req::isValidPtrRelopArg<A>::value && req::isValidPtrRelopArg<B>::value),
230 bool
231 >::type operator==(const A& a, const B& b) {
232 return deref(a) == deref(b);
235 template <typename A, typename B>
236 inline
237 typename std::enable_if<
238 (req::isValidPtrRelopArg<A>::value && req::isValidPtrRelopArg<B>::value),
239 bool
240 >::type operator!=(const A& a, const B& b) {
241 return !(a == b);
244 template <typename T, typename P>
245 inline auto detach(P&& p) -> decltype(P().detach()) {
246 return p.detach();
249 template <typename T, typename P>
250 inline auto deref(const P& p) -> decltype(P().get()) {
251 return p.get();
254 struct ResourceData;
255 struct ObjectData;
256 struct Resource;
257 class Object;
258 struct Variant;
260 ATTRIBUTE_NORETURN extern void throw_null_pointer_exception();
261 ATTRIBUTE_NORETURN void throw_invalid_object_type(ResourceData* p);
262 ATTRIBUTE_NORETURN void throw_invalid_object_type(ObjectData* p);
263 ATTRIBUTE_NORETURN void throw_invalid_object_type(const Variant& p);
264 ATTRIBUTE_NORETURN void throw_invalid_object_type(const Resource& p);
265 ATTRIBUTE_NORETURN void throw_invalid_object_type(const Object& p);
266 template <typename T>
267 ATTRIBUTE_NORETURN void throw_invalid_object_type(const req::ptr<T>& p);
268 template <typename T>
269 void throw_invalid_object_type(const req::ptr<T>& p) {
270 throw_invalid_object_type(p.get());
273 template <typename T>
274 inline bool is_null(const T& p) {
275 return !p;
278 template <typename T, typename P>
279 inline bool isa_non_null(const P& p) {
280 assert(!is_null(p));
281 return p->template instanceof<T>();
284 // Is pointer contained in p castable to a T?
285 template <typename T, typename P>
286 inline bool isa(const P& p) {
287 return !is_null(p) && isa_non_null<T>(p);
290 // Is pointer contained in p null or castable to a T?
291 template <typename T, typename P>
292 inline bool isa_or_null(const P& p) {
293 return is_null(p) || isa_non_null<T>(p);
296 // Perform an unsafe cast operation on p. The value p is assumed
297 // to be castable to T (checked by assertion). Null pointers will
298 // be passed through.
299 template <typename T, typename P>
300 struct unsafe_cast_helper {
301 req::ptr<T> operator()(P&& p) const {
302 return req::ptr<T>::attach(static_cast<T*>(detach<T>(std::move(p))));
304 req::ptr<T> operator()(const P& p) const {
305 return req::ptr<T>(static_cast<T*>(deref<T>(p)));
309 template <typename T, typename P>
310 inline req::ptr<T> unsafe_cast_or_null(P&& p) {
311 using decayedP = typename std::decay<P>::type;
312 return unsafe_cast_helper<T,decayedP>()(std::forward<P>(p));
315 // Perform an unsafe cast operation on p. The value p is assumed
316 // to be castable to T (checked by assertion). Null pointers will
317 // result in an exception.
318 template <typename T, typename P>
319 inline req::ptr<T> unsafe_cast(P&& p) {
320 if (!is_null(p)) {
321 return unsafe_cast_or_null<T>(std::forward<P>(p));
323 throw_null_pointer_exception();
326 // Perform a cast operation on p. If p is null or not castable to
327 // a T, an exception will be thrown.
328 template <typename T, typename P>
329 inline req::ptr<T> cast(P&& p) {
330 if (!is_null(p)) {
331 if (isa_non_null<T>(p)) {
332 return unsafe_cast_or_null<T>(std::forward<P>(p));
335 throw_invalid_object_type(p);
338 // Perform a cast operation on p. If p is not castable to
339 // a T, an exception will be thrown. Null pointers will be
340 // passed through.
341 template <typename T, typename P>
342 inline req::ptr<T> cast_or_null(P&& p) {
343 if (!is_null(p)) {
344 if (isa_non_null<T>(p)) {
345 return unsafe_cast_or_null<T>(std::forward<P>(p));
347 throw_invalid_object_type(p);
349 return nullptr;
352 // Perform a cast operation on p. If p not castable to
353 // a T, a null value is returned. If p is null, an exception
354 // is thrown.
355 template <typename T, typename P>
356 inline req::ptr<T> dyn_cast(P&& p) {
357 if (!is_null(p)) {
358 if (isa_non_null<T>(p)) {
359 return unsafe_cast_or_null<T>(std::forward<P>(p));
361 return nullptr;
363 throw_null_pointer_exception();
366 // Perform a cast operation on p. If p is null or not castable to
367 // a T, a null value is returned.
368 template <typename T, typename P>
369 inline req::ptr<T> dyn_cast_or_null(P&& p) {
370 return isa<T>(p) ? unsafe_cast_or_null<T>(std::forward<P>(p)) : nullptr;
373 ///////////////////////////////////////////////////////////////////////////////
377 ///////////////////////////////////////////////////////////////////////////////
379 namespace std {
380 template<class T>
381 struct hash<HPHP::req::ptr<T>> {
382 size_t operator()(const HPHP::req::ptr<T>& p) const {
383 return std::hash<T*>()(p.get());
388 ///////////////////////////////////////////////////////////////////////////////
390 #endif