-
Notifications
You must be signed in to change notification settings - Fork 116
[oneTBB] Improve named requirements to better deal with value categories #647
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
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -10,9 +10,9 @@ Named Requirements | |
This section describes named requirements used in the oneTBB Specification. | ||
|
||
A *named requirement* is a set of requirements on a type. The requirements may be syntactic or semantic. | ||
The *named_requirement* term is similar to “Requirements on types and expressions” term which is defined | ||
by the ISO C++ Standard (chapter “Library Introduction”) or `“Named Requirements” section <https://en.cppreference.com/w/cpp/named_req>`_ | ||
on the cppreference.com site. | ||
The *named requirement* term is similar to “Requirements on types and expressions” term which is defined | ||
by the ISO C++ Standard (chapter “Library Introduction”) or | ||
`“Named Requirements” section <https://en.cppreference.com/w/cpp/named_req>`_ on the cppreference.com site. | ||
|
||
For example, the named requirement of *sortable* could be defined as a set of requirements that enable | ||
an array to be sorted. A type ``T`` would be *sortable* if: | ||
|
@@ -22,9 +22,14 @@ an array to be sorted. A type ``T`` would be *sortable* if: | |
|
||
You can write a sorting template function in C++ that sorts an array of any type that is *sortable*. | ||
|
||
.. _pseudo_signatures: | ||
|
||
Pseudo-Signatures | ||
----------------- | ||
|
||
Two approaches for defining named requirements are *valid expressions* and *pseudo-signatures*. | ||
The ISO C++ standard follows the valid *expressions* approach, which shows what the usage pattern looks like for a requirement. | ||
It has the drawback of relegating important details to notational conventions. This document uses | ||
It has the drawback of relegating important details to notational conventions. This document mostly uses | ||
pseudo-signatures because they are concise and can be cut-and-pasted for an initial implementation. | ||
|
||
For example, the table below shows pseudo-signatures for a *sortable* type ``T``: | ||
|
@@ -43,12 +48,19 @@ For example, the table below shows pseudo-signatures for a *sortable* type ``T`` | |
|
||
--------------------------------------------------------------------------------------------- | ||
|
||
A real signature may differ from the pseudo-signature that it implements in ways where implicit | ||
conversions would deal with the difference. For an example type ``U``, the real signature that | ||
implements ``operator<`` in the table above can be expressed as ``int operator<( U x, U y )``, | ||
because C++ permits implicit conversion from ``int`` to ``bool``, and implicit conversion from ``U`` | ||
to (``const U&``). Similarly, the real signature ``bool operator<( U& x, U& y )`` is acceptable | ||
because C++ permits implicit addition of a const qualifier to a reference type. | ||
A pseudo-signature describes how an implementation interacts with a type or a function. | ||
A real signature (after template instantiation, if applicable) may differ from the pseudo-signature | ||
that it implements in ways where implicit | ||
conversions would deal with the difference: its function parameter types need to implicitly convert | ||
from the ones in the pseudo-signature, and the return value type needs to implicitly convert to the one | ||
in the pseudo-signature. | ||
|
||
For an example type ``U``, the real signature that implements ``operator<`` in the table above | ||
can be expressed as ``int operator<( U x, U y )``, because C++ permits implicit conversion from | ||
``int`` to ``bool``, and implicit conversion from ``const U&`` to ``U`` if the type is copy-constructible. | ||
For a counter-example, the real signature ``bool operator<( U& x, U& y )`` is not acceptable | ||
because C++ does not permit implicit removal of a ``const`` qualifier from a type, and so the code | ||
would not compile if the implementation attempts to pass a const object to the function. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. May be it makes sense to also explicitly mention that if pseudo-signature declares return type as |
||
|
||
Algorithms | ||
---------- | ||
|
@@ -81,7 +93,6 @@ Mutexes | |
|
||
Containers | ||
---------- | ||
|
||
.. toctree:: | ||
:titlesonly: | ||
|
||
|
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -15,46 +15,28 @@ It should also meet one of the following requirements: | |||||
|
||||||
**ParallelForEachBody Requirements: Pseudo-Signature, Semantics** | ||||||
|
||||||
.. cpp:function:: Body::operator()( ItemType item ) const | ||||||
.. cpp:function:: void Body::operator()( ReferenceType item ) const | ||||||
|
||||||
Process the received item. | ||||||
|
||||||
.. cpp:function:: Body::operator()( ItemType item, oneapi::tbb::feeder<ItemType>& feeder ) const | ||||||
.. cpp:function:: void Body::operator()( ItemType&& item, oneapi::tbb::feeder<ItemType>& feeder ) const | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should it be
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This question is related to the question below about "removing" a requirement:
As I understand it, it says that if the feeder is used, the body has to accept rvalues - because the work elements supplied through the feeder will be "moved" to the body for processing. Since If the idea is that the original sequence elements are passed just as the result of iterator dereferencing (i.e., Another option is to use a templated operator with universal references or use different overloads for lvalues and rvalues. Let's figure out what semantics we want, how it could be implemented in practice, and then hopefully it will be more clear how to describe that in the named requirement. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, the original idea was that the elements of the input sequence are passed as the result of dereferencing. and additional items are always provided as rvalues. The semantics proposed would break the first part, for example if the iterator have non-const lvalue I disagree that struct body {
// overload for feeder items
void operator()(ItemType&& value_from_feeder) const {}
// overload for input sequence
void operator()(ItemType& input_sequence, oneapi::tbb::feeder<ItemType>& feeder) const {}
}; There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This document specify that the body should meet one of the requirements - either be invocable without a feeder or with it. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, I think it should be specified. I do not see how the implementation could use both overloads; it would need to decide whether to provide the feeder or not, and how would it decide? So using the overload with the feeder all the time is the "safest" choice. |
||||||
|
||||||
Process the received item. May invoke the ``feeder.add(T)`` function to spawn additional items. | ||||||
Process the received item. May invoke the ``feeder.add`` function to spawn additional items. | ||||||
|
||||||
----------------------------------------------------------------- | ||||||
|
||||||
.. note:: | ||||||
|
||||||
``ItemType`` may be optionally passed to ``Body::operator()`` by reference. | ||||||
``const`` and ``volatile`` type qualifiers are also applicable. | ||||||
|
||||||
Terms | ||||||
----- | ||||||
|
||||||
* ``iterator`` determines the type of the iterator passed into the ``parallel_for_each`` algorithm, | ||||||
which is ``decltype(std::begin(c))`` for the overloads that accept the ``Container`` template argument or ``InputIterator``. | ||||||
* ``value_type`` - the type ``std::iterator_traits<iterator>::value_type``. | ||||||
* ``reference`` - the type ``std::iterator_traits<iterator>::reference``. | ||||||
where ``ItemType`` is ``std::iterator_traits<InputIterator>::value_type`` for the type of the iterator | ||||||
the ``parallel_for_each`` algorithm operates with, and ``ReferenceType`` is | ||||||
|
||||||
``oneapi::tbb::parallel_for_each`` requires the ``Body::operator()`` call with an object of the ``reference`` type to be well-formed if | ||||||
the ``iterator`` meets the `Forward iterator` requirements described in the [forward.iterators] section of the | ||||||
ISO C++ Standard. | ||||||
* ``std::iterator_traits<InputIterator>::reference`` if the iterator type is a forward iterator | ||||||
as described in the [forward.iterators] section of the ISO C++ Standard; | ||||||
* otherwise, ``ItemType&&``. | ||||||
|
||||||
`oneapi::tbb::parallel_for_each algorithm <../../algorithms/functions/parallel_for_each_func>`_ | ||||||
requires the ``Body::operator()`` call with an object of type ``const value_type&`` or ``value_type&&`` to be well-formed if following requirements are met: | ||||||
|
||||||
* the iterator meets the `Input iterator` requirements described in the [input.iterators] section of the ISO C++ Standard | ||||||
* the iterator does not meet the `Forward iterator` requirements described in the [forward.iterators] section of the ISO C++ Standard | ||||||
|
||||||
.. caution:: | ||||||
|
||||||
If the ``Body`` only takes non-const lvalue reference to the ``value_type``, the requirements described above | ||||||
are violated, and the program can be ill-formed. | ||||||
.. note:: | ||||||
|
||||||
Additional elements submitted into ``oneapi::tbb::parallel_for_each`` through the ``feeder::add`` are passed to the ``Body`` as rvalues. In this case, the corresponding | ||||||
execution of the ``Body`` is required to be well-formed. | ||||||
Comment on lines
-56
to
-57
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is it intentional that this requirement is removed? |
||||||
The usual rules for :ref:`pseudo-signatures <pseudo_signatures>` apply. | ||||||
Therefore, ``Body::operator()`` may optionally take ``ItemType`` by value. | ||||||
``const`` and ``volatile`` type qualifiers are also applicable. | ||||||
|
||||||
See also: | ||||||
|
||||||
|
Uh oh!
There was an error while loading. Please reload this page.