Skip to content

[oneDPL] Indirectly Device Accessible Iterator Customization Point and Public Trait #620

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

Open
wants to merge 39 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 23 commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
d90682b
Initial draft
danhoeflinger Apr 3, 2025
24b879d
grammar, clarity
danhoeflinger Apr 3, 2025
ff940e8
formatting
danhoeflinger Apr 3, 2025
0c0804b
code indentation
danhoeflinger Apr 4, 2025
8cff3e4
testing indentation formatting
danhoeflinger Apr 4, 2025
a36a006
indentation of comments in code
danhoeflinger Apr 4, 2025
cb102cb
removing unnecessary const ref
danhoeflinger Apr 4, 2025
bc32ce2
more indentation changes
danhoeflinger Apr 4, 2025
e7ffb99
language improvements
danhoeflinger Apr 4, 2025
913d555
remove some repetition
danhoeflinger Apr 4, 2025
29e209f
fix underline
danhoeflinger Apr 4, 2025
180a500
improve example, shorten var names
danhoeflinger Apr 4, 2025
a852f0f
improve comment clarity
danhoeflinger Apr 4, 2025
41b9828
Adding link for SYCL
danhoeflinger Apr 4, 2025
96b1b45
Device accessible content instead of passed directly
danhoeflinger Apr 4, 2025
dd71fdf
more information about device policy iterator compatibility
danhoeflinger Apr 4, 2025
21b8d0f
language improvement
danhoeflinger Apr 4, 2025
7c76593
readjusting content
danhoeflinger Apr 4, 2025
250aed2
minor improvements
danhoeflinger Apr 4, 2025
696d925
Improve language
danhoeflinger Apr 4, 2025
b250e77
accept suggestion
danhoeflinger Apr 7, 2025
8928a06
removing legacy passed direcly description
danhoeflinger Apr 7, 2025
1fff43d
Adjusting structure, and some language improvements
danhoeflinger Apr 7, 2025
59c147a
remove "always"
danhoeflinger Apr 8, 2025
0fe3101
Address feedback, take suggestions
danhoeflinger Apr 8, 2025
1d44b35
adding template to the text
danhoeflinger Apr 8, 2025
a918a45
remove usm std::vector::iterators mention
danhoeflinger Apr 8, 2025
25edb0b
adjust code formatting
danhoeflinger Apr 8, 2025
b4aeaf0
Signed-off-by: Dan Hoeflinger <[email protected]>
danhoeflinger Apr 8, 2025
4967f43
are -> is
danhoeflinger Apr 9, 2025
583fd52
language adjustment for base characteristic;
danhoeflinger Apr 9, 2025
b8a6018
rename to "indirectly device accessible iterators"
danhoeflinger Apr 9, 2025
311f2cb
formatting
danhoeflinger Apr 9, 2025
8676878
Adding a section for other iterators
danhoeflinger Apr 10, 2025
ef67c3d
remove unnecessary implementation details
danhoeflinger Apr 11, 2025
0f64be0
trait<buffer wrapper> = true
danhoeflinger Apr 11, 2025
780f501
restricting permutation_iterator SourceIterator to indirectly device …
danhoeflinger Apr 11, 2025
a57c7d7
formatting
danhoeflinger Apr 11, 2025
6114c2c
language improvements
danhoeflinger Apr 16, 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
Original file line number Diff line number Diff line change
Expand Up @@ -99,9 +99,9 @@ to run algorithms on a SYCL device. When an algorithm runs with ``device_policy`
it is capable of processing SYCL buffers (passed via ``oneapi::dpl::begin/end``),
data in the host memory and data in Unified Shared Memory (USM), including device USM.
Data placed in the host memory and USM can be passed to oneDPL algorithms
as pointers and random access iterators. The way to transfer data from the host memory
to a device and back is unspecified; per-element data movement to/from a temporary storage
is a possible valid implementation.
as pointers and random access iterators, including :doc:`oneDPL iterators <iterators>`. The way to transfer data from
the host memory to a device and back is unspecified; per-element data movement to/from a temporary storage is a possible
valid implementation.

The ``KernelName`` template parameter, also aliased as ``kernel_name`` within the class template,
is to explicitly provide a name for SYCL kernels executed by an algorithm the policy is passed to.
Expand Down
140 changes: 140 additions & 0 deletions source/elements/oneDPL/source/parallel_api/iterators.rst
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,31 @@
..
.. SPDX-License-Identifier: CC-BY-4.0

.. _iterators:

Iterators
---------

Requirements For Iterator Use With Device Policies
++++++++++++++++++++++++++++++++++++++++++++++++++
Iterator are, by default, not assumed to refer to content which is accessible on the device which requires the content
to be copied to the device prior to being used inside a `SYCL`_ kernel. We define iterators to be "device accessible
content iterators" when they can inherently be dereferenced on the device in a SYCL kernel. When using oneDPL algorithms
with a ``device_policy``, "device accessible content iterators" avoid unnecessary data movement when provided as input
or output arguments.

Examples of "device accessible content iterators" include SYCL USM shared or device memory, or iterator types like
``counting_iterator`` or ``discard_iterator`` that do not require any data to be copied to the device to be used in a
SYCL kernel. An example of an iterator which does not refer to "device accessible content" is a ``std::vector``
iterator, which requires the data to be copied to the device in some way prior to usage in a SYCL kernel within
algorithms used with a ``device_policy``.

When used as input or output with algorithms using a ``device_policy`` "device accessible content iterators" must also
be SYCL device-copyable, as defined by the SYCL Specification.

oneDPL Iterators
++++++++++++++++

The oneDPL iterators are defined in the ``<oneapi/dpl/iterator>`` header,
in ``namespace oneapi::dpl``.

Expand Down Expand Up @@ -64,6 +86,8 @@ counter; dereference operations cannot be used to modify the counter. The arithm
operators of ``counting_iterator`` behave as if applied to the values of Integral type
representing the counters of the iterator instances passed to the operators.

``counting_iterator`` are always SYCL device-copyable, and are always "device accessible content iterators".

.. code:: cpp

class discard_iterator
Expand Down Expand Up @@ -104,6 +128,8 @@ lvalue that may be assigned an arbitrary value. The assignment has no effect on
of ``discard_iterator`` behave as if applied to integer counter values maintained by the
iterator instances to determine their position relative to each other.

``discard_iterator`` are always SYCL device-copyable, and are always "device accessible content iterators".

.. code:: cpp

template <typename SourceIterator, typename IndexMap>
Expand Down Expand Up @@ -174,6 +200,10 @@ to index into the index map. The corresponding value in the map is then used
to index into the value set defined by the source iterator. The resulting lvalue is returned
as the result of the operator.

``permutation_iterator`` are SYCL device-copyable if both the ``SourceIterator`` and the ``IndexMap``
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
``permutation_iterator`` are SYCL device-copyable if both the ``SourceIterator`` and the ``IndexMap``
``permutation_iterator`` is SYCL device-copyable if both the ``SourceIterator`` and the ``IndexMap``

Copy link
Contributor Author

Choose a reason for hiding this comment

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

adjusted all of these "are" -> "is" [a]

are SYCL device-copyable, and are always "device accessible content iterators" if both the ``SourceIterator``
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
are SYCL device-copyable, and are always "device accessible content iterators" if both the ``SourceIterator``
are SYCL device-copyable, and are "device accessible content iterators" if both the ``SourceIterator``

"Always" word seems redundant to me. The same comment is applicable for the iterators below.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

removed all instances of this.

and the ``IndexMap`` are "device accessible content iterators".

.. code:: cpp

template <typename SourceIterator, typename IndexMap>
Expand Down Expand Up @@ -234,6 +264,10 @@ arithmetic and comparison operators of ``transform_iterator`` behave as if appli
source iterator itself. The template type ``Iterator`` must satisfy
``AdaptingIteratorSource``.

``transform_iterator`` is SYCL device-copyable if the source iterator is SYCL device-copyable, and
is always "device accessible content iterator" if the source iterator is a "device accessible
content iterator".

.. code:: cpp

template <typename UnaryFunc, typename Iterator>
Expand Down Expand Up @@ -293,6 +327,10 @@ source iterators over which the ``zip_iterator`` is defined. The arithmetic oper
operation were applied to each of these iterators. The types ``T`` within the template pack
``Iterators...`` must satisfy ``AdaptingIteratorSource``.

``zip_iterator`` is SYCL device-copyable if all the source iterators are SYCL device-copyable, and
is always "device accessible content iterator" if all the source iterators are "device accessible
content iterators".

.. code:: cpp

template <typename... Iterators>
Expand All @@ -301,3 +339,105 @@ operation were applied to each of these iterators. The types ``T`` within the te

``make_zip_iterator`` constructs and returns an instance of ``zip_iterator``
using the set of source iterators provided.

.. _iterators-device-accessible:

Customization For User Defined Iterators
++++++++++++++++++++++++++++++++++++++++

oneDPL provides a mechanism to indicate whether custom iterators are "device accessible content iterators" by defining
an Argument-Dependent Lookup (ADL) customization point, ``is_onedpl_device_accessible_content_iterator``. oneDPL also
defines a public trait, ``is_device_accessible_content_iterator[_v]`` to indicate whether an iterator is a "device
accessible content iterator".

oneDPL queries this information at compile time to determine how to handle iterator types when they are passed to
algorithms with a ``device_policy`` to avoid unnecessary data movement.

ADL Customization Point: ``is_onedpl_device_accessible_content_iterator``
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

A free function ``is_onedpl_device_accessible_content_iterator(IteratorT)`` may be defined, which accepts an argument
of type ``IteratorT`` and returns a type with the characteristics of ``std::true_type`` if ``IteratorT`` refers to
Copy link
Contributor

Choose a reason for hiding this comment

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

I think it's not true for a generic case. As I mentioned, I believe std::conjunction is an important use case. However, std::conjunction might not have std::true_type or std::false_type characteristics at all.

I believe that this sentence about characteristics has a perfect fit to where you describe is_device_accessible_content_iterator trait itself instead of the current wording, which is "... evaluates to a type...". I would just say "...has characteristics of std::true_type ..."

Here we need something else. Don't have immediate suggestions, though

Copy link
Contributor

Choose a reason for hiding this comment

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

However, std::conjunction might not have std::true_type or std::false_type characteristics at all.

If it is used with true_type/false_type or derivatives, it will:

The specialization std::conjunction<B1, ..., BN> has a public and unambiguous base that is

  • if sizeof...(B) == 0, std::true_type;
  • otherwise the first type Bi in B1, ..., BN for which bool(Bi::value) == false, or BN if there is no such type.

And that's exactly our goal I believe.

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 suppose I don't know exactly what we mean by "characteristics of std::true_type" if a std::conjunction or similar types will not qualify.

Copy link
Contributor

Choose a reason for hiding this comment

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

conjunction, disjunction and possibly negation (though, I am not sure) are the only exceptions I am aware of. All other traits have characteristics of either true_type or false_type because they inherit from bool_constant

Copy link
Contributor

@akukanov akukanov Apr 9, 2025

Choose a reason for hiding this comment

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

I suppose I don't know exactly what we mean by "characteristics of std::true_type" if a std::conjunction or similar types will not qualify.

The wording "with a base characteristic of" is used in a few places in the C++ standard, for example in https://eel.is/c++draft/meta.unary:

Subclause [meta.unary] contains templates that may be used to query the properties of a type at compile time.
Each of these templates shall be a Cpp17UnaryTypeTrait with a base characteristic of true_type if the corresponding condition is true, otherwise false_type.

My understanding of this wording is: even though it is not a true_type or false_type, it has all the same characteristics as these types and can be used as such. Practically it means that the trait class inherits either true_type or false_type (maybe indirectly).

In my comment above, I say that std::conjunction also falls into this category if the chosen template parameter does. Though the standard does not prohibit to use other types with std::conjunction, typical arguments it is used with will be traits etc. classes that inherit bool_constant. Therefore requiring that the return type of our ADL customization function should have these characteristics does not preclude the use of std::conjunction, but limits what types can be passed to it.

Copy link
Contributor

Choose a reason for hiding this comment

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

My understanding of this wording is: even though it is not a true_type or false_type, it has all the same characteristics as these types and can be used as such.

Actually, it's even more strict - the standard requires public inheritance, see https://eel.is/c++draft/meta.rqmts#:Cpp17UnaryTypeTrait:

It shall be ... publicly and unambiguously derived, directly or indirectly, from its base characteristic ... The member names of the base characteristic shall not be hidden and shall be unambiguously available ....

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Is see, thanks for the clarification. I think I agree that our current formulation makes sense then, with a small tweak to the language perhaps to follow the example language you provided.

Unless we have a compelling example to allow conjunction disjunction negation which use something other than types with the base characteristic of a bool_constant, I think simplicity is better.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

To follow up here, I've adjusted the language slightly, but think this simple version is best without making it more broad. I've not been able to come up with any real compelling case for conjunction or disjunction of an integral_constant which is not a bool_constant worth making this language more complex.

However, I think a valid implementation could easily accommodate arbitrary usage of conjunction and disjunction, ensuring that the public trait is always a bool_constant.

"device accessible content", or alternatively returns a type with the characteristics of ``std::false_type``
otherwise. The function must be defined in one of the valid search locations for ADL lookup, which includes the
namespace of the definition of the iterator type ``IteratorT``.
Copy link
Contributor

Choose a reason for hiding this comment

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

This sentence sounds a bit awkward to me. ADL last letter already means lookup. Also, "valid search locations" is a sort of unusual. How about this:

Suggested change
otherwise. The function must be defined in one of the valid search locations for ADL lookup, which includes the
namespace of the definition of the iterator type ``IteratorT``.
otherwise. The function must be discoverable by ADL, which includes the
namespace of the definition of the iterator type ``IteratorT``.

You may leave this part, if you think it's useful: "... , which includes the
namespace of the definition of the iterator type IteratorT" but it also might be removed, I think. They choice is yours.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Taken, I removed the second part. It doesn't really make sense after the edit. Its more something for the documentation than the spec.


The function ``is_onedpl_device_accessible_content_iterator`` may be used by oneDPL to determine if the iterator refers
to "device accessible content" by interrogating its return type at compile-time only. It shall not be called by oneDPL
outside a ``decltype`` context to determine the return type. Overloads may be provided as forward declarations only,
Copy link
Contributor

Choose a reason for hiding this comment

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

Not sure if it's important. I would probably remove it.

Suggested change
to "device accessible content" by interrogating its return type at compile-time only. It shall not be called by oneDPL
outside a ``decltype`` context to determine the return type. Overloads may be provided as forward declarations only,
to "device accessible content" by interrogating its return type at compile-time only. Overloads may be provided as forward declarations only,

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Well, it justifies the technical reason that "Overloads may be provided as forward declarations only,...", but perhaps its just enough to say that. I've removed it.

Copy link
Contributor

Choose a reason for hiding this comment

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

Yes, but the thing about decltype is that it makes allowed use-cases broader. So, users are allowed to provide both declarations and constexpr definitions. If it would narrow the use-cases It would have made a perfect sense to me.

With the current design I am glad that you decided to remove it.

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 don't think I understand this most recent comment. As it stands, users are able to provide either declarations or full definitions. As we discussed offline previously, return type must be knowable at compile-time regardless of what is marked as constexpr or not, so any implementation for which that isn't the case will have errors anyway.

I'm not understanding why knowing the decltype thing actually changes anything other than "Overloads may be provided as forward declarations only...", other than giving some insight into why that is the case technically.

Can you help me understand with an example of something allowed for a user in with this decltype knowledge which isn't allowed with the current language?

without a body defined. ADL lookup is used to determine which function overload to use according to the rules in the
`C++ Standard`_. Therefore, derived iterator types without an overload for their exact type will match their most
specific base iterator type if such an overload exists.

The default implementation of ``is_onedpl_device_accessible_content_iterator`` marks the following iterators as to
"device accessible content iterators":
* Pointers (to handle USM pointers)
* Iterators to USM shared allocated ``std::vector``-s when the allocator type is knowable from the iterator type
Copy link
Contributor

Choose a reason for hiding this comment

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

Is it ever a case?

Copy link
Contributor Author

@danhoeflinger danhoeflinger Apr 8, 2025

Choose a reason for hiding this comment

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

In practice, with a good amount of certainty with some compilers, yes. However, its a good question if its worth mentioning in the specification, since it is not always knowable (and not 100% knowable I believe).
uxlfoundation/oneDPL#1438 (comment)
uxlfoundation/oneDPL#1438 (comment)

How it looks in the oneDPL's implementation currently:
https://github.com/uxlfoundation/oneDPL/blob/1625f6a2dcc981a537c65fdc3b40951cb63b7326/include/oneapi/dpl/pstl/hetero/dpcpp/sycl_iterator.h#L154-L179

Copy link
Contributor

Choose a reason for hiding this comment

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

Think of that as of specification, not as of documentation.
If we want to require that std::vector with USM allocators is used without excessive copying, then it is a part of the spec. Otherwise, it can be mentioned as an implementation-specific or completely omitted.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

In practice, oneDPL's implementation is relying upon standard library implementation details which are not part of the C++ standard library specification of std::vector::iterator to enable handling of usm allocator vector iterators when it can confidently guess that the allocator type is knowable from the iterator type. From purely the C++ standard specification, I don't think it is possible to definitively know anything about the allocator from the type of a std::vector::iterator.

In practice, it would be quite unlikely that a standard library implementation could / would have a std::vector::iterator type which "tricks" the current oneDPL implementation's detection of a knowable "USM shared allocator", but I do think you could technically contrive such an implementation if you really wanted to.

From a specification perspective, it doesn't belong, I think.

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've removed the line for now, but I'm open to discussion of course.

Copy link
Contributor

@rarutyun rarutyun Apr 9, 2025

Choose a reason for hiding this comment

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

Yeah, if we speak about Allocator we should write an iterator type slightly differently: std::vector<T, Allocator>::iterator.

As you can see Allocator is a part of specialized vector type, not an iterator type. I don't know a way in C++ do deduce a outer type by inner type: in this concrete example: deduce vector<T, Allocator> type from nested iterator type

Copy link
Contributor Author

Choose a reason for hiding this comment

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

If we re-introduce something like this, we can describe it with std::vector<T,Allocator>::iterator, but I think its not a requirement that makes a lot of sense in the specification, because I don't can't be achieved for a generic implementation of the standard library with 100% certainty.

* ``std::reverse_iterator<IteratorT>`` when ``IteratorT`` refers to "device accessible content"



Public Trait: ``oneapi::dpl::is_device_accessible_content_iterator[_v]``
Copy link
Contributor

Choose a reason for hiding this comment

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

naming suggestions to consider

  • is_passed_directly_to_device (perhaps obsolete)
  • is_device_accessible_iterator
  • is_indirectly_device_accessible

For the last one it might be is_device_indirectly_accessible; not sure what is more accurate from English perspective. But the last proposed option (by me) is my favorite, so far. It also corelates with several C++20 concepts, e.g., indirectly_writable

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 don't really like is_device_accessible_iterator because I think there is a difference between the iterator being accessible and its content it refers to being accessible. However, is_indirectly_device_accessible is more clear and concise than what is currently in the PR.

Copy link
Contributor

@akukanov akukanov Apr 8, 2025

Choose a reason for hiding this comment

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

I am fine with is_indirectly_device_accessible, but we need to get feedback from the customers (that applies to any name(s) we eventually propose).

Copy link
Contributor

Choose a reason for hiding this comment

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

I was also thinking that is_device_accessible_iterator partially reflects the purpose. On the other hand we pass iterators to eventually get data from it, right? :)

That's how the idea about is_indirectly_device_accessible comes to my mind. Since three of us like it we should probably reach customers to get feedback (as Alexey suggested). Whether we should change "device accessible content iterators", I am not sure. Perhaps, we should (if we agree on the new name) but I don't have a very strong preference

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 adjusted the name here in the spec.

^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

The public trait ``oneapi::dpl::is_device_accessible_content_iterator[_v]`` can be used to query whether an iterator
is a "device accessible content iterator". The trait is defined in ``<oneapi/dpl/iterator>``.

``oneapi::dpl::is_device_accessible_content_iterator<T>`` evaluates to a type with the characteristics of
Copy link
Contributor

Choose a reason for hiding this comment

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

Still don't like this style of writing after a template name because it confuses (probably experienced) C++ users. Is the any chance we will define the API properly?

Something like

The following class template and variable template are defined in namespace oneapi::dpl:

template <typename T>
struct is_device_accessible_content_iterator;

template <typename T>
inline constexpr bool is_device_accessible_content_iterator = is_device_accessible<T>::value;

and then the descriptive part.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Adjusted to be close to your proposal.

``std::true_type`` if ``T`` refers to "device accessible content", otherwise it evaluates to a type with the
characteristics of ``std::false_type``.

``oneapi::dpl::is_device_accessible_content_iterator<T>`` is a ``constexpr bool`` that evaluates to ``true`` if ``T``
refers to "device accessible content", otherwise it evaluates to ``false``.




Example
^^^^^^^

The following example shows how to define a ADL overload for a simple user defined iterator. It also shows an example
for a more complicated case which uses a hidden friend to provide the ADL overload within the definition of the
iterator.

.. code:: cpp

namespace usr
{
struct accessible_it
{
/* unspecified user definition of a iterator which refers to "device accessible content" */
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
/* unspecified user definition of a iterator which refers to "device accessible content" */
/* unspecified user definition of a iterator which refers to "device accessible content" */

Copy link
Contributor Author

Choose a reason for hiding this comment

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

fixed.

};

std::true_type
is_onedpl_device_accessible_content_iterator(accessible_it);

struct inaccessible_it
{
/* unspecified user definition of iterator which doesn't refer to "device accessible content" */
};

// This could be omitted. It would rely upon the default implementation with the same result
std::false_type
is_onedpl_device_accessible_content_iterator(inaccessible_it);
}

static_assert(oneapi::dpl::is_device_accessible_content_iterator<usr::accessible_it> == true);
static_assert(oneapi::dpl::is_device_accessible_content_iterator<usr::inaccessible_it> == false);

// Example with base iterators and ADL overload as a hidden friend
template <typename It1, typename It2>
struct it_pair
{
It1 first;
It2 second;
friend auto is_onedpl_device_accessible_content_iterator(it_pair) ->
std::conjunction<oneapi::dpl::is_device_accessible_content_iterator<It1>,
oneapi::dpl::is_device_accessible_content_iterator<It2>>;
};

static_assert(oneapi::dpl::is_device_accessible_content_iterator<it_pair<usr::accessible_it, usr::accessible_it>> == true);
static_assert(oneapi::dpl::is_device_accessible_content_iterator<it_pair<usr::accessible_it, usr::inaccessible_it>> == false);

.. _`C++ Standard`: https://isocpp.org/std/the-standard
.. _`SYCL`: https://registry.khronos.org/SYCL/specs/sycl-2020/html/sycl-2020.html