如何创建一个抑制错误和警告的宏?

时间:2016-08-26 15:57:53

标签: macros metaprogramming julia

我希望重新定义或覆盖Base中的某些功能,而不会让用户注意到这一点。我不久前发现了这个伎俩:

original_stderr = STDERR
redirect_stderr()
# code
redirect_stderr(original_stderr)

这对我来说是一个重复的模式,所以我做了一个宏:

macro suppress_err(block)
    quote
        orig_err = STDERR
        redirect_stderr()
        val = $block
        redirect_stderr(orig_err)
        val
    end
end

但它从未像预期的那样表现(即使没有宏):

在Windows中:

julia> @suppress_err warn()

julia> @suppress_err error()
ERROR:
 in error at error.jl:22

适用于Linux的Windows子系统:

julia> @suppress_err warn()
[1]    17 abort (core dumped)  julia
julia  7.69s user 2.36s system 106% cpu 9.441 total

在Linux中:

julia> @suppress_err +(x, y) = x - y                                                                                                                                                                                                      
ERROR: UndefVarError: #11#x not defined                                                                                                                                                                                                   
 in eval(::Module, ::Any) at ./boot.jl:234                                                                                                                                                                                                
 in macro expansion at ./REPL.jl:92 [inlined]                                                                                                                                                                                             
 in (::Base.REPL.##1#2{Base.REPL.REPLBackend})() at ./event.jl:46  

显然,我做了一件非常错误的事情,我应该采取哪些正确的方法解决这个问题?我应该在Julia存储库中提出问题吗?

我还尝试在close上使用err_rd, err_wr = redirect_stderr(),然后再次重定向到原始STDERR,但没有成功。

更新

我已经使用我已经完成的工作制作了Suppressor包,因此您可以轻松测试它:

Pkg.clone("https://github.com/Ismael-VC/Suppressor.jl")

所有宏都具有相同的基本形式:

macro suppress_err(block)
    quote
        let
            if ccall(:jl_generating_output, Cint, ()) == 0
                ORIGINAL_STDERR = STDERR
                err_rd, err_wr = redirect_stderr()

                value = $(esc(block))

                REDIRECTED_STDERR = STDERR
                redirect_stderr(ORIGINAL_STDERR)

                # This doesn't help:
                # close(err_rd)
                # close(err_wr)

                return value
            end
        end
    end
end
  • 我添加了JuliaParser here
  • 的安全检查

1 个答案:

答案 0 :(得分:6)

您需要保留对原始redirect_stderr的返回值的引用。它返回读取和写入结束,但是当写入结束存储在STDERR中时,读取结束不存在,因此GC可能决定将其删除。如果发生这种情况,它会在系统级别关闭,所以当julia尝试写入它时,它会收到该错误。

注意:根据当前Suppressor.jl版本更新了以下示例代码。

macro suppress_err(block)
    quote
        if ccall(:jl_generating_output, Cint, ()) == 0
            ORIGINAL_STDERR = STDERR
            err_rd, err_wr = redirect_stderr()
            err_reader = @async readstring(err_rd)

            value = $(esc(block))

            redirect_stderr(ORIGINAL_STDERR)
            close(err_wr)

            return value
        end
    end
end