Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Host/Device accessors for mdspan #3686

Draft
wants to merge 26 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from 23 commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
b115039
first draft
fbusato Feb 5, 2025
0ea452d
split implemenation
fbusato Feb 5, 2025
9ae4953
add host/device pointer checks
fbusato Feb 5, 2025
96f8419
add pointer checks
fbusato Feb 6, 2025
52b12a6
add tests
fbusato Feb 6, 2025
cab748e
fix device/host memory accessibility detection
fbusato Feb 6, 2025
31b84c7
fix typo
fbusato Feb 6, 2025
430e46d
add recursive traits
fbusato Feb 6, 2025
3c76d32
prevent ADL
fbusato Feb 6, 2025
d63980d
size_t namespace
fbusato Feb 6, 2025
e9ce8a4
is_pointer namespace
fbusato Feb 6, 2025
de4264c
fix __self
fbusato Feb 7, 2025
c0d12da
add noexcept
fbusato Feb 7, 2025
572447d
Update libcudacxx/include/cuda/__mdspan/host_device_accessor.h
fbusato Feb 8, 2025
0eb90ef
Update libcudacxx/include/cuda/__mdspan/host_device_accessor.h
fbusato Feb 8, 2025
71a9161
expose accessor types
fbusato Feb 8, 2025
921273a
remove default, copy constructors
fbusato Feb 8, 2025
aef9602
fix default and copy constructors
fbusato Feb 10, 2025
0647703
add inheritance aliases
fbusato Feb 10, 2025
c30482e
Merge branch 'main' into host-device-mdspan-accessors
fbusato Feb 10, 2025
fc82009
add default_accessor constructor and conversion
fbusato Feb 10, 2025
21e2feb
guard cuda_runtime_api.h
fbusato Feb 14, 2025
22fecf3
Merge branch 'main' into host-device-mdspan-accessors
fbusato Feb 14, 2025
ca47b44
Update libcudacxx/include/cuda/__mdspan/host_device_accessor.h
fbusato Feb 20, 2025
9a7e393
add detectably_invalid
fbusato Feb 20, 2025
a0af8f4
add __managed_accessor conversions from/to default_accessors
fbusato Feb 21, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
383 changes: 383 additions & 0 deletions libcudacxx/include/cuda/__mdspan/host_device_accessor.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,383 @@
//===----------------------------------------------------------------------===//
//
// Part of libcu++, the C++ Standard Library for your entire system,
// under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
// SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES.
//
//===----------------------------------------------------------------------===//

#ifndef _CUDA___MDSPAN_HOST_DEVICE_ACCESSOR
#define _CUDA___MDSPAN_HOST_DEVICE_ACCESSOR

#include <cuda/std/detail/__config>

#if defined(_CCCL_IMPLICIT_SYSTEM_HEADER_GCC)
# pragma GCC system_header
#elif defined(_CCCL_IMPLICIT_SYSTEM_HEADER_CLANG)
# pragma clang system_header
#elif defined(_CCCL_IMPLICIT_SYSTEM_HEADER_MSVC)
# pragma system_header
#endif // no system header

#if _CCCL_CUDA_COMPILER(CLANG)
# include <cuda_runtime_api.h>
#endif // _CCCL_CUDA_COMPILER(CLANG) && !_CCCL_COMPILER(NVRTC)

#include <cuda/std/__mdspan/default_accessor.h>
#include <cuda/std/__type_traits/is_convertible.h>
#include <cuda/std/__type_traits/is_default_constructible.h>
#include <cuda/std/__type_traits/is_pointer.h>
#include <cuda/std/cassert>
#include <cuda/std/cstddef>
//
#include <cuda/std/__concepts/__concept_macros.h>

_LIBCUDACXX_BEGIN_NAMESPACE_CUDA

template <typename _Accessor>
struct __host_accessor;

template <typename _Accessor>
struct __device_accessor;

template <typename _Accessor>
struct __managed_accessor;

template <typename _Accessor>
using host_accessor = __host_accessor<_Accessor>;

template <typename _Accessor>
using device_accessor = __device_accessor<_Accessor>;

template <typename _Accessor>
using managed_accessor = __managed_accessor<_Accessor>;

/***********************************************************************************************************************
* Host/Device/Managed Accessor Traits
**********************************************************************************************************************/

template <typename>
inline constexpr bool is_host_accessor_v = false;

