Declaring war on macros

S

Siemel Naran

Jacek Dziedzic said:
but that's not all to macros. There are, IMHO, constructs that
don't translate well into inline functions. Consider a macro
like

#define for_all_masses_energies_and_momenta \
for(byte m=Lattice::MIN_MASS;m<=Lattice::MAX_MASS;m++) \
for(byte e=Lattice::MIN_ENERGY;e<=Lattice::MAX_ENERGY;e++) \
for(signed_bytevec p={Lattice::MIN_MOMENTUM_COMPONENT,
-1,-1};p[0]<=Lattice::MAX_MOMENTUM_COMPONENT;p[0]++) \
for(p[1]=Lattice::MIN_MOMENTUM_COMPONENT;
p[1]<=Lattice::MAX_MOMENTUM_COMPONENT;p[1]++) \
for(p[2]=Lattice::MIN_MOMENTUM_COMPONENT;
p[2]<=Lattice::MAX_MOMENTUM_COMPONENT;p[2]++) \

it allows me to write things like

for_all_masses_energies_and_momenta {
cout << m << " " << e << p[0] << p[1] << p[2] << endl
}

It is indeed true that macro functions let you write nice looking code. But
the reason to stay away from them is that they're hard to maintain, don't
offer type safety, and hard to debug, among other reasons.

I think that as long as you're careful about these, remember
what they stand for and are aware of their limitations, they are
much more readable than

for(byte m=Lattice::MIN_MASS;m<=Lattice::MAX_MASS;m++)
for(byte e=Lattice::MIN_ENERGY;e<=Lattice::MAX_ENERGY;e++)
for(signed_bytevec p={Lattice::MIN_MOMENTUM_COMPONENT,
-1,-1};p[0]<=Lattice::MAX_MOMENTUM_COMPONENT;p[0]++)
for(p[1]=Lattice::MIN_MOMENTUM_COMPONENT;
p[1]<=Lattice::MAX_MOMENTUM_COMPONENT;p[1]++)
for(p[2]=Lattice::MIN_MOMENTUM_COMPONENT;
p[2]<=Lattice::MAX_MOMENTUM_COMPONENT;p[2]++)
{
// do whatever
}

even though they use the evil preprocessor.

Apart from that I know of no other mechanism that allows my
programs to compile differently when different options are
passed to the compiler. Thanks to things like
#ifdef PARRALEL
// use MPI
#else
// remain serial
#endif
I can make my program compile to two different things
depending on a compiler option. Can an inline function do
that?

just my $2e-2,
- J.
 
S

Siemel Naran

Alf P. Steinbach said:
* Rolf Magnus <[email protected]> schriebt:
Well, Andrei Alexandrescu did once show what a bullet-proof "max" function
had to be like. Unfortunately I don't have a reference. I just remember
(vaguely) that it was hairy & sort of incomprehensible stuff.

We do have std::max and it works well in most situations, but it's not
perfect.

What's wrong with std::max? Can you dig up the reference?

So I gather that there are situations, e.g. typewise, where a macro function
is the only practical way -- this aside from debugging and such, just
normal straightforward programming. But I think it would be a good idea in
project guidelines to restrict use of macro functions to some seniority
level and above. Huh, why didn't I think of that before?

I don't think macro functions and variables are ever a good idea, unless
you're maintaining legacy code.

Something about your seniority idea bothers me, something along the lines of
allowing those with power to abuse their power. But that's getting OT.
 
D

David Harmon

Very good answer. In my experience it is very rare to use address of a
constant, such as these:

Sure, such as those. But if your template function takes its argument by const reference, then you have to go out of
your way to pass literal rvalues.
 
B

Bill Seurer

Siemel said:
The stuff in ... may be compilable only on one platform. Like in Linux
include ncurses.h and use the functions in there, but in Windows include
conio.h and use the functions there and the function names are totally
different.

You put those into platform specific modules and only compile the ones
for your system. I work on a project (multiple host, multiple platform)
where it is done and it takes a while to get used to it but it can be
done. You might not be able to get rid of all macro stuff but you can
get rid of most of it.

By the way, something else I work on (Xerces/Xalan from Apache) went the
other way. Someone who looked at the code one time described it as
"macros with some C++ thrown in".
 
B

Bill Seurer

Jacek said:
and how are you going to translate things like

#ifdef _WIN32
#include "time.h"
#else
#include "sys/time.h"
#endif

into the platformSupportsXYZ() lingo?

You might not be able to. Or perhaps
#include "time.h"
will work for both if you use the right options on the compiler to point
it at the proper directories for includes.
I'd stick to "macros are evil so use them only when you have to"
rule.

I agree.
 
S

Siemel Naran

Bill Seurer said:
You put those into platform specific modules and only compile the ones
for your system. I work on a project (multiple host, multiple platform)
where it is done and it takes a while to get used to it but it can be
done. You might not be able to get rid of all macro stuff but you can
get rid of most of it.

You could also create a directories like win and unix, and put the platform
specific files into their respective directories.
 
A

Alf P. Steinbach

* "Siemel Naran said:
What's wrong with std::max? Can you dig up the reference?


I don't think macro functions and variables are ever a good idea, unless
you're maintaining legacy code.

Not sure what you mean. A for-loop is one way to create a scope inside
a macro, while leaving semicolon usage in client code intact.

Something about your seniority idea bothers me, something along the lines of
allowing those with power to abuse their power. But that's getting OT.

Uhm, yes. ;-)
 
S

Siemel Naran

Alf P. Steinbach said:
Googled on "Andrei Alexandrescu max" and there it was second in the list,
<url: http://www.cuj.com/documents/s=7996/cujcexp1904alexandr/>.

<Quote>
As Scott notes, adding a second version:
template <class T>
T& min(T& lhs, T& rhs)
{
return lhs < rhs ? lhs : rhs;
}
still won't work satisfactorily because the compiler won't be able to figure
out mixed cases - one const and one non-const argument.
</Quote>

I don't understand this. Why would you call min with one argument const and
the other not. What should min return -- the T& or const T&?



<Quote>
Furthermore, templates don't play nicely with automatic conversion and
promotions, which means that the following code won't compile:

int a;
short int b;
....
int smallest = min(a, b); // error: can't figure out T
// in template instantiation
Needless to say, the macro-based min works nicely again with such
conversions
</Quote>

That's OK usually. Usually I like the type safety std::min.



Incidentally, Andrei lists a traits mechanism by which short + long ==>
long, etc. This is one place where typeof would be nice.
 
A

Alf P. Steinbach

* "Siemel Naran said:
<Quote>
As Scott notes, adding a second version:
template <class T>
T& min(T& lhs, T& rhs)
{
return lhs < rhs ? lhs : rhs;
}
still won't work satisfactorily because the compiler won't be able to figure
out mixed cases - one const and one non-const argument.
</Quote>

I don't understand this. Why would you call min with one argument const and
the other not. What should min return -- the T& or const T&?

The latter. In this case you're not interested in using the result as a
non-const lvalue. Or if that is what is attempted then it is a logic error.

I'm not sure why the compiler shouldn't be able to figure out the overload,
though.

But then all that interests me is that there are complications, not the
complications themselves as such (my take is: keep things simple).
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
474,168
Messages
2,570,914
Members
47,455
Latest member
Delilah Code

Latest Threads

Top