PAWN Pre-Processor - String generation - Part 4/7 -
Misiur - 14.04.2015
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
String GenerationThe SA:MP compiler has a custom feature (since incorporated into the main PAWN compiler) for converting symbols into strings, and for concatenating constant strings. Consider the following code:
Code:
#define VERSION_MAJOR 1
#define VERSION_MINOR 6
#define VERSION_BUILD 2345
main()
{
printf("==============");
printf(" Y_Less' Mode ");
printf(" V %d.%d.%d ", VERSION_MAJOR, VERSION_MINOR, VERSION_BUILD);
printf("==============");
}
Every time the mode is started that printf will format the string with (run-time) constant values - this is just a waste of time for the server when we can get the compiler to do it for us using hash (#):
Code:
printf(" V " #VERSION_MAJOR "," #VERSION_MINOR "," #VERSION_BUILD " ");
Let's break this down:
That is a standard define, it will replace itself with "1":
Code:
printf(" V " #1 "," #6 "," #2345 " ");
A hash within a string will convert anything after it into a string:
Code:
printf(" V " "1" "," "6" "," "2345" " ");
Now we have lots of strings separated by spaces. These will be IMplicitly combined to form one long string as they are not separated by commas:
Code:
printf(" V 1,6,2345 ");
If this was the standard PAWN compiler you would need to EXplicitly tell it to append them:
Code:
printf(" V " ... #VERSION_MAJOR ... "," ... #VERSION_MINOR ... "," ... #VERSION_BUILD ... " ");
The implicit string concatenation will work for anything prefixed by a hash (implicit strings) and anything in double quotes (explicit strings).
There are a few restrictions. If the compiler comes across ";", ")" or "," after a hash it will stop the string:
Code:
printf("hi" ", there");
Is not the same as:
Code:
printf("hi" #, there);
Both will compile but the second will only print "hi" as the compiler sees the comma as the end of the implicit string. This may be more subtle, for example:
Code:
#define VERSION_MAJOR 1
#define VERSION_MINOR 6
#define VERSION_BUILD 2345
#define VERSION VERSION_MAJOR,VERSION_MINOR,VERSION_BUILD
main()
{
printf("==============");
printf(" Y_Less' Mode ");
printf(" V " #VERSION);
printf("==============");
}
That will convert to:
Code:
printf(" V " #1,6,2345);
Which will compile as:
Code:
printf(" V 1", 6, 2345);
This is wrong as it will only display " V 1".
Additionally, due to the selection of string end markers you can now no longer use strings as triadic operator results:
Code:
a = var ? "one" : "two";
A colon (
is not seen as the end of a string, so the compiler complains that is was expecting a string, however there is a very simple way around this:
Code:
a = var ? ("one") : ("two");
That does not change the meaning of the code in any way but compiles. This problem does not occur at all for the explicit string concatenation in the standard PAWN compiler.
Because a close bracket is seen as the end of a string you can't do:
Code:
#define A (5)
printf("A = " #A);
That will try to compile with two close brackets outside the string:
An ideal solution to this would stringify the whole of the result of the macro, regardless of context and content, but it doesn't. Note that "stringify" is the technical term for what the hash operator is doing, despite not sounding very technical!
This:
Code:
printf("hello" # there "you");
Will print: "hellothereyou". Any spaces before or after a stringified token are ignored.
The reason we need this operator is that:
Code:
#define VERSION_MAJOR 1
#define VERSION_MINOR 6
#define VERSION_BUILD 2345
main()
{
printf("==============");
printf(" Y_Less' Mode ");
printf(" V VERSION_MAJOR,VERSION_MINOR,VERSION_BUILD");
printf("==============");
}
Will print exactly that:
Code:
V VERSION_MAJOR,VERSION_MINOR,VERSION_BUILD
Pre-processor macros inside strings are not evaluated, in the same way as variables are not. There used to a bug (or feature) where "%N" macro parameters were evaluated inside strings, and this was used by the original version of dcmd, however that "bug" was "fixed" - prompting the development of this alternate compiler.
To summararise - to put the value of a compile time constant in a string use hash in front of it, to put the name of a compile time constant in a string enclose it it quotes. This also works for macro parameters:
Code:
#define PRINT(%0,%1) printf(#%0,%1)
PRINT(HELLO %d, var);
This will result in:
Code:
printf("HELLO %d", var);
With the first macro parameter (%0) being stringified and the second macro parameter becoming the passed "var".