Pre-compiler is actually a bit of a misnomer since LPC code is never truly compiled. Although this is changing with prototypes of newer LPC drivers, LPC drivers interpret the LPC code written by creators rather than compile it into binary format. Nevertheless, the LPC pre- compiler functions still perform much like pre-compilers for compiled languages in that pre-compiler directives are interpreted before the driver even starts to look at object code.
The pre-compiler searches a file sent to it for pre-compiler directives. These are little instructions in the file meant only for the pre-compiler and are not really part of the LPC language. A pre-compiler directive is any line in a file beginning with a pound (#) sign. Pre-compiler directives are generally used to construct what the final code of a file will look at. The most common pre-compiler directives are:
#define #undefine #include #ifdef #ifndef #if #elseif #else #endif #pragmaMost realm coders on muds use exclusively the directives #define and #include. The other directives you may see often and should understand what they mean even if you never use them. The first pair of directives are:
#define #undefineThe #define directive sets up a set of characters which will be replaced any where they exist in the code at precompiler time with their definition. For example, take:
#define OB_USER "/std/user"This directive has the pre-compiler search the entire file for instances of OB_USER. Everywhere it sees OB_USER, it replaces with "/std/user". Note that it does not make OB_USER a variable in the code. The LPC interpreter never sees the OB_USER label. As stated above, the pre- compiler is a process which takes place before code interpretation. So what you wrote as:
#define OB_USER "/std/user" void create() { if(!file_exists(OB_USER+".c")) write("Merde! No user file!"); else write("Good! User file still exists!"); }would arrive at the LPC interpreter as:
void create() { if(!file_exists("/std/user"+".c")) write("Merde! No user file!"); else write("Good! User file still exists!"); }Simply put, #define just literally replaces the defined label with whatever follows it. You may also use #define in a special instance where no value follows. This is called a binary definition. For example:
#define __NIGHTMAREexists in the config file for the Nightmare Mudlib. This allows for pre- compiler tests which will be described later in the chapter.
The other pre-compiler directive you are likely to use often is #include. As the name implies, #include includes the contents of another file right into the file being pre-compiled at the point in the file where the directive is placed. Files made for inclusion into other files are often called header files. They sometimes contain things like #define directives used by multiple files and function declarations for the file. The traditional file extension to header files is .h.
Include directives follow one of 2 syntax's:
#includeIf you give the absolute name of the file, then which syntax you use is irrelevant. How you enclose the file name determines how the pre- compiler searches for the header files. The pre-compiler first searches in system include directories for files enclosed in <>. For files enclosed in "", the pre-compiler begins its search in the same directory as the file going through the pre-compiler. Either way, the pre-compiler will search the system include directories and the directory of the file for the header file before giving up. The syntax simply determines the order.#include "filename"
The simplest pre-compiler directive is the #pragma directive. It is doubtful you will ever use this one. Basically, you follow the directive with some keyword which is meaningful to your driver. The only keyword I have ever seen is strict_types, which simply lets the driver know you want this file interpreted with strict data typing. I doubt you will ever need to use this, and you may never even see it. I just included it in the list in the event you do see it so you do not think it is doing anything truly meaningful.
The final group of pre-compiler directives are the conditional pre- compiler directives. They allow you to pre-compile the file one way given the truth value of an expression, otherwise pre-compile the file another way. This is mostly useful for making code portable among mudlibs, since putting the m_delete() efun in code on a MudOS mud would normally cause an error, for example. So you might write the following:
#ifdef MUDOS map_delete(map, key); #else map = m_delete(map, key); #endifwhich after being passed through the pre-compiler will appear to the interpreter as:
map_delete(map, key);on a MudOS mud, and:
map = m_delete(map, key);on other muds. The interpreter never sees the function call that would cause it to spam out in error.
Notice that my example made use of a binary definition as described above. Binary definitions allow you to pass certain code to the interpreter based on what driver or mudlib you are using, among other conditions.
#define HARD_CURRENCIES ({ "gold", "platinum", "silver", "electrum", "copper" })so that if ever you wanted to add a new hard currency, you only need change this directive in order to update all files needing to know what the hard currencies are.
The LPC pre-compiler also allows you to write code which can be ported without change among different mudlibs and drivers. Finally, you should be aware that the pre-compiler only accepts lines ending in carriage returns. If you want a multiple line pre-compiler directive, you need to end each incomplete line with a backslash(\).
Copyright (c) George Reese 1993