ラベル Lua の投稿を表示しています。 すべての投稿を表示
ラベル Lua の投稿を表示しています。 すべての投稿を表示

2016年3月29日火曜日

Luaのユーザーデータの利用例

C++側で定義したクラス(構造体)をLuaスクリプト側で使用する際には、専用のメモリブロックを利用する必要がある。

主に必要な手続きは以下。
  • void *lua_newuserdata (lua_State *L, size_t size)でユーザーデータ用のメモリブロックを確保
  • 確保したメモリブロック内に、対象クラスのインスタンスを配置
  • Luaスクリプト側に公開するメソッドを__indexメタメソッドへ登録

各ユーザーデータで取り違え(?)が生じないように以下を実施。

識別用のメタテーブルをC++側で設定しておき、Luaスクリプト側へ渡す。
(口述するサンプルコードではメタテーブルに紐づく名前を"person_type"としている)
Luaスクリプト側からユーザーデータを受け取った際(luaL_checkudata呼び出し時)には正しい型か否かを上記メタテーブルを介して検証する。

2016年3月28日月曜日

LuaのC関数側で非ローカルデータを格納する場所

Lua実行環境で非ローカルデータ(呼び出しの外部に存在するデータ)を格納するための場所は、Lua関数/C関数でそれぞれ以下が存在する。

Lua関数
  • グローバル変数
  • 関数の環境
  • 上位値(クロージャー内に保持される非ローカル変数)
C関数
  • レジストリ
  • 環境 ※Lua 5.2より廃止
  • 上位値(クロージャー内に保持される非ローカル変数)

後者のC関数における格納場所に関する覚書。

2016年3月22日火曜日

Luaでクラスの継承

__indexメタメソッドを利用する。

テーブルに対してキー値を参照する場合、
  1. 対象テーブル
  2. 1のテーブルのメタテーブル内の__indexメタメソッドにセットされているテーブル
  3. 2のテーブルのメタテーブル内の__indexメタメソッドにセットされているテーブル
  4. ・・・
の順番に該当キーが見つかるまで探索される。
__indexメタメソッドに基底クラスをセットする事で継承関係を実現出来る。

Luaでコンストラクタ定義

__callメタメソッドを使用する方法


local ClassA = setmetatable({}, {
    __call = function(cls, init)
	local self = {}

	-- localで変数を定義し、
	-- クロージャー経由でアクセスする事でprivate化。
	local private_field = init
	function self.get()
	    return private_field
	end

	return self
    end,
})

instanceA1 = ClassA(10)
instanceA2 = ClassA(20)
print(instanceA1.get())
print(instanceA2.get())

通常のメソッドを使用する方法


local function ClassB(init)
    local self = {}

    -- localで変数を宣言し、
    -- クロージャー経由でアクセスする事でprivate化。
    local private_field = init
    function self.get()
	return private_field
    end

    return self
end

instanceB1 = ClassB(10)
instanceB2 = ClassB(20)
print(instanceB1.get())
print(instanceB2.get())

[参考]

2016年3月21日月曜日

Luaのメタテーブル設定の省略形

テーブル t (以下の例では空)に対してメタテーブル mt を設定する場合は以下となる。

t = {}
setmetatable(t, mt)

上記は以下の省略形で記述可能。
第一引数で指定した空テーブルに対して、メタテーブル mt が設定されたものが、t に設定される。

t = setmetatable({}, mt)

[参考]

2016年3月19日土曜日

Luaの環境

書籍「Programming in Lua」の「14章 環境」を読んでいて、サンプルコード内のsetfenv/getfenvが利用できず躓いていたが、Lua 5.2からAPIが廃止されていた・・・。

Lua 5.2~

Lua 5.2 リファレンスマニュアル」から抜粋。

環境

  • 任意のグローバル変数 var への参照は構文的に _ENV.var に変換される。
  • _ENV は完全に正規の名前であり、同じ名前の変数や引数を定義しても構わない。
  • グローバル変数への参照は、Lua の通常の可視性ルール(レキシカルスコープ)に従い、 プログラムのその地点から見える _ENV が使われる。
  • _ENV の値として使われるテーブルは「環境」と呼ばれる。

グローバル環境

  • Luaには「グローバル環境」と呼ばれる特別な環境が存在し、変数 _G がこの値で初期化される。
  • チャンクをコンパイルすると、 そのチャンクの _ENV の値はグローバル環境で初期化される。そのため、デフォルトでは、 Lua コードのグローバル変数はグローバル環境のエントリを参照する。
  • すべての標準ライブラリはグローバル環境にロードされ、 それらの関数はグローバル環境で実行される。
print(_ENV == _G) -- true

a = 1

local function f(t)
    print(_ENV == _G) -- true
    local print = print
    -- ローカルの_ENVを宣言。参照先は引数で指定されたテーブルとする。
    local _ENV = t
    print(_ENV == _G) -- false
    -- 関数内で宣言された変数はローカルの_ENVに格納される。
    a = 2
    b = 3
end

local t = {}
f(t)
print(a,b)     -- 1   nil
print(t.a,t.b) -- 2   3
仮に「local _ENV = t」をコメントアウトすると、
print(a,b)     -- 2   3
print(t.a,t.b) -- nil nil
となる。

Lua 5.1以前

_ENVが用意されたのはLua 5.2から。
Lua 5.1までは、
getfenv(1)
setfenv(1, table)
を介して該当レベルの環境に対する参照と更新が可能だった。
Lua 5.2からはgetfenv/setfenvのAPIは廃止されている。

[参考]

2016年3月11日金曜日

Luaの末尾呼び出しの最適化

Luaの末尾呼び出しは最適化されるため、スタック領域を消費しない。
末尾呼び出しとは、関数A内の末尾で他の関数Bを呼び出することで、
関数Bの呼び出し後、他に処理が存在しない状態を指す。

通常は、関数呼び出しを行うと、呼び出す関数用にスタック領域を消費するが、
末尾呼び出しの場合は、消費されない。

(ex-1)
function foo(n)
    if n > 0 then return foo(n-1) end 
end

foo(1000000)

(ex-2)
function foo(n)
    if n > 0 then return (foo(n-1)) end 
end

foo(1000000)

上記はいずれも関数fooを1M回呼び出しているが、
(ex-1)は正常終了するものの、
(ex-2)はStack over flowで異常終了となる(※)。
(※)スタックの上限値による。上記例はスタック容量を1MByteとした場合の例。

(ex-2)の方は (foo(n-1)) のように関数呼び出しを括弧で括っており、
呼び出した後の戻り値を1つに調整するという処理が入るため、
末尾呼び出しとならず最適化が行われない。

2016年3月10日木曜日

Luaの関数定義

Programing in Luaの第6章の動作確認。


関数定義とは実際には「function」型の値を作成し、これを変数に代入するステートメント。
「function」型の値を無名関数と呼ぶ。
以下の2つのステートメントは同義。

function foo (x) return 2*x end

foo = function (x) return 2*x end

2016年3月9日水曜日

Luaの論理演算子

クセがあったのでメモ。
(Programming in Luaより抜粋)

Luaの論理演算子には、

  • and
  • or
  • not
が存在する。
全ての論理演算子は、falseとnilを偽として、それ以外のすべてが真となる

2016年3月8日火曜日

Visual Studio 2015 でLuaのスタティックライブラリ(lib)を作成する方法

VC++からLuaのスクリプトを実行する際には、Luaのスタティックライブラリ(lib)が必要となる。
以下の手順を備忘録として記載する。
  • Luaのスタティックライブラリを作成
  • 作成したスタティックライブラリを用いてC++アプリからLuaのスクリプトを実行