template <typename>
inline constexpr bool is_device_accessor_v = false;

template <typename>
inline constexpr bool is_managed_accessor_v = false;

template <typename _Accessor>
inline constexpr bool is_host_accessor_v<__host_accessor<_Accessor>> = true;

template <typename _Accessor>
inline constexpr bool is_device_accessor_v<__device_accessor<_Accessor>> = true;

template <typename _Accessor>
inline constexpr bool is_managed_accessor_v<__managed_accessor<_Accessor>> = true;

template <typename _Tp>
inline constexpr bool is_host_device_managed_accessor_v =
is_host_accessor_v<_Tp> || is_device_accessor_v<_Tp> || is_managed_accessor_v<_Tp>;

/***********************************************************************************************************************
* Host Accessor
**********************************************************************************************************************/

template <typename _Accessor>
struct __host_accessor : public _Accessor
{
static_assert(!is_host_device_managed_accessor_v<_Accessor>,
"cuda::__host_accessor/cuda::__device_accessor/cuda::__managed_accessor cannot be nested");

using offset_policy = __host_accessor<typename _Accessor::offset_policy>;
using data_handle_type = typename _Accessor::data_handle_type;
using reference = typename _Accessor::reference;
using element_type = typename _Accessor::element_type;

private:
using __self = __host_accessor<_Accessor>;

static constexpr bool __is_ctor_noexcept = noexcept(_Accessor{});
static constexpr bool __is_copy_ctor_noexcept = noexcept(_Accessor{_Accessor{}}); // TODO: this could be a move ctor
static constexpr bool __is_access_noexcept = noexcept(_Accessor{}.access(data_handle_type{}, 0));
static constexpr bool __is_offset_noexcept = noexcept(_Accessor{}.offset(data_handle_type{}, 0));

template <typename data_handle_type>
_LIBCUDACXX_HIDE_FROM_ABI static constexpr bool __is_host_accessible_pointer(data_handle_type __p) noexcept
{
if constexpr (_CUDA_VSTD::is_pointer_v<data_handle_type>)
{
cudaPointerAttributes __attrib;
_CCCL_VERIFY(::cudaPointerGetAttributes(&__attrib, __p) == ::cudaSuccess, "cudaPointerGetAttributes failed");
return __attrib.hostPointer != nullptr || __attrib.type == ::cudaMemoryTypeUnregistered;
}
else
{
return true; // cannot be verified
}
}

_LIBCUDACXX_HIDE_FROM_ABI static constexpr void __check_host_pointer(data_handle_type __p) noexcept
{
_CCCL_ASSERT(__self::__is_host_accessible_pointer(__p), "cuda::__host_accessor data handle is not a HOST pointer");
}

template <typename _Sp = bool> // lazy evaluation
_LIBCUDACXX_HIDE_FROM_ABI static constexpr void __prevent_device_instantiation() noexcept
{
static_assert(sizeof(_Sp) != sizeof(_Sp), "cuda::__host_accessor cannot be used in HOST code");
}

public:
_CCCL_TEMPLATE(typename _NotUsed = void)
_CCCL_REQUIRES(_CCCL_TRAIT(_CUDA_VSTD::is_default_constructible, _Accessor))
_LIBCUDACXX_HIDE_FROM_ABI __host_accessor() noexcept(__is_ctor_noexcept)
: _Accessor{}
{}

_CCCL_TEMPLATE(typename _OtherElementType)
_CCCL_REQUIRES(_CCCL_TRAIT(_CUDA_VSTD::is_convertible, _OtherElementType (*)[], element_type (*)[]))
_LIBCUDACXX_HIDE_FROM_ABI constexpr __host_accessor(__host_accessor<_OtherElementType>) noexcept(
__is_copy_ctor_noexcept)
{}

_CCCL_TEMPLATE(typename _OtherElementType)
_CCCL_REQUIRES(_CCCL_TRAIT(_CUDA_VSTD::is_convertible, _OtherElementType (*)[], element_type (*)[]))
_LIBCUDACXX_HIDE_FROM_ABI constexpr __host_accessor(__managed_accessor<_OtherElementType>) noexcept(
__is_copy_ctor_noexcept)
{}
Comment on lines +131 to +147
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In a previous review, I mentioned the issue that this omits all the conversions to and from _Accessor that the _Accessor class defines. I just want to mention it again so I don't forget : - ) . Conversions are an important part of an accessor's interface, because they help define conversions for mdspan itself. For example, without default_accessor's converting constructor, you can't assign mdspan-of-nonconst to mdspan-of-const. Section 5.8 of P2897 elaborates this explanation.

Should we consider instead a CRTP base class approach to __*_accessor? That would let developers of custom accessors opt into the run-time pointer checking functionality.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

could be that you reviewed an older version? I previously added conversions from/to default_accessor for host_accessor and device_accessor.
Honestly, I'm was sure about managed_accessor because it looks unsafe...

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we consider instead a CRTP base class approach to __*_accessor? That would let developers of custom accessors opt into the run-time pointer checking functionality.

the idea is nice. I'm a bit concerned about the implications for users. Could host_accessor be roughly defined in the following way?

class host_accessor : public __host_accessor<cuda::std::default_accessor> { .. };

Copy link
Contributor

@mhoemmen mhoemmen Feb 21, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@fbusato wrote:

could be that you reviewed an older version? I previously added conversions from/to default_accessor for host_accessor and device_accessor.

The issue is with transitive conversions for a possibly generic _Accessor.

For example, suppose that I define two accessors A and B. A is weaker (more type-erased) than B. A defines an implicit conversion from B, and B defines an explicit conversion from A. (This follows the idiom that explicit conversions assert preconditions, while implicit conversions have no preconditions.) One example of this case is A = default_accessor<float>, and B = aligned_accessor<float, 16>.

struct A {
  // ... other accessor stuff ...
  A(B) {}
};

struct B {
  // ... other accessor stuff ...
  explicit B(A) {}
};

The problem is, these conversions don't carry over to {host,device,managed}_accessor. For example, host_accessor<A> does not have an implicit conversion from host_accessor<B>, and host_accessor<B> does not have an explicit conversion from host_accessor<A>.

Here's a draft for how to fix this. I would need to prototype this a bit to make sure it works.

template<class _Accessor>
class host_accessor {
private:
  _Accessor acc_;

