Macros are an ancient and controversial way to transform source code. The controversy is due to the fact that like "goto", macros are too powerful and general to be trusted.
There are a number of different features in the C preprocessor:
The standard uses of this are pretty straightforward:
#define symbol replacement
makes the preprocessor replace every occurrence of "symbol" with "replacement" before compiling. So this works fine, and returns 17:
#define n 17
return n;
(Try this in NetRun now!)
Unlike everything else in C++, "n" is now defined as 17 from this point onwards, even across scope like classes or functions. Declaring "int n=3;" gets rewritten to "int 17=3;", which won't compile! To avoid this, by convention macros are written in capital letters, like "NUM_ELEMENTS", not just bare n.
There's another problem with plain string replacement. Let's say you've defined a constant like:
#define n 10+10
return n*n;
(Try this in NetRun now!)
This returns 10+10*10+10 = 120. What?!
There are several well known fixes for this bug:
#define n (10+10)This works, but you must remember to do it every time!
If somebody #includes your header, but they also #include a second header that itself #includes your header, your header code gets compiled twice and causes weird error messages about multiple definitions. To avoid this, it is common to start your header with:
#ifndef LAWLOR_CODE_ALREADY_INCLUDED
#define LAWLOR_CODE_ALREADY_INCLUDED
...
#endif
The first time around, the ifndef fires, and your header gets compiled normally. Any subsequent time, the macro has been defined, so your whole header gets skipped.
As usual, there are several dangerous things about this. First, the macro name must be globally unique, or the second header using a given name will be skipped the first time through, so the macro name should include at least the name of the header, project, and author. Second, each header must remember to do this, or the second use of the header will fail.
Portable code often needs to do different things on different compilers. For example, code that can be compiled by both a C++ or a C compiler can add additional features to the C++ version using:
#ifdef __cplusplus
class foo { ... };
#endif
Portable code intended to be run on different systems like OS X and Windows often needs to distinguish between them, to access OS-dependent features or work around OS-specific bugs. The full list of predefined platform macros is long, but the most useful ones are:
Inside NetRun's timing code, I use #ifdef _WIN32 to access a different timer on Windows systems.
In plain C, before inline functions, it was pretty common to write short utility functions using macros that take arguments. As with all macros, the argument gets text-replaced at the very first stage of compilation:
#define times3(x) x*3
return times3(10);
Again, the problem is this is a plain text replacment. So calling the same macro with a sum:
return times3(10+10);
This returns... 40. Thats 10+10*3, not (10+10)*3. Again, the fix is to wrap the expression in parenthesis. Note that arguments and the overall expression both need parenthesis, to protect against operators inside or outside the macro call:
#define times3(x) ((x)*3)
return times3(10+10);
This works basically reliably, although there's not much benefit to using a macro over using a small function here, and there are many limitations--for example, there's no standard way to make a temporary variable inside a function-like macro.
Macros have the ability to adjust syntax in interesting and abitrary ways. For example, I can rename the curly braces with macros, like so:
#define begin {
#define end }
int x=5;
if (x>3)
begin
return 7;
end
A FORTRAN programmer used to typing in ALL CAPS might be more comfortable using #define FOR for
People are merely annoyed by most of the above uses. It can get much worse, though:
#define BOOGA int x=9; return (x+
BOOGA 7);
In addition to screwing up any syntax highlighting editor, this sort of syntax-bending basically destroys the readability of the code, and is bad enough to make people seriously talk of banning all macros.
Here's an example of stringification. Note the quoted lines are just one long macro argument, and the newlines get removed before printing. Annoyingly, you can't have a bare comma, or the preprocessor thinks you're passing two arguments into "quoted", which only takes one argument.
#define quoted(x) #x cout<<quoted( This is my string. There are many like it but this one is mine. )<<"\n";
I use stringification all the time for GPU programming, where the graphics driver wants the GPU code as a string, but I don't want to wrap every line in a quote, so I pass a long multi-line "argument" to a quoting macro. Also, I can have the same code work in C++ directly, then have a macro spit out a stringified version for the GPU to run.
Another place stringification is useful is in error checking. This error checking macro not only checks for errors, but shows you the code that went wrong, and tells you the line number where it happened:
#define checkErrs(code) { int err=code; /* run */ if (err!=0) std::cout<<"Error in "<<#code<<" at line "<<__LINE__<<" of file "<<__FILE__<<"\n"; }
int x=18;
checkErrs(x-18);
checkErrs(x-10);
return 0;
(Try this in NetRun now!)
This macro definition looks a little better using backslashes to separate the lines (known as "line splicing"):
#define checkErrs(code) { \
int err=code; /* run */ \
if (err!=0) { \
std::cout<<"Error "<<err<<" in '"<<#code<<"' at line "<<__LINE__<<" of file "<<__FILE__<<"\n"; \
} \
}
I need the curly braces to be able to declare "int err" repeatedly. But people typically add an extra semicolon at the end of a function-like macro call, like checkErrs(x-18); above. This extra semicolon is untidy, and will throw off an "if..else" statement with the macro in the middle. There's a bizarre well-known solution, which is to add a worthless do{}while(0) that only exists to consume the semicolon:
#define checkErrs(code) do { \
int err=code; /* run */ \
if (err!=0) { \
std::cout<<"Error "<<err<<" in '"<<#code<<"' at line "<<__LINE__<<" of file "<<__FILE__<<"\n"; \
} \
} while(0)
Another trick is to generate classes inside a macro. For example, if my calculator needs ten "operator" classes for each of the basic operators, instead of typing them all out I'll generate them with a macro like this:
#define makeop(name,op) \
class calcop_##name { public: \
int calculate(int a,int b) { return a op b; } \
}
makeop(add,+);
makeop(sub,-);
makeop(mul,*);
makeop(div,/);
makeop(and,&);
makeop(or,|);
makeop(left,<<);
makeop(right,>>);
int foo(void) {
calcop_add a;
return a.calculate(100,10);
}
(Try this in NetRun now!)
The nice part is now if you need the calcop classes to inherit from some base class, you can add it to the macro. Forgot the "const" in calculate? Just add it to the macro. To add a "getSymbol" method returning the operator's symbol, you can use stringification to add it to the macro definition like "const char *getSymbol(void) const { return #op; }". If each operator needs to be registered into the list of operators, you can add that as well.
C++ macros can become quite complex, which is bad, but they provide very useful "metaprogramming" abilities, especially in big programs. "Metaprogramming" is when the output of the first program (the preprocessor) is the source code for another program, here C++. You can even do explicit metaprogramming, where one program outputs a second program:
Compile generator code.
Run generator. Output is final code.
Compile final code.
Run final code.
This sort of complexity is common in real systems!
CS 301 Lecture Note, 2014, Dr. Orion Lawlor, UAF Computer Science Department.