Flat Leon Works

アプリやゲームを作ってます。

【Nim】弱参照を実装してみる

この記事は「Nim Advent Calendar 2021」の23日目の記事として登録させてもらっています。

弱参照とは

弱参照の定義はいろいろあると思いますが、ここでは参照先が破棄されたことを検知できる参照という意味で扱います。Nimは言語としては弱参照は用意されていません。Nimの参照は、参照している限りインスタンスは破棄されないので、破棄されたことを検知することはできません。

弱参照を実装してみる

弱参照は、参照情報を共有することで実現できます。インスタンスが破棄されたときに参照情報が持つ参照先をnilにすることで、同じ参照先を指す弱参照すべてでインスタンスが破棄されたということを検知することができます。

type
  WeakRef[T] = ref object
    p: pointer

proc get*[T]( self: WeakRef[T] ): T =
  return cast[T]( self.p )
 
type
  UniqueRef[T] = object
    uniqueRef*: WeakRef[T]
    
proc init*[T](self: var UniqueRef[T], p: var T) =
  self.uniqueRef = WeakRef[T]()
  self.uniqueRef.p = p[].addr
    
proc `=destroy`[T]( self: var UniqueRef[T] ) =
  self.uniqueRef.p = nil
  
proc getWeakRef*[T]( self: var UniqueRef[T] ): WeakRef[T] =
  self.uniqueRef

WeakRefが弱参照です。WeakRefはUniqueRefから得る必要があります。そして、UniqueRefが破棄されるとWeakRefの参照先がnilになります。

使用例

type
  MyClass = ref object
    name*: string
    
var weakRef: WeakRef[MyClass]
block:
  var myClass = MyClass(name:"hoge")
  var uniqueRef = UniqueRef[MyClass]() # UniqueRefを作成
  uniqueRef.init( myClass ) # UniqueRefを初期化
  weakRef = uniqueRef.getWeakRef() # WeakRefを取得
  assert weakRef.get() != nil # WeakRefの参照先がnilでないことを確認
  echo weakRef.get().name 
  
assert weakRef.get() == nil # UniqueRefが破棄されたのでWeakRefの参照先がnilになっていることを確認

まず、UniqueRefを作成し、そこからWeakRefを取得します。WeakRefのblockを抜けてUniqueRefが破棄されたあとは、WeakRefの参照先がnilになっています。

注意点

  • 参照先の型Tは、参照型である必要があります。
  • 全然テストしていないので、何か問題があるかもしれません。
  • Nimでデストラクタを機能させるには、ビルドオプションで--gc:arcにする必要があるかもしれません。

参考リンク