[轉載] [蟑螂賀失戀] 中階 LPC 第3~4章
看板mud_sanc (Sanctuary - 聖殿)作者laechan (小太保)時間17年前 (2009/03/06 15:35)推噓0(0推 0噓 0→)留言0則, 0人參與討論串1/2 (看更多)
作者 spock.bbs@bbs.csmc.edu.tw (打混的蟑螂史巴克), 看板 Mud
標題 [蟑螂賀失戀] 中階 LPC - 第三章 - 複雜資料型態
時間 中山醫學院BBS站 (Sat Jul 25 00:08:42 1998)
───────────────────────────────────────
中階 LPC
Descartes of Borg
November 1993
第三章: 複雜資料型態
3.1 簡單的資料型態
在基礎 LPC 課本裡, 你學到常見的基本 LPC 資料型態: 整數 (int)、字串
(string)、物件 (object) 、無傳回值 (void). 重要的是, 你學到很多運算式
(operation) 和函式 (function) 會因為運算不同的變數資料型態而有不同的行
為. 如果"b" 處理起來就跟 1 + 1 不同. "a" + "b" 把 "b" 加在 "a" 的
如: "a" + "b" 處理起來就跟 1 + 1 不同. "a" + "b" 把 "b" 加在 "a" 的
後面, 得到 "ab". 另一方面, 1 + 1 你不會得到 11, 你會得到你所期望的 2.
我把這些資料型態歸類為簡單資料型態, 因為它們基本到無法拆成更小的資料型
態元件. 物件資料型態是個例外, 但是你實際上也沒辦法知道它由什麼元素組成.
所以我把它歸類為簡單資料型態.
本章介紹複雜資料型態的概念, 它是由許多簡單資料型態單元所組成的. LPC 有
兩種常見的複雜資料型態, 兩種都屬於陣列. 第一種, 傳統的陣列 (array), 以
連續的各個元素儲存數值, 並以數字代表所儲存的值在第幾號元素 (element)
中. 第二種是稱為映射 (mapping) 的關聯性陣列 (associative array). 映射
把一些數值結合起來, 讓資料處理起來更接近一般人的習性.
3.2 數值: NULL (虛無無值 (null value) 由整數 0 代表之. 雖然整數 0 和
NULL 常常隨意轉換, 在你進入複雜資料型態的領域時, 這種情況常會導致莫大
的困擾. 你可能在使用字串時, 已經碰過此種困擾.
0 對整數來說, 表示你把任何數值加上 0 還是原來的數值. 對任何資料型態的
加法運算來說, ZERO (零值) 對此資料型態來講, 就是你把任何值加上去都維持
原值. 所以: A 加 ZERO 等於 A. A 是一個已定資料型態的值, 而且 ZERO 是該
資料型態的零值. 這並不算是任何一種正式的數學定義. 雖然還是有一種定義,
但是我不是數學家, 所以我也不知道它的名詞是什麼. 總之對整數來說, 0 是零
值, 因為 1 + 0 等於 1.
另一方面來說, NULL 表示沒有任何值或沒有意義. LPC driver 如果能了解 NULL
在該處的意義, 就把 NULL 解釋成整數 0. 除了整數的加法以外, 加上 NULL
會導致錯誤. NULL 產生錯誤的原因是, 把那些資料型態加上其他沒有值的資料
型態沒有意義.
從另一個觀點來看, 我們知道 "a" 加上什麼值結果會得到 "a", 所以我們可以
得出字串的零值. 答案不是 0, 而是 "". 對整數來說, NULL 和 0 可以互換
是因為 0 代表整數資料型態沒有其值. 這種可互換性對其他的資料型態並不適
用, 因為其他資料型態的零值並不代表沒有其值. 換句話說, "" 表示一個沒有
長度的字串, 而它與 0 完全不一樣.
當你第一次宣告任何型態的變數, 它都沒有值. 除了整數以外, 在執行任何運算
之前都需要經過初始化. 通常, 全域變數在 create() 函式中初始化, 而區域變
數在區域函數的開端指定某些值, 通常是該資料型態的零值. 舉例來說, 在以下
的程式碼中, 我想要作出一個由隨機單字組成的字串:
string build_nonsense() {
string str;
int i;
str = ""; /* 在此, str 以字串的零值初始化 */
for(i=0; i<6; i++) {
switch(random(3)+1) {
case 1: str += "bing"; break;
case 2: str += "borg"; break;
case 3: str += "foo"; break;
}
if(i==5) str += ".\n";
else str += " ";
}
return capitalize(str);
}
如果我們沒有對 str 初始化, 嘗試把一個字串加上零值會導致錯誤. 不過, 在
此段程式碼中將 str 以字串的零值 "" 初始化. 之後, 程式進入一個有六次週
期的迴圈, 每次把字串加上三個單字的其中一個. 除了最後一個單字之外, 每個
單字後面均加上一個空白字元. 此函式最後離開迴圈, 把這個無意義的字串轉換
成大寫, 然後結束.
3.3 LPC 的陣列 (array)
字串是 LPC 一種強大的複雜資料型態, 讓你在一個單一變數中存取多個值. 舉
例來說, Nightmare mud 中, 玩家交易時使用多種貨幣. 但是, 其中只有五種貨
幣是硬貨幣 (hard currency). 在此, 硬貨幣隨時可以兌換成其他種類的硬貨幣
, 但是軟貨幣 (soft currency) 只能購買之, 不能出售. 在銀行裡, 有一張硬
貨幣表讓銀行老闆知道哪種貨幣屬於硬貨幣. 使用簡單資料型態, 每次處理貨幣
兌換交易時, 我們必須要執行以下難看的運算:
int exchange(string str) {
string from, to;
int amt;
if(!str) return 0;
if(sscanf(str, "%d %s for %s", amt, from, to) != 3)
return 0;
if(from != "platinum" && from != "gold" && from !=
"silver" &&
from != "electrum" && from != "copper") {
notify_fail("我們不接受軟貨幣 !\n");
return 0;
}
...
}
以五種硬貨幣來說, 我們有一個相當簡單的例子. 全部只需要兩行的程式碼, 用
於 if 敘述中過濾不接受兌換的貨幣種類. 但是, 如果你必須檢查所有遊戲中不
能使用的貨幣種類, 怎麼辦 ? 遊戲中可能有 100 種; 你想寫一百條 if 敘述 ?
如果你想在硬貨幣表上加上一種新的貨幣呢 ? 這表示, 你必須把遊戲中每一項
檢查硬貨幣的 if 子句加入新的部分. 陣列讓你簡易地存取一組相關的資料, 讓
你每次執行運算時, 不用分別處理每一個值.
一個陣列常數看起來大概像這樣:
({ "platinum", "gold", "silver", "electrum", "copper" })
這是一個字串陣列. 陣列中個別的資料值稱為元素 (element), 或是有時候稱為
成員 (member). 在程式碼裡, 作為常數的字串前後以 "" 表示, 陣列常數前後
以 ({ }) 表示, 陣列中個別的元素以 , (逗號) 分開.
你可以使用任何簡單的或複雜的 LPC 資料型態陣列. 由不同種類的值所組成的
陣列稱作混合 (mixed) 型態陣列. 在多數的 LPC driver 中, 你使用一種 C
語言的陣列語法來宣告陣列. 這種語法常常困擾撰寫 LPC 程式的人, 因為這種
語法在 C 中的意義並不能轉用到 LPC 中. 無論如何, 如果我們想用一個字串
型態的陣列, 我們要用以下的方式宣告它:
string *arr;
換句話說, 陣列中包含的元素, 其資料型態之後跟著一個空白字元和一個星號.
不過請你記住, 新宣告的字串陣列, 其宣告時裡頭是 NULL 值.
3.4 使用陣列
你應該了解如何宣告並認識程式碼中的陣列. 要了解它們在程式碼中如何運作,
讓我們回顧一下前面銀行的程式碼, 這次我們用陣列:
string *hard_currencies;
int exchange(string str) {
string from, to;
int amt;
if(!str) return 0;
if(sscanf(str, "%d %s for %s", amt, from, to) != 3)
return 0;
if(member_array(from, hard_currencies) == -1) {
notify_fail("我們不接受軟貨幣 !\n");
return 0;
}
...
}
這段程式碼假設 hard_currencies 是一個全域變數, 並且在 create() 中初始
化:
hard_currencies = ({ "platinum", "gold", "electrum", "silver",
"copper" });
最佳的做法是把硬貨幣在標頭檔 (header file) 中定義為 #define, 讓所有的
物件都能使用之, 不過 #define 在以後的章節會提到.
一旦你知道 member_array() 外部函式的功能後, 這種方式就比較容易讀懂, 也
比較容易撰寫. 實際上, 你大概已經猜到 member_array() 外部函式的功能: 它
告訴你一個指定的值是否在某個陣列中. 此處特別是指, 我們想知道玩家想賣出
的貨幣是否為 hard_currencies 陣列中的元素. 你可能會感到混淆的是,
member_array() 不只告訴我們特定值是否為陣列中的元素, 實際上還告訴我們
陣列中的哪一個元素是此值.
它要怎麼告訴你是哪個元素 ? 如果你把陣列變數當作是擁有一個數字, 就比較
容易瞭解它. 對上面的參數舉例來說, 我們假設 hard_currencies 擁有 179000
的值. 這個值告訴 driver 要到哪裡尋找 hard_currencies 所代表的陣列. 所
以, hard_currencies 指向一個可以找到陣列值的地方. 當有人談到陣列的第一
個元素時, 它們希望該元素位於 179000. 當一個物件需要陣列第二個元素的值
時, 它就找 179000 + 一個值, 然後 179000 加上兩個值就是第三個, 以此類推.
我們因此可以藉由陣列元素的索引來存取個別的陣列元素, 索引就是在陣列起點
之後第幾個值, 而我們在陣列中找尋數值. 對 hard_currencies 陣列來說:
"platinum" 索引為 0.
"gold" 索引為 1.
"electrum" 索引為 2.
"silver" 索引為 3.
"copper" 索引為 4.
如果在陣列中有此種貨幣, member_array() 傳回其元素的索引, 如果陣列中沒
有則傳回 0. 要參考一個陣列中的單獨元素時, 你要照著以下的方式使用之:
陣列名稱[索引號]
範例:
hard_currencies[3]
hard_currencies[3] 會是 "silver".
所以, 你現在應該知道陣列以全體或個別元素出現的方式. 全體而言, 你用它的
名稱參考 (reference) 之, 而一個陣列常數前後以 ({ }) 圍住, 並且用 ,
(逗號) 分隔其元素. 對個別的元素而言, 你用陣列名稱跟著前後加上 [] 的索
引號碼來參考陣列變數, 而對陣列常數來說, 你可以如同相同型態的簡單資料型
態常數般參考之.
整個陣列:
變數: arr
常數: ({ "platinum", "gold", "electrum", "silver", "copper" })
陣列中個別的元素:
變數: arr[2]
常數: "electrum"
你可以將這些參考的方式, 用於你以前習慣其他資料型態的方法. 你可以指定其
值、將其值用於運算式中、將其值當成參數傳入函式中、用其值當作傳回值. 請
記得一件很重要的事, 當你單獨處理一個元素時, 單獨的元素本身不是陣列 (除
非你處理的是陣列的陣列). 在上述的範例中, 單獨的元素是字串. 所以:
str = arr[3] + " and " + arr[1];
會造出一個字串等於 "silver and gold". 雖然這看起來很簡單, 很多剛開始接
觸陣列的人試著在陣列中加入新元素時, 就遇到麻煩. 當你處理整個陣列, 並想
要加入新元素時, 你必須用另一個陣列加上去.
注意以下的例子:
string str1, str2;
string *arr;
str1 = "hi";
str2 = "bye";
/* str1 + str2 等於 "hibye" */
arr = ({ str1 }) + ({ str2 });
/* arr 等於 ({ str1, str2 }) */
更深入以前, 我必須說明這個製作陣列的例子是極為恐怖的方法. 你應該這樣來
設定陣列: arr = ({ str1, str2 }). 不過, 這個例子的重點是, 你必須以同樣
的資料型態進行加法. 如果你試著把一個元素以其資料型態加入一個陣列, 你會
得到錯誤. 你必須將它視為一個只有單一元素的陣列處理之.
3.5 映射 (mapping)
LPMud 中, 一個最重要的進步是創立了映射資料型態. 大家亦稱它為關聯性陣列.
實際上來說, 一個陣列讓你不用像陣列般使用數字索引一個值. 映射讓你使用實
際上對你有意義的值當作其值的索引, 比較像一個相關的資料庫 (relational
database).
在一個有五個元素的陣列中, 你個別使用它們 0 到 4 的整數索引存取這些值.
想像一下, 再回到錢幣的範例中. 玩家有不同數量、不同種類的錢幣. 在玩家物
件中, 你需要一個方法儲存這些錢幣的種類, 並把該種貨幣與玩家有多少數量連
結起來. 對陣列來說, 最好的方法就是儲存一個表示錢幣種類的字串陣列, 和另
一個整數陣列代表有多少錢. 這樣會產生一段吃光 CPU 的難看程式碼:
int query_money(string type) {
int i;
i = member_array(type, currencies);
if(i>-1 && i < sizeof(amounts)) /* sizeof 外部函式傳回元素的總數 */
return amounts[i];
else return 0;
}
這是一個簡單的查詢函式. 接下來看一個加法函式:
void add_money(string type, int amt) {
string *tmp1;
int * tmp2;
int i, x, j, maxj;
i = member_array(type, currencies);
if(i >= sizeof(amounts)) /* 錯誤的資料, 我們用了一個爛方法 */
return;
else if(i== -1) {
currencies += ({ type });
amounts += ({ amt });
return;
}
else {
amounts[i] += amt;
if(amounts[i] < 1) {
tmp1 = allocate(sizeof(currencies)-1);
tmp2 = allocate(sizeof(amounts)-1);
for(j=0, x =0, maxj=sizeof(tmp1); j < maxj;
j++) {
if(j==i) x = 1;
tmp1[j] = currencies[j+x];
tmp2[j] = amounts[j+x];
}
currencies = tmp1;
amounts = tmp2;
}
}
}
這實在是一些很爛的程式碼, 只為了增加錢這種簡單的概念. 首先, 我們要得知
玩家有哪些種類的錢幣, 如果有, 它是貨幣陣列中的哪一個元素. 之後, 我們必
須檢查更動過之貨幣資料是否完整. 如果在貨幣陣列中, 貨幣種類的索引大於錢
幣數量陣列的元素總數, 則我們就出了問題. 因為這兩個陣列之間僅靠索引連結
其關係. 只要我們知道資料正確無誤, 如果玩家手上目前沒有該種貨幣, 我們僅
把這種貨幣當作新的元素加入貨幣陣列, 並把其數量也當作新元素加入數量陣列.
最後, 如果玩家手上持有該種貨幣, 我們就把其數量加在數量陣列中相對的索引
上. 如果錢幣數量小於 1, 表示用完該種貨幣, 我們想把該種貨幣從記憶體中清
除之.
從一個陣列中減去一個陣列不是一件簡單的事. 舉個例子, 下面的結果:
string *arr;
arr = ({ "a", "b", "a" });
arr -= ({ arr[2] });
你認為 arr 最後的值是多少 ? 唔, 它是:
({ "b", "a" })
從原來的陣列減去 arr[2] 並不會從該陣列中除去第三個元素. 反之, 它從該陣
列減去其第三個元素的值. 而陣列的減法是把該陣列中第一次出現的該值刪除之.
既然我們不想被迫去計算該元素在陣列中是否唯一, 我們就被迫要翻幾個觔斗以
從兩個陣列中同時除去正確的元素. 如此才能保持兩個陣列索引的關聯性.
映射提供了一個比較好的方式. 它們讓你直接把錢幣種類和其總數連結在一起.
有些人認為映射就相當於, 一種不限制你只能用整數當索引的陣列. 事實上, 映
射是一種徹底不同的概念, 用於儲存多個集團資訊. 陣列強迫你選擇一種對機器
才有意義的索引, 該索引用於尋找正確資料位置之用. 這種索引告訴機器在首值
之後第幾個元素才是你想要找的值. 而映射, 你可以選擇對你有意義的索引, 不
用擔心機器要怎麼去尋找和儲存它.
以下是映射的格式:
常數:
整個: ([ 索引:值, 索引:值 ]) 例: ([ "gold":10, "silver":20 ])
元素: 10
變數值:
整個: map (map 是映射變數的名稱)
元素: map["gold"]
所以現在我的貨幣函式看起來像:
int query_money(string type) { return money[type]; }
void add_money(string type, int amt) {
if(!money[type]) money[type] = amt;
else money[type] += amt;
if(money[type] < 1)
map_delete(money, type); /* 用於 MudOS */
...或...
money = m_delete(money, type) /* 用於 LPMud 3.* 衍生版本 */
... 或...
m_delete(money, type); /* 用於 LPMud 3.* 衍生版本 */
}
請先注意, 從一個映射中清除一個映射元素的外部函式, 每種 driver 都不同.
查詢你的 driver 文件說明, 以得知適當的外部函式名稱及語法.
你可以馬上看到, 你不需要檢查你資料的完整性, 因為你想得知的兩個值密不
可分地結合在一起. 另外, 刪除無用的值只需要一個簡單的外部函式呼叫, 不
用一個繁雜而耗費 CPU 的迴圈. 最後, 查詢的函式只需要一行 return 指令.
使用映射以前, 你必須宣告並將其初始化.
宣告看來如下:
mapping map;
而通常初始化看來如下:
map = ([]);
map = allocate_mapping(10) ...OR... map = m_allocate(10);
map = ([ "gold": 20, "silver": 15 ]);
跟其他的資料型態一樣, 它們通常的運算也有其規則定義, 像是加法和減法:
([ "gold":20, "silver":30 ]) + ([ "electrum":5 ])
得到:
(["gold":20, "silver":30, "electrum":5])
雖然我的示範顯示出映射有個順序, 但是實際上, 映射在儲存元素時, 不保證會
遵照其順序. 所以, 最好別比較兩個映射是否相等.
3.6 總結
映射和陣列可以依照你的需求, 要有多複雜就有多複雜. 你可以造出一個陣列的
映射的陣列. 這種東西可以宣告如下:
mapping *map_of_arrs;
它看起來像:
({ ([ ind1: ({valA1, valA2}), ind2: ({valB1, valB2}) ]),
([ indX: ({valX1,valX2}) ]) })
映射可以使用任何一種資料型態作為索引, 包括物件. 映射索引常常稱作關鍵
(key), 是來自資料庫的名詞. 你隨時要謹記在心, 對於任何非整數的資料型
態而言, 作一般像是加法或減法的運算使用之前, 你必須先將其變數初始化.
雖然利用映射和陣列撰寫 LPC 程式變得簡單又方便, 沒有正確地將其初始化
所產生的錯誤, 常常把剛接觸這種資料型態的新手逼瘋. 我敢說大家最常碰到
映射和陣列的錯誤, 是以下三者之一:
Indexing on illegal type.
Illegal index.
Bad argument 1 to (+ += - -=) /* 看你最喜歡哪一種運算 */
第一個和第三個幾乎都是因為出問題的陣列或映射沒有正確初始化. 第二種錯誤
訊息通常是當你試著使用一個已初始化過的陣列中所沒有的索引. 另外, 對陣列
來說, 剛接觸陣列的人常得到第三種錯誤訊息, 因為他們常試著將一個單獨的元
素加入一個陣列, 把初始的陣列與單一的元素值相加, 而沒有把一個含有該單一
元素的陣列與初始的陣列相加. 請記住, 只能把陣列加上陣列.
行文至此, 你應該覺得能自在地使用映射和陣列. 剛開始使用它們時, 應會碰上
以上的錯誤訊息. 使用映射成功的關鍵, 在於除去這些錯誤訊息, 並找出你程式
設計上, 何處使你試著使用沒有初始化的映射和陣列. 最後, 回到最基本的房間
程式碼, 並看看像是 set_exits() 之類的函式 (或在你的 mudlib 上相當的函
式). 它有可能使用映射. 在某些情況下, 它會使用陣列以保持與 mudlib.h 的
相容性.
Copyright (c) George Reese 1993
譯者: Spock of the Final Fronier 98.Jul.24.
第四章: LPC 前編譯器 (pre-compiler)
4.1 回顧
上一章的份量相當重, 所以我現在的步調會放慢一些, 藉由 LPC 前編譯器這個
簡單的課題, 讓你能消化並使用映射和陣列. 不過在此, 你應該相當了解 driver
如何與 mudlib 互動, 並能撰寫呼叫延遲呼叫和心跳的物件. 附帶一提, 你應該
撰寫一些使用映射和陣列的簡單物件, 注意這些資料型態如何在物件中使用. 開
始閱讀實際的 mudlib 程式碼是個不錯的主意, 這樣能讓你製作你自己的 mud.
看看你自己是否了解你的 mudlib 房間和怪物程式碼其中的每一件事. 對你不懂
的事, 就詢問你 mud 中負責回答創作人程式碼問題的人.
前編譯器實際上有點誤導人, 因為 LPC 碼永遠不會真正編譯過. 雖然這一點隨
著新的 LPC driver 原型而漸漸改變, LPC driver 解譯創作人所寫的程式碼,
而非編譯為二進位格式. 雖然如此, LPC 前編譯器的功能仍然表現得比較像是編
譯語言的前編譯器, 其指令甚至在 driver 開始看物件碼之前就已解譯.
4.2 前編譯器指令
如果你不知道什麼是前編譯器, 你不用擔心. 對 LPC 而言, 它基本上是在
driver 開始解譯 LPC 碼, 以讓你執行檔案中整段程式碼的動作之前的一個程
序. 因為程式碼還未解譯, 前編譯器程序在檔案以物件存在之前、檢查任何 LPC
函式和指令之前執行. 所以前編譯器在檔案層次上工作, 表示它並不會處理任何
在繼承檔案中的程式碼.
前編譯器在送給它的檔案中尋找前編譯器指令. 這些檔案中的小指令只對前編譯
器有意義, 並不算是 LPC 語言的一部份. 一個前編譯器指令是在檔案中任何以
# 號開頭的一行. 前編譯器指令通常用於製造一個檔案看起來的最終程式碼. 最
常見的前編譯器指令是:
#define
#undefine
#include
#ifdef
#ifndef
#if
#elseif
#else
#endif
#pragma
mud 裡大多數的區域碼撰寫人並不使用 #define 和 #include 指令. 其他你常
見的指令即使你從未用過, 你也大概知道它們的意義.
第一對指令是:
#define
#undefine
#define 指令設定一組字元, 這組字元在程式碼中的任何地方都會在前編譯器處
理時段換成它們所定義的東西.
舉例:
#define OB_USER "/std/user"
這個指令讓前編譯器尋找整個檔案中是否有 OB_USER. 任何有 OB_USER 的地方,
它就換成 "/std/user". 注意, OB_USER 在程式碼中並不算是變數. LPC 解譯器
永遠不會看到 OB_USER 的標籤. 前面已經說過, 前編譯器是在程式碼解譯之前
的一段過程. 所以你所寫的:
#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!");
}
到了 LPC 解譯器的手上就變成:
void create() {
if(!file_exists("/std/user"+".c")) write("Merde! No user file!");
else write("Good! User file still exists!");
}
只要放個 #define, 它就會將定義的標籤換成標籤後面的任何東西. 你也可以把
#define 用於一種特殊的情況, 標籤後面不跟著任何值. 這種情形稱為二進位定
義 (binary definition). 舉個例子:
#define __NIGHTMARE
出現在 Nightmare Mudlib 的組態檔 (config) 中. 這樣讓前編譯器測試一些東
西, 我們在本章稍後會說明.
其他你常用的前編譯器指令是 #include. 正如其名字所暗示的, #include 在
前編譯時將其他檔案的內容放入該指令出現的地方. 專為其他檔案納入而製作的
檔案常稱為標頭檔 (header file). 它們有時候含有一些東西被很多檔案共用,
像是 #define 指令和函式宣告. 標頭檔傳統的檔案延伸名是 .h .
include 指令的語法有兩種:
#include <filename>
#include "filename"
如果你用檔案的絕對名稱, 則你用哪一種語法都無所謂. 檔案名稱前後使用什麼
符號決定前編譯器如何尋找標頭檔. 用 <> 圍住的檔案, 前編譯器首先尋找系統
include 目錄. 用 "" 圍住的檔案, 前編譯器開始從前編譯器正在處理的檔案所
在之目錄找起. 不然在放棄之前, 前編譯器會尋找系統 include 目錄和該檔案
所在的目錄. 使用的語法決定了尋找的順序.
最簡單前編譯器指令是 #pragma 指令. 我懷疑你大概從未使用過. 基本上, 你
在 #pragma 指令之後跟著對 driver 有意義的一些關鍵字. 我唯一見過的關鍵
字是 strict_types, 它讓 driver 知道你希望這個檔案以嚴格資料型態解譯之.
我懷疑你會需要使用這種指令, 而且你可能從未看過它. 我在此介紹它, 只是因
為當你看到它時, 不會讓你認為它實際上不具有任何意義.
最後一組前編譯器指令是條件前編譯器指令 (conditional pre-compiler
directives) . 它們讓你在一個運算式為真值時, 以一種方式前編譯一個檔案,
運算式為偽值時, 以另一種方式前編譯該檔案. 這是讓程式碼在不同 mudlib 之
間具有移植性 (portable) 最方便的方法, 舉例來說, 因為在 MudOS mud 的程
式碼中放入 m_delete() 外部函式會導致錯誤, 所以你大概會照著以下撰寫:
#ifdef MUDOS
map_delete(map, key);
#else
map = m_delete(map, key);
#endif
經過前編譯器處理之後, 解譯器會看到:
在 MudOS mud 中:
map_delete(map, key);
其他的 mud:
map = m_delete(map, key);
解譯器永遠看不到會產生錯誤的函式呼叫.
請注意, 我前面用於說明二進位定義的例子. 二進位定義讓你對解譯器傳入一些
程式碼, 基於其他條件下, 你所使用的 driver 或 mudlib 為何.
4.3 總結
前編譯器是在你程式之間維持模組性的有用工具. 當你有易受影響而改變的值,
而此值在你的檔案中普遍使用, 你可以在標頭檔使用 #define 敘述將它們全部
置換之, 這樣一來你以後需要改變這些值時, 只需要更改 #define 指令. 在此
最好的例子是 money.h , 它包含這個指令:
#define HARD_CURRENCIES ({ "gold", "platinum", "silver", "electrum",
"copper" })
如果你想加上新的硬貨幣, 你只需要更改這個指令, 就能更新所有需要硬貨幣為
何的檔案.
LPC 前編譯器也讓你撰寫不用隨 mudlib 和 driver 而改寫的可攜性程式碼. 最
後, 你應該小心, 前編譯器只接受以 carriage return 結束的一行字. 如果你
要撰寫一個多行的前編譯器指令, 你必須在未結束的一行末尾加上反斜線 (\).
Copyright (c) George Reese 1993
譯者: Spock of the Final Frontier 98.Jul.26.
--
Boldly go where no mudder has gone before...
Spock (roach admin) 蟑螂管理員
homepage: http://bbs.csmc.edu.tw/spock/
From The Final Frontier 140.128.136.12 4000
※ 來源:‧中山醫學院BBS -- 絮情小站 bbs.csmc.edu.tw‧[FROM: www.csmc.edu.tw]
--
※ 發信站: 批踢踢實業坊(ptt.cc)
◆ From: 218.170.228.153
討論串 (同標題文章)
完整討論串 (本文為第 1 之 2 篇):
mud_sanc 近期熱門文章
PTT遊戲區 即時熱門文章
15
19