Phantasmal MUD Lib for DGD
|
Phantasmal Site > DGD > DGD Reference Manual > LPC > The Preprocessor The PreprocessorThe preprocessor isn't a part of the LPC language proper. It's a special process that is run before the actual compilation of the program occurs. It can be seen as a very smart string translator. Specified strings in the code are replaced by other strings. All preprocessor directives are given as strings starting with the character # on the first non-whitespace column of the line. It's considered good practice to put preprocessor directives on the very far left of the code, with the # in the very first column. The #include statementThis is by far the most common preprocessor command. It simply tells the preprocessor to replace that line with the contents of an entire other file before going any further. Data you put in included files is usually data that won't ever change and that you'll be referencing in several files. Instead of having to copy and paste the same lines into multiple places and maintaining multiple copies, you simply collect that data in an include file and include it in the program files as appropriate. Included file names traditionally end in .h. The syntax for inclusion is simple: #include <standard_file> #include "special_file" Note the absence of a There are two different ways to write this. Which you use depends on where the file is that you want to include. There are a usually standard include files which may be in any of several different directories. Rather than having to remember exactly where they are, you can just give the name of the file you want to include if it's in a standard include directory. #include <limits.h> #include <types.h> If you want to include files that aren't in the standard include path, for example files of your own, you have to specify where they are. You can do that either relative to the position of the file that uses it or by an absolute path. (NOTE: does DGD allow non-absolute include paths?) #include "/d/Genesis/login/login.h" #include "my_defs.h" #include "/sys/adverbs.h" /* Same as the shorter one above */ When you include standard files, always use the <>-path notation. Not only is it shorter and easier to distinguish, but also if the files move around then files included with relative or absolute syntax won't be found. If you use the special include syntax then they will be found in any standard directory, even if they move between them. It's possible to include LPC files -- entire files full of code. Doing so is normally considered very bad form. Error handling usually has a bad time tracing errors in included files -- there are frequently problems with line numbers. Since you include the uncompiled code into several different objets, you will waste memory and CPU for these identical functions and variables. It also tends to be harder to figure out where functions are defined. What does the extension of the file name really have to do with the contents then? Technically, nothing at all. But the convention is to keep functions in .cc files and definitions in .h files. Many mudlibs enforce this, and accept only certain file suffixes for compilation or other tasks. The #define statement#define is a very powerful macro or preprocessor command that can be abused endlessly. It's recommended that you use it with caution and only for simple tasks. Using it for complex tasks tends to make reading or debugging your code quite difficult. The syntax is as follows: #define <pattern> <substitute pattern> #undef <pattern> Any text in the file that matches Although the preprocessor allows #define labels to be any sort of legal text, it is customary to use only capital letters. This is so that they will be easily distinguishable as what they are. Place all defines in the beginning of the file. Again, the compiler doesn't enforce this but it's a very good idea. It guarantees not only that they are easy to find, but also that they're usable throughout the file. Common defines include paths, names and above all constants of any kind. By defining them, you avoid writing them over and over. #define MAX_LOGIN 100 /* Max logged on players */ #define MY_USER "/usr/System/my_user" /* My user object */ #define GREET_TEXT "Welcome!" /* The login message */ Anywhere the pattern strings above occur in the file where they're defined (and any file that includes it), they will be replaced by the defined value above. The substitution includes the comments above, but they're ignored by the compiler. DRIVER->message(GREET_TEXT + "\n"); If a macro extends beyond the end of the line you can terminate
the lines with a #define LONG_DEFINE "beginning of string \ and end of the same" Function-like defines are fairly common and often abused. It's important to write macros such that every argument to the macro is enclosed in parenthesis where it's used. If you don't do that you can end up with some very strange results. 1: #define MUL_IT(a, b) a * b /* Wrong */ 2: #define MUL_IT(a, b) (a * b) /* Not enough */ 3: #define MUL_IT(a, b) ((a) * (b)) /* Correct */ What's the big difference? Look at this example: result = MUL_IT(2 + 3, 4 * 5) / 5; Expanded with the three different macros, this becomes: 1: result = 2 + 3 * 4 * 5 / 5; /* = 14, Wrong */ 2: result = (2 + 3 * 4 * 5) / 5 /* = 12, Still wrong */ 3: result = ((2 + 3) * (4 * 5)) / 5 /* = 20, Correct! */ Common problems with defined constants and functions include badly formulated macros, complicated macros used inside other macros (making the code almost impossible to understand) or humongous arrays or mappings in defines that are used often. The basic rule is to keep macros short and fairly simple. The #if, #ifdef, #ifndef, #else and #elseif statementsThe commands above are all preprocessor directives aimed at selecting certain parts of code and perhaps removing others depending on the state of a preprocessor variable. The Assume you may have one of the following definitions somewhere: #define code_VAR 2 or #define code_VAR 3 Then you can write #if code_VAR == 2 <code that will be kept only if code_VAR == 2> #else <code that will be kept only if code_VAR != 2> #endif You don't have to have the It's sufficient to have the following statement to 'define' a preprocessor pattern as existing: #define code_VAR /* This defines the existance of code_VAR */ Then you can use #ifdef code_VAR <code that will be kept only if code_VAR is defined> #else <code that will be kept only if code_VAR isn't defined> #endif or you can use #ifndef, which is essentially an #ifdef with an extra "not" implied. #ifndef code_VAR <code that will be kept only if code_VAR isn't defined> #else <code that will be kept only if code_VAR is defined> #endif Again, the The CommentsComments may seem like an odd thing to start with, but they're everywhere so you need to be able to recognize them from the very start. They're also important, so you need to know how to write them from the very start. In some LPC dialects, there are two kinds of comments: <code> // This is a comment stretching to the end of the line. // NOT SUPPORTED BY DGD! <code> /* This is an enclosed comment */ <more code>The first type of comment starts off with the //
characters and then stretches all the way to the end of the line.
If you want more lines of comments, you'll have to start off those
as well with new // characters. This is like C++
single-line comments, and DGD does not support them!
The second type has a definite length. Those comments start with
Please note that the /* A comment /* A nested comment */ the first continues */What will happen is that the comment will end with the first */ found, leaving the text the first continues
*/ to be interpreted as if it was LPC code. That's not valid
LPC code, so instead you'll get an error.
|