Передача интерполированных значений строки в макрос - PullRequest
4 голосов
/ 18 января 2020

Моя проблема с этим кодом заключается в том, что макрос завершается с ошибкой при передаче интерполированной строки:

macro t(a, b, c)
    quote
        println(:c, " = " , $c)
        ($a, $b, $c)
    end
end

function test()
    # works:
    @t 1 2 3

    # doesn't work:
    x = π
    @t 1 2 "x is $x"
end

test()
test()
c = 3   # works
ERROR: UndefVarError: x not defined

Так что внутри t интерполированное значение x недоступно. Есть ли решение для этого, или использование макроса здесь просто плохая идея?

Ответы [ 2 ]

4 голосов
/ 18 января 2020

Чтобы добиться желаемого, вам нужно использовать esc:

macro t(a, b, c)
    quote
        local a1 = $(esc(a))
        local b1 = $(esc(b))
        local c1 = $(esc(c))
        println(:c, " = ", c1)
        (a1, b1, c1)
    end
end

Обратите внимание, что я определяю переменные a1, b1 и c1 один раз, а затем снова их использую. Причина в том, что если вы написали что-то вроде:

macro t(a, b, c)
    quote
        println(:c, " = ", $(esc(c)))
        ($(esc(a)), $(esc(b)), $(esc(c)))
    end
end

, что естественно (или, возможно, нет :)), вы получите проблемы, поскольку c будет оцениваться дважды, как, например. в этом примере:

julia> macro t(a, b, c)
           quote
               println(:c, " = ", $(esc(c)))
               ($(esc(a)), $(esc(b)), $(esc(c)))
           end
       end
@t (macro with 1 method)

julia> function test()
           @t 1 2 rand()
       end
test (generic function with 1 method)

julia> test()
c = 0.03771143425073453
(1, 2, 0.1819496773810383)

Обратите внимание, что печатается другое значение и возвращается другое значение. Эта проблема присутствовала в вашем исходном макросе (даже если он использовал глобальные переменные, как заметил @Bill):

julia> macro t(a, b, c)
           quote
               println(:c, " = " , $c)
               ($a, $b, $c)
           end
       end
@t (macro with 1 method)

julia> @t 1 2 rand()
c = 0.7021554643798531
(1, 2, 0.6363717837673994)

В общем, я думаю, что макросы @code_lowered и @macroexpand будут вам полезны, когда вы код отладки с использованием метапрограммирования.

1 голос
/ 18 января 2020

Такие макросы оцениваются таким образом, что любые имена переменных, не локальные для самой функции макроса, считаются глобальными. Область вызова в точке макроса не используется.

Так что это работает:

macro t(a, b, c)
    quote
        println(:c, " = " , $c)
        ($a, $b, $c)
    end
end

function test()
    # works:
    @t 1 2 3

    x = π  # this x is not seen inside the macro's scope
    @t 1 2 "x is $x"
end

x = 2π  # this one is global

test()
...