|
Boost : |
From: William E. Kempf (wekempf_at_[hidden])
Date: 2003-02-07 13:51:42
David Abrahams said:
>>> ...and if it can't be default-constructed?
>>
>> That's what boost::optional<> is for ;).
>
> Yeeeh. Once the async_call returns, you have a value, and should be able
> to count on it. You shouldn't get back an object whose invariant allows
> there to be no value.
I'm not sure I can interpret the "yeeeh" part. Do you think there's still
an issue to discuss here?
>>>> Second, and this is more important, you've bound this concept to
>>>> boost::thread explicitly. With the fully seperated concerns of my
>>>> proposal, async_result can be used with other asynchronous call
>>>> mechanisms, such as the coming boost::thread_pool.
>>>>
>>>> asyc_result<double> res1, res2;
>
> no fair - I'm calling it async_call now ;-)
>
>>>> thread_pool pool;
>>>> pool.dispatch(bind(res1.call(foo), a, b, c));
>>>> pool.dispatch(bind(res2.call(foo), d, e, f));
>>>> d = res1.value() + res2.value();
>>>
>>> This one is important. However, there are other ways to deal with
>>> this.
>>> An async_call object could take an optional thread-creation
>>> parameter,
>>> for example.
>>
>> It's not "thread-creation" in this case. You don't create threads
>> when you use a thread_pool.
>
> OK, "thread acquisition", then.
No, not even that. An RPC mechanism, for instance, isn't acquiring a
thread. And a message queue implementation wouldn't be acquiring a thread
either. These are the two obvious (to me) alternatives, but the idea is
to leave the call/execute portion orthogonal and open. Alexander was
quite right that this is similar to the "Future" concept in his Java link.
The "Future" holds the storage for the data to be returned and provides
the binding mechanism for what actually gets called, while the "Executor"
does the actual invocation. I've modeled the "Future" to use function
objects for the binding, so the "Executor" can be any mechanism which can
invoke a function object. This makes thread, thread_pool and other such
classes "Executors".
>> And there's other examples as well, such as RPC mechanisms.
>
> True.
>
>> And personally, I find passing such a "creation parameter" to be
>> turning the design inside out.
>
> A bit, yes.
>
>> It might make things a little simpler for the default case, but it
>> complicates usage for all the other cases. With the design I
>> presented every usage is treated the same.
>
> There's a lot to be said for making "the default case" very easy.
Only if you have a clearly defined "default case". Someone doing a lot of
client/server development might argue with you about thread creation being
a better default than RPC calling, or even thread_pool usage.
>> More importantly, if you really don't like the syntax of my design, it
>> at least allows you to *trivially* implement your design.
>
> I doubt most users regard anything involving typesafe varargs as
> "trivial to implement."
Well, I'm not claiming to support variadric parameters here. I'm only
talking about supporting a 0..N for some fixed N interface. And with
Boost.Bind already available, that makes other such interfaces "trivial to
implement". At least usually. The suggestion that the binding occur at
the time of construction is going to complicate things for me, because it
makes it much more difficult to handle the reference semantics required
here.
>> Sometimes there's something to be said for being "lower level".
>
> Sometimes. I think users have complained all along that the
> Boost.Threads library takes the "you can implement it yourself using our
> primitives" line way too much. It's important to supply
> simplifying high-level abstractions, especially in a domain as
> complicated as threading.
OK, I actually believe this is a valid criticism. But I also think it's
wrong to start at the top of the design and work backwards. In other
words, I expect that we'll take the lower level stuff I'm building now and
use them as the building blocks for the higher level constructs later. If
I'd started with the higher level stuff, there'd be things that you
couldn't accomplish.
>>>> > That's what we mean by the terms "high-level" and "encapsulation"
>>>> ;-)
>>>>
>>>> Yes, but encapsulation shouldn't hide the implementation to the
>>>> point that users aren't aware of what the operations actually are.
>>>> ;)
>>>
>>> I don't think I agree with you, if you mean that the implementation
>>> should be apparent from looking at the usage. Implementation details
>>> that must be revealed should be shown in the documentation.
>>
>> I was referring to the fact that you have no idea if the "async call"
>> is being done via a thread, a thread_pool, an RPC mechanism, a simple
>> message queue, etc. Sometimes you don't care, but often you do.
>
> And for those cases you have a low-level interface, right?
Where's the low level interface if I don't provide it? ;)
-- William E. Kempf wekempf_at_[hidden]
Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk