14.04.2015, 21:17
(
Last edited by Misiur; 15/04/2015 at 04:34 PM.
)
Contents
Part 1 - Covers an introduction to the pre-processor and covers some important things when writing function-like macros.
Part 2 - Explains exactly what the compiler searches for and looks at some common macro uses.
Part 3 - Describes the other directives available (beside "#define") and looks at definitions with no replacement value.
Part 4 - How to use strings with the pre-processor.
Part 5 - Alternatives to the pre-processor, multiple symbols and recursion.
Part 6 - Case Study (y_remote).
Part 7 - Macro issues and spaces.
Additional
Utilising tagof - By g_aSlice.
Future-proof string concatenation
String bug
Macros that do multiple things
Advanced "tag" macros
Macro issues
In this part we will detail problems that can stem from using the pre-processor, errors and other issues that can arise. Most of these come from the fact that the pre-processor is a pure text replacement system, not a function system.
Now the general intention of this code (not that it makes much sense) is to take one parameter and instead pass two parameters, the second being 42 greater than the first:
Becomes:
Unfortunately, after the pre-processor replaces some text it re-runs its match against the result so we get:
Which still matches and we get:
And so on forever - thus getting the compiler stuck in a loop until it crashes! The problem is that the compiler can't detect the endless loop - we've already shown that loops can sometimes be useful and will eventually end.
Looks simple enough, until someone decides to be clever:
Now, after the text-based replacement of the pre-processor, you compile:
I don't even know if that will print the right answer, but I DO know that it will skip every other number in the loop. In cases like this you should really just use a function as their parameters will only be evaluated once:
Just to support the previous argument, try run this code:
Moving on from that though, using "if" can cause problems of its own when used with other "if2 statements:
If you run that code you will get:
Err, what?
Let's look at the expansion of this code:
That still looks OK until we remember that "else" is associated with the previous "if", not the one at the same level. In short, this code compiles as:
Which is NOT what is wanted, and is almost invisible if you don't know that "TEST" is a macro. The best way to avoid this is to always use braces after "if" statements. The other way (which is less good) is to do this:
That will compile as equivalent to:
Spaces
It is documented in pawn-lang.pdf that should you ever need to detect a space you could use "\32;" instead but I could never get it to work! Doing this:
Will NOT detect spaces before the brackets and remove them. Similarly this will not work:
That will NOT detect a call like "TEST2( 42)" - I'm still not sure why or what the exact rules governing how the macros work are, but this will work:
In that case, even just a leading space like in "TEST3( 42)" can be detected and removed. This lack of space detection has been the bane of my code for literally YEARS (if you don't believe me, that's what the point of this topic was). It is why this works:
But this gives multiple cryptic errors:
I knew the documentation claimed it could be done, but my repeated failed attempts led me to believe that maybe the SA:MP compiler version couldn't until I started really digging about in its code. Anyway, i am very happy about this and will be retrofitting certain bits of code to find any errors caused by bad spacing that I can now fix! As Slice points out this can also be used to detect the end of a line using "\10;":
Part 1 - Covers an introduction to the pre-processor and covers some important things when writing function-like macros.
Part 2 - Explains exactly what the compiler searches for and looks at some common macro uses.
Part 3 - Describes the other directives available (beside "#define") and looks at definitions with no replacement value.
Part 4 - How to use strings with the pre-processor.
Part 5 - Alternatives to the pre-processor, multiple symbols and recursion.
Part 6 - Case Study (y_remote).
Part 7 - Macro issues and spaces.
Additional
Utilising tagof - By g_aSlice.
Future-proof string concatenation
String bug
Macros that do multiple things
Advanced "tag" macros
Macro issues
In this part we will detail problems that can stem from using the pre-processor, errors and other issues that can arise. Most of these come from the fact that the pre-processor is a pure text replacement system, not a function system.
- Recursion
Code:
MyFunc(a, b) { } #define MyFunc(%0) MyFunc(%0, %0 + 42)
Code:
main() { MyFunc(12); }
Code:
main() { MyFunc(12, 12 + 42); }
Code:
main() { MyFunc(12, 12 + 42 + 42); }
Code:
main() { MyFunc(12, 12 + 42 + 42 + 42); }
- Parameters
Code:
#define square(%0) ((%0) * (%0))
Code:
new i = 0; while (i != 10) { printf("%d squared = %d", square(i++)); }
Code:
new i = 0; while (i != 10) { printf("%d squared = %d", ((i++) * (i++))); }
Code:
square(n) return n * n;
- "if"
Code:
#define TEST(%0) if ((%0) >= 0) printf("%d is positive.", (%0));
Code:
main() { new a = 0; TEST(a--) }
Code:
new a = -7; if (a <= 10) TEST(a) else printf("%d is greater than 10.", a);
Code:
-7 is greater than 10.
Let's look at the expansion of this code:
Code:
new a = -7; if (a <= 10) if ((a) >= 0) printf("%d is positive.", (a)); else printf("%d is greater than 10.", a);
Code:
new a = -7; if (a <= 10) { if ((a) >= 0) printf("%d is positive.", (a)); else printf("%d is greater than 10.", a); }
Code:
#define TEST(%0) if ((%0) < 0) {} else printf("%d is positive.", (%0));
Code:
new a = -7; if (a <= 10) { if ((a) < 0) {} else printf("%d is positive.", (a)); } else { printf("%d is greater than 10.", a); }
It is documented in pawn-lang.pdf that should you ever need to detect a space you could use "\32;" instead but I could never get it to work! Doing this:
Code:
#define TEST1\32;(%1) TEST1(%1)
Code:
#define TEST2(\32;%1) %1
Code:
#define TEST3(%0\32;%1) %1
Code:
foreach (new i : Player)
Code:
foreach ( new i : Player )
Code:
#define EAT_LINE:%0\10;%1 <%0> #define foo%0\10; foo, #define bar%0\10; bar, EAT_LINE:boop boop foo bar // Output: <boop boop>foo,bar,