Raised This Month: $32 Target: $400
 8% 

[TUT] The Preprocessor (#if, #include, #pragma, etc.)


  
 
 
Thread Tools Display Modes
Prev Previous Post   Next Post Next
Author Message
Hawk552
AMX Mod X Moderator
Join Date: Aug 2005
Old 11-02-2006 , 22:01   [TUT] The Preprocessor (#if, #include, #pragma, etc.)
Reply With Quote #1

This article assumes you have intermediate knowledge of Pawn and its implementation in AMXX, and that you have advanced knowledge of optimization as well as what a compiled plugin looks like (opcodes and various reasons for optimization). It is still possible to read and understand this without this knowledge, but it is not recommended and certain parts may seem strange / unfamiliar.

The preprocessor is a part of the compiler specifically designed to process commands/routines before they are actually compiled or interpreted. It is generally used to make things easier for modification, add readability, and speed up operations.

The most basic operation involved in the Pawn precompiler is the #include directive. An example of this, that hopefully you've seen before, is:

Code:
#include <amxmodx>

What this is literally doing is replacing the directive with the contents of amxmodx.inc. Instead of having to copy out all the constants and natives each time you want to make a script, the preprocessor does the work for you and allows you to include header files quickly and easily. Another less common way to "include" a header file is:

Code:
#include "amxmodx.inc"

Notice you include the extension for this - this means you can also include .sma files or any other file format (as long as the contents make sense to the compiler).

The preprocessor, albeit much more limited than most compilers out there, is capable of much more, however. It can be used to easily change settings in a plugin without needing a cvar. This is particularly useful for a variable of n size, allowing you to do something like this:

Code:
#include <amxmodx> #define VAR_SIZE 33 new g_Variable[VAR_SIZE] public plugin_init() {     copy(g_Variable,VAR_SIZE - 1,"test") }

Ignoring the fact that we have sizeof (another preprocessor directive that will be covered later on), this makes for easy manipulation of variables.

This feature is often misused however, like so:

Code:
#include <amxmodx> #define AUTHOR "Hawk552" public plugin_init()     register_plugin("whatever","1.0",AUTHOR) public client_putinserver(id)     client_print(0,print_chat,"This server is running something made by %s.",AUTHOR)

Why is this a misuse? Well, if you know about optimization, you would know that every instance of AUTHOR is literally replaced by the definition (in this case, "Hawk552").

Normally, this isn't bad, but the Pawn compiler sucks at optimization. Instead of combining all of these duplicates into one variable, it instead makes 1 for each time you actually use the definition. If you only use it in register_plugin (like most people do), then this is no faster (and probably better, since global variables are stored even after needed) than a global variable. The other problem is that it is literally created each time it's needed, meaning the memory must be freed and set to whatever the definition is.

Again, there are some things acceptable. For instance, only using a define for integers (as in, one cell) is usually not too bad.

What I usually do to get around this, however, is the following:

Code:
#define AUTHOR "Hawk552" // ... later on ... new g_Author[] = AUTHOR

Not only does this eliminate the optimization problem, it also eliminates the chance that a user might do something stupid like change AUTHOR to 3, which would throw off the entire plugin.

The next important preprocessor macro is pragma. This directive is used mainly for things that the AMXX core (or any other core running a Pawn plugin) must know before the plugin is loaded.

The pragma directive can be used for many things in AMXX - for instance, increasing the amount of memory (16kb by default) allocated to each plugin. If you want 128kb, for example, you can run this macro somewhere in your plugin:

Code:
#pragma dynamic 32768

This is not particularly useful for most plugins, but is generally needed for plugins that use a lot of memory, such as WC3, Superhero Mod, or any mod of that calibre.

pragma is also used for many other operations, such as semicolon (forces the compiler to use semicolons to close lines, use #pragma semicolon 1), libraries/classes (#pragma library "library", #pragma reqlib "mylib", #pragma reqclass "myclass", etc. - for a demonstration of these, look at the top of the module header files).

The next important preprocessor macro is #if (which will have #else and #endif explained with it). #if allows you to check defined variables, and perform actions and add or remove code depending on an environment.

A prime example of this is checking the AMXX version and acting correspondingly, like so:

Code:
// or whenever query_client_cvar was introduced #if AMXX_VERSION_NUM < 160 native query_client_cvar ( id, const cvar[], const resultFunc[], paramlen=0, const params[]"") #endif

Another way of checking this, is like so:

Code:
#if !defined query_client_cvar native query_client_cvar ( id, const cvar[], const resultFunc[], paramlen=0, const params[]"") #endif

NOTE: If you try to run query_client_cvar on a version of AMXX lower than 1.60, it WILL NOT FUNCTION. This simply adds the native, allowing the compiler to compile it properly even if it is a version earlier than 1.60.

As you can see, the preprocessor is quite powerful this way. You can also use #else, in the structure "#if / #else / #endif".

The final important preprocessor macro (in my opinion) is sizeof. Most people seem to think this is actually a function, for some reason, and use it like "sizeof(string)". This usage is not incorrect, it just, to be blunt, makes you look like an idiot.

sizeof is useful for strings or variable dimension arrays. The most prime example is like so:

Code:
new var[] = "zomg" copy(var,sizeof var - 1,"gomz")

The size of var can change, but the sizeof will keep up with it anyway. A common misconception is to use:

Code:
copy(var,sizeof var,"gomz")

This is totally incorrect and can lead to buffer overflows. The reason is that sizeof returns the size of the entire array, not the size that would make it format perfectly (or rather 1 cell less than it really is). This means that, if the string copied is longer than the buffer size, the 0 (null terminator) will be copied onto a variable outside of the declared bounds.

There are many, many more preprocessor macros (such as #endinput, #error, #assert), but I will not go into them as they are not very important, and it's not really worth pushing this farther than it is already.

Like always, and questions or comments can be posted about.
__________________
Hawk552 is offline
Send a message via AIM to Hawk552
 



Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off

Forum Jump


All times are GMT -4. The time now is 05:46.


Powered by vBulletin®
Copyright ©2000 - 2024, vBulletin Solutions, Inc.
Theme made by Freecode