5

我正在尝试使用一个函数来确保我需要的表已经创建,如果没有创建它。这是示例:

ensure_table_exists(Table, MnesiaTables, Nodes) ->
case lists:member(Table, MnesiaTables) of
    true ->
        throw({error, db_might_have_already_been_created});
    false ->
        mnesia:create_table(Table, [{disc_copies, Nodes},
                {attributes, record_info(fields, Table)}]), 
        ok  
end.

问题是编译时出现错误:illegal record info. 可能必须在编译时解析record_info,或者记录信息的第二个参数实际上应该是可以从源代码中检索的记录?

4

3 回答 3

5

是的,所有与记录相关的事情record_info/2在编译时解决。这意味着在编译时必须知道记录和字段名称。这就是编译器错误的原因。

我不认为您的功能真的太具有防御性,因为您正在做的是发出更具体的错误信号。如果你要回来 ,那就另当别论了{error, ...}

最后一点是,如果您要引发异常,则不应使用throw/1,而应使用erlang:error/1. throw用于非本地返回(被 a 捕获catch),而erlang:error用于引发异常。在许多情况下,结果可能相同,但实际错误值可能具有误导性 ( nocatch)。你越能清楚地表明你的意图总是越好,在这种情况下,这是一个错误信号。

PS 是的,我知道catch也会捕获错误/退出。这是故意的。在一个完美的世界中,也许catch应该只捕获抛出并且try只捕获错误/退出。

于 2010-09-10T17:42:51.160 回答
3

不幸的是,即使它看起来像一个函数,record_info 也不是一个真正的函数。

您可以通过测试以下内容来验证这一点。创建一个文件:

 -module(something).
 -record(a, {}).

启动 Erlang shell:

 > rr(something).
 [a]
 > record_info(fields, a).
 []
 > A = a.
 > record_info(fields, A).
 * 2: illegal record info

所以我的建议是对 record_info 部分使用宏或专用函数。

回答你原来的问题。使用类似的东西:

 tables() ->
   [?TABLE_MACRO(tablename),
    ?TABLE_MACRO(tablename2),
    ...].

其中 TABLE_MACRO 类似于:

 -define(TABLE_MACRO(Table), fun() ->
      mnesia:create_table(Table, [{disc_copies, Nodes},
               {attributes, record_info(fields, Table)}])
      end).

然后使用类似下面的功能。

 [case CreateTable of
   {aborted, {already_exists, _}} -> ok;
   {atomic, ok} -> ok
  end || CreateTable <- tables()].

呸!可以清理很多,但希望您了解总体思路。

  • 宏而不是变量的使用。
  • 匹配 {atomic, ok} 和 {aborted, {already_exists, _TableName}}
于 2010-09-13T15:28:54.407 回答
0

您可能想看看Ulf Wiger 的exprecs

从描述中读取:

该模块是一个解析转换,允许您导出记录。转换添加了用于实例化、检查和修改记录的访问器函数,而无需在模块之间引入编译时依赖关系。

话虽如此,你的功能对我来说听起来有点防御性。以下页面解释了为什么在 Erlang 中进行防御性编程是一个坏习惯:

http://www.erlang.se/doc/programming_rules.shtml#HDR11

于 2010-09-10T14:01:00.513 回答