りんごがでている

何か役に立つことを書きます

Juliaのデストラクター

Juliaのマニュアルを読むとコンストラクターの記述はちゃんとあるのですが、デストラクターに関しての記述が見当たりません。ググっても情報が出てこないようなので、ここにその方法を残しておきます。

JuliaにはC++~ClassName()のようにデストラクター専用の関数はありませんが、コンストラクターの中でオブジェクトがGCされたときの処理を記述できます。 やり方は簡単で、Base.finalizer()関数がまさにオブジェクトが破壊された時の処理を登録するための関数ですので、これを使うわけです。

finalizer(x, function)xが到達不可能になったときにfunction(x)を呼ぶ仕組みになっているため、functionに資源を解放するような処理を置いておけば良いことになります。

何かDBなどとの接続を管理する型Connectionの例で考えてみると、以下のように何かmallocなどの直後にfinalizer()で後処理を登録すればいいことになります。

type Connection
    conn::Ptr{Void}
    open::Bool

    function Connection()
        this = new(c_malloc(sizeof_connection_info), false)
        finalizer(this.conn, connection_finalizer)
        err = ccall(
            (:connection_init, "libsomedb"),
            Cint,
            (Ptr{Void},),
            this.conn
        )

        if err != 0
            error("failed to create a new connection")
        end
        this.open = true
        this
    en

Juliaのソースで実際に使われている例を見てみましょう。 正規表現のライブラリで確保したメモリへのポインタをfinalizer()で開放しています。

type Regex
    pattern::ByteString
    options::Uint32
    regex::Ptr{Void}
    extra::Ptr{Void}

    function Regex(pattern::String, options::Integer)
        pattern = bytestring(pattern)
        options = uint32(options)
        if (options & ~PCRE.OPTIONS_MASK) != 0
            error("invalid regex options: $options")
        end
        re = compile(new(pattern, options, C_NULL, C_NULL))
        finalizer(re,
            function(re::Regex)
                re.extra != C_NULL && PCRE.free_study(re.extra)
                re.regex != C_NULL && PCRE.free(re.regex)
            end)
        re
    end
end

https://github.com/JuliaLang/julia/blob/0f54ca52e350c89d821069cbdc349f1e49e43cbc/base/regex.jl#L7-27

注意する点としては、

  • finalizer()はimmutableな型には適用できない
  • コンストラクターでは最後に自分自身を返すことを忘れない

ことが挙げられます。

参考