308 lines
8.4 KiB
Plaintext
308 lines
8.4 KiB
Plaintext
LPC Preprocessor Manual
|
|
|
|
The preprocessor is a front end to the LPC compiler that provides such
|
|
handy features as:
|
|
|
|
<DL>
|
|
* sharing definitions and code (#include)
|
|
* macros (#define, #undef)
|
|
* conditional compilation (#if, #ifdef, #ifndef, #else, #elif, #endif)
|
|
* debugging (#echo)
|
|
* compiler specific (#pragma)
|
|
* text formatting short cuts (@, @@)
|
|
</DL>
|
|
|
|
The first three are identical to C usage, so those already familiar with
|
|
C may want to just skim the last few sections of this document.
|
|
|
|
Note:
|
|
For those directives that begin with '#' (such as #include), the
|
|
'#' symbol must start in the first column (of the line).
|
|
|
|
Sharing Definitions and Code
|
|
|
|
This facility is provided through the #include directive.
|
|
|
|
Syntax 1: #include <file.h>
|
|
|
|
Syntax 2: #include "file.h"
|
|
|
|
Notes:
|
|
The '#include <file.h>' form looks for the file, 'file.h' in the
|
|
system's standard include directories.
|
|
(On TMI this is just '/include'.)
|
|
|
|
The '#include "file.h"' form looks for the file, 'file.h' in the
|
|
same directory as the file that is including it.
|
|
|
|
The #include statement is a way to textually include one file into another.
|
|
Putting a statement such as '#include "file.h"' in a file gives the same
|
|
effect as if the contents of file.h had been directly entered into the file
|
|
at the point where the #include statement occurred. Included files are
|
|
recompiled each time the object that include's them is recompiled. If the
|
|
included file contains variables or functions of the same name as variables
|
|
in the file doing the including, then a duplicate-name error will occur at
|
|
compile time (in the same way that the error would occur if you simply typed
|
|
in file.h rather than using #include).
|
|
|
|
Macros
|
|
|
|
Macro definitions are used to replace subsequent instances of a given
|
|
word with a different sequence of text. Reasons for doing so include
|
|
hiding implementation details, reducing the number of keystrokes, and
|
|
ease in changing constants.
|
|
|
|
Syntax 1: #define identifier token_sequence
|
|
|
|
Syntax 2: #define identifier(id_list) token_sequence
|
|
|
|
Notes:
|
|
As a matter of convention, identifiers are usually capitalized to
|
|
emphasize their presence in the code, and defined close to the
|
|
start of program, or in a separate header file which you #include.
|
|
|
|
The second case allows identifiers in the id_list to be substituted
|
|
back into the token_sequence.
|
|
|
|
Example:
|
|
|
|
<pre>
|
|
// Create a 40 cell array of integers and initialize each cell
|
|
// to its cell number times 2,
|
|
// i.e. stack[0] = 0, stack[1] = 2, stack[2] = 4, etc
|
|
|
|
#define STACKSIZE 40
|
|
#define INITCELL(x) 2*x
|
|
|
|
int *stack;
|
|
|
|
create() {
|
|
int i;
|
|
|
|
stack = allocate(STACKSIZE);
|
|
|
|
for (i = 0; i < STACKSIZE; i++)
|
|
stack[i] = INITCELL(i);
|
|
}
|
|
</pre>
|
|
|
|
Lastly, it's sometimes useful to undefine (i.e. make the compiler forget
|
|
about) a macro. The following directive is then used:
|
|
|
|
Syntax: #undef identifier
|
|
|
|
Note:
|
|
It's perfectly acceptable to undefine an identifier that hasn't been
|
|
defined yet.
|
|
|
|
Conditional Compilation
|
|
|
|
These directives can add flexibility to your code. Based on whether an
|
|
identifier is defined (or not defined), variations of the code can be
|
|
produced for different effects. Applications include selective admin
|
|
logging and support for multiple drivers (or versions of the same driver).
|
|
|
|
<pre>
|
|
Syntax: #ifdef <identifier>
|
|
#ifndef <identifier>
|
|
#if <expression>
|
|
#elif <expression>
|
|
#else
|
|
#endif
|
|
</pre>
|
|
|
|
Note:
|
|
<identifier> refers to an identifier that has been (or could be) defined
|
|
by your program, a file you have included, or a symbol predefined by
|
|
the driver.
|
|
|
|
<expression> is a constant expression that evaluates to a boolean
|
|
condition. The expression may contain any legal combination of the
|
|
following:
|
|
</pre>
|
|
operators: ||, &&, >>, <<,
|
|
+, -, *, /, %,
|
|
&, |, ^, !, ~,
|
|
==, !=, <, >, <=, >=, ?:
|
|
</pre>
|
|
parentheses for grouping: (, )
|
|
calls of the form: defined(identifier)
|
|
and identifiers
|
|
|
|
#ifdef identifier
|
|
can be considered shorthand for:
|
|
#if defined(identifier)
|
|
|
|
#ifndef identifier
|
|
can be considered shorthand for:
|
|
#if !defined(identifier)
|
|
|
|
#elif expression
|
|
can be considered shorthand for the sequence:
|
|
#else
|
|
# if expression
|
|
# endif
|
|
|
|
Example 1:
|
|
|
|
<pre>
|
|
// Using #if 0 allows you to comment out a block of code that
|
|
// contains comments. One reason to do so may be to keep a copy
|
|
// of the old code around in case the new code doesn't work.
|
|
#if 0
|
|
// In this case, the constant expression evaluates
|
|
// (or is) 0, so the code here is not compiled
|
|
|
|
write(user_name + " has " + total_coins + " coins\n");
|
|
#else
|
|
// This is the alternate case (non-zero), so the code
|
|
// here _is_ compiled
|
|
|
|
printf("%s has %d coins\n", user_name, total_coins);
|
|
#endif
|
|
</pre>
|
|
|
|
Example 2:
|
|
|
|
</pre>
|
|
// This example is derived from TMI's /adm/simul_efun/system.c
|
|
#ifdef __VERSION
|
|
string version() { return "2.7.2"; }
|
|
#elif defined(MUDOS_VERSION)
|
|
string version() { return MUDOS_VERSION; }
|
|
#else
|
|
# if defined(VERSION)
|
|
string version() { return VERSION; }
|
|
# else
|
|
string version() { return -1; }
|
|
# endif
|
|
#endif
|
|
</pre>
|
|
|
|
Debugging
|
|
|
|
The '#echo' directive allows you to print messages to the driver's stderr
|
|
(STanDard ERRor) stream. This facility is useful for diagnostics and
|
|
debugging.
|
|
|
|
Syntax: #echo This is a message
|
|
|
|
Note:
|
|
The rest of the line (or end-of-file, which ever comes first) is the
|
|
message, and is printed verbatim. It's not necessary to enclose text
|
|
with quotes.
|
|
|
|
Compiler Specific
|
|
|
|
This facility performs implementation-dependent actions.
|
|
|
|
Syntax: #pragma keyword
|
|
|
|
At this time the following control keywords are recognized:
|
|
|
|
<DL>
|
|
* strict_types
|
|
* save_binary
|
|
* save_types
|
|
* warnings
|
|
* optimize
|
|
* show_error_context
|
|
</DL>
|
|
|
|
Notes:
|
|
'strict_types' informs the compiler that extensive type checking should
|
|
be done
|
|
|
|
'save_binary' informs the compiler to save the binary object;
|
|
loading will go faster after a reboot/shutdown since object has been
|
|
precompiled
|
|
|
|
'save_types' is saves the types of function arguments for type checking
|
|
in objects that inherit this program
|
|
|
|
'warnings' enables the printing of warnings about things the driver thinks
|
|
are dangerous or likely to be incorrect.
|
|
|
|
'optimize' directs the compiler to spend a little extra time generating
|
|
better code
|
|
|
|
'show_error_context' adds information about where on the line an error
|
|
occured to error messages
|
|
|
|
Text Formatting Shortcuts
|
|
|
|
This facility makes it easier to format text for help messages, room
|
|
descriptions, etc.
|
|
|
|
<pre>
|
|
Syntax 1: @marker
|
|
<... text block ...>
|
|
marker
|
|
</pre>
|
|
|
|
<pre>
|
|
Syntax 2: @@marker
|
|
<... text block ...>
|
|
marker
|
|
</pre>
|
|
|
|
Notes:
|
|
<pre>
|
|
@ - produces a string suitable for write()
|
|
|
|
@@ - produces an array of strings, suitable for the body pager
|
|
</pre>
|
|
|
|
These are used by prepending '@' (or '@@') before an end marker word. This
|
|
is followed by your formatted text, as you would have it appear to the user.
|
|
The text block is terminated by the end marker word, without the '@'
|
|
(or '@@'). With '@', the text block is processed as if it were a single
|
|
string surrounded by quotes and '\n' (newlines) in between the lines.
|
|
With '@@', the text block is processed as it were an array of strings,
|
|
with each line being a string surrounded by quotes.
|
|
|
|
<pre>
|
|
Example 1:
|
|
|
|
int help() {
|
|
write( @ENDHELP
|
|
This is the help text.
|
|
It's hopelessly inadequate.
|
|
ENDHELP
|
|
);
|
|
return 1;
|
|
}
|
|
|
|
</pre>
|
|
Is equivalent to:
|
|
|
|
<pre>
|
|
int help() {
|
|
write( "This is the help text\nIt's hopelessly inadequate.\n" );
|
|
return 1;
|
|
}
|
|
</pre>
|
|
|
|
Example 2:
|
|
|
|
<pre>
|
|
int help() {
|
|
this_player()->more( @@ENDHELP
|
|
This is the help text.
|
|
It's hopelessly inadequate.
|
|
ENDHELP
|
|
, 1);
|
|
return 1;
|
|
}
|
|
</pre>
|
|
|
|
Is equivalent to:
|
|
|
|
<pre>
|
|
int help() {
|
|
this_player()->more( ({ "This is the help text.",
|
|
"It's hopelessly inadequate." }), 1);
|
|
return 1;
|
|
}
|
|
</pre>
|