Guru of the Week #77: #Definition

Guru of the Week #77: #Definition

Post by Herb Sutte » Wed, 27 Dec 2000 09:20:26



 -------------------------------------------------------------------
   Guru of the Week problems and solutions are posted regularly on

   see the GotW archive at www.peerdirect.com. (c) 2000 H.P.Sutter
            News archives may keep copies of this article.
 -------------------------------------------------------------------

_______________________________________________________

GotW #77:   #Definition

Difficulty: 4/ 10
_______________________________________________________

JG Question
-----------

1. Demonstrate how to write a simple max() preprocessor macro that
   takes two arguments and evaluates to the one that is greater, using
   normal < comparison.  What are the usual pitfalls in writing such a
   macro?

Guru Questions
--------------

2. What can a preprocessor macro not create?  Why not?

Herb

---

Secretary, ISO WG21 / ANSI J16 (C++)
Contributing Editor, C/C++ Users Journal (http://www.cuj.com)


      [ about comp.lang.c++.moderated. First time posters: do this! ]

 
 
 

Guru of the Week #77: #Definition

Post by siemelna.. » Thu, 28 Dec 2000 02:40:12


Quote:> 1. Demonstrate how to write a simple max() preprocessor macro that
>    takes two arguments and evaluates to the one that is greater, using
>    normal < comparison.  What are the usual pitfalls in writing such a
>    macro?

#define max(a,b) ((a)<(b) ? (b) : (a))

I wrote the above function the way it is because if a==b then the
function returns a.

One pitfall is to enclose all variables in parenthesis because
otherwise the operation priority might get messed up, as when a itself
contains a '<'.

There are probably other pitfalls.

Quote:> 2. What can a preprocessor macro not create?  Why not?

Type safety.

Const safety.

Evaluation safety.  Consider max(++x,y).

peace of mind

--
------------
Siemel Naran

Sent via Deja.com
http://www.deja.com/


      [ about comp.lang.c++.moderated. First time posters: do this! ]

 
 
 

Guru of the Week #77: #Definition

Post by Ken Bloo » Fri, 29 Dec 2000 12:02:36



Quote:

> > 1. Demonstrate how to write a simple max() preprocessor macro that
> >    takes two arguments and evaluates to the one that is greater, using
> >    normal < comparison.  What are the usual pitfalls in writing such a
> >    macro?

> #define max(a,b) ((a)<(b) ? (b) : (a))

> I wrote the above function the way it is because if a==b then the
> function returns a.

> One pitfall is to enclose all variables in parenthesis because
> otherwise the operation priority might get messed up, as when a itself
> contains a '<'.

> There are probably other pitfalls.

This is an easy one. The greater of the two arguments is evaluated twice

int x=1,y=2

max (++x,++y) returns 4 not 3. (x will then equal 2, y will then equal 4)

Quote:> > 2. What can a preprocessor macro not create?  Why not?

> Type safety.

> Const safety.

> Evaluation safety.  Consider max(++x,y).

> peace of mind

another preprocessor macro?


      [ about comp.lang.c++.moderated. First time posters: do this! ]

 
 
 

Guru of the Week #77: #Definition

Post by Robert J. Hanse » Sat, 30 Dec 2000 00:03:40


Quote:> > 2. What can a preprocessor macro not create?  Why not?

> Type safety.
> ...
> peace of mind

You forgot recursion.  Inlined functions can be recursive; macro expansions
cannot be (this is, of course, IIRC).


      [ about comp.lang.c++.moderated. First time posters: do this! ]

 
 
 

Guru of the Week #77: #Definition

Post by Alf P. Steinbac » Sat, 30 Dec 2000 01:32:02




> > 1. Demonstrate how to write a simple max() preprocessor macro that
> >    takes two arguments and evaluates to the one that is greater,
> >    using normal < comparison.  What are the usual pitfalls in
> >    writing such a macro?
...
> One pitfall is to enclose all variables in parenthesis because
> otherwise the operation priority might get messed up, as when a
> [macro argument] itself contains a '<'.

> There are probably other pitfalls.

The main problem with macros is name collision, in this case with
the std::max function from <algorithm> (I think).  And guess what?
The Microsoft compiler (I won't mention its evil name) is shipped
with a standard library that *does not* define std::max, because
there's a max macro in <windows.h>... ;-)

Another pitfall, but in using, not writing, a macro, see below.

Quote:

> > 2. What can a preprocessor macro not create?  Why not?

> Type safety.

Use of macros is type-safe, since the expanded code is checked by
the compiler  --  but macros can't *create* type safety, so this is
literally correct.

Quote:

> Const safety.

Ditto.

Quote:> Evaluation safety.  Consider max(++x,y).

I'd put that in the pitfalls category?  (See above.)

Quote:

> peace of mind

Right!

I'd add to this list, in my opinion at the top: a macro cannot
create another macro or preprocessor directive.  Probably because
of efficiency and language complexity considerations, but this is
an engineering decision.  There are ways to do Bad Things if
macros can create macros, but then Bad Things can always be done
in any general programming language.

Some of the things that could be accomplished with a "dynamic"
macro language can be accomplished by template classes.

The main thing I miss is to be able to define a module "USING"
macro (I don't use "#pragma once" since for portability include
guards must be defined anyway).

- Alf

--
alf_DOT_steinbach_AT_ac_DOT_com (clean the address before replying)
After 1. january 2000: alf_DOT_steinbach_AT_mogul_DOT_com

Sent via Deja.com
http://www.deja.com/


      [ about comp.lang.c++.moderated. First time posters: do this! ]

 
 
 

Guru of the Week #77: #Definition

Post by Thomas Luza » Sat, 30 Dec 2000 01:50:44



Quote:

>> 1. Demonstrate how to write a simple max() preprocessor macro that
>>    takes two arguments and evaluates to the one that is greater, using
>>    normal < comparison.  What are the usual pitfalls in writing such a
>>    macro?

>#define max(a,b) ((a)<(b) ? (b) : (a))

>I wrote the above function the way it is because if a==b then the
>function returns a.

>One pitfall is to enclose all variables in parenthesis because
>otherwise the operation priority might get messed up, as when a itself
>contains a '<'.

>There are probably other pitfalls.

You can't take the address of max and use it as a function pointer for
example. Furthermore this #define evaluates a and b twice, which might
not lead to correct results if a and b have side effects and also
might need more time (evaluation safety?). In addition to that type
safety, and const safety aren't guaranteed here as already said.

And again: There are probably other pitfalls :-)

Cheers,
Thomas Luzat


      [ about comp.lang.c++.moderated. First time posters: do this! ]

 
 
 

Guru of the Week #77: #Definition

Post by Andrei Alexandresc » Sat, 30 Dec 2000 01:51:05



Quote:> _______________________________________________________

> GotW #77:   #Definition

> Difficulty: 4/ 10
> _______________________________________________________

> JG Question
> -----------

> 1. Demonstrate how to write a simple max() preprocessor macro that
>    takes two arguments and evaluates to the one that is greater, using
>    normal < comparison.  What are the usual pitfalls in writing such a
>    macro?

> Guru Questions
> --------------

> 2. What can a preprocessor macro not create?  Why not?

Herb, mind if I chime in? This is the meat of an upcoming CUJ online
article. I thought it's kind of interesting.

ber-Guru Question:

3. Write a max() function that does what the max preprocessor macro does,
without its usual pitfalls mentioned at point 1.

Hints: Yes, it's doable. No, it's not a no-brainer :o).

