...one of the most highly
regarded and expertly designed C++ library projects in the
world.
— Herb Sutter and Andrei
Alexandrescu, C++
Coding Standards
It seems more useful to add the ability to capture the return value from the first of the task functions to complete. Again, we assume that none will throw an exception.
One tactic would be to adapt our Done
class to store the first
of the return values, rather than a simple bool
.
However, we choose instead to use a buffered_channel<>
.
We'll only need to enqueue the first value, so we'll buffered_channel::close()
it
once we've retrieved that value. Subsequent push()
calls will return closed
.
// Assume that all passed functions have the same return type. The return type // of wait_first_value() is the return type of the first passed function. It is // simply invalid to pass NO functions. template< typename Fn, typename ... Fns > typename std::result_of< Fn() >::type wait_first_value( Fn && function, Fns && ... functions) { typedef typename std::result_of< Fn() >::type return_t; typedef boost::fibers::buffered_channel< return_t > channel_t; auto chanp( std::make_shared< channel_t >( 64) ); // launch all the relevant fibers wait_first_value_impl< return_t >( chanp, std::forward< Fn >( function), std::forward< Fns >( functions) ... ); // retrieve the first value return_t value( chanp->value_pop() ); // close the channel: no subsequent push() has to succeed chanp->close(); return value; }
The meat of the wait_first_value_impl()
function is as you might expect:
template< typename T, typename Fn > void wait_first_value_impl( std::shared_ptr< boost::fibers::buffered_channel< T > > chan, Fn && function) { boost::fibers::fiber( [chan, function](){ // Ignore channel_op_status returned by push(): // might be closed; we simply don't care. chan->push( function() ); }).detach(); }
It calls the passed function, pushes its return value and ignores the
push()
result. You might call it like this:
std::string result = wait_first_value( [](){ return sleeper("wfv_third", 150); }, [](){ return sleeper("wfv_second", 100); }, [](){ return sleeper("wfv_first", 50); }); std::cout << "wait_first_value() => " << result << std::endl; assert(result == "wfv_first");