檢視原始碼 預處理器

檔案包含

可以如下包含檔案

-include(File).
-include_lib(File).

File,一個字串,用於指出檔案。此檔案的內容會原封不動地包含在指示詞的位置。

包含檔案通常用於在多個模組之間共享的記錄和巨集定義。建議使用檔案名稱副檔名 .hrl 作為包含檔案。

File 可以以路徑組件 $VAR 開頭,其中 VAR 為某個字串。如果符合這種情況,則會將環境變數 VAR 的值(由 os:getenv(VAR) 傳回)替換為 $VAR。如果 os:getenv(VAR) 傳回 false,則 $VAR 會保持不變。

如果檔案名稱 File 是絕對路徑(可能在變數替換之後),則會包含該名稱的包含檔案。否則,會依序在下列目錄中搜尋指定的檔案:

  1. 目前的工作目錄
  2. 正在編譯模組的目錄
  3. 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. - 僅允許在 ifdefifndefifelif 指示詞之後。如果先前的指示詞評估為 false,則會評估 else 後面的行。

  • -if(Condition). - 僅當 Condition 評估為 true 時,才會評估後續行。

  • -elif(Condition). - 僅允許在 if 或另一個 elif 指示詞之後。如果先前的 ifelif 指示詞未評估為 true,且 Condition 評估為 true,則會改為評估 elif 後面的行。

  • -endif. - 指定一系列控制流程指示詞的結尾。

注意

巨集指示詞無法在函式內部使用。

從語法上來說,ifelif 中的 Condition 必須是保護運算式。其他結構(例如 case 運算式)會導致編譯錯誤。

與標準保護運算式相反,ifelif 中的運算式也支援呼叫虛擬函式 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)]).

也就是說,具有函式呼叫和結果值的追蹤輸出。