Declaring war on macros

J

JKop

const unsigned short int AmountHumanAdultTeeth = 32;

inline unsigned short int AddFive(unsigned short int Numbr)
{
return Numbr + 5;
}


USE THESE!!


I've heard a stupid argument before that:

#define AmountHumanAdultTeeth 32


is better because it saves memory, ie. there's no variable declared which
takes up memory.

Enlightenment time:

Upon execution, the ENTIRE program is loaded into memory, all the code for
all of the functions, all of the numbers and strings littered throughout the
code, eg.:

cout << "Hello";


are all loaded into memory. Thus, somewhere in memory you will have "Hello".

Thus, concordantly and therefore, you most certainly _are_ taking up memory
by using a macro instead of a const global variable. Furthermore, you may
even use MORE memory with a macro; For example, if you have the number "5"
littered throughout your program, you're not necessarily guaranteed that all
of the "5"'s will be taken from the same place in memory! There may be a
unique "5" in memory for every single seperate time you use it. Const global
variables are superior in EVERY WAY, BAR NONE.

As for macro functions: Similarly, there is absolutley NO JUSTIFICATION for
their use. Inline functions are superior in every way, BAR NONE!
 
R

Rolf Magnus

JKop said:
const unsigned short int AmountHumanAdultTeeth = 32;

inline unsigned short int AddFive(unsigned short int Numbr)
{
return Numbr + 5;
}


USE THESE!!


I've heard a stupid argument before that:

#define AmountHumanAdultTeeth 32


is better because it saves memory, ie. there's no variable declared
which takes up memory.

Enlightenment time:

Upon execution, the ENTIRE program is loaded into memory, all the code
for all of the functions, all of the numbers and strings littered
throughout the code, eg.:

cout << "Hello";


are all loaded into memory. Thus, somewhere in memory you will have
"Hello".

Thus, concordantly and therefore, you most certainly _are_ taking up
memory by using a macro instead of a const global variable.
Furthermore, you may even use MORE memory with a macro; For example,
if you have the number "5" littered throughout your program, you're
not necessarily guaranteed that all of the "5"'s will be taken from
the same place in memory! There may be a unique "5" in memory for
every single seperate time you use it.

Actually, they might not even be taken from memory at all, or let's
better say from data memory. If you have a variable, it resides in
memory somewhere, and the compiler might need to create an instruction
that loads it from there. On assembler level that means there is an
instruction that says something like "load the value from memory
address 0xdeadbeef". If it's a macro, the literal 5 is usually directly
included in the instruction on assembler level, so the assembler
instruction would say "load the value 0x00000005". As you can see, the
instructions can be (and often are) of the same size, but in the first
case, you additionally need a memory location to save the value to (in
my example at address 0xdeadbeef). However, a constant can be used
"inline", just like the macro, so any decent compiler can also directly
put the literal 5 into the assembler instruction unless you try to take
its address, in which case the compiler needs to put it into data
memory to ensure it actually has an address. Since constants have
internal linkage by default, it would mean that each translation unit
that needs the address of the constant would need to store its own copy
of it in memory.

Anyway, in most cases, it's just not worth thinking about whether your
program will need a few bytes more. There are lots of much more
important reasons to use constants instead of macros.
Const global variables are superior in EVERY WAY, BAR NONE.

Some would say "global variables are evil".
As for macro functions: Similarly, there is absolutley NO
JUSTIFICATION for their use. Inline functions are superior in every
way, BAR NONE!

Right.
 
T

tom_usenet

As for macro functions: Similarly, there is absolutley NO JUSTIFICATION for
their use. Inline functions are superior in every way, BAR NONE!

Rolf's commented on the rest of it, but I take slight issue with the
above. Inline functions are superior only when it is possible to write
an inline function to do what the macro was doing. In many cases it
isn't. (For an example of advanced usage of preprocessor macros, check
out the boost library at www.boost.org)

Tom
 
M

Michiel Salters

JKop said:
As for macro functions: Similarly, there is absolutley NO JUSTIFICATION for
their use. Inline functions are superior in every way, BAR NONE!

inline int BYTES_NEEDED_FOR_INTS ( int no_ints )
{
return no_ints * sizeof(int);
}
typedef char[ BYTES_NEEDED_FOR_INTS( 5 ) ] int5;

Does your compiler accept this?

Regards,
Michiel Salters
 
J

Julie

