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.
只有经过允许ifdef
或ifndef
指令。如果该条件为假,else
则会对下面的行进行评估。-endif.
指定一个ifdef
或ifndef
指令的结束。
注
宏指令不能在函数内部使用。
例子:
-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
扩展为包含参数标记的字符串。这与#arg
C中的串化结构类似。
例子:
-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)]).
也就是说,一个跟踪输出,函数调用和结果值都是这样。