0

I'm aware of the pitfalls of macros and avoid them when what I really want is a function.

What I've found them helpful for, though, is storing things like "magic" numbers, file names, font names and so on. So things like:

ProjectHeader.h

#ifndef __PROJECT_HEADER__
#define __PROJECT_HEADER__

#define kXMLFileName "scoresAndSettings.xml"

#define AUDIO_DATA_TYPE_FORMAT SInt16
#define NUM_AUDIO_BUFFERS 3
#define AUDIO_SAMPLE_RATE 44100.
#define NUM_AUDIO_CHANNELS 2

#define kGameFont @"Helvetica-Bold"
#define kGameFontSizeNormal 18
#define kGameFontSizeSmall 16
#define kGameFontSizeTiny 11

These let me (a) store UI specifics in one place where I can change them and know the change will propagate throughout the code, (b) give them a name that describes their function in the code, and (c) use code autocompletion to know that I've actually typed the right term.

I feel fairly confident that this isn't a terrible way to work, but I'd like to know if someone thinks it is, and if so how to do it better.

These are perhaps a little dodgier but I still find them very helpful:

#define __COPY_PROTECTION__
#define __SHOW_FPS__ NO
#define __SKIP_LAUNCH_SCREEN__ 0
#define __START_IN_GAMEPLAY__ 1
#define __START_IN_PREFS__ 0
#define __START_IN_WIN_SCENE__ 0
#define __AUTO_WIN_TESTING__ 0

(In various points, then, in the code:

AppDelegate.h
if (__START_IN_GAMEPLAY__) {
 [self show:MyGameplay];
 return;
}
[self show:MainScreen];

). This allows me to jump straight into testing whatever part of the project I'm working on, by manipulating only macros #defined in the ProjectHeader.h file.

Is this good? Bad? Is there a better way for next time?

Community
  • 1
  • 1
buildsucceeded
  • 4,203
  • 4
  • 34
  • 72
  • (This is an Objective-C++ project, by the way, but I think conceptually the question is fairly language-agnostic.) – buildsucceeded May 28 '12 at 11:46
  • @Adam Obj-C++ is a thing. It's a wild and messy thing, but it's also been described as "ridiculously powerful". I will probably keep using it until someone pays me to stop... – buildsucceeded May 28 '12 at 11:51
  • I give up. But your post seems to be a duplicate of this one: http://stackoverflow.com/questions/653839/what-are-c-macros-useful-for – Adam May 28 '12 at 11:53
  • 8
    Why don't you use `const`s and `typedef`s instead? Also, those names with the two underscores are reserved. – Pubby May 28 '12 at 11:55
  • @Pubby ah, didn't know about the underscores, thanks. – buildsucceeded May 28 '12 at 12:11
  • @Pubby yes, the `const`/`typedef` approach is a good one. Does it compile faster / run smaller than #define? Or is it more emblematic of better coding practice generally? – buildsucceeded May 28 '12 at 12:38
  • 1
    @buildsucceeded it will likely compile equally fast. albeit the processing is done in different compilation stages. However, the const/typedef approach is typesafe, macro's are not. There are also some benefits for debugging (a const has a name). – KillianDS May 28 '12 at 12:46
  • @buildsucceeded The compilation speed is irrelevant when talking about best practices, but you shouldn't notice a change. The execution speed should be identical. It's stupid to use macros when you have perfectly good language features that provide the same functionality but with type-safety and actual symbols. – Pubby May 28 '12 at 12:50

1 Answers1

1

First, there's nothing in your first set of of macros that wouldn't better be done with const variables, at least in C++:

char const kXMLFileName[] = "socresAndSettings.xml";
typedef SInt16 audioDataTypeFormat;
int const numAudioBuffers = 3;
//  ...

The advantage of using const instead of macros is that the names will obey scope; for numeric values, there's also the advantage that you can more easily specify the type (if it's not int). In case of misuse, the error messages are also generally more understandable.

For your second block, it's harder to say (but the names you've chosen result in undefined behavior). My impression is that these are to be used for conditional compliation. If so, then they must be macros. But in general, conditional compilation is something to be avoided, unless your goal is obfuscation.

This is not to say that you should never use macros. There's no other way to get automatic insertion of __FILE__ and __LINE__ in logging primitives, for example. I've also used them extensively when interfacing to Python, or other languages which define a C API: in the C API, "overloading" is done by manual name mangling, and token pasting in the macros is about the only easy way to achieve this.

James Kanze
  • 150,581
  • 18
  • 184
  • 329
  • Excellent answer, thanks! (Can you explain why the names result in undefined behavior though?) – buildsucceeded May 28 '12 at 13:06
  • @buildsucceeded Because the standard says so:-) (§17.4.3.1.2 in C++03): "Each name that contains a double underscore (`__`) or begins with an underscore followed by an upper case letter is reserved for the implementation for any use." The standard is rather unclear as to what happens if you violate this constraint, but traditionally, it has been considered undefined behavior. – James Kanze May 28 '12 at 14:36