Andrei


      [ about comp.lang.c++.moderated. First time posters: do this! ]

 
 
 

Guru of the Week #77: #Definition

Post by Mark Wilde » Sat, 30 Dec 2000 04:40:04



Quote:> (I don't use "#pragma once" since for portability include guards must be defined anyway).

So why not have both? The include guards are portable, but require reading and parsing the whole header. #pragma once is more
portable than I thought (it's not just Microsoft), and avoids reading the header file a second timel.


      [ about comp.lang.c++.moderated. First time posters: do this! ]

 
 
 

Guru of the Week #77: #Definition

Post by Luis Pedro Coelh » Sat, 30 Dec 2000 04:50:20



 >
 > > 1. Demonstrate how to write a simple max() preprocessor macro that
 > >    takes two arguments and evaluates to the one that is greater, using
 > >    normal < comparison.  What are the usual pitfalls in writing such a
 > >    macro?
 >
 > #define max(a,b) ((a)<(b) ? (b) : (a))
 >
 > I wrote the above function the way it is because if a==b then the
 > function returns a.

Why does it matter?

 > > 2. What can a preprocessor macro not create?  Why not?
 >
 > Type safety.
 >
 > Const safety.
 >
 > Evaluation safety.  Consider max(++x,y).
 >
 > peace of mind
 >

- A function.

Consider taking a pointer to it:

int (*func_ref)(int,int) = max; // "max undeclared"

- scope safety

You cannot declare any other function named max (or anything called max to
which you aply the () operator, actually).

namespace I_Am_Weasel {
        struct X { ... };
        X max(X a, X b) { .... }

Quote:}

- overloading / specializing possibilities.

Suppose your class does not have an operator <, or it is inappropriate for
this. You cannot provide a max function which takes two arguments of you
class type and does special things on them. In this max case, I suspect
this to be mostly an academic disadvantage, but on a more complex example
it is a real problem. Imagine if something like std::swap had been a macro.

Regards,
--
Luis Coelho.

Check out my game of Hearts, a card game, for KDE at:

http://www.geocities.com/deepblack9/index.html


      [ about comp.lang.c++.moderated. First time posters: do this! ]

 
 
 

Guru of the Week #77: #Definition

Post by Pete Becke » Sat, 30 Dec 2000 04:50:59



> Type safety.

Depending on your definition of type safety...

void f(short s)
{
cout << s << endl;

Quote:}

f(LONG_MAX);    // displays wrong value on most systems

#define f(x) (cout << x << endl)
f(LONG_MAX);    // displays right value on all systems

--
Pete Becker
Dinkumware, Ltd. (http://www.dinkumware.com)
Contributing Editor, C/C++ Users Journal (http://www.cuj.com)


      [ about comp.lang.c++.moderated. First time posters: do this! ]

 
 
 

Guru of the Week #77: #Definition

Post by Greg » Sun, 31 Dec 2000 02:19:26




[...]

Quote:> > 1. Demonstrate how to write a simple max() preprocessor macro that
> >    takes two arguments and evaluates to the one that is greater,
using
> >    normal < comparison.  What are the usual pitfalls in writing
such a
> >    macro?

Another pitfall: there is no guarantee that operator< will actually
perform the desired comparison.

[...]

Quote:> Herb, mind if I chime in? This is the meat of an upcoming CUJ online
> article. I thought it's kind of interesting.

> ber-Guru Question:

> 3. Write a max() function that does what the max preprocessor macro
does,
> without its usual pitfalls mentioned at point 1.

> Hints: Yes, it's doable. No, it's not a no-brainer :o).

    template <class T>
    inline T& max(T& x, T& y) {
       return x < y ? y : x;
    }

Which doesn't guarantee const-ness. One can overload this version with
a const version, but that will fail to compile with some compilers.

If you only declare the const version, then you can't assign the result
of max() to a non-const variable (at least not without casting.) Also,
you're SOL if the author of the class being compared forgot to declare
operator< as a const method.

The macro version will succeed for:
     max (2L, 3)
but the template version won't.

This version also doesn't guarantee that operator< will actually
perform the desired comparison.

Greg

--
http://homestead.deja.com/user.gmc333/index.html

Sent via Deja.com
http://www.deja.com/


      [ about comp.lang.c++.moderated. First time posters: do this! ]

 
 
 

Guru of the Week #77: #Definition

Post by Luis Pedro Coelh » Sun, 31 Dec 2000 02:20:17



> ber-Guru Question:

> 3. Write a max() function that does what the max preprocessor macro does,
> without its usual pitfalls mentioned at point 1.

> Hints: Yes, it's doable. No, it's not a no-brainer :o).

> Andrei

I am going to give the reply I gave when this came up some time ago in
another thread: overload on return type.

<code>
template <typename T1, typename T2>
struct max_struct {
        max_struct(T1 o1, T2 o2)
                : a(o1), b(o2)
                { }
        T1 operator T1() {
                return a > b ? a : b;
        }
        T2 operator T2() {
                return a > b ? a : b;
        }
        private:
                T1 a;
                T2 b;

Quote:};

template <typename T1, typename T2>
inline max_struct<const T1&,const T2&> max(const T1& a,const T2& b)
{
        return max_struct<const T1&,const T2>(a,b);

Quote:}

template <typename T1, typename T2
inline max_struct<T1&, T2&> max(T1& a, T2& b)
{
        return max_struct<T1&,T2&>(a,b);
Quote:}

</code>

I assume that by usual pitfalls you mean the max(x++,y++) problem, because
I don't think one can reproduce the effects a macro has using a function.

Regards
--
Luis Coelho.

Check out my game of Hearts, a card game, for KDE at:

http://www.geocities.com/deepblack9/index.html


      [ about comp.lang.c++.moderated. First time posters: do this! ]

 
 
 

Guru of the Week #77: #Definition

Post by Scott Blachowic » Sun, 31 Dec 2000 02:44:08




> > Type safety.

> Depending on your definition of type safety...

> void f(short s)
> {
> cout << s << endl;
> }
> f(LONG_MAX);       // displays wrong value on most systems

> #define f(x) (cout << x << endl)
> f(LONG_MAX);       // displays right value on all systems

How about this one:

    template <typename ARGTYPE>
    void f (const ARGTYPE& x)
    {
        cout << x << endl;
    }

type safety & const correctness :-).

--
Scott Blachowicz


      [ about comp.lang.c++.moderated. First time posters: do this! ]

 
 
 

1. Guru of the Week #77

#define max(a, b) ((a) > (b) ? (a) : (b))

This works fine until someone writes max(x++, y), which will cause x to
be incremented either once or twice.

Usual pitfalls are using a macro rather than an inline function in the
first place :-), forgetting the parentheses to avoid problems with
operator precedence, double evaluation of expressions with side effects,
and placing a space between max and the opening bracket.  Other pitfalls
include possible interaction with std::max if someone uses using
namespace std;, debugging the damned things because the compiler sees
something different from what you see (cryptic errors are reported at
the point of use not at the point of definition), and the blatant and
often malevolent disregard for scope.

