Topic: implementing min() and max()


Author: phalpern@newview.org (Pablo Halpern)
Date: 1999/10/14
Raw View
Francis Glassborow <francis@robinton.demon.co.uk> wrote:

>Something along the lines of?
>
>template <typename T, typename U, typename V = T>
>V max(T const & t, U const & u) {
>        return  ( t>u? static_cast<V> t : static_cast<V> u);
>}
>
>Just thinking at the keyboard.

Well, that would work, but not transparently. You would either have to
make sure that the larger type is the first parameter to the function,
or you would have to call the function with explicit template parameters
as in:

 max<int, long, long>(i, l)

with a single partial specialization, I think you could reduce this to:

 max<long>(i, l)


-------------------------------------------------------------
Pablo Halpern                            phalpern@newview.org

I am self-employed. Therefore, my opinions *do* represent
those of my employer.
---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]





Author: phalpern@newview.org (Pablo Halpern)
Date: 1999/10/09
Raw View
Rich Paul <rich-paul@rich-paul.net> wrote:

> int i1 = 3;
>
>        long l1 = long(std::numeric_limits<int>::max())+3;
>
>
> l1 = max(i1,l1); // this is the hard one

Others have proposed various solutions that should work with today's
standard (although they all require a lot of typing). If there were a
typeof() operator, it would be a cinch:

template <class U, class T>
typeof(U(0)+T(0)) max(const U& u, const T& t);

This assumes, of course, that the promotion rules for + are the same as
the ones you want for max, which seems like a reasonable assumption for
arithmetic types.

-------------------------------------------------------------
Pablo Halpern                            phalpern@newview.org

I am self-employed. Therefore, my opinions *do* represent
those of my employer.
---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]





Author: Francis Glassborow <francis@robinton.demon.co.uk>
Date: 1999/10/10
Raw View
In article <38017123.359766106@news.internetconnect.net>, Pablo Halpern
<phalpern@newview.org> writes
>Others have proposed various solutions that should work with today's
>standard (although they all require a lot of typing). If there were a
>typeof() operator, it would be a cinch:
>
>template <class U, class T>
>typeof(U(0)+T(0)) max(const U& u, const T& t);

Something along the lines of?

template <typename T, typename U, typename V = T>
V max(T const & t, U const & u) {
        return  ( t>u? static_cast<V> t : static_cast<V> u);
}

Just thinking at the keyboard.



Francis Glassborow      Journal Editor, Association of C & C++ Users
64 Southfield Rd
Oxford OX4 1PA          +44(0)1865 246490
All opinions are mine and do not represent those of any organisation
---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]





Author: "Paul D. DeRocco" <pderocco@ix.netcom.com>
Date: 1999/10/01
Raw View
Rich Paul <rich-paul@rich-paul.net> wrote in message
news:37EF6122.122ECD6F@rich-paul.net...

> > >         max(i1,i2) = 6; // since both are ints, this is leagal
>
> The idea is to replace the larger value with the new value so in this
> case, i2 would become 6, which i1 would remain 3.  This can be done with
> a macro min/max

That's true, and it could be done with a C++ function that returned a
reference. But it would be surprising to most users, and not commonly
useful.




[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]






Author: Christopher Eltschka <celtschk@physik.tu-muenchen.de>
Date: 1999/09/29
Raw View
Rich Paul wrote:
>
> I'm having a problem implementing min() and max() to my liking ( not to
> be confused with std::min() and std::max() ).
>
> I'd like to have a version that is able to do type promotion, as the
> macro version does, without resorting to a macro.  For example, I'd like
> to be able to do this: ( assume that sizeof(long)!=sizeof(int) );
>
> void foo() {
>         int i1 = 3;
>         int i2 = 5;
>
>         long l1 = long(std::numeric_limits<int>::max())+3;
>
>         max(i1,i2) = 6; // since both are ints, this is leagal
>
>         // max(i1,l1) = 1; // not legal
>         // max(l1,i1) = 2; // same
>
>         l1 = max(i1,l1); // this is the hard one
> };
>
> The problem with the last one is that there is no good, portable way (
> that I'm aware of ) to determine the proper return type for this
> function.  By looking at it, I know that I'd like it to return a const
> long, but how can I tell the compiler?
>
> Now if there was a way to implement this struct:
>
> template<typename T, typename U>
> struct max_type {
>         typedef (numeric_limits<T>::max()>numeric_limits<U>::max()?T:U)
>                  type;
> };
>
> the functions are no problem.  Just
>
> template<class T, class U>
> max_type<T,U> type max( const T&t, const U&u ) {
>         return (t>u)?t:u;
> };
>
> of course you cannot put a conditional in a typedef, so that's out.
>
> now this has some promise:
>
> enum eCode {
>         eChar,
>         eInt,
>         eLong,
>         eEtc
> };
>
> template <eCode code>   struct CodeType;
> template <>             struct CodeType <eChar> {typedef char type;};
> template <>             struct CodeType <eInt>  {typedef int type;};
>
> template<typename t>    struct TypeCode;
> template<>              struct TypeCode<int>
>                             { const static eCode code = eInt; };
> template<>              struct TypeCode<char>
>                             { const static eCode code = eChar; };
>
> template<typename T, typename U>
> inline eCode larger_code ( const T&, const U& ) {
>         return std::numeric_limits<T>::max() >
>                std::numeric_limits<U>::max() ?
>                 (TypeCode<T>::code):(TypeCode<Y>::code);
> };
>
> template<typename T, typename U>
> struct max_type {
>   typedef CodeType<larger_code(T(),U())>::type type;
> };
>
> But I've not yet found a compiler for which it works, nor am I entirely
> convinced that it SHOULD work.  What do you think?

It should not work. Template parameters must be compile time
constants.

However, you might be able to do what you want with
help of <climits>, which - unlike limits - has constants
instead of functions for min and max. Of course you have
to do some work to bring them into a template framework
(all untested code):

// constlimits.h
#ifndef CONSTLIMITS_H_INC
#define CONSTLIMITS_H_INC

#include <climits>

template<class T> struct climits;

template<> struct climits<char>
{
  static char const min = CHAR_MIN;
  static char const max = CHAR_MAX;
};

template<> struct climits<signed char>
{
  static signed char const min = SCHAR_MIN;
  static signed char const max = SCHAR_MAX;
};

template<> struct climits<unsigned char>
{
  static unsigned char const min = 0;
  static unsigned char const max = UCHAR_MAX;
};

// same for (unsigned) short/int/long

#endif

// constlimits.cc
#include <constlimits.h>

// here we can use the preprocessor without much harm
#define TYPES(what) \
  what(char) what(signed char) what(unsigned char) \
  what(short) what(unsigned short) \
  what(int) what(unsigned int) \
  what(long) what(unsigned long)

#define DECL(type) \
  type const climits<type>::min; \
  type const climits<type>::max;

TYPES(DECL)

Now you can do a type selection at compile time:

template<bool b, class T1, class T2> struct if_else
{
  typedef T1 type;
};

template<class T1, class T2> struct if_else<false, T1, T2>
{
  typedef T2 type;
};

template<class T1, class T2> struct max_type
{
  typedef
    typename if_else<(climits<T1>::max > climits<T2>::max),
                     T1, T2>::type type;
};

BTW, your max template itself still doesn't allow your
wanted "max(foo, bar) = baz;" since you return by value only
(it also wouldn't make sense to return by reference if the
types are different).

However, simple overloading should help here:

template<class T, class U>
 typename max_type<T, U>::type max(T& t, U& u)
{
  return t>u? t : u;
}

template<class T>
 T& max_type(T&, T&)
{
  return t>u? t : u;
}

Partial template ordering should ensure that the second
version is called if both arguments have the same type.
---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]





Author: "Paul D. DeRocco" <pderocco@ix.netcom.com>
Date: 1999/09/27
Raw View
Rich Paul wrote:
>
> I'm having a problem implementing min() and max() to my liking ( not to
> be confused with std::min() and std::max() ).
>
> I'd like to have a version that is able to do type promotion, as the
> macro version does, without resorting to a macro.  For example, I'd like
> to be able to do this: ( assume that sizeof(long)!=sizeof(int) );
>
> void foo() {
>         int i1 = 3;
>         int i2 = 5;
>
>         long l1 = long(std::numeric_limits<int>::max())+3;
>
>         max(i1,i2) = 6; // since both are ints, this is leagal
>
>         // max(i1,l1) = 1; // not legal
>         // max(l1,i1) = 2; // same
>
>         l1 = max(i1,l1); // this is the hard one
> };

(Putting max on the left side of an assignment looks nonsensical to me, but
I'll let that pass.)

If you only need to deal with built-in types, then you can write an
abstract template parameterized by two types, that defines typedefs for the
stronger (and might as well include the weaker) of the two, and then
explicitly specialize it for all possible combinations of built-in types:

template <typename T1, T2> struct whichtype {};

template <> struct whichtype<char, char> {
    typedef char stronger;
    typedef char weaker;
    };

template <> struct whichtype<char, unsigned char> {
    typedef unsigned char stronger;
    typedef char weaker;
    };

and so on. Then, you can write:

template <typename T1, T2> inline const
        whichtype<T1, T2>::stronger& min(const T1& v1, const T2& v2) {
    typedef const which_type<T1, T2>::stronger T;
    return static_cast<T&>(v1) > static_cast<T&>(v2)
            ? static_cast<T&>(v1) : static_cast<T&>(v2);
    }

Of course, since there are only two functions to define, min and max, this
isn't much simpler than writing overloaded versions of min and max for all
possible combinations of built-in types.

--

Ciao,                       Paul D. DeRocco
Paul                        mailto:pderocco@ix.netcom.com
---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]





