• Welcome to Valhalla Legends Archive.
 

Using C++ Macros...

Started by MyndFyre, September 04, 2003, 12:27 PM

Previous topic - Next topic

Camel

Macros can only be icky if you use them with the wrong additude. For example, he could instead do this (and yes I realize how retarded and useless looks):#define SQUARE(x1,x2) ((x1)*(x2))

int i = 0, sum = 0;
while(i < 10)
 sum += SQUARE(++i, i);

This is forward-thinking programming: macros are to make code look more neat, and shouldn't be used just because one is lazy.

MyndFyre

Quote from: Eibro on September 05, 2003, 07:52 PM
Quote from: Camel on September 05, 2003, 05:50 PM
Quote from: Adron on September 05, 2003, 04:24 AM
int i = 0, sum = 0;
while(i < 10)
  sum += SQUARE(++i);

Or, in this case, you could use a for loop.
Honestly, I don't think Adron knew that.
Obviously, the point of his post was to demonstrate the [sometimes] icky behavior of macros.

So then...  let me make sure I understand what happens...

A #define statement replaces all instances of a symbol (the one following #define) with the symbol or statement following it....

An inline function has its code injected by the compiler wherever it is called....

And a const variable is stored and referenced in memory, whereas the #define macro for variables replaces the symbol (again) with the actual value.

[Conjecture]So then, is a const variable held in the data segment of memory, while the macro, because the preprocessor makes it part of the actual instruction, is held in the code segment of memory?[/Conjecture]

Sorry, I'm just now also taking my first Assembly class, and I'm trying to get things figured out...
QuoteEvery generation of humans believed it had all the answers it needed, except for a few mysteries they assumed would be solved at any moment. And they all believed their ancestors were simplistic and deluded. What are the odds that you are the first generation of humans who will understand reality?

After 3 years, it's on the horizon.  The new JinxBot, and BN#, the managed Battle.net Client library.

Quote from: chyea on January 16, 2009, 05:05 PM
You've just located global warming.

Adron

Quote from: Myndfyre on September 06, 2003, 01:36 AM
[Conjecture]So then, is a const variable held in the data segment of memory, while the macro, because the preprocessor makes it part of the actual instruction, is held in the code segment of memory?[/Conjecture]

Yes, that is the idea.


#define SOME_VALUE 47
    a = SOME_VALUE;


should assemble into


    mov a, 47


while


    const int SOME_VALUE = 47;
    a = SOME_VALUE;


should assemble into


    .const
SOME_VALUE dd 47
    .code
    mov a, SOME_VALUE


As you can see, the const case produces a symbol. I'm not sure that #define's ever do. That's why I think consts may be better when debugging. Someone more familiar with #define's should clarify whether they can be made to show up in pdb files.

Note that when optimized, both well may come out the same. I think that applies when the const is defined in the same source file as it is used, and the const is not available to other files.



Adron

#18
Quote from: Camel on September 05, 2003, 05:50 PM
Quote from: Adron on September 05, 2003, 04:24 AM
int i = 0, sum = 0;
while(i < 10)
  sum += SQUARE(++i);

Or, in this case, you could use a for loop.

The smallest change I can think of to fix this is:


int i = 0, sum = 0;
while(i++ < 10)
  sum += SQUARE(i);


Anyway, it's not really a problem when you write the macros yourself. What you should look out for is if there are macros included in your compiler's header files, that might be referencing arguments twice. I'm not sure if there are any in the mostly used compilers?

Kp

Quote from: Myndfyre on September 06, 2003, 01:36 AM
And a const variable is stored and referenced in memory, whereas the #define macro for variables replaces the symbol (again) with the actual value.

[Conjecture]So then, is a const variable held in the data segment of memory, while the macro, because the preprocessor makes it part of the actual instruction, is held in the code segment of memory?[/Conjecture]
At least in gcc with optimizations on, a static const will not be emitted in its own right unless a need for it exists.  That is, if you only ever read from it (but don't try to take its address), it won't be emitted since the static keyword blocks it from being accessible in other files, and thus we can know they won't try to take its address because they can't see it at all.  If its address is taken, it must be emitted - otherwise what address are you taking? :)

As was pointed out earlier in the thread, this is one of the big uses of consts over preprocessor symbols: their address can be taken.  To elaborate a bit on how the #define handling works, that replacement takes place before the C compiler attempts to parse it.  So in the code example where you #define a value to 47, then set a from that symbol, the compiler actually reads it as though you'd just written "a = 47;" directly.  Of course, using a preprocessor macro (or a const) for magic numbers is generally a good thing since you can update all the instances of the magic number by updating one (or a few) places and recompiling.

The behavior of emission suppression is dependent upon your compiler's optimizer - I haven't looked to see whether MS C compilers do this, but I'd expect them to (it's a good optimization).
[19:20:23] (BotNet) <[vL]Kp> Any idiot can make a bot with CSB, and many do!

Adron

