Keywords:Directives

From SA-MP Wiki

Jump to: navigation, search

Contents

#assert

This checks if something is true and if not halts the compile.

#define MOO 10
#assert MOO > 5

This will compile fine.

#define MOO 1
#assert MOO > 5

That won't and will give a fatal error.

This is similar to:

#define MOO 1
#if MOO <= 5
	#error Moo check failed
#endif

However assert will give an error of:

"Assertation failed: 1 > 5"

Wheras the second will give an error of:

"User error: Moo check failed"

Which may or may not be more helpful.

#define

#define defines a symbol replacement, wherever the first symbol of the define is found the rest of it will be placed.

#define MOO 7
printf("%d", MOO);

Will be changed to:

printf("%d", 7);

This is why all defines are lost in decompilation as they don't exist when the code is compiled (all directives are pre-processor). Defines don't have to contain numbers:

#define PL new i = 0; i < MAX_PLAYERS; i++) if (IsPlayerConnected(i)
 
for (PL)
{
	printf("%d connected", i);
}

Will compile to the player loop we all know and love (despise). Notice how the brackets are used here, some from the for and some from the define macro (the replacement).

Another little known fact about defines is that they can be multi-line if you escape the new line. Generally a new line ends the define however the following is valid:

#define PL \
	new i = 0; i < MAX_PLAYERS; i++) \
		if (IsPlayerConnected(i)
 
for (PL)
{
	printf("%d connected", i);
}

The \ character means the define continues onto a new line.

The third major characteristic of defines is their ability to have what are effectively parameters. These are %0 to %9 and behave very similarly to normal parameters:

#define MOO(%0) \
	((%0) * 7)
 
printf("%d", MOO(6));

That will output 42 (no, not chosen randomly). Notice the excessive brackets in the define? This is because defines are straight text replacements so that will compile as:

printf("%d", ((6) * 7));

That's fine as it is but let's take this example:

printf("%d", MOO(5 + 6));

You would expect that to compile to output 77 ((5 + 6) * 7) and with the brackets it will do, however without the brackets you have:

#define MOO(%0) \
	%0 * 7
 
printf("%d", MOO(5 + 6));

Which converts to:

printf("%d", MOO(5 + 6 * 7));

Which, due to BODMAS, compiles as (5 + (6 * 7)), which is 47 and very wrong.

One interesting fact about the parameters is that if you have too many, the last one is all the extra ones. So doing:

#define PP(%0,%1) \
	printf(#%0, %1)
 
PP(%s %s %s, "hi", "hello", "hi");

Will infact print:

hi hello hi

As %1 contains "hi", "hello", "hi". You may have also noticed the use of # to convert a literal into a string, this is a SA:MP only feature and can be useful. It was just added here to give a distinct distinguishing between the parameters.

#else

#else is like else, but for #if instead of if.

#elseif

#elseif is like else if but for #if. I have also never got this to work. If you can't either the next best thing is:

#define MOO 10
 
#if MOO == 9
	printf("if");
#else
	#if MOO == 8
		printf("else if");
	#else
		printf("else");
	#endif
#endif

#emit

This directive is unlisted in the pawn-lang.pdf table however does exist. It is basically an inline compiler, if you know AMX you can use this to put AMX opcodes directly into your code.

#endif

#endif is like a close brace for if. #if doesn't use bracess, everything is contitionally added up to the corresponding #endif

#endinput

This stops the inclusion of a single file. It is often used to avoid compiling include files twice by accident.

#include "bla.inc"
#include "bla.inc"

bla.inc:

#if defined _BLA_INC
	#endinput
#endif
#define _BLA_INC

First time the file is called _BLA_INC is not defined so it defines it and continues. Second time the file is called _BLA_INC is defined so it doesn't parse the file again. The explanation of #include explains what that does, this basically marks the end of the code to be included into the main script.

#error

This halts the compiler instantly and gives a custom error message. See #assert for an example.

#file

Sets the current filename. Basically pointless.

#if

#if is to the preprocessor what if is to code. You can choose exactly what to compile and what not to from here. For example consider the following code:

#define LIMIT 10
 
if (LIMIT < 10)
{
	printf("Limit too low");
}

That will compile as:

if (10 < 10)
{
	printf("Limit too low");
}

Which will clearly never be true and the compiler knows it - so it tells you so, giving you a "constant expression" warning. The question is, if it will never be true what's the point of including it at all? You could just remove the code but then there will be no checks if someone changes LIMIT and recompiles. This is what #if is for. Unlike normal if which give a warning if the expression is constant, #if expressions MUST be constant. So:

#define LIMIT 10
 
#if LIMIT < 10
	#error Limit too low
#endif

That will check that the limit is not too small when you compile and if it is will give a compile time error, rather than you having to test the mode to see if there's something wrong. This also means that no excess code is generated. Note the lack of brackets too, you can use them, and may need them in more complex expressions, but they're not required.

Here's another example:

#define LIMIT 10
 
if (LIMIT < 10)
{
	printf("Limit less than 10");
}
else
{
	printf("Limit equal to or above 10");
}

Again this is a constant check, which will give a warning, but both prints will be compiled when we KNOW only one will ever run. Using #if this becomes:

#define LIMIT 10
 
#if LIMIT < 10
	printf("Limit less than 10");
#else
	printf("Limit equal to or above 10");
#endif

That way only the printf which is required will be compiled and the other one will still be in your source code incase they change LIMIT and recompile, but won't be included in the code as it's not needed. This way also means the pointless if isn't run every time your code is run, which is always good.

#include

This takes all the code from a specified file and inserts it into your code at the point at which the include line is. There are two types of include: relative and system (I just made those terms up, if you have better ones please say). Relative includes use double quotes around the filename and are located relative to the current file, so:

#include "me.pwn"

would include the file "me.pwn" from the same directory as the file including that file. The other type, system, includes the file from the pawno include directory (or goes from there):

#include <me>

Would include the file "me.inc" (note the lack of extension, you can specify one if the file is not .p (not .pwn) or .inc) from the pawno/include directory (assuming you're using pawno).

Both these types can take directories:

#include "folder/me.pwn"
#include <folder/me>

Both of those will include a file one directory down from their respective default directories.

Code included from another file is in it's own section, see #section for more information.

If the file does not exist compile fails instantly.

#line

This is another mostly useless directive like #file. It just specifies the current line number if you feel the need to change it.

#pragma

Information

This is one of the most complex directives. It has a number of options to control how your script works. An example setting would look like:

#pragma ctrlchar '$'

That changes the escape character from \ to $, so a new line, instead of being "\r\n" (windows CR-LF) would be "$r$n". Many of the options are designed to control amx compilation for embedded systems and so limit things which are really almost unlimited on a PC, they are all listed in pawn-lang.pdf but only selected ones relevant to SA:MP are here:

List

Name Values Description
codepage name/value Sets the Unicode codepage to use for strings.
compress 1/0 Unsupported in SA:MP - don't try use it.
deprecated symbol Generates a warning if the given symbol is used to tell people there's a better version available.
dynamic value (generally a power of 2) Sets the size of memory assigned to the stack and heap. Required if you get the excess memory usage warning after compilation (a wierd table after the compiler copyright line)
library dll name Widely incorrectly used in SA:MP. This specifies the dll to get the native functions defined in the file it is in from, it does not defined a file as a library.
pack 1/0 Swap the meanings of !"" and "". See pawn-lang.pdf for more information on packed strings.
tabsize value Another widely misused setting. This should be used to set the size of a tab to avoid compiler warnings which are wrong due to spaces and tabs being used interchangably. This is set to 4 in SA:MP as that is the size of a tab in pawno. Setting this to 0 will surpress all your indentation warnings but is highly unadvised as it allows entirely unreadable code.
unused symbol Like deprecated this appears after the symbol you wish to surpress the "symbol is never used" warning for. Generally the preferred method of doing this is by using stock however this is not always applicable (e.g. function parameters cannot not be compiled).

Examples

Deprecated

new
	gOldVariable = 5;
 
#pragma deprecated gOldVariable
 
main()
{
	printf("%d", gOldVariable);
}

That will give a warning that gOldVariable should not be used anymore. This is mostly useful for functions to preserve backwards compatability while updating the API.

#section

A section (every file is implicitly in it's own section) is an area of code to which scope limited globals are restricted. If you declare a global variable using static instead of new it is limited to be used only in the section in which it is defined.

static
	g_sMyVar = 5;
 
MyFunc1()
{
	printf("%d", g_sMyVar);
}
 
#section
 
MyFunc2()
{
	printf("%d", g_sMyVar);
}

That will generate an error as g_sMyVar is declared as static and then the section is changed, thus the variable doesn't exist anymore. This is another bit of data lost in decompilation as scope is statically checked by the compiler, once it's compiled all global static are just global, their section scope is lost.

You can also name sections:

static
	g_sMyVar = 5;
 
MyFunc1()
{
	printf("%d", g_sMyVar);
}
 
#section _SECTION_NUMBER_TWO
 
MyFunc2()
{
	printf("%d", g_sMyVar);
}

#tryinclude

This is similar to #include but if the file doesn't exist the compilation doesn't fail. This is useful for only including features in your script if a person has the correct plugin installed (or at least the plugin include):

myinc.inc:

#if defined _MY_INC_INC
	#endinput
#endif
#define _MY_INC_INC
 
stock MyIncFunc()
{
	printf("Hello");
}

Gamemode:

#tryinclude <myinc>
 
main()
{
	#if defined _MY_INC_INC
		MyIncFunc();
	#endif
}

That will only call MyIncFunc if the file with it in was found and compiled. This, as stated before, is good for things like IRC plugins to check if they actually have the plugin.

#undef

Removes a previously defined define or const symbol.

#define MOO 10
printf("%d", MOO);
#undef MOO
printf("%d", MOO);

That will fail to compile as MOO doesn't exist anymore by the time the second printf is reached.

Personal tools