  // ... details ...
public:
  // ... other constructors ...

  // This case covers host_accessor<default_accessor<OtherElementType>> as well.
  template<class InputAccessor>
  // If _Accessor is constructible from InputAccessor, then
  // host_accessor<_Accessor> is constructible from
  // host_accessor<InputAccessor>.
  requires(is_constructible<_Accessor, const InputAccessor&>)
  constexpr
  // Conversion is explicit if there is no implicit conversion
  // from InputAccessor to _Accessor.
  explicit(! is_convertible_v<const InputAccessor&, _Accessor>)
  host_accessor(const host_accessor<InputAccessor>& input_acc)
    : acc_(input_acc)
  {}
};

Copy link
Contributor

@mhoemmen mhoemmen Feb 22, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@fbusato I just sent you an offline message with a link to an example of how to do this correctly. wrapper (see below) is like host_accessor or device_accessor -- an accessor that wraps another accessor. The link explains the design and includes tests and use examples.

template<class Accessor>
requires(not is_wrapper_v<Accessor>)
class wrapper : public Accessor {
public:
  using offset_policy = typename Accessor::offset_policy;
  using element_type = typename Accessor::element_type;
  using reference = typename Accessor::reference;
  using data_handle_type = typename Accessor::data_handle_type;

  constexpr wrapper() noexcept 
    requires(std::is_default_constructible_v<Accessor>)
  = default;

  // "Wrapping constructor" -- takes the inner accessor.
  constexpr wrapper(const Accessor& input_acc)
    : Accessor(input_acc)
  {}

  template<class OtherAccessor>
  requires(
    std::is_constructible_v<Accessor, OtherAccessor>
  )
  constexpr
  explicit(
    not std::is_convertible_v<OtherAccessor, Accessor>
  )
  wrapper(const wrapper<OtherAccessor>& input_acc)
    : Accessor(input_acc)
  {}

  constexpr reference
  access(data_handle_type p, size_t k) const {
    return Accessor::access(p, k);
  }

