Valhalla Legends Forums Archive | C/C++ Programming | Using C++ Macros...

AuthorMessageTime
Myndfyr
Just out of curiousity, why do C++ programmers use macros:
[code]
#define MYVALUE 5
[/code]
instead of using constants:
[code]
const int MyValue = 5;
[/code]

?

Just wondering, thanks!

--Rob
September 4, 2003, 5:27 PM
Eibro
[quote author=Myndfyre link=board=5;threadid=2583;start=0#msg20148 date=1062696462]
Just out of curiousity, why do C++ programmers use macros:
[code]
#define MYVALUE 5
[/code]
instead of using constants:
[code]
const int MyValue = 5;
[/code]

?

Just wondering, thanks!

--Rob
[/quote]I'd say C++ programmers use constants, and C programmers use #defines. At least, that's how it should be. :) constants have many advantages over preprocessor macros.
September 4, 2003, 5:32 PM
Kp
[quote author=Eibro link=board=5;threadid=2583;start=0#msg20149 date=1062696772]
[quote author=Myndfyre link=board=5;threadid=2583;start=0#msg20148 date=1062696462]
Just out of curiousity, why do C++ programmers use macros:[code]#define MYVALUE 5[/code]instead of using constants:
[code]const int MyValue = 5;[/code]?
Just wondering, thanks!
--Rob[/quote]I'd say C++ programmers use constants, and C programmers use #defines. At least, that's how it should be. :) constants have many advantages over preprocessor macros.
[/quote]With a const, you can't do something dangerously useful like alias declfn(X) to be [code]static void X (int my1, char *my2, float my3) /* we can change the signatures of all functions declared with declfn just by updating this macro */[/code]

I've actually never seen a good argument for a const in place of a preprocessor definition. The only excuse I can come up with is "type safety", but that seems unlikely - good symbolic constants are explicitly cast in their expansion or are named such that you'd have to be quite inattentive to use it with the wrong type.

Note that using const as a modifier on pointers is very useful, both for documentation that the callee won't tamper and to have the compiler enforce such when the callee is built.
September 4, 2003, 10:34 PM
Adron
A good reason to use a const is that you can take its address? Or can you?
September 4, 2003, 10:36 PM
Kp
[quote author=Adron link=board=5;threadid=2583;start=0#msg20189 date=1062715019]
A good reason to use a const is that you can take its address? Or can you?[/quote]My intent (though not stated) was why use a const if you are only defining the value of it for use assigning to other objects? That is, assuming you don't plan on ever passing its address, what use is there?

I've grown unaccustomed to trying to pass the addresses of true consts because of some problems I had where various Windows functions failed with EFAULT equivalent when they shouldn't have. Specificially: I requested an operation which would require that it query the value at the address I passed, and do something with it; however, it failed because the OS insisted on having read and write access to the location -- apparently they felt it better to move the check up above whatever dispatcher breaks out read requests from write requests.
September 4, 2003, 10:42 PM
Adron
For basic use, a #define is just fine. If you have a set of constants, I like to make them an enum. Sometimes debuggers handle enums better than #define's.
September 4, 2003, 10:49 PM
Camel
With a #define macro, the compiler (at least in theory) simply replaces the macro name with the macro content upon (?)precompile. So, this: [code]#define mymacro 5
int main()
{
return mymacro;
}[/code] should simply be replaced with: [code]int main()
{
return 5;
}[/code]

If the macro is used multiple times, it will appear as multiple (?)constants -- I hesitate to say constants because I'm not sure if the standard is to treat all literal numbers the same way as constants or not.

A constant on the other hand should be the same as a variable, except it is not intended to be modified. I am unaware of the extent to which this is enforced. The idea being that it is stored as one number post-compile.


Anyways, this is just my undererstanding of how consts work; feel free to correct me. :)
September 4, 2003, 11:36 PM
Myndfyr
I guess the reason I asked is because I'm starting to play around with Visual C++, and when I go to a definition, I always find macro definitions in the header files rather than const definitions.... For example, I recently had to look up the message number of something or another to scroll a window. The problem was, I had to prototype the method in C#, and since I couldn't access the API values, I had to explicitly put the correct value into the function call.

So... when using #define, is the value explicitly put into the locations of the macro, whereas a const doesn't? I'd imagine a smart optimization would explicitly put the values of enums, consts, AND #defines all into the proper place.... I can see from an assembly standpoint where this would make sense....
September 5, 2003, 12:05 AM
Eibro
When you do something like #define PI 3.14f the symbolic name PI will never be seen by compilers. This would get pretty confusing if the number 3.14 started popping up as part of compile errors. Especially if PI was defined in a file which you didn't write.

Preprocessor defines usually take two forms:
#define SOME_CONSTANT 0xFF
#define SOME_FN(X) (X * X)

The first could be replaced by a type-safe and symbolic const, const unsigned char SOME_CONSTANT = 0xff;
The second, could obviously be replaced by an inline function. inline int SOME_FN(int X) { return X * X; }
Another thing to note is the weirdness of something like:
[code]#define SQUARE(X) (X * X)
int result = SQUARE(var + some_other_var); // Or other more complex expressions[/code]
September 5, 2003, 12:44 AM
Kp
[quote author=Eibro link=board=5;threadid=2583;start=0#msg20217 date=1062722647]
When you do something like #define PI 3.14f the symbolic name PI will never be seen by compilers. This would get pretty confusing if the number 3.14 started popping up as part of compile errors.[/quote]Yes. That's one thing I hadn't considered - but it shouldn't be a problem if people don't go polluting the namespace with simplisticly named macros. :)

[quote author=Eibro link=board=5;threadid=2583;start=0#msg20217 date=1062722647]Preprocessor defines usually take two forms:
#define SOME_CONSTANT 0xFF
#define SOME_FN(X) (X * X)

The first could be replaced by a type-safe and symbolic const, const unsigned char SOME_CONSTANT = 0xff;[/quote]Yes. Or you could #define SOME_CONSTANT ((unsigned char) 0xFF). :)

[quote author=Eibro link=board=5;threadid=2583;start=0#msg20217 date=1062722647]The second, could obviously be replaced by an inline function. inline int SOME_FN(int X) { return X * X; }[/quote]Yes. However, as a minor argument, the inline solution doesn't allow for non-int squaring. :)
[quote author=Eibro link=board=5;threadid=2583;start=0#msg20217 date=1062722647]Another thing to note is the weirdness of something like:
[code]#define SQUARE(X) (X * X)
int result = SQUARE(var + some_other_var); // Or other more complex expressions[/code]
[/quote]Yes. That's why you're supposed to be paranoid with parantheses. #define SQUARE(X) ((X)*(X)).
September 5, 2003, 1:20 AM
Eibro
[quote]Yes. Or you could #define SOME_CONSTANT ((unsigned char) 0xFF). [/quote]
Bah, that's what I meant :)
[quote]Yes. However, as a minor argument, the inline solution doesn't allow for non-int squaring. [/quote]This is exactly what templates are for.
[quote]Yes. That's why you're supposed to be paranoid with parantheses. #define SQUARE(X) ((X)*(X)). [/quote]Yes, I suppose anyone that's apt to use macros wouldn't make this mistake anyway.
September 5, 2003, 2:15 AM
Adron
[quote author=Kp link=board=5;threadid=2583;start=0#msg20222 date=1062724828]
[quote author=Eibro link=board=5;threadid=2583;start=0#msg20217 date=1062722647]Another thing to note is the weirdness of something like:
[code]#define SQUARE(X) (X * X)
int result = SQUARE(var + some_other_var); // Or other more complex expressions[/code]
[/quote]Yes. That's why you're supposed to be paranoid with parantheses. #define SQUARE(X) ((X)*(X)).
[/quote]

You also have to be careful with some expressions. Like, producing the sum of all squares from 1 to 10:

[code]
int i = 0, sum = 0;
while(i < 10)
sum += SQUARE(++i);
[/code]

For this reason, I prefer inline functions. Summing squares might seem artifical, but if you instead consider doing something with an array element and having a postincrement for the indexing variable you'll have a much more common case.
September 5, 2003, 9:24 AM
Kp
[quote author=Eibro link=board=5;threadid=2583;start=0#msg20223 date=1062728154]This is exactly what templates are for.
[/quote]I hadn't considered templates for inline functions, but that's a very good use for them. Too bad they don't work in straight C. :p

[quote author=Adron link=board=5;threadid=2583;start=0#msg20236 date=1062753869]
[quote author=Kp link=board=5;threadid=2583;start=0#msg20222 date=1062724828]
Yes. That's why you're supposed to be paranoid with parantheses. #define SQUARE(X) ((X)*(X)).
[/quote]
You also have to be careful with some expressions. Like, producing the sum of all squares from 1 to 10:
[code]
int i = 0, sum = 0;
while(i < 10)
sum += SQUARE(++i);
[/code]

For this reason, I prefer inline functions. Summing squares might seem artifical, but if you instead consider doing something with an array element and having a postincrement for the indexing variable you'll have a much more common case.
[/quote]Yes. I remember both seeing and writing warnings about various macros that if you use the macro, you must not use expressions which have side effects (e.g. pre/post incremenet/decrement) because of exactly the situation you point out.

However, we're getting off topic. :) The original question was why use a const over a preprocessor macro or vice versa.
September 5, 2003, 7:41 PM
Camel
[quote author=Adron link=board=5;threadid=2583;start=0#msg20236 date=1062753869][code]
int i = 0, sum = 0;
while(i < 10)
sum += SQUARE(++i);
[/code][/quote]

Or, in this case, you could use a for loop.
September 5, 2003, 10:50 PM
Eibro
[quote author=Camel link=board=5;threadid=2583;start=0#msg20264 date=1062802237]
[quote author=Adron link=board=5;threadid=2583;start=0#msg20236 date=1062753869][code]
int i = 0, sum = 0;
while(i < 10)
sum += SQUARE(++i);
[/code][/quote]

Or, in this case, you could use a for loop.
[/quote]Honestly, I don't think Adron knew that.
Obviously, the point of his post was to demonstrate the [sometimes] icky behavior of macros.
September 6, 2003, 12:52 AM
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):[code]#define SQUARE(x1,x2) ((x1)*(x2))

int i = 0, sum = 0;
while(i < 10)
sum += SQUARE(++i, i);
[/code]
This is forward-thinking programming: macros are to make code look more neat, and shouldn't be used just because one is lazy.
September 6, 2003, 1:18 AM
Myndfyr
[quote author=Eibro link=board=5;threadid=2583;start=0#msg20279 date=1062809541]
[quote author=Camel link=board=5;threadid=2583;start=0#msg20264 date=1062802237]
[quote author=Adron link=board=5;threadid=2583;start=0#msg20236 date=1062753869][code]
int i = 0, sum = 0;
while(i < 10)
sum += SQUARE(++i);
[/code][/quote]

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

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...
September 6, 2003, 6:36 AM
Adron
[quote author=Myndfyre link=board=5;threadid=2583;start=15#msg20316 date=1062830185]
[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]
[/quote]

Yes, that is the idea.

[code]
#define SOME_VALUE 47
a = SOME_VALUE;
[/code]

should assemble into

[code]
mov a, 47
[/code]

while

[code]
const int SOME_VALUE = 47;
a = SOME_VALUE;
[/code]

should assemble into

[code]
.const
SOME_VALUE dd 47
.code
mov a, SOME_VALUE
[/code]

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.


September 6, 2003, 11:49 AM
Adron
[quote author=Camel link=board=5;threadid=2583;start=0#msg20264 date=1062802237]
[quote author=Adron link=board=5;threadid=2583;start=0#msg20236 date=1062753869][code]
int i = 0, sum = 0;
while(i < 10)
sum += SQUARE(++i);
[/code][/quote]

Or, in this case, you could use a for loop.
[/quote]

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

[code]
int i = 0, sum = 0;
while(i++ < 10)
sum += SQUARE(i);
[/code]

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?
September 6, 2003, 11:51 AM
Kp
[quote author=Myndfyre link=board=5;threadid=2583;start=15#msg20316 date=1062830185]
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][/quote]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).
September 6, 2003, 3:10 PM
Adron
What about getting consts/defines as symbols into the debugger though? Does anyone know anything about that?
September 6, 2003, 3:36 PM
Skywing
[quote author=Adron link=board=5;threadid=2583;start=15#msg20326 date=1062849119]
The smallest change I can think of to fix this is:

[code]
int i = 0, sum = 0;
while(i++ < 10)
sum += SQUARE(i);
[/code]

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?
[/quote]

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.
September 7, 2003, 12:39 AM
Camel
[quote author=Skywing link=board=5;threadid=2583;start=15#msg20395 date=1062895170]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.[/quote]

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

Even better, if the macro doesn't need to return anything: [code]#define SomeFunction() { ... }[/code]
September 7, 2003, 1:44 AM
Adron
[quote author=Camel link=board=5;threadid=2583;start=15#msg20416 date=1062899065]
Even better, if the macro doesn't need to return anything: [code]#define SomeFunction() { ... }[/code]
[/quote]

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.
September 7, 2003, 11:21 AM
Yoni
The "best" practice for multiline/multi-statement macros that don't return a value is the following:

[code]#define SomeFunction() do { ... } while(0)[/code]

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.
September 7, 2003, 1:12 PM
Adron
[quote author=Yoni link=board=5;threadid=2583;start=15#msg20471 date=1062940357]
[code]#define SomeFunction() do { ... } while(0)[/code]

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

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:

[code]

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

[/code]
September 7, 2003, 2:16 PM
Kp
[quote author=Adron link=board=5;threadid=2583;start=15#msg20477 date=1062944173]
[quote author=Yoni link=board=5;threadid=2583;start=15#msg20471 date=1062940357]
[code]#define SomeFunction() do { ... } while(0)[/code]
It's better than using just braces because of a situation or two that I can't seem to recall at the moment...[/quote]
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: [code]
if(expression)
{ function1(); function2(); } ;
else
something();
[/code][/quote]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:[code]xor eax, eax
test eax, eax
jne loop_top[/code]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.
September 7, 2003, 4:14 PM
Adron
[quote author=Kp link=board=5;threadid=2583;start=15#msg20488 date=1062951255]
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:[code]xor eax, eax
test eax, eax
jne loop_top[/code]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.
[/quote]

And that is with optimizations? I could understand it for a debug build, but not for release mode...
September 8, 2003, 3:46 AM
Skywing
[quote author=Adron link=board=5;threadid=2583;start=15#msg20575 date=1062992788]
[quote author=Kp link=board=5;threadid=2583;start=15#msg20488 date=1062951255]
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:[code]xor eax, eax
test eax, eax
jne loop_top[/code]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.
[/quote]

And that is with optimizations? I could understand it for a debug build, but not for release mode...
[/quote]
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).
September 8, 2003, 3:40 PM

Search