Avoiding memory allocation with function passed as argument

It shouldn’t be hard:

struct NoCaptureClosure{F,Args} <: Function
    f::F
    args::Args
end
function NoCaptureClosure(f::F,args::Args) where{F,Args} 
    NoCaptureClosure{F,Args}(f,args)
end
@inline function (f::NoCaptureClosure)(args...)
    f.f(args...,f.args)
end

macro nocapture(nf...)
    if isempty(nf)
        return :(error())
    else
        nametypes = nf[1:end-1]
        names = [i.args[1] for i in nametypes]
        types = [i.args[2] for i in nametypes]
        f = nf[end]
        if f.head == :(->)
            args = gensym("args")
            if isa(f.args[1],Symbol)
                paralist = :($(esc(f.args[1])),$args)
            else
                paralist = :($(esc.(f.args[1].args)...),$args)
            end
            assigns = [:($(esc(i)) = $(args).$(i)) for i in names]
            funcname = gensym("f_nocapture")
            func = quote
                local function $(funcname)($(paralist.args...))
                    return let $(assigns...)
                        $(esc(f.args[end]))
                    end
                end
            end
            Args = :($(NamedTuple){$(tuple(names...)),$(Tuple){$(esc(types...))}}(tuple($(esc(tuple(names...)...,)))))
            FuncWrap = :($(NoCaptureClosure)($funcname,$Args))
            return quote
                $func
                $FuncWrap
            end
        end
    end
end

It’s still a little inconvenient and buggy, but should work for simple cases. You can try to improve it.

1 Like