1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

Luaでrefのようなものを作る

Posted at

Luaでrefのようなものを作る

Luaで参照のようなものを作ります。テーブルに対する「参照値」ではなくプリミティブにも使える参照のような動作をするものです。

実装の方針

メタメソッドの __index および __newIndex によってテーブルへのアクセスを制御することができることを利用します。参照名と参照先 (テーブルとキーのペア) の対応を保持しローバルのrefオブジェクトへのアクセスにより参照先を操作します。

実装

refオブジェクト

refオブジェクトに次のメンバを持たせます。

キー
table "table" 参照名と参照先のリスト
put "function" 参照を追加するメソッド
delete "function" 参照を削除するメソッド

今回は簡単のためrefはグローバルオブジェクトとしますがインスタンスとする場合はメタテーブルから put メソッドと delete メソッドを呼べるようにすればよいです。

インデックスへのアクセス

refオブジェクトの __index を設定します。フィールドにアクセスされるとこの関数が呼ばれるため、 ref.table 内を参照して返します。ただし put メソッドや delete メソッドへのアクセスは特別に直接 ref オブジェクト内のメソッドを呼び出します。呼び出しが循環しないために rawget メソッドを使い ref 内のフィールドを直接参照します。なお、コード内ではメタテーブルに設定するためのオブジェクトを meta と表しています。

meta.__index = function(ref, key)
  if type(ref) ~= "table" then
    return nil
  end

  -- putフィールドとdeleteフィールドはref内を直接参照
  if key == "put" or key == "delete" then
    return rawget(ref, key);
  end

  -- ref.table内のフィールドを参照
  local obj = rawget(ref, "table")[key]
  if obj ~= nil then
    return (obj.table)[obj.key]
  end

  return nil
end

代入も同様にして行えます。

meta.__newindex = function(ref, key, value)
  if type(ref) ~= "table" then
    return nil
  end

  -- ref.table内のフィールドを参照
  local obj = rawget(ref, "table")[key]
  if obj ~= nil then
    (obj.table)[obj.key] = value
  end
end

put メソッドと delete メソッドは単純に ref.table 内を書き換えるだけです。メタテーブルの設定前に書くと rawget を使わずに済みます。

ref.put = function(name, table, key)
  rawget(ref, "table")[name] = {
    table = table,
    key = key
  }
end

ref.delete = function(name)
  rawget(ref, "table")[name] = nil
end

振る舞い

次のように参照のような使い方をできます。ただしテーブルへの参照はテーブルを参照する値への参照になります。

local table = {
  a = 0
}

ref.put("a", table, "a")
ref.put("a2", table, "a")

print(table.a .. ", " .. ref.a .. ", " .. ref.a2)    -- 0, 0, 0
table.a = 10
print(table.a .. ", " .. ref.a .. ", " .. ref.a2)    -- 10, 10, 10

ref.a = 20
print(table.a .. ", " .. ref.a .. ", " .. ref.a2)    -- 20, 20, 20

ref.a2 = 30
print(table.a .. ", " .. ref.a .. ", " .. ref.a2)    -- 30, 30, 30

また、次のように参照に対して参照を設定することもできます。この場合も結果は同様です。

ref.put("a2", ref, "a")
1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?