View Full Version : How to make global #defines?
forseti
2002.06.04, 10:04 AM
Hi,
I have a definition called kTrace in a class, and it's a boolean value to tell whether or not the program wants debug logs to be printed.
I want it to be recognized by all my classes in the project, not just one. What's the best way to do this?
Thanks.
rangaroek
2002.06.04, 10:34 AM
Originally posted by forseti
Hi,
I have a definition called kTrace in a class, and it's a boolean value to tell whether or not the program wants debug logs to be printed.
I want it to be recognized by all my classes in the project, not just one. What's the best way to do this?
Thanks.
There are several different ways to do it ...
1. use something like a global include file ... global_defs.h to define logging and include it in any other project file ...
2. write a custom logging function
void doLog(char *msg)
{
#ifdef __LOGGING
fprintf(stdout, "%s\n", msg);
#endif
}
Now you can write #define __LOGGING to log a msg ....
3. And many more .... :)
bye, rangaroek
forseti
2002.06.04, 11:02 AM
Thanks, the global def header works just fine for me.
OneSadCookie
2002.06.04, 03:37 PM
If you're compiling with GCC/Project Builder, you could also add -DkTrace=1 (or -DkTrace=0) to your compiler flags. This does the equivalent of
#define kTrace 1
at the beginning of each source file.
I suspect the logging function is a better idea, though :)
RedWolf
2002.06.04, 10:17 PM
Originally posted by OneSadCookie
If you're compiling with GCC/Project Builder, you could also add -DkTrace=1 (or -DkTrace=0) to your compiler flags. This does the equivalent of
#define kTrace 1
at the beginning of each source file.
That should work with almost any C/C++ compiler (e.g. works with mrc/mrcpp). It's one of the compiler options that has been pretty much been standardized among all the vendors.
Pazolli
2002.06.05, 12:53 AM
IMHO no-one should ever create a multiple source file project without a global (or semi-global) header(s).
henryj
2002.06.06, 04:35 PM
#defines are the work of the devil
ibullard
2002.06.06, 07:08 PM
They're not the work of the devil, they're just a necessary evil that should be avoided whenever possible.
NecrosaJoe
2002.06.07, 10:32 AM
They can make things a little bit more readable, and they don't cost much... it's easier to find out what I'm talking about when I write:
case IMPASSABLE: //1
//You can't walk through there!
break;
case PASSABLE: //0
//walking through is OK!
break;
case STAIRSTONEXTFLOOR: //7
//go to the next floor!
break;
etc...
^_^ They have their useful points. it assists readability, and as long as you remember they're just string substitutions, what could go wrong? ^_^
--joie
Feanor
2002.06.07, 10:47 AM
#define's are bad when you use them as system toggles. Better to use them only for code-path decisions and to set them as flags to the compiler, not in the code itself.
If you want to use human-readable constants in your code, you are best off to use enums, because they are "real", in that they are treated by the compiler as variables, not by the preprocessor as text to replace.
typedef enum {
PASSABLE;
IMPASSABLE;
} passableState;
Now you've got a nice variable type that can be set to one of those values and changed dynamically, while a #define cannot be changed. It can also be extended, say to allow for (in this example) a region that can is passable to one kind of character and not to another. But you don't have to change existing code or the existing enum constants. AND you don't have to bother managing the enum equivalents if you don't want to -- the compiler assigns integer values automatically.
I personally only use #defined values when I'm being too lazy to set up an enum, like when I'm writing experimental code.
Brent
Feanor
2002.06.07, 10:52 AM
Originally posted by rangaroek
2. write a custom logging function
Or a custom logging macro, like
#ifdef DEBUGGING
#define Log(x) NSLog(x)
#else
#define Log(x)
or something like that. Again, note that the #define is not a value (its a context-aware code-path), and it depends upon a constant you can provide as a compiler flag (in the target settings in PB or a similar place in CW).
Brent
NecrosaJoe
2002.06.07, 11:03 AM
hm, you make a lot of good points. ^^;; I can see that enumerations are more flexible... that's nice. however, for my purposes-- using a huge field of ints to handle collision mapping-- and with huge maps, replacing(or even placing) a bunch of words like "PASSABLE, PASSABLE, IMPASSABLE" would increase the number of keystrokes at least 8-fold... and with enums, if I added an enumeration between, say, IMPASSABLE and PASSABLE, it'd break the same as a #define would, I think.
and I think it's possible to check for something like a locked door or a wall that's passable by, say, ghost characters... for instance:
#define GHOSTWALL 5
... assume that it's placed somewhere on the map ...
in a collision detection function:
...
case MOVEUP:
switch(theKindOfTileRightAboveThePlayer)
{
...
case (GHOSTWALL && [player isAGhost]):
//moving OK!
//otherwise, it just goes to default:, which is the 'you just walked into a wall, you dummy.' sort of situation.
break;
...
}
break;
case MOVELEFT:
...
the above usage is OK, isn't it? I don't have a player class yet, so I haven't been able to test, i've just been... <wicked grin> #defining the state of the key. (:
kainsin
2002.06.07, 03:28 PM
No, that would not work. In that statement you are testing to see if theKindOfTileRightAboveThePlayer == ( GHOSTWALL && [player isAGhost])
This would be a comparison between an integer and a boolean value, which would always fail since a GHOSTWALL is defined as 5 and the boolean would only return 0 or 1.
What you probably wanted is:
switch( theKindOfTileRightAboveThePlayer )
{
...
case GHOSTWALL:
if( [player isAGhost] )
{
// Can go through or whatnot
...
}
break;
...
}
NecrosaJoe
2002.06.07, 03:53 PM
You're absolutely right. ^_^ That's what I meant. I got confused for a moment... last night was a late one. ^_^
-joie
Feanor
2002.06.07, 11:55 PM
Originally posted by NecrosaJoe
hm, you make a lot of good points. ^^;; I can see that enumerations are more flexible... that's nice. however, for my purposes-- using a huge field of ints to handle collision mapping-- and with huge maps, replacing(or even placing) a bunch of words like "PASSABLE, PASSABLE, IMPASSABLE" would increase the number of keystrokes at least 8-fold... and with enums, if I added an enumeration between, say, IMPASSABLE and PASSABLE, it'd break the same as a #define would, I think.
Whoa, don't worry, I didn't mean to go that far. I just meant that you can use an enum anywhere that you can use a define but it's more strict, and moreover you can typedef variables, meaning that you can *only* assign legitimate enums, as opposed to say any old int, which is a *great* way to leave room for weird bugs.
and I think it's possible to check for something like a locked door or a wall that's passable by, say, ghost characters... for instance:
#define GHOSTWALL 5
That's where I would have added "ghostPassable = 5" in the enum typedef. It's that simple, and you just make a passable variable -- but certainly in your map data I wouldn't use the enums/defines, only the raw numbers, unless maybe you're using XML format or something.
... assume that it's placed somewhere on the map ...
in a collision detection function:
...
case MOVEUP:
switch(theKindOfTileRightAboveThePlayer)
{
...
case (GHOSTWALL && [player isAGhost]):
//moving OK!
//otherwise, it just goes to default:, which is the 'you just walked into a wall, you dummy.' sort of situation.
break;
...
}
break;
case MOVELEFT:
...
I'd have put the internal switch as something like:
switch(mapSquare[targetSquare].passable)
case passable:
case impassable:
case ghostPassable:
where mapSquare is an array of structures for the squares of the map, targetSquare is the square the player tried to move to, and passable is an instance of passableState, the enum I defined.
Or maybe:
mapSquareContent type = mapSquare[targetSquare].type; // happens to be kbrickWall
switch(passable[type])
case passable: ...
case ghostPassable:
if( [player characterType] == characterGhostType )
[gameWorld movePlayer: player to:targetSquare];
else
NSBeep();
break;
case impassable:
NSBeep();
break;
...
Actually an array for holding the passable values for the types of map square would be kind of fragile, since it uses the actual value of the constant. It would require careful management to avoid getting out of alignment, and if you skipped any values there'd be holes in the array. Better to scrap the whole thing and use an NSDictionary :). But that's another story.
FÎanor
NecrosaJoe
2002.06.08, 12:04 AM
see, now here i was using a C array instead of an NSArray of NSArrays because it's easier to deal with ints than with NSDictionaries, but I suppose I could have done it like that... i just simply can't see myself needing more than 65535(or whatever. ^^:;) kinds of tiles in a single zone... well, 65529, since 6 are predefined: passable, impassable, toworldmap, door, keydoor, emptychest.... ^_^; even if i had a bunch of NPCs, tiles that initiated random battles, treasure chests, and crazy ghost-walls, i don't think I could ever use that many numbers. ^^;; so, what have we learned?
A: #defines and typedef enums can accomplish similar goals, but the approach to take depends on one's situation and urge to code prettily. or if you have the strange urge to _avoid_ getting weird, hard-to-pin bugs in your code. yeah, those enum-using, bug-hating weirdos. (:
B: structs and NSDictionaries are much more flexible than plain ints(duh! ^_^), but have much higher overhead and are harder to just place... with ints, one is usually below 10, so things stay nice and neatly aligned...
C: the Quicktime ASCII movie player is the most wonderful waste of time ever: http://developer.apple.com/samplecode/Sample_Code/QuickTime/Goodies/ASCIIMoviePlayerSample.htm
-joie
Feanor
2002.06.10, 09:39 AM
All valid observations. I myself find the message-passing world of Cocoa objects much simpler to user and easier to wrap my mind around than trying to remember what int meant what. Of course Cocoa adds overhead and kills speed, but it's so cool and flexible. Objects are just so convenient.
But if your map format is fixed you can write good code that interfaces with it directly and does everything quick and simple, too. Myself I'm very much used to changing my mind, adding new things, iterating over and over (no matter what I've said about design strictness, some designs simply mutate and that's part of the fun). When things change a lot, abstraction is like a safety buffer.
FÎanor
vBulletin® v3.8.4, Copyright ©2000-2010, Jelsoft Enterprises Ltd.