求助:宏展开时显示 UndefVarError 错误

一个普通的模块定义,其中包含宏 @extend

module mymacro
    export @extend

    macro extend(name,vecs)
        @eval Base.length(m::$name)=length(m.$vecs)
    end
end

然后定义一个结构体

mutable struct shell
    blocks::Vector
    function shell()
        c1 = "a"
        c2 = "b"
        c3 = "c"
        new([c1,c2,c3])
    end
end

对结构体执行宏的时候结果得到

julia> mymacro.@extend(shell,blocks)
ERROR: LoadError: UndefVarError: shell not defined
Stacktrace:
 [1] top-level scope
   @ none:1
 [2] eval(m::Module, e::Any)
   @ Core .\boot.jl:360
 [3] var"@extend"(__source__::LineNumberNode, __module__::Module, name::Any, vecs::Any)
   @ Main.mymacro .\REPL[7]:6
in expression starting at REPL[9]:1

但是当我在模块外部定义同样宏的时候

macro extend2(name,vecs)
    @eval Base.length(m::$name)=length(m.$vecs)
end

然后执行

julia> @extend2(shell,blocks)

julia> s = shell();length(s)
3

却可以得到想要的效果。使用模块里的宏扩展模块外的结构体的方法就不行? :rofl:
求解决方案!谢谢! :heartpulse:

宏展开有上下文,直接 @eval 会在宏定义处展开并执行表达式。
可以直接返回表达式,并推迟展开类型名,让它在调用处求值。

module mymacro
    export @extend

    macro extend(name, vecs)
        quote
            Base.length(m::$(esc(name)))=length(m.$vecs)
        end
    end
end

调用输出

julia> module mymacro
           export @extend
           macro extend(name, vecs)
               quote
                   Base.length(m::$(esc(name)))=length(m.$vecs)
               end
           end
           macro old_extend(name,vecs)
               quote
                   Base.length(m::$name)=length(m.$vecs)
               end
           end
       end
Main.mymacro

julia> Main.mymacro.@extend shell blocks

julia> length(shell())
3

julia> macroexpand(Main, :(Main.mymacro.@extend shell blocks))
quote
    #= REPL[2]:5 =#
    (Main.mymacro.Base).length(var"#4#m"::shell) = begin
            #= REPL[2]:5 =#
            Main.mymacro.length((var"#4#m").blocks)
        end
end

julia> macroexpand(Main, :(Main.mymacro.@old_extend shell blocks))
quote
    #= REPL[2]:10 =#
    (Main.mymacro.Base).length(var"#3#m"::Main.mymacro.shell) = begin
            #= REPL[2]:10 =#
            Main.mymacro.length((var"#3#m").blocks)
        end
end

文档里有类似的例子:
注意它是在调用处直接 @eval 的,所以指定了类型名。

https://docs.juliacn.com/latest/manual/metaprogramming/#代码生成

1 个赞

确实,换了后解决了我的问题,非常感谢 :pray:t2: :beers: