9.预处理器 | 9. Preprocessor
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扩展为包含参数标记的字符串。这与#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)]).也就是说,一个跟踪输出,函数调用和结果值都是这样。
本文档系腾讯云开发者社区成员共同维护,如有问题请联系 cloudcommunity@tencent.com

