查看源代码 预处理器
文件包含
可以使用以下方式包含文件
-include(File).
-include_lib(File).File,一个字符串,用于指向一个文件。此文件的内容将按原样包含在指令的位置。
包含文件通常用于多个模块共享的记录和宏定义。建议为包含文件使用文件扩展名 .hrl。
File 可以以路径组件 $VAR 开头,其中 VAR 是一个字符串。在这种情况下,环境变量 VAR 的值(由 os:getenv(VAR) 返回)将替换 $VAR。如果 os:getenv(VAR) 返回 false,则 $VAR 将保持原样。
如果文件名 File 是绝对路径(可能在变量替换之后),则包含该名称的包含文件。否则,将在以下目录中按顺序搜索指定的文件
- 当前工作目录
- 正在编译的模块所在的目录
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.- 仅允许在ifdef、ifndef、if和elif指令之后。如果前面的指令的计算结果为 false,则计算else后面的行。-if(Condition).- 仅当Condition的计算结果为 true 时才计算以下行。-elif(Condition).- 仅允许在if或另一个elif指令之后。如果前面的if或elif指令的计算结果不为 true,并且Condition的计算结果为 true,则改为计算elif后面的行。-endif.- 指定一系列控制流指令的结束。
注意
宏指令不能在函数内部使用。
在语法上,if 和 elif 中的 Condition 必须是 保护表达式。其他构造(例如 case 表达式)将导致编译错误。
与标准的保护表达式相反,if 和 elif 中的表达式还支持调用伪函数 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)]).也就是说,一个带有调用的函数和结果值的跟踪输出。