JKop wrote:
[snip stuff that I don't want to comment on]
As for macro functions: Similarly, there is absolutley NO JUSTIFICATION for
their use. Inline functions are superior in every way, BAR NONE!

Please describe how to implement the (standard library) offsetof macro in terms
of an inline function that is 'far superior' (or at least equally inferior!).
 
J

Julie

JKop said:
const unsigned short int AmountHumanAdultTeeth = 32;

inline unsigned short int AddFive(unsigned short int Numbr)
{
return Numbr + 5;
}

USE THESE!!

I've heard a stupid argument before that:

#define AmountHumanAdultTeeth 32

is better because it saves memory, ie. there's no variable declared which
takes up memory.

Enlightenment time:

Upon execution, the ENTIRE program is loaded into memory, all the code for
all of the functions, all of the numbers and strings littered throughout the
code, eg.:

cout << "Hello";

are all loaded into memory. Thus, somewhere in memory you will have "Hello".

Thus, concordantly and therefore, you most certainly _are_ taking up memory
by using a macro instead of a const global variable. Furthermore, you may
even use MORE memory with a macro; For example, if you have the number "5"
littered throughout your program, you're not necessarily guaranteed that all
of the "5"'s will be taken from the same place in memory! There may be a
unique "5" in memory for every single seperate time you use it. Const global
variables are superior in EVERY WAY, BAR NONE.

As for macro functions: Similarly, there is absolutley NO JUSTIFICATION for
their use. Inline functions are superior in every way, BAR NONE!

Do you consider:

#if defined (CODE_1)
// some code
#else if defined (CODE_2)
// other code
#endif

the use of macros?
 
S

Siemel Naran

Rolf Magnus said:
JKop wrote:

Actually, they might not even be taken from memory at all, or let's
better say from data memory. If you have a variable, it resides in
memory somewhere, and the compiler might need to create an instruction
that loads it from there. On assembler level that means there is an
instruction that says something like "load the value from memory
address 0xdeadbeef". If it's a macro, the literal 5 is usually directly
included in the instruction on assembler level, so the assembler
instruction would say "load the value 0x00000005". As you can see, the
instructions can be (and often are) of the same size, but in the first
case, you additionally need a memory location to save the value to (in
my example at address 0xdeadbeef). However, a constant can be used
"inline", just like the macro, so any decent compiler can also directly
put the literal 5 into the assembler instruction unless you try to take
its address, in which case the compiler needs to put it into data
memory to ensure it actually has an address. Since constants have
internal linkage by default, it would mean that each translation unit
that needs the address of the constant would need to store its own copy
of it in memory.

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



const double pi=3.14159265358979323846;


namespace physics
{

const double hbar=1.054572590e-34; // Joule*second
const double hbarsquared=1.112123347e-68; // (Joule*second)^2
const double boltzman=1.380658000e-23; // Joule/Kelvin
const double elemcharge=1.602177330e-19; // Coulomb
const double lightspeed=299792458.0; // meter/second
const double dielectric0=8.854187817e-12; // Coulomb^2/Newton/meter^2
const double electron_mass=9.1093897e-31; // kilogram

}

How come Superman could stop bullets with his chest,
but always ducked when someone threw a gun at him?

Good question.
 
S

Siemel Naran

Julie said:
JKop wrote:

Do you consider:

#if defined (CODE_1)
// some code
#else if defined (CODE_2)
// other code
#endif

the use of macros?

Don't know about JKop, but this seems fine to me. How else to have code
compiled for Windows only, or Borland only, or debug mode only?
 
S

Siemel Naran

Julie said:
JKop wrote:

Please describe how to implement the (standard library) offsetof macro in terms
of an inline function that is 'far superior' (or at least equally
inferior!).

Use the C++ pointer to member mechanism. The offsetof is only for C.
 
S

Siemel Naran

Michiel Salters said:
As for macro functions: Similarly, there is absolutley NO JUSTIFICATION for
their use. Inline functions are superior in every way, BAR NONE!

inline int BYTES_NEEDED_FOR_INTS ( int no_ints )
{
return no_ints * sizeof(int);
}
typedef char[ BYTES_NEEDED_FOR_INTS( 5 ) ] int5;

Does your compiler accept this?

BTW, is int5 above misplaced?

Anyway, that should be rewritten using structs and static const so that you
can use compile time constants. So basically I agree with JKop.

template <size_t N>
struct bytes_needed_for_ints
{
static const size_t result = N * sizeof(int);
};

typedef char int5[ BYTES_NEEDED_FOR_INTS<5>::result ];
 
B

Bill Seurer

Siemel said:
Don't know about JKop, but this seems fine to me. How else to have code
compiled for Windows only, or Borland only, or debug mode only?

if (platformSupportsXYZ()) {
. . .
} else {
. . .
}

Where platformSupportsXYZ() is a function that returns a const bool
value depending on whether the platform suports that feature (or
whatever). Just switch platforms when you, err switch platforms.
Optimization will eliminate the dead code.
 
X

Xenos

Bill Seurer said:
if (platformSupportsXYZ()) {
. . .
} else {
. . .
}

Where platformSupportsXYZ() is a function that returns a const bool
value depending on whether the platform suports that feature (or
whatever). Just switch platforms when you, err switch platforms.
Optimization will eliminate the dead code.

How does this help you with definitions that are platform specific? Are you
going to define all the definitions from the headers of platforms' that
aren't being compiled for? And then creates stubs for all the functions?
And what about #include headers that aren't present because you are not
compiling for that system? What about all the compiler specific bugs,
quirks, etc.
 
J

Julie

Siemel said:
inferior!).

