Skip to content

Conversation

@akukanov
Copy link
Contributor

@akukanov akukanov commented Sep 22, 2025

This patch attempts to clarify how named requirements are defined, especially for dealing with various value categories a function can be applied to.

Using these clarifications, the definition of ParallelForEachBody is changed to be simpler, while the semantics should remain the same (unless I missed some nuances).

The updated pseudo-signature rules are also used to add rvalue support to the named requirements used with parallel_reduce.

Co-authored-by: Ruslan Arutyunyan <[email protected]>
Copy link
Contributor

@kboyarinov kboyarinov left a comment

Choose a reason for hiding this comment

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

The part describing the pseudo-signature looks good.
Need a bit more time to review the correctness of parallel_for_each semantics

@akukanov akukanov force-pushed the Improve-named-requirements branch from 61de833 to c47c112 Compare October 17, 2025 10:53
Copy link
Contributor

@kboyarinov kboyarinov left a comment

Choose a reason for hiding this comment

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

LGTM

@akukanov akukanov force-pushed the Improve-named-requirements branch from d262146 to dfa3e02 Compare October 29, 2025 13:49
It should also meet one of the following requirements:
A type `Body` satisfies `ParallelForEachBody` if it meets the `Function Objects`
requirements described in the [function.objects] section of the ISO C++ standard,
as well as it meets exactly one of the following two requirements for ``operator()``:
Copy link
Contributor

Choose a reason for hiding this comment

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

Would like to check my understanding: If I remember correctly, the necessity to satisfy exactly one requirement stems from the fact that it becomes ambiguous to the library implementation which one to call.

I wonder why the library cannot prefer one of the signatures if both exist? For example, is it possible to make it to prefer the one with the feeder?
I can imagine a use case with type hierarchy where instances of the base do not need to add additional work, while the derived classes may include more sophisticated processing. As far as I understand, the design where only one type of the signature can be present makes such inheritance impossible.

Also, perhaps, adding the word "alternative" helps in comprehension:

Suggested change
as well as it meets exactly one of the following two requirements for ``operator()``:
as well as it meets exactly one of the following two alternative requirements for ``operator()``:

Copy link
Contributor Author

@akukanov akukanov Oct 31, 2025

Choose a reason for hiding this comment

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

Yes, adding the word "alternative" makes sense.

We can relax the requirements later if necessary, but we cannot make it stronger later without breaking compatibility.

For example, is it possible to make it to prefer the one with the feeder?

If the operator with a feeder is always preferred (which is the only reasonable choice really), why the one without the feeder is ever there? Also, the body can always ignore the feeder if it does not need to use it.

a use case with type hierarchy where instances of the base do not need to add additional work, while the derived classes may include more sophisticated processing. As far as I understand, the design where only one type of the signature can be present makes such inheritance impossible.

I do not get how a type hierarchy is relevant. The dereferenced iterator gives a very specific type. It can be a pointer-to-base, of course, but then the decision of using or not using the feeder is done at runtime, not at compile time. At compile time, the parameter of the operator should be either the base class or the derived class, but not both. If the processing is different, two different body types for parallel_for_each seemingly make more sense.

Copy link
Contributor

Choose a reason for hiding this comment

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

why the one without the feeder is ever there?

I tried to come up with the example, where "the one without feeder" part cannot be easily changed, but whose implementation is desired to be reused.

but then the decision of using or not using the feeder is done at runtime, not at compile time.

I guess, unless the invocation routine is a template itself. Consider this code snippet: https://godbolt.org/z/qbx8eYMcP.

Not sure how realistic such example might be as the Derived::operator() could accept Derived& type just as well.

Copy link

@vossmjp vossmjp left a comment

Choose a reason for hiding this comment

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

LGTM

@akukanov akukanov merged commit 1304c58 into uxlfoundation:main Oct 31, 2025
3 checks passed
@akukanov akukanov deleted the Improve-named-requirements branch October 31, 2025 19:13
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants