Is it better to use a macro or a function?

T

Tejas Kokje

Mark said:
Registers.
This is a very common optimisation.

Which ones for x86 architecture ? Also what if you want to pass
arguments ? Will they be stored in registers as well ?
Sure, but you agree that an inline function would not have a
stackframe.

I don't know how inline functions are implemented by compilers. But I
would be surprised if compiler inlined a function without me telling it
to do so (-O, -O2 etc for gcc).

Tejas Kokje
 
J

Joe Wright

Ben said:
Some tasks can only be accomplished with a macro. Otherwise, use
a function: in general, they're safer.

Ok, I'll bite. Why is a function safer than a function-like macro?
 
L

Lew Pitcher

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

Joe said:
Ok, I'll bite. Why is a function safer than a function-like macro?

One reason (not necessarily the reason Ben was thinking of) is that
side-effects can be contained when you use a function.

Think of what
foo(getchar())
might do, if foo() were
#define foo(x) (x+x)
versus
int foo(int x) { return x+x; }



- --
Lew Pitcher

Master Codewright & JOAT-in-training | Registered Linux User #112576
http://pitcher.digitalfreehold.ca/ | GPG public key available by request
- ---------- Slackware - Because I know what I'm doing. ------


-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.7 (GNU/Linux)
Comment: Armoured with GnuPG

iD8DBQFGZ2MIagVFX4UWr64RArcFAKDWk/uzLIRvMKniYVa0Rkk1VQaflwCdG+ML
Rbrs0LpddqmfFLY7ikPCSv4=
=pBKk
-----END PGP SIGNATURE-----
 
B

Ben Pfaff

Lew Pitcher said:
One reason (not necessarily the reason Ben was thinking of) is that
side-effects can be contained when you use a function.

Also, functions provide a higher degree of type-safety.
 
C

Chris Dollin

Tejas said:
Chris Dollin wrote:

I may be going off topic, but which architecture has R14 register ?

Several have /an/ R14, but the R14 I have in mind is that of the ARM.
Is it a general purpose register ?

Yes. Apart from having the return address stuffed into it on
a call, it's a plain ordinary register, AFAIR.

(As opposed to R15, the PC, which has some careful restrictions
on its use -- but is still a register.)
I don't think it is IA32.

Neither do I.
 
J

James Dow Allen

One reason (not necessarily the reason Ben was thinking of) is that
side-effects can be contained when you use a function.

Historical tidbit: The way SunOS 3.5 calculated date
involved a "month++" or some such that was passed to a macro.
IIRC it failed March 1, 1989 after working fine for
over a year! (Programmers: please test date-dependent
routines for more than just today's date!)

I was working at a Sun lab late on Feb. 28 1989 and
noticed worried messages appearing on Usenet from
Sun machines in Australia. (IIRC the messages were
posted to sun.whatever, not comp.sun.whatever, so
probably aren't in archive.)

I agree with the comment downthread that spoons are
preferable to saws when both do the job. But there
are instances where (sometimes very) complicated macros
*are* just the right solution to a programming problem.

For some reason the *correct* reason to use macros
reminds me of these quotations:
The present letter is a very long one, simply because
I had no leisure to make it shorter.
- Blaise Pascal (1623-62)
I didn't have time to write a short letter, so I wrote
a long one instead.
- Mark Twain (1835-1910)

jamesdowallen at gmail
 
F

Flash Gordon

Tejas Kokje wrote, On 06/06/07 23:52:
Which ones for x86 architecture ? Also what if you want to pass
arguments ? Will they be stored in registers as well ?

Try reading the documentation for a few compilers. You will often find
it tells you what optimisations are done.
I don't know how inline functions are implemented by compilers. But I
would be surprised if compiler inlined a function without me telling it
to do so (-O, -O2 etc for gcc).

Be surprised then. Also try reading the documentation instead of assuming.
 
J

Johan Bengtsson

Ben said:
Some tasks can only be accomplished with a macro. Otherwise, use
a function: in general, they're safer.

An example:
I have in some of my programs a lot of pointers to structs.

struct a
{
char *s1;
char *s2
} *b;

char *s;

A general problem then is that such a pointer might be NULL and
therefore referencing a member of that struct is illegal.

s=b->s1; /*if b is NULL this doesn't work well...*/

Now if what I want is something like this:
if (b)
s=b->s1;
else
s=NULL;

That is I want to assign *something* to s, if b is NULL (or for that
matter b->s1 is NULL) I do want s to be NULL too. This could be written:
s=b?b->s1:NULL;

That's OK, so know if b isn't really a variable but rather a long
complex expression I need to type that twice to get the desired effect.

#define spt(ptr)(!(ptr))?NULL:ptr

would make it possible to write
s=spt(b)->s1;

That trick is *not* possible with a function!
Oh, and before someone points it out, it is not entirely safe either, an
expression in the place of b with side effects would not do what is
intended! But it have helped me write some shorter and more (IMO)
readable code. The expression b is evaluated twice if it is non-NULL,
but as long as it doesn't have side effects and there is no speed
requirements or the compiler is good enough at optimizing that doesn't
matter.

It is even possible to chain them like this (obviously with some other
declarations necessary)
res=spt(spt(spt(ptr)->member)->member)->member;

This allows for every step to be NULL and expands to (line breaks
inserted for readability):
res=(!((!((!(ptr))?NULL:ptr->member))?NULL:(!(ptr))?NULL:
ptr->member->member))?NULL:(!((!(ptr))?NULL:ptr->member))?NULL:
(!(ptr))?NULL:ptr->member->member->member;

and doing exactly what I want but much more readable with the macro
than without. Of course typing
if (ptr)
{
if (ptr->member)
{
if (ptr->member->member)
res=ptr->member->member->member;
else
res=NULL;
}
else
res=NULL;
}
else
res=NULL;

would also produce the same result but would not (again IMO) be as readable.

Oh, once again, don't try this at home unless you understand the
restriction "expression *must* be without side effects"
 
B

Ben Bacarisse

Johan Bengtsson said:
An example:
I have in some of my programs a lot of pointers to structs.

struct a
{
char *s1;
char *s2
} *b;

char *s;

A general problem then is that such a pointer might be NULL and
therefore referencing a member of that struct is illegal.

s=b->s1; /*if b is NULL this doesn't work well...*/

Now if what I want is something like this:
if (b)
s=b->s1;
else
s=NULL;

That is I want to assign *something* to s, if b is NULL (or for that
matter b->s1 is NULL) I do want s to be NULL too. This could be
written:
s=b?b->s1:NULL;

That's OK, so know if b isn't really a variable but rather a long
complex expression I need to type that twice to get the desired
effect.

#define spt(ptr)(!(ptr))?NULL:ptr

would make it possible to write
s=spt(b)->s1;

That trick is *not* possible with a function!

Did you consider:

struct a *spt(struct a *b)
{
static struct a null_a = { NULL, NULL };
return b ? b : &null_a;
}


Sure, you need one for each structure type, that is one thing macros
are *really* good for. You get more type safety, and when the time
comes (as it always does on my programs) you have somewhere to put
your printfs!
 
C

Clark Cox

This makes spt(ptr) equivalent to ptr.

Not exactly
No, it wouldn't.


Yes it does. After preprocessing:

s = spt(b)->s1;

becomes:

s = (!(b))?NULL:b->s1;

which, assuming that the evaluation of b has no side effects, is
functionally equivalent to :


if(b)
s = b->s1;
else
s = NULL;
 
J

Johan Bengtsson

Ben said:
Did you consider:

struct a *spt(struct a *b)
{
static struct a null_a = { NULL, NULL };
return b ? b : &null_a;
}


Sure, you need one for each structure type, that is one thing macros
are *really* good for. You get more type safety, and when the time
comes (as it always does on my programs) you have somewhere to put
your printfs!
I might be dumb, but I don't see how that solves the same thing as the
macro.

I repeat myself...

#define spt(ptr) (!(ptr))?NULL:ptr
a=spt(b)->c;
expands to
a=(!(ptr))?NULL:ptr->c;

now if ptr is not NULL: (normal case)
a=ptr->c;
and if ptr is NULL:
a=NULL;

You see? the ->c part "disappears" because of the macro even if it is
outside what looks like the call, and that is the code that might
actually crash the program.

And the part about type safety, well I would probably need hundreds (if
not thousands) of those functions in that project... (I could write it
so I have one function for each struct *and* each member of that struct,
but that would be a *lot* of functions)

And the speed would suffer too I think... I guess (but have not
verified) that the speed difference (on most systems with fairly good
optimizing compilers) with the macro (vs not having any checks at all)
would be that of the two assembler instructions needed to compare the
pointer to NULL and then make a conditional jump.
 
B

Ben Bacarisse

Johan Bengtsson said:
I might be dumb, but I don't see how that solves the same thing as the
macro.

You are right it does something different, but unless I missed what
you were doing, the important part is dealing simply with a null pointer.
I repeat myself...

#define spt(ptr) (!(ptr))?NULL:ptr
a=spt(b)->c;
expands to
a=(!(ptr))?NULL:ptr->c;

now if ptr is not NULL: (normal case)
a=ptr->c;
and if ptr is NULL:
a=NULL;

You see? the ->c part "disappears" because of the macro even if it is
outside what looks like the call, and that is the code that might
actually crash the program.

Yes, but making the "->c" disappear did not seem to me to be the key
part of what you wanted. I was suggesting a way to make spt(b)->c
always valid (indeed the function makes spt(b) always valid).
And the part about type safety, well I would probably need hundreds
(if not thousands) of those functions in that project... (I could
write it so I have one function for each struct *and* each member of
that struct, but that would be a *lot* of functions)

I don't follow this. You do need one per struct, but why for each
member?

It is not my purpose to start a style war (that's why I just said
"have you considered..."). I tend not to worry about shaving a few
instructions off a piece of code, but I do worry about syntactically
fragile macros.
 
J

Johan Bengtsson

Ben said:
Yes, but making the "->c" disappear did not seem to me to be the key
part of what you wanted. I was suggesting a way to make spt(b)->c
always valid (indeed the function makes spt(b) always valid).
Oh, sorry, but that was exactly the point. I do think we agree at the
end then...
I don't follow this. You do need one per struct, but why for each
member?
Umm, probably because you missed my main point (the handling of the last
->member part).
It is not my purpose to start a style war (that's why I just said
"have you considered..."). I tend not to worry about shaving a few
instructions off a piece of code, but I do worry about syntactically
fragile macros.
It's ok with me, I do accept your suggestion but it didn't solve the
same thing as the macro (because I somehow didn't get my point thru to you).

I think I basically do agree with you, sometimes a function (probably
preferably an inline function so the optimizer might do some additional
tricks) is the right tool for the job and sometimes a macro is.

inline double squaredouble(double a) { return a*a; }
would be preferable to
#define square(a) ((a)*(a))
 
B

Ben Bacarisse

Johan Bengtsson said:
Oh, sorry, but that was exactly the point. I do think we agree at the
end then...

No, I think we are have a sound and reasonable disagreement! I did
not miss the point about the ->c part "dropping off" when the macro
is used. I was suggesting that it *might* be better to let go of that
objective.

I (and I am sure I am not alone) have an aversion to macros that
silently miss-behave when combined with C's syntax rules. In your
case, for example, sptb(b)->c is (silently) quite different to
"(spt(b))->c" and "(*spt(b)).c" and the effect of expressions like
"spt(*pp)->c" and "spt(b)->c || f(x)" are best described as
"interesting".

I have no delusions of persuading you on this point. I am sure you
will say that all intelligent programmers must beware of such
pitfalls, and only a fool would write "spt(*pp)->c" and expect it to
mean (roughly) the same as "v = *pp; spt(v)->c". You may be right,
but there is often merit in making your code "fool"-proof.
 
J

Johan Bengtsson

Ben said:
No, I think we are have a sound and reasonable disagreement! I did
not miss the point about the ->c part "dropping off" when the macro
is used. I was suggesting that it *might* be better to let go of that
objective.
Umm, If your function is used the program will most surely crash for
those cases where it would make any difference to have the function
compared to not have it, it is pointless but the macro isn't.
I (and I am sure I am not alone) have an aversion to macros that
silently miss-behave when combined with C's syntax rules. In your
case, for example, sptb(b)->c is (silently) quite different to
"(spt(b))->c" and "(*spt(b)).c" and the effect of expressions like
"spt(*pp)->c" and "spt(b)->c || f(x)" are best described as
"interesting".
Oh, I do definitely not agree with you there, that's fine however - in
this case it is a genuine disagreement and can be left as one.
I have no delusions of persuading you on this point. I am sure you
will say that all intelligent programmers must beware of such
pitfalls, and only a fool would write "spt(*pp)->c" and expect it to
mean (roughly) the same as "v = *pp; spt(v)->c". You may be right,
but there is often merit in making your code "fool"-proof.
Agreed, there is however also a point in making it readable and it those
two goals are in conflict - what to pick? Well that is a thing to have
as much disagreement as wanted.

a=spt(*pp)->c;

would mean exactly the same as

v=*pp;
a=spt(v)->c;

just typing
spt(*pp)->c;
would be quite pointless (excuse me for the joke) since it doesn't do
anything and have no side effects. Did you intend this: spt(*pp++)->c;
Because then I do agree that it is dangerous and your point is valid,
your solution, however, isn't.
 

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

No members online now.

Forum statistics

Threads
474,433
Messages
2,571,683
Members
48,796
Latest member
Greg L.

Latest Threads

Top