Use the C++ pointer to member mechanism. The offsetof is only for C.

You can't say that offsetof is only for C, the macro exists in C++ (as well).

I've used offsetof in the past when packing/unpacking structure data into a
binary stream for serialization. Pointer-to-member has no meaning in this
context.
 
J

Jacek Dziedzic

JKop said:
[some anti-preprocessor hate rant]

I agree that things like
#define pi 3.14159265
are better expressed as
const double pi=3.13159265

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
}

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.
 
J

Jacek Dziedzic

Bill said:
if (platformSupportsXYZ()) {
. . .
} else {
. . .
}

Where platformSupportsXYZ() is a function that returns a const bool
value depending on whether the platform suports that feature (or
whatever). Just switch platforms when you, err switch platforms.
Optimization will eliminate the dead code.

and how are you going to translate things like

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

into the platformSupportsXYZ() lingo?

I'd stick to "macros are evil so use them only when you have to"
rule.

- J.
 
A

Alf P. Steinbach

* Rolf Magnus said:

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.

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?
 
J

Julie

Alf P. Steinbach said:
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.

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?

Personally, I don't know what seniority has to do w/ anything relating to
anything at all. I'd be hard-pressed to find a term that is more abused,
subjective, and downright useless, but that is just my experience in being a
developer for 15+ years at numerous companies...

How about something a little more practical *and* beneficial to the staff
(especially non-'senior' staff) such as: reserved for those that have a
(fairly?) complete understanding of the preprocessor and have thoroughly
studied (and been tested on?) the project/team notes, comments, references, and
dissection of the preprocessor (or specifically macros).

Honestly (digressing from Alf's response), I feel that these types of threads
such as "NO JUSTIFICATION for [macros]" serves absolutely no one. How about a
complete and unbiased dissection of the preprocessor in all of its glory (or
lack thereof), compared and contrasted w/ potential substitute C++ (and C)
constructs and idioms so that people can actually *LEARN* something of value,
other than people's individual agendas or senseless blather. If there is such
a dissection of the preprocessor, I'd love to hear about it and read it, but to
date, I've never ran across anything other than topical/cursor examinations of
the preprocessor, and most conclude w/ the same "don't use it" result, rather
than leaving that determination up to the user.
 
J

Jack Klein

const unsigned short int AmountHumanAdultTeeth = 32;
[snip]

Thus, concordantly and therefore, you most certainly _are_ taking up memory
by using a macro instead of a const global variable.

You seem to be confused. The term "global" does not exist in C++.
Assuming you mean external linkage, you are wrong. The definition of
unsigned short int above has either internal or no linkage, depending
on whether its definition is at namespace or block scope.
 
O

Old Wolf

Julie said:
Please describe how to implement the (standard library) offsetof
macro in terms of an inline function that is 'far superior'
(or at least equally inferior!).

This seems to work:

template<typename T, typename S>
size_t offset_of(T S::*p)
{
S *t = 0;
return (char *)&(t->*p) - (char *)t;
}

Example usage:

#include <iostream>
struct s { int a; char b; double c; };
int main(void)
{
std::cout << offset_of(&s::c) << std::endl;
}

Note: If you're going to allow 0->blah in a macro,
you have to allow my example too. Personally I think both are UB.
 
S

Siemel Naran

Bill Seurer said:
Siemel Naran wrote:

if (platformSupportsXYZ()) {
. . .
} else {
. . .
}

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.
 

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,167
Messages
2,570,913
Members
47,454
Latest member
eibaloja

Latest Threads

Top