首页
学习
活动
专区
工具
TVP
发布
精选内容/技术社群/优惠产品,尽在小程序
立即前往

9.预处理器 | 9. Preprocessor

9.1文件包含

文件可以包括如下:

代码语言:javascript
复制
-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)手册页和编译器中的手册页。

例子:

代码语言:javascript
复制
-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,但不是指出一个绝对的文件。相反,第一个路径组件(可能在变量替换之后)被假定为应用程序的名称。

例子:

代码语言:javascript
复制
-include_lib("kernel/include/file.hrl").

代码服务器用来code:lib_dir(kernel)查找当前(最新)版本的内核的目录,然后include搜索该文件的子目录file.hrl

9.2定义和使用宏

宏的定义如下:

代码语言:javascript
复制
-define(Const, Replacement).
-define(Func(Var1,...,VarN), Replacement).

宏定义可以放在模块的属性和函数声明之间,但是定义必须在宏的任何使用之前。

如果在多个模块中使用宏,建议将宏定义放在包含文件中。

宏使用如下:

代码语言:javascript
复制
?Const
?Func(Arg1,...,ArgN)

编译期间宏会扩展。一个简单的宏?Const被替换为Replacement

例子:

代码语言:javascript
复制
-define(TIMEOUT, 200).
...
call(Request) ->
    server:call(refserver, Request, ?TIMEOUT).

这扩展到:

代码语言:javascript
复制
call(Request) ->
    server:call(refserver, Request, 200).

一个宏?Func(Arg1,...,ArgN)被替换为Replacement,其中Var来自宏定义的所有变量都被替换为相应的参数Arg

例子:

代码语言:javascript
复制
-define(MACRO1(X, Y), {a, X, b, Y}).
...
bar(X) ->
    ?MACRO1(a, b),
    ?MACRO1(X, 123)

这一范围扩大到:

代码语言:javascript
复制
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个参数。

假设这些定义:

代码语言:javascript
复制
-define(F0(), c).
-define(F1(A), A).
-define(C, m:f).

以下内容不起作用:

代码语言:javascript
复制
f0() ->
    ?F0. % No, an empty list of arguments expected.

f1(A) ->
    ?F1(A, A). % No, exactly one argument expected.

另一方面,

代码语言:javascript
复制
f() ->
    ?C().

扩展为

代码语言:javascript
复制
f() ->
    m:f().

9.5宏中的流量控制

提供了下列宏指令:

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

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

例子:

代码语言:javascript
复制
-module(m).
...

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

...

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

代码语言:javascript
复制
% erlc -Ddebug m.erl

or

1> c(m, {d, debug}).
{ok,m}

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

9.6 -error()和-warning()指令

该指令-error(Term)导致编译错误。

例子:

代码语言:javascript
复制
-module(t).
-export([version/0]).

-ifdef(VERSION).
version() -> ?VERSION.
-else.
-error("Macro VERSION must be defined.").
version() -> "".
-endif.

错误消息如下所示:

代码语言:javascript
复制
% erlc t.erl
t.erl:7: -error("Macro VERSION must be defined.").

指令-warning(Term)导致编译警告。

例子:

代码语言:javascript
复制
-module(t).
-export([version/0]).

-ifndef(VERSION).
-warning("Macro VERSION not defined -- using default version.").
-define(VERSION, "0").
-endif.
version() -> ?VERSION.

警告消息如下所示:

代码语言:javascript
复制
% erlc t.erl
t.erl:5: Warning: -warning("Macro VERSION not defined -- using default version.").

该指令-error()-warning()OTP 19中添加了指令。

9.7强化宏观论点

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

例子:

代码语言:javascript
复制
-define(TESTCALL(Call), io:format("Call ~s: ~w~n", [??Call, Call])).

?TESTCALL(myfunction(1,2)),
?TESTCALL(you:function(2,1)).

结果

代码语言:javascript
复制
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)]).

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

扫码关注腾讯云开发者

领取腾讯云代金券