
-----Original Message----- From: boost-bounces@lists.boost.org [mailto:boost-bounces@lists.boost.org] On Behalf Of Fernando Cacciola Sent: Saturday, July 16, 2005 12:48 AM To: boost@lists.boost.org Subject: Re: [boost] New Library Proposal: dual_state
<clip -- discussion on why ?: is clearer than "adaptor">
You said that
"the door has been flung open to many templated constructs that are more difficlt with the ?: operator. For example, a boost::transform_iterator could be created with the adaptor to automatically access the underlying objects in a sequence."
I disagree.. can you post a concrete example with a construct that is indeed more difficult with operator ?: (... and be fair; i.e. code the operator version the right way...)
IMO you still need to show that the adpator is indeed a good thing; if you do that we can start discussing a proper design and implementation for it.
Okay, I'll try. How about finding the max_element (or min_element) of a series of optional integers? Using the ?: operator, we might do this in two steps, the first to filter out non-existent values, the second to apply the max_element algorithm. Another way would be to write a custom predicate function that knows how to compare "full" and "empty" Boost::Optional objects or that duplicates the functionality of adaptor. However, by using an adaptor in conjunction with boost::transform_iterator, the job can be accomplished in one loop without the difficulty of writing a custom predicate. Here is some example code (BTW, others seem to prefer the spelling "adaptor," which is more consistent with other standardized names like "iterator," so I'll conform to that): // -- begin typedef boost::optional<int> opt_int; typedef std::vector<opt_int> Vec; typedef Vec::const_iterator cIter; Vec v; int max; v.push_back(0); v.push_back(1); v.push_back(2); v.push_back(boost::none); v.push_back(3); v.push_back(4); v.push_back(boost::none); v.push_back(5); // METHOD #1a: copy to a new vector std::vector<int> tmp; for( cIter p = v.begin(); p != v.end(); ++p ) { if( *p ) tmp.push_back( p->get() ); } max = *std::max_element( tmp.begin(), tmp.end() ); // METHOD #1b: copy using ?: to provide a signal value for undefined elements (similar to adaptor) tmp.clear(); for( cIter p = v.begin(); p != v.end(); ++p ) { tmp.push_back( *p ? p->get() : -1 ); } max = *std::max_element( tmp.begin(), tmp.end() ); // METHOD #2a: define a custom predicate template< class T, class Pred, T Def = T() > class optPred : public std::binary_function<boost::optional<T>, boost::optional<T>, bool> { Pred f; T _eval( const boost::optional<T>& x ) const { return ( x ? x.get() : Def ); } public: optPred() : f( Pred() ) {} bool operator()( const first_argument_type& a, const second_argument_type& b ) const { return f( _eval(a), _eval(b) ); } }; opt_int opt_max; opt_max= *std::max_element( v.begin(), v.end(), optPred< int, std::less<int>, -1 >() ); // METHOD #2b: use an adaptor-like predicate template< class T > class adaptor_substitute : public std::unary_function< boost::optional<T>, T > { T Def; public: _adaptor_() : Def( T() ) {} _adaptor_(T def) : Def( def ) {} T operator()(const argument_type& x ) { return ( x ? x.get() : Def ); } }; max = *std::max_element( boost::make_transform_iterator( v.begin, adaptor_substitute<int>(-1) ), boost::make_transform_iterator( v.end, adaptor_substitute<int>(-1) ) ); // METHOD #3: use an adaptor max = *std::max_element( boost::make_transform_iterator( v.begin, boost::optional<int>::adaptor(-1) ), // NOTE: boost::optional<int>::adaptor does not currently exist boost::make_transform_iterator( v.end, boost::optional<int>::adaptor(-1) ) ); // -- end It seems to me the adaptor, at the very least, saves us the trouble of defining a custom predicate. The similar exercise for min_element can be done by choosing a different signal value, such as 0x7FFFFFFF (the point at "infinity"). I hope this clears up my comment about using adaptor with boost::transform_iterator, but let me know if I haven't convinced you. -Andy