檢視原始碼 預處理器
檔案包含
可以如下包含檔案
-include(File).
-include_lib(File).
File
,一個字串,用於指出檔案。此檔案的內容會原封不動地包含在指示詞的位置。
包含檔案通常用於在多個模組之間共享的記錄和巨集定義。建議使用檔案名稱副檔名 .hrl
作為包含檔案。
File
可以以路徑組件 $VAR
開頭,其中 VAR
為某個字串。如果符合這種情況,則會將環境變數 VAR
的值(由 os:getenv(VAR)
傳回)替換為 $VAR
。如果 os:getenv(VAR)
傳回 false
,則 $VAR
會保持不變。
如果檔案名稱 File
是絕對路徑(可能在變數替換之後),則會包含該名稱的包含檔案。否則,會依序在下列目錄中搜尋指定的檔案:
- 目前的工作目錄
- 正在編譯模組的目錄
- 由
include
選項提供的目錄
如需詳細資訊,請參閱 ERTS 中的 erlc 和 Compiler 中的 compile
。
範例
-include("my_records.hrl").
-include("incdir/my_records.hrl").
-include("/home/user/proj/my_records.hrl").
-include("$PROJ_ROOT/my_records.hrl").
include_lib
類似於 include
,但不是指出絕對檔案。而是假設第一個路徑組件(可能在變數替換之後)是應用程式的名稱。
範例
-include_lib("kernel/include/file.hrl").
程式碼伺服器使用 code:lib_dir(kernel)
來尋找目前(最新)版本 Kernel 的目錄,然後在子目錄 include
中搜尋檔案 file.hrl
。
定義和使用巨集
巨集的定義如下
-define(Const, Replacement).
-define(Func(Var1,...,VarN), Replacement).
巨集定義可以放置在模組的屬性和函式宣告之間的任何位置,但定義必須在任何巨集使用之前。
如果巨集在多個模組中使用,建議將巨集定義放在包含檔案中。
巨集的使用方式如下
?Const
?Func(Arg1,...,ArgN)
巨集會在編譯期間展開。簡單巨集 ?Const
會被 Replacement
取代。
範例
-define(TIMEOUT, 200).
...
call(Request) ->
server:call(refserver, Request, ?TIMEOUT).
這會展開為
call(Request) ->
server:call(refserver, Request, 200).
巨集 ?Func(Arg1,...,ArgN)
會被 Replacement
取代,其中巨集定義中變數 Var
的所有出現次數都會被對應的引數 Arg
取代。
範例
-define(MACRO1(X, Y), {a, X, b, Y}).
...
bar(X) ->
?MACRO1(a, b),
?MACRO1(X, 123)
這會展開為
bar(X) ->
{a,a,b,b},
{a,X,b,123}.
確保巨集定義是有效的 Erlang 語法形式是一種良好的編程習慣,但並非強制性。
若要檢視巨集展開的結果,可以使用 'P'
選項編譯模組。compile:file(File, ['P'])
。這會在檔案 File.P
中產生預處理和剖析轉換後已剖析程式碼的清單。
預定義巨集
下列巨集為預先定義
?MODULE
- 目前模組的名稱,以原子形式表示。?MODULE_STRING
- 目前模組的名稱,以字串形式表示。?FILE
- 目前模組的檔案名稱,以字串形式表示。?LINE
- 目前的行號,以整數形式表示。?MACHINE
- 機器名稱,'BEAM'
。?FUNCTION_NAME
- 目前函式的名稱,以原子形式表示。?FUNCTION_ARITY
- 目前函式的算數(引數數量),以整數形式表示。?OTP_RELEASE
- 執行編譯器的執行時間系統的 OTP 版本,以整數形式表示。例如,當使用 Erlang/OTP 27 進行編譯時,巨集會傳回27
。注意
若要在執行時間找出版本,請呼叫
erlang:system_info(otp_release)
。請注意,它會以字串形式傳回版本。例如,當版本為 Erlang/OTP 27 時,會傳回字串"27"
。變更
?OTP_RELEASE
巨集是在 Erlang/OTP 21 中引入的。?FEATURE_AVAILABLE(Feature)
- 如果 功能Feature
可用,則會展開為true
。該功能可能會也可能不會啟用。變更
?FEATURE_AVAILABLE()
巨集是在 Erlang/OTP 25 中引入的。?FEATURE_ENABLED(Feature)
- 如果 功能Feature
已啟用,則會展開為true
。變更
?FEATURE_ENABLED()
巨集是在 Erlang/OTP 25 中引入的。
巨集多載
可以多載巨集,預先定義的巨集除外。多載巨集具有多個定義,每個定義都有不同數量的引數。
變更
Erlang 5.7.5/OTP R13B04 中加入了對巨集多載的支援。
如果 Func
至少有一個帶引數的定義,但沒有帶 N 個引數的定義,則具有(可能為空的)引數清單的巨集 ?Func(Arg1,...,ArgN)
會導致錯誤訊息。
假設這些定義
-define(F0(), c).
-define(F1(A), A).
-define(C, m:f).
則下列程式碼無法運作
f0() ->
?F0. % No, an empty list of arguments expected.
f1(A) ->
?F1(A, A). % No, exactly one argument expected.
另一方面,
f() ->
?C().
會展開為
f() ->
m:f().
移除巨集定義
可以透過以下方式移除巨集的定義
-undef(Macro).
條件編譯
下列巨集指示詞支援條件編譯
-ifdef(Macro).
- 僅當定義Macro
時才評估後續行。-ifndef(Macro).
- 僅當未定義Macro
時才評估後續行。-else.
- 僅允許在ifdef
、ifndef
、if
和elif
指示詞之後。如果先前的指示詞評估為 false,則會評估else
後面的行。-if(Condition).
- 僅當Condition
評估為 true 時,才會評估後續行。-elif(Condition).
- 僅允許在if
或另一個elif
指示詞之後。如果先前的if
或elif
指示詞未評估為 true,且Condition
評估為 true,則會改為評估elif
後面的行。-endif.
- 指定一系列控制流程指示詞的結尾。
注意
巨集指示詞無法在函式內部使用。
從語法上來說,if
和 elif
中的 Condition
必須是保護運算式。其他結構(例如 case
運算式)會導致編譯錯誤。
與標準保護運算式相反,if
和 elif
中的運算式也支援呼叫虛擬函式 defined(Name)
,以測試 Name
引數是否為先前定義的巨集名稱。defined(Name)
如果定義了巨集,則會評估為 true
,否則會評估為 false
。嘗試呼叫其他函式會導致編譯錯誤。
範例
-module(m).
...
-ifdef(debug).
-define(LOG(X), io:format("{~p,~p}: ~p~n", [?MODULE,?LINE,X])).
-else.
-define(LOG(X), true).
-endif.
...
如果想要追蹤輸出,則在編譯模組 m
時,會定義 debug
% erlc -Ddebug m.erl
or
1> c(m, {d, debug}).
{ok,m}
然後,?LOG(Arg)
會展開為對 io:format/2
的呼叫,並為使用者提供一些簡單的追蹤輸出。
範例
-module(m)
...
-if(?OTP_RELEASE >= 25).
%% Code that will work in OTP 25 or higher
-elif(?OTP_RELEASE >= 26).
%% Code that will work in OTP 26 or higher
-else.
%% Code that will work in OTP 24 or lower.
-endif.
...
此程式碼會使用 OTP_RELEASE
巨集,根據版本有條件地選取程式碼。
範例
-module(m)
...
-if(?OTP_RELEASE >= 26 andalso defined(debug)).
%% Debugging code that requires OTP 26 or later.
-else.
%% Non-debug code that works in any release.
-endif.
...
此程式碼會使用 OTP_RELEASE
巨集和 defined(debug)
,僅針對 OTP 26 或更新版本編譯偵錯程式碼。
-feature() 指示詞
指示詞 -feature(FeatureName, enable | disable)
可用於啟用或停用功能 FeatureName
。這是啟用(停用)功能的首選方式,儘管也可以使用編譯器的選項來執行。
請注意,-feature(..)
指示詞只能在任何語法使用之前出現。實際上,這表示它應該出現在任何 -export(..)
或記錄定義之前。
-error() 和 -warning() 指示詞
指示詞 -error(Term)
會導致編譯錯誤。
範例
-module(t).
-export([version/0]).
-ifdef(VERSION).
version() -> ?VERSION.
-else.
-error("Macro VERSION must be defined.").
version() -> "".
-endif.
錯誤訊息會像這樣
% erlc t.erl
t.erl:7: -error("Macro VERSION must be defined.").
指示詞 -warning(Term)
會導致編譯警告。
範例
-module(t).
-export([version/0]).
-ifndef(VERSION).
-warning("Macro VERSION not defined -- using default version.").
-define(VERSION, "0").
-endif.
version() -> ?VERSION.
警告訊息會像這樣
% erlc t.erl
t.erl:5: Warning: -warning("Macro VERSION not defined -- using default version.").
變更
-error()
和-warning()
指示詞是在 Erlang/OTP 19 中新增的。
字串化巨集引數
建構 ??Arg
(其中 Arg
是巨集引數)會展開為包含引數符號的字串。這類似於 C 中的 #arg
字串化建構。
範例
-define(TESTCALL(Call), io:format("Call ~s: ~w~n", [??Call, Call])).
?TESTCALL(myfunction(1,2)),
?TESTCALL(you:function(2,1)).
會產生
io:format("Call ~s: ~w~n",["myfunction ( 1 , 2 )",myfunction(1,2)]),
io:format("Call ~s: ~w~n",["you : function ( 2 , 1 )",you:function(2,1)]).
也就是說,具有函式呼叫和結果值的追蹤輸出。