Is it better to use a macro or a function?

B

Ben Bacarisse

Johan Bengtsson said:
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.

Ah, then if you are right I have misunderstood. Please give me an
example because the function I suggested did not, I think, "crash" if
used in the context you originally posted (s=spt(b)->s1;). Of course,
I often make mistakes, but I've looked again and I don't see what you
mean.
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.
OK!

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;

Yes, that is what I meant. I should have written "mean (roughly) the
same as '(v = *pp), (spt(v)->c)'. In other words I was extolling the
virtue of what is often called referential transparency in
expressions. You've said above that you don't value it, so I will not
push the case any more.
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.

That is not what I meant, but I *could* have written it because it
gives a good example of why a function is so often a safer solution.
spt(*pp++)->c is dangerous when spt is the macro you posted but is
safer (in that it has fewer surprises in what it means) if it is the
function I gave. All this, of course, subject to your first point -- I
can't see the error to which you refer.

[BTW, this thread having gone on longer than I'd expected, I've had
time to reflect that I should have made spt return a "const struct A
*" -- such functions are safer still if they object they return a pointer
to is read-only.]
 
S

Stephen Sprunk

Which ones for x86 architecture ?

x86 doesn't store the return address in a register, but it's possible for
simple leaf functions to not have their own stack frames, just require
callers to push the return address on the stack. Also, the latest AMD and
Intel chips can optimize stack instructions and simple calls so that they're
(nearly?) free.

OTOH, most of the RISCs I'm aware of have a "link" register that holds the
most recent return address. Only non-leaf functions have to push that onto
the stack or save it in another register.
Also what if you want to pass arguments ? Will they be stored in
registers as well ?

Many calling conventions do that; it's not x86's default for any ABI I'm
aware of, though it's usually selectable, and it's the default on many other
architectures (including AMD64).
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).

Any decent compiler set to high optimization levels will inline a function
if it makes sense and the source is available, particularly if you mark it
"inline". They also usually have a variety of knobs you can play with to
fine-tune their choices, but the defaults are tuned so that they'll be
near-optimal in almost all cases. You're more likely to make things worse
than better when you second-guess a modern compiler.

</OT>

Returning to the original question, function-like macros have many gotchas
that trip up novices. They were frequently used to improve performance over
functions back in the days compilers weren't so good, but today the
difference (if any) should be minimal and usually doesn't justify the risk.
Lots of people, however, still use them out of inertia or ignorance, so
don't be surprised when you see them.

Personally, I only use function-like macros when I want to take advantage of
those gotchas, e.g. when I need a function _not_ to be type-safe or to be
able to modify its arguments. If a "static inline" function meets my needs,
that's what I'll use.

S
 
S

Spiro Trikaliotis

Hello,

Stephen said:
x86 doesn't store the return address in a register, but it's possible for
simple leaf functions to not have their own stack frames, just require
callers to push the return address on the stack.

<ot>

Which can be dangerous in some cases. At one point, I was debugging
some code a co-worker had written.

So, the collegue did not generate stack-frame. Now, the machine the code
was running own showed erroneous behaviour, or even crashed. What did
happen? Out of a habbit, he had left out the stack frame, as he was
writing a leaf function. In this case, it was hand-written assembly, but
the problem could have been produced by a compiler if it left out a
stack-frame, too.

The problem here was that this was kernel-mode code, that is, code in
ring 0. The code shared the stack with every other part of the operating
system. Thus, whenever an interrupt occurred, the local variables were
overwritten be the interrupt routine. I think you get the picture.

so, what is the bottom line of this story: Saving some clock ticks by
leaving out the stack frame might seem to be a good idea, but one should
not forget that there are circumstances where this is not a good idea.
IMHO, a compiler should NEVER perform such risky tasks without
specifically being asked to do so.

Any decent compiler set to high optimization levels will inline a function
if it makes sense and the source is available, particularly if you mark it
"inline". They also usually have a variety of knobs you can play with to
fine-tune their choices, but the defaults are tuned so that they'll be
near-optimal in almost all cases. You're more likely to make things worse
than better when you second-guess a modern compiler.