What about getting consts/defines as symbols into the debugger though? Does anyone know anything about that?

Skywing

Quote from: Adron on September 06, 2003, 06:51 AM
The smallest change I can think of to fix this is:


int i = 0, sum = 0;
while(i++ < 10)
  sum += SQUARE(i);


Anyway, it's not really a problem when you write the macros yourself. What you should look out for is if there are macros included in your compiler's header files, that might be referencing arguments twice. I'm not sure if there are any in the mostly used compilers?

That's a real problem with the WinDDK.  A lot of the "functions" documented are really implemented as macros.  Even more evil is that some of them expand to compound statements, so if you do code like: if(something) SomeFunction(); you'll be wondering why really weird things start happening.

Oneys book points out a few of these "functions" that you should watch out for.

Camel

#22
Quote from: Skywing on September 06, 2003, 07:39 PMThat's a real problem with the WinDDK.  A lot of the "functions" documented are really implemented as macros.  Even more evil is that some of them expand to compound statements, so if you do code like: if(something) SomeFunction(); you'll be wondering why really weird things start happening.

Oneys book points out a few of these "functions" that you should watch out for.

That's why I encourage people to always use braces, even if there is only one statement. if(something) { SomeFunction(); }

Even better, if the macro doesn't need to return anything: #define SomeFunction() { ... }

Adron

Quote from: Camel on September 06, 2003, 08:44 PM
Even better, if the macro doesn't need to return anything: #define SomeFunction() { ... }

Actually, a macro that is multiple lines can't really be used in an expression anyway, so if it is multiple lines you should always wrap it in braces. Dunno why Microsoft doesn't.

Yoni

The "best" practice for multiline/multi-statement macros that don't return a value is the following:

#define SomeFunction() do { ... } while(0)

It's better than using just braces because of a situation or two that I can't seem to recall at the moment...

Why Microsoft doesn't do this in the DDK? Just careless laziness, I suppose.

Adron

Quote from: Yoni on September 07, 2003, 08:12 AM
#define SomeFunction() do { ... } while(0)

It's better than using just braces because of a situation or two that I can't seem to recall at the moment...

I don't know the situation, but the difference must be that doing it with do/while, it becomes a statement, terminated by a semicolon. If you only surround it with braces, you don't actually need the semicolon, and maybe the compiler would be confused by something like:



if(expression)
{ function1(); function2(); } ;
else
 something();


Kp

Quote from: Adron on September 07, 2003, 09:16 AM
Quote from: Yoni on September 07, 2003, 08:12 AM
#define SomeFunction() do { ... } while(0)
It's better than using just braces because of a situation or two that I can't seem to recall at the moment...
I don't know the situation, but the difference must be that doing it with do/while, it becomes a statement, terminated by a semicolon. If you only surround it with braces, you don't actually need the semicolon, and maybe the compiler would be confused by something like:
if(expression)
{ function1(); function2(); } ;
else
 something();
If I remember my parsing rules correctly, the code you provide Adron should fail with a complaint about unattached else.  Compiler sees that semicolon as a null statement, which ends the if/elseif tree right there.  It then encounters an else without any if statements backlogged, and issues a parse error.  (I just confirmed this with gcc too.)

Also, as an amusing quirk, has anyone else noticed that MS's optimizer sucks at recognizing constants in expressions?  Several times I've seen it compile a do { ... } while(0); loop such that the end of the loop is:xor eax, eax
test eax, eax
jne loop_top
Similarly, I've seen it compile while(1) loops into code that sets a register to one, tests it to itself, and jumps past the loop if the result is zero.
[19:20:23] (BotNet) <[vL]Kp> Any idiot can make a bot with CSB, and many do!

Adron

Quote from: Kp on September 07, 2003, 11:14 AM
Also, as an amusing quirk, has anyone else noticed that MS's optimizer sucks at recognizing constants in expressions?  Several times I've seen it compile a do { ... } while(0); loop such that the end of the loop is:xor eax, eax
test eax, eax
jne loop_top
Similarly, I've seen it compile while(1) loops into code that sets a register to one, tests it to itself, and jumps past the loop if the result is zero.

And that is with optimizations? I could understand it for a debug build, but not for release mode...

Skywing

#28
Quote from: Adron on September 07, 2003, 10:46 PM
Quote from: Kp on September 07, 2003, 11:14 AM
Also, as an amusing quirk, has anyone else noticed that MS's optimizer sucks at recognizing constants in expressions?  Several times I've seen it compile a do { ... } while(0); loop such that the end of the loop is:xor eax, eax
test eax, eax
jne loop_top
Similarly, I've seen it compile while(1) loops into code that sets a register to one, tests it to itself, and jumps past the loop if the result is zero.

And that is with optimizations? I could understand it for a debug build, but not for release mode...
I saw a section in Starcraft.exe which had what looked like an if(0){} block compiled in.  Basically, it zeroed a register and then right after did a comparison against it (no jumps to in between).