2 +----------------------------------------------------------------------+
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2010-present 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"
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
33 const std::size_t kExpectedMPxOffset
= 0;
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
) {
57 ptr(ptr
&& src
) noexcept
: m_px(src
.get()) {
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
67 ptr(ptr
<Y
>&& src
) : m_px(src
.get()) {
74 m_px
= reinterpret_cast<T
*>(0xdeadbeeffaceb004);
81 ptr
& operator=(const ptr
<T
>& src
) {
82 return operator=(src
.m_px
);
85 ptr
& operator=(ptr
&& src
) {
86 std::swap(m_px
, src
.m_px
);
90 ptr
& operator=(const ptr
<Y
>& src
) {
91 return operator=(src
.get());
93 // Move assignment for derived types
95 ptr
& operator=(ptr
<Y
>&& src
) {
96 assert((void*)this != (void*)&src
);
97 // Update m_px before releasing the goner
104 ptr
& operator=(T
* px
) {
105 // Works with self-assignment because incRefCount is
106 // called before decRefCount.
107 if (LIKELY(px
!= nullptr)) px
->incRefCount();
117 void swap(ptr
& rhs
) {
118 std::swap(m_px
, rhs
.m_px
);
124 explicit operator bool() const { return m_px
!= nullptr; }
129 T
* operator->() const {
130 return m_px
; // intentionally skip nullptr check.
134 * Get the raw pointer.
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
145 void reset(T
* ptr
= nullptr) {
150 * Release the raw pointer.
151 * Note: be careful when using this. It does not decrement the
161 * Take ownership of a pointer without touching the ref count.
163 template <typename Y
>
164 static ptr
<Y
> attach(Y
* y
) {
171 // For templatized ptr<Y> move constructor.
172 template <typename Y
> friend struct 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 };
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 };
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
>
228 typename
std::enable_if
<
229 (req::isValidPtrRelopArg
<A
>::value
&& req::isValidPtrRelopArg
<B
>::value
),
231 >::type
operator==(const A
& a
, const B
& b
) {
232 return deref(a
) == deref(b
);
235 template <typename A
, typename B
>
237 typename
std::enable_if
<
238 (req::isValidPtrRelopArg
<A
>::value
&& req::isValidPtrRelopArg
<B
>::value
),
240 >::type
operator!=(const A
& a
, const B
& b
) {
244 template <typename T
, typename P
>
245 inline auto detach(P
&& p
) -> decltype(P().detach()) {
249 template <typename T
, typename P
>
250 inline auto deref(const P
& p
) -> decltype(P().get()) {
260 [[noreturn
]] extern void throw_null_pointer_exception();
261 [[noreturn
]] void throw_invalid_object_type(ResourceData
* p
);
262 [[noreturn
]] void throw_invalid_object_type(ObjectData
* p
);
263 [[noreturn
]] void throw_invalid_object_type(const Variant
& p
);
264 [[noreturn
]] void throw_invalid_object_type(const Resource
& p
);
265 [[noreturn
]] void throw_invalid_object_type(const Object
& p
);
266 template <typename T
>
267 [[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
) {
278 template <typename T
, typename P
>
279 inline bool isa_non_null(const P
& 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
) {
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
) {
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
341 template <typename T
, typename P
>
342 inline req::ptr
<T
> cast_or_null(P
&& 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
);
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
355 template <typename T
, typename P
>
356 inline req::ptr
<T
> dyn_cast(P
&& p
) {
358 if (isa_non_null
<T
>(p
)) {
359 return unsafe_cast_or_null
<T
>(std::forward
<P
>(p
));
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 ///////////////////////////////////////////////////////////////////////////////
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 ///////////////////////////////////////////////////////////////////////////////