> 
> From: "Peter Dimov" <[EMAIL PROTECTED]>
> Date: 2003/02/10 Mon AM 09:08:08 EST
> To: "Boost mailing list" <[EMAIL PROTECTED]>
> Subject: Re: [boost] Re: A new boost::thread implementation?
> 
> David Abrahams wrote:
> > "Peter Dimov" <[EMAIL PROTECTED]> writes:
> >
> >> I am not saying that this is never useful, but syntax should target
> >> the typical scenario, not corner cases.
> >
> > Agreed.  I suppose that you'll say it doesn't target the typical
> > scenario because of its confusability.  I wouldn't argue.  Any other
> > reasons?
> >
> > What about:
> >
> >     result(f)
> 
> Unqualified? ;-)

Sorry, I don't understand this response.

> >> It makes a lot more sense (to me) to reserve operator() for the
> >> Runnable concept, since that's what Boost.Threads currently uses.
> >
> > And prevent any other concepts from using operator()?  Surely you
> > don't mean that.
> 
> No, I meant in that particular case.

I tend to agree with this.
 
> We have three concepts: Runnable, Executor (executes Runnables), and
> HasResult (for lack of a better name.) The AsyncCall concept I had in mind
> is both Runnable and HasResult, so it can't use operator() for both.
> x.result() or result(x) are both fine for HasResult.

I tend to prefer x.result() because it doesn't require a friend declaration.  Adding a 
result(x) on top of that is certainly easy, and if we think it's useful enough to be 
provided by the library, I'd vote for providing both forms because of this.

> Here's some compilable code, to put things in perspective:

Thanks.  This helps me, at least.

> #include <boost/detail/lightweight_mutex.hpp>
> #include <boost/function.hpp>
> #include <boost/bind.hpp>
> #include <stdexcept>
> #include <string>
> #include <iostream>
> 
> template<class R> class async_call
> {
> public:
> 
>     template<class F> explicit async_call(F f): f_(f), ready_(false)
>     {
>     }
> 
>     void operator()()
>     {
>         mutex_type::scoped_lock lock(mutex_);
>         new(result_) R(f_());
>         ready_ = true;
>     }

Hmm... is this truly portable?  Don't you have to use the same techniques as 
optional<> here... or even just use optional<> in the implementation?  Also, though 
not on subject with discussion of the design, it's probably a bad idea to lock the 
mutex in this way (mutexes shouldn't be held for extensive periods of time), though 
the alternative implementation requires 2 copies of R and a condition for waiting on 
the result.
 
>     R result() const
>     {
>         mutex_type::scoped_lock lock(mutex_);
>         if(ready_) return reinterpret_cast<R const &>(result_);
>         throw std::logic_error("async_call not completed");
>     }
> 
> private:
> 
>     typedef boost::detail::lightweight_mutex mutex_type;
> 
>     mutable mutex_type mutex_;
>     boost::function<R ()> f_;
>     char result_[sizeof(R)];
>     bool ready_;
> };
> 
> int f(int x)
> {
>     return x * 2;
> }
> 
> int main()
> {
>     // step 1: construct an async_call
>     async_call<int> call( boost::bind(f, 3) );
> 
>     // 1a: attempt to obtain result before execution
>     try
>     {
>         std::cout << call.result() << std::endl;
>     }
>     catch(std::exception const & x)
>     {
>         std::cout << x.what() << std::endl;
>     }
> 
>     // step 2: execute an async_call
>     call();

This example, and the implementation above, are just complex synchronous calls.  I 
assume you really meant for either the constructor or this call to also take an 
Executor concept?
 
>     // step 3: obtain result
>     try
>     {
>         std::cout << call.result() << std::endl;
>     }
>     catch(std::exception const & x)
>     {
>         std::cout << x.what() << std::endl;
>     }
> }

The one "issue" I see with using operator() to invoke the function is the race 
conditions that can occur if the user calls this multiple times.  I'd consider it a 
non-issue personally, since you'd have to go out of your way to use this design 
incorrectly, but thought I should at least point it out.

Actually, there's another minor issue as well.  The user can call operator() and then 
let the async_call go out of scope with out ever calling result().  Mayhem would 
ensue.  The two options for dealing with this are to either block in the destructor 
until the call has completed or to simply document this as undefined behavior.


William E. Kempf
[EMAIL PROTECTED]

_______________________________________________
Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost

Reply via email to