  constexpr typename offset_policy::data_handle_type
  offset(data_handle_type p, size_t k) const {
    return Accessor::offset(p, k);
  }
};


_CCCL_NV_DIAG_SUPPRESS(554) // _Accessor could be exactly default_accessor

_CCCL_TEMPLATE(class _OtherElementType)
_CCCL_REQUIRES(_CCCL_TRAIT(_CUDA_VSTD::is_convertible, _OtherElementType (*)[], element_type (*)[]))
_LIBCUDACXX_HIDE_FROM_ABI constexpr __host_accessor(_CUDA_VSTD::default_accessor<_OtherElementType>) noexcept
{
NV_IF_TARGET(NV_IS_DEVICE, (__self::__prevent_device_instantiation();))
}

#if defined(_CCCL_TEMPLATED_CONVERSION_TO_DEFAULT_ACCESSOR)
_CCCL_TEMPLATE(class _OtherElementType)
_CCCL_REQUIRES(_CCCL_TRAIT(_CUDA_VSTD::is_convertible, _OtherElementType (*)[], element_type (*)[]))
_LIBCUDACXX_HIDE_FROM_ABI constexpr operator _CUDA_VSTD::default_accessor<OtherElementType>() const noexcept
#else
_LIBCUDACXX_HIDE_FROM_ABI constexpr operator _CUDA_VSTD::default_accessor<element_type>() const noexcept
#endif
{
NV_IF_TARGET(NV_IS_DEVICE, (__self::__prevent_device_instantiation();))
return {};
}

_CCCL_NV_DIAG_DEFAULT(554)

Copy link
Contributor

@mhoemmen mhoemmen Feb 7, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A key feature of any accessor is the set of permitted conversions, both implicit and explicit. mdspan depends on accessors' conversions in order for its own conversions to work. Implicit conversions make mdspan assignment work; explicit conversions make explicit construction of an mdspan of one type from an mdspan of another type work. Even default_accessor has a conversion to permit assigning an mdspan of nonconst to an mdspan of const.

An accessor's conversions are defined by its constructors and/or conversion operators. Not exposing those from the parent class means that mdspan of host_accessor<Accessor> won't be able to perform any of the conversions that mdspan of Accessor can perform.

A public using _Accessor::_Accessor; declaration would pull in all of the base class' constructors, but it wouldn't pull in any conversion operators.

Another issue is that _Accessor might have a virtual destructor. That would be weird, but it's legal in the Standard and nothing in this class prevents it.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Section 5.8 of P2897R7 elaborates the design intent behind accessor conversions.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A public using _Accessor::_Accessor; declaration would pull in all of the base class' constructors, but it wouldn't pull in any conversion operators.

For example, if Accessor A provides a conversion operator to B and a conversion operator to C, the only way to provide those in device_accessor<A> would be to use reflection to enumerate all conversion operators of A, and paste new ones into device_accessor<A> for device_accessor<B> and device_accessor<C>. While I'm not a C++ reflection expert, I don't think even P2996 reflection can do that, as it would require injecting member function definitions.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I entirely missed that part. I added the constructors following the aligned_accessor model.

_LIBCUDACXX_HIDE_FROM_ABI constexpr reference access(data_handle_type __p, _CUDA_VSTD::size_t __i) const
noexcept(__is_access_noexcept)
{
NV_IF_ELSE_TARGET(NV_IS_HOST,
(__self::__check_host_pointer(__p);), //
(static_assert(false, "cuda::__host_accessor cannot be used in DEVICE code");))
return _Accessor::access(__p, __i);
}

_LIBCUDACXX_HIDE_FROM_ABI constexpr data_handle_type offset(data_handle_type __p, _CUDA_VSTD::size_t __i) const
noexcept(__is_offset_noexcept)
{
NV_IF_ELSE_TARGET(NV_IS_HOST,
(__self::__check_host_pointer(__p);), //
(static_assert(false, "cuda::__host_accessor cannot be used in DEVICE code");))
return _Accessor::offset(__p, __i);
}
};

/***********************************************************************************************************************
* Device Accessor
**********************************************************************************************************************/

template <typename _Accessor>
struct __device_accessor : public _Accessor
{
static_assert(!is_host_device_managed_accessor_v<_Accessor>,
"cuda::__host_accessor/cuda::__device_accessor/cuda::__managed_accessor cannot be nested");

using offset_policy = __device_accessor<typename _Accessor::offset_policy>;
using data_handle_type = typename _Accessor::data_handle_type;
using reference = typename _Accessor::reference;
using element_type = typename _Accessor::element_type;

private:
using __self = __device_accessor<_Accessor>;

static constexpr bool __is_ctor_noexcept = noexcept(_Accessor{});
static constexpr bool __is_copy_ctor_noexcept = noexcept(_Accessor{_Accessor{}});
static constexpr bool __is_access_noexcept = noexcept(_Accessor{}.access(data_handle_type{}, 0));
static constexpr bool __is_offset_noexcept = noexcept(_Accessor{}.offset(data_handle_type{}, 0));

template <typename _Sp = bool> // lazy evaluation
_LIBCUDACXX_HIDE_FROM_ABI static constexpr void __prevent_host_instantiation() noexcept
{
static_assert(sizeof(_Sp) != sizeof(_Sp), "cuda::__device_accessor cannot be used in HOST code");
}

public:
_CCCL_TEMPLATE(typename _NotUsed = void)
_CCCL_REQUIRES(_CCCL_TRAIT(_CUDA_VSTD::is_default_constructible, _Accessor))
_LIBCUDACXX_HIDE_FROM_ABI __device_accessor() noexcept(__is_ctor_noexcept)
: _Accessor{}
{}

_CCCL_TEMPLATE(typename _OtherElementType)
_CCCL_REQUIRES(_CCCL_TRAIT(_CUDA_VSTD::is_convertible, _OtherElementType (*)[], element_type (*)[]))
_LIBCUDACXX_HIDE_FROM_ABI constexpr __device_accessor(__device_accessor<_OtherElementType>) noexcept(
__is_copy_ctor_noexcept)
{}

_CCCL_TEMPLATE(typename _OtherElementType)
_CCCL_REQUIRES(_CCCL_TRAIT(_CUDA_VSTD::is_convertible, _OtherElementType (*)[], element_type (*)[]))
_LIBCUDACXX_HIDE_FROM_ABI constexpr __device_accessor(__managed_accessor<_OtherElementType>) noexcept(
__is_copy_ctor_noexcept)
{}

_CCCL_NV_DIAG_SUPPRESS(554) // _Accessor could be exactly default_accessor

_CCCL_TEMPLATE(class _OtherElementType)
_CCCL_REQUIRES(_CCCL_TRAIT(_CUDA_VSTD::is_convertible, _OtherElementType (*)[], element_type (*)[]))
_LIBCUDACXX_HIDE_FROM_ABI constexpr __device_accessor(_CUDA_VSTD::default_accessor<_OtherElementType>) noexcept
{
NV_IF_TARGET(NV_IS_DEVICE, (__self::__prevent_host_instantiation();))
}

#if defined(_CCCL_TEMPLATED_CONVERSION_TO_DEFAULT_ACCESSOR)
_CCCL_TEMPLATE(class _OtherElementType)
_CCCL_REQUIRES(_CCCL_TRAIT(_CUDA_VSTD::is_convertible, _OtherElementType (*)[], element_type (*)[]))
_LIBCUDACXX_HIDE_FROM_ABI constexpr operator _CUDA_VSTD::default_accessor<OtherElementType>() const noexcept
#else
_LIBCUDACXX_HIDE_FROM_ABI constexpr operator _CUDA_VSTD::default_accessor<element_type>() const noexcept
#endif
{
NV_IF_TARGET(NV_IS_DEVICE, (__self::__prevent_host_instantiation();))
return {};
}

_CCCL_NV_DIAG_DEFAULT(554)

_LIBCUDACXX_HIDE_FROM_ABI constexpr reference access(data_handle_type __p, _CUDA_VSTD::size_t __i) const
noexcept(__is_access_noexcept)
{
NV_IF_TARGET(NV_IS_HOST, (__self::__prevent_host_instantiation();))
return _Accessor::access(__p, __i);
}

_LIBCUDACXX_HIDE_FROM_ABI constexpr data_handle_type offset(data_handle_type __p, _CUDA_VSTD::size_t __i) const
noexcept(__is_offset_noexcept)
{
NV_IF_TARGET(NV_IS_HOST, (__self::__prevent_host_instantiation();))
return _Accessor::offset(__p, __i);
}
};

/***********************************************************************************************************************
* Managed Accessor
**********************************************************************************************************************/