Author: Rich Paul <rich-paul@rich-paul.net>
Date: 1999/09/27
Raw View
"Paul D. DeRocco" wrote:

> > void foo() {
> >         int i1 = 3;
> >         int i2 = 5;
> >
> >         long l1 = long(std::numeric_limits<int>::max())+3;
> >
> >         max(i1,i2) = 6; // since both are ints, this is leagal
> >
> >         // max(i1,l1) = 1; // not legal
> >         // max(l1,i1) = 2; // same
> >
> >         l1 = max(i1,l1); // this is the hard one
> > };
>
> (Putting max on the left side of an assignment looks nonsensical to me, but
> I'll let that pass.)

The idea is to replace the larger value with the new value so in this
case, i2 would become 6, which i1 would remain 3.  This can be done with
a macro min/max
---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]





Author: Andrei Alexandrescu <andrewalex@hotmail.com>
Date: 1999/09/27
Raw View
In article <37EC7E0D.18414625@rich-paul.net>,
  Rich Paul <rich-paul@rich-paul.net> wrote:
[snip]
> template<typename T, typename U>
> struct max_type {
>  typedef (numeric_limits<T>::max()>numeric_limits<U>::max()?T:U)
>    type;
> };

You can do something like this. Here's a piece of code I posted a while
ago:

template <bool, class T, class>
struct Choose
{
    typedef T Type;
};

template <class T, class U>
struct Choose<false, T, U>
{
    typedef U Type;
};

This effectively lets you choose a type based upon a boolean condition
at compile time.

The bad news is that numeric_limits<T>::max() are functions, so you
cannot use them with Choose. What I suggest you is to combine Choose
with some traits mechanism that's entirely compile-time based.

[snip]
> But I've not yet found a compiler for which it works, nor am I
entirely
> convinced that it SHOULD work.  What do you think?

It won't work. You combine runtime stuff with compile-time stuff. For
templates to work, you have to stick to compile-time constructs. Hint:
sizeof is computed at compile-time and it can accept quite any legal
expression that doesn't return an undefined type.

Cheers,

Andrei


Sent via Deja.com http://www.deja.com/
Before you buy.


[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]






Author: Rich Paul <rich-paul@rich-paul.net>
Date: 1999/09/25
Raw View
I'm having a problem implementing min() and max() to my liking ( not to
be confused with std::min() and std::max() ).

I'd like to have a version that is able to do type promotion, as the
macro version does, without resorting to a macro.  For example, I'd like
to be able to do this: ( assume that sizeof(long)!=sizeof(int) );


void foo() {
 int i1 = 3;
        int i2 = 5;

        long l1 = long(std::numeric_limits<int>::max())+3;

 max(i1,i2) = 6; // since both are ints, this is leagal

 // max(i1,l1) = 1; // not legal
 // max(l1,i1) = 2; // same

 l1 = max(i1,l1); // this is the hard one
};

The problem with the last one is that there is no good, portable way (
that I'm aware of ) to determine the proper return type for this
function.  By looking at it, I know that I'd like it to return a const
long, but how can I tell the compiler?

Now if there was a way to implement this struct:

template<typename T, typename U>
struct max_type {
 typedef (numeric_limits<T>::max()>numeric_limits<U>::max()?T:U)
   type;
};

the functions are no problem.  Just

template<class T, class U>
max_type<T,U> type max( const T&t, const U&u ) {
 return (t>u)?t:u;
};

of course you cannot put a conditional in a typedef, so that's out.

now this has some promise:

enum eCode {
 eChar,
 eInt,
 eLong,
 eEtc
};

template <eCode code>   struct CodeType;
template <>             struct CodeType <eChar> {typedef char type;};
template <>             struct CodeType <eInt>  {typedef int type;};

template<typename t>    struct TypeCode;
template<>              struct TypeCode<int>
                            { const static eCode code = eInt; };
template<>              struct TypeCode<char>
                            { const static eCode code = eChar; };

template<typename T, typename U>
inline eCode larger_code ( const T&, const U& ) {
 return std::numeric_limits<T>::max() >
               std::numeric_limits<U>::max() ?
  (TypeCode<T>::code):(TypeCode<Y>::code);
};

template<typename T, typename U>
struct max_type {
  typedef CodeType<larger_code(T(),U())>::type type;
};

But I've not yet found a compiler for which it works, nor am I entirely
convinced that it SHOULD work.  What do you think?


[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]