184 lines
7.6 KiB
Plaintext
184 lines
7.6 KiB
Plaintext
chapter 12 "The LPC Pre-Compiler"
|
|
Intermediate LPC
|
|
Descartes of Borg
|
|
November 1993
|
|
|
|
Chapter 4: The LPC Pre-Compiler
|
|
|
|
4.1 Review
|
|
The previous chapter was quite heavy, so now I will slow down a bit so
|
|
you can digest and play with mappings and arrays by taking on the
|
|
rather simple topic of the LPC pre-compiler. By this point, however,
|
|
you should well understand how the driver interacts with the mudlib and
|
|
be able to code objects which use call outs and heart beats. In addition,
|
|
you should be coding simple objects which use mappings and arrays,
|
|
noting how these data types perform in objects. It is also a good idea to
|
|
start looking in detail at the actual mudlib code that makes up your mud.
|
|
See if you understand everything which is going on in your mudlibs
|
|
room and monster codes. For things you do not understand, ask the
|
|
people on your mud designated to answer creator coding questions.
|
|
|
|
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.
|
|
|
|
4.2 Pre-compiler Directives
|
|
If you do not know what a pre-compiler is, you really do not need to
|
|
worry. With respect to LPC, it is basically a process which happens
|
|
before the driver begins to interpret LPC code which allows you to
|
|
perform actions upon the entire code found in your file. Since the code
|
|
is not yet interpreted, the pre-compiler process is involved before the file
|
|
exists as an object and before any LPC functions or instructions are ever
|
|
examined. The pre-compiler is thus working at the file level, meaning
|
|
that it does not deal with any code in inherited files.
|
|
|
|
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
|
|
#pragma
|
|
|
|
Most 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
|
|
#undefine
|
|
|
|
The #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 __NIGHTMARE
|
|
|
|
exists 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:
|
|
|
|
#include <filename>
|
|
#include "filename"
|
|
|
|
If 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.
|
|
|
|
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);
|
|
#endif
|
|
|
|
which 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.
|
|
|
|
4.3 Summary
|
|
The pre-compiler is a useful LPC tool for maintaining modularity among
|
|
your programs. When you have values that might be subject to change,
|
|
but are used widely throughout your files, you might stick all of those
|
|
values in a header file as #define statements so that any need to make a
|
|
future change will cause you to need to change just the #define directive.
|
|
A very good example of where this would be useful would be a header
|
|
file called money.h which includes the directive:
|
|
#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
|