510 lines
16 KiB
C++
510 lines
16 KiB
C++
/*
|
|
* Copyright (c) Facebook, Inc. and its affiliates.
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
#include <memory>
|
|
#include <utility>
|
|
|
|
#include <folly/Portability.h>
|
|
#include <folly/lang/Exception.h>
|
|
|
|
namespace folly {
|
|
|
|
namespace detail {
|
|
template <typename T>
|
|
struct is_not_null_helper : std::false_type {};
|
|
template <typename T>
|
|
struct is_not_null_helper<not_null<T>> : std::true_type {};
|
|
template <typename T>
|
|
struct is_not_null
|
|
: is_not_null_helper<std::remove_cv_t<std::remove_reference_t<T>>> {};
|
|
template <typename T>
|
|
inline constexpr bool is_not_null_v = is_not_null<T>::value;
|
|
|
|
template <typename T, typename = std::enable_if_t<!is_not_null_v<T>>>
|
|
auto maybeUnwrap(T&& t) {
|
|
return std::forward<T>(t);
|
|
}
|
|
template <typename T>
|
|
auto maybeUnwrap(const not_null_base<T>& t) {
|
|
return t.unwrap();
|
|
}
|
|
template <typename T>
|
|
auto maybeUnwrap(not_null_base<T>&& t) {
|
|
return std::move(t).unwrap();
|
|
}
|
|
|
|
template <typename T>
|
|
struct maybe_unwrap_not_null {
|
|
using type = T;
|
|
};
|
|
template <typename T>
|
|
struct maybe_unwrap_not_null<const T> {
|
|
using type = const typename maybe_unwrap_not_null<T>::type;
|
|
};
|
|
template <typename T>
|
|
struct maybe_unwrap_not_null<T&> {
|
|
using type = typename maybe_unwrap_not_null<T>::type&;
|
|
};
|
|
template <typename T>
|
|
struct maybe_unwrap_not_null<T&&> {
|
|
using type = typename maybe_unwrap_not_null<T>::type&&;
|
|
};
|
|
template <typename PtrT>
|
|
struct maybe_unwrap_not_null<not_null<PtrT>> {
|
|
using type = PtrT;
|
|
};
|
|
|
|
template <typename FromT, typename ToT>
|
|
struct is_not_null_convertible
|
|
: std::is_convertible<
|
|
typename maybe_unwrap_not_null<FromT>::type,
|
|
typename maybe_unwrap_not_null<ToT>::type> {};
|
|
|
|
template <typename FromT, typename ToPtrT>
|
|
struct is_not_null_nothrow_constructible
|
|
: std::integral_constant<
|
|
bool,
|
|
is_not_null_v<FromT> &&
|
|
std::is_nothrow_constructible_v<ToPtrT, FromT>> {};
|
|
|
|
struct secret_guaranteed_not_null : guaranteed_not_null_provider {
|
|
static guaranteed_not_null get() {
|
|
return guaranteed_not_null_provider::guaranteed_not_null();
|
|
}
|
|
};
|
|
|
|
// In order to be able to cast from not_null<PtrT> to ToT:
|
|
// - It must not already be castable, otherwise the compiler will raise an
|
|
// ambiguity error
|
|
// - PtrT must be castable to ToT
|
|
template <typename FromPtrT, typename ToT>
|
|
struct is_not_null_castable
|
|
: std::integral_constant<
|
|
bool,
|
|
std::is_convertible_v<const FromPtrT&, ToT> &&
|
|
!std::is_convertible_v<const not_null<FromPtrT>&, ToT>> {};
|
|
template <typename FromPtrT, typename ToT>
|
|
struct is_not_null_move_castable
|
|
: std::integral_constant<
|
|
bool,
|
|
std::is_convertible_v<FromPtrT&&, ToT> &&
|
|
!std::is_convertible_v<not_null<FromPtrT>&&, ToT>> {};
|
|
|
|
template <typename T, typename = decltype(*std::declval<T*>() == nullptr)>
|
|
inline std::true_type is_comparable_to_nullptr_fn(const T&) {
|
|
return {};
|
|
}
|
|
inline std::false_type is_comparable_to_nullptr_fn(...) {
|
|
return {};
|
|
}
|
|
template <typename T>
|
|
constexpr bool is_comparable_to_nullptr_v =
|
|
decltype(is_comparable_to_nullptr_fn(*std::declval<T*>()))::value;
|
|
} // namespace detail
|
|
|
|
template <typename PtrT>
|
|
template <typename U>
|
|
not_null_base<PtrT>::not_null_base(U&& u, private_tag)
|
|
: ptr_(detail::maybeUnwrap(std::forward<U>(u))) {
|
|
if constexpr (!detail::is_not_null_v<U>) {
|
|
throw_if_null();
|
|
}
|
|
}
|
|
|
|
template <typename PtrT>
|
|
not_null_base<PtrT>::not_null_base(
|
|
PtrT&& ptr, guaranteed_not_null_provider::guaranteed_not_null) noexcept
|
|
: ptr_(std::move(ptr)) {}
|
|
|
|
template <typename PtrT>
|
|
template <typename U, typename>
|
|
not_null_base<PtrT>::not_null_base(U&& u, implicit_tag<true>) noexcept(
|
|
detail::is_not_null_nothrow_constructible<U&&, PtrT>::value)
|
|
: not_null_base(std::forward<U>(u), private_tag{}) {}
|
|
|
|
template <typename PtrT>
|
|
template <typename U, typename>
|
|
not_null_base<PtrT>::not_null_base(U&& u, implicit_tag<false>) noexcept(
|
|
detail::is_not_null_nothrow_constructible<U&&, PtrT>::value)
|
|
: not_null_base(std::forward<U>(u), private_tag{}) {}
|
|
|
|
template <typename PtrT>
|
|
typename not_null_base<PtrT>::element_type& not_null_base<PtrT>::operator*()
|
|
const noexcept {
|
|
return *unwrap();
|
|
}
|
|
|
|
template <typename PtrT>
|
|
const PtrT& not_null_base<PtrT>::operator->() const noexcept {
|
|
return unwrap();
|
|
}
|
|
|
|
template <typename PtrT>
|
|
not_null_base<PtrT>::operator const PtrT&() const& noexcept {
|
|
return unwrap();
|
|
}
|
|
|
|
template <typename PtrT>
|
|
not_null_base<PtrT>::operator PtrT&&() && noexcept {
|
|
return std::move(*this).unwrap();
|
|
}
|
|
|
|
template <typename PtrT>
|
|
template <typename U, typename>
|
|
not_null_base<PtrT>::operator U() const& noexcept(
|
|
std::is_nothrow_constructible_v<U, const PtrT&>) {
|
|
if constexpr (detail::is_not_null_v<U>) {
|
|
return U(*this);
|
|
}
|
|
return U(unwrap());
|
|
}
|
|
|
|
template <typename PtrT>
|
|
template <typename U, typename>
|
|
not_null_base<PtrT>::operator U() && noexcept(
|
|
std::is_nothrow_constructible_v<U, PtrT&&>) {
|
|
if constexpr (detail::is_not_null_v<U>) {
|
|
return U(std::move(*this));
|
|
}
|
|
return U(std::move(*this).unwrap());
|
|
}
|
|
|
|
template <typename PtrT>
|
|
void not_null_base<PtrT>::swap(not_null_base& other) noexcept {
|
|
mutable_unwrap().swap(other.mutable_unwrap());
|
|
}
|
|
|
|
template <typename PtrT>
|
|
const PtrT& not_null_base<PtrT>::unwrap() const& noexcept {
|
|
if constexpr (folly::kIsDebug) {
|
|
terminate_if_null(ptr_);
|
|
}
|
|
return ptr_;
|
|
}
|
|
|
|
template <typename PtrT>
|
|
PtrT&& not_null_base<PtrT>::unwrap() && noexcept {
|
|
if constexpr (folly::kIsDebug) {
|
|
terminate_if_null(ptr_);
|
|
}
|
|
return std::move(ptr_);
|
|
}
|
|
|
|
template <typename PtrT>
|
|
PtrT& not_null_base<PtrT>::mutable_unwrap() noexcept {
|
|
return const_cast<PtrT&>(const_cast<const not_null_base&>(*this).unwrap());
|
|
}
|
|
|
|
template <typename PtrT>
|
|
void not_null_base<PtrT>::throw_if_null() const {
|
|
throw_if_null(ptr_);
|
|
}
|
|
|
|
template <typename PtrT>
|
|
template <typename T>
|
|
void not_null_base<PtrT>::throw_if_null(const T& ptr) {
|
|
if (ptr == nullptr) {
|
|
folly::throw_exception<std::invalid_argument>("non_null<PtrT> is null");
|
|
}
|
|
}
|
|
|
|
template <typename PtrT>
|
|
template <typename T>
|
|
void not_null_base<PtrT>::terminate_if_null(const T& ptr) {
|
|
if (ptr == nullptr) {
|
|
folly::terminate_with<std::runtime_error>(
|
|
"not_null internal pointer is null");
|
|
}
|
|
}
|
|
|
|
template <typename PtrT>
|
|
template <typename Deleter>
|
|
Deleter&& not_null_base<PtrT>::forward_or_throw_if_null(Deleter&& deleter) {
|
|
if constexpr (detail::is_comparable_to_nullptr_v<Deleter>) {
|
|
if (deleter == nullptr) {
|
|
folly::throw_exception<std::invalid_argument>(
|
|
"non_null<PtrT> deleter is null");
|
|
}
|
|
}
|
|
return std::forward<Deleter>(deleter);
|
|
}
|
|
|
|
/**
|
|
* not_null<std::unique_ptr<>> specialization.
|
|
*/
|
|
template <typename T, typename Deleter>
|
|
not_null<std::unique_ptr<T, Deleter>>::not_null(pointer p, const Deleter& d)
|
|
: not_null_base<std::unique_ptr<T, Deleter>>(
|
|
std::unique_ptr<T, Deleter>(
|
|
std::move(p).unwrap(), this->forward_or_throw_if_null(d)),
|
|
guaranteed_not_null_provider::guaranteed_not_null()) {}
|
|
|
|
template <typename T, typename Deleter>
|
|
not_null<std::unique_ptr<T, Deleter>>::not_null(pointer p, Deleter&& d)
|
|
: not_null_base<std::unique_ptr<T, Deleter>>(
|
|
std::unique_ptr<T, Deleter>(
|
|
std::move(p).unwrap(),
|
|
this->forward_or_throw_if_null(std::move(d))),
|
|
guaranteed_not_null_provider::guaranteed_not_null()) {}
|
|
|
|
template <typename T, typename Deleter>
|
|
void not_null<std::unique_ptr<T, Deleter>>::reset(pointer ptr) noexcept {
|
|
this->mutable_unwrap().reset(ptr.unwrap());
|
|
}
|
|
|
|
template <typename T, typename Deleter>
|
|
typename not_null<std::unique_ptr<T, Deleter>>::pointer
|
|
not_null<std::unique_ptr<T, Deleter>>::get() const noexcept {
|
|
return pointer(
|
|
this->unwrap().get(),
|
|
guaranteed_not_null_provider::guaranteed_not_null());
|
|
}
|
|
|
|
template <typename T, typename Deleter>
|
|
Deleter& not_null<std::unique_ptr<T, Deleter>>::get_deleter() noexcept {
|
|
return this->mutable_unwrap().get_deleter();
|
|
}
|
|
|
|
template <typename T, typename Deleter>
|
|
const Deleter& not_null<std::unique_ptr<T, Deleter>>::get_deleter()
|
|
const noexcept {
|
|
return this->unwrap().get_deleter();
|
|
}
|
|
|
|
template <typename T, typename... Args>
|
|
not_null_unique_ptr<T> make_not_null_unique(Args&&... args) {
|
|
return not_null_unique_ptr<T>(
|
|
std::make_unique<T>(std::forward<Args>(args)...),
|
|
detail::secret_guaranteed_not_null::get());
|
|
}
|
|
|
|
/**
|
|
* not_null<std::shared_ptr<>> specialization.
|
|
*/
|
|
template <typename T>
|
|
template <typename U, typename Deleter>
|
|
not_null<std::shared_ptr<T>>::not_null(U* ptr, Deleter d)
|
|
: not_null_base<std::shared_ptr<T>>(std::shared_ptr<T>(
|
|
ptr, this->forward_or_throw_if_null(std::move(d)))) {}
|
|
|
|
template <typename T>
|
|
template <typename U, typename Deleter>
|
|
not_null<std::shared_ptr<T>>::not_null(not_null<U*> ptr, Deleter d)
|
|
: not_null_base<std::shared_ptr<T>>(
|
|
std::shared_ptr<T>(
|
|
ptr.unwrap(), this->forward_or_throw_if_null(std::move(d))),
|
|
guaranteed_not_null_provider::guaranteed_not_null()) {}
|
|
|
|
template <typename T>
|
|
template <typename U>
|
|
not_null<std::shared_ptr<T>>::not_null(
|
|
const std::shared_ptr<U>& r, not_null<element_type*> ptr) noexcept
|
|
: not_null_base<std::shared_ptr<T>>(
|
|
std::shared_ptr<T>(r, ptr.unwrap()),
|
|
guaranteed_not_null_provider::guaranteed_not_null()) {}
|
|
|
|
template <typename T>
|
|
template <typename U>
|
|
not_null<std::shared_ptr<T>>::not_null(
|
|
const not_null<std::shared_ptr<U>>& r, not_null<element_type*> ptr) noexcept
|
|
: not_null_base<std::shared_ptr<T>>(
|
|
std::shared_ptr<T>(r.unwrap(), ptr.unwrap()),
|
|
guaranteed_not_null_provider::guaranteed_not_null()) {}
|
|
|
|
template <typename T>
|
|
template <typename U>
|
|
not_null<std::shared_ptr<T>>::not_null(
|
|
std::shared_ptr<U>&& r, not_null<element_type*> ptr) noexcept
|
|
: not_null_base<std::shared_ptr<T>>(
|
|
std::shared_ptr<T>(std::move(r), ptr.unwrap()),
|
|
guaranteed_not_null_provider::guaranteed_not_null()) {}
|
|
|
|
template <typename T>
|
|
template <typename U>
|
|
not_null<std::shared_ptr<T>>::not_null(
|
|
not_null<std::shared_ptr<U>>&& r, not_null<element_type*> ptr) noexcept
|
|
: not_null_base<std::shared_ptr<T>>(
|
|
std::shared_ptr<T>(std::move(r).unwrap(), ptr.unwrap()),
|
|
guaranteed_not_null_provider::guaranteed_not_null()) {}
|
|
|
|
template <typename T>
|
|
template <typename U>
|
|
void not_null<std::shared_ptr<T>>::reset(U* ptr) {
|
|
this->throw_if_null(ptr);
|
|
this->mutable_unwrap().reset(ptr);
|
|
}
|
|
|
|
template <typename T>
|
|
template <typename U>
|
|
void not_null<std::shared_ptr<T>>::reset(not_null<U*> ptr) noexcept {
|
|
this->mutable_unwrap().reset(ptr.unwrap());
|
|
}
|
|
|
|
template <typename T>
|
|
template <typename U, typename Deleter>
|
|
void not_null<std::shared_ptr<T>>::reset(U* ptr, Deleter d) {
|
|
this->throw_if_null(ptr);
|
|
this->mutable_unwrap().reset(
|
|
ptr, this->forward_or_throw_if_null(std::move(d)));
|
|
}
|
|
|
|
template <typename T>
|
|
template <typename U, typename Deleter>
|
|
void not_null<std::shared_ptr<T>>::reset(not_null<U*> ptr, Deleter d) {
|
|
this->mutable_unwrap().reset(
|
|
ptr.unwrap(), this->forward_or_throw_if_null(std::move(d)));
|
|
}
|
|
|
|
template <typename T>
|
|
typename not_null<std::shared_ptr<T>>::pointer
|
|
not_null<std::shared_ptr<T>>::get() const noexcept {
|
|
return pointer(
|
|
this->unwrap().get(),
|
|
guaranteed_not_null_provider::guaranteed_not_null());
|
|
}
|
|
|
|
template <typename T>
|
|
long not_null<std::shared_ptr<T>>::use_count() const noexcept {
|
|
return this->unwrap().use_count();
|
|
}
|
|
|
|
template <typename T>
|
|
template <typename U>
|
|
bool not_null<std::shared_ptr<T>>::owner_before(
|
|
const std::shared_ptr<U>& other) const noexcept {
|
|
return this->unwrap().owner_before(other);
|
|
}
|
|
|
|
template <typename T>
|
|
template <typename U>
|
|
bool not_null<std::shared_ptr<T>>::owner_before(
|
|
const not_null<std::shared_ptr<U>>& other) const noexcept {
|
|
return this->unwrap().owner_before(other.unwrap());
|
|
}
|
|
|
|
template <typename T, typename... Args>
|
|
not_null_shared_ptr<T> make_not_null_shared(Args&&... args) {
|
|
return not_null_shared_ptr<T>(
|
|
std::make_shared<T>(std::forward<Args>(args)...),
|
|
detail::secret_guaranteed_not_null::get());
|
|
}
|
|
|
|
template <typename T, typename Alloc, typename... Args>
|
|
not_null_shared_ptr<T> allocate_not_null_shared(
|
|
const Alloc& alloc, Args&&... args) {
|
|
return not_null_shared_ptr<T>(
|
|
std::allocate_shared<T, Alloc, Args...>(
|
|
alloc, std::forward<Args>(args)...),
|
|
detail::secret_guaranteed_not_null::get());
|
|
}
|
|
|
|
/**
|
|
* Comparators.
|
|
*/
|
|
#define FB_NOT_NULL_MK_OP(op) \
|
|
template <typename PtrT, typename T> \
|
|
bool operator op(const not_null<PtrT>& lhs, const T& rhs) { \
|
|
return lhs.unwrap() op rhs; \
|
|
} \
|
|
template <typename PtrT, typename T, typename> \
|
|
bool operator op(const T& lhs, const not_null<PtrT>& rhs) { \
|
|
return lhs op rhs.unwrap(); \
|
|
}
|
|
|
|
FB_NOT_NULL_MK_OP(==)
|
|
FB_NOT_NULL_MK_OP(!=)
|
|
FB_NOT_NULL_MK_OP(<)
|
|
FB_NOT_NULL_MK_OP(<=)
|
|
FB_NOT_NULL_MK_OP(>)
|
|
FB_NOT_NULL_MK_OP(>=)
|
|
#undef FB_NOT_NULL_MK_OP
|
|
|
|
/**
|
|
* Output.
|
|
*/
|
|
template <typename U, typename V, typename PtrT>
|
|
std::basic_ostream<U, V>& operator<<(
|
|
std::basic_ostream<U, V>& os, const not_null<PtrT>& ptr) {
|
|
return os << ptr.unwrap();
|
|
}
|
|
|
|
/**
|
|
* Swap
|
|
*/
|
|
template <typename PtrT>
|
|
void swap(not_null<PtrT>& lhs, not_null<PtrT>& rhs) noexcept {
|
|
lhs.swap(rhs);
|
|
}
|
|
|
|
/**
|
|
* Getters
|
|
*/
|
|
template <typename Deleter, typename T>
|
|
Deleter* get_deleter(const not_null_shared_ptr<T>& ptr) {
|
|
return std::get_deleter<Deleter>(ptr.unwrap());
|
|
}
|
|
|
|
/**
|
|
* Casting
|
|
*/
|
|
template <typename T, typename U>
|
|
not_null_shared_ptr<T> static_pointer_cast(const not_null_shared_ptr<U>& r) {
|
|
auto p = std::static_pointer_cast<T, U>(r.unwrap());
|
|
return not_null_shared_ptr<T>(
|
|
std::move(p), detail::secret_guaranteed_not_null::get());
|
|
}
|
|
template <typename T, typename U>
|
|
not_null_shared_ptr<T> static_pointer_cast(not_null_shared_ptr<U>&& r) {
|
|
auto p = std::static_pointer_cast<T, U>(std::move(r).unwrap());
|
|
return not_null_shared_ptr<T>(
|
|
std::move(p), detail::secret_guaranteed_not_null::get());
|
|
}
|
|
template <typename T, typename U>
|
|
std::shared_ptr<T> dynamic_pointer_cast(const not_null_shared_ptr<U>& r) {
|
|
return std::dynamic_pointer_cast<T, U>(r.unwrap());
|
|
}
|
|
template <typename T, typename U>
|
|
std::shared_ptr<T> dynamic_pointer_cast(not_null_shared_ptr<U>&& r) {
|
|
return std::dynamic_pointer_cast<T, U>(std::move(r).unwrap());
|
|
}
|
|
template <typename T, typename U>
|
|
not_null_shared_ptr<T> const_pointer_cast(const not_null_shared_ptr<U>& r) {
|
|
auto p = std::const_pointer_cast<T, U>(r.unwrap());
|
|
return not_null_shared_ptr<T>(
|
|
std::move(p), detail::secret_guaranteed_not_null::get());
|
|
}
|
|
template <typename T, typename U>
|
|
not_null_shared_ptr<T> const_pointer_cast(not_null_shared_ptr<U>&& r) {
|
|
auto p = std::const_pointer_cast<T, U>(std::move(r).unwrap());
|
|
return not_null_shared_ptr<T>(
|
|
std::move(p), detail::secret_guaranteed_not_null::get());
|
|
}
|
|
template <typename T, typename U>
|
|
not_null_shared_ptr<T> reinterpret_pointer_cast(
|
|
const not_null_shared_ptr<U>& r) {
|
|
auto p = std::reinterpret_pointer_cast<T, U>(r.unwrap());
|
|
return not_null_shared_ptr<T>(
|
|
std::move(p), detail::secret_guaranteed_not_null::get());
|
|
}
|
|
template <typename T, typename U>
|
|
not_null_shared_ptr<T> reinterpret_pointer_cast(not_null_shared_ptr<U>&& r) {
|
|
auto p = std::reinterpret_pointer_cast<T, U>(std::move(r).unwrap());
|
|
return not_null_shared_ptr<T>(
|
|
std::move(p), detail::secret_guaranteed_not_null::get());
|
|
}
|
|
|
|
} // namespace folly
|