[interest?] min, max and more ... solved!

<< Background ... >> Way back in 1995, Scott Meyers issued a challenge to the C++ community to build a better min/max utility [1]. In 2003, Andrei took up the challenge [2], but his results were unsatisfactory for reasons both technical and aesthetic [3]. In 2005, I wrote about some tricks you can do with the conditional operator [4]. Andrei suggested a way the tricks could be used to finally resolve Scott's min/max challenge. Still, the nagging lvalue/rvalue problems remained. (Consider that for some user-defined type A, this: "A const &a = std::min(A(1),A(2));" causes the reference to "a" to dangle.) << The Present ... >> Last week, at Scott's prodding, I took the issue up again and finally resolved the lvalue/rvalue problem. The result is a macro MAX(a,b) that behaves *exactly* like ((a)<(b)?(b):(a)), except that it does not reevaluate its arguments. It has the following advantages over std::max: 1) It supports both const and non-const arguments (including mixing the two in a single call). 2) It supports arguments of different types. 3) The expression type of MAX(a,b) is naturally identical to "((a)<(b)?(b):(a))", relying on the compiler's type promotion rules. 4) The rvalue/lvalue-ness of MAX(a,b) is identical to "((a)<(b)?(b):(a))", making it possible to use it on the left side of an assignment, for instance: "MAX(a,b) = 1". This also means that dangling references are impossible. The code is 100% C++98 compliant, and should work on gcc and comeau. (msvc will need some work-arounds.) The code is below. << The Future ... >> Is there interest in turning this into a BOOST_MIN/BOOST_MAX library? << The Code >> #include <boost/type.hpp> #include <boost/mpl/if.hpp> #include <boost/mpl/or.hpp> #include <boost/type_traits/is_array.hpp> #include <boost/type_traits/is_abstract.hpp> template<typename Type> boost::type<Type>* encode_type(Type &) { return 0; } template<typename Type> boost::type<Type const>* encode_type(Type const &) { return 0; } /////////////////////////////////////////////////////////////////////////////// // max_impl template<typename Ret, typename Left, typename Right> struct max_impl { max_impl(Left &left, Right &right) : left_(left) , right_(right) {} struct private_type_ {}; // can't ever return an array or an abstract type by value typedef BOOST_DEDUCED_TYPENAME boost::mpl::if_< boost::mpl::or_<boost::is_abstract<Ret>, boost::is_array<Ret> > , private_type_ , Ret >::type value_type; operator value_type() { return this->left_ < this->right_ ? this->right_ : this->left_; } operator Ret &() const { return this->left_ < this->right_ ? this->right_ : this->left_; } private: Left &left_; Right &right_; }; /////////////////////////////////////////////////////////////////////////////// // max_fun template<typename Ret, typename Left, typename Right> max_impl<Ret, Left, Right> max_fun(Left &left, Right &right, boost::type<Ret> *) { return max_impl<Ret, Left, Right>(left, right); } template<typename Ret, typename Left, typename Right> max_impl<Ret, Left const, Right> max_fun(Left const &left, Right &right, boost::type<Ret> *) { return max_impl<Ret, Left const, Right>(left, right); } template<typename Ret, typename Left, typename Right> max_impl<Ret, Left, Right const> max_fun(Left &left, Right const &right, boost::type<Ret> *) { return max_impl<Ret, Left, Right const>(left, right); } template<typename Ret, typename Left, typename Right> max_impl<Ret, Left const, Right const> max_fun(Left const &left, Right const &right, boost::type<Ret> *) { return max_impl<Ret, Left const, Right const>(left, right); } #define MAX(a,b)\ (true\ ? max_fun((a), (b), \ (true? 0 : encode_type(true? (a) : (b))))\ : (true? (a) : (b))) [1] http://www.aristeia.com/Papers/C++ReportColumns/jan95.pdf [2] http://www.ddj.com/dept/cpp/184403774 [3] Andrei's solution ignores lvalue/rvalue issues as raised in Francis Glassborow's c++std-lib-15426, and it depends on Loki, requiring a large amount of template code that largely duplicates the type promotion logic already present in the compiler. [4] http://www.artima.com/cppsource/foreach.html -- Eric Niebler Boost Consulting www.boost-consulting.com

