Open lewissbaker opened 2 years ago
I have a prototype of a simple, non-intrusive way to add receiver-guided sender algorithm customization. It involves adding a connector
receiver query. A connector is a binary callable that can be used to connect a sender to a receiver. A call to connect(s, r)
is evaluated as follows:
connector(r)(s, r)
is well-formed, that is what is used.tag_invoke(connect, s, r)
is well-formed, that is what is used.connect(s, r)
is ill-formed.This is prototyped here, together with a pattern-matching facility to make discovering the deep structure of a sender tree simple.
EDIT: The pattern matching stuff (matches[_v]
, etc) is not a fundamental part of customization mechanism and can safely be ignored when evaluating the efficacy of customizing connect
like this.
@lewissbaker I would really appreciate your thoughts about how compatible this approach is with your language-based CPO mechanism. I don't want P2300 adopting a design for the connect
CPO that makes migration to a language-based mechanism difficult.
The current implementation of scheduler affinity for the coroutine
task
type relies on special behaviour of being able to identify when the coroutine awaits a sender returned byschedule(some_scheduler)
so that it can treat that as a change of the current scheduler to some other scheduler.The current solution complicates the implementation of
schedule()
a lot and makes it difficult to extend to other kinds of expressions as well.One option could be to turn invocation of sender algorithms into generic algorithms that just curry their arguments into a
sender<CPO, Args...>
type which then customisesconnect()
to then pass the arguments through toconnect(receiver, CPO, args...)
.As all senders would then be effectively just an argument currying mechanism we could customise
await_transform()
when the awaited object is of typesender<tag_t<schedule>, SomeScheduler>
to handle this.This would also allow simpler customisation of entire sender expressions when, e.g. the expression is passed to
on(sched, some_sender_expression)
. The sender expression can just be an expression-template of a well-known structure/type that the scheduler can use to customiseon()
. See below for an example of how this might work.Implementation Sketch
It might also help if you think of
connect()
instead asasync_invoke()
.For example: We can define a generic
sender
type, parameterised on a CPO which is just responsible for currying arguments.Then we can define CPOs to have a default implementation of async_invoke() that has the default implementation. We can use a helper here to allow invocation of the CPO to return the sender.
Then a customisation of the algorithm could be implemented as follows:
This doesn't yet handle things like sender-queries or sender-traits which also need to be considered, however.
Sketch of Scheduler Customisation