template <typename _Accessor>
struct __managed_accessor : public _Accessor
{
static_assert(!is_host_device_managed_accessor_v<_Accessor>,
"cuda::__host_accessor/cuda::__device_accessor/cuda::__managed_accessor cannot be nested");

using offset_policy = __managed_accessor<typename _Accessor::offset_policy>;
using data_handle_type = typename _Accessor::data_handle_type;
using reference = typename _Accessor::reference;
using element_type = typename _Accessor::element_type;

private:
using __self = __managed_accessor<_Accessor>;

static constexpr bool __is_ctor_noexcept = noexcept(_Accessor{});
static constexpr bool __is_copy_ctor_noexcept = noexcept(_Accessor{_Accessor{}});
static constexpr bool __is_access_noexcept = noexcept(_Accessor{}.access(data_handle_type{}, 0));
static constexpr bool __is_offset_noexcept = noexcept(_Accessor{}.offset(data_handle_type{}, 0));

static_assert(!is_host_device_managed_accessor_v<_Accessor>,
"cuda::__host_accessor/cuda::__device_accessor/cuda::__managed_accessor cannot be nested");

template <typename data_handle_type>
_LIBCUDACXX_HIDE_FROM_ABI static constexpr bool __is_managed_pointer(data_handle_type __p) noexcept
{
if constexpr (_CUDA_VSTD::is_pointer_v<data_handle_type>)
{
cudaPointerAttributes __attrib;
_CCCL_VERIFY(::cudaPointerGetAttributes(&__attrib, __p) == ::cudaSuccess, "cudaPointerGetAttributes failed");
return __attrib.devicePointer != nullptr && __attrib.hostPointer == __attrib.devicePointer;
}
else
{
return true; // cannot be verified
}
}

_LIBCUDACXX_HIDE_FROM_ABI static constexpr void __check_managed_pointer(data_handle_type __p) noexcept
{
_CCCL_ASSERT(__self::__is_managed_pointer(__p), "cuda::__managed_accessor data handle is not a MANAGED pointer");
}

public:
_CCCL_TEMPLATE(typename _NotUsed = void)
_CCCL_REQUIRES(_CCCL_TRAIT(_CUDA_VSTD::is_default_constructible, _Accessor))
_LIBCUDACXX_HIDE_FROM_ABI __managed_accessor() noexcept(__is_ctor_noexcept)
: _Accessor{}
{}

_CCCL_TEMPLATE(typename _OtherElementType)
_CCCL_REQUIRES(_CCCL_TRAIT(_CUDA_VSTD::is_convertible, _OtherElementType (*)[], element_type (*)[]))
_LIBCUDACXX_HIDE_FROM_ABI constexpr __managed_accessor(__managed_accessor<_OtherElementType>) noexcept(
__is_copy_ctor_noexcept)
{}

_LIBCUDACXX_HIDE_FROM_ABI constexpr reference access(data_handle_type __p, _CUDA_VSTD::size_t __i) const
noexcept(__is_access_noexcept)
{
NV_IF_TARGET(NV_IS_HOST, (__self::__check_managed_pointer(__p);))
return _Accessor::access(__p, __i);
}

_LIBCUDACXX_HIDE_FROM_ABI constexpr data_handle_type offset(data_handle_type __p, _CUDA_VSTD::size_t __i) const
noexcept(__is_offset_noexcept)
{
NV_IF_TARGET(NV_IS_HOST, (__self::__check_managed_pointer(__p);))
return _Accessor::offset(__p, __i);
}
};

/***********************************************************************************************************************
* Accessibility Traits
**********************************************************************************************************************/

template <typename>
inline constexpr bool is_host_accessible_v = false;

template <typename>
inline constexpr bool is_device_accessible_v = false;

template <typename _Accessor>
inline constexpr bool is_host_accessible_v<__host_accessor<_Accessor>> = true;

template <typename _Accessor>
inline constexpr bool is_host_accessible_v<__managed_accessor<_Accessor>> = true;

template <template <typename> class _TClass, typename _Accessor>
inline constexpr bool is_host_accessible_v<_TClass<_Accessor>> = is_host_accessible_v<_Accessor>;

template <typename _Accessor>
inline constexpr bool is_device_accessible_v<__device_accessor<_Accessor>> = true;

template <typename _Accessor>
inline constexpr bool is_device_accessible_v<__managed_accessor<_Accessor>> = true;

template <template <typename> class _TClass, typename _Accessor>
inline constexpr bool is_device_accessible_v<_TClass<_Accessor>> = is_device_accessible_v<_Accessor>;

_LIBCUDACXX_END_NAMESPACE_CUDA

#endif // _CUDA___MDSPAN_HOST_DEVICE_ACCESSOR
Loading
Loading