如何在Erlang R17中使用-extends

时间:2014-07-06 10:48:04

标签: erlang extends

在最近的Erlang版本中删除了-extends功能

我有一些遗留模块大量使用-extends

如何让它在R17中运行?

2 个答案:

答案 0 :(得分:2)

这是一项实验性功能,已在R17中删除。

因此,如果您真的必须使用R17,则必须在所有子模块中添加每个继承函数的定义:

inherited_function (Param) -> parent:inherited(Param).

我编写了一个简单的代码,插入所需的函数并直接导出到现有代码中。它需要在src目录中工作,可以访问ebin目录(调用Module:module_info(exports)) - 所以我会在项目的副本上使用它:o)。它对extend指令做了一些假设(没有空白字符),并且没有将添加的export指令拆分成几行,这可能是个问题,但它适用于我的例子

要修改的代码示例: 根模块

-module (gd_father).

-export([gdf1/0,gdf2/1,gdf2/2]).

gdf1() -> gd_father.

gdf2(P) -> {gd_father,P}.

gdf2(P1,P2) when is_list(P2) -> [gd_father,P1|P2];
gdf2(P1,P2) -> [gd_father,P1,P2].

儿童等级1

-module (father).

-extends(gd_father).

-export([ft1/1,ft2/0]).

-export([gdf2/1]).

ft1(X) -> {father,X}.

ft2() -> father.

gdf2(P1) -> {ovl_father,P1}.
2岁儿童

-module (child1).

-extends(father).

-export ([cd1/0]).

-export ([ft1/1]).

cd1() -> child1.

ft1(X) -> {ovl_child1,X}.

-module (child2).

-extends(father).

-export ([cd2/0]).

-export ([ft2/0,gdf1/0]).

cd2() -> child2.

ft2() -> ovl_child2.

gdf1() -> ovl_child2.

一个测试模块,用于验证它是否正常工作(使用R17进行编译在-extend指令使用情况下不会出错!)

-module (test).

-export([t/0]).

t() ->
    gd_father = gd_father:gdf1(),
    {gd_father,3} = gd_father:gdf2(3),
    [gd_father,1,2] = gd_father:gdf2(1,2),
    [gd_father,1,2,3] = gd_father:gdf2(1,[2,3]),

    {father,4} = father:ft1(4),
    father = father:ft2(),
    {ovl_father,5} = father:gdf2(5),
    [gd_father,1,2] = father:gdf2(1,2),
    gd_father = father:gdf1(),
    [gd_father,1,2,3] = father:gdf2(1,[2,3]),

    child1 = child1:cd1(),
    {ovl_child1,test} = child1:ft1(test),
    father = child1:ft2(),
    {ovl_father,5} = child1:gdf2(5),
    [gd_father,1,2] = child1:gdf2(1,2),
    gd_father = child1:gdf1(),
    [gd_father,1,2,3] = child1:gdf2(1,[2,3]),

    child2 = child2:cd2(),
    ovl_child2 = child2:ft2(),
    ovl_child2 = child2:gdf1(),
    {father,4} = child2:ft1(4),
    {ovl_father,5} = child2:gdf2(5),
    [gd_father,1,2] = child2:gdf2(1,2),
    [gd_father,1,2,3] = child2:gdf2(1,[2,3]),

    ok.

转换模块:

-module (transform).

-export([transform_dir/0,transform_file/1]).


transform_dir() ->
    {ok,AllFiles} = file:list_dir("."),
    Files = [list_to_atom(lists:sublist(X,length(X)-4)) || X <- AllFiles, is_src(lists:reverse(X))],
    transform_file(Files).

transform_file([]) -> ok;
transform_file([H|Q]) ->
    {_,_,Add,Father,Lines} = transform_file(H),
    mod_file(H,Add,Father,Lines),
    transform_file(Q);
transform_file(Name) ->
    FileName = atom_to_list(Name) ++ ".erl",
    {ok,Bin} = file:read_file(FileName),
    List = binary_to_list(Bin),
    Lines = string:tokens(List,"\n"),
    Father = to_atom([string:strip(X) || X <- Lines, is_extend(X)]),
    OrExp = Name:module_info(exports),
    case Father of
        none -> {OrExp,OrExp,[],none,[]};
        Father ->   {_,Fa_Tot,_,_,_} = transform_file(Father),
                Or = lists:usort(OrExp),
                Tot = lists:usort(Fa_Tot ++ OrExp),
                Add = lists:usort(lists:subtract(Tot,Or)),
                {Or,Tot,Add,Father,Lines}
    end.

is_extend([$-,$e,$x,$t,$e,$n,$d,$s|_]) -> true;
is_extend(_) -> false.

is_src([$l,$r,$e,$.|_]) -> true;
is_src(_) -> false.

to_atom([]) -> none;
to_atom([L]) ->
    list_to_atom(tl(lists:takewhile(fun(X) -> X =/= $) end , lists:dropwhile(fun(X) -> X =/= $( end,L)))).

mod_file(_,[],_,_) -> ok;
mod_file(F,L,Father,Lines) ->
    Export = lists:flatten(["\n%Replace expends directive by export\n-export([",
              tl(lists:flatten([", " ++ atom_to_list(X) ++ "/" ++ integer_to_list(N) || {X,N} <- L])),
              "]).\n"]), 
    Def = lists:flatten(["\n\n%Insert relay functions to replace the expends directive\n" |[add_def(X,Father) || X <- L]]),
    NewLines = insert(Lines,Export,Def,[]),
    file:write_file(atom_to_list(F)++".erl",NewLines,[write]).

insert([],_,Def,R) -> lists:reverse([Def|R]);
insert([H|Q],Export,Def,R) ->
    Line = case is_extend(H) of
        true -> Export;
        false -> H
    end,
    insert(Q,Export,Def,[Line|R]).

add_def({N,A},F) -> 
    Args = args(A,[]),
    atom_to_list(N) ++ Args ++ " -> " ++ atom_to_list(F) ++ ":" ++ atom_to_list(N) ++ Args ++ ".\n".

args(0,[]) -> "()";
args(0,R) -> "(" ++ lists:reverse(tl(R)) ++ ")";
args(A,R) -> args(A-1,[$,,A-1+$A,$P|R]).

结果在子级别2,编译完所有模块后,测试结果正常:

-module (child1).

%Replace expends directive by export
-export([ ft2/0, gdf1/0, gdf2/1, gdf2/2]).
-export ([cd1/0]).

-export ([ft1/1]).

cd1() -> child1.

ft1(X) -> {ovl_child1,X}.

%Insert relay functions to replace the expends directive
ft2() -> father:ft2().
gdf1() -> father:gdf1().
gdf2(PA) -> father:gdf2(PA).
gdf2(PB,PA) -> father:gdf2(PB,PA).

答案 1 :(得分:0)

您可以编写一个parse transform,在每次函数调用之前插入父模块的名称,这是1)不合格的; 2)不参考当前模块中的功能; 3)没有提到内置功能。

不应该太困难但是如果你真的大量使用extends我仍然会这样做,因为这会使代码不易理解,如果抽象格式发生变化则可能需要维护。