Erlang 20

9.预处理器 | 9. Preprocessor

9预处理程序

9.1文件包含

文件可以包括如下:

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

File,一个字符串,是指出一个文件。该文件的内容按原样包含在指令的位置。

包含文件通常用于由多个模块共享的记录和宏定义。建议使用.hrl包含文件的文件扩展名。

File可以从一个路径组件开始$VAR,用于某些字符串VAR。如果是这种情况,则VAR返回的环境变量的值将os:getenv(VAR)被替换$VAR。如果os:getenv(VAR)返回false$VAR则保持原样。

如果文件名File是绝对的(可能在变量替换之后),则包含具有该名称的包含文件。否则,将在以下目录中搜索指定的文件,并按以下顺序:

  • 当前工作目录

  • 正在编译模块的目录。

  • include选项给出的目录

有关详细信息,请参阅erlc(1)ERTS中的compile(3)手册页和编译器中的手册页。

例子:

-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)查找当前(最新)版本的内核的目录,然后include搜索该文件的子目录file.hrl

9.2定义和使用宏

宏的定义如下:

-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

9.3预定义宏

预定义了下列宏:

?MODULE当前模块的名称。?MODULE_STRING**当前模块的名称,作为字符串。?FILE* *。当前模块的文件名。?LINE**当前行号。?MACHINE* *。机器名称'BEAM'?FUNCTION_NAME当前函数的名称。?FUNCTION_ARITY当前函数的参数(参数个数)。

9.4宏超载

除了预定义的宏之外,可以重载宏。一个重载的宏有多个定义,每个定义都有不同数量的参数。

该功能添加到Erlang 5.7.5/OTP R13B04中。

?Func(Arg1,...,ArgN)用的错误消息中的参数的结果(可能为空)列表中,如果有至少一个定义Func具有参数,但没有与N个参数。

假设这些定义:

-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().

9.5宏中的流量控制

提供了下列宏指令:

-undef(Macro).导致宏表现得好像它从未被定义过一样。-ifdef(Macro).只有在Macro定义时才评估以下几行。-ifndef(Macro).仅在Macro未定义时才评估以下几行。-else.只有经过允许ifdefifndef指令。如果该条件为假,else则会对下面的行进行评估。-endif.指定一个ifdefifndef指令的结束。

宏指令不能在函数内部使用。

例子:

-module(m). ... -ifdef(debug). -define(LOG(X), io:format("{~p,~p}: ~p~n", [?MODULE,?LINE,X])). -else. -define(LOG(X), true). -endif. ...

当需要跟踪输出时,需要debug在模块m编译时进行定义:

% erlc -Ddebug m.erl or 1> c(m, {d, debug}). {ok,m}

?LOG(Arg)然后扩展为调用io:format/2并为用户提供一些简单的跟踪输出。

9.6 -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()OTP 19中添加了指令。

9.7强化宏观论点

宏的参数??Arg在哪里构建,Arg扩展为包含参数标记的字符串。这与#argC中的串化结构类似。

例子:

-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)]).

也就是说,一个跟踪输出,函数调用和结果值都是这样。