CodeQL / Analyze (actions) (push) Canceled after 0s
CodeQL / Analyze (python) (push) Canceled after 0s
pre-commit / pre-commit (push) Canceled after 0s
Multi-Arch CI / setup (push) Canceled after 0s
Publish build_manylinux_x86_64 images / publish_build_manylinux_x86_64 (push) Canceled after 0s
Setup (Multi-Arch) / setup (push) Canceled after 0s
Multi-Arch CI / Linux::${{ fromJSON(needs.setup.outputs.linux_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubi10 image / publish_no_rocm_image_ubi10 (push) Canceled after 0s
Multi-Arch CI / Windows::${{ fromJSON(needs.setup.outputs.windows_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Multi-Arch CI / Manifest Diff (push) Canceled after 0s
TheRock Manifest Diff Report / Generate Manifest Diff Report (push) Canceled after 0s
Multi-Arch CI / CI Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_ocl_rt images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_rocgdb images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Unit Tests / Unit Tests :: ubuntu-24.04 (push) Canceled after 0s
Unit Tests / Unit Tests :: windows-2022 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_media (push) Canceled after 0s
Unit Tests / Unit Tests Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_openmpi (push) Canceled after 0s
1444 lines
52 KiB
C++
1444 lines
52 KiB
C++
// Copyright © Advanced Micro Devices, Inc., or its affiliates.
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
|
|
/*! \file unique_ptr.h
|
|
* \brief A smart pointer that owns and manages another object through a
|
|
* pointer and disposes of that object when the \p unique_ptr goes
|
|
* out of scope.
|
|
*/
|
|
|
|
#pragma once
|
|
|
|
#include <thrust/detail/config.h>
|
|
|
|
#include <thrust/detail/type_traits.h>
|
|
#include <thrust/device_free.h>
|
|
#include <thrust/device_new.h>
|
|
#include <thrust/device_ptr.h>
|
|
#include <thrust/device_reference.h>
|
|
#include <thrust/for_each.h>
|
|
|
|
#include <compare>
|
|
#include <functional>
|
|
#include <type_traits>
|
|
#include <utility>
|
|
|
|
THRUST_NAMESPACE_BEGIN
|
|
|
|
/*! \addtogroup memory_management Memory Management
|
|
* \{
|
|
*/
|
|
|
|
/*! \brief Default deleter for \p unique_ptr.
|
|
*
|
|
* The default deleter used by \p unique_ptr when no custom deleter is specified.
|
|
* Calls the destructor (if needed) and deallocates memory using \p thrust::device_free.
|
|
*
|
|
* \tparam T The type of object to delete (single object form).
|
|
*
|
|
* \see thrust::unique_ptr
|
|
* \see thrust::device_free
|
|
*/
|
|
template <class T, class = void>
|
|
struct default_delete;
|
|
|
|
/*! \brief Default deleter specialization for single objects.
|
|
*
|
|
* This specialization handles deletion of single objects allocated in device memory.
|
|
*
|
|
* \tparam T The type of object to delete.
|
|
*/
|
|
template <class T>
|
|
struct default_delete<T, std::enable_if_t<!std::is_array_v<T>>>
|
|
{
|
|
/*! \typedef pointer
|
|
* \brief The pointer type (`thrust::device_ptr<T>`).
|
|
*/
|
|
using pointer = thrust::device_ptr<T>;
|
|
|
|
THRUST_HOST constexpr default_delete() noexcept = default;
|
|
|
|
/*! \brief Converting constructor from compatible deleter.
|
|
*
|
|
* Allows construction from a deleter for a convertible type.
|
|
*
|
|
* \tparam U A type convertible to \p T.
|
|
*/
|
|
template <class U>
|
|
THRUST_HOST default_delete(
|
|
const default_delete<U>&,
|
|
std::enable_if_t<std::is_convertible_v<thrust::device_ptr<U>, pointer>>* = nullptr) noexcept
|
|
{}
|
|
|
|
/*! \brief Deletes the object pointed to by \p ptr.
|
|
*
|
|
* Calls the destructor (if needed) and deallocates memory using thrust::device_free.
|
|
*
|
|
* \param ptr Pointer to the object to delete.
|
|
*/
|
|
THRUST_HOST void operator()(pointer ptr) const noexcept
|
|
{
|
|
if (ptr.get() == nullptr)
|
|
{
|
|
return;
|
|
}
|
|
|
|
// The ideal implementation would be a simple call to `thrust::device_delete`:
|
|
//
|
|
// thrust::device_delete(ptr);
|
|
//
|
|
// However, for non-trivially destructible types, `thrust::device_delete`
|
|
// calls `thrust::destroy_range`, which requires an allocator with a
|
|
// `value_type` typedef. The internal `thrust::detail::device_delete_allocator`
|
|
// is an empty struct that lacks this typedef, causing a compilation error.
|
|
//
|
|
// As a workaround, we manually invoke the destructor on the device using
|
|
// `thrust::for_each_n` and then separately free the memory.
|
|
if constexpr (!std::is_trivially_destructible_v<T>)
|
|
{
|
|
thrust::for_each_n(ptr, 1, [] __device__(T & x) {
|
|
x.~T();
|
|
});
|
|
}
|
|
thrust::device_free(ptr);
|
|
}
|
|
};
|
|
|
|
/*! \brief Default deleter specialization for arrays of non-trivially destructible types.
|
|
*
|
|
* This specialization handles deletion of arrays where element destructors must be called.
|
|
*
|
|
* \tparam T The array element type (non-trivially destructible).
|
|
*/
|
|
template <class T>
|
|
struct default_delete<T[], std::enable_if_t<!std::is_trivially_destructible_v<T>>>
|
|
{
|
|
/*! \typedef pointer
|
|
* \brief The pointer type (`thrust::device_ptr<T>`).
|
|
*/
|
|
using pointer = thrust::device_ptr<T>;
|
|
|
|
// Allow other instantiations to access private members for converting constructor
|
|
template <class U, class>
|
|
friend struct default_delete;
|
|
|
|
/*! \brief Constructs a deleter with the specified array size.
|
|
*
|
|
* \param n The number of elements in the array (default: 0).
|
|
*/
|
|
THRUST_HOST constexpr default_delete(size_t n = 0) noexcept
|
|
: m_size(n){};
|
|
|
|
/*! \brief Copy and converting constructor from compatible array deleter.
|
|
*
|
|
* Copies the size from another deleter for a convertible array type.
|
|
*
|
|
* \tparam U An array element type convertible to \p T.
|
|
* \param other The deleter to copy from.
|
|
*/
|
|
template <class U>
|
|
THRUST_HOST
|
|
default_delete(const default_delete<U[]>& other,
|
|
std::enable_if_t<std::is_convertible_v<U (*)[], T (*)[]>>* = nullptr) noexcept
|
|
: m_size(other.m_size)
|
|
{}
|
|
|
|
/*! \brief Deletes the array pointed to by \p ptr.
|
|
*
|
|
* Calls destructors (if needed) and deallocates memory using thrust::device_free.
|
|
*
|
|
* \param ptr Pointer to the array to delete.
|
|
*/
|
|
THRUST_HOST void operator()(pointer ptr) const noexcept
|
|
{
|
|
if (ptr.get() == nullptr)
|
|
{
|
|
return;
|
|
}
|
|
|
|
// The ideal implementation would be a call to `thrust::device_delete`
|
|
// with the number of elements:
|
|
//
|
|
// thrust::device_delete(ptr, m_size);
|
|
//
|
|
// However, for non-trivially destructible types, `thrust::device_delete`
|
|
// calls `thrust::destroy_range`, which requires an allocator with a
|
|
// `value_type` typedef. The internal `thrust::detail::device_delete_allocator`
|
|
// is an empty struct that lacks this typedef, causing a compilation error.
|
|
//
|
|
// As a workaround, we manually invoke the destructor on each element
|
|
// using `thrust::for_each_n` and then separately free the memory.
|
|
if (m_size)
|
|
{
|
|
thrust::for_each_n(ptr, m_size, [] __device__(T & x) {
|
|
x.~T();
|
|
});
|
|
}
|
|
thrust::device_free(ptr);
|
|
}
|
|
|
|
private:
|
|
size_t m_size;
|
|
};
|
|
|
|
/*! \brief Default deleter specialization for arrays of trivially destructible types.
|
|
*
|
|
* For arrays of trivially destructible element types, this specialization intentionally
|
|
* does NOT store the element count. Their destruction is a no-op, so only the raw
|
|
* deallocation is required. Omitting the size keeps this deleter specialization an
|
|
* empty (zero-size) type, allowing \p unique_ptr<T[]> instantiations that use it to
|
|
* remain a low/zero-cost abstraction (the default deleter need not increase the overall
|
|
* size of unique_ptr<T[]> above the size of a simple pointer `T *`).
|
|
*
|
|
* \tparam T The array element type (trivially destructible).
|
|
*/
|
|
template <class T>
|
|
struct default_delete<T[], std::enable_if_t<std::is_trivially_destructible_v<T>>>
|
|
{
|
|
/*! \typedef pointer
|
|
* \brief The pointer type (`thrust::device_ptr<T>`).
|
|
*/
|
|
using pointer = thrust::device_ptr<T>;
|
|
|
|
/*! \brief Constructs a deleter (size parameter ignored for trivially destructible types).
|
|
*
|
|
* The size parameter is accepted but ignored since trivially destructible types
|
|
* don't require element-wise destruction.
|
|
*/
|
|
THRUST_HOST constexpr default_delete(size_t = 0) noexcept {};
|
|
|
|
/*! \brief Copy and converting constructor from compatible deleter.
|
|
*
|
|
* Allows construction from a deleter for a convertible type.
|
|
*
|
|
* \tparam U A type convertible to \p T.
|
|
*/
|
|
template <class U>
|
|
THRUST_HOST default_delete(
|
|
const default_delete<U>&,
|
|
std::enable_if_t<std::is_convertible_v<thrust::device_ptr<U>, pointer>>* = nullptr) noexcept
|
|
{}
|
|
|
|
/*! \brief Deletes the array pointed to by \p ptr.
|
|
*
|
|
* Deallocates memory using thrust::device_free.
|
|
*
|
|
* \param ptr Pointer to the array to delete.
|
|
*/
|
|
THRUST_HOST void operator()(pointer ptr) const noexcept
|
|
{
|
|
thrust::device_free(ptr);
|
|
}
|
|
};
|
|
|
|
namespace detail
|
|
{
|
|
|
|
template <class T, class D, class = void>
|
|
struct pointer_detector
|
|
{
|
|
using type = thrust::device_ptr<T>;
|
|
};
|
|
|
|
template <class T, class D>
|
|
struct pointer_detector<T, D, std::void_t<typename D::pointer>>
|
|
{
|
|
using type = typename D::pointer;
|
|
};
|
|
|
|
template <class Deleter>
|
|
struct unique_ptr_deleter_sfinae
|
|
{
|
|
static_assert(!std::is_reference_v<Deleter>, "incorrect specialization");
|
|
using lval_ref_type = const Deleter&;
|
|
using good_rval_ref_type = Deleter&&;
|
|
using bad_rval_ref_type = void;
|
|
using enable_rval_overload = thrust::detail::true_type;
|
|
};
|
|
|
|
template <class Deleter>
|
|
struct unique_ptr_deleter_sfinae<const Deleter&>
|
|
{
|
|
using lval_ref_type = const Deleter&;
|
|
using good_rval_ref_type = void;
|
|
using bad_rval_ref_type = const Deleter&&;
|
|
using enable_rval_overload = thrust::detail::false_type;
|
|
};
|
|
|
|
template <class Deleter>
|
|
struct unique_ptr_deleter_sfinae<Deleter&>
|
|
{
|
|
using lval_ref_type = Deleter&;
|
|
using good_rval_ref_type = void;
|
|
using bad_rval_ref_type = Deleter&&;
|
|
using enable_rval_overload = thrust::detail::false_type;
|
|
};
|
|
|
|
} // namespace detail
|
|
|
|
/*! \p thrust::unique_ptr is a smart pointer that owns and manages another object,
|
|
* allocated in device memory, via a pointer and subsequently disposes of that
|
|
* object when the \p unique_ptr goes out of scope.
|
|
*
|
|
* The object is disposed of using the associated `Deleter` when either of the
|
|
* following happens:
|
|
* - the managing `unique_ptr` object is destroyed.
|
|
* - the managing `unique_ptr` object is assigned another pointer via `operator=` or `reset()`.
|
|
*
|
|
* The object is disposed of by calling `get_deleter()(get())`. The default deleter,
|
|
* `thrust::default_delete`, calls the destructor (if needed) and deallocates memory
|
|
* using `thrust::device_free`.
|
|
*
|
|
* A `unique_ptr` may alternatively own no object, in which case it is described as
|
|
* *empty*.
|
|
*
|
|
* There are two versions of `thrust::unique_ptr`:
|
|
* 1. Manages a single object
|
|
* 2. Manages a dynamically-allocated array of objects
|
|
*
|
|
* \par Nested Types
|
|
* - `pointer` - The type of the stored pointer (defaults to `thrust::device_ptr<T>`)
|
|
* - `element_type` - The type of the managed object (`T`)
|
|
* - `deleter_type` - The type of the deleter (`D`)
|
|
*
|
|
* \tparam T The type of the managed object.
|
|
* \tparam D The type of the deleter.
|
|
*
|
|
* \see thrust::default_delete
|
|
* \see thrust::make_unique
|
|
* \see thrust::device_ptr
|
|
* \see thrust::device_malloc
|
|
* \see thrust::device_new
|
|
* \see thrust::device_free
|
|
* \see https://en.cppreference.com/w/cpp/memory/unique_ptr
|
|
*/
|
|
|
|
template <class T, class D = default_delete<T>>
|
|
class __attribute__((trivial_abi)) unique_ptr
|
|
{
|
|
public:
|
|
/*! \typedef pointer
|
|
* \brief The type of the stored pointer (defaults to `thrust::device_ptr<T>`).
|
|
*/
|
|
using pointer = typename thrust::detail::pointer_detector<T, D>::type;
|
|
|
|
/*! \typedef element_type
|
|
* \brief The type of the managed object (`T`).
|
|
*/
|
|
using element_type = T;
|
|
|
|
/*! \typedef deleter_type
|
|
* \brief The type of the deleter (`D`).
|
|
*/
|
|
using deleter_type = D;
|
|
|
|
// TODO: When a standard "trivially relocatable" facility lands, add an
|
|
// annotation/macro here to advertise that unique_ptr can be bitwise relocated
|
|
|
|
private:
|
|
pointer m_ptr;
|
|
[[no_unique_address]] deleter_type m_deleter;
|
|
|
|
using DeleterSFINAE = thrust::detail::unique_ptr_deleter_sfinae<D>;
|
|
|
|
// Next section implements SFINAE constraints for the unique_ptr constructors
|
|
// using a pattern that mirrors the implementation in libc++.
|
|
//
|
|
// The `dependent_type` helper makes the type aliases below (e.g., LValRefType)
|
|
// dependent on a dummy template parameter from the constructor itself. This
|
|
// forces the compiler to defer constraint checking until function overload
|
|
// resolution, rather than at class instantiation time.
|
|
//
|
|
// NOTE: A simpler SFINAE pattern using a `static constexpr bool` evaluated at
|
|
// class-instantiation time also works correctly. However, we intentionally
|
|
// follow the more complex libc++ pattern for consistency with a proven
|
|
// implementation, aiming to inherit its robustness against
|
|
// potential compiler-specific edge cases.
|
|
|
|
template <bool Dummy>
|
|
using LValRefType = typename thrust::detail::dependent_type<DeleterSFINAE, Dummy>::type::lval_ref_type;
|
|
|
|
template <bool Dummy>
|
|
using GoodRValRefType = typename thrust::detail::dependent_type<DeleterSFINAE, Dummy>::type::good_rval_ref_type;
|
|
|
|
template <bool Dummy>
|
|
using BadRValRefType = typename thrust::detail::dependent_type<DeleterSFINAE, Dummy>::type::bad_rval_ref_type;
|
|
|
|
template <
|
|
bool Dummy,
|
|
class Deleter =
|
|
typename thrust::detail::dependent_type<typename thrust::detail::identity_<deleter_type>::type, Dummy>::type>
|
|
using EnableIfDeleterDefaultConstructible =
|
|
std::enable_if_t<std::is_default_constructible_v<Deleter> && !std::is_pointer_v<Deleter>>;
|
|
|
|
template <class ArgType>
|
|
using EnableIfDeleterConstructible =
|
|
std::enable_if_t<std::is_constructible_v<deleter_type, ArgType>>;
|
|
|
|
template <class U, class E>
|
|
using EnableIfMoveConvertible =
|
|
std::enable_if_t<std::is_convertible_v<typename U::pointer, pointer> && !std::is_array_v<E>>;
|
|
|
|
template <class E>
|
|
using EnableIfDeleterConvertible =
|
|
std::enable_if_t<(std::is_reference_v<D> && std::is_same_v<D, E>)
|
|
|| (!std::is_reference_v<D> && std::is_convertible_v<E, D>)>;
|
|
|
|
template <class E>
|
|
using EnableIfDeleterAssignable = std::enable_if_t<std::is_assignable_v<D&, E&&>>;
|
|
|
|
template <
|
|
bool Dummy,
|
|
class Deleter =
|
|
typename thrust::detail::dependent_type<typename thrust::detail::identity_<deleter_type>::type, Dummy>::type>
|
|
using EnableIfDeleterDefaultDelete = std::enable_if_t<std::is_same_v<Deleter, default_delete<T>>>;
|
|
|
|
public:
|
|
//==========================================================================
|
|
// Constructors
|
|
//==========================================================================
|
|
|
|
/*! \brief Constructs a \p unique_ptr that does not own an object.
|
|
*/
|
|
template <bool Dummy = true, class = EnableIfDeleterDefaultConstructible<Dummy>>
|
|
THRUST_HOST constexpr unique_ptr() noexcept
|
|
: m_ptr()
|
|
, m_deleter()
|
|
{}
|
|
|
|
/*! \brief Constructs a \p unique_ptr that does not own an object.
|
|
*/
|
|
template <bool Dummy = true, class = EnableIfDeleterDefaultConstructible<Dummy>>
|
|
THRUST_HOST constexpr unique_ptr(std::nullptr_t) noexcept
|
|
: unique_ptr()
|
|
{}
|
|
|
|
/*! \brief Constructs a \p unique_ptr that owns the object pointed to by \p p.
|
|
* \param p A pointer to the object in device memory to manage.
|
|
*/
|
|
template <bool Dummy = true, class = EnableIfDeleterDefaultConstructible<Dummy>>
|
|
THRUST_HOST THRUST_CONSTEXPR_SINCE_CXX23 explicit unique_ptr(pointer p) noexcept
|
|
: m_ptr(p)
|
|
, m_deleter()
|
|
{}
|
|
|
|
/*! \brief Constructs a \p unique_ptr that owns the object pointed to by \p raw_p.
|
|
* \param raw_p A raw pointer to the object in device memory to manage.
|
|
*/
|
|
template <bool Dummy = true,
|
|
class = EnableIfDeleterDefaultConstructible<Dummy>,
|
|
class = EnableIfDeleterDefaultDelete<Dummy>>
|
|
THRUST_HOST THRUST_CONSTEXPR_SINCE_CXX23 explicit unique_ptr(T* raw_p) noexcept
|
|
: m_ptr(device_pointer_cast(raw_p))
|
|
, m_deleter()
|
|
{}
|
|
|
|
/*! \brief Constructs a \p unique_ptr that owns the object pointed to by \p p and uses \p d as the deleter.
|
|
* \param p A pointer to the object in device memory to manage.
|
|
* \param d The deleter to use.
|
|
*/
|
|
template <bool Dummy = true, class = EnableIfDeleterConstructible<LValRefType<Dummy>>>
|
|
THRUST_HOST THRUST_CONSTEXPR_SINCE_CXX23 unique_ptr(pointer p, LValRefType<Dummy> d) noexcept
|
|
: m_ptr(p)
|
|
, m_deleter(d)
|
|
{}
|
|
|
|
/*! \brief Constructs a \p unique_ptr that owns the object pointed to by \p p and uses \p d as the deleter.
|
|
* \param p A pointer to the object in device memory to manage.
|
|
* \param d The deleter to use.
|
|
*/
|
|
template <bool Dummy = true, class = EnableIfDeleterConstructible<GoodRValRefType<Dummy>>>
|
|
THRUST_HOST THRUST_CONSTEXPR_SINCE_CXX23 unique_ptr(pointer p, GoodRValRefType<Dummy> d) noexcept
|
|
: m_ptr(p)
|
|
, m_deleter(std::move(d))
|
|
{
|
|
static_assert(!std::is_reference_v<deleter_type>, "rvalue deleter bound to reference");
|
|
}
|
|
|
|
template <bool Dummy = true, class = EnableIfDeleterConstructible<BadRValRefType<Dummy>>>
|
|
unique_ptr(pointer p, BadRValRefType<Dummy> d) = delete;
|
|
|
|
/*! \brief Move constructor. Constructs a \p unique_ptr by taking ownership of the object managed by \p u.
|
|
* \param u The \p unique_ptr to move from.
|
|
*/
|
|
THRUST_HOST THRUST_CONSTEXPR_SINCE_CXX23 unique_ptr(unique_ptr&& u) noexcept
|
|
: m_ptr(u.release())
|
|
, m_deleter(std::forward<deleter_type>(u.get_deleter()))
|
|
{}
|
|
|
|
/*! \brief Converting move constructor. Constructs a \p unique_ptr by taking ownership of the object managed by \p u.
|
|
*
|
|
* Allows converting from \p unique_ptr<U, E> to \p unique_ptr<T, D> when
|
|
* the pointer and deleter types are compatible.
|
|
*
|
|
* \param u The \p unique_ptr to move from.
|
|
*/
|
|
template <class U, class E, class = EnableIfMoveConvertible<unique_ptr<U, E>, U>, class = EnableIfDeleterConvertible<E>>
|
|
THRUST_HOST THRUST_CONSTEXPR_SINCE_CXX23 unique_ptr(unique_ptr<U, E>&& u) noexcept
|
|
: m_ptr(u.release())
|
|
, m_deleter(std::forward<E>(u.get_deleter()))
|
|
{}
|
|
|
|
//==========================================================================
|
|
// Assignment
|
|
//==========================================================================
|
|
/*! \brief Move assignment operator. Replaces the managed object with the one from \p u.
|
|
* \param u The \p unique_ptr to move from.
|
|
* \return `*this`
|
|
*/
|
|
THRUST_HOST THRUST_CONSTEXPR_SINCE_CXX23 unique_ptr& operator=(unique_ptr&& u) noexcept
|
|
{
|
|
reset(u.release());
|
|
m_deleter = std::forward<deleter_type>(u.get_deleter());
|
|
return *this;
|
|
}
|
|
|
|
/*! \brief Converting assignment operator.. Replaces the managed object with the one from \p u.
|
|
* \param u The \p unique_ptr to move from.
|
|
* \return `*this`
|
|
*/
|
|
template <class U, class E, class = EnableIfMoveConvertible<unique_ptr<U, E>, U>, class = EnableIfDeleterAssignable<E>>
|
|
THRUST_HOST THRUST_CONSTEXPR_SINCE_CXX23 unique_ptr& operator=(unique_ptr<U, E>&& u) noexcept
|
|
{
|
|
reset(u.release());
|
|
m_deleter = std::forward<E>(u.get_deleter());
|
|
return *this;
|
|
}
|
|
|
|
/*! \brief Assigns a null pointer, deallocating the managed object. Effectively the same as calling reset().
|
|
* \return `*this`
|
|
*/
|
|
THRUST_HOST THRUST_CONSTEXPR_SINCE_CXX23 unique_ptr& operator=(std::nullptr_t) noexcept
|
|
{
|
|
reset();
|
|
return *this;
|
|
}
|
|
|
|
//==========================================================================
|
|
// Destructor
|
|
//==========================================================================
|
|
/*! \brief Destroys the \p unique_ptr, the managed object is destroyed via `get_deleter()(get())`. If get() == nullptr there are no effects.
|
|
*/
|
|
THRUST_HOST THRUST_CONSTEXPR_SINCE_CXX23 ~unique_ptr()
|
|
{
|
|
reset();
|
|
}
|
|
|
|
//==========================================================================
|
|
// Observers
|
|
//==========================================================================
|
|
/*! \brief Returns a pointer to the managed object or `nullptr` if no object is owned.
|
|
* \return Pointer to the managed object.
|
|
*/
|
|
THRUST_HOST THRUST_CONSTEXPR_SINCE_CXX23 pointer get() const noexcept
|
|
{
|
|
return m_ptr;
|
|
}
|
|
|
|
/*! \brief Returns a raw pointer to the managed object or `nullptr` if no object is owned.
|
|
* \return Raw pointer to the managed object.
|
|
*/
|
|
template <bool Dummy = true, class = EnableIfDeleterDefaultDelete<Dummy>>
|
|
THRUST_HOST THRUST_CONSTEXPR_SINCE_CXX23 T* get_raw() const noexcept
|
|
{
|
|
return raw_pointer_cast(m_ptr);
|
|
}
|
|
|
|
/*! \brief Returns a reference to the deleter object which would be used for destruction of the managed object.
|
|
* \return A reference to the stored deleter.
|
|
*/
|
|
THRUST_HOST THRUST_CONSTEXPR_SINCE_CXX23 deleter_type& get_deleter() noexcept
|
|
{
|
|
return m_deleter;
|
|
}
|
|
|
|
/*! \brief Returns a reference to the deleter object which would be used for destruction of the managed object.
|
|
* \return A reference to the stored deleter.
|
|
*/
|
|
THRUST_HOST THRUST_CONSTEXPR_SINCE_CXX23 const deleter_type& get_deleter() const noexcept
|
|
{
|
|
return m_deleter;
|
|
}
|
|
|
|
/*! \brief Checks if the \p unique_ptr owns an object.
|
|
*
|
|
* Equivalent to `get() != nullptr`.
|
|
*
|
|
* \return `true` if an object is owned, `false` otherwise.
|
|
*/
|
|
THRUST_HOST THRUST_CONSTEXPR_SINCE_CXX23 explicit operator bool() const noexcept
|
|
{
|
|
return m_ptr != nullptr;
|
|
}
|
|
|
|
/*! \brief Dereferences the stored pointer.
|
|
*
|
|
* The default `unique_ptr` implementation uses `thrust::device_ptr`.
|
|
* Dereferencing this pointer in host code is a valid operation that
|
|
* results in a copy of the object from device to host memory.
|
|
*
|
|
* \return A reference to the managed object.
|
|
*/
|
|
THRUST_HOST THRUST_CONSTEXPR_SINCE_CXX23 auto operator*() const noexcept
|
|
{
|
|
return *m_ptr;
|
|
}
|
|
|
|
/*! \brief Disabled for device pointers - cannot dereference device memory from host.
|
|
*
|
|
* \warning This operator is intentionally disabled because dereferencing
|
|
* device memory from host code is invalid. Use `operator*()` to copy the
|
|
* object to host first (`T host_obj = *ptr`), or access members inside
|
|
* device code.
|
|
*
|
|
* Attempting to use this operator will produce a compile-time error with
|
|
* a clear diagnostic message.
|
|
*
|
|
* \return This function does not return; it triggers a compile-time error.
|
|
*/
|
|
THRUST_HOST THRUST_CONSTEXPR_SINCE_CXX23 pointer operator->() const noexcept
|
|
{
|
|
static_assert(false,
|
|
"thrust::unique_ptr<T>::operator->(): cannot dereference device memory from host. "
|
|
"Copy the object to host first (T host = *ptr) or access members inside device code.");
|
|
|
|
return m_ptr;
|
|
}
|
|
|
|
//==========================================================================
|
|
// Modifiers
|
|
//==========================================================================
|
|
/*! \brief Releases ownership of the managed object, if any.
|
|
* \return A pointer to the released object.
|
|
*/
|
|
THRUST_HOST THRUST_CONSTEXPR_SINCE_CXX23 pointer release() noexcept
|
|
{
|
|
pointer temp = m_ptr;
|
|
m_ptr = pointer();
|
|
return temp;
|
|
}
|
|
|
|
/*! \brief Replaces the managed object.
|
|
* \param p The new object to manage.
|
|
*/
|
|
THRUST_HOST THRUST_CONSTEXPR_SINCE_CXX23 void reset(pointer p = pointer()) noexcept
|
|
{
|
|
pointer temp = m_ptr;
|
|
m_ptr = p;
|
|
if (temp)
|
|
{
|
|
m_deleter(temp);
|
|
}
|
|
}
|
|
|
|
/*! \brief Swaps the managed object and deleter with another \p unique_ptr.
|
|
*
|
|
* Exchanges the contents of `*this` and \p u. This operation is `noexcept`
|
|
* and swaps both the stored pointer and the deleter.
|
|
*
|
|
* \param u The \p unique_ptr to swap with.
|
|
*/
|
|
THRUST_HOST THRUST_CONSTEXPR_SINCE_CXX23 void swap(unique_ptr& u) noexcept
|
|
{
|
|
using std::swap;
|
|
swap(m_ptr, u.m_ptr);
|
|
swap(m_deleter, u.m_deleter);
|
|
}
|
|
};
|
|
|
|
/*! \brief Array specialization of \p unique_ptr for managing dynamically-allocated arrays.
|
|
*
|
|
* This specialization manages arrays allocated in device memory. The array form
|
|
* provides `operator[]` for element access.
|
|
*
|
|
* \par Array Destruction Behavior
|
|
* The array form has different constructor requirements and performance characteristics
|
|
* based on the element type's destructibility:
|
|
*
|
|
* **Trivially-destructible types** (e.g., `int`, `float`, POD types):
|
|
* - Constructors do NOT require array size parameter
|
|
* - Deleter is zero-size (no memory overhead)
|
|
* - Destruction only deallocates memory (no destructors to call)
|
|
* - Example: `unique_ptr<int[]> ptr(device_malloc<int>(100));`
|
|
*
|
|
* **Non-trivially-destructible types** (e.g., types with custom destructors):
|
|
* - Constructors REQUIRE array size parameter
|
|
* - Deleter stores the size (small memory overhead: one `size_t`)
|
|
* - Destruction calls destructor for each element on device before deallocation
|
|
* - Example: `unique_ptr<MyClass[]> ptr(device_new<MyClass>(100), 100);`
|
|
*
|
|
* This design ensures zero overhead for simple types while properly handling
|
|
* complex types that require cleanup.
|
|
*
|
|
* \tparam T The array element type.
|
|
* \tparam D The type of the deleter.
|
|
*
|
|
* \see thrust::default_delete
|
|
* \see thrust::make_unique
|
|
* \see https://en.cppreference.com/w/cpp/memory/unique_ptr
|
|
*/
|
|
template <class T, class D>
|
|
class __attribute__((trivial_abi)) unique_ptr<T[], D>
|
|
{
|
|
public:
|
|
/*! \typedef pointer
|
|
* \brief The type of the stored pointer (defaults to `thrust::device_ptr<T>`).
|
|
*/
|
|
using pointer = typename thrust::detail::pointer_detector<T, D>::type;
|
|
|
|
/*! \typedef element_type
|
|
* \brief The type of the array elements (`T`).
|
|
*/
|
|
using element_type = T;
|
|
|
|
/*! \typedef deleter_type
|
|
* \brief The type of the deleter (`D`).
|
|
*/
|
|
using deleter_type = D;
|
|
|
|
private:
|
|
template <class Up, class OtherDeleter>
|
|
friend class unique_ptr;
|
|
|
|
pointer m_ptr;
|
|
[[no_unique_address]] deleter_type m_deleter;
|
|
|
|
using DeleterSFINAE = thrust::detail::unique_ptr_deleter_sfinae<D>;
|
|
|
|
template <bool Dummy>
|
|
using LValRefType = typename thrust::detail::dependent_type<DeleterSFINAE, Dummy>::type::lval_ref_type;
|
|
|
|
template <bool Dummy>
|
|
using GoodRValRefType = typename thrust::detail::dependent_type<DeleterSFINAE, Dummy>::type::good_rval_ref_type;
|
|
|
|
template <bool Dummy>
|
|
using BadRValRefType = typename thrust::detail::dependent_type<DeleterSFINAE, Dummy>::type::bad_rval_ref_type;
|
|
|
|
template <
|
|
bool Dummy,
|
|
class Deleter =
|
|
typename thrust::detail::dependent_type<typename thrust::detail::identity_<deleter_type>::type, Dummy>::type>
|
|
using EnableIfDeleterDefaultConstructible =
|
|
std::enable_if_t<std::is_default_constructible_v<Deleter> && !std::is_pointer_v<Deleter>>;
|
|
|
|
template <class ArgType>
|
|
using EnableIfDeleterConstructible =
|
|
std::enable_if_t<std::is_constructible_v<deleter_type, ArgType>>;
|
|
|
|
template <class Pp>
|
|
using EnableIfPointerConvertible = std::enable_if_t<std::is_same_v<Pp, pointer>>;
|
|
|
|
template <bool Dummy,
|
|
class Tp = typename thrust::detail::dependent_type<typename thrust::detail::identity_<element_type>::type,
|
|
Dummy>::type>
|
|
using EnableIfTriviallyDestructible = std::enable_if_t<std::is_trivially_destructible_v<Tp>>;
|
|
|
|
template <bool Dummy,
|
|
class Tp = typename thrust::detail::dependent_type<typename thrust::detail::identity_<element_type>::type,
|
|
Dummy>::type>
|
|
using EnableIfNotTriviallyDestructible = std::enable_if_t<!std::is_trivially_destructible_v<Tp>>;
|
|
|
|
template <class UPtr, class Up, class ElemT = typename UPtr::element_type>
|
|
using EnableIfMoveConvertible =
|
|
std::enable_if_t<std::is_array_v<Up> && std::is_same_v<pointer, element_type*>
|
|
&& std::is_same_v<typename UPtr::pointer, ElemT*>
|
|
&& std::is_convertible_v<ElemT (*)[], element_type (*)[]>>;
|
|
|
|
template <class E>
|
|
using EnableIfDeleterConvertible =
|
|
std::enable_if_t<(std::is_reference_v<D> && std::is_same_v<D, E>)
|
|
|| (!std::is_reference_v<D> && std::is_convertible_v<E, D>)>;
|
|
|
|
template <class E>
|
|
using EnableIfDeleterAssignable = std::enable_if_t<std::is_assignable_v<D&, E&&>>;
|
|
|
|
template <
|
|
bool Dummy,
|
|
class Deleter =
|
|
typename thrust::detail::dependent_type<typename thrust::detail::identity_<deleter_type>::type, Dummy>::type>
|
|
using EnableIfDeleterDefaultDelete = std::enable_if_t<std::is_same_v<Deleter, default_delete<T[]>>>;
|
|
|
|
public:
|
|
//==========================================================================
|
|
// Constructors
|
|
//==========================================================================
|
|
|
|
/*! \brief Constructs an empty \p unique_ptr that does not own an array.
|
|
*/
|
|
template <bool Dummy = true, class = EnableIfDeleterDefaultConstructible<Dummy>>
|
|
THRUST_HOST constexpr unique_ptr() noexcept
|
|
: m_ptr()
|
|
, m_deleter()
|
|
{}
|
|
|
|
/*! \brief Constructs an empty \p unique_ptr that does not own an array.
|
|
*/
|
|
template <bool Dummy = true, class = EnableIfDeleterDefaultConstructible<Dummy>>
|
|
THRUST_HOST constexpr unique_ptr(std::nullptr_t) noexcept
|
|
: unique_ptr()
|
|
{}
|
|
|
|
/*! \brief Constructs a \p unique_ptr that owns the object pointed to by \p p.
|
|
*
|
|
* This overload is only available for arrays of trivially-destructible types.
|
|
*
|
|
* \param p A pointer to an array in device memory to manage.
|
|
*/
|
|
template <class Pp,
|
|
bool Dummy = true,
|
|
class = EnableIfDeleterDefaultConstructible<Dummy>,
|
|
class = EnableIfPointerConvertible<Pp>,
|
|
class = EnableIfTriviallyDestructible<Dummy>>
|
|
THRUST_HOST THRUST_CONSTEXPR_SINCE_CXX23 explicit unique_ptr(Pp p) noexcept
|
|
: m_ptr(p)
|
|
, m_deleter()
|
|
{}
|
|
|
|
/*! \brief Constructs a \p unique_ptr from a raw device array pointer.
|
|
*
|
|
* This overload is only available for arrays of trivially-destructible types.
|
|
*
|
|
* \param raw_p A raw pointer to an array in device memory to manage.
|
|
*/
|
|
template <class Pp,
|
|
bool Dummy = true,
|
|
class = EnableIfDeleterDefaultConstructible<Dummy>,
|
|
class = EnableIfPointerConvertible<device_ptr<T>>,
|
|
class = EnableIfTriviallyDestructible<Dummy>,
|
|
class = EnableIfDeleterDefaultDelete<Dummy>>
|
|
THRUST_HOST THRUST_CONSTEXPR_SINCE_CXX23 explicit unique_ptr(Pp* raw_p) noexcept
|
|
: m_ptr(device_pointer_cast(raw_p))
|
|
, m_deleter()
|
|
{}
|
|
|
|
/*! \brief Constructs a \p unique_ptr that owns the object pointed to by \p p with known size.
|
|
*
|
|
* For arrays of non-trivially-destructible types, the size is required to ensure
|
|
* all element destructors are properly called during deletion.
|
|
*
|
|
* \param p A pointer to an array in device memory to manage.
|
|
* \param size The number of elements in the array.
|
|
*/
|
|
template <class Pp,
|
|
bool Dummy = true,
|
|
class = EnableIfDeleterDefaultConstructible<Dummy>,
|
|
class = EnableIfPointerConvertible<Pp>>
|
|
THRUST_HOST THRUST_CONSTEXPR_SINCE_CXX23 explicit unique_ptr(Pp p, size_t size) noexcept
|
|
: m_ptr(p)
|
|
, m_deleter(size)
|
|
{}
|
|
|
|
/*! \brief Constructs a \p unique_ptr from a raw device array pointer with known size.
|
|
*
|
|
* For arrays of non-trivially-destructible types, the size is required to ensure
|
|
* all element destructors are properly called during deletion.
|
|
*
|
|
* \param raw_p A raw pointer to an array in device memory to manage.
|
|
* \param size The number of elements in the array.
|
|
*/
|
|
template <class Pp,
|
|
bool Dummy = true,
|
|
class = EnableIfDeleterDefaultConstructible<Dummy>,
|
|
class = EnableIfPointerConvertible<device_ptr<T>>,
|
|
class = EnableIfDeleterDefaultDelete<Dummy>>
|
|
THRUST_HOST THRUST_CONSTEXPR_SINCE_CXX23 explicit unique_ptr(Pp* raw_p, size_t size) noexcept
|
|
: m_ptr(device_pointer_cast(raw_p))
|
|
, m_deleter(size)
|
|
{}
|
|
|
|
/*! \brief Constructs a \p unique_ptr with a custom deleter (lvalue reference).
|
|
*
|
|
* \param p A pointer to the array in device memory to manage.
|
|
* \param deleter The deleter to use for destroying the array.
|
|
*/
|
|
template <class Pp,
|
|
bool Dummy = true,
|
|
class = EnableIfDeleterConstructible<LValRefType<Dummy>>,
|
|
class = EnableIfPointerConvertible<Pp>>
|
|
THRUST_HOST THRUST_CONSTEXPR_SINCE_CXX23 unique_ptr(Pp p, LValRefType<Dummy> deleter) noexcept
|
|
: m_ptr(p)
|
|
, m_deleter(deleter)
|
|
{}
|
|
|
|
/*! \brief Constructs an empty \p unique_ptr with a custom deleter (lvalue reference).
|
|
*
|
|
* \param deleter The deleter to store.
|
|
*/
|
|
template <bool Dummy = true, class = EnableIfDeleterConstructible<LValRefType<Dummy>>>
|
|
THRUST_HOST THRUST_CONSTEXPR_SINCE_CXX23 unique_ptr(std::nullptr_t, LValRefType<Dummy> deleter) noexcept
|
|
: m_ptr(nullptr)
|
|
, m_deleter(deleter)
|
|
{}
|
|
|
|
/*! \brief Constructs a \p unique_ptr with a custom deleter (rvalue reference).
|
|
*
|
|
* \param p A pointer to the array in device memory to manage.
|
|
* \param deleter The deleter to use for destroying the array (moved).
|
|
*/
|
|
template <class Pp,
|
|
bool Dummy = true,
|
|
class = EnableIfDeleterConstructible<GoodRValRefType<Dummy>>,
|
|
class = EnableIfPointerConvertible<Pp>>
|
|
THRUST_HOST THRUST_CONSTEXPR_SINCE_CXX23 unique_ptr(Pp p, GoodRValRefType<Dummy> deleter) noexcept
|
|
: m_ptr(p)
|
|
, m_deleter(std::move(deleter))
|
|
{
|
|
static_assert(!std::is_reference_v<deleter_type>, "rvalue deleter bound to reference");
|
|
}
|
|
|
|
/*! \brief Constructs an empty \p unique_ptr with a custom deleter (rvalue reference).
|
|
*
|
|
* \param deleter The deleter to store (moved).
|
|
*/
|
|
template <bool Dummy = true, class = EnableIfDeleterConstructible<GoodRValRefType<Dummy>>>
|
|
THRUST_HOST THRUST_CONSTEXPR_SINCE_CXX23 unique_ptr(std::nullptr_t, GoodRValRefType<Dummy> deleter) noexcept
|
|
: m_ptr(nullptr)
|
|
, m_deleter(std::move(deleter))
|
|
{
|
|
static_assert(!std::is_reference_v<deleter_type>, "rvalue deleter bound to reference");
|
|
}
|
|
|
|
template <class Pp,
|
|
bool Dummy = true,
|
|
class = EnableIfDeleterConstructible<BadRValRefType<Dummy>>,
|
|
class = EnableIfPointerConvertible<Pp>>
|
|
THRUST_HOST unique_ptr(Pp ptr, BadRValRefType<Dummy> deleter) = delete;
|
|
|
|
/*! \brief Move constructor that transfers ownership from another array \p unique_ptr.
|
|
*
|
|
* \param u The \p unique_ptr to move from.
|
|
*/
|
|
THRUST_HOST THRUST_CONSTEXPR_SINCE_CXX23 unique_ptr(unique_ptr&& u) noexcept
|
|
: m_ptr(u.release())
|
|
, m_deleter(std::forward<deleter_type>(u.get_deleter()))
|
|
{}
|
|
|
|
/*! \brief Converting move constructor from a compatible array \p unique_ptr.
|
|
*
|
|
* Allows converting from \p unique_ptr<U[], E> to \p unique_ptr<T[], D> when
|
|
* the array element types and deleter types are compatible (e.g., derived to base).
|
|
*
|
|
* \tparam Up An array element type convertible to \p T.
|
|
* \tparam Ep A deleter type convertible to \p D.
|
|
* \param u The \p unique_ptr to move from.
|
|
*/
|
|
template <class Up,
|
|
class Ep,
|
|
class = EnableIfMoveConvertible<unique_ptr<Up, Ep>, Up>,
|
|
class = EnableIfDeleterConvertible<Ep>>
|
|
THRUST_HOST THRUST_CONSTEXPR_SINCE_CXX23 unique_ptr(unique_ptr<Up, Ep>&& u) noexcept
|
|
: m_ptr(u.release())
|
|
, m_deleter(std::forward<Ep>(u.get_deleter()))
|
|
{}
|
|
|
|
//==========================================================================
|
|
// Assignment
|
|
//==========================================================================
|
|
/*! \brief Move assignment operator. Replaces the managed object with the one from \p u.
|
|
*
|
|
* Releases the currently managed array (if any) and takes ownership of
|
|
* the array managed by \p p.
|
|
*
|
|
* \param p The \p unique_ptr to move from.
|
|
* \return `*this`
|
|
*/
|
|
THRUST_HOST THRUST_CONSTEXPR_SINCE_CXX23 unique_ptr& operator=(unique_ptr&& p) noexcept
|
|
{
|
|
reset(p.release());
|
|
m_deleter = std::forward<deleter_type>(p.get_deleter());
|
|
return *this;
|
|
}
|
|
|
|
/*! \brief Converting assignment operator. Replaces the managed object with the one from \p p.
|
|
* \param p The \p unique_ptr to move from.
|
|
* \return `*this`
|
|
*/
|
|
template <class Up,
|
|
class Ep,
|
|
class = EnableIfMoveConvertible<unique_ptr<Up, Ep>, Up>,
|
|
class = EnableIfDeleterAssignable<Ep>>
|
|
THRUST_HOST THRUST_CONSTEXPR_SINCE_CXX23 unique_ptr& operator=(unique_ptr<Up, Ep>&& p) noexcept
|
|
{
|
|
reset(p.release());
|
|
m_deleter = std::forward<Ep>(p.get_deleter());
|
|
return *this;
|
|
}
|
|
|
|
/*! \brief Assigns a null pointer, deallocating the managed object. Effectively the same as calling reset().
|
|
* \return `*this`
|
|
*/
|
|
THRUST_HOST THRUST_CONSTEXPR_SINCE_CXX23 unique_ptr& operator=(std::nullptr_t) noexcept
|
|
{
|
|
reset();
|
|
return *this;
|
|
}
|
|
|
|
//==========================================================================
|
|
// Destructor
|
|
//==========================================================================
|
|
/*! \brief Destroys the \p unique_ptr, the managed array is destroyed via `get_deleter()(get())`. If get() == nullptr there are no effects.
|
|
*/
|
|
THRUST_HOST THRUST_CONSTEXPR_SINCE_CXX23 ~unique_ptr()
|
|
{
|
|
reset();
|
|
}
|
|
|
|
//==========================================================================
|
|
// Observers
|
|
//==========================================================================
|
|
/*! \brief Accesses an element of the managed array.
|
|
*
|
|
* For device arrays, accessing an element from host code triggers a copy
|
|
* from device to host memory.
|
|
*
|
|
* \warning Using this operator when iterating over the array is STRONGLY discouraged
|
|
* as the excessive number of single-element copies usually results in poor performance.
|
|
*
|
|
* \param i The index of the element to access.
|
|
* \return A reference to (or copy of) the element at index \p i.
|
|
*/
|
|
THRUST_HOST THRUST_CONSTEXPR_SINCE_CXX23 auto operator[](size_t i) const noexcept
|
|
{
|
|
return m_ptr[i];
|
|
}
|
|
|
|
/*! \brief Returns a pointer to the managed array or `nullptr` if no object is owned.
|
|
*
|
|
* \return Pointer to the managed array.
|
|
*/
|
|
THRUST_HOST THRUST_CONSTEXPR_SINCE_CXX23 pointer get() const noexcept
|
|
{
|
|
return m_ptr;
|
|
}
|
|
|
|
/*! \brief Returns a raw pointer to the managed array or `nullptr` if no object is owned.
|
|
*
|
|
* \return Pointer to the managed array.
|
|
*/
|
|
template <bool Dummy = true, class = EnableIfDeleterDefaultDelete<Dummy>>
|
|
THRUST_HOST THRUST_CONSTEXPR_SINCE_CXX23 T* get_raw() const noexcept
|
|
{
|
|
return raw_pointer_cast(m_ptr);
|
|
}
|
|
|
|
/*! \brief Returns a reference to the deleter object which would be used for destruction of the managed array.
|
|
* \return A reference to the stored deleter.
|
|
*/
|
|
THRUST_HOST THRUST_CONSTEXPR_SINCE_CXX23 deleter_type& get_deleter() noexcept
|
|
{
|
|
return m_deleter;
|
|
}
|
|
|
|
/*! \brief Returns a const reference to the deleter object which would be used for destruction of the managed array.
|
|
* \return A const reference to the stored deleter.
|
|
*/
|
|
THRUST_HOST THRUST_CONSTEXPR_SINCE_CXX23 const deleter_type& get_deleter() const noexcept
|
|
{
|
|
return m_deleter;
|
|
}
|
|
|
|
/*! \brief Checks if the \p unique_ptr owns an array.
|
|
* \return `true` if an array is owned, `false` otherwise.
|
|
*/
|
|
THRUST_HOST THRUST_CONSTEXPR_SINCE_CXX23 explicit operator bool() const noexcept
|
|
{
|
|
return m_ptr != nullptr;
|
|
}
|
|
|
|
//==========================================================================
|
|
// Modifiers
|
|
//==========================================================================
|
|
/*! \brief Releases ownership of the managed array, if any.
|
|
* \return A pointer to the released array.
|
|
*/
|
|
THRUST_HOST THRUST_CONSTEXPR_SINCE_CXX23 pointer release() noexcept
|
|
{
|
|
pointer temp = m_ptr;
|
|
m_ptr = pointer();
|
|
return temp;
|
|
}
|
|
|
|
/*! \brief Replaces the managed array.
|
|
* \param p The new array to manage.
|
|
*/
|
|
template <class Pp, class = EnableIfPointerConvertible<Pp>>
|
|
THRUST_HOST THRUST_CONSTEXPR_SINCE_CXX23 void reset(Pp p) noexcept
|
|
{
|
|
pointer temp = m_ptr;
|
|
m_ptr = p;
|
|
if (temp)
|
|
{
|
|
m_deleter(temp);
|
|
}
|
|
}
|
|
|
|
/*! \brief Replaces the managed array with \p nullptr.
|
|
*
|
|
* Destroys the currently managed array (if any) and resets to empty state.
|
|
*/
|
|
THRUST_HOST THRUST_CONSTEXPR_SINCE_CXX23 void reset(std::nullptr_t = nullptr) noexcept
|
|
{
|
|
pointer temp = m_ptr;
|
|
m_ptr = nullptr;
|
|
if (temp)
|
|
{
|
|
m_deleter(temp);
|
|
}
|
|
}
|
|
|
|
/*! \brief Swaps the managed array and deleter with another array \p unique_ptr.
|
|
* \param u The \p unique_ptr to swap with.
|
|
*/
|
|
THRUST_HOST THRUST_CONSTEXPR_SINCE_CXX23 void swap(unique_ptr& u) noexcept
|
|
{
|
|
using std::swap;
|
|
swap(m_ptr, u.m_ptr);
|
|
swap(m_deleter, u.m_deleter);
|
|
}
|
|
};
|
|
|
|
/*! \brief Swaps two \p unique_ptr objects.
|
|
*
|
|
* Exchanges the contents of \p x and \p y by calling `x.swap(y)`.
|
|
*
|
|
* \param x The first \p unique_ptr.
|
|
* \param y The second \p unique_ptr.
|
|
*/
|
|
template <class T, class D, std::enable_if_t<std::is_swappable_v<D>, void>>
|
|
inline THRUST_CONSTEXPR_SINCE_CXX23 void swap(unique_ptr<T, D>& x, unique_ptr<T, D>& y) noexcept
|
|
{
|
|
x.swap(y);
|
|
}
|
|
|
|
//==============================================================================
|
|
// Comparison Operators
|
|
//==============================================================================
|
|
/*! \brief Compares two \p unique_ptr objects for equality.
|
|
*
|
|
* Two \p unique_ptr objects are considered equal if they point to the same
|
|
* memory address or are both null.
|
|
*
|
|
* \param x The first \p unique_ptr to compare.
|
|
* \param y The second \p unique_ptr to compare.
|
|
* \return `true` if the pointers are equal, `false` otherwise.
|
|
*/
|
|
template <class T1, class D1, class T2, class D2>
|
|
THRUST_HOST inline THRUST_CONSTEXPR_SINCE_CXX23 bool operator==(const unique_ptr<T1, D1>& x, const unique_ptr<T2, D2>& y)
|
|
{
|
|
return x.get() == y.get();
|
|
}
|
|
|
|
#if THRUST_STD_VER <= 17
|
|
template <class T1, class D1, class T2, class D2>
|
|
/*! \brief Compares two \p unique_ptr objects for inequality (C++17 and earlier).
|
|
*
|
|
* \param x The first \p unique_ptr to compare.
|
|
* \param y The second \p unique_ptr to compare.
|
|
* \return `true` if the pointers are not equal, `false` otherwise.
|
|
*/
|
|
THRUST_HOST inline THRUST_CONSTEXPR_SINCE_CXX23 bool operator!=(const unique_ptr<T1, D1>& x, const unique_ptr<T2, D2>& y)
|
|
{
|
|
return !(x == y);
|
|
}
|
|
#endif
|
|
|
|
/*! \brief Compares two \p unique_ptr objects using less-than ordering.
|
|
*
|
|
* \param x The first \p unique_ptr to compare.
|
|
* \param y The second \p unique_ptr to compare.
|
|
* \return `true` if the pointer stored in \p x is less than the pointer stored in \p y, `false` otherwise.
|
|
*
|
|
* \note Operators `>`, `<=`, and `>=` are also provided and defined in terms of this operator.
|
|
*/
|
|
template <class T1, class D1, class T2, class D2>
|
|
THRUST_HOST inline THRUST_CONSTEXPR_SINCE_CXX23 bool operator<(const unique_ptr<T1, D1>& x, const unique_ptr<T2, D2>& y)
|
|
{
|
|
using P1 = typename unique_ptr<T1, D1>::element_type*;
|
|
using P2 = typename unique_ptr<T2, D2>::element_type*;
|
|
using CTP = typename std::common_type<P1, P2>::type;
|
|
return std::less<CTP>()(thrust::raw_pointer_cast(x.get()), thrust::raw_pointer_cast(y.get()));
|
|
}
|
|
|
|
/*! \brief Compares two \p unique_ptr objects using greater-than ordering.
|
|
*
|
|
* \param x The first \p unique_ptr to compare.
|
|
* \param y The second \p unique_ptr to compare.
|
|
* \return `true` if \p x is greater than \p y, `false` otherwise.
|
|
*/
|
|
template <class T1, class D1, class T2, class D2>
|
|
THRUST_HOST inline THRUST_CONSTEXPR_SINCE_CXX23 bool operator>(const unique_ptr<T1, D1>& x, const unique_ptr<T2, D2>& y)
|
|
{
|
|
return y < x;
|
|
}
|
|
|
|
/*! \brief Compares two \p unique_ptr objects using less-than-or-equal ordering.
|
|
*
|
|
* \param x The first \p unique_ptr to compare.
|
|
* \param y The second \p unique_ptr to compare.
|
|
* \return `true` if \p x is less than or equal to \p y, `false` otherwise.
|
|
*/
|
|
template <class T1, class D1, class T2, class D2>
|
|
THRUST_HOST inline THRUST_CONSTEXPR_SINCE_CXX23 bool operator<=(const unique_ptr<T1, D1>& x, const unique_ptr<T2, D2>& y)
|
|
{
|
|
return !(y < x);
|
|
}
|
|
|
|
/*! \brief Compares two \p unique_ptr objects using greater-than-or-equal ordering.
|
|
*
|
|
* \param x The first \p unique_ptr to compare.
|
|
* \param y The second \p unique_ptr to compare.
|
|
* \return `true` if \p x is greater than or equal to \p y, `false` otherwise.
|
|
*/
|
|
template <class T1, class D1, class T2, class D2>
|
|
THRUST_HOST inline THRUST_CONSTEXPR_SINCE_CXX23 bool operator>=(const unique_ptr<T1, D1>& x, const unique_ptr<T2, D2>& y)
|
|
{
|
|
return !(x < y);
|
|
}
|
|
|
|
#if THRUST_STD_VER >= 20
|
|
template <class T1, class D1, class T2, class D2>
|
|
THRUST_HOST inline auto operator<=> (const unique_ptr<T1, D1>& x, const unique_ptr<T2, D2>& y)
|
|
{
|
|
// TODO: once thrust::device_ptr supports three_way_comparison, we should be using that
|
|
return std::compare_three_way()(x.get_raw(), y.get_raw());
|
|
}
|
|
#endif
|
|
|
|
/*! \brief Compares a \p unique_ptr with nullptr for equality.
|
|
*
|
|
* \param x The \p unique_ptr to compare.
|
|
* \return `true` if \p x is empty, `false` otherwise.
|
|
*/
|
|
template <class T, class D>
|
|
THRUST_HOST inline THRUST_CONSTEXPR_SINCE_CXX23 bool operator==(const unique_ptr<T, D>& x, std::nullptr_t) noexcept
|
|
{
|
|
return !x;
|
|
}
|
|
|
|
#if THRUST_STD_VER <= 17
|
|
/*! \brief Compares nullptr with a \p unique_ptr for equality (C++17 and earlier).
|
|
*
|
|
* \param y The \p unique_ptr to compare.
|
|
* \return `true` if \p y is empty, `false` otherwise.
|
|
*/
|
|
template <class T, class D>
|
|
THRUST_HOST inline THRUST_CONSTEXPR_SINCE_CXX23 bool operator==(std::nullptr_t, const unique_ptr<T, D>& y) noexcept
|
|
{
|
|
return !y;
|
|
}
|
|
|
|
/*! \brief Compares a \p unique_ptr with nullptr for inequality (C++17 and earlier).
|
|
*
|
|
* \param x The \p unique_ptr to compare.
|
|
* \return `true` if \p x is not empty, `false` otherwise.
|
|
*/
|
|
template <class T, class D>
|
|
THRUST_HOST inline THRUST_CONSTEXPR_SINCE_CXX23 bool operator!=(const unique_ptr<T, D>& x, std::nullptr_t) noexcept
|
|
{
|
|
return static_cast<bool>(x);
|
|
}
|
|
|
|
/*! \brief Compares nullptr with a \p unique_ptr for inequality (C++17 and earlier).
|
|
*
|
|
* \param y The \p unique_ptr to compare.
|
|
* \return `true` if \p y is not empty, `false` otherwise.
|
|
*/
|
|
template <class T, class D>
|
|
THRUST_HOST inline THRUST_CONSTEXPR_SINCE_CXX23 bool operator!=(std::nullptr_t, const unique_ptr<T, D>& y) noexcept
|
|
{
|
|
return static_cast<bool>(y);
|
|
}
|
|
#endif
|
|
|
|
/*! \brief Compares a \p unique_ptr with nullptr using less-than ordering.
|
|
*
|
|
* \param x The \p unique_ptr to compare.
|
|
* \return `true` if the pointer in \p x is less than nullptr, `false` otherwise.
|
|
*/
|
|
template <class T, class D>
|
|
THRUST_HOST inline THRUST_CONSTEXPR_SINCE_CXX23 bool operator<(const unique_ptr<T, D>& x, std::nullptr_t)
|
|
{
|
|
return std::less<typename unique_ptr<T, D>::pointer>()(x.get(), nullptr); // x.get() < nullptr;
|
|
}
|
|
|
|
/*! \brief Compares nullptr with a \p unique_ptr using less-than ordering.
|
|
*
|
|
* \param y The \p unique_ptr to compare.
|
|
* \return `true` if nullptr is less than the pointer in \p y, `false` otherwise.
|
|
*/
|
|
template <class T, class D>
|
|
THRUST_HOST inline THRUST_CONSTEXPR_SINCE_CXX23 bool operator<(std::nullptr_t, const unique_ptr<T, D>& y)
|
|
{
|
|
return std::less<typename unique_ptr<T, D>::pointer>()(nullptr, y.get()); // nullptr < y.get();
|
|
}
|
|
|
|
/*! \brief Compares a \p unique_ptr with nullptr using greater-than ordering.
|
|
*
|
|
* \param x The \p unique_ptr to compare.
|
|
* \return `true` if the pointer in \p x is greater than nullptr, `false` otherwise.
|
|
*/
|
|
template <class T, class D>
|
|
THRUST_HOST inline THRUST_CONSTEXPR_SINCE_CXX23 bool operator>(const unique_ptr<T, D>& x, std::nullptr_t)
|
|
{
|
|
return nullptr < x;
|
|
}
|
|
|
|
/*! \brief Compares nullptr with a \p unique_ptr using greater-than ordering.
|
|
*
|
|
* \param y The \p unique_ptr to compare.
|
|
* \return `true` if nullptr is greater than the pointer in \p y, `false` otherwise.
|
|
*/
|
|
template <class T, class D>
|
|
THRUST_HOST inline THRUST_CONSTEXPR_SINCE_CXX23 bool operator>(std::nullptr_t, const unique_ptr<T, D>& y)
|
|
{
|
|
return y < nullptr;
|
|
}
|
|
|
|
/*! \brief Compares a \p unique_ptr with nullptr using less-than-or-equal ordering.
|
|
*
|
|
* \param x The \p unique_ptr to compare.
|
|
* \return `true` if the pointer in \p x is less than or equal to nullptr, `false` otherwise.
|
|
*/
|
|
template <class T, class D>
|
|
THRUST_HOST inline THRUST_CONSTEXPR_SINCE_CXX23 bool operator<=(const unique_ptr<T, D>& x, std::nullptr_t)
|
|
{
|
|
return !(nullptr < x);
|
|
}
|
|
|
|
/*! \brief Compares nullptr with a \p unique_ptr using less-than-or-equal ordering.
|
|
*
|
|
* \param y The \p unique_ptr to compare.
|
|
* \return `true` if nullptr is less than or equal to the pointer in \p y, `false` otherwise.
|
|
*/
|
|
template <class T, class D>
|
|
THRUST_HOST inline THRUST_CONSTEXPR_SINCE_CXX23 bool operator<=(std::nullptr_t, const unique_ptr<T, D>& y)
|
|
{
|
|
return !(y < nullptr);
|
|
}
|
|
|
|
/*! \brief Compares a \p unique_ptr with nullptr using greater-than-or-equal ordering.
|
|
*
|
|
* \param x The \p unique_ptr to compare.
|
|
* \return `true` if the pointer in \p x is greater than or equal to nullptr, `false` otherwise.
|
|
*/
|
|
template <class T, class D>
|
|
THRUST_HOST inline THRUST_CONSTEXPR_SINCE_CXX23 bool operator>=(const unique_ptr<T, D>& x, std::nullptr_t)
|
|
{
|
|
return !(x < nullptr);
|
|
}
|
|
|
|
/*! \brief Compares nullptr with a \p unique_ptr using greater-than-or-equal ordering.
|
|
*
|
|
* \param y The \p unique_ptr to compare.
|
|
* \return `true` if nullptr is greater than or equal to the pointer in \p y, `false` otherwise.
|
|
*/
|
|
template <class T, class D>
|
|
THRUST_HOST inline THRUST_CONSTEXPR_SINCE_CXX23 bool operator>=(std::nullptr_t, const unique_ptr<T, D>& y)
|
|
{
|
|
return !(y < nullptr);
|
|
}
|
|
|
|
#if THRUST_STD_VER >= 20
|
|
template <class T, class D>
|
|
THRUST_HOST inline auto operator<=> (const unique_ptr<T, D>& x, std::nullptr_t)
|
|
{
|
|
// TODO: once thrust::device_ptr supports three_way_comparison, we should be using that
|
|
return std::compare_three_way()(x.get_raw(), static_cast<T*>(nullptr));
|
|
}
|
|
#endif
|
|
|
|
//==============================================================================
|
|
// Make unique
|
|
//==============================================================================
|
|
/*! \brief Constructs an object of type \p T in device memory and wraps it in a \p unique_ptr.
|
|
*
|
|
* Allocates device memory for a single object of type \p T, direct-initializes the object
|
|
* by forwarding the provided arguments, and returns a \p unique_ptr managing the
|
|
* allocated object.
|
|
*
|
|
* This overload participates in overload resolution only if \p T is not an array type.
|
|
*
|
|
* \tparam T The type of object to construct (must not be an array).
|
|
* \tparam Args The types of arguments to forward to the constructor of \p T.
|
|
* \param args Arguments to forward to the constructor of \p T.
|
|
* \return A \p unique_ptr<T> managing the newly created object.
|
|
*/
|
|
template <class T, class... Args, class = std::enable_if_t<!std::is_array_v<T>>>
|
|
THRUST_HOST inline THRUST_CONSTEXPR_SINCE_CXX23 unique_ptr<T> make_unique(Args&&... args)
|
|
{
|
|
thrust::device_ptr<T> p = thrust::device_malloc<T>(1);
|
|
return unique_ptr<T>(thrust::device_new<T>(p, T(std::forward<Args>(args)...), 1));
|
|
}
|
|
|
|
/*! \brief Constructs an array of objects of type \p T in device memory and wraps it in a \p unique_ptr.
|
|
*
|
|
* Allocates device memory for an array of \p n objects of type \p U (where \p T is \p U[]),
|
|
* default-initializes each element, and returns a \p unique_ptr managing the allocated array.
|
|
*
|
|
* This overload participates in overload resolution only if \p T is an array of unknown
|
|
* bound (e.g., \p T[]).
|
|
*
|
|
* \tparam T The array type (e.g., \p int[], \p MyClass[]).
|
|
* \param n The number of elements in the array.
|
|
* \return A \p unique_ptr<T> managing the newly created array.
|
|
*/
|
|
template <class T, class = std::enable_if_t<thrust::detail::is_unbounded_array<T>::value>>
|
|
THRUST_HOST inline THRUST_CONSTEXPR_SINCE_CXX23 unique_ptr<T> make_unique(size_t n)
|
|
{
|
|
using U = typename std::remove_extent<T>::type;
|
|
return unique_ptr<T>(thrust::device_new<U>(n), n);
|
|
}
|
|
|
|
template <class T, class... Args, class = std::enable_if_t<thrust::detail::is_bounded_array<T>::value>>
|
|
THRUST_HOST void make_unique(Args&&...) = delete;
|
|
|
|
#if THRUST_STD_VER >= 20
|
|
|
|
/*! \brief Constructs an object of type \p T in device memory without initialization (C++20).
|
|
*
|
|
* Allocates device memory for a single object of type \p T without initializing it,
|
|
* and returns a \p unique_ptr managing the allocated memory. The object has
|
|
* indeterminate value.
|
|
*
|
|
* This overload participates in overload resolution only if \p T is not an array type.
|
|
*
|
|
* \tparam T The type of object to allocate (must not be an array).
|
|
* \return A \p unique_ptr<T> managing the uninitialized memory.
|
|
*/
|
|
template <class T, class = std::enable_if_t<!std::is_array_v<T>>>
|
|
THRUST_HOST THRUST_CONSTEXPR_SINCE_CXX23 unique_ptr<T> make_unique_for_overwrite()
|
|
{
|
|
return unique_ptr<T>(thrust::device_malloc<T>(1));
|
|
}
|
|
|
|
/*! \brief Constructs an array without initialization (C++20).
|
|
*
|
|
* Allocates device memory for an array of \p n objects of type \p U (where \p T is \p U[])
|
|
* without initializing the elements, and returns a \p unique_ptr managing the allocated
|
|
* array. The elements have indeterminate values.
|
|
*
|
|
* This overload participates in overload resolution only if \p T is an array of unknown
|
|
* bound (e.g., \p T[]).
|
|
*
|
|
* \tparam T The array type (e.g., \p int[], \p MyClass[]).
|
|
* \param n The number of elements in the array.
|
|
* \return A \p unique_ptr<T> managing the uninitialized array.
|
|
*/
|
|
template <class T, class = std::enable_if_t<thrust::detail::is_unbounded_array<T>::value>>
|
|
THRUST_HOST THRUST_CONSTEXPR_SINCE_CXX23 unique_ptr<T> make_unique_for_overwrite(size_t n)
|
|
{
|
|
using U = typename std::remove_extent<T>::type;
|
|
|
|
return unique_ptr<T>(thrust::device_malloc<U>(n), n);
|
|
}
|
|
|
|
template <class T, class... Args, class = std::enable_if_t<thrust::detail::is_bounded_array<T>::value>>
|
|
THRUST_HOST void make_unique_for_overwrite(Args&&...) = delete;
|
|
|
|
#endif
|
|
|
|
/*! \} // end smart_pointers
|
|
*/
|
|
|
|
THRUST_NAMESPACE_END
|