Commit 405149cf authored by Nicholas Ormrod's avatar Nicholas Ormrod Committed by Facebook GitHub Bot

Move not_null to folly

Summary:
- Changed namespace from facebook::memory to folly
- Changed include paths
- Left a stub file in common/memory/not_null.h

Differential Revision: D23740776

fbshipit-source-id: 6eff6823017e3a5d20a540fde10b99aa0381d4b0
parent 53874b38
/*
* 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
/*
* 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.
*/
#pragma once
/**
* C++ Core Guideline's not_null PtrT>.
*
* not_null<PtrT> holds a pointer-like type PtrT which is not nullptr.
*
* not_null<T*> is a drop-in replacement for T* (as long as it's never null).
* Specializations not_null_unique_ptr<T> and not_null_shared_ptr<T> are
* drop-in replacements for unique_ptr<T> and shared_ptr<T>, respecitively.
*
* Example:
* void foo(not_null<int*> nnpi) {
* *nnpi = 7; // Safe, since `nnpi` is not null.
* }
*
* void bar(not_null_shared_ptr<int> nnspi) {
* foo(nnsp.get());
* }
*
* Notes:
* - Constructing a not_null<PtrT> from a nullptr-equivalent argument throws
* a std::invalid_argument exception.
* - Cannot be used after move.
* - In debug mode, not_null checks that it is not null on all accesses,
* since use-after-move can cause the underlying PtrT to be null.
*/
#include <cstddef>
#include <functional>
#include <iosfwd>
#include <memory>
#include <type_traits>
namespace folly {
namespace detail {
template <typename T>
struct is_not_null;
template <typename FromT, typename ToT>
struct is_not_null_convertible;
template <typename FromT, typename ToPtrT>
struct is_not_null_nothrow_constructible;
template <typename FromPtrT, typename ToT>
struct is_not_null_castable;
template <typename FromPtrT, typename ToT>
struct is_not_null_move_castable;
} // namespace detail
class guaranteed_not_null_provider {
protected:
struct guaranteed_not_null {};
};
/**
* not_null_base, the common interface for all not_null subclasses.
* - Implicitly constructs and casts just like a PtrT.
* - Has unwrap() function to access the underlying PtrT.
*/
template <typename PtrT>
class not_null_base : protected guaranteed_not_null_provider {
template <bool>
struct implicit_tag {};
public:
using pointer = PtrT;
using element_type = typename std::pointer_traits<PtrT>::element_type;
/**
* Construction:
* - Throws std::invalid_argument if null.
* - Cannot default construct.
* - Cannot construct from nullptr.
* - Allows implicit construction iff PtrT allows implicit construction.
* - Construction from another not_null skips null check (in opt builds).
*/
not_null_base() = delete;
/* implicit */ not_null_base(std::nullptr_t) = delete;
not_null_base(const not_null_base& nn) = default;
not_null_base(not_null_base& nn) = default;
not_null_base(not_null_base&& nn) = default;
template <
typename U,
typename =
std::enable_if_t<detail::is_not_null_convertible<U&&, PtrT>::value>>
/* implicit */ not_null_base(U&& u, implicit_tag<true> = {}) noexcept(
detail::is_not_null_nothrow_constructible<U&&, PtrT>::value);
template <
typename U,
typename =
std::enable_if_t<!detail::is_not_null_convertible<U&&, PtrT>::value>>
explicit not_null_base(U&& u, implicit_tag<false> = {}) noexcept(
detail::is_not_null_nothrow_constructible<U&&, PtrT>::value);
// Allow construction without a null check for trusted callsites.
explicit not_null_base(
PtrT&& ptr,
guaranteed_not_null_provider::guaranteed_not_null) noexcept;
/**
* Assignment:
* - Due to implicit construction, just need to assign from self.
* - Cannot assign from nullptr.
*/
not_null_base& operator=(std::nullptr_t) = delete;
not_null_base& operator=(const not_null_base& nn) = default;
not_null_base& operator=(not_null_base&& nn) = default;
/**
* Dereferencing:
* - Does not return mutable references, since that would allow the
* underlying pointer to be assigned to nullptr.
*/
element_type& operator*() const noexcept;
const PtrT& operator->() const noexcept;
/**
* Casting:
* - Implicit casting to PtrT allowed, so that not_null<PtrT> can be used
* wherever a PtrT is expected.
* - Does not return mutable references, since that would allow the
* underlying pointer to be assigned to nullptr.
* - Boolean cast is always true.
*/
operator const PtrT&() const& noexcept;
operator PtrT &&() && noexcept;
template <
typename U,
typename = std::enable_if_t<detail::is_not_null_castable<PtrT, U>::value>>
operator U() const& noexcept(std::is_nothrow_constructible_v<U, const PtrT&>);
template <
typename U,
typename =
std::enable_if_t<detail::is_not_null_move_castable<PtrT, U>::value>>
operator U() && noexcept(std::is_nothrow_constructible_v<U, PtrT&&>);
explicit inline operator bool() const noexcept {
return true;
}
/**
* Swap
*/
void swap(not_null_base& other) noexcept;
/**
* Accessor:
* - Can explicitly access the underlying type via `unwrap`.
* - Does not return mutable references, since that would allow the
* underlying pointer to be assigned to nullptr.
*/
const PtrT& unwrap() const& noexcept;
PtrT&& unwrap() && noexcept;
protected:
void throw_if_null() const;
template <typename T>
static void throw_if_null(const T& ptr);
template <typename T>
static void terminate_if_null(const T& ptr);
template <typename Deleter>
static Deleter&& forward_or_throw_if_null(Deleter&& deleter);
// Non-const accessor.
PtrT& mutable_unwrap() noexcept;
private:
struct private_tag {};
template <typename U>
not_null_base(U&& u, private_tag);
PtrT ptr_;
};
/**
* not_null specializable class.
*
* Default implementation is not_null_base.
*/
template <typename PtrT>
class not_null : public not_null_base<PtrT> {
public:
using pointer = typename not_null_base<PtrT>::pointer;
using element_type = typename not_null_base<PtrT>::element_type;
using not_null_base<PtrT>::not_null_base;
};
/**
* not_null<std::unique_ptr<>> specialization.
*
* alias: not_null_unique_ptr
*
* Provides API compatibility with unique_ptr, except:
* - Pointer arguments must be non-null.
* - Cannot reset().
* - Functions are not noexcept, since debug-mode checks can throw exceptions.
* - Promotes returned pointers to be not_null pointers. Implicit casting
* allows these to be used in place of regular pointers.
*
* Notes:
* - Has make_not_null_unique, equivalent to std::make_unique
*/
template <typename T, typename Deleter>
class not_null<std::unique_ptr<T, Deleter>>
: public not_null_base<std::unique_ptr<T, Deleter>> {
public:
using pointer = not_null<typename std::unique_ptr<T, Deleter>::pointer>;
using element_type = typename std::unique_ptr<T, Deleter>::element_type;
using deleter_type = typename std::unique_ptr<T, Deleter>::deleter_type;
/**
* Constructors. Most are inherited from not_null_base.
*/
using not_null_base<std::unique_ptr<T, Deleter>>::not_null_base;
not_null(pointer p, const Deleter& d);
not_null(pointer p, Deleter&& d);
/**
* not_null_unique_ptr cannot be released - that would cause it to be null.
*/
pointer release() = delete;
/**
* not_null_unique_ptr can only be reset to a non-null pointer.
*/
void reset(std::nullptr_t) = delete;
void reset(pointer ptr) noexcept;
/**
* get() returns a not_null (pointer type is not_null<T*>).
*
* Due to implicit casting, can still capture the result of get() as a regular
* pointer type:
*
* int* ptr = not_null_unique_ptr<int>(...).get(); // valid
*/
pointer get() const noexcept;
/**
* get_deleter(): same as for unique_ptr.
*/
Deleter& get_deleter() noexcept;
const Deleter& get_deleter() const noexcept;
};
template <typename T, typename Deleter = std::default_delete<T>>
using not_null_unique_ptr = not_null<std::unique_ptr<T, Deleter>>;
template <typename T, typename... Args>
not_null_unique_ptr<T> make_not_null_unique(Args&&... args);
/**
* not_null<std::shared_ptr<>> specialization.
*
* alias: not_null_shared_ptr
*
* Provides API compatibility with shared_ptr, except:
* - Pointer arguments must be non-null.
* - Cannot reset().
* - Functions are not noexcept, since debug-mode checks can throw exceptions.
* - Promotes returned pointers to be not_null pointers. Implicit casting
* allows these to be used in place of regular pointers.
*
* Notes:
* - Has make_not_null_shared, equivalent to std::make_shared.
*/
template <typename T>
class not_null<std::shared_ptr<T>> : public not_null_base<std::shared_ptr<T>> {
public:
using element_type = typename std::shared_ptr<T>::element_type;
using pointer = not_null<element_type*>;
using weak_type = typename std::shared_ptr<T>::weak_type;
/**
* Constructors. Most are inherited from not_null_base.
*/
using not_null_base<std::shared_ptr<T>>::not_null_base;
template <typename U, typename Deleter>
not_null(U* ptr, Deleter d);
template <typename U, typename Deleter>
not_null(not_null<U*> ptr, Deleter d);
/**
* Aliasing constructors.
*
* Note:
* - The aliased shared_ptr argument, @r, is allowed to be null. The
* constructed object is not null iff @ptr is.
*/
template <typename U>
not_null(const std::shared_ptr<U>& r, not_null<element_type*> ptr) noexcept;
template <typename U>
not_null(
const not_null<std::shared_ptr<U>>& r,
not_null<element_type*> ptr) noexcept;
template <typename U>
not_null(std::shared_ptr<U>&& r, not_null<element_type*> ptr) noexcept;
template <typename U>
not_null(
not_null<std::shared_ptr<U>>&& r,
not_null<element_type*> ptr) noexcept;
/**
* not_null_shared_ptr can only be reset to a non-null pointer.
*/
void reset() = delete;
template <typename U>
void reset(U* ptr);
template <typename U>
void reset(not_null<U*> ptr) noexcept;
template <typename U, typename Deleter>
void reset(U* ptr, Deleter d);
template <typename U, typename Deleter>
void reset(not_null<U*> ptr, Deleter d);
/**
* get() returns a not_null.
*
* Due to implicit casting, can still capture the result of get() as a regular
* pointer type:
*
* int* ptr = not_null_shared_ptr<int>(...).get(); // valid
*/
pointer get() const noexcept;
/**
* use_count()
* owner_before()
*
* Same as shared_ptr.
*
* Notes:
* - unique() is deprecated in c++17, so is not implemented here. Can call
* not_null_shared_ptr.unwrap().unique() as a workaround, until unique()
* is removed in C++20.
*/
long use_count() const noexcept;
template <typename U>
bool owner_before(const std::shared_ptr<U>& other) const noexcept;
template <typename U>
bool owner_before(const not_null<std::shared_ptr<U>>& other) const noexcept;
};
template <typename T>
using not_null_shared_ptr = not_null<std::shared_ptr<T>>;
template <typename T, typename... Args>
not_null_shared_ptr<T> make_not_null_shared(Args&&... args);
template <typename T, typename Alloc, typename... Args>
not_null_shared_ptr<T> allocate_not_null_shared(
const Alloc& alloc,
Args&&... args);
/**
* Comparison:
* - Forwards to underlying PtrT.
* - Works when one of the operands is not not_null.
* - Works when one of the operands is nullptr.
*/
#define FB_NOT_NULL_MK_OP(op) \
template <typename PtrT, typename T> \
bool operator op(const not_null<PtrT>& lhs, const T& rhs); \
template < \
typename PtrT, \
typename T, \
typename = std::enable_if_t<!detail::is_not_null<T>::value>> \
bool operator op(const T& lhs, const not_null<PtrT>& rhs);
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:
* - Forwards to underlying PtrT.
*/
template <typename U, typename V, typename PtrT>
std::basic_ostream<U, V>& operator<<(
std::basic_ostream<U, V>& os,
const not_null<PtrT>& ptr);
/**
* Swap
*/
template <typename PtrT>
void swap(not_null<PtrT>& lhs, not_null<PtrT>& rhs) noexcept;
/**
* Getters
*/
template <typename Deleter, typename T>
Deleter* get_deleter(const not_null_shared_ptr<T>& ptr);
/**
* Casting
*/
template <typename T, typename U>
not_null_shared_ptr<T> static_pointer_cast(const not_null_shared_ptr<U>& r);
template <typename T, typename U>
not_null_shared_ptr<T> static_pointer_cast(not_null_shared_ptr<U>&& r);
template <typename T, typename U>
std::shared_ptr<T> dynamic_pointer_cast(const not_null_shared_ptr<U>& r);
template <typename T, typename U>
std::shared_ptr<T> dynamic_pointer_cast(not_null_shared_ptr<U>&& r);
template <typename T, typename U>
not_null_shared_ptr<T> const_pointer_cast(const not_null_shared_ptr<U>& r);
template <typename T, typename U>
not_null_shared_ptr<T> const_pointer_cast(not_null_shared_ptr<U>&& r);
template <typename T, typename U>
not_null_shared_ptr<T> reinterpret_pointer_cast(
const not_null_shared_ptr<U>& r);
template <typename T, typename U>
not_null_shared_ptr<T> reinterpret_pointer_cast(not_null_shared_ptr<U>&& r);
} // namespace folly
namespace std {
/**
* Hashing:
* - Forwards to underlying PtrT.
*/
template <typename PtrT>
struct hash<::folly::not_null<PtrT>> : hash<PtrT> {};
} // namespace std
#include <folly/memory/not_null-inl.h>
/*
* 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 <folly/memory/not_null.h>
#include <memory>
#include <strstream>
#include <unordered_set>
#include <folly/portability/GTest.h>
using namespace folly;
namespace {
struct Base {
virtual ~Base() = default;
};
struct Derived : public Base {};
struct Derived2 : public Base {};
struct my_deleter {
explicit my_deleter(int* counter) : counter_(counter) {}
void operator()(int* ptr) {
(*counter_)++;
delete ptr;
}
int* counter_;
};
} // namespace
template <typename PtrT>
void nullify(not_null<PtrT>& nn) {
// Super bad practice, but useful for testing.
const_cast<PtrT&>(nn.unwrap()) = nullptr;
}
template <typename T>
struct Wrapper {
template <typename U>
/* implicit */ Wrapper(U&& u) : t(std::forward<U>(u)) {}
T t;
};
TEST(nn, is_not_null) {
static_assert(detail::is_not_null_v<void> == false, "is_not_null failure");
static_assert(detail::is_not_null_v<int> == false, "is_not_null failure");
static_assert(detail::is_not_null_v<int*> == false, "is_not_null failure");
static_assert(
detail::is_not_null_v<const int* const> == false, "is_not_null failure");
static_assert(
detail::is_not_null_v<not_null<int*>> == true, "is_not_null failure");
static_assert(
detail::is_not_null_v<const not_null<int*>&> == true,
"is_not_null failure");
static_assert(
detail::is_not_null_v<not_null<int*>&&> == true, "is_not_null failure");
}
template <typename To, typename From>
bool ctor_throws(From&& from) {
try {
not_null<To> nn(std::forward<From>(from));
EXPECT_TRUE(nn);
} catch (std::invalid_argument&) {
return true;
}
return false;
}
TEST(nn, ctor_exception) {
Derived* dp = nullptr;
Derived d;
not_null<Derived*> nnd(&d);
nullify(nnd);
EXPECT_TRUE(ctor_throws<Derived*>(dp));
EXPECT_TRUE(ctor_throws<Base*>(dp));
EXPECT_TRUE(ctor_throws<Base*>(std::move(dp)));
// Copy/move constructor never throws
EXPECT_FALSE(ctor_throws<Derived*>(nnd));
EXPECT_FALSE((ctor_throws<Derived*, const not_null<Derived*>&>(nnd)));
// Converting constructor fails in debug mode
#ifndef NDEBUG
EXPECT_DEATH(not_null<Base*> nb1(nnd), "not_null internal pointer is null");
EXPECT_DEATH(
not_null<Base*> nb2(std::move(nnd)), "not_null internal pointer is null");
#else
EXPECT_FALSE(ctor_throws<Base*>(nnd));
EXPECT_FALSE(ctor_throws<Base*>(std::move(nnd)));
#endif
}
TEST(nn, ctor_conversion) {
int* p = new int(7);
not_null_unique_ptr<int> a(p);
not_null_shared_ptr<int> b(std::move(a));
EXPECT_EQ(*b, 7);
}
TEST(nn, explicit_construction) {
int* i = new int(7);
// Can explicitly construct a unique_ptr from a raw pointer...
not_null_unique_ptr<int> a(i);
EXPECT_EQ(*a, 7);
// ...but not implicitly; the following code does not (and should not)
// compile.
#if 0
int* j = new int(8);
auto f = [](not_null_unique_ptr<int>) {};
f(j);
#endif
}
TEST(nn, dereferencing) {
not_null_unique_ptr<std::vector<int>> nn =
std::make_unique<std::vector<int>>();
nn->push_back(2);
EXPECT_EQ((*nn)[0], 2);
}
TEST(nn, bool_cast) {
int i = 7;
not_null<int*> nn(&i);
if (nn) {
} else {
EXPECT_FALSE(true);
}
if (!nn) {
EXPECT_FALSE(true);
}
}
TEST(nn, ptr_casting) {
int i = 7;
not_null<int*> nn(&i);
auto f = [](int* p) { *p = 8; };
f(nn);
EXPECT_EQ(i, 8);
}
TEST(nn, conversion_casting) {
Derived d;
not_null<Derived*> nnd(&d);
auto f = [&](Base* b) { EXPECT_EQ(&d, b); };
f(nnd);
}
TEST(nn, move_casting) {
not_null<std::unique_ptr<Derived>> nnd = std::make_unique<Derived>();
auto f = [](std::unique_ptr<Base>) {};
f(std::move(nnd));
#ifdef NDEBUG
// use-after-move is disallowed, but possible
EXPECT_EQ(nnd.unwrap(), nullptr);
#else
EXPECT_DEATH(nnd.unwrap(), "not_null internal pointer is null");
#endif
}
// If there are multiple ways to convert, not_null's casting operator should
// yield. If the casting operator did not yield, then Wrapper could either be
// implicitly constructed from not_null, or not_null could be casted to Wrapper,
// leading to a compiler error for ambiguity.
TEST(nn, multi_cast) {
int i = 5;
not_null<int*> nn(&i);
auto f = [](Wrapper<not_null<int*>> w) { EXPECT_EQ(*w.t, 5); };
const auto& cnn = nn;
f(cnn);
f(std::move(nn));
}
TEST(nn, unwrap) {
not_null_unique_ptr<int> nn = std::make_unique<int>(7);
auto f = [](const std::unique_ptr<int>& u_i) { return *u_i; };
auto g = [](std::unique_ptr<int>&& u_i) { return *u_i; };
EXPECT_EQ(f(nn.unwrap()), 7);
EXPECT_EQ(g(std::move(nn).unwrap()), 7);
// Because g accepts an rvalue-reference, rather than a value, and does not
// move-from the argument, nn.unwrap() is still valid.
EXPECT_NE(nn.unwrap(), nullptr);
}
/**
* not_null_unique_ptr API tests
*/
TEST(nn_up, deleter) {
int counter = 0;
{
not_null_unique_ptr<int, my_deleter> a(new int(5), my_deleter(&counter));
auto d = a.get_deleter();
EXPECT_EQ(d.counter_, &counter);
}
EXPECT_EQ(counter, 1);
}
TEST(nn_up, reset) {
not_null_unique_ptr<int> a(new int(5));
int* i = new int(6);
a.reset(i);
EXPECT_EQ(*a, 6);
}
TEST(nn_up, swap) {
not_null_unique_ptr<int> a(new int(5));
not_null_unique_ptr<int> b(new int(6));
a.swap(b);
EXPECT_EQ(*a, 6);
EXPECT_EQ(*b, 5);
}
TEST(nn_up, get) {
int* i = new int(5);
not_null_unique_ptr<int> a(i);
int* p = a.get();
EXPECT_EQ(p, i);
auto g = a.get();
static_assert(
detail::is_not_null_v<decltype(g)>, "get() does not return not_null");
}
TEST(nn_up, assignment) {
not_null_unique_ptr<int> nnup(new int(5));
auto up = std::make_unique<int>(6);
nnup = std::move(up);
EXPECT_EQ(*nnup, 6);
}
/**
* not_null_shared_ptr API tests
*/
TEST(nn_sp, deleter) {
int counter = 0;
{
not_null_shared_ptr<int> nnsp(new int(5), my_deleter(&counter));
auto* deleter = get_deleter<my_deleter>(nnsp);
EXPECT_EQ(deleter->counter_, &counter);
}
EXPECT_EQ(counter, 1);
// Also test that the first argument can be a not_null pointer.
{
not_null<int*> nnp(new int(6));
not_null_shared_ptr<int> nnsp(nnp, my_deleter(&counter));
auto* deleter = get_deleter<my_deleter>(nnsp);
EXPECT_EQ(deleter->counter_, &counter);
}
EXPECT_EQ(counter, 2);
}
TEST(nn_sp, aliasing) {
auto sp = std::make_shared<int>(5);
int i = 6;
not_null_shared_ptr<int> nnsp1(sp, &i);
int j = 7;
not_null_shared_ptr<int> nnsp2(nnsp1, &j);
EXPECT_EQ(*sp, 5);
EXPECT_EQ(*nnsp1, 6);
EXPECT_EQ(*nnsp2, 7);
EXPECT_EQ(sp.use_count(), 3);
EXPECT_EQ(nnsp1.use_count(), 3);
EXPECT_EQ(nnsp2.use_count(), 3);
// The move-aliasing-constructor is a c++20 API, and falls back to the const&
// API without c++20 support. Cannot therefore test use_count().
not_null_shared_ptr<int> nnsp3(std::move(sp), &i);
EXPECT_EQ(*nnsp3, 6);
not_null_shared_ptr<int> nnsp4(std::move(nnsp1), &j);
EXPECT_EQ(*nnsp4, 7);
}
TEST(nn_sp, null_aliasing) {
int* i = new int(5);
int* j = new int(6);
std::shared_ptr<int> sp1;
std::shared_ptr<int> sp2(sp1, i);
ASSERT_NE(sp2.get(), nullptr);
EXPECT_EQ(*sp2, 5);
EXPECT_EQ(sp2.use_count(), 0);
not_null_shared_ptr<int> nnsp(sp1, j);
EXPECT_EQ(*nnsp, 6);
EXPECT_EQ(nnsp.use_count(), 0);
// Null-aliased pointers do not get deleted.
delete j;
delete i;
}
TEST(nn_sp, assignment) {
not_null_shared_ptr<int> nnsp(new int(5));
auto& ret = nnsp = std::make_unique<int>(6);
EXPECT_EQ(*nnsp, 6);
static_assert(
std::is_same<decltype(ret), not_null_shared_ptr<int>&>::value,
"operator= wrong return type");
}
TEST(nn_sp, reset) {
not_null_shared_ptr<int> nnsp(new int(5));
not_null<int*> nnp1(new int(6));
nnsp.reset(nnp1);
EXPECT_EQ(*nnsp, 6);
int* n = nullptr;
EXPECT_THROW(nnsp.reset(n), std::invalid_argument);
EXPECT_EQ(*nnsp, 6); // Strong exception guarantee.
int counter = 0;
nnsp.reset(new int(7), my_deleter(&counter));
EXPECT_EQ(*nnsp, 7);
not_null<int*> nnp2(new int(8));
nnsp.reset(nnp2);
EXPECT_EQ(counter, 1);
}
TEST(nn_sp, swap) {
not_null_shared_ptr<int> a(new int(5));
not_null_shared_ptr<int> b(new int(6));
a.swap(b);
EXPECT_EQ(*a, 6);
EXPECT_EQ(*b, 5);
}
TEST(nn_sp, get) {
not_null_shared_ptr<int> nnsp(new int(5));
auto p = nnsp.get();
EXPECT_EQ(*p, 5);
EXPECT_EQ(*nnsp, 5);
static_assert(
std::is_same_v<decltype(p), not_null<int*>>, "wrong return type");
}
TEST(nn_sp, use_count) {
not_null_shared_ptr<int> nnsp1(new int(5));
EXPECT_EQ(nnsp1.use_count(), 1);
{
not_null_shared_ptr<int> nnsp2(nnsp1);
EXPECT_EQ(nnsp1.use_count(), 2);
EXPECT_EQ(nnsp2.use_count(), 2);
}
EXPECT_EQ(nnsp1.use_count(), 1);
}
TEST(nn_sp, owner_before) {
not_null_shared_ptr<int> nnsp1(new int(5));
not_null_shared_ptr<int> nnsp2(new int(6));
auto sp = std::make_shared<int>(7);
auto f = [](const auto& p1, const auto& p2, bool same) {
bool cmp1 = p1.owner_before(p2);
bool cmp2 = p2.owner_before(p1);
if (same) {
EXPECT_FALSE(cmp1);
EXPECT_FALSE(cmp2);
} else {
EXPECT_NE(cmp1, cmp2);
}
};
f(nnsp1, nnsp1, /* same */ true);
f(nnsp1, nnsp2, /* same */ false);
nnsp1.owner_before(sp); // compiles
}
/**
* Non-member not_null helpers.
*/
TEST(nn_helper, maker) {
auto nnu = make_not_null_unique<int>(7);
EXPECT_EQ(*nnu, 7);
auto nns = make_not_null_shared<int>(8);
EXPECT_EQ(*nns, 8);
}
TEST(nn, cmp_correctness) {
int* a = new int(6);
int* b = new int(7);
not_null<int*> nna = a;
not_null<int*> nnb = b;
#define FB_NOT_NULL_CHECK_OP(op) \
do { \
bool cmp1 = a op b; \
bool cmp2 = nna op nnb; \
EXPECT_EQ(cmp1, cmp2); \
bool self1 = a op a; \
bool self2 = nna op nna; \
EXPECT_EQ(self1, self2); \
} while (0)
FB_NOT_NULL_CHECK_OP(==);
FB_NOT_NULL_CHECK_OP(!=);
FB_NOT_NULL_CHECK_OP(<);
FB_NOT_NULL_CHECK_OP(<=);
FB_NOT_NULL_CHECK_OP(>);
FB_NOT_NULL_CHECK_OP(>=);
delete b;
delete a;
}
TEST(nn, cmp_types) {
int i = 5;
int* p = &i;
int* j = new int(6);
not_null<int*> nnp(j);
EXPECT_TRUE(nnp == nnp);
EXPECT_FALSE(nnp == p);
EXPECT_FALSE(p == nnp);
EXPECT_FALSE(nnp == nullptr);
EXPECT_FALSE(nullptr == nnp);
delete j;
}
TEST(nn_helper, output) {
not_null_shared_ptr<int> nn(new int(5));
std::stringstream ss1, ss2;
ss1 << nn;
ss2 << nn.unwrap();
auto s1 = ss1.str();
auto s2 = ss2.str();
EXPECT_EQ(s1, s2);
EXPECT_NE(s2, "");
}
TEST(nn_helper, casting) {
not_null_shared_ptr<Derived> nnd(new Derived());
auto s = static_pointer_cast<Base>(nnd);
EXPECT_EQ(s.get(), nnd.get());
static_assert(
std::is_same_v<decltype(s), not_null_shared_ptr<Base>>, "wrong cast");
auto d1 = dynamic_pointer_cast<Derived>(s);
EXPECT_EQ(d1.get(), nnd.get());
static_assert(
std::is_same_v<decltype(d1), std::shared_ptr<Derived>>, "wrong cast");
auto d2 = dynamic_pointer_cast<Derived2>(s);
EXPECT_EQ(d2.get(), nullptr);
static_assert(
std::is_same_v<decltype(d2), std::shared_ptr<Derived2>>, "wrong cast");
auto c = const_pointer_cast<const Derived>(nnd);
EXPECT_EQ(c.get(), nnd.get());
static_assert(
std::is_same_v<decltype(c), not_null_shared_ptr<const Derived>>,
"wrong cast");
auto r = reinterpret_pointer_cast<Base>(nnd);
EXPECT_EQ(r.get(), nnd.get());
static_assert(
std::is_same_v<decltype(r), not_null_shared_ptr<Base>>, "wrong cast");
}
TEST(nn, hash) {
int* i = new int(5);
{
std::unordered_set<not_null<int*>> s;
s.emplace(i);
}
delete i;
}
TEST(nn, null_deleter) {
int* i = new int(5);
auto f = [&]() { std::unique_ptr<int, void (*)(int*)> nnui(i, nullptr); };
// Error messages are different in build modes, but both die
EXPECT_DEATH(f(), "");
delete i;
int* j = new int(6);
try {
not_null_unique_ptr<int, void (*)(int*)> nnui(j, nullptr);
} catch (std::invalid_argument&) {
delete j;
return;
}
EXPECT_FALSE(true);
}
template <typename Aliased>
void testAliasedSharedPtrNullOwner() {
int* p = new int(7);
{
std::shared_ptr<int> sp; // null
Aliased aliased_sp(sp, p);
// Despite having a null owner, the pointer is valid.
EXPECT_EQ(*aliased_sp, *p);
*p = 8;
EXPECT_EQ(*aliased_sp, *p);
}
// ASAN will abort if aliased_sp deleted p.
EXPECT_EQ(*p, 8);
delete p;
}
TEST(nn_sp, null_aliased_shared_ptr) {
// It is legal to construct an aliased shared_ptr with a null owner.
// Verify that this is so for regular shared_ptr.
testAliasedSharedPtrNullOwner<std::shared_ptr<int>>();
// It works for std::shared_ptr, so should also work for not_null_shared_ptr.
testAliasedSharedPtrNullOwner<not_null_shared_ptr<int>>();
}
TEST(nn_sp, pointer_cast_check) {
auto nnd = make_not_null_shared<Derived>();
auto nnb1 = static_pointer_cast<Base>(nnd);
EXPECT_EQ(nnb1.use_count(), 2);
EXPECT_EQ(nnd.get(), nnb1.get());
nullify(nnd);
#ifndef NDEBUG
EXPECT_DEATH(
static_pointer_cast<Base>(nnd), "not_null internal pointer is null");
#else
auto nnb2 = static_pointer_cast<Base>(nnd);
EXPECT_EQ(nnb2.get(), nullptr);
#endif
}
class MyAllocator : public std::allocator<int> {
public:
explicit MyAllocator(int* count) : count_(count) {}
template <typename... Args>
int* allocate(Args&&... args) {
++*count_;
return std::allocator<int>::allocate(std::forward<Args>(args)...);
}
template <typename U, typename... Args>
void construct(U* p, Args&&... args) {
++*count_;
return std::allocator<int>::construct(p, std::forward<Args>(args)...);
}
public:
using std::allocator<int>::value_type;
using std::allocator<int>::pointer;
using std::allocator<int>::const_pointer;
using std::allocator<int>::reference;
using std::allocator<int>::const_reference;
using std::allocator<int>::size_type;
using std::allocator<int>::difference_type;
using std::allocator<int>::propagate_on_container_move_assignment;
using std::allocator<int>::rebind;
using std::allocator<int>::is_always_equal;
private:
int* count_;
};
TEST(nn_sp, allocate_shared) {
int count = 0;
auto nnsp = allocate_not_null_shared<int>(MyAllocator(&count), 7);
EXPECT_EQ(*nnsp, 7);
EXPECT_EQ(count, 1);
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment