
                     LPC 前置处理区 (preprocessor) 手册
                    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
                              (93.07.17 更新)

对 LPC  编译器 (compiler) 而言, 前置处理区是程式码的最前端, 它提供以下
便利的特性:

    o	共享的定义和程式码 (#include)
    o	巨集 (macros) (#define, #undef)
    o	编译条件 (#if, #ifdef, #ifndef, #else, #elif, #endif)
    o	除错 (#echo)
    o	使用特定的编译器功能 (#pragma)
    o	快速的文章格式 (@, @@)

前三个与 C  相同. 如果您熟悉 C  语言, 大概只会浏览此份文件的最後几段文字.

说明:
    前面以 #  开头的命令 (像是 #include), 其 #  符号必须摆在每行的第一
    个字, 前面不可留有空白.


共享的定义和程式码
~~~~~~~~~~~~~~~~~~
#include 提供此功能.

    语法 1 :   #include <file.h>

    语法 2 :   #include "file.h"

说明:
    语法 1  的形式, 会在系统的标准引用 (include)  目录中, 寻找 file.h.
     (在 TMI  中是 /include)

    语法 2  的形式, 会在程式档案所在的同一个目录中, 寻找欲引用的 file.h

#include  叙述是以文字命令的方式, 在一个档案中引用另一个档案. 在一个档案
中放上像 #include "file.h"  的叙述, 就如同 file.h 的内容直接放入 #include
所在的位置. 在每次物件重新编译时, 物件所引用的档案也会重新编译一次. 如果
引用的档案里面有变数或函式与此档案中的相同, 编译此档案时会产生双重命名的
错误 (duplicate-name error)  (如果您把 file.h 的内容放在 #include 的位置
, 一样会产生错误). 


巨集
~~~~
巨集定义将後面程式码中的一些字换成定义的内容. 目的在於隐藏程式内部的细节
、减少打字的数量、便於更改常数 (constant).

    语法 1 :	#define identifier token_sequence

    语法 2 :	#define identifier(id_list) token_sequence

说明:
	惯例上, 程式码中的 identifier 使用大写英文字母以强调它们所出现的
	位置, 并且在程式码开端或是所引用的表头档 (header file)  中定义其
	内容.

	第二种语法让 id_list 列出的 identifier 能代换成 token_sequence.

□例:

    //  造出一个有 40 个元素的整数阵列, 并将所有元素的值初始化为其编号的
    //  两倍.
    //  即 stack[0] = 0, stack[1] = 2, stack[2] = 4 ....以此类推.

    #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);
    }

最後, 有时会用到清除定义 (undefine) ［让编译器 (compiler) 别理会此定义］
. 这样就用到下面的命令.

    语法 :	#undef identifier

说明:
    #undef 可以清除一个根本不存在的 identifier.


编译条件
~~~~~~~~
这些命令可以让您的程式更具有可塑性 (flexibility). 利用 identifier 定义与
否, 可以为不同的目的而变化程式码. 用途像是挑选系统管理者连线、支援多个驱
动程式 (或不同版本的驱动程式).

    语法 :	#ifdef <identifier>
                #ifndef <identifier>
                #if <expression>
                #elif <expression>
                #else
                #endif

说明:
    <identifier>  是一个已经定义 (或应该定义) 的 identifier.  它应於您的
    程式或是已经引用的档案事先定义, 不然就是驱动程式已定义的符号 (symbol).

    <expression>  是一个常数运算式 (constant expression), 计算布林
    (boolean) 逻辑条件. 运算式中, 可以在合於语法的情形下, 使用下面的组合:

        运算子: ||, &&, >>, <<,
                +, -, *, /, %,
                &, |, ^, !, ~,
                ==, !=, <, >, <=, >=, ?:
        用来分组的小括号: (, )
        呼叫形式: defined(identifier)
        以及 identifiers

    #ifdef identifier
	可以视为这行的缩写:
            #if defined(identifier)

    #ifndef identifier
	可以视为这行的缩写:
            #if !defined(identifier)

    #elif expression
	可以视为这些的缩写:
            #else
            #  if expression
            #  endif

□例 1:

    //  使用 #if 0  可以让你在一段程式码中加上注解 (comment). 这样做的其
	中一个理由是把旧的程式码保留在原位, 防止新的程式失败.
    #if 0
        //  在此, 常数运算式算出 (或说本来就是) 0 , 所以此段程式码不予编
	    译.

        write(user_name + " 有 " + total_coins + " 元.\n");
    #else
        //  这是另外的情形 (if 算出零值, else 就是非零值), 所以会编译此段
	//  程式.

        printf("%s 有 %d 元\n", user_name, total_coins);
    #endif

□例 2:

    //  这个□例由 TMI 的 /adm/simul_efun/system.c 改写而来.
    #ifdef __VERSION
          string version() { return __VERSION__; }
    #elif defined(MUDOS_VERSION)
          string version() { return MUDOS_VERSION; }
    #else
    #  if defined(VERSION)
          string version() { return VERSION; }
    #  else
          string version() { return -1; }
    #  endif
    #endif


除错
~~~~
#echo 命令让您向驱动程式的 stderr (STanDard ERRor, 标准错误处理) 印出讯息.
这项功能在诊断和除错时非常有用.

    语法:     #echo This is a message

说明:
    #echo 这行就是讯息的内容, 并逐字印出. 讯息前後不用加上 ". 


特定的编辑器功能
~~~~~~~~~~~~~~~~
这项是驱动程式内建的功能.

    语法:	#pragma keyword

目前可用的 keyword 列在下面:

    o   strict_types
    o   save_binary
    o   save_types
    o   warnings
    o   optimize
    o   error_context

使用 #pragma no_keyword 可以关闭指定的功能.

说明:
    'strict_types' 告诉编译器呼叫 call_other() 的函式, 其传回值不可忽略.

    'save_binary'  告诉编译器要储存此物件的二进位 (binary) 档.
		   在重新启动或关闭游戏之後, 载入物件的时间会变快, 因为
		   编译过的物件已经储存为二进位档, 不必重新编译.

    'save_types' 目前停用

    'warnings' 在您的 LPC  程式码开启一些警告措施. 这些警告不见得会依照
	       您预料中的结果反应.

    'optimize' 用一点时间改进编译过的程式码效率.
    
    'error_context'  於错误讯息中使用更多的文字指示哪一行发生错误.

快速文章格式
~~~~~~~~~~~~
这项功能便於求助讯息、房间叙述等长篇内容使用文章格式.

    语法 1 :	@marker
                  <... text block ...>
                marker
    
    语法 2 :	@@marker
                  <... text block ...>
                marker

Notes:
    @   - 产生一个适於 write() 的字串.

    @@  - 产生一个字串阵列, 适於分页文章 (body pager).
produces an array of strings, suitable for the body pager

在结束标记 (end marker) 之前, 使用 @marker 或 @@marker. 两者之间是您想对
使用者显示的文章. 文章以 marker 作为结束, 不必加 @ 或 @@. 使用 @, 文章就
如同一个字串, 换行处加上 \n.  使用 @@,  文章就如同一个字串阵列, 而每一行
分别是一个字串.

□例 1 :

        int help() {
            write( @ENDHELP
        这是求助文章。
        It's hopelessly inadequate.
        ENDHELP
              );
            return 1;
        }

    与此相同:

        int help() {
            write( "这是求助文章。\nIt's hopelessly inadequate.\n" );
            return 1;
        }

□例 2 :

        int help() {
            this_player()->more( @@ENDHELP
        这是求助文章。
        It's hopelessly inadequate.
        ENDHELP
              , 1);
            return 1;
        }

    与底下相同:

        int help() {
            this_player()->more( ({ "这是求助文章。",
                  "It's hopelessly inadequate." }), 1);
            return 1;
        }

翻译:	Spock @ FF	97.Aug.9.
