Boost logo

Boost :

From: Wolfgang Bangerth (bangerth_at_[hidden])
Date: 2003-02-06 12:13:32


> >> async_result<double> res;
> >> thread t(bind(res.call(), a, b, c));
> >> // do something else
> >> d = res.value(); // Explicitly waits for the thread to return a
> >> value?
> >
> > This does the same, indeed. Starting a thread this way is just a little
> > more complex (and -- in my view -- less obvious to read) than writing
> > thread t = spawn(foo)(a,b,c);
>
> Not sure I agree about "less obvious to read". If your syntax had been
> thread t = spawn(foo, a, b, c);
> I think you'd have a bit more of an argument here. And I certainly could
> fold the binding directly into boost::thread so that my syntax would
> become:
>
> thread t(res.call(), a, b, c);
>
> I could even eliminate the ".call()" syntax with some implicit
> conversions, but I dislike that for the obvious reasons. I specifically
> chose not to include syntactic binding in boost::thread a long time ago,
> because I prefer the explicit seperation of concerns. So, where you think
> my syntax is "less obvious to read", I think it's "more explicit".

If you do all this, then you'll probably almost arrive at the code I
posted :-)

Still, keeping the analogy to the usual call foo(a,b,c), I prefer the
arguments to foo in a separate pair of parentheses. However, there is
another point that I guess will make your approach very hard: assume
    void foo(int, double, char);
and a potential constructor for your thread class
    template <typename A, typename B, typename C>
    thread (void (*p)(A,B,C), A, B, C);
Then you can write
    foo(1,1,1)
and arguments will be converted automatically. However, you cannot write
    thread t(foo, 1, 1, 1);
since template parameters must be exact matches.

There really is no other way than to first get at the argument types in a
first step, and pass the arguments in a second step. You _need_ two sets
of parentheses to get the conversions.

> > Actually it does duplicate the work, but not because I am stubborn. We
> > have an existing implementation for a couple of years, and the present
> > version just evolved from this. However, there's a second point: when
> > starting threads, you have a relatively clear picture as to how long
> > certain objects are needed, and one can avoid several copying steps if
> > one does some things by hand. It's short anyway, tuple type and tie
> > function are your friend here.
>
> I'm not sure how you avoid copies here.

Since you have control over lifetimes of objects, you can pass references
instead of copies at various places.

> > t->kill ();
> > t->suspend ();
> > Someone sees that there's a function yield() but doesn't have the time
> > to read the documentation, what will he assume what yield() does?
>
> How does "someone see that there's a function yield()" with out also
> seeing that it's static? No need to read documentation for that, as it's
> an explicit part of the functions signature.

Seeing it used in someone else's code? Just not being careful when reading
the signature?

I think it's the same argument as with void* : if applied correctly it's
ok, but in general it's considered harmful.

W.

-------------------------------------------------------------------------
Wolfgang Bangerth email: bangerth_at_[hidden]
                              www: http://www.ticam.utexas.edu/~bangerth/


Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk