What is the correct strategy to limit the scope of #define
labels and avoid unwarranted token collision?
In the following configuration:
Main.c
# include "Utility_1.h"
# include "Utility_2.h"
# include "Utility_3.h"
VOID Main() { ... }
Utility_1.h
# define ZERO "Zero"
# define ONE "One"
BOOL Utility_1(); // Uses- ZERO:"Zero" & ONE:"One"
Utility_2.h
# define ZERO '0'
# define ONE '1'
BOOL Utility_2(); // Uses- ZERO:'0' & ONE:'1'
Utility_3.h
const UINT ZERO = 0;
const UINT ONE = 1;
BOOL Utility_3(); // Uses- ZERO:0 & ONE:1
Note: Utility _1
, Utility_2
and Utility_3
have been written independently
Error: Macro Redefinition and Token Collision
Also, Most Worrying: Compiler does not indicate what replaced what incase of token replacement
{Edit} Note: This is meant to be a generic question so please: do not propose enum
or const
i.e. What to do when: I MUST USE #define
& _Please comment on my proposed solution below.. __
#define
s don't have scope that corresponds to C++ code; you cannot limit it. They are naive textual replacement macros. Imagine asking "how do I limit the scope when I replace text with grep?"You should avoid them whenever you possibly can, and favor instead using real C++ typing.
Proper use of macros will relieve this problem almost by itself via naming convention. If the macro is named like an object, it should be an object (and not a macro). Problem solved. If the macro is named like a function (for example a verb), it should be a function.
That applies to literal values, variables, expressions, statements... these should all not be macros. And these are the places that can bite you.
In other cases when you're using like some kind syntax helper, your macro name will almost certainly not fit the naming convention of anything else. So the problem is almost gone. But most importantly, macros that NEED to be macros are going to cause compile errors when the naming clashes.
Some options:
Use different capitalization conventions for macros vs. ordinary identifiers.
Fake a namespace by prepending a module name to the macros:
Where available (C++), ditch macros altogether and use a real namespace:
There are two types of
#define
Macros:One which are need only in a single file. Let's call them
Private #defines
eg.
PI 3.14
In this case:As per the standard practice: the correct strategy is to place
#define
labels - in only the implementation, ie.c
, files and not the headerh
file.Another that are needed by multiple files: Let's call these
Shared #defines
eg.
EXIT_CODE 0x0BAD
In this case:Place only such common
#define
labels in headerh
file.Additionally try to name labels uniquely with
False NameSpaces
or similar conventions like prefixing the label withMACRO_
eg:#define MACRO_PI 3.14
so that the probability of collision reducesI think you really just have to know what it is you're including. That's like trying to include windows.h and then declare a variable named WM_KEYDOWN. If you have collisions, you should either rename your variable, or (somewhat of a hack), #undef it.
The correct strategy would be to not use
at all. If you need constant values, use, in this case, a
const char
instead, wrapped in a namespace.#define
labels - in only the implementation, ie.c
, filesFurther all
#define
could be put separately in yet another file- say:Utility_2_Def.h
(Quite like Microsoft's
WinError.h
:Error code definitions for the Win32 api functions)Overheads:
Gains:
ZERO
is:0
,'0'
or"Zero"
as to where you use itUtility_2.h
Utility_2_Def.h
Utility_2.c