On Mon, 10 Jul 2006 10:24:29 -0700, "Eric Niebler" <eric@boost-consulting.com> wrote:
[...]
Last week, at Scott's prodding, I took the issue up again and finally resolved the lvalue/rvalue problem. The result is a macro MAX(a,b) that behaves *exactly* like ((a)<(b)?(b):(a)), except that it does not reevaluate its arguments.
Hi Eric, I thought I could throw an idea even if I had no time to examine your code. Generally speaking I find it grotesque that one has to go through contortions for such simple "primitives" (gcc has had builtin operators for min and max for years now). My question is: is your solution extendible to different operations, such that one could write (not necessarily with that syntax): f(x, y) = v rather then max(x, y) = v ? In this case you would have a little "framework" to build modifiable lvalues upon binary functions or function objects. -- [ Gennaro Prota, C++ developer for hire ] [ resume: available on request ]

Gennaro Prota wrote:
On Mon, 10 Jul 2006 10:24:29 -0700, "Eric Niebler" <eric@boost-consulting.com> wrote:
[...]
Last week, at Scott's prodding, I took the issue up again and finally resolved the lvalue/rvalue problem. The result is a macro MAX(a,b) that behaves *exactly* like ((a)<(b)?(b):(a)), except that it does not reevaluate its arguments.
Hi Eric,
I thought I could throw an idea even if I had no time to examine your code. Generally speaking I find it grotesque that one has to go through contortions for such simple "primitives" (gcc has had builtin operators for min and max for years now). My question is: is your solution extendible to different operations, such that one could write (not necessarily with that syntax):
f(x, y) = v
rather then
max(x, y) = v
?
In this case you would have a little "framework" to build modifiable lvalues upon binary functions or function objects.
I don't see why not. The techniques used are fairly general. If you have a macro that expands to an expression, and you don't want it to reevaluate its arguments, and you want to preserve const-correctness and lvalue/rvalue-ness, this approach should work. This is probably a lack of imagination on my part, but I can't think of another use besides min/max. What do you have in mind? -- Eric Niebler Boost Consulting www.boost-consulting.com

"Eric Niebler" <eric@boost-consulting.com> writes:
This is probably a lack of imagination on my part, but I can't think of another use besides min/max. What do you have in mind?
for example, difference-of-squares: (a+b)(a-b) -- Dave Abrahams Boost Consulting www.boost-consulting.com

On Mon, 10 Jul 2006 21:06:00 -0700, "Eric Niebler" <eric@boost-consulting.com> wrote:
This is probably a lack of imagination on my part, but I can't think of another use besides min/max. What do you have in mind?
I hadn't a precise case in mind. I just thought that "the greatest of" or "the smallest of" are nothing but two possible criteria to select between two (or many) expressions. -- [ Gennaro Prota, C++ developer for hire ] [ resume: available on request ]

On Tue, Jul 11, 2006 at 02:21:02AM +0200, Gennaro Prota wrote:
I thought I could throw an idea even if I had no time to examine your code. Generally speaking I find it grotesque that one has to go through contortions for such simple "primitives" (gcc has had builtin operators for min and max for years now).
The C++ <? and >? operators were deprecated in GCC 4.0 jon

"Eric Niebler" <eric@boost-consulting.com> writes:
Is there interest in turning this into a BOOST_MIN/BOOST_MAX library?
I dunno. The mixed-type min/max problem is not a major issue for most people, is it? I'm pretty sure decltype/rvalue ref will give us everything we need to do it in C++0x with a real function template, so I'm leary of building macros for this purpose. -- Dave Abrahams Boost Consulting www.boost-consulting.com

David Abrahams wrote:
"Eric Niebler" <eric@boost-consulting.com> writes:
Is there interest in turning this into a BOOST_MIN/BOOST_MAX library?
I dunno. The mixed-type min/max problem is not a major issue for most people, is it?
IMO, it's a minor inconvenience. The dangling reference problem is more insidious, as the compiler won't catch it, but I've never been bitten by it, personally.
I'm pretty sure decltype/rvalue ref will give us everything we need to do it in C++0x with a real function template, so I'm leary of building macros for this purpose.
How long will it be before all the compilers Boost supports have decltype and rvalue-reference? ;-) I'm not sure how I feel about it either. Maybe I'll whip something up, put it in the sandbox and leave it there. -- Eric Niebler Boost Consulting www.boost-consulting.com

On Tue, 11 Jul 2006 09:03:05 -0700, "Eric Niebler" <eric@boost-consulting.com> wrote:
I'm not sure how I feel about it either. Maybe I'll whip something up, put it in the sandbox and leave it there.
I think I know how you feel :) These are the kinds of problems which give as much satisfaction for solving as disappointment for not having much application. I felt basically the same when solving the "is enum" challenge. Unless serendipity happens to be on your side, they are pure intellectual fun. -- [ Gennaro Prota, C++ developer for hire ] [ resume: available on request ]

