|
|
|
/*
|
|
|
|
* Copyright (c) Facebook, Inc. and its affiliates.
|
|
|
|
*
|
|
|
|
* This source code is licensed under the MIT license found in the
|
|
|
|
* LICENSE file in the root directory of this source tree.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#pragma once
|
|
|
|
// ASSERT that __cplusplus >= 201103L
|
|
|
|
|
|
|
|
#include <infer_model/common.h>
|
|
|
|
#include <infer_model/infer_traits.h>
|
|
|
|
#include <infer_model/weak_ptr.h>
|
|
|
|
|
|
|
|
INFER_NAMESPACE_STD_BEGIN
|
|
|
|
|
|
|
|
// use inheritance to avoid compilation errors when using
|
|
|
|
// methods / non-member functions that are not modeled
|
|
|
|
// WARNING: if sizeof(shared_ptr) becomes different than 16, it may
|
|
|
|
// lead to compilation errors
|
|
|
|
template <class T>
|
|
|
|
class shared_ptr : public std__shared_ptr<T> {
|
|
|
|
|
|
|
|
// translate shared_ptr as type T*
|
|
|
|
friend class infer_traits::TranslateAsType<T*>;
|
|
|
|
|
|
|
|
// shared_ptr<T> in infer is translated as T*.
|
|
|
|
// Some facts:
|
|
|
|
// 1. shared_ptr<T>* translated as T**
|
|
|
|
// 2. typeof(this) translated as T**
|
|
|
|
// 3. typeof(this) in clang's AST is shared_ptr<T>*
|
|
|
|
// When writing models for shared_ptr, we need to use infer's representation
|
|
|
|
// In order to achieve that and not break compilation, there is some ugly
|
|
|
|
// casting going around. We are using void* and void** to make compilation
|
|
|
|
// happy - infer doesn't care about those types that much since they are
|
|
|
|
// pointers anyway.
|
|
|
|
// Example of model_X function declaration:
|
|
|
|
// static void model_X(infer_shared_ptr_t self, ... params)
|
|
|
|
// model_X are really C functions, but to simplify linking process, they are
|
|
|
|
// defined inside shared_ptr class as static methods.
|
|
|
|
// When using model_X functions, call them like so:
|
|
|
|
// model_X(__cast_to_infer_ptr(this), args)
|
|
|
|
|
|
|
|
/// type of 'this' in shared_ptr<T> as seen by infer
|
|
|
|
typedef const void** infer_shared_ptr_t;
|
|
|
|
// use it to avoid compilation errors and make infer analyzer happy
|
|
|
|
#define __cast_to_infer_ptr(self) ((infer_shared_ptr_t)self)
|
|
|
|
|
|
|
|
// provide overload for volatile void* to accommodate for situation when
|
|
|
|
// T is volatile ('volatile int' for example). 'void*' and 'nullptr_t'
|
|
|
|
// overloads are to avoid 'call to model_set is ambiguous' compilation errors
|
|
|
|
static void model_set(infer_shared_ptr_t self, nullptr_t value) {
|
|
|
|
*self = value;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void model_set(infer_shared_ptr_t self, const void* value) {
|
|
|
|
*self = value;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void model_set(infer_shared_ptr_t self, volatile void* value) {
|
|
|
|
*self = const_cast<const void*>(value);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void model_set(infer_shared_ptr_t self, void* value) {
|
|
|
|
*self = const_cast<const void*>(value);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void model_copy(infer_shared_ptr_t self, infer_shared_ptr_t other) {
|
|
|
|
/* TODO - increase refcount*/
|
|
|
|
*self = *other;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void model_move(infer_shared_ptr_t self, infer_shared_ptr_t other) {
|
|
|
|
model_copy(self, other);
|
|
|
|
model_set(other, nullptr);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void model_swap(infer_shared_ptr_t infer_self,
|
|
|
|
infer_shared_ptr_t infer_other) {
|
|
|
|
const void* t = *infer_self;
|
|
|
|
*infer_self = *infer_other;
|
|
|
|
*infer_other = t;
|
|
|
|
}
|
|
|
|
|
|
|
|
public:
|
|
|
|
// Conversion constructors to allow implicit conversions.
|
|
|
|
// it's here purely to avoid compilation errors
|
|
|
|
template <class Y,
|
|
|
|
typename = typename enable_if<is_convertible<Y*, T*>::value>::type>
|
|
|
|
shared_ptr(const std__shared_ptr<Y>& r) {}
|
|
|
|
|
|
|
|
template <class Y>
|
|
|
|
shared_ptr(const std__shared_ptr<Y>& r, T* p) noexcept {}
|
|
|
|
|
|
|
|
// constructors:
|
|
|
|
constexpr shared_ptr() noexcept {
|
|
|
|
model_set(__cast_to_infer_ptr(this), nullptr);
|
|
|
|
}
|
|
|
|
|
|
|
|
shared_ptr(nullptr_t) : shared_ptr() {}
|
|
|
|
|
|
|
|
// Extra template argument is used to create constructors/assignment overloads
|
|
|
|
// for Y types where it's possible to convert Y* to T*.
|
|
|
|
// typename = typename enable_if<is_convertible<Y*, T*>::value>::type
|
|
|
|
// thanks to that, clang will not create some functions that would cause
|
|
|
|
// compilation errors. More info:
|
|
|
|
// http://en.cppreference.com/w/cpp/language/sfinae
|
|
|
|
template <class Y,
|
|
|
|
typename = typename enable_if<is_convertible<Y*, T*>::value>::type>
|
|
|
|
explicit shared_ptr(Y* p) {
|
|
|
|
model_set(__cast_to_infer_ptr(this), p);
|
|
|
|
}
|
|
|
|
|
|
|
|
template <class Y,
|
|
|
|
class D,
|
|
|
|
typename = typename enable_if<is_convertible<Y*, T*>::value>::type>
|
|
|
|
shared_ptr(Y* p, D d) : shared_ptr<T>(p) {}
|
|
|
|
|
|
|
|
template <class Y,
|
|
|
|
class D,
|
|
|
|
class A,
|
|
|
|
typename = typename enable_if<is_convertible<Y*, T*>::value>::type>
|
|
|
|
shared_ptr(Y* p, D d, A a) : shared_ptr<T>(p) {}
|
|
|
|
|
|
|
|
template <class D>
|
|
|
|
shared_ptr(nullptr_t p, D d) : shared_ptr<T>(p) {}
|
|
|
|
|
|
|
|
template <class D, class A>
|
|
|
|
shared_ptr(nullptr_t p, D d, A a) : shared_ptr<T>(p) {}
|
|
|
|
|
|
|
|
template <class Y>
|
|
|
|
shared_ptr(const shared_ptr<Y>& r, T* p) noexcept {
|
|
|
|
model_set(__cast_to_infer_ptr(this), p); /* TODO */
|
|
|
|
}
|
|
|
|
|
|
|
|
shared_ptr(const shared_ptr& r) noexcept {
|
|
|
|
model_copy(__cast_to_infer_ptr(this), __cast_to_infer_ptr(&r));
|
|
|
|
}
|
|
|
|
|
|
|
|
template <class Y,
|
|
|
|
typename = typename enable_if<is_convertible<Y*, T*>::value>::type>
|
|
|
|
shared_ptr(const shared_ptr<Y>& r) noexcept {
|
|
|
|
model_copy(__cast_to_infer_ptr(this), __cast_to_infer_ptr(&r));
|
|
|
|
}
|
|
|
|
|
|
|
|
shared_ptr(shared_ptr&& r) noexcept {
|
|
|
|
model_move(__cast_to_infer_ptr(this), __cast_to_infer_ptr(&r));
|
|
|
|
}
|
|
|
|
|
|
|
|
template <class Y,
|
|
|
|
typename = typename enable_if<is_convertible<Y*, T*>::value>::type>
|
|
|
|
shared_ptr(shared_ptr<Y>&& r) noexcept {
|
|
|
|
model_move(__cast_to_infer_ptr(this), __cast_to_infer_ptr(&r));
|
|
|
|
}
|
|
|
|
|
|
|
|
template <class Y,
|
|
|
|
typename = typename enable_if<is_convertible<Y*, T*>::value>::type>
|
|
|
|
explicit shared_ptr(const weak_ptr<Y>& r) : shared_ptr(std::move(r.lock())) {
|
|
|
|
// TODO: throw if r is empty
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Because of implementation differences between libc++ and stdlibc++, don't
|
|
|
|
* define this constructor (it will be defined elsewhere in case of
|
|
|
|
* stdlibc++). Because it may be defined elsewhere, don't check whether Y*
|
|
|
|
* converts to T* - otherwise there might be compilation error (out-of-line
|
|
|
|
* definition).
|
|
|
|
* No definition here might cause compilation problems if project is
|
|
|
|
* using auto_ptrs with libc++ */
|
|
|
|
template <class Y>
|
|
|
|
shared_ptr(auto_ptr<Y>&& r); // {}
|
|
|
|
|
|
|
|
template <class Y,
|
|
|
|
class D,
|
|
|
|
typename = typename enable_if<is_convertible<Y*, T*>::value>::type>
|
|
|
|
shared_ptr(unique_ptr<Y, D>&& r) : shared_ptr<T>(r.release()) {}
|
|
|
|
|
|
|
|
// destructor:
|
|
|
|
~shared_ptr() { reset((T*)nullptr); }
|
|
|
|
|
|
|
|
// assignment:
|
|
|
|
shared_ptr& operator=(const shared_ptr& r) noexcept {
|
|
|
|
// shared_ptr<T>(r).swap(*this);
|
|
|
|
model_copy(__cast_to_infer_ptr(this), __cast_to_infer_ptr(&r));
|
|
|
|
return *this;
|
|
|
|
}
|
|
|
|
|
|
|
|
template <class Y,
|
|
|
|
typename = typename enable_if<is_convertible<Y*, T*>::value>::type>
|
|
|
|
shared_ptr& operator=(const shared_ptr<Y>& r) noexcept {
|
|
|
|
// shared_ptr<T>(r).swap(*this);
|
|
|
|
model_copy(__cast_to_infer_ptr(this), __cast_to_infer_ptr(&r));
|
|
|
|
return *this;
|
|
|
|
}
|
|
|
|
|
|
|
|
shared_ptr& operator=(shared_ptr&& r) noexcept {
|
|
|
|
// shared_ptr<T>(std::move(r)).swap(*this);
|
|
|
|
model_move(__cast_to_infer_ptr(this), __cast_to_infer_ptr(&r));
|
|
|
|
return *this;
|
|
|
|
}
|
|
|
|
|
|
|
|
template <class Y,
|
|
|
|
typename = typename enable_if<is_convertible<Y*, T*>::value>::type>
|
|
|
|
shared_ptr& operator=(shared_ptr<Y>&& r) {
|
|
|
|
// shared_ptr<T>(std::move(r)).swap(*this);
|
|
|
|
model_move(__cast_to_infer_ptr(this), __cast_to_infer_ptr(&r));
|
|
|
|
return *this;
|
|
|
|
}
|
|
|
|
|
|
|
|
template <class Y,
|
|
|
|
typename = typename enable_if<is_convertible<Y*, T*>::value>::type>
|
|
|
|
shared_ptr& operator=(auto_ptr<Y>&& r) { /* ?? */
|
|
|
|
}
|
|
|
|
template <class Y,
|
|
|
|
class D,
|
|
|
|
typename = typename enable_if<is_convertible<Y*, T*>::value>::type>
|
|
|
|
shared_ptr& operator=(unique_ptr<Y, D>&& r) {
|
|
|
|
// shared_ptr<T>(std::move(r)).swap(*this);
|
|
|
|
return *this;
|
|
|
|
}
|
|
|
|
|
|
|
|
// modifiers:
|
|
|
|
void swap(shared_ptr& r) noexcept {
|
|
|
|
model_swap(__cast_to_infer_ptr(this), __cast_to_infer_ptr(&r));
|
|
|
|
}
|
|
|
|
|
|
|
|
void reset() noexcept { reset((T*)nullptr); }
|
|
|
|
|
|
|
|
template <class Y,
|
|
|
|
typename = typename enable_if<is_convertible<Y*, T*>::value>::type>
|
|
|
|
void reset(Y* p) {
|
|
|
|
/*
|
|
|
|
if (unique()) {
|
|
|
|
delete __data;
|
|
|
|
}
|
|
|
|
*/
|
|
|
|
model_set(__cast_to_infer_ptr(this), p);
|
|
|
|
// TODO adjust refcounts
|
|
|
|
}
|
|
|
|
|
|
|
|
template <class Y,
|
|
|
|
class D,
|
|
|
|
typename = typename enable_if<is_convertible<Y*, T*>::value>::type>
|
|
|
|
void reset(Y* p, D d) {
|
|
|
|
reset(p);
|
|
|
|
}
|
|
|
|
|
|
|
|
template <class Y,
|
|
|
|
class D,
|
|
|
|
class A,
|
|
|
|
typename = typename enable_if<is_convertible<Y*, T*>::value>::type>
|
|
|
|
void reset(Y* p, D d, A a) {
|
|
|
|
reset(p);
|
|
|
|
}
|
|
|
|
|
|
|
|
// observers:
|
|
|
|
T* get() const noexcept INFER_MODEL_AS_DEREF_FIRST_ARG;
|
|
|
|
|
|
|
|
typename std::add_lvalue_reference<T>::type operator*() const
|
|
|
|
noexcept INFER_MODEL_AS_DEREF_FIRST_ARG;
|
|
|
|
|
|
|
|
T* operator->() const noexcept INFER_MODEL_AS_DEREF_FIRST_ARG;
|
|
|
|
long use_count() const noexcept { return 2; /* FIXME */ }
|
|
|
|
bool unique() const noexcept { return use_count() == 1; /* FIXME */ }
|
|
|
|
explicit operator bool() const noexcept {
|
|
|
|
// for some reason analyzer can't cast to bool correctly, trick with two
|
|
|
|
// negations creates right specs for this function
|
|
|
|
return !!(bool)(*__cast_to_infer_ptr(this));
|
|
|
|
}
|
|
|
|
template <class U>
|
|
|
|
bool owner_before(shared_ptr<U> const& b) const {
|
|
|
|
return true; /* FIXME - use non-det*/
|
|
|
|
}
|
|
|
|
template <class U>
|
|
|
|
bool owner_before(weak_ptr<U> const& b) const {
|
|
|
|
return true; /* FIXME - use non-det */
|
|
|
|
}
|
|
|
|
};
|
|
|
|
template <class _Tp, class _Up>
|
|
|
|
inline bool operator==(const shared_ptr<_Tp>& __x,
|
|
|
|
const shared_ptr<_Up>& __y) noexcept {
|
|
|
|
return __x.get() == __y.get();
|
|
|
|
}
|
|
|
|
|
|
|
|
template <class _Tp, class _Up>
|
|
|
|
inline bool operator!=(const shared_ptr<_Tp>& __x,
|
|
|
|
const shared_ptr<_Up>& __y) noexcept {
|
|
|
|
return !(__x == __y);
|
|
|
|
}
|
|
|
|
|
|
|
|
template <class _Tp, class _Up>
|
|
|
|
inline bool operator<(const shared_ptr<_Tp>& __x,
|
|
|
|
const shared_ptr<_Up>& __y) noexcept {
|
|
|
|
typedef typename common_type<_Tp*, _Up*>::type _Vp;
|
|
|
|
return less<_Vp>()(__x.get(), __y.get());
|
|
|
|
}
|
|
|
|
|
|
|
|
template <class _Tp, class _Up>
|
|
|
|
inline bool operator>(const shared_ptr<_Tp>& __x,
|
|
|
|
const shared_ptr<_Up>& __y) noexcept {
|
|
|
|
return __y < __x;
|
|
|
|
}
|
|
|
|
|
|
|
|
template <class _Tp, class _Up>
|
|
|
|
inline bool operator<=(const shared_ptr<_Tp>& __x,
|
|
|
|
const shared_ptr<_Up>& __y) noexcept {
|
|
|
|
return !(__y < __x);
|
|
|
|
}
|
|
|
|
|
|
|
|
template <class _Tp, class _Up>
|
|
|
|
inline bool operator>=(const shared_ptr<_Tp>& __x,
|
|
|
|
const shared_ptr<_Up>& __y) noexcept {
|
|
|
|
return !(__x < __y);
|
|
|
|
}
|
|
|
|
|
|
|
|
template <class _Tp>
|
|
|
|
inline bool operator==(const shared_ptr<_Tp>& __x, nullptr_t) noexcept {
|
|
|
|
return !__x;
|
|
|
|
}
|
|
|
|
|
|
|
|
template <class _Tp>
|
|
|
|
inline bool operator==(nullptr_t, const shared_ptr<_Tp>& __x) noexcept {
|
|
|
|
return !__x;
|
|
|
|
}
|
|
|
|
|
|
|
|
template <class _Tp>
|
|
|
|
inline bool operator!=(const shared_ptr<_Tp>& __x, nullptr_t) noexcept {
|
|
|
|
return static_cast<bool>(__x);
|
|
|
|
}
|
|
|
|
|
|
|
|
template <class _Tp>
|
|
|
|
inline bool operator!=(nullptr_t, const shared_ptr<_Tp>& __x) noexcept {
|
|
|
|
return static_cast<bool>(__x);
|
|
|
|
}
|
|
|
|
|
|
|
|
template <class _Tp>
|
|
|
|
inline bool operator<(const shared_ptr<_Tp>& __x, nullptr_t) noexcept {
|
|
|
|
return less<_Tp*>()(__x.get(), nullptr);
|
|
|
|
}
|
|
|
|
|
|
|
|
template <class _Tp>
|
|
|
|
inline bool operator<(nullptr_t, const shared_ptr<_Tp>& __x) noexcept {
|
|
|
|
return less<_Tp*>()(nullptr, __x.get());
|
|
|
|
}
|
|
|
|
|
|
|
|
template <class _Tp>
|
|
|
|
inline bool operator>(const shared_ptr<_Tp>& __x, nullptr_t) noexcept {
|
|
|
|
return nullptr < __x;
|
|
|
|
}
|
|
|
|
|
|
|
|
template <class _Tp>
|
|
|
|
inline bool operator>(nullptr_t, const shared_ptr<_Tp>& __x) noexcept {
|
|
|
|
return __x < nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
template <class _Tp>
|
|
|
|
inline bool operator<=(const shared_ptr<_Tp>& __x, nullptr_t) noexcept {
|
|
|
|
return !(nullptr < __x);
|
|
|
|
}
|
|
|
|
|
|
|
|
template <class _Tp>
|
|
|
|
inline bool operator<=(nullptr_t, const shared_ptr<_Tp>& __x) noexcept {
|
|
|
|
return !(__x < nullptr);
|
|
|
|
}
|
|
|
|
|
|
|
|
template <class _Tp>
|
|
|
|
inline bool operator>=(const shared_ptr<_Tp>& __x, nullptr_t) noexcept {
|
|
|
|
return !(__x < nullptr);
|
|
|
|
}
|
|
|
|
|
|
|
|
template <class _Tp>
|
|
|
|
inline bool operator>=(nullptr_t, const shared_ptr<_Tp>& __x) noexcept {
|
|
|
|
return !(nullptr < __x);
|
|
|
|
}
|
|
|
|
|
|
|
|
template <class T>
|
|
|
|
struct hash<shared_ptr<T>> : public hash<std__shared_ptr<T>> {};
|
|
|
|
|
|
|
|
// shared_ptr casts - call original functions but change return type to
|
|
|
|
// std::shared_ptr
|
|
|
|
template <class T, class U>
|
|
|
|
shared_ptr<T> static_pointer_cast(shared_ptr<U> const& r) noexcept {
|
|
|
|
return static_pointer_cast<T, U>((const std__shared_ptr<U>&)r);
|
|
|
|
}
|
|
|
|
template <class T, class U>
|
|
|
|
shared_ptr<T> dynamic_pointer_cast(shared_ptr<U> const& r) noexcept {
|
|
|
|
return dynamic_pointer_cast<T, U>((const std__shared_ptr<U>&)r);
|
|
|
|
}
|
|
|
|
template <class T, class U>
|
|
|
|
shared_ptr<T> const_pointer_cast(shared_ptr<U> const& r) noexcept {
|
|
|
|
return const_pointer_cast<T, U>((const std__shared_ptr<U>&)r);
|
|
|
|
}
|
|
|
|
|
|
|
|
template <class T>
|
|
|
|
class enable_shared_from_this : public std__enable_shared_from_this<T> {
|
|
|
|
public:
|
|
|
|
shared_ptr<T> shared_from_this() {
|
|
|
|
return std__enable_shared_from_this<T>::shared_from_this();
|
|
|
|
}
|
|
|
|
shared_ptr<T const> shared_from_this() const {
|
|
|
|
return std__enable_shared_from_this<T>::shared_from_this();
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
template <class T, class... Args>
|
|
|
|
shared_ptr<T> make_shared(Args&&... args) {
|
|
|
|
return shared_ptr<T>(::new T(std::forward<Args>(args)...));
|
|
|
|
}
|
|
|
|
|
|
|
|
template <class T>
|
|
|
|
struct owner_less;
|
|
|
|
|
|
|
|
template <class T>
|
|
|
|
struct owner_less<shared_ptr<T>>
|
|
|
|
: binary_function<shared_ptr<T>, shared_ptr<T>, bool> {
|
|
|
|
typedef bool result_type;
|
|
|
|
|
|
|
|
bool operator()(shared_ptr<T> const& x, shared_ptr<T> const& y) const {
|
|
|
|
return x.owner_before(y);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool operator()(shared_ptr<T> const& x, weak_ptr<T> const& y) const {
|
|
|
|
return x.owner_before(y);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool operator()(weak_ptr<T> const& x, shared_ptr<T> const& y) const {
|
|
|
|
return x.owner_before(y);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
#undef __cast_to_infer_ptr
|
|
|
|
INFER_NAMESPACE_STD_END
|