macros - 从 Julia 生成的函数中调用宏

标签 macros julia metaprogramming auto-generate

我一直在弄乱 Julia 中的生成函数,并遇到了一个我不完全理解的奇怪问题:我的最终目标是从内部调用宏(更具体地说是 @tullio)生成的函数(执行一些取决于输入张量的张量收缩)。但我一直遇到问题,我将问题范围缩小到从生成的函数中调用宏。

为了说明问题,让我们考虑一个也失败的非常简单的示例:

macro my_add(a,b) 
    return :($a + $b)
end

function add_one_expr(x::T) where T
    y = one(T)
    return :( @my_add($x,$y) )
end

@generated function add_one_gen(x::T) where T
    y = one(T)
    return :( @my_add($x,$y) )
end

通过这些声明,我发现 eval(add_one_expr(2.0)) 按预期工作并返回和表达式

:(@my_add 2.0 1.0)

正确计算结果为3.0

但是评估 add_one_gen(2.0) 会返回以下错误:

MethodError: no method matching +(::Type{Float64}, ::Float64)

做了一些研究,我发现@ generated实际上生成了两个代码,并且在一个代码中只能使用变量的类型。我认为这就是这里正在发生的事情,但我根本不明白发生了什么。这一定是宏和生成函数之间有一些奇怪的交互。

有人可以解释和/或提出解决方案吗?谢谢!

最佳答案

我发现将生成的函数视为具有两个组件是有帮助的:主体和任何生成的代码(quote..end)。主体在编译时进行评估,并且不“知道”值,只“知道”类型。因此,对于以 x::T 作为参数的生成函数,主体中对 x 的任何引用实际上都指向类型 T。这可能会非常令人困惑。为了让事情更清楚,我建议正文引用类型,而不是值。

这是一个小例子:

julia> @generated function show_val_and_type(x::T) where {T}
           quote
               println("x is ", x)
               println("\$x is ", $x)
               println("T is ", T)
               println("\$T is ", $T)
           end
       end
show_val_and_type

julia> show_val_and_type(3)
x is 3
$x is Int64
T is Int64
$T is Int64

插值的 $x 表示“从主体(指的是 T)中取出 x 并将其拼接进去。

如果您遵循从不引用正文中的值的方法,则可以通过删除 @ generated 来测试生成的函数,如下所示:

julia> function add_one_gen(x::T) where T
           y = one(T)
           quote
               @my_add(x,$y)
           end
       end
add_one_gen

julia> add_one_gen(3)
quote
    #= REPL[42]:4 =#
    #= REPL[42]:4 =# @my_add x 1
end

这看起来很合理,但是当我们测试它时,我们得到

julia> add_one_gen(3)
ERROR: UndefVarError: x not defined
Stacktrace:
 [1] macro expansion
   @ ./REPL[48]:4 [inlined]
 [2] add_one_gen(x::Int64)
   @ Main ./REPL[48]:1
 [3] top-level scope
   @ REPL[49]:1

让我们看看这个宏给我们带来了什么

julia> @macroexpand @my_add x 1
:(Main.x + 1)

它指向 Main.x,但它不存在。宏太急切了,我们需要延迟它的评估。标准方法是使用 esc。最后,这有效:

julia> macro my_add(a,b) 
           return :($(esc(a)) + $(esc(b)))
       end
@my_add

julia> @generated function add_one_gen(x::T) where T
           y = one(T)
           quote
               @my_add(x,$y)
           end
       end
add_one_gen

julia> add_one_gen(3)
4

关于macros - 从 Julia 生成的函数中调用宏,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/72830506/

相关文章:

macros - 如何在 GNU Make 中使用 $(call ...) 执行可变宏

c++ - 为什么 GCC 在我有至少一个参数时声称我违反了 "at least one argument for variadic macro"?

visual-studio - Visual Studio 2010 中加载设置文件的宏

.net - 有没有一种获取开放泛型方法的 MethodInfo 的好方法?

Boost.Range 的 C++ 元组 - 获取元素类型的元组?

C++:宏扩展解释

julia - 使用 Julia HDF5 以 numpy 轴顺序读取 HDF5 数据

julia - 自动生成 IJulia 笔记本

arrays - 在 Julia 中使用 StaticArrays.jl 进行性能分配和复制

Emacs 元编程,动态定义方法