Eric Niebler writes:
David Abrahams wrote:
"Eric Niebler" <eric@boost-consulting.com> writes:
Is there interest in turning this into a BOOST_MIN/BOOST_MAX library?
I dunno. The mixed-type min/max problem is not a major issue for most people, is it?
IMO, it's a minor inconvenience. The dangling reference problem is more insidious, as the compiler won't catch it, but I've never been bitten by it, personally.
I'm pretty sure decltype/rvalue ref will give us everything we need to do it in C++0x with a real function template, so I'm leary of building macros for this purpose.
How long will it be before all the compilers Boost supports have decltype and rvalue-reference? ;-)
I'm not sure how I feel about it either. Maybe I'll whip something up, put it in the sandbox and leave it there.
</lurk> I'd favor having it in Boost. Boost is not only a collection of useful libraries, it's also a collection of _good examples_ and _educational_ libraries, and I think Eric's macros are both of the latter. People don't _have_ to use them. :-) <lurk> ---------------------------------------------------------------------- Dave Steffen, Ph.D. Software Engineer IV Disobey this command! Numerica Corporation ph (970) 419-8343 x27 fax (970) 223-6797 - Douglas Hofstadter dgsteffen@numerica.us

On Tue, 11 Jul 2006 11:46:52 -0600, Dave Steffen <dgsteffen@numerica.us> wrote:
</lurk>
I'd favor having it in Boost. Boost is not only a collection of useful libraries, it's also a collection of _good examples_ and _educational_ libraries, and I think Eric's macros are both of the latter. People don't _have_ to use them. :-)
<lurk>
Eric, maybe this can go in www.boost.org/lurk ... er... www.boost.org/more/ ? ;) -- [ Gennaro Prota, C++ developer for hire ] [ resume: available on request ]

On 7/10/06 1:24 PM, "Eric Niebler" <eric@boost-consulting.com> wrote: [SNIP problem of making a macro like MAX(a, b) == ((a)<(b)?(b):(a)) except that it evaluates its arguments only once, but still works on arguments of different types and maintains the arguments' const and l/r-value states.]
Is there interest in turning this into a BOOST_MIN/BOOST_MAX library? [TRUNCATE the actual code]
Yes, please. -- Daryle Walker Mac, Internet, and Video Game Junkie darylew AT hotmail DOT com

Daryle Walker wrote:
On 7/10/06 1:24 PM, "Eric Niebler" <eric@boost-consulting.com> wrote:
[SNIP problem of making a macro like MAX(a, b) == ((a)<(b)?(b):(a)) except that it evaluates its arguments only once, but still works on arguments of different types and maintains the arguments' const and l/r-value states.]
Is there interest in turning this into a BOOST_MIN/BOOST_MAX library? [TRUNCATE the actual code]
Yes, please.
BOOST_MIN() and BOOST_MAX() now live in minmax_macro.zip at: http://boost-consulting.com/vault/index.php?directory=Algorithms and also in boost-sandbox CVS at boost/algorithm/minmax_macro.hpp. It's been tested with gcc 3.x and msvc 7.1, and I took a stab at a version for Comeau, but which is currently untested. This code pokes into C++'s dark, dusty corners, and no two compilers seem to agree, so any feedback/workarounds for other compilers is much appreciated. Thanks go to Howard Hinnant for feedback and test cases. -- Eric Niebler Boost Consulting www.boost-consulting.com

On 7/24/06 4:41 PM, "Eric Niebler" <eric@boost-consulting.com> wrote: [SNIP recap]
BOOST_MIN() and BOOST_MAX() now live in minmax_macro.zip at:
http://boost-consulting.com/vault/index.php?directory=Algorithms
and also in boost-sandbox CVS at boost/algorithm/minmax_macro.hpp. It's been tested with gcc 3.x and msvc 7.1, and I took a stab at a version for Comeau, but which is currently untested.
This code pokes into C++'s dark, dusty corners, and no two compilers seem to agree, so any feedback/workarounds for other compilers is much appreciated.
Works on XCode 2.3 (Mac OS X 10.4.7 on PowerPC, using special GCC 4.0.1).
Thanks go to Howard Hinnant for feedback and test cases.
-- Daryle Walker Mac, Internet, and Video Game Junkie darylew AT hotmail DOT com
participants (6)
-
Daryle Walker
-
Dave Steffen
-
David Abrahams
-
Eric Niebler
-
Gennaro Prota
-
Jonathan Wakely