In many cases, you might be right. Sometimes, one is really surprised
how the compiler optimized some code you would never have thought about.

Unfortunately, in many other cases, many "obvious" optimizations are not
done at all.

to summarize, a doubt the choices are near-optimal
_in_almost_all_cases_. They might be very good in many cases, but that
is all.

Of course, it depends if it is worth to play with the knobs in a
particular case. Often, you can live with the default solution without
problems.

Regards,
Spiro.
 
C

Chris Dollin

Spiro said:
Hello,



<ot>

Which can be dangerous in some cases. At one point, I was debugging
some code a co-worker had written.

So, the collegue did not generate stack-frame. Now, the machine the code
was running own showed erroneous behaviour, or even crashed. What did
happen? Out of a habbit, he had left out the stack frame, as he was
writing a leaf function. In this case, it was hand-written assembly, but
the problem could have been produced by a compiler if it left out a
stack-frame, too.

Then it would be called a "bug", and fixed when spotted. Compilers have
lots of places where they can have bugs: leaf optimisation isn't
special here.
so, what is the bottom line of this story: Saving some clock ticks by
leaving out the stack frame might seem to be a good idea, but one should
not forget that there are circumstances where this is not a good idea.
Indeed.

IMHO, a compiler should NEVER perform such risky tasks without
specifically being asked to do so.

It's not "risky" if it's done properly. I think the compiler should
do leaf optimisation /routinely/ -- this gives the best chance for problems
to be detected early.
 
J

Johan Bengtsson

Ben said:
Ah, then if you are right I have misunderstood. Please give me an
example because the function I suggested did not, I think, "crash" if
used in the context you originally posted (s=spt(b)->s1;). Of course,
I often make mistakes, but I've looked again and I don't see what you
mean.

Ahh, sorry, now I see how it works - thanks. You are right that it
should work like that too, I do however like my macro better partly
because it isn't type safe (and can be applied to all types of pointers
to structs without the need to create one for each type). But to answer
your original reply: no I didn't consider that.

<OT>
The fact that I like my macro better than the function is however a
proper disagreement and is partly because I have a quite large number of
structs in the project where this code is found. Besides that it is a
WIN32 program with a large number of dll:s and that would require me to
either export each of those functions from the dll where the struct is
"defined", or create one instance of each such function in each dll.
Yes I know that "define" and structs doesn't really mix well, they are
declared in headers specific to a certain dll and then used in other dll:s.
That is not what I meant, but I *could* have written it because it
gives a good example of why a function is so often a safer solution.
spt(*pp++)->c is dangerous when spt is the macro you posted but is
safer (in that it has fewer surprises in what it means) if it is the
function I gave. All this, of course, subject to your first point -- I
can't see the error to which you refer.
I see that, because the error was mine, thanks!
 
S

Spiro Trikaliotis

Hello Chris,

In my opinion, your are contradicting yourself:

Chris Dollin said:
Then it would be called a "bug", and fixed when spotted. Compilers have
lots of places where they can have bugs: leaf optimisation isn't
special here.

Here, you call that type of optimisation a "bug", while:
It's not "risky" if it's done properly. I think the compiler should
do leaf optimisation /routinely/ -- this gives the best chance for problems
to be detected early.

Here, you tell me that it is not risky if it's done properly.

This optimisation is something done very frequently, But, depending on
the circumstances, it can work perfectly (because a "leaf function" is
really a leaf function, as the environment guarantees that no other
function can interfere with the stack), while in other circumstances
(kernel-mode on Windows or Linux, and, most probably, in many other
cases), the leaf function cannot be guaranteed that it is a leaf
function, as an interrupt outside of the scope of the compiled piece of
code can interfere, using the same stack.

Regards,
Spiro.
 

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
473,772
Messages
2,569,593
Members
45,112
Latest member
VinayKumar Nevatia
Top