A preprocessor macro cannot create another preprocessor command.  For
instance, you can't define a macro to include another file, so this
doesn't work because macro expansion happens after the handling of
preprocessor directives (16.3.4.3):

        #define include(x) #include "<" #x ".h>"

A preprocessor macro cannot create a recursive definition because macro
substition is not recursive.  For example:

        #define fact(x) ((x) > 0 ? (x) * fact((x)-1) : 1)
        int x = fact(3);

expands as (16.3.4.2):

        int x = ((3) > 0 ? (3) * fact((3)-1) : 1);

(See Clause 16.3.4 of the Standard [cpp.rescan] for details of macro
rescanning.)

(I could go on ... can't create world peace, because some people insist
on using Java ... etc...)

Hubert


      [ about comp.lang.c++.moderated. First time posters: do this! ]

2. ATM over SMDS

3. Guru of the Week #77: Solution

4. cma_dump.log (How to interpret it ??)

5. Compiler Fortran 77 on WindowsNT platforms or Cross Compiler from SunSolaris2.5 platform to WindowsNT platform

6. Q328020 Redirected Printing Through a Terminal Services Session May Not Work with Windows 2000 SP3

7. DCF-77 driver

8. Wanted: Gateway to Apshai

9. If you use Microsoft FORTRAN 77 on Windows NT...

10. pthread API callable from Fortran 77 ??

11. C/C++ and fortran 77

12. Object Oriented Fortran 77 (long)