新トップページへ | Tip

Lua言語についてメモ

LastUpdate : 11/06/09

Lua言語を扱うにあたって調べたことなどを書いていきます。対象のバージョンは5.1以降です。
個人的にLua言語を使って開発をするのに必要そうなことをメモってます。自分用のメモなので、参考になるかどーかは不明w
サンプルソースがどーしよーもない内容だったりしますが、ご容赦を。

内容について、あやふやだったり、謎だったりするところがあるかと思います。個人のメモ帳レベルの内容なので、許してネorz
※内容が長くなりすぎて推敲を放棄してます(ぉ

以下の本を参考にしてます。
『Programming in Lua プログラミング言語Lua公式解説書』(2009年 アスキー・メディアワークス 初版)

前提とする環境として(私が試した環境)
luaのバージョン : 5.1.4(LuaのソースをVisualStudioを使い自分でビルドしたもの)
C/C++の環境 : VisualStudio2010 Express(Visual C++ 2010 Express) ←無料で使えるやつ

lua言語 もくじ

WindowsでLuaをビルドする
文字列中のエスケープシーケンス
予約語について
Shift-JISでソースを記述し、文字列に「表」と「ソ」かやると文字化けするんですけど?w
文字列の連結演算子は?
明示的に文字列・数値を表したい場合(tonumber関数・tostring関数)
文字列の長さを知る方法
論理演算子について
テーブルについて
ブロックの記述
制御構文について(その1)
制御構文について(その2)
関数について(その1)
関数について(その2)
関数について(その3)
関数について(その4)
関数について(その5)
ジェネリックfor
クロージャについて
外部ファイルの*.luaファイルの実行、および文字列をluaソースにみたて実行する方法
assert関数について
pcall関数について
コルーチンについて
配列の扱い方
多次元配列
外部luaファイルの実行
算術メタメソッドについて
関係メタ演算子
テーブルアクセスメタメソッドその1
テーブルアクセスメタメソッドその2
テーブルアクセスメタメソッドその3
環境変数その1
環境変数その2
環境変数その3
モジュールのロード
オブジェクト指向プログラミングその1
オブジェクト指向プログラミングその2
クラス
継承その1
継承その2
多重継承
プライベート性
弱いテーブル
メモ化関数
ライブラリ
デバッグライブラリ
Cモジュール

lua言語のC/C++との連携 もくじ

このサンプルの構成について
sample1 インタープリターのようなものを作る
sample2 luaスクリプトに記述されている変数の値を取得する
sample3 luaスクリプトに記述されているテーブルの値を取得する
sample4 luaのデータへCのソースから、テーブルデータを設定する
sample5 luaの関数をCから呼び出す
sample6 luaの配列のデータをCから取得する
sample7 レジストリの使い方その1
sample8 レジストリの使い方その2
sample9 環境の使い方
sample10 上位値の使い方
sample11 ユーザ定義型について
sample12 ユーザ定義型に対し、メタテーブルを設定する方法
sample13 luaのソースをオブジェクト指向風に記述する方法
sample14 luaのオブジェクトを配列風に記述できるようにする方法
sample15 ライトユーザーデータについて
sample16 __gcについて
sample17 スレッドについて

よくある書き方とか、その他もろもろ

デバッグ中、処理の途中でreturnした場合の書き方
名前つき引数っぽく使う方法
データ処理している気がしてくるサンプル
VisualStudio2010で、C++からluaを使う場合の環境設定
VisualStudio2010でDLLを作成し、そのDLLをluaから使う場合の流れ
luaである範囲すべてをコメントかする方法(ブロックコメント)



WindowsでLuaをビルドする

※2013/01/04 追記

5.2になってから、下記で使っているファイルなどは一切添付されなくなった模様(5.2.1にて確認)。
スタティックリンクライブラリの作成手順をこちらにまとめました。
lua自体のインタプリタがほしい場合は、バイナリ配布が有るみたいなので(http://luabinaries.sourceforge.net/)こちらを利用するのが良いかと。
以下は参考のため、残しておきます。

LuaのソースはANSI Cに厳格に基準して記述したものらしい。なので、普通のコンパイラがあれば、ビルド可能とのこと。
以下の方法は、VisualStudio2008 Expressでのビルド方法です。

ソースは公式サイトからDLする → http://www.lua.org/download.html
コマンドプロンプトにて作業を行います。
(1)下準備(環境変数設定)
VisualStudio用のビルド用バッチがLuaのソースアーカイブの中に入っています(etc\luavs.bat)。バッチ内で、直にCLを実行してるだけなので環境を整えてやります。
VisualStudioのインストールディレクトリ内に、それ用っぽいバッチがあったので、それを使うこととします(私はこれを使った)。

コマンドプロンプトにて、以下のコマンドを実行

"C:\Program Files\Microsoft Visual Studio 9.0\Common7\Tools\vsvars32.bat"
(VisualStudioがデフォルトのインストール先にある場合)
(2)ビルドバッチの実行
次に、ビルドバッチを実行します。
まず、Luaのソースアーカイブの解凍先ディレクトリへ移動します。
そこで、以下のコマンドを実行。

etc\luavs.bat

バッチが終了すると、srcディレクトリの中にビルド生成物が出力されます。
まぁ・・・lua.exeがluaのインタープリタです。
あとはご自由に・・・という感じです。
私は、生成物をまとめて新しいディレクトリに配置し、そのディレクトリにパスでも通しました。
※2010/11/20 追記
仕事とかなんだとかで上のことを書いてから1年ぐらい経ってしまったwwwww
いつのまにかVisualStudiio2010とか発売されてるしwwwwww

VisualStudio2010 Expressでのコンパイル方法について
http://www.lua.org/download.htmlからソースをDLする(私がやったときのバージョンは、5.1.4でした)
コマンドプロンプトにて、「C:\Program Files\Microsoft Visual Studio 10.0\Common7\Tools\vsvars32.bat」 を実行(デフォルトのインストール先の場合)
コマンドプロンプトにて、Luaのソースを解凍し、解凍先のディレクトリへ移動(例:D:\tmp\lua-5.1.4)
コマンドプロンプトにて、「etc\luavs.bat」を実行(※etcディレクトリに移動して、luavs.batを実行するのはNG)。
この手順を踏めば「src」ディレクトリの中に、「luac.exe」や「lua.exe」ができてます。

文字列中のエスケープシーケンス

C言語と一緒です(たぶん)。

代表的なものだけ書いてみます。

\n 改行
\r キャリッジリターン
\t 水平タブ
\\ '\'記号
\" ダブルコーテーション
\' シングルコーテーション


予約語について

Luaのスクリプト記述用のエディタカスタマイズの際に必要かと・・・。スペル間違ってたらすまそw

end
break
do
else
elseif
end
false
for
function
if
in
local
nil
not
or
repeat
return
then
true
until
while

Shift-JISでソースを記述し、文字列に「表」と「ソ」かやると文字化けするんですけど?w

Windowsでスクリプトを書くとぶち当たる問題かと思います。

Shift-JIS的な文字セットはそういう風になります。Lua言語としては、ShiftJISは想定外っぽい。
私が知ってる範囲だと「ソ」「表」「噂」とかがNG。理由は、文字コードとして評価した場合こいつらは「\」にあたる文字になってしまうため。

LuaのスクリプトをUTF-8などで書くとこの問題は発生しません。
・・・が、LuaのスクリプトはUTF-8だけど、ホストするプログラムで使う文字セットはShift-JISだ!とかなると、たぶん、文字コードの変換で悲しい思いをするかもしれません(しないこともありましょうけど)。
なんで、普通にShift-JISがどうしても使いたい感じです。

Lua本体のソースをいじりたくないなーと思った私は(保守がめんどい)、私は以下の方法で逃げることにしました。
Luaには文字列を定数として定義するには2種類の方法があります。

その1
str1 = "表\示"
その2
str2 = [[表示]]

その1では、\をいれて\の文字コードとして評価しるようにしてやります。
その2は、lua言語では[[]]で囲った文字列を1文字列とすることもできます。perlでいうヒアドキュメント的なことができます。これだと普通に動く様子(予測ですが・・・、この場合は内部の文字列を]]が来るまで無条件で文字列として見るようになってるのかもしれませんね)。

これがLua的に良いかどうかは不明。
私は「動いてるんだからいいんじゃネ?w」とか思ってます。

文字列の連結演算子は?

「+」じゃないので注意。「..」です(ピリオド2つ)。

■ソース
str = "あうあう".."うひw"
print(str)
■出力結果
あうあううひw


明示的に文字列・数値を表したい場合(tonumber関数・tostring関数)

tonumber関数・tostring関数を使うと可能です。
変換不能な場合nilを返します。

■ソース

str = "にゃんにゃん"
num = "100"

print("変換可能\な場合")
print(tostring(str))
print(tonumber(num))

print("\n変換不可能\な場合")
print(tostring(num))
print(tonumber(str))

■出力結果

変換可能な場合
にゃんにゃん
100

変換不可能な場合
100
nil


文字列の長さを知る方法

前置演算子をつけることで簡単に求められます(長さ演算子)。

■ソース

str = "にゃんにゃん"
print("長さ:"..#str)

■出力結果

長さ:12


論理演算子について

and/or/notのことについてです。
左式、右式の値を評価し、TRUE/FALSEの結果を生成するものでは無いので注意。

ちなみにluaでは、0もtrueなので注意。falseとして評価されるケースは、false,nil、この二つしかない。

andは、左式がtrueとなった場合、右式の結果を返します。一方、左式がfalseとなったら、左式の結果を返します。
orは、左式がtrueとなった場合、左式の結果を返します。一方、左式がfalseとなったら、右式の結果を返します。
notは、評価結果を反転させます(falseをtrue,trueをfalse)。

短縮評価が行われます(例 : andにて、左式がfalseなら右式については実行しません)。

■ソース

print("★and演算子について")
result1 = 100 and 200
result2 = "" and 200
result3 = false and 200
result4 = nil and 200

print(result1)
print(result2)
print(result3)
print(result4)



print("\n★or演算子について")
result1 = 100 or 200
result2 = "" or 200
result3 = false or 200
result4 = nil or 200

print(result1)
print(result2)
print(result3)
print(result4)


print("\n★not演算子について")
result1 = not 100
result2 = not ""
result3 = not false
result4 = not nil

print(result1)
print(result2)
print(result3)
print(result4)

■出力結果

★and演算子について
200
200
false
nil

★or演算子について
100

200
200

★not演算子について
false
false
true
true


テーブルについて

luaにおいてデータの格納先はテーブルがすべてです。普通の配列、連想配列、配列の配列・・・などかなり自由に使えます。

テーブルの作成・アクセス方法について

テーブルの作成には、テーブルコンストラクタを使用します。
からのテーブルを作成するには以下のようにします。
table1 = {}
なにも書かなければ良いです。
初期値を指定することもも可能です。
table1 = {"浦和", "与野", "川越"}
各要素には、添え字を使ってアクセス可能です。通常添え字は1からスタートします。与野という文字列がほしい場合table1[2]と記述することになります。
連想配列的に使うことも、また上の例との混在も可能です。

■ソース

table1 = {
        "浦和",
        "与野",
        "川越",
        
        x=100,
        y=200,
        name="ひゃっはー",
        
        {
                name="内部その1です。",
                x=2000
        },
        
        inner_arr = {
                name="内部その2です。",
                x=3000
        }
}

--以下は同じnameにアクセスしてます(二種類の記述方法があります)。
print(table1.name)
print(table1["name"])

--table1[4]に当たるものは、最初にある{}ブロックです
print(table1[4].name)

--inner_arrの内部のnameを表示します。
print(table1.inner_arr.name)


print("\n\nrepeatを使った場合")
i=1
repeat
        print(table1[i])
        i = i + 1
until i>=#table1


print("\nforを使った場合")
for i=1,#table1-1 do
        print(table1[i])
end

■出力結果

ひゃっはー
ひゃっはー
内部その1です。
内部その2です。


repeatを使った場合
浦和
与野
川越

forを使った場合
浦和
与野
川越

※ おまけ

以下のことも可能。

■ソース

table1 = {
        ["あはw"]="うひw",
        ["仕事なんてやりたくないお!"]="家賃払えなくなるお!",
        x="あいうえお",
--      "x"="かきくけこ",  ←これは文法エラー(なのでコメントアウトしてます)
        ["x"]="さしすせそ"
}

print(table1["あはw"])
print(table1["仕事なんてやりたくないお!"])
print(table1.x)
print(table1["x"])

■出力結果

うひw
家賃払えなくなるお!
さしすせそ
さしすせそ

まっとーにソースを書いてれば、コンストラクタ内のキー名が重複することは無いですが、ミスって重複してしまった場合は、最後に書かれたものが、そのキーの値になるっぽいですね、動きを見ると。

テーブルへの追加・削除について

テーブルへの要素の追加・削除は簡単に可能です。
また、nilを代入すると、削除したことになります(ガーベッジコレクタの回収対象となる)。
■ソース
table1 = {
        "浦和",
        "与野",
        "川越",
        
        x=100,
        y=200,
        name="ひゃっはー",
        
        {
                name="内部その1です。",
                x=2000
        },
        
        inner_arr = {
                name="内部その2です。",
                x=3000
        }
}

--追加されていないものへアクセスしてもnilとなります。
print(table1["add"])

--追加
table1.add="追加ですよー"
print(table1["add"])

--削除
table1.add=nul
print(table1["add"])

--※ table1.addとtable1["add"]は同じ意味です(記述方法が2種類ある)
■出力結果
nil
追加ですよー
nil


ブロックの記述

Cでいう{}で囲むところです。Luaではdo-endで記述します。

■ソース

a = 100
do
        local a = 200
        print(a)
end
print(a);

■出力結果

200
100


制御構文について(その1)

それぞれの制御構文を使ったサンプルコードを書いてみました(悲しいぐらい意味の無いコードです。。。)。
ちなみに、if(a != 10)とか、同じでない・・・を示す演算子に「~=」を使います(→ if( a ~=10 )とかなる)

■ソース

--if文の文法(驚くほど無意味なコードですが・・・書き方の例です)
if 100 ~= 10 then
        print("100と10は等しくないです")
end

--elseifも可能。
if 10 < 100 then
        print("10と100では100が大きいです")
        
        --elseifも可能。
        if 10 < 1 then
                print("10より1は大きいです。んなあほな!?")
        elseif 10==1 then 
                print("10は1と等しいです。んなあほな!?")
        elseif 2 >= 10 then
                print("2は10以上です。んなあほな!?")
        else
                print("10<1の結果はfalseです")
        end
else
        print("10<100の演算が失敗してるだろこれw")
end



--whileの文法
i = 0
while i < 5 do
        print("while counter val : "..i)
        i=i+1
end


--repeatの文法(Cで言うdo-whileと同じ)
i = 0
repeat
        print("repeat counter val : "..i);
        i = i+1;
until i >= 5


--forの文法(for exp1, exp2, exp3 do にて、exp1は初期値、exp2は上限値(exp2より大きくなった場合という意味になる様子)、exp3は増分)
for i=0,6,2 do
        print("for counter val : "..i);
end

--for文にて、exp3を略すと+1と解釈してくれます。
for i=0,2 do
        print("for2 counter val : "..i)
end

--ループ内でbreakも記述できます。
for i=1,10 do
        print("break counter : "..i)
        if i%3 == 0 then
                break
        end
end

■出力結果

100と10は等しくないです
10と100では100が大きいです
10<1の結果はfalseです
while counter val : 0
while counter val : 1
while counter val : 2
while counter val : 3
while counter val : 4
repeat counter val : 0
repeat counter val : 1
repeat counter val : 2
repeat counter val : 3
repeat counter val : 4
for counter val : 0
for counter val : 2
for counter val : 4
for counter val : 6
for2 counter val : 0
for2 counter val : 1
for2 counter val : 2
break counter : 1
break counter : 2
break counter : 3


制御構文について(その2)

ジェネリックforというものがあるらしい。よーはイテレータ的なことができるみたいですね。

■ソース

array = {
        "一番目!",
        "二番目!",
        "三番目!"
}


-- 配列の要素を順次一要素づつ見ていくことが可能です
for i,v in pairs(array) do
        print("i/v : "..i.." / "..v)
end

print()

-- indexのみ受け取ることも可能
for i in pairs(array) do
        print("i : "..i)
end

■出力結果

i/v : 1 / 一番目!
i/v : 2 / 二番目!
i/v : 3 / 三番目!

i : 1
i : 2
i : 3


関数について(その1)

関数の記述方法、呼び出し方についてです。
戻り値で、複数値を返せるところがC言語などと異なります(例でいうfunc3関数)。
例が長くなってすいませんtt

特殊な点として、関数は複数の戻り値を返せる点です。

■ソース

--関数その1(引数の無い関数)
function func1()
        print("関数その1ですよー")
end

--関数その2(引数のある関数)
function func2(arg1, arg2)
        print("関数その2ですよー")
        
        if arg1 == nil then print("第一引数はnilです") 
        else print("第一引数:"..arg1); end
        
        if arg2 == nil then print("第二引数はnilです") 
        else print("第二引数:"..arg2); end
        
end

--関数その3(複数の戻り値を返す関数)
function func3()
        print("関数その3ですよー")
        return "一番目の戻り値", "二番目の戻り値"
end



--関数その1を呼び出す
print("■関数その1")
func1()
print("----------")
--関数その2を呼び出す
print("■関数その2 普通に引数を渡す場合")
func2("にゃん","わん")
print("■関数その2 引数を渡さない場合(引数はすべてnilになります)")
func2()
print("■関数その2 引数を少なくして渡した場合(足りない箇所はnilになります)")
func2("にゃん")
print("■関数その2 引数を多く渡した場合(余分なところは無視されます)")
func2("にゃん", "わん", "あーうー")
print("----------")
--関数その3を呼び出す
print("■関数その3")
local res1,res2 = func3()
print("関数3の戻り値: res1->"..res1.." / res2->"..res2)
--配列を構成する際、位置によって戻り値が1個になるか、すべてになるか差が出るので注意
print("特殊な扱い : ", func3(), "♪", func3())

■出力結果

■関数その1
関数その1ですよー
----------
■関数その2 普通に引数を渡す場合
関数その2ですよー
第一引数:にゃん
第二引数:わん
■関数その2 引数を渡さない場合(引数はすべてnilになります)
関数その2ですよー
第一引数はnilです
第二引数はnilです
■関数その2 引数を少なくして渡した場合(足りない箇所はnilになります)
関数その2ですよー
第一引数:にゃん
第二引数はnilです
■関数その2 引数を多く渡した場合(余分なところは無視されます)
関数その2ですよー
第一引数:にゃん
第二引数:わん
----------
■関数その3
関数その3ですよー
関数3の戻り値: res1->一番目の戻り値 / res2->二番目の戻り値
関数その3ですよー
関数その3ですよー
特殊な扱い :    一番目の戻り値  ♪      一番目の戻り値  二番目の戻り値

関数について(その2)

関数に可変引数も指定することが可能です。

■ソース

function func1(arg1, ...)
        return {arg1, ...}
end


function func2(...)
        for i=1, select("#", ...) do 
                print(select(i, ...))
                
        end
end


--なんと、配列に要素をこれでくっつけることができてしまいます!
print("■リスト連結のテスト")
list = func1("aaa", "bbb", "ccc")

for i=1, #list do
        print(list[i])
end

--select関数は、配列の中から、指定した添え字の箇所以降のデータを返します
print("\n■select関数の使い方")
func2("その1", "その2", "その3")

■出力結果

■リスト連結のテスト
aaa
bbb
ccc

■select関数の使い方
その1  その2  その3
その2  その3
その3


関数について(その3)

関数は戻り値として、関数も返すことが可能です。
以下の例では、func1が戻り値として関数を返しています。

また、注意したいのは、local valの値です。func1が戻り値を返す時点で、valはローカル変数のため消えてしまい、結果として、戻り値の関数内での処理である x*val のvalが行方不明になり、エラーとなってしまうはずですが、lua言語には「クロージャ」という概念があり、valの値は適切に保持されます。
例のソースで示していますが、valの値が変わっても(10000のものと20000のものを作っている)適切に値が保持されます。

■ソース

--関数は関数を戻り値とすることも可能です
function func1(v)
        local val = v*100
        return  function(x)
                                return x*val
                        end
end


f1 = func1(100)
f2 = func1(200)

print(f1(200))
print(f2(400))
print(f1(200))
print(f2(400))

■出力結果

2000000
8000000
2000000
8000000


関数について(その4)

関数にて、一番最後の記述方法によって、内部での扱いが変化します(luaのコード的にはどちらにせよ正しく実行されます)。
以下のサンプルにて、末尾の処理が「return func1(num-1)」か「func1(num-1) 」で処理のされ方が異なります。前者では、func1の処理が終了したあと、やるべきことが無しとなり、後者は、やるべきこと有り(関数の戻り値を捨てる処理)と判定されます。
前者の場合、スタックからfunc1の呼び出し元の情報を捨ててから、再度func1を呼び出します。そのため、再帰処理をいくらでも続けることが可能となります。後者の場合、呼び出し元の情報が残るので、再帰を繰り返すとスタックオーバーフローします。

■ソース

--再帰をする関数にて、↓の記述方法では
--function func1(num)
--関数中のfunc1がグローバル関数を指すことになります。ローカルとグローバルで関数名が
--同じ場合、厄介です。しかし、以下の記述方法を使えば、ローカルのfunc1を指定することができます
local func1
func1 = function (num)
        
        if (num % 10000000) == 0 then
                print(num.."回目の呼び出しだよ!")
        end
        
        if num > 1 then 
                return func1(num-1)      --(1)こっちはOK
                --func1(num-1)           --(2)こちらではスタックオーバーフローする
        end
end

func1(99999999)
print("終了!")

■出力結果

90000000回目の呼び出しだよ!
80000000回目の呼び出しだよ!
70000000回目の呼び出しだよ!
60000000回目の呼び出しだよ!
50000000回目の呼び出しだよ!
40000000回目の呼び出しだよ!
30000000回目の呼び出しだよ!
20000000回目の呼び出しだよ!
10000000回目の呼び出しだよ!
終了!


※後者の「func1(num-1)」という処理を実行すると以下のようになり、動きません↓

■出力結果

lua: src1.lua:15: stack overflow
stack traceback:
        src1.lua:15: in function 'func1'
        src1.lua:15: in function 'func1'
        src1.lua:15: in function 'func1'
        src1.lua:15: in function 'func1'
        src1.lua:15: in function 'func1'
        src1.lua:15: in function 'func1'
        src1.lua:15: in function 'func1'
        src1.lua:15: in function 'func1'
        src1.lua:15: in function 'func1'
        src1.lua:15: in function 'func1'
        ...
        src1.lua:15: in function 'func1'
        src1.lua:15: in function 'func1'
        src1.lua:15: in function 'func1'
        src1.lua:15: in function 'func1'
        src1.lua:15: in function 'func1'
        src1.lua:15: in function 'func1'
        src1.lua:15: in function 'func1'
        src1.lua:15: in function 'func1'
        src1.lua:19: in main chunk
        [C]: ?

(src1.luaというのは、私がソースを記述しているソースファイルのファイル名です)


関数について(その5)

ローカル関数というものを作成することが可能です。記述方法はいくつか用意されており、自分の好きな記述方法を使えばよいみたい。記述方法を3つ例に上げました(これ以外の書き方があるのかは知らないw)。

■ソース

--library1を作成する
library1 = {}
library1.func1 = function ()
                     print("library1にあるローカル関数func1だよん")
                 end
library1.func2= function ()
                     print("library1にあるローカル関数func2だよん")
                 end

--library2を作成する
library2 = {
        func1 = function ()
                    print("library2にあるローカル関数func1だよん")
                end,  -- ←この箇所の","を忘れないように!
        func2 = function ()
                    print("library2にあるローカル関数func2だよん")
                end
}

--library3を作成する
library3 = {}

function library3.func1()
        print("library3にあるローカル関数func1だよん")
end

function library3.func2()
        print("library3にあるローカル関数func2だよん")
end


--library1の関数を呼び出す
library1.func1()
library1.func2()

--library2の関数を呼び出す
library2.func1()
library2.func2()

--library3の関数を呼び出す
library3.func1()
library3.func2()

■出力結果

library1にあるローカル関数func1だよん
library1にあるローカル関数func2だよん
library2にあるローカル関数func1だよん
library2にあるローカル関数func2だよん
library3にあるローカル関数func1だよん
library3にあるローカル関数func2だよん


ジェネリックfor

ちょっと違った方法(?)でfor文を使うことができるよ、という話です。

arr = {
        "にゃん!",
        "わん!",
        "うひw"
}


print("■ipairs関数を使う例1")
for i,v in ipairs(arr) do
        print("index : "..i.." / value : "..v)
end

print("■ipairs関数を使う例2")
-- iを省略した形で記述も可能
for i in ipairs(arr) do
        print("index : "..i.." / value : "..arr[i])
end

■出力結果

■ipairs関数を使う例1
index : 1 / value : にゃん!
index : 2 / value : わん!
index : 3 / value : うひw
■ipairs関数を使う例2
index : 1 / value : にゃん!
index : 2 / value : わん!
index : 3 / value : うひw


クロージャについて

クロージャという概念があります(Perlとかのエンクロージャとたぶん一緒)
ここで注目すべき点は、numの変数の扱われ方です。numのスコープは本来、func関数の中のみのはずですが、戻り値で返す関数の中で使われてしまっています。本来、この関数を実行した 場合、メモリ上にnumが存在しないため動かないはずですが、クロージャの仕組みのおかげで動きます。
func関数が返す関数ごとに、numを生成してくれています(実行結果をみるとわかるはず)。

-- クロージャのサンプル(num変数の振る舞いに注意)
function func()
        local num = 0
        
        return 
                function ()
                        num =  num+1
                        return num
                end
end


f1 = func()
f2 = func()

print("■f1の実行")
print(f1())
print(f1())
print(f1())


print("■f2の実行")
print(f2())
print(f2())
print(f2())

■出力結果

■f1の実行
1
2
3
■f2の実行
1
2
3


外部ファイルの*.luaファイルの実行、および文字列をluaソースにみたて実行する方法

以下のような実行方法もあります。

-- ソースに記述されている文字列を実行する
func1 = assert(loadstring("print(\"文字列を実行コードとして実行しています\")"));
func1();     -- 上の文字列のコードを実行

-- 外部ファイル(src2.lua)の実行
func2 = assert(loadfile("src2.lua"))
func2();     -- src2.luaを実行

※src2.luaファイルの内容

mes = [[これはsrc2.luaファイルのソースです]]
print(mes)

■出力結果

文字列を実行コードとして実行しています
これはsrc2.luaファイルのソースです


assert関数について

よくあるassert関数です。戻り値が、false もしくは nil の場合に、この関数は反応します。

■ソース

function test1()
	return true; 
end

function test2()
	return false; 
end

function test3()
	return nil; 
end


local res1 = assert(test1());
print "test1の呼び出しが完了しました";

local res2 = assert(test2());
print "test2の呼び出しが完了しました";

local res3 = assert(test3());
print "test3の呼び出しが完了しました";

■出力結果

test1の呼び出しが完了しました
E:\usr\bin\lua\lua.exe: E:\usr\my\eclipse\Lua\LuaStudy\case1/Sample1.lua:20: assertion failed!
stack traceback:
	[C]: in function 'assert'
	E:\usr\my\eclipse\Lua\LuaStudy\case1/Sample1.lua:20: in main chunk
	[C]: ?
※res3については、res2のassertで処理が終わっているため、実行されていません。

pcall関数について

関数を実行した場合の、例外(error関数が呼ばれたり、数字が入っている変数に配列アクセスをしたり・・・など想定外の動作をさせた場合に発生)の取り扱い方についてです。
pcall関数の引数に、実行する関数、そして、その関数に渡す引数を指定し、実行します。
処理が問題なく実行されれば真、例外が発生したならば偽が返ります。luaではpcallメソッドを利用し、例外の取り扱います。

■ソース

function func1(numArray)
	if numArray[1] == 0 then
		print("numArray[10]にアクセス");
	end
	-- error関数で故意に例外を発生させることもできる
	-- error();
end


-- func1の引数は、数字の配列を想定していたが、引数を渡す側が誤って
-- 数値を渡してしまったケース(単純な数値に対し配列アクセスしようとするので例外が発生する)
local msg = 1;

if pcall(func1,msg) then
	-- func1の処理が正常に行われた場合
	print("キャーサイタマー");
else
    -- func1の処理中に例外が発生した場合
	print("サイタマを一日1000回唱えなさい");
end

■出力結果

サイタマを一日1000回唱えなさい

コルーチンについて

luaのコルーチンとは、スレッドと似たような概念です。ただし、コルーチンの処理が同時処理されることはありません。
ソース上からは、指定した関数をエントリーポイントとして、コルーチンを開始。ソース上で、resume関数/yield関数を使って、実行・停止の制御をします。

コルーチンの状態遷移につい。

     suspended → running → deaded 
           → suspended→running → suspended →running・・・・(コルーチンの処理が終わるまで) → deaded
                 → normal状態(コルーチン実行中になんらかの例外が発生した場合)
よくある状態遷移です(てきとうに脳内補完して下さい)。
normal状態とは、コルーチン内で、例外が発生し、そのルーチンが終了してしまった状態を指すらしい。resume関数を呼び出し、処理を再開することはできないとのこと。

コルーチンについて、私自身、まだちゃんと把握できていません・・・。
とりあえず使い方レベルのサンプルとなりますが、以下にソースを置いておきます(いつもどおりぜんぜん意味のないソースサンプルですw

■ソース

-- コルーチンその1
function coroutine1()
	for i=0,5 do
		print("coroutine1内のループ "..i.." 回目");
		coroutine.yield();	-- 処理を他のコルーチンに受け渡す
	end
end

-- コルーチンその2
function coroutine2()
	for i=0,5 do
		print("coroutine2内のループ "..i.." 回目");
		coroutine.yield();	-- 処理を他のコルーチンに受け渡す
	end
end


local co1 = coroutine.create(coroutine1); -- コルーチンを生成
local co2 = coroutine.create(coroutine2); -- コルーチンを生成

-- コルーチン1を実行
print(coroutine.status(co1))
coroutine.resume(co1);
print(coroutine.status(co1))
coroutine.resume(co1);
print(coroutine.status(co1))
coroutine.resume(co1);
print(coroutine.status(co1))
coroutine.resume(co1);
print(coroutine.status(co1))
coroutine.resume(co1);
print(coroutine.status(co1))
coroutine.resume(co1);
print(coroutine.status(co1))
coroutine.resume(co1);
print(coroutine.status(co1))


-- コルーチン2を実行
print(coroutine.status(co2))
coroutine.resume(co2);
print(coroutine.status(co2))
coroutine.resume(co2);
print(coroutine.status(co2))
coroutine.resume(co2);
print(coroutine.status(co2))
coroutine.resume(co2);
print(coroutine.status(co2))
coroutine.resume(co2);
print(coroutine.status(co2))
coroutine.resume(co2);
print(coroutine.status(co2))
coroutine.resume(co2);
print(coroutine.status(co2))

■出力結果

suspended
coroutine1内のループ 0 回目
suspended
coroutine1内のループ 1 回目
suspended
coroutine1内のループ 2 回目
suspended
coroutine1内のループ 3 回目
suspended
coroutine1内のループ 4 回目
suspended
coroutine1内のループ 5 回目
suspended
dead
suspended
coroutine2内のループ 0 回目
suspended
coroutine2内のループ 1 回目
suspended
coroutine2内のループ 2 回目
suspended
coroutine2内のループ 3 回目
suspended
coroutine2内のループ 4 回目
suspended
coroutine2内のループ 5 回目
suspended
dead

配列の扱い方

他の言語と同じように配列を使用可能です。ただ、注意したいのが、基本的に配列の添え字が1からスタートするという点です(ライブラリなどはそれを前提にしているものがあるしかし、言語上は別に1でなくてもよいです。むしろマイナス値の添え字も可能です)。
配列の長さを取得することができます。変数名の前に#をつけると、要素がいくつあるかを返します。配列に歯抜けの状態で値を設定してあると正しい要素の数を返さないので注意。

■ソース

-- 配列の作成
local array1 = {};
-- 配列の作成と、値の設定を同時に実行しています
local array2 = { "その1", "その2", "その3"};


-- 配列の添え字のは1からスタートするのが基本です
-- 値を設定していない添え字にアクセスすると、nilとなります
-- #array1と、#の記号を手前に置くとその配列の要素の数を取得できます。
array1[1]="うはw";
array1[2]="うひw";
for i=1,#array1 do
        print("index:"..i.." / "..array1[i]);
end

-- 配列の添え字については基本的に1からスタートするのが基本だが、それ以外も可能
for i=-5,5 do
        array1[i]="今の値は"..i.."ですよ";
end
for i=-5,5 do
        print("index:"..i..array1[i]);
end

■出力結果

index:1 / うはw
index:2 / うひw
index:-5今の値は-5ですよ
index:-4今の値は-4ですよ
index:-3今の値は-3ですよ
index:-2今の値は-2ですよ
index:-1今の値は-1ですよ
index:0今の値は0ですよ
index:1今の値は1ですよ
index:2今の値は2ですよ
index:3今の値は3ですよ
index:4今の値は4ですよ
index:5今の値は5ですよ

多次元配列

配列の配列を作成するサンプルです。

■ソース

-- 多次元配列を作成
local arr1 = {};        -- 配列を作成
for i=1,3 do
        arr1[i] = {};   -- 配列を作成
end
-- 以上のコードで、配列の配列ができました。


-- 配列の要素をすべて出力してみる
arr1[1] = { ">>301", ">>302", ">>610" };
arr1[2] = { "うはw", "うひw", "うふw", "うへw", "うほw" };
arr1[3] = { "日暮里", "西日暮里", "新日暮里" };

for i=1,#arr1 do
        for j=1,#arr1[i] do
                print("["..i.."]".."["..j.."]".." : "..arr1[i][j]);
        end
end

■出力結果

[1][1] : >>301
[1][2] : >>302
[1][3] : >>610
[2][1] : うはw
[2][2] : うひw
[2][3] : うふw
[2][4] : うへw
[2][5] : うほw
[3][1] : 日暮里
[3][2] : 西日暮里
[3][3] : 新日暮里

外部luaファイルの実行

luaのスクリプト内で、ほかのluaスクリプトを実行できます。
データ登録したいcsvなどを、ちょいちょいと加工し、luaのテーブルデータにしてしまい、それを実行したあと、luaスクリプトないでそれを処理する・・・なんてことも可能です。

■ソース

-- data.luaを実行し、データをentryに追加させます

-- 実行結果を格納する配列
local result = {};

-- Sample6_1.dataに記述されている関数
function insert(data)
	-- 3番目の要素が"A"のものだけ抽出する
	if data[3]=="A" then
		result[#result+1] = data[2];
	end
end

dofile("E:/usr/my/eclipse/Lua/LuaStudy/data/data.lua");


-- data[3]が"A"だったものの一覧を出力
for i=1, #result do
	print(result[i]);
end

■出力結果

データその1
データその4

■data.luaの内容

-- データの並び順 → { データID, タイトル, 属性 }
insert{"000001", "データその1", "A"}
insert{"000002", "データその2", "B"}
insert{"000003", "データその3", "C"}
insert{"000004", "データその4", "A"}
insert{"000005", "データその5", "B"}
insert{"000006", "データその6", "C"}

算術メタメソッドについて

演算子のオーバーライドっぽいことができます。メタテーブルに、対応する演算子に対する関数(これをメタメソッドを呼ぶ)を設定すればOKです。
メタテーブルのフィールドと、それに対応する演算子。

+ __add
- __sub
* __mul
/ __div
(符号反転) __unm
% __mod
^(累乗) __pow
.. __concat

メタメソッドの検索順序は以下の通りです。

(1)左項が__addフィールドを含むメタテーブルがある場合 左項のメタテーブルが使われる
(2)左項が__addフィールドを含むメタテーブルが無い 右項に__addフィールドを含むメタテーブルが無い
→ 右項のメタテーブルが使われる
(3)左項・右項共にメタテーブル無し エラー

■ソース

-- newTableで作成する配列用のメタテーブルを作成 
local mt = {};

-- メタテーブルにmtをセットし、配列を作成する関数
function newTable(c)
	local arr = {};
	setmetatable(arr, mt);	-- メタテーブルを独自のものに変更
	for i=1, #c do
		arr[i] = c[i];
	end
	return arr;
end

-- 配列+配列という計算を行う最の、+演算子の振る舞いを定義する関数
function add(a, b)
	for i=1, #b do
		a[#a+1] = b[i]; 
	end
	return a;
end
-- +演算子の振る舞いを設定
mt.__add = add;

-- #############################################################
local sample1 = { "あ", "い", "う", "え", "お" };
local sample2 = { "か", "き", "く", "け", "こ" };
local arr1 = newTable(sample1);
local arr2 = newTable(sample2);

-- 設定されているメタテーブルを表示する
print("sample1 getmetatable : ");
print(getmetatable(sample1));
print("arr1 getmetatable : ");
print(getmetatable(arr1));

-- 配列+配列の演算を行う
local result = arr1+arr2;

-- 結果の表示
for i=1, #arr1 do
	print(i.." : "..arr1[i]);
end

■出力結果

sample1 getmetatable : 
nil
arr1 getmetatable : 
table: 00347530
1 : あ
2 : い
3 : う
4 : え
5 : お
6 : か
7 : き
8 : く
9 : け
10 : こ

関係メタ演算子

以下の3つしか存在しません。

演算子 対応するメタテーブルのフィールド
== __eq
< __lt
<= __le


luaにおいて、以下のものは右側の値へ変換され処理されます。

a ~=b not(a==b)
a>b b<a
a>=b b<=a

等価性の比較でエラーになることはないです。
2つのオブジェクトでメタメソッドが異なっていると等価演算結果は偽となる(このとき、メタメソッドは呼び出されない)
  → 等価性の比較メソッドを呼び出すのは、2つのオブジェクトが同じメタメソッドを共有している場合のみ

■ソース


local mt = {};
mt.__eq = function(a, b)
	local result = true;
	for i=1, #a do
		for v=1, #b do
			if i==v then
				print("cmp : "..a[i].."/"..b[v]);
				if a[i]~=b[v] then
					return false;
				end
				break;
			end 
		end
	end
	return result;
end



-- メタテーブルにmtをセットし、配列を作成する関数
function newTable(c)
	local arr = {};
	setmetatable(arr, mt);	-- メタテーブルを独自のものに変更
	for i=1, #c do
		arr[i] = c[i];
	end
	return arr;
end


-- #############################################################
local sample1 = { "あ", "い", "う", "え", "お" };
--local sample2 = { "か", "き", "く", "け", "こ" };
local sample2 = { "あ", "い", "う", "え", "お" };
local arr1 = newTable(sample1);
local arr2 = newTable(sample2);

if arr1==arr2 then
	print("arr1とarr2の値は同じです");
else
	print("arr1とarr2の値は同じではないです");
end

■出力結果

cmp : あ/あ
cmp : い/い
cmp : う/う
cmp : え/え
cmp : お/お
arr1とarr2の値は同じです

テーブルアクセスメタメソッドその1

__indexメタメソッドについて

テーブルに存在しないフィールドにアクセスした際、通常nilになるが、__indexに関数もしくはテーブルを指定することでテーブルに存在しないフィールドにアクセスした際の振る舞いを指定可能になる。

※テーブルに存在しないフィールドにアクセスしようとした時しか呼び出されない
 テーブルに存在するキーが指定された場合は単純にそれが使用される。
 テーブルに対し、__indexを呼ばないで値を取得したい場合は、rawget関数を使います。

以下の例では、arrという配列に対し、存在するフィールドと存在しないフィールドにアクセスし、その動きをみています。
存在するフィールドについては、単純にそのフィールドの値にアクセスするだけです。
存在しないフィールドにアクセスした場合は、メタテーブルに設定されている__indexを見に行きます(関数が設定されている場合はその関数を呼び出す)。parentArrayに、指定のフィールド(ここでは"c")があるか見に行きますが、parentArrayには"num"しかフィールドは存在しません。そこで、parentArrayの__indexが呼び出されます。ここでは戻り値が0を返す関数が設定されているので、存在しないフィールドにアクセスすると、結果は必ず0となります。

■ソース

-- テーブルに指定したフィールドが存在しない場合の振る舞いをセットする
local parentArray = { 
        num="parentArrayのnumフィールド" ,
};
-- 指定したフィールドがparentArrayに存在しない場合、その値は0になるように__indexに関数を設定
local mt = { __index= function() return 0 end };
setmetatable(parentArray, mt);


-- 関数を指定せず、テーブルを直接指定することも可能。
--function search(table ,key)
--      -- 指定されたフィールドが無い場合はparentArrayの内容を参照する
--      return parentArray[key];        
-- end
-- local mt = {__index=search};
local mt = {__index=parentArray};

-- メタメソッドをセットする関数
function createArray(o)
        setmetatable(o, mt);
        return o; 
end


-- ############################################################
-- aとbというフィールドを持つテーブルarrを作成する
local arr = createArray({ a=100, b=200 });

-- arrにはaというフィールドは存在するため、__indexは使用されない
print("val[a] : "..arr["a"])
-- arrにはcというフィールドは存在しないため、__indexが使用される
print("val[c] : "..arr["c"]);

■出力結果

val[a] : 100
val[c] : 0

テーブルアクセスメタメソッドその2

テーブルアクセスメタメソッドその1でコメントアウトされていたsearch関数を使った例です。実行結果はテーブルアクセスメタメソッドその1と変わりありません。

■ソース

-- テーブルに指定したフィールドが存在しない場合の振る舞いをセットする
local parentArray = { 
        num="parentArrayのnumフィールド" ,
};
-- 指定したフィールドがparentArrayに存在しない場合、その値は0になるように__indexに関数を設定
local mt = { __index= function() return 0 end };
setmetatable(parentArray, mt);


-- メタメソッド__indexの定義
function search(table ,key)
        -- 指定されたフィールドが無い場合はparentArrayの内容を参照する
        return parentArray[key];        
end
local mt = {__index=search};

-- メタメソッドをセットする関数
function createArray(o)
        setmetatable(o, mt);
        return o; 
end


-- ############################################################
-- aとbというフィールドを持つテーブルarrを作成する
local arr = createArray({ a=100, b=200 });

-- arrにはaというフィールドは存在するため、__indexは使用されない
print("val[a] : "..arr["a"])
-- arrにはcというフィールドは存在しないため、__indexが使用される
print("val[c] : "..arr["c"]);

■出力結果

val[a] : 100
val[c] : 0

テーブルアクセスメタメソッドその3

__newindexメタメソッドについてです。
__indexメタメソッドは、テーブルに無い要素へのアクセス時の振る舞いを指定するものでしたが、__newindexメタメソッドは、テーブルに要素を追加する際の振る舞いを指定するメタメソッドです。

以下の例では、配列へ新たな要素を追加した場合、add関数を呼ぶようにしています。この場合、arrテーブルに要素をいくら追加したとしても、arrテーブルに要素は追加されず、parentArrayテーブルに追加されることとなります。

■ソース

-- テーブルにフィールドを追加する場合は、常にこちらに追加されます。
local parentArray = { };

-- __newindexにセットする関数。新規にセットするものはすべてparentArrayにセットするようにする
function add(table ,key, val)
        parentArray[key]=val;   
end
local mt = {__newindex=add};

-- メタメソッドをセットする関数
function createArray(o)
        setmetatable(o, mt);
        return o; 
end


-- ############################################################
-- aとbというフィールドを持つテーブルarrを作成する
local arr = createArray({ a=100, b=200 });

-- 新たにcというフィールドに300の値を設定する(このタイミングで__newindexが動作する)
arr["c"]=300;

-- parentArrayの内容一覧を表示
for i,v in pairs(parentArray) do
        print("parentArray : "..i.." : "..v);
end

-- arrの内容一覧を表示
for i,v in pairs(arr) do
        print("arr : "..i.." : "..v);
end

■出力結果

parentArray : c : 300
arr : a : 100
arr : b : 200

環境変数その1

環境変数は_Gに収められています。
また、スコープがlocalではないものも、_Gにはいる様です。

以下のソースでは、numという変数を定義する前・した後の環境変数の一覧を表示しています。

■ソース

function envPrint() 
        for v in pairs(_G) do
                print(v);
                end
end


envPrint();
num = 10;
print("####################################")
envPrint();

■出力結果

string
xpcall
package
tostring
print
envPrint
os
unpack
require
getfenv
setmetatable
next
assert
tonumber
io
rawequal
collectgarbage
arg
getmetatable
module
rawset
math
debug
pcall
table
newproxy
type
coroutine
_G
select
gcinfo
pairs
rawget
loadstring
ipairs
_VERSION
dofile
setfenv
load
error
loadfile
####################################
string
xpcall
package
tostring
print
envPrint
os
unpack
require
getfenv
setmetatable
next
assert
tonumber
io
rawequal
collectgarbage
arg
getmetatable
module
rawset
num
math
debug
pcall
table
newproxy
type
coroutine
_G
select
gcinfo
pairs
rawget
loadstring
ipairs
_VERSION
dofile
setfenv
load
error
loadfile

環境変数その2

環境(_G)を関数毎に変えることが可能です。

setfenvの第一引数には、数値(関数のスタックレベル)、もしくは関数を指定します。

1を指定した場合 現在の関数のグローバル変数のテーブルを変更
2を指定した場合 現在の関数を呼び出している関数のグローバル変数のテーブルを変更
関数 その関数を実行する際のグローバル変数のテーブルの値を変更(?)

■ソース

num = 100;

function printNum()
        print("num : ");
        print(_G.num);
        print();
end


-- setfenvを実行すると、グローバル変数の保存領域である_Gの値を変更できる。
printNum();
setfenv(1, { g = _G })
-- ここでprintNumを呼んでもエラー。
-- なぜなら、_Gの中にprintNumがあるが、現在設定されたグローバル変数の保存領域にはprintNumが無いため
-- そのため、g.printNumとしている
g.printNum();

-- ※setfenv(1, {  })などとすると、グローバル変数が何も無くなります。

■出力結果

num : 
100

num : 
100

環境変数その3

継承を使い、新しい環境変数のテーブルを設定する方法の例

■ソース

num = 10;
print("num : "..num);

local newG = { };
setmetatable(newG, { __index = _G });
setfenv(1, newG);

num = 20;       -- 新しくnumという変数が定義されたこととなる
print("num : "..num);
print("_G.num : ".._G.num);     -- もともと会ったnumは_Gの中にある

■出力結果

num : 10
num : 20
_G.num : 10

モジュールのロード

モジュールのロードについて。
このサンプルを実行するには、lua.exeやluac.exeからパスの通っているところへSample13Mod.luaファイルを置いておく必要があります。

■ソース

require("Sample13Mod");
Sample13Mod.sample13ModFunction();

■出力結果

これはSample13Mod.luaにあるmod.sample13ModFunctionという関数ですよー

■Sample13Mod.luaのソース

-- このファイルはluaから見てパスの通ったところになければなりません

local modName = ...;
local sample13Mod = {};
_G[modName] = sample13Mod;

-- スコープが狭い関数も定義することが可能
local function p(mes)
        print(mes);
end 

function sample13Mod.sample13ModFunction()
        p("これはSample13Mod.luaにあるmod.sample13ModFunctionという関数ですよー");
end

package.loaded[modName] = sample13Mod;
setmetatable(sample13Mod, {__index = _G});
setfenv(1, sample13Mod);

オブジェクト指向プログラミングその1

:(コロン)を使用した書き方(selfという変数を使うと、自身のオブジェクトを指すようになる)も可能です。

■ソース

Data = { num=10 };

function Data:f(val)
        self.num = self.num + val;
end 

function Data:print()
        print("num : "..self.num)
end


Data:f(10);
Data:print();

■出力結果

num : 20

オブジェクト指向プログラミングその2

オブジェクト指向プログラミングその1の例を、:(コロン)を使わずに書いた例です。

■ソース

Data = { num=10 };

function Data.f(s, val)
        s.num = s.num + val;
end 

function Data.print(s)
        print("num : "..s.num)
end


Data.f(Data, 10);
Data.print(Data);

■出力結果

num : 20

クラス

本来メソッドの呼び出しの際、自身のオブジェクトを渡してやらねばならなかったが、:(コロン)を使うことで、内部的に自身のオブジェクトがselfという変数の形で渡すことが可能です。

■ソース

Data  = { num=10 };

function Data:new(o)
        o = o or {};
        self.__index = self;
        setmetatable(o, self);
        return o;
end

function Data:add(v)
        self.num = self.num + v;
end

function Data:print()
        print("num : "..self.num);
end


cls = Data:new();
cls:print();
cls:add(10);    -- ここで値が追加されるのはData.numのnumではなく、cls.numのnumとなる
cls:print();

print();
print([[Data.numを表示]]..Data.num)
print([[cls.numを表示]]..cls.num)

■出力結果

num : 10
num : 20

Data.numを表示10
cls.numを表示20

継承その1

継承元のクラスからオブジェクトを作成し、そのオブジェクトに対し、関数を追加する例。

■ソース

-- Dataクラスの定義
Data = { num = 10 };
function Data:new(o)
        o = o or { };
        setmetatable(o, self);
        self.__index = self;
        return o;
end
function Data:print()
        print("num : "..self.num);
end
function Data:originPrint()
        print("Data:originPrint : "..self.num);
end
function Data.add(v)
        self.num = self.num + v;
end


-- Dataを継承し新しくクラスを作成する
NewCls = Data:new();
-- print関数をもう一度定義(オーバーライド)
function NewCls:print()
        print("NewCls:print : "..self.num);
end
-- NewClsへnumをインクリメントする関数を追加
function NewCls:incliment()
        self.num = self.num+1; 
end

-- ※フィールドを追加する方法について
-- 以下のソースでも追加することが可能。
-- NewCls.newField1 = "NewClsに追加した新しいフィールドnewField1です";
-- これでも可能であるが、new関数の引数に配列を渡してやる方法のほうがそれっぽく見える。
-- cls = NewCls:new({ num1 = 10, num2= 20});
-- みたいな書き方をすると、NewCls:newで生成したクラスはフィールドにnum1とnum2を持つようになる。
-- こちらの書き方のほうが、わかりいい気がする。

cls = NewCls:new();     
cls:originPrint();
cls:print();

-- インクリメントする
cls:incliment();
print("インクリメントの実施後");
cls:originPrint();
cls:print();

■出力結果

Data:originPrint : 10
NewCls:print : 10
インクリメントの実施後
Data:originPrint : 11
NewCls:print : 11

継承その2

継承元のクラスからオブジェクトを作成し、そのオブジェクトに対し、フィールドを追加する方法
単純に、NewCls.newField=10;などとして、クラスに対してフィールドを追加してもよいが、new関数の引数に配列を渡すやりかたで行うと、それっぽく見える。

■ソース

-- Dataクラスの定義
Data = { num = 10 };
function Data:new(o)
        o = o or { };
        setmetatable(o, self);
        self.__index = self;
        return o;
end
function Data:print()
        print("num : "..self.num);
end
function Data:originPrint()
        print("Data:originPrint : "..self.num);
end
function Data.add(v)
        self.num = self.num + v;
end


-- Dataを継承し新しくクラスを作成する
NewCls = Data:new();
-- print関数をもう一度定義(オーバーライド)
function NewCls:print()
        print("NewCls:print : "..self.num);
end
-- NewClsへnumをインクリメントする関数を追加
function NewCls:incliment()
        self.num = self.num+1; 
end
-- num1とnum2を表示する関数。フィールドにnum1・num2がない場合は0とする
function NewCls:printNum1Num2()
        -- この値の取得の仕方にすることで、フィールドが存在しない場合は0になるようにしている。
        local nm1 = self.num1 or 0;
        local nm2 = self.num2 or 0;
        
        print("NewCls:printNum1 : "..nm1);
        print("NewCls:printNum2 : "..nm2);
end

-- フィールドnum1と、num2を追加した新しいクラスを作る。
cls = NewCls:new({num1=10, num2=20});   
cls:originPrint();
cls:print();
cls:printNum1Num2();

-- インクリメントする
cls:incliment();
print("インクリメントの実施後");
cls:originPrint();
cls:print();
cls:printNum1Num2();

■出力結果

Data:originPrint : 10
NewCls:print : 10
NewCls:printNum1 : 10
NewCls:printNum2 : 20
インクリメントの実施後
Data:originPrint : 11
NewCls:print : 11
NewCls:printNum1 : 10
NewCls:printNum2 : 20

多重継承

多重継承のようなことが可能です。しかし、lua上に関数を実装し、それっぽくみせているだけで、言語仕様上サポートしているわけではありません。

■ソース

-- ####################### 多重継承を実現するための補助関数 #######################
-- 多重継承を実現させるための補助関数
function search(k, list)
        for i=1, #list do
                local v = list[i][k];
                if v then
                        return v;
                end
        end
end

-- 多重継承したクラスを作成するための関数
function createClass(...)
        local c = {};
        local parents = {...}
        
        
        -- ※setmetatableの内容について
        -- 本来ならコメントアウトされている方のものを使用すればよい。
        -- 両社の差異は「t[k] = v;」だけである。
        -- 何をしているかというと、検索した結果をキャッシュしている。
        -- こちらの方がパフォーマンスが上がる・・・らしい。
        -- setmetatable(c, {__index = function(t, k)
        --                      return search(k, parents);
        --              end
        --              });
        setmetatable(c, {__index = function(t, k)
                                local v = search(k, parents)
                                t[k] = v; 
                                return v;
                        end
                        });
        c.__index = c;
        
        -- cにnew関数を追加
        function c:new (o)
                o = o or {};
                setmetatable(o, c);
                return o;
        end
        
        return c;
end
-- ##############################################


-- 基本クラス
Base = {name="Base"};
function Base:print()
        print("name : "..name);
end

-- 継承するクラス その1(気温情報を保持する)
ExtendClassA = {kion=0};
function ExtendClassA:printKion()
        print("ExtendClassA:printKion "..self.kion);
end

-- 継承するクラス その2(湿度情報を保持する)
ExtendClassB = {situdo=0};
function ExtendClassB:printSitudo()
        print("ExtendClassB:printSitudo "..self.situdo);
end

-- 多重継承したクラスtenki(天気)を作成
Tenki = createClass(ExtendClassA, ExtendClassB);

-- Tenkiクラスのインスタンス作成
tenki = Tenki:new({kion=20, situdo=70});

tenki:printKion();
tenki:printSitudo();

■出力結果

ExtendClassA:printKion 20
ExtendClassB:printSitudo 70

プライベート性

とある値をインターフェイスを介してアクセスさせることにより、プライベート性を示すことができる。

■ソース

function newClass(initialNum)
        local self = {num = initialNum};
        
        local printNum = function()
                                                        print(self.num);
                                                end

        local setNum = function (v)
                                                self.num = v;
                                        end

        return {
                printNum = printNum;
                setNum = setNum;
        };
end



cls = newClass(100);

-- ここで使用されるのは、newClass関数が返した配列
-- 直接selfの値を触らないようにして使用することが可能。
cls.printNum();
cls.setNum(10);
cls.printNum();

■出力結果

100
10

弱いテーブル

よくある「弱いテーブル」を作成することがluaでも可能です。
弱いテーブルからガーベジコレクションされるのは、オブジェクトだけです。数値などはガーベジコレクションの対象外です。
文字列は値なので、ガーベジコレクション対象外となります。

■ソース

array = {};

-- __modeに文字列をセットすることで、keyが使われなくなっている場合
-- セットされている値が使われていない場合を、を指定できる。
mt = {__mode="k"};
setmetatable(array, mt);


-- key1を作成し、arrayに設定
key1 = {}
array[key1] = 10;

-- key2を作成し、arrayに設定
key2 = {}
array[key2] = 100;


-- 現在のarrayをすべて表示
print("ガーベジコレクション実行前")
for k, v in pairs(array) do
        print(v);
end

-- key2を削除しガーベジコレクションを実行
key2 = nil;
collectgarbage();


-- 現在のarrayをすべて表示
print("ガーベジコレクション実行後")
for k, v in pairs(array) do
        print(v);
end

■出力結果

ガーベジコレクション実行前
10
100
ガーベジコレクション実行後
10

メモ化関数

弱いテーブルのテクニック的なものです。

関数の応答結果が要求に対して固定である場合、一度処理を実行した後、応答を記録しておき二回目以降の呼び出しは、処理を実施せず、記録しておいた応答を返すことをいいます。

要求と応答のパターンがものすごい数がある場合、結果を記録しておくテーブルだけでメモリが満杯になってしまうのでこの記録しておく領域は弱いテーブルにしておきます。
ガーベジコレクションが実施されるたびに、応答を記録していた情報は消えますが、結果としてメモリ使用量と実行速度がバランスし、パフォーマンスが上がる・・・らしいです。

■ソース

local result = {};
setmetatable(result, {__mode="kv"})

function func(request)
        local res = result[request];
        if res == nil then
                -- 結果がキャッシュされていない場合処理を実行する
                res = mes(request);
                result[request] = res;
        end
        
        return res;
end


function mes(request)
        -- なにか処理する・・・
        
        local result = "";
        if request == "その1" then
                result = "うはwwwwwwwwwwwwwww";
        elseif request == "その2" then
                result = "orz";
        else
                result = "うひw";
        end
        
        return result;
end


-----------------
print("result : "..func("その1"));
print("result : "..func("その1"));
print("result : "..func("その1"));
print("result : "..func("その1"));
print("result : "..func("その1"));

■出力結果

result : うはwwwwwwwwwwwwwww
result : うはwwwwwwwwwwwwwww
result : うはwwwwwwwwwwwwwww
result : うはwwwwwwwwwwwwwww
result : うはwwwwwwwwwwwwwww

ライブラリ

各種関数のかいつまんだサンプルです。
基本的にリファレンスを参照してから使った方がよいと思います。。。

■ソース

-- #################### 数学ライブラリ ####################
print("■数学ライブラリ")
print("math.huge : "..math.huge);       -- math.hugeは大きい数を示すライブラリの変数
print("math.pi : "..math.pi);           -- math.piは円周率を示すライブラリの変数
print("math.sin(math.rad(0.5)) : "..math.sin(math.rad(0.5)));   -- 三角関数系の関数も用意されている

-- ランダムな数値を作成
math.randomseed(os.time());     -- seedを時刻で設定(実行するたびに異なる値にする)
print("math.random : "..math.random()); -- 0〜1の間のランダムな値を生成する。

print();
-- #################### テーブルライブラリ(挿入・削除) ####################
print("■テーブルライブラリ(挿入・削除)")
array1 = {"その1", "その2", "その3", "その4", "その5"};
-- テーブルへ位置を指定して挿入
table.insert(array1, 6, "その6");
-- テーブルへ位置を指定せず挿入(最後に挿入されます)
table.insert(array1, "その0")
-- テーブルの要素の削除(2番目の要素を削除=この場合"その1"が該当)
table.remove(array1, 2);
-- 状態を表示する
for i=1, #array1 do
        print(i.." : "..array1[i]);
end

print();
-- #################### テーブルライブラリ(ソート) ####################
print("■テーブルライブラリ(ソ\ート)")

-- 単純なソート
array2 = {"その2", "その3", "その1"}
table.sort(array2, function(a, b) return (a<b) end)

-- 配列の並び順に値を取得したい場合はipairs関数を使用する。
-- pairs関数は配列の並び順にデータが帰ってくるとは限らない
print("単純なsort")
for i,v in ipairs(array2) do    
        print(i.." : "..v);
end

-- key項目でソート
print("key項目でソ\ート")
array2 = {a3="その3", a1="その1", a2="その2"}

-- まず、key項目の一覧を取得する
tmp = {};
for n in pairs(array2) do       tmp[#tmp+1] = n end
-- key項目のソート
table.sort(tmp, function(a, b) return (a<b) end);
-- ソートしたkey順に表示
for i, v in ipairs(tmp) do
        print("key:"..v.." : "..array2[v]);
end


print();
-- #################### テーブルライブラリ(文字列連結) ####################
print("■テーブルライブラリ(文字列連結)")

-- テーブルの各要素が文字列の場合、それらを連結して1つの文字列にします。
array3 = {"明日", "天気に", name="val", "なぁれ"}
print(table.concat(array3));




print();
-- #################### 文字列操作 ####################
print("文字列操作")

msg1 = "AaBbCcDdEeFf"

-- すべて大文字にする
print(msg1.."を小文字にする : "..string.lower(msg1));
print(msg1.."を大文字にする : "..string.upper(msg1));

-- 文字列の切り出し
print(msg1.."を2文字目以降切り出す : "..string.sub(msg1, 2));
print(msg1.."を2文字目から最後の2文字目までを以降切り出す : "..string.sub(msg1, 2, -2));

-- 文字、文字コードを相互に変換
print(string.char(97).."の文字コード"..string.byte("a"));

-- 文字列のフォーマットを指定(書式化文字列の書き方はC言語と同じ・・・というかこの処理はCのライブラリに丸投げらしい)
print(string.format("PI : %.4f",math.pi));
print("100 : "..string.format("%d, %o, %x, %f",100,100,100,100));


print();
-- #################### 文字列操作(検索) ####################
print("文字列操作(検索)");

msg2 = "美希ちゃんのブラジャーペロペロ〜";

-- 文字列のパターン検索
print("検索対象の文字列 : "..msg2)
i, j = string.find(msg2, "ペロ")
print(msg2.."の中で「"..string.sub(msg2, i, j).."」の位置は"..i.."から"..j.."です。");
-- ※ブラジャーの「ー」を検索対象にいれるとうまく動きません(文字コードがらみで動かないんだと思います。

-- 検索する単語を順番に検索
msg3 = "ナウい息子♂ナウい息子♂ナウい息子♂ナウい息子♂ナウい息子♂♂♂♂♂♂♂♂"
basyo = {}
i = 0;
print(msg3.."の中から「ナウい」を検索")
while true do
        i = string.find(msg3, "ナウい", i+1) -- 第3引数で文字列のどの場所以降を検索するかを指定している
        if i==nil then break end
        basyo[#basyo+1] = i;
end
for i=1, #basyo do
        print("   「ナウい」のあった場所 "..i.."番目 ♂ "..basyo[i])
end

print();
-- #################### 文字列操作(置換) ####################
print("文字列操作(置換)");

-- 文字列を置換する
msg3 = "ひきこもりになりたい"
res, c = string.gsub(msg3, "ひきこもり", "お金持ち")
print(res.."    置換した個数:"..c)

-- gsub関数の第三引数に関数を指定することも可能
-- 歪という字が化けるのでひらがなにしてます
msg3 = "妖精哲学の三信:  だらしねぇというa  ゆがみねぇというb  仕方ないというc";
print (string.gsub(msg3, "[abc]", function(n)
                if n=="a" then
                        return "戒めの心"; 
                elseif n=="b" then
                        return "賛美の心";
                else
                        return "許容の心";
                end
        end))

-- 文字列中の空白を数える
msg4 = "a b c d e f"
print(msg4.."の中に含まれるスペースは : "..select(2, string.gsub(msg4, " ", " ")))


print();
-- #################### 文字列操作(パターンマッチング) ####################
print("文字列操作(パターンマッチング)");

-- 指定のフォーマットの文字列を探す
msg5 = "2011/04/30 今日はいい日だ!    2011/5/01 何事もなく無事に一日が終わった!"
print(string.match(msg5, "%d+/%d+/%d+"))

-- 指定のフォーマットの文字列をすべて探す
for arr in string.gmatch(msg5, "%d+/%d+/%d+") do
        print("d+/%d+/%d+にマッチしたもの".." : "..arr);
end

-- いろいろやりかたがある様子。詳細は公式のリファレンスを参照
-- ■文字クラス
-- .  文字
-- %a 英文字
-- %c 制御文字
-- %d 数字
-- %l 英小文字
-- %p 句読点
-- %s 空白文字
-- %u 英大文字
-- %w 英数字
-- %x 十六進数
-- %z 0として表現される文字を表す(数値表現が0の文字)

-- ■修飾子
-- + 1回以上の繰り返し
-- * 0回以上の繰り返し(この繰り返し要素は、可能な限り長いシーケンスにマッチする)
-- - 0回以上の繰り返し(この繰り返し要素は、可能な限り長いシーケンスにマッチする)
-- ? 1回、もしくは1回以上の繰り返し

print();
-- #################### 文字列操作(キャプチャ) ####################
-- ()で囲んだ部分だけを、抜き出すことができます。
print("文字列操作(キャプチャ)");

msg5 = "stm = saitama! ";
key, val = string.match(msg5, "(%a+)%s*=%s*(.*)");
print(msg5.."のkeyとvalueを取得 → "..key.." : "..val);


print();
-- #################### 文字列操作(I/Oライブラリ) ####################
print("文字列操作(I/Oライブラリ)");

-- シンプル版(雑多な処理が不要な代わりに、1つのファイルしか扱えません。デフォルトではstdin/stdoutに初期化されています)
io.write("フヒヒ、サーセンwwwwwwwwwwwっうぇwっうぇwwww");
msg6 = io.read("*line");        --      stdinから入力を受け付けます
print("\n入力された文字 : "..msg6);    -- コンソールから"yugaminelenaと入力しました"

print();
tmp = io.input();       --ファイルハンドルを取得
io.input("E:/usr/my/eclipse/Lua/LuaStudy/aa.txt");      --ファイルハンドルを設定(INPUTはこのファイルを対象に処理される)
print("一時的にaa.txtにファイルハンドルを変更し1行読み込んでみた結果:"..io.read("*line"));
io.input():close();     --ファイルハンドルをクローズする
io.input(tmp);          --変更したファイルハンドルを元に戻す

-- フルセット版(ファイルハンドルを使用し、複数のファイルを扱えます)
-- open関数の第二引数はw, r, a・・・・など大方C言語と同じです(バイナリで読み書きさせるには"rb","wb"を使います)
f = assert(io.open("E:/usr/my/eclipse/Lua/LuaStudy/aa.txt", "w"))
f:write("だらしねぇという 戒めの心\n");
f:write("歪みねぇという 賛美の心\n");
f:write("仕方ないという  許容の心\n");
f:write("それが大切だね。");
f:close();

print();
-- 書き込みを行ったファイルを読み出す(1行づつ読み出す)
f = assert(io.open("E:/usr/my/eclipse/Lua/LuaStudy/aa.txt", "r"))

local counter = 0;
while true do
        local lines = f:read("*line");
        if not lines then break; end    -- 読み出す文字がない場合はbreakする
        
        -- 1行のデータを画面へ出力
        counter = counter+1;
        print(counter.." : "..lines);
end
f:close();

-- 書き込みを行ったファイルを読み出す(一度にすべて読み出す)
f = assert(io.open("E:/usr/my/eclipse/Lua/LuaStudy/aa.txt", "r"))
msg6 = f:read("*all")
f:close();
print("\n取得した文字列:")
print(msg6);




print();
-- #################### 時間取得など。。。 ####################
print("時間取得など");

-- 現在の日付を出力
-- いろいろ種類があるので、詳細はLuaのリファレンスマニュアル参照
print("(1)現在の日付:"..os.date("%Y / %m / %d  [%A]     %H:%M"));
print("(2)現在の日付:"..os.date("%x  %X"));


-- CPU時間を出力(ベンチマークなどで使う)
startTime = os.clock();
tmp = "tmp";
for i=0,20 do
        tmp = tmp..tmp;
end
finishTime = os.clock();
print("文字列の連結を20回繰り返すのにかかった時間:"..finishTime-startTime);




print("---------------------------------------------------")
print("サンプルを終了します")

■出力結果

aaaa って入力したよ!
■数学ライブラリ
math.huge : 1.#INF
math.pi : 3.1415926535898
math.sin(math.rad(0.5)) : 0.0087265354983739
math.random : 0.80529190954314

■テーブルライブラリ(挿入・削除)
1 : その1
2 : その3
3 : その4
4 : その5
5 : その6
6 : その0

■テーブルライブラリ(ソート)
単純なsort
1 : その1
2 : その2
3 : その3
key項目でソート
key:a1 : その1
key:a2 : その2
key:a3 : その3

■テーブルライブラリ(文字列連結)
明日天気になぁれ

文字列操作
AaBbCcDdEeFfを小文字にする : aabbccddeeff
AaBbCcDdEeFfを大文字にする : AABBCCDDEEFF
AaBbCcDdEeFfを2文字目以降切り出す : aBbCcDdEeFf
AaBbCcDdEeFfを2文字目から最後の2文字目までを以降切り出す : aBbCcDdEeF
aの文字コード97
PI : 3.1416
100 : 100, 144, 64, 100.000000

文字列操作(検索)
検索対象の文字列 : 美希ちゃんのブラジャーペロペロ〜
美希ちゃんのブラジャーペロペロ〜の中で「ペロ」の位置は23から26です。
ナウい息子♂ナウい息子♂ナウい息子♂ナウい息子♂ナウい息子♂♂♂♂♂♂♂♂の中から「ナウい」を検索
   「ナウい」のあった場所 1番目 ♂ 1
   「ナウい」のあった場所 2番目 ♂ 13
   「ナウい」のあった場所 3番目 ♂ 25
   「ナウい」のあった場所 4番目 ♂ 37
   「ナウい」のあった場所 5番目 ♂ 49

文字列操作(置換)
お金持ちになりたい    置換した個数:1
妖精哲学の三信:  だらしねぇという戒めの心  ゆがみねぇという賛美の心  仕方ないという許容の心       3
a b c d e fの中に含まれるスペースは : 5

文字列操作(パターンマッチング)
2011/04/30
d+/%d+/%d+にマッチしたもの : 2011/04/30
d+/%d+/%d+にマッチしたもの : 2011/5/01

文字列操作(キャプチャ)
stm = saitama! のkeyとvalueを取得 → stm : saitama! 

文字列操作(I/Oライブラリ)
フヒヒ、サーセンwwwwwwwwwwwっうぇwっうぇwwww
入力された文字 : aaaa って入力したよ!

一時的にaa.txtにファイルハンドルを変更し1行読み込んでみた結果:だらしねぇという 戒めの心

1 : だらしねぇという 戒めの心
2 : 歪みねぇという 賛美の心
3 : 仕方ないという  許容の心
4 : それが大切だね。

取得した文字列:
だらしねぇという 戒めの心
歪みねぇという 賛美の心
仕方ないという  許容の心
それが大切だね。

時間取得など
(1)現在の日付:2011 / 06 / 08  [Wednesday]     07:21
(2)現在の日付:06/08/11  07:21:24
文字列の連結を20回繰り返すのにかかった時間:0.0060000000000002
---------------------------------------------------
サンプルを終了します
■aa.txtの内容
だらしねぇという 戒めの心
歪みねぇという 賛美の心
仕方ないという  許容の心
それが大切だね。

※処理結果を出力させるため、「msg6 = io.read("*line"); -- stdinから入力を受け付けます」の処理に対し、「aaaa って入力したよ!」と入力しました

デバッグライブラリ

luaに備わっているdebug関数についてのサンプル。

debug.getinfoの戻り値の各種フィールドの説明(詳細はよくわからんw)

nups 関数が持つ、上位値の数
what 関数の種類。"Lua"ならLua関数、"C"ならC関数、"main"ならluaのメインチャンク
func 関数自身
lastlinedefined 関数が定義されているソースの最後の行
source 関数の定義されているファイル名。頭には@がつく。
currentline 現在実行中の行番号。使用不可能な場合は-1となる。
namewhat どのように関数が呼ばれたか。"global","local","method","field"のどれかになる
linedefined 関数が定義されているソースの最初の行
short_src

sourceの短縮形(最大60文字)

■ソース

-- 関数の情報を取得
print("■getinfo関数について")
function testFunction()
        print("にゃんにゃんにゃん")
end

arr = debug.getinfo(testFunction);
for i,v in pairs(arr) do
        io.write(i, " : ", tostring(v));
        print(); 
end


print();
-- ローカル変数の値を取得・設定するデバッグ関数
print("■getlocal/setlocal関数について")
function test2Function(a, b, c)
        local val = "うひw"
        print("フヒヒ、サーセンwwwwwwwwwwwっうぇw");
        
        -- デバッグ関数を使用して、ローカル変数をアクセス
        local counter = 1;
        while true do
                -- getlocal関数の第一引数は、調査するスタックレベル、第二引数は変数のインデックスです。
                local key, val = debug.getlocal(1, counter);
                if not key then break; end
                print(key.." : "..val);
                counter = counter+1;
        end
        
        -- デバッグ関数でローカル変数の値を変更する(aの値を100にセットします)。
        print("a(変更前) : "..a);
        -- 引数の意味 : スタックレベル、変数のインデックス、変更する値、戻り値に、変更した変数名が返ります。
        name = debug.setlocal(1, 1, 100);
        print("a(変更後) : "..a.."  変更した変数の変数名:"..name);
end

test2Function(1, 2, 3);




print();
-- 上位値を取得・設定するデバッグ関数(上位値を特に設定していないので何も表示されない)
-- ここについては手抜きです。。。
print("■getupvalue/setupvalue関数について")

gVal1 = "にゃ";
gVal2 = "うへwww";

function test3Funciton(q)
        -- 呼び出し元の関数名を取得
        local func = debug.getinfo(2).func;
        --print("func : "..tostring(func).." / "..tostring(debug.getinfo(2).name));
        local i=1;
        while true do
                local key, val = debug.getupvalue(func, i);
                if not key then break; end
                print(key.." : "..val);
                i = i+1; 
        end
end

function a(num)
        local aaa=100;
        bbb=1000;
        test3Funciton(1000);
end

a(10);




print();
-- フック(デバッグ関数)
print("■フック(デバッグ関数)について")


function test4Function()
        print("にゃー");
        print("フヒヒ");
        print("あーうー");
end

function hookFunction(event, line)
        print("-------------");
        print("event : "..event.." / line : "..tostring(line))
        local s = debug.getinfo(2).short_src;
        print("src : "..s);
        print("-------------");
        
end

-- フックをセット(第一引数に、フックした場合、呼び出す関数、第二引数にフック対象のイベント)
-- フック対象のイベントは、"call", "return","line"."count"の4種類のみ。引数に値として渡す場合、頭の文字だけを渡す。
debug.sethook(hookFunction, "l");
test4Function();

■出力結果

■getinfo関数について
nups : 0
what : Lua
func : function: 0050E858
lastlinedefined : 9
source : @E:\usr\my\eclipse\Lua\LuaStudy\case1/Sample22.lua
currentline : -1
namewhat : 
linedefined : 7
short_src : E:\usr\my\eclipse\Lua\LuaStudy\case1/Sample22.lua

■getlocal/setlocal関数について
フヒヒ、サーセンwwwwwwwwwwwっうぇw
a : 1
b : 2
c : 3
val : うひw
counter : 5
a(変更前) : 1
a(変更後) : 100  変更した変数の変数名:a

■getupvalue/setupvalue関数について

■フック(デバッグ関数)について
-------------
event : line / line : 116
src : E:\usr\my\eclipse\Lua\LuaStudy\case1/Sample22.lua
-------------
-------------
event : line / line : 99
src : E:\usr\my\eclipse\Lua\LuaStudy\case1/Sample22.lua
-------------
にゃー
-------------
event : line / line : 100
src : E:\usr\my\eclipse\Lua\LuaStudy\case1/Sample22.lua
-------------
フヒヒ
-------------
event : line / line : 101
src : E:\usr\my\eclipse\Lua\LuaStudy\case1/Sample22.lua
-------------
あーうー
-------------
event : line / line : 102
src : E:\usr\my\eclipse\Lua\LuaStudy\case1/Sample22.lua
-------------

Cモジュール

SampleDll.dllを読み込み実行する
SampleDll.dllを、パスの通ったところにコピー後実行してください(例:SampleDll.dllをlua.exeやluac.exeのあるところにコピーする・・・など)

■ソース

require "SampleDll"

result = sampleDll.sampleFunc1("aaaaaaaaaa");
print("\nsampleFunc1結果 : "..result);

■出力結果

luaopen_alctailLibが呼ばれました!
sampleFunc1が呼ばれました 処理スタート。。。!
sampleFunc1処理完了。。。

sampleFunc1結果 : 10







このサンプルの構成について

Sample1以外については、サンプルソースには、SampleX関数が書いてあるだけで、main関数がありません。各サンプルは、単純にmain関数からsampleX関数を呼ぶだけで動くようになっています。
また、stackDump関数を使っているところがありますが、これは私が作った関数です。

以下のようなmain関数が書いてあるファイルを作成し、各サンプルのSampleX関数を呼び出してやってください。。。手抜きですみませんw
ここで使用したluaファイル、cppファイルなどをまとめたものを置いておきます。 → まとめたもの

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "lua.hpp"

//luaファイルのあるディレクトリのパス
#define LUAFILE_DIRECTORY       "E:/usr/my/vc/LuaStudy/Sample1/Sample1/"


extern int stackDump(lua_State*);
extern int sample1();
extern int sample2();
extern int sample3();
extern int sample4();
extern int sample5();
extern int sample6();
extern int sample7();
extern int sample8();
extern int sample9();
extern int sample10();
extern int sample11();
extern int sample12();
extern int sample13();
extern int sample14();
extern int sample15();
extern int sample16();
extern int sample17();

int main() {
        //呼び出すサンプル関数のコメントを消して実行してください。
        //sample2();
        //sample3();
        //sample4();
        //sample5();
        //sample6();
        //sample7();
        //sample8();
        //sample9();
        //sample10();
        //sample11();
        //sample12();
        //sample13();
        //sample14();
        //sample15();
        //sample16();
        //sample17();

        return 0;
}

/**
 * luaのスタック(C←→luaのデータやり取りをするためのスタック)の情報を表示します
 */
int stackDump(lua_State *L) {

        printf("----------------------------------\n");
        int sp = lua_gettop(L);
        for(int i=1; i<=sp; i++) {
                
                printf(":stackDump  sp : %d  ", i);

                switch(lua_type(L, i)) {
                        case LUA_TNONE : {
                                printf("  → type : LUA_TNONE");
                                break;
                        }
                        case LUA_TNIL : {
                                printf("  → type : LUA_TNIL");
                                break;
                        }
                        case LUA_TBOOLEAN : {
                                char *msg = lua_toboolean(L, i) ? "true" : "false";
                                printf("  → type : LUA_TBOOLEAN   value : %s", msg);
                                break;
                        }
                        case LUA_TLIGHTUSERDATA : {
                                printf("  → type : LUA_TLIGHTUSERDATA");
                                break;
                        }
                        case LUA_TNUMBER : {
                                printf("  → type : LUA_TNUMBER   value : %g", lua_tonumber(L, i));
                                break;
                        }
                        case LUA_TSTRING : {
                                printf("  → type : LUA_TSTRING   value : %s", lua_tostring(L, i));
                                break;
                        }
                        case LUA_TTABLE : {
                                printf("  → type : LUA_TTABLE");
                                break;
                        }
                        case LUA_TFUNCTION : {
                                printf("  → type : LUA_TFUNCTION");
                                break;
                        }
                        case LUA_TUSERDATA : {
                                printf("  → type : LUA_TUSERDATA");
                                break;
                        }
                        case LUA_TTHREAD : {
                                printf("  → type : LUA_TTHREAD");
                                break;
                        }
                        default : {
                                printf("  → type : N/A   value : %s", lua_typename(L, i));
                                break;
                        }
                }
                printf("\n");
        }
        printf("----------------------------------\n");
        return 0;
}

Sample1 インタープリターのようなものを作る

標準入力から1行づつ読み込み、入力された文字列をluaへ放り込み処理させます。

■ソース

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "lua.hpp"
extern int sample1();

int main() {
        sample1();

        return 0;
}


/**
 * インタープリターのような動きをします。
 */
int sample1() {
        char buff[256];
        int error;

        lua_State *L = luaL_newstate();
        luaL_openlibs(L);

        while(fgets(buff, sizeof(buff), stdin) != NULL) {
                error = luaL_loadbuffer(L, buff, strlen(buff), "line") || lua_pcall(L, 0, 0, 0);

                if (error) {
                        fprintf(stderr, "%s", lua_tostring(L, -1));
                        lua_pop(L, 1);
                }
        }

        lua_close(L);
        return 0;
}

■出力結果

print("test!")
test!

※コンソールにて1行目を入力しエンターキーを押下します。すると、入力した文字列がluaにわたり、その処理が実行されます

sample2 luaスクリプトに記述されている変数の値を取得する

Cからluaスクリプトを実行後、lua上の変数の値を取得する方法について

■ソース

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "lua.hpp"

#define LUAFILE_DIRECTORY       "E:/usr/my/vc/LuaStudy/Sample1/Sample1/"

extern int stackDump(lua_State *L);


/**
 * Luaのスクリプトから、変数の値を取得します。
 */
int sample2() {
        lua_State *L = luaL_newstate();
        luaL_openlibs(L);
        int width = 0;
        int height = 0;

        try {
                //.luaファイルを実行する
                char file_path[256];
                strcpy_s(file_path, LUAFILE_DIRECTORY); //VS2010ではstrcpyを使うとワーニングが出るためこの関数に変更
                strcat_s(file_path, "luascript2.lua");  //VS2010ではstrcpyを使うとワーニングが出るためこの関数に変更
                printf(".lua file_path : %s\n", file_path);
                if(luaL_loadfile(L, file_path) || lua_pcall(L, 0, 0, 0) ) {
                        //エラー
                        throw lua_tostring(L, -1);
                }

                stackDump(L);
                //luaのスクリプトから指定した名前の変数の値を取得
                lua_getglobal(L, "width");
                stackDump(L);
                lua_getglobal(L, "height");
                stackDump(L);

                //もってきた値の型チェック
                if(!lua_isnumber(L, -2)) {
                        //widthは数字型ではない場合
                        throw "width is not number type";
                }
                if(!lua_isnumber(L, -1)) {
                        //heightは数字型ではない場合
                        throw "height is not number type";
                }

                width = lua_tointeger(L, -2);
                height = lua_tointeger(L, -1);


                printf("width : %d \nheight : %d\n", width, height);
                
        } catch(char *msg) {
                // とりあえずなんでもかんでもエラーとする
                lua_close(L);

                printf("error message : %s", msg);
                printf("エラー発生。終了します。\n");
                exit(0);
        }

        lua_close(L);
        printf("処理が正常に完了しました。\n");
        return 0;
}

■ソース(luascript2.lua)

width = 100;
height = 200;

■出力結果

.lua file_path : E:/usr/my/vc/LuaStudy/Sample1/Sample1/luascript2.lua
----------------------------------
----------------------------------
----------------------------------
:stackDump  sp : 1    → type : LUA_TNUMBER   value : 100
----------------------------------
----------------------------------
:stackDump  sp : 1    → type : LUA_TNUMBER   value : 100
:stackDump  sp : 2    → type : LUA_TNUMBER   value : 200
----------------------------------
width : 100
height : 200
処理が正常に完了しました。

sample3 luaスクリプトに記述されているテーブルの値を取得する

sample2に引き続き、今度はテーブルの値を取得するサンプルです。

■ソース

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "lua.hpp"

#define LUAFILE_DIRECTORY       "E:/usr/my/vc/LuaStudy/Sample1/Sample1/"

extern int stackDump(lua_State *L);



/**
 * luaより、テーブルの値の取得を行います

 lua_to〜だとか、lua_push〜だとかの関数は、セットorゲットするデータの型
 毎に、関数が用意されています。
 lua_tostringだとかlua_tonumber・・・とか。どんなものがあるかは、詳細はリファレンスマニュアルを見てください。
 */
int sample3() {

        lua_State *L = luaL_newstate();
        luaL_openlibs(L);
        char *index1_string = NULL;
        char *index2_string = NULL;
        char *index3_string = NULL;

        try {
                //.luaファイルを実行する
                char file_path[256];
                strcpy_s(file_path, LUAFILE_DIRECTORY);
                strcat_s(file_path, "luascript3.lua");
                printf(".lua file_path : %s\n", file_path);
                if(luaL_loadfile(L, file_path) || lua_pcall(L, 0, 0, 0) ) {
                        //エラー
                        throw lua_tostring(L, -1);
                }
        
                //luaで、mapというテーブルの値を取得します
                lua_getglobal(L, "map");
                //テーブル型であるか確認をします
                if(!lua_istable(L, -1)) {
                        throw "mapはテーブル型ではありません。終了します。";
                }

                //index1の値を取得します
                lua_pushstring(L, "index1");
                lua_gettable(L, -2);    //上でpushした値をキーとして、その値を取得
                //文字列型であるか確認をします
                if(!lua_isstring(L, -1)) {
                        throw "文字列ではありません。終了します。";
                }
                //値を取得
                index1_string = (char*)lua_tostring(L, -1);
                printf("index1 : %s\n", index1_string);
                lua_pop(L, 1);  //取得したデータを削除しておく

                //index2の値を取得します
                lua_pushstring(L, "index2");
                lua_gettable(L, -2);    //上でpushした値をキーとして、その値を取得
                //文字列型であるか確認をします
                if(!lua_isstring(L, -1)) {
                        throw "文字列ではありません。終了します。";
                }
                //値を取得
                index2_string = (char*)lua_tostring(L, -1);
                printf("index2 : %s\n", index2_string);
                lua_pop(L, 1);  //取得したデータを削除しておく

                //index3の値を取得します
                //上記のindex1/index2の値の取得方法を簡便化した関数がluaのライブラリより提供されているのでそれを使用します
                lua_getfield(L, -1, "index3");
                //文字列型であるか確認をします
                if(!lua_isstring(L, -1)) {
                        throw "文字列ではありません。終了します。";
                }
                //値を取得
                index3_string = (char*)lua_tostring(L, -1);
                printf("index3 : %s\n", index3_string);
                lua_pop(L, 1);  //取得したデータを削除しておく


        } catch(char *msg) {
                lua_close(L);
                printf("error : %s", msg);
                printf("エラーが起きたので終了します。");
                exit(0);
        }

        lua_close(L);
        printf("処理が正常に完了しました。\n");
        return 0;
}

■ソース(luascript3.lua)

map = {
        index1="うー☆", index2="うーうー", index3="そのうーうー言うの(ry",
}

■出力結果

.lua file_path : E:/usr/my/vc/LuaStudy/Sample1/Sample1/luascript3.lua
index1 : うー☆
index2 : うーうー
index3 : そのうーうー言うの(ry
処理が正常に完了しました。

sample4 luaのデータへCのソースから、テーブルデータを設定する

Cのソースからluaのデータへ、テーブルデータを新規作成し設定します。

■ソース(C/C++)

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "lua.hpp"

#define LUAFILE_DIRECTORY       "E:/usr/my/vc/LuaStudy/Sample1/Sample1/"

extern int stackDump(lua_State *L);
extern void showUserAddTable(lua_State *L);

/**
 * luaのデータへCのソースから、テーブルデータを設定する
 */
int sample4() {
        lua_State *L = luaL_newstate();
        luaL_openlibs(L);

        try {
                //lua側へ、"userAddTable"という変数名でテーブルを追加します
                lua_newtable(L);

                //key:"index1", value:"あーうー"の値をテーブルへ追加します
                lua_pushstring(L, "あーうー");
                lua_setfield(L, -2, "index1");

                //key:"index2", value:"うー☆"の値をテーブルへ追加します
                lua_pushstring(L, "うー☆");
                lua_setfield(L, -2, "index2");

                //変数名"userAddTable"を追加
                lua_setglobal(L, "userAddTable");

                //---------------------------------------------
                //変数名"userAddTable"の値を取得してみます。
                showUserAddTable(L);

        } catch(char *msg) {
                lua_close(L);
                printf("error : %s\n", msg);
                printf("エラーが起きたので終了します。");
                exit(0);
        }

        lua_close(L);
        printf("処理が正常に完了しました。\n");
        return 0;
}

/**
 * "userAddTable"の値を表示する
 */
static void showUserAddTable(lua_State *L) {

        //luaで、mapというテーブルの値を取得します
        lua_getglobal(L, "userAddTable");
        //テーブル型であるか確認をします
        if(!lua_istable(L, -1)) {
                throw "テーブル型ではありません。終了します。";
        }

        //index1の値を取得します
        lua_getfield(L, -1, "index1");
        //文字列型であるか確認をします
        if(!lua_isstring(L, -1)) {
                throw "文字列ではありません。終了します。";
        }
        //値を取得
        char *index1_string = (char*)lua_tostring(L, -1);
        lua_pop(L, 1);  // 取得した値をスタックから消す
        printf("index1_string : %s\n", index1_string);

        //index2の値を取得します
        lua_getfield(L, -1, "index2");
        //文字列型であるか確認をします
        if(!lua_isstring(L, -1)) {
                throw "文字列ではありません。終了します。";
        }
        //値を取得
        char *index2_string = (char*)lua_tostring(L, -1);
        lua_pop(L, 1);  // 取得した値をスタックから消す
        printf("index2_string : %s\n", index2_string);
}

■出力結果

index1_string : あーうー
index2_string : うー☆
処理が正常に完了しました。

sample5 luaの関数をCから呼び出す

C側から、luaの関数を呼び出す方法です。引数の受け渡しも、もちろん可能です。

■ソース(C/C++)

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "lua.hpp"

#define LUAFILE_DIRECTORY       "E:/usr/my/vc/LuaStudy/Sample1/Sample1/"

extern int stackDump(lua_State *L);


/**
 * luaにて定義されている関数をC言語側から呼び出して結果を取得する
 * 関数呼び出しに使用しているlua_pcallの詳細はリファレンスマニュアル参照。
 * lua_pcall:
 *              第1引数:lua_State*を指定
 *              第2引数:関数の引数の数を指定
 *              第3引数:luaの関数の戻り値の数を指定
 *              第4引数:エラー処理関数を指定(スタックの中の何番目にあるかを指定)。
 *                               0が指定された場合は、エラー処理関数無しとなる。
 *                               この場合、オリジナルのエラーメッセージが最終的なエラーメッセージとなる。
 */
int sample5() {
        lua_State *L = luaL_newstate();
        luaL_openlibs(L);

        try {
                //.luaファイルを実行する
                char file_path[256];
                strcpy_s(file_path, LUAFILE_DIRECTORY);
                strcat_s(file_path, "luascript5.lua");
                printf(".lua file_path : %s\n", file_path);
                if(luaL_loadfile(L, file_path) || lua_pcall(L, 0, 0, 0) ) {
                        //エラー
                        throw lua_tostring(L, -1);
                }
        
                //luaに書いてあるluaNiKaitearuKannsuudesu1を取得
                lua_getglobal(L, "luaNiKaitearuKannsuudesu1");
                lua_pushnumber(L, 100.0);       //第一引数
                lua_pushnumber(L, 200.0);       //第二引数
                //luaNiKaitearuKannsuudesu1関数を呼び出し結果を取得する。
                if(lua_pcall(L, 2, 1, 0) != 0) {
                        throw "関数呼び出しでエラーが起きました";
                }
                //戻り値を取得する
                if(!lua_isnumber(L, -1)) {
                        throw "戻り値が数値ではありません";
                }
                double result1 = lua_tonumber(L, -1);
                lua_pop(L, 1);  //取得した値を消しておく
                printf("luaNiKaitearuKannsuudesu1 の結果 : %f\n", result1);


                //luaに書いてあるluaNiKaitearuKannsuudesu2を取得
                lua_getglobal(L, "luaNiKaitearuKannsuudesu2");
                //luaNiKaitearuKannsuudesu1関数を呼び出す。
                if(lua_pcall(L, 0, 0, 0) != 0) {
                        throw "関数呼び出しでエラーが起きました";
                }

        } catch(char *msg) {
                lua_close(L);
                printf("error : %s\n", msg);
                printf("エラーが起きたので終了します。");
                exit(0);
        }

        lua_close(L);
        printf("処理が正常に完了しました。\n");
        return 0;
}

■ソース(luascript5.lua)

-- 引数を足し算して返す関数
function luaNiKaitearuKannsuudesu1(x, y) 
        return x+y;
end


-- 戻り値無しの関数
function luaNiKaitearuKannsuudesu2() 
        print("luaの関数内からメッセージを出力しています : ひぎぃーーーーー");
end

■出力結果

.lua file_path : E:/usr/my/vc/LuaStudy/Sample1/Sample1/luascript5.lua
luaNiKaitearuKannsuudesu1 の結果 : 300.000000
luaの関数内からメッセージを出力しています : ひぎぃーーーーー
処理が正常に完了しました。

sample6 luaの配列のデータをCから取得する

Sample2ではグローバル変数, Sample4ではテーブルの名前がついている値を扱いましたが、今度は、配列のデータ取得方法です。

■ソース(C/C++)

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "lua.hpp"

#define LUAFILE_DIRECTORY       "E:/usr/my/vc/LuaStudy/Sample1/Sample1/"

extern int stackDump(lua_State *L);


/**
 * luaの配列のデータをCから取得する
 * (今までは名前のついたものしか取得してませんでしたので
 *  単純な配列のデータを取得するサンプルを書いてみました)
 */
int sample6() {
        lua_State *L = luaL_newstate();
        luaL_openlibs(L);

        try {
                //.luaファイルを実行する
                char file_path[256];
                strcpy_s(file_path, LUAFILE_DIRECTORY);
                strcat_s(file_path, "luascript6.lua");
                printf(".lua file_path : %s\n", file_path);
                if(luaL_loadfile(L, file_path) || lua_pcall(L, 0, 0, 0) ) {
                        //エラー
                        throw lua_tostring(L, -1);
                }

                //luaで、mapというテーブルの値を取得します
                lua_getglobal(L, "array");
                //テーブル型であるかのチェック
                luaL_checktype(L, 1, LUA_TTABLE);
                
                //テーブルの長さを取得する
                int len = lua_objlen(L, 1);

                for(int i=1; i<=len; i++) {
                        lua_rawgeti(L, 1, i);   //array[i]の値をスタックにいれる
                        //文字列型であるか確認をします
                        if(!lua_isstring(L, -1)) {
                                throw "文字列ではありません。終了します。";
                        }
                        //array[i]の値を取得
                        const char *msg = lua_tostring(L, -1);
                        printf("index:%d  value:%s\n", i, msg);
                        lua_pop(L, 1);  //取得した値をスタックから消しておく
                }

                lua_pop(L, 1);  //テーブルの値をスタックから消す
        } catch(char *msg) {
                lua_close(L);
                printf("error : %s", msg);
                printf("エラーが起きたので終了します。\n");
                exit(0);        
        }

        lua_close(L);
        printf("処理が正常に完了しました。\n");
        return 0;
}

■ソース(luascript6.lua)

array = {
        "日暮里",
        "西日暮里",
        "新日暮里"
};

■出力結果

.lua file_path : E:/usr/my/vc/LuaStudy/Sample1/Sample1/luascript6.lua
index:1  value:日暮里
index:2  value:西日暮里
index:3  value:新日暮里
処理が正常に完了しました。

sample7 レジストリの使い方その1

レジストリの使い方についてです。
レジストリとは、Cコードからのみアクセス可能なグローバルテーブルのことです。複数のCモジュール間で値を共有する場合の、格納先として使用するのが一般的な使用方法です。

■ソース(C/C++)

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "lua.hpp"

#define LUAFILE_DIRECTORY       "E:/usr/my/vc/LuaStudy/Sample1/Sample1/"

extern int stackDump(lua_State *L);


/**
 * Lua側から呼び出すことのできる関数その1
 * レジストリへデータを設定します
 */
static int l_func1(lua_State *L) {

        //レジストリにデータを設定する(indexの指定にLUA_REGISTRYINDEXを指定するとレジストリに
        //データをセットすることとなる)

        //1個目のデータをセット(key="index1" value="いざいかんっ")
        lua_pushstring(L, "いざいかんっ");
        lua_setfield(L, LUA_REGISTRYINDEX, "index1");
        //2個目のデータをセット(key="index2" value="やむおえないっ")
        lua_pushstring(L, "やむおえないっ");
        lua_setfield(L, LUA_REGISTRYINDEX, "index2");
        //3個目のデータをセット(key="index3" value="救いは無いんですかっ!")
        lua_pushstring(L, "救いは無いんですかっ!");
        lua_setfield(L, LUA_REGISTRYINDEX, "index3");

        printf("l_func1:レジストリへ3個のデータを設定しました\n");

        return 0;
}

/**
 * Lua側から呼び出すことのできる関数その2
 * レジストリからデータを取得します。
 */
static int l_func2(lua_State *L) {

        printf("l_func2:レジストリからデータを取得します\n");
        char *msg = NULL;

        //index1の値をレジストリから取得
        lua_getfield(L, LUA_REGISTRYINDEX, "index1");
        msg = (char*)lua_tostring(L, -1);
        printf("index1 : %s\n", msg);
        //index2の値をレジストリから取得
        lua_getfield(L, LUA_REGISTRYINDEX, "index2");
        msg = (char*)lua_tostring(L, -1);
        printf("index2 : %s\n", msg);
        //index3の値をレジストリから取得
        lua_getfield(L, LUA_REGISTRYINDEX, "index3");
        msg = (char*)lua_tostring(L, -1);
        printf("index3 : %s\n", msg);

        return 0;
}

static  const struct luaL_Reg lua_funcArray[] = {
        { "func1", l_func1 },
        { "func2", l_func2 },
        { NULL, NULL },
};

/**
 * レジストリの使い方
 * レジストリはCの関数間でデータを共有するための手段です。
 * (データ共有として提供されている機能は、レジストリ、環境、上位値の3つがあります。
 *  そのうちのレジストリを今回は扱います。)
 * ソース上から、luaにC言語の関数を登録する方法のサンプルでもあります。
 *
 * レジストリは、Luaのテーブルです(それ以外にする方法があるかどうかは不明)。
 * レジストリはすべてのCモジュール間で共有されます。
 * レジストリはCコードのみからアクセス可能です。
 * レジストリで使うキー名は数値をキーにするのはNGです。参照で使う用に予約されているためです。
 * 
 */
int sample7() {
        lua_State *L = luaL_newstate();
        luaL_openlibs(L);

        try {
                //alctailなんて名前をつけてみた
                luaL_register(L, "alctail", lua_funcArray);

                //.luaファイルを実行する
                char file_path[256];
                strcpy_s(file_path, LUAFILE_DIRECTORY);
                strcat_s(file_path, "luascript7.lua");
                printf(".lua file_path : %s\n", file_path);
                if(luaL_loadfile(L, file_path) || lua_pcall(L, 0, 0, 0)) {
                        //エラー
                        throw lua_tostring(L, -1);
                }

        } catch(char *msg) {
                lua_close(L);
                printf("error : %s", msg);
                printf("エラーが起きたので終了します。\n");
                exit(0);
        }

        lua_close(L);
        printf("処理が正常に完了しました。\n");
        return 0;
}


■ソース(luascript7.lua)

alctail.func1();
alctail.func2();

■出力結果

.lua file_path : E:/usr/my/vc/LuaStudy/Sample1/Sample1/luascript7.lua
l_func1:レジストリへ3個のデータを設定しました
l_func2:レジストリからデータを取得します
index1 : いざいかんっ
index2 : やむおえないっ
index3 : 救いは無いんですかっ!
処理が正常に完了しました。

sample8 レジストリの使い方その2

LuaのオブジェクトをCモジュール側で独自に格納したい場合どうするかについてのサンプルです

■ソース(C/C++)

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "lua.hpp"

#define LUAFILE_DIRECTORY       "E:/usr/my/vc/LuaStudy/Sample1/Sample1/"

extern int stackDump(lua_State *L);

static int ref_key;

/**
 * Lua側から呼び出すことのできる関数その1
 * レジストリへデータを設定します
 */
static int l_func1(lua_State *L) {
        
        //luaから渡ってきたテーブルの、参照キーを取得する。
        ref_key = luaL_ref(L,  LUA_REGISTRYINDEX);

        return 0;
}

/**
 * Lua側から呼び出すことのできる関数その2
 * レジストリからデータを取得します。
 */
static int l_func2(lua_State *L) {

        //レジストリから値を取得(参照キーを指定し、l_func1で設定したデータを取得)
        lua_rawgeti(L, LUA_REGISTRYINDEX, ref_key);

        //テーブルから値の取得
        lua_pushstring(L, "msg1");
        lua_gettable(L, 1);
        const char *msg1 = lua_tostring(L, -1);
        printf("msg1 : %s\n", msg1);

        lua_pushstring(L, "msg2");
        lua_gettable(L, 1);
        const char *msg2 = lua_tostring(L, -1);
        printf("msg2 : %s\n", msg2);
        
        //不要になった場合は、この関数を呼び解放する
        luaL_unref(L, LUA_REGISTRYINDEX, ref_key); 

        return 0;
}

static  const struct luaL_Reg lua_funcArray[] = {
        { "func1", l_func1 },
        { "func2", l_func2 },
        { NULL, NULL },
};

/**
 * レジストリの使い方 その2
 * LuaのオブジェクトをCモジュール側で独自に格納したい場合どうするかについてのサンプルです。
 * 以下の問題点があり、この問題点への対処手段です。
 *
 * Luaの文字列へのポインタを受け取り、それをCのモジュール内で共有することはできません。
 * Lua側の、関数やテーブルのポインタはCモジュール側から受け取れません
 *(関数へのポインタ、テーブルへのポインタが提供されることがない)。
 */
int sample8() {
        lua_State *L = luaL_newstate();
        luaL_openlibs(L);

        try {
                //alctailなんて名前をつけてみた
                luaL_register(L, "alctail", lua_funcArray);

                //.luaファイルを実行する
                char file_path[256];
                strcpy_s(file_path, LUAFILE_DIRECTORY);
                strcat_s(file_path, "luascript8.lua");
                printf(".lua file_path : %s\n", file_path);
                if(luaL_loadfile(L, file_path) || lua_pcall(L, 0, 0, 0)) {
                        //エラー
                        throw lua_tostring(L, -1);
                }

        } catch(char *msg) {
                lua_close(L);
                printf("error : %s", msg);
                printf("エラーが起きたので終了します。\n");
                exit(0);
        }

        lua_close(L);
        printf("処理が正常に完了しました。\n");
        return 0;
}

■ソース(luascript8.lua)

arr = {
msg1 = "どうしようかな?",
msg2 = "ナイスでぇ〜す"
};
alctail.func1(arr);
alctail.func2();

■出力結果

.lua file_path : E:/usr/my/vc/LuaStudy/Sample1/Sample1/luascript8.lua
msg1 : どうしようかな?
msg2 : ナイスでぇ〜す
処理が正常に完了しました。

sample9 環境の使い方

レジストリと同じように共有データを格納する手段です。
Cモジュール単体で共有するデータを、格納するのに使用されます。

■ソース(C/C++)

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "lua.hpp"

#define LUAFILE_DIRECTORY       "E:/usr/my/vc/LuaStudy/Sample1/Sample1/"

extern int stackDump(lua_State *L);
static int counter=0;

extern int l_func1(lua_State *L);
extern int l_func2(lua_State *L);
extern int l_register(lua_State *L);

static  const struct luaL_Reg lua_funcArray[] = {
        { "register", l_register },
        { "func1", l_func1 },
        { "func2", l_func2 },
        { NULL, NULL },
};

/**
 * Lua側から呼び出すことのできる関数その1
 * 初期化
 */
static int l_register(lua_State *L) {
        printf("l_funcRegisterを実行...\n");

        lua_newtable(L);
        lua_replace(L, LUA_ENVIRONINDEX);
        //モジュールの登録を行います(このモジュールには上で作成した独自の"環境"が使われることとなります。
        luaL_register(L, "alctail1", lua_funcArray);

        lua_newtable(L);
        lua_replace(L, LUA_ENVIRONINDEX);
        //モジュールの登録を行います(このモジュールには上で作成した独自の"環境"が使われることとなります。
        luaL_register(L, "alctail2", lua_funcArray);

        return 0;
}

/**
 * Lua側から呼び出すことのできる関数その2
 * データを環境に設定
 */
static int l_func1(lua_State *L) {
        printf("l_func1を実行...\n");
        
        //引数を受け取る
        const char *msg = luaL_checkstring(L, 1);

        //受け取った値を環境に設定(key="index1")
        lua_pushstring(L, msg);
        lua_setfield(L, LUA_ENVIRONINDEX, "index1");

        printf("設定した文字列:%s\n", msg);
        return 0;
}

/**
 * Lua側から呼び出すことのできる関数その3
 * 環境からデータを取得
 */
static int l_func2(lua_State *L) {
        printf("l_func2を実行...\n");
        //環境からデータを取得する(key="index1")
        lua_getfield(L, LUA_ENVIRONINDEX, "index1");
        char *msg = (char*)luaL_checkstring(L, -1);

        printf("取得した文字列:%s\n", msg);
        return 0;
}


/**
 * 環境の使い方
 * レジストリと同じように共有データを格納する手段です。
 * ただし、レジストリは、各Cモジュールとのデータ共有用
 * 環境は、そのモジュールだけで共有するデータ用・・・と
 * 使い分けされるのが普通の使いたみたいです。
 *
 * 共有するデータはluaからわたってきた値のみの様子(たぶん。
 */
int sample9() {
        lua_State *L = luaL_newstate();
        luaL_openlibs(L);

        try {
                luaL_register(L, "alctail", lua_funcArray);

                //.luaファイルを実行する
                char file_path[256];
                strcpy_s(file_path, LUAFILE_DIRECTORY);
                strcat_s(file_path, "luascript9.lua");
                printf(".lua file_path : %s\n", file_path);
                if(luaL_loadfile(L, file_path) || lua_pcall(L, 0, 0, 0)) {
                        //エラー
                        throw lua_tostring(L, -1);
                }

        } catch(char *msg) {
                lua_close(L);
                printf("error : %s", msg);
                printf("エラーが起きたので終了します。\n");
                exit(0);
        }

        lua_close(L);
        printf("処理が正常に完了しました。\n");
        return 0;
}


■ソース(luascript9.lua)

--
-- レジストリ・環境・上位値に格納するのは、luaからわたってきたデータです。
-- Cモジュールが生成したデータを格納するわけではないっぽい(?)
--
print("alctailを実行します");
alctail.register();

print("alctail1を実行します");
alctail1.func1("alctail1:うー☆");
alctail1.func2();
print("alctail2を実行します");
alctail2.func1("alctail2:あーうー");
alctail2.func2();

■出力結果

.lua file_path : E:/usr/my/vc/LuaStudy/Sample1/Sample1/luascript9.lua
alctailを実行します
l_funcRegisterを実行...
alctail1を実行します
l_func1を実行...
設定した文字列:alctail1:うー☆
l_func2を実行...
取得した文字列:alctail1:うー☆
alctail2を実行します
l_func1を実行...
設定した文字列:alctail2:あーうー
l_func2を実行...
取得した文字列:alctail2:あーうー
処理が正常に完了しました。

sample10 上位値の使い方

特定のモジュールだけで、値を共有したい場合は上位値を使うこともできます。
私自身、使いどころがいまいちわかってませんが。。。

■ソース(C/C++)

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "lua.hpp"

#define LUAFILE_DIRECTORY       "E:/usr/my/vc/LuaStudy/Sample1/Sample1/"

extern int stackDump(lua_State *L);
static int counter=0;

extern int setup(lua_State *L);
extern int l_func(lua_State *L);
extern int l_showUpValue(lua_State *L);

static  const struct luaL_Reg lua_funcArray[] = {
        { "setup", setup },
        { NULL, NULL },
};


/**
 * Lua側から呼び出すことのできる関数
 * 
 */
static int setup(lua_State *L) {
        printf("setupを実行...\n");
        
        //クロージャとする値をセットする
        lua_pushstring(L, "うー");        //上位値の一個目(初期値)
        lua_pushstring(L, 0);           //上位値の二個目(初期値)
        //第三引数は上位値の数(ここでは2個指定するので2とした)
        lua_pushcclosure(L, l_func, 2);

        return 1;
}

/**
 * Luaにおいて、func1を呼び出した際の戻り値として返される関数
 */
static int l_func(lua_State *L) {
        printf("l_funcを実行...\n");

        //上位値があるかチェックする
        if( lua_isnone(L, lua_upvalueindex(1)) || lua_isnone(L, lua_upvalueindex(2))) {
                printf("上位値がありません");
                return 0;
        }
        //上位値の取得
        const char *msg1 = lua_tostring(L, lua_upvalueindex(1));        //一個目
        int val = lua_tointeger(L, lua_upvalueindex(2));                        //二個目
        
        //引数の取得
        const char *msg2 = luaL_checkstring(L, -1);

        //引数で指定された文字列を追加する
        luaL_Buffer b;
        luaL_buffinit(L, &b);
        luaL_addstring(&b, msg1);
        luaL_addstring(&b, msg2);
        luaL_pushresult(&b);    //上記の処理(msg1とmsg2の連結)の結果がスタックの一番上にセットされる

        //上位値を変更する
        lua_replace(L, lua_upvalueindex(1));    //上位値一番目の値をスタックの一番上の値に変更する
        
        lua_pushinteger(L, ++val);
        lua_replace(L, lua_upvalueindex(2));    //上位値二番目の値をスタックの一番上の値に変更する


        //上位値を取得して表示する
        msg1 = lua_tostring(L, lua_upvalueindex(1));    //一個目
        val = lua_tointeger(L, lua_upvalueindex(2));    //二個目
        printf("上位値[1] : %s\n", msg1);
        printf("上位値[2] : %d\n", val);

        return 0;
}



/**
 * 上位値の使い方
 */
int sample10() {
        lua_State *L = luaL_newstate();
        luaL_openlibs(L);

        try {
                //関数をluaへ登録
                luaL_register(L, "alctail", lua_funcArray);

                //.luaファイルを実行する
                char file_path[256];
                strcpy_s(file_path, LUAFILE_DIRECTORY);
                strcat_s(file_path, "luascript10.lua");
                printf(".lua file_path : %s\n", file_path);
                if(luaL_loadfile(L, file_path) || lua_pcall(L, 0, 0, 0)) {
                        //エラー
                        throw lua_tostring(L, -1);
                }

        } catch(char *msg) {
                lua_close(L);
                printf("error : %s", msg);
                printf("エラーが起きたので終了します。\n");
                exit(0);
        }

        lua_close(L);
        printf("処理が正常に完了しました。\n");
        return 0;
}

■ソース(luascript10.lua)

-- 一個目(呼び出す関数は同じだが、上位値に違いが生まれる)
x1 = alctail.setup()
x1("うー")
x1("☆")


-- 二個目(呼び出す関数は同じだが、上位値に違いが生まれる)
x2 = alctail.setup()
x2("☆")
x2("うー")
x2("☆")

■出力結果

.lua file_path : E:/usr/my/vc/LuaStudy/Sample1/Sample1/luascript10.lua
setupを実行...
l_funcを実行...
上位値[1] : うーうー
上位値[2] : 1
l_funcを実行...
上位値[1] : うーうー☆
上位値[2] : 2
setupを実行...
l_funcを実行...
上位値[1] : うー☆
上位値[2] : 1
l_funcを実行...
上位値[1] : うー☆うー
上位値[2] : 2
l_funcを実行...
上位値[1] : うー☆うー☆
上位値[2] : 3
処理が正常に完了しました。

sample11 ユーザ定義型について

lua上では、謎のオブジェクトですが、C関数上で扱えるデータのポインタです(たぶん。
具体例としては構造体などです。

■ソース(C/C++)

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "lua.hpp"

#define LUAFILE_DIRECTORY       "E:/usr/my/vc/LuaStudy/Sample1/Sample1/"

extern int stackDump(lua_State *L);
static int counter=0;

extern int l_func1(lua_State *L);
extern int l_func2(lua_State *L);

static  const struct luaL_Reg lua_funcArray[] = {
        { "func1", l_func1 },
        { "func2", l_func2 },
        { NULL, NULL },
};


struct Data {
        char *msg;
        int val;
};


/**
 * データを生成して、そのデータを戻り値とする関数
 * 
 */
static int l_func1(lua_State *L) {
        printf("func1を実行...\n");

        //メモリの確保
        //lua_newuserdataを使う限り、こちらでメモリを開放する必要無し。luaのGC対象となる。
        struct Data *d = (struct Data*)lua_newuserdata(L, sizeof(struct Data)*5);
        for(int i=0; i<5; i++) {
                d[i].msg = (char*)lua_newuserdata(L, 64);
                sprintf_s(d[i].msg, 64, "うー☆ : %d", i);
                d[i].val = i*100+100;   //適当に値をいれておく

                //lua_newuserdataを呼び出すと、作成した値がスタックのトップに設定される。
                //戻り値としてはsruct Dataを返したいので、forループの中でスタックに
                //いれたものをpopしておく。
                lua_pop(L, 1);
        }

        //struct Dataのポインタがスタックの1番目にきているのでそれを返す。
        return 1;
}

/**
 * データ(ユーザ定義型)をうけとり、そのデータの内容を表示する関数
 */
static int l_func2(lua_State *L) {
        printf("func2を実行...\n");

        //引数が渡されていることを確認する
        luaL_checkany(L, 1);
        //引数を取得
        struct Data *d = (struct Data*)lua_touserdata(L, 1);
        //渡された値がNULLじゃないことの確認
        luaL_argcheck(L, d != NULL, 1, "一番目の引数がNULLですよ");
        
        
        //渡ってきた値を表示する
        for(int i=0; i<5; i++) {
                printf("index:%d\t", i);
                printf("%s / %d\n", d[i].msg, d[i].val);
        }

        return 0;
}



/**
 * ユーザ定義型について
 * Cの関数にて生成したデータをLua側に反して、
 * Lua側からそのデータ(ユーザ定義型)を、引数に指定し関数を呼び出し
 * Cの関数内で、再びそのデータを受け取る・・・という処理を行います。
 */
int sample11() {
        lua_State *L = luaL_newstate();
        luaL_openlibs(L);

        try {
                //関数をluaへ登録
                luaL_register(L, "alctail", lua_funcArray);

                //.luaファイルを実行する
                char file_path[256];
                strcpy_s(file_path, LUAFILE_DIRECTORY);
                strcat_s(file_path, "luascript11.lua");
                printf(".lua file_path : %s\n", file_path);
                if(luaL_loadfile(L, file_path) || lua_pcall(L, 0, 0, 0)) {
                        //エラー
                        throw lua_tostring(L, -1);
                }

        } catch(char *msg) {
                lua_close(L);
                printf("error : %s", msg);
                printf("エラーが起きたので終了します。\n");
                exit(0);
        }

        lua_close(L);
        printf("処理が正常に完了しました。\n");
        return 0;
}


■ソース(luascript.lua)

-- ユーザ定義型の戻り値を受け取る
obj = alctail.func1();
-- 受け取ったデータを引数に関数呼び出し
alctail.func2(obj);

■出力結果

.lua file_path : E:/usr/my/vc/LuaStudy/Sample1/Sample1/luascript11.lua
func1を実行...
func2を実行...
index:0 うー☆ : 0 / 100
index:1 うー☆ : 1 / 200
index:2 うー☆ : 2 / 300
index:3 うー☆ : 3 / 400
index:4 うー☆ : 4 / 500
処理が正常に完了しました。

sample12 ユーザ定義型に対し、メタテーブルを設定する方法

ユーザ定義型に対しメタテーブルを設定することによる、どのようなデータか識別できるようになります。

■ソース(C/C++)

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "lua.hpp"

#define LUAFILE_DIRECTORY       "E:/usr/my/vc/LuaStudy/Sample1/Sample1/"

extern int stackDump(lua_State *L);
static int counter=0;

extern int l_func1(lua_State *L);
extern int l_func2(lua_State *L);

static  const struct luaL_Reg lua_funcArray[] = {
        { "func1", l_func1 },
        { "func2", l_func2 },
        { NULL, NULL },
};


struct Data {
        char *msg;
        int val;
};


/**
 * データを生成して、そのデータを戻り値とする関数
 * 
 */
static int l_func1(lua_State *L) {
        printf("func1を実行...\n");

        //メモリの確保
        //lua_newuserdataを使う限り、こちらでメモリを開放する必要無し。luaのGC対象となる。
        struct Data *d = (struct Data*)lua_newuserdata(L, sizeof(struct Data)*5);
        for(int i=0; i<5; i++) {
                d[i].msg = (char*)lua_newuserdata(L, 64);
                sprintf_s(d[i].msg, 64, "うー☆ : %d", i);
                d[i].val = i*100+100;   //適当に値をいれておく

                //lua_newuserdataを呼び出すと、作成した値がスタックのトップに設定される。
                //戻り値としてはsruct Dataを返したいので、forループの中でスタックに
                //いれたものをpopしておく。
                lua_pop(L, 1);
        }

        //レジストリからメタテーブルを取得する(スタックの一番上にセットされる)
        luaL_getmetatable(L, "uhawwwwww");
        //今作成したユーザデータへセットする
        lua_setmetatable(L, -2);
        //struct Dataのポインタがスタックの1番目にきているのでそれを返す。
        return 1;
}

/**
 * データ(ユーザ定義型)をうけとり、そのデータの内容を表示する関数
 */
static int l_func2(lua_State *L) {
        printf("func2を実行...\n");

        //引数が渡されていることを確認する
        luaL_checkany(L, 1);
        //引数を取得
        //struct Data *d = (struct Data*)lua_touserdata(L, 1);
        //↑のようなやり方で取得可能であるが、指定したメタテーブルが設定されている
        // ユーザデータであるかのチェックがしたいなら、以下を使う
        //※指定したメタテーブルがセットされていないとabortします
        struct Data *d = (struct Data*)luaL_checkudata(L, 1, "uhawwwwww");
        //渡された値がNULLじゃないことの確認
        luaL_argcheck(L, d != NULL, 1, "一番目の引数がNULLですよ");
        
        //渡ってきた値を表示する
        for(int i=0; i<5; i++) {
                printf("index:%d\t", i);
                printf("%s / %d\n", d[i].msg, d[i].val);
        }

        return 0;
}



/**
 * ユーザ定義型に対し、メタテーブルを設定する。
 * そして、データを取得する際も、指定のメタテーブルが設定されているユーザデータ
 * であるかの確認を行う。
 * 具体的な用途としては、ユーザデータが想定通りのものかどうかのチェックに使える。
 */
int sample12() {
        lua_State *L = luaL_newstate();
        luaL_openlibs(L);

        try {
                //レジストリへメタテーブルを登録する
                luaL_newmetatable(L, "uhawwwwww");      //指定する名前はほかと重複しなければ何でもよい
                //関数をluaへ登録
                luaL_register(L, "alctail", lua_funcArray);

                //.luaファイルを実行する
                char file_path[256];
                strcpy_s(file_path, LUAFILE_DIRECTORY);
                strcat_s(file_path, "luascript12.lua");
                printf(".lua file_path : %s\n", file_path);
                if(luaL_loadfile(L, file_path) || lua_pcall(L, 0, 0, 0)) {
                        //エラー
                        throw lua_tostring(L, -1);
                }

        } catch(char *msg) {
                lua_close(L);
                printf("error : %s", msg);
                printf("エラーが起きたので終了します。\n");
                exit(0);
        }

        lua_close(L);
        printf("処理が正常に完了しました。\n");
        return 0;
}


■ソース(luascript12.lua)

-- ユーザ定義型の戻り値を受け取る
obj = alctail.func1();
-- 受け取ったデータを引数に関数呼び出し
alctail.func2(obj);

■出力結果

.lua file_path : E:/usr/my/vc/LuaStudy/Sample1/Sample1/luascript12.lua
func1を実行...
func2を実行...
index:0 うー☆ : 0 / 100
index:1 うー☆ : 1 / 200
index:2 うー☆ : 2 / 300
index:3 うー☆ : 3 / 400
index:4 うー☆ : 4 / 500
処理が正常に完了しました。

sample13 luaのソースをオブジェクト指向風に記述する方法

luaのスクリプトが、メタテーブルを駆使ることにより、オブジェクト指向風な記述が可能となります。

■ソース(C/C++)

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "lua.hpp"

#define LUAFILE_DIRECTORY       "E:/usr/my/vc/LuaStudy/Sample1/Sample1/"

extern int stackDump(lua_State *L);
static int counter=0;

extern int l_func1(lua_State *L);
extern int l_func2(lua_State *L);
extern int l_getMsg(lua_State *L);
extern int l_getVal(lua_State *L);


static  const struct luaL_Reg lua_funcArray[] = {
        { "func1", l_func1 },
        { "func2", l_func2 },
        { NULL, NULL },
};

static  const struct luaL_Reg lua_funcArray_m[] = {
        { "getMsg", l_getMsg },
        { "getVal", l_getVal },
        { NULL, NULL },
};


struct Data {
        char *msg;
        int val;
};


/**
 * データを生成して、そのデータを戻り値とする関数
 * 
 */
static int l_func1(lua_State *L) {
        printf("func1を実行...\n");

        //メモリの確保
        //lua_newuserdataを使う限り、こちらでメモリを開放する必要無し。luaのGC対象となる。
        struct Data *d = (struct Data*)lua_newuserdata(L, sizeof(struct Data)*5);
        for(int i=0; i<5; i++) {
                d[i].msg = (char*)lua_newuserdata(L, 64);
                sprintf_s(d[i].msg, 64, "うー☆ : %d", i);
                d[i].val = i*100+100;   //適当に値をいれておく

                //lua_newuserdataを呼び出すと、作成した値がスタックのトップに設定される。
                //戻り値としてはsruct Dataを返したいので、forループの中でスタックに
                //いれたものをpopしておく。
                lua_pop(L, 1);
        }

        //レジストリからメタテーブルを取得する(スタックの一番上にセットされる)
        luaL_getmetatable(L, "uhawwwwww");
        //今作成したユーザデータへセットする
        lua_setmetatable(L, -2);
        //struct Dataのポインタがスタックの1番目にきているのでそれを返す。
        return 1;
}

/**
 * データ(ユーザ定義型)をうけとり、そのデータの内容を表示する関数
 */
static int l_func2(lua_State *L) {
        printf("func2を実行...\n");

        //引数が渡されていることを確認する
        luaL_checkany(L, 1);
        //引数を取得
        //struct Data *d = (struct Data*)lua_touserdata(L, 1);
        //↑のようなやり方で取得可能であるが、指定したメタテーブルが設定されている
        // ユーザデータであるかのチェックがしたいなら、以下を使う
        //※指定したメタテーブルがセットされていないとabortします
        struct Data *d = (struct Data*)luaL_checkudata(L, 1, "uhawwwwww");
        //渡された値がNULLじゃないことの確認
        luaL_argcheck(L, d != NULL, 1, "一番目の引数がNULLですよ");
        
        //渡ってきた値を表示する
        for(int i=0; i<5; i++) {
                printf("index:%d\t", i);
                printf("%s / %d\n", d[i].msg, d[i].val);
        }

        return 0;
}

/**
 * struct Dataのmsgを返す
 * 
 */
static int l_getMsg(lua_State *L) {
        printf("l_getMsgを実行中...\n");
        //オブジェクト指向風の書き方をすると、第一引数は自身のオブジェクトが指定される
        struct Data *d = (struct Data*)lua_touserdata(L, 1);
        //第二引数に何番目のデータを返すか指定される
        int pos = lua_tointeger(L, 2);
        
        //戻り値をスタックにpushする。
        lua_pushstring(L, d[pos].msg);
        return 1;
}

/**
 * struct Dataのvalを返す
 * 
 */
static int l_getVal(lua_State *L) {
        printf("l_getMsgを実行中...\n");
        //オブジェクト指向風の書き方をすると、第一引数は自身のオブジェクトが指定される
        struct Data *d = (struct Data*)lua_touserdata(L, 1);
        //第二引数に何番目のデータを返すか指定される
        int pos = lua_tointeger(L, 2);
        
        //戻り値をスタックにpushする。
        lua_pushinteger(L, d[pos].val);
        return 1;
}



/**
 * luaのソースをオブジェクト指向風に記述できるようにします。
 * luaL_register関数の第二引数は、関数を収める関数名を指定しますが、今回はNULLを指定します。
 * NULLが指定されると、スタックのトップにあるテーブルに、指定された関数を登録します。
 * ここで、スタックのトップにメタテーブルを取得した状態でluaL_register関数を呼び出すようにします。
 * metatable.__index = metatableというようになるようにソースを記述することで、
 * オブジェクト指向風の記述を可能とさせます(ソースを見るとよくわかるかと。。。)
 */
int sample13() {
        lua_State *L = luaL_newstate();
        luaL_openlibs(L);

        try {
                //レジストリへメタテーブルを登録する
                luaL_newmetatable(L, "uhawwwwww");      //指定する名前はほかと重複しなければ何でもよい

                //metatable.__index = metatable となるようにする
                lua_pushvalue(L, -1);   //上で作成したメタテーブルを複製
                lua_setfield(L, -2, "__index");
                
                //オブジェクト指向風にアクセスさせる関数たちを登録
                luaL_register(L, NULL, lua_funcArray_m);


                //関数をluaへ登録
                luaL_register(L, "alctail", lua_funcArray);

                //.luaファイルを実行する
                char file_path[256];
                strcpy_s(file_path, LUAFILE_DIRECTORY);
                strcat_s(file_path, "luascript13.lua");
                printf(".lua file_path : %s\n", file_path);
                if(luaL_loadfile(L, file_path) || lua_pcall(L, 0, 0, 0)) {
                        //エラー
                        throw lua_tostring(L, -1);
                }

        } catch(char *msg) {
                lua_close(L);
                printf("error : %s", msg);
                printf("エラーが起きたので終了します。\n");
                exit(0);
        }

        lua_close(L);
        printf("処理が正常に完了しました。\n");
        return 0;
}

■ソース(luascript13.lua)

-- ユーザ定義型の戻り値を受け取る
obj = alctail.func1()
-- 受け取ったデータを引数に関数呼び出し
alctail.func2(obj)

-- オブジェクト指向風アクセス
-- obj.getMsg(obj, 0)と、obj:getMsg(0)は等価です
print("\nオブジェクト指向風アクセス")
for i=1,5 do
        msg = obj:getMsg(i-1)
        val = obj:getVal(i-1)
        print("msg["..i.."] : "..msg)
        print("msg["..i.."] : "..val)
end

■出力結果

.lua file_path : E:/usr/my/vc/LuaStudy/Sample1/Sample1/luascript13.lua
func1を実行...
func2を実行...
index:0 うー☆ : 0 / 100
index:1 うー☆ : 1 / 200
index:2 うー☆ : 2 / 300
index:3 うー☆ : 3 / 400
index:4 うー☆ : 4 / 500

オブジェクト指向風アクセス
l_getMsgを実行中...
l_getMsgを実行中...
msg[1] : うー☆ : 0
msg[1] : 100
l_getMsgを実行中...
l_getMsgを実行中...
msg[2] : うー☆ : 1
msg[2] : 200
l_getMsgを実行中...
l_getMsgを実行中...
msg[3] : うー☆ : 2
msg[3] : 300
l_getMsgを実行中...
l_getMsgを実行中...
msg[4] : うー☆ : 3
msg[4] : 400
l_getMsgを実行中...
l_getMsgを実行中...
msg[5] : うー☆ : 4
msg[5] : 500
処理が正常に完了しました。

sample14 luaのオブジェクトを配列風に記述できるようにする方法

使いどころは微妙な気がしますが。。。
こういうメタテーブルもあるんだよ・・・というサンプルです。

■ソース(C/C++)

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "lua.hpp"

#define LUAFILE_DIRECTORY       "E:/usr/my/vc/LuaStudy/Sample1/Sample1/"

extern int stackDump(lua_State *L);
static int counter=0;

extern int l_func1(lua_State *L);
extern int l_func2(lua_State *L);
extern int l_getMsg(lua_State *L);
extern int l_getVal(lua_State *L);
extern int l_setarray(lua_State *L);
extern int l_getarray(lua_State *L);
extern int l_getsize(lua_State *L);
extern int l_tostring(lua_State *L);


static  const struct luaL_Reg lua_funcArray[] = {
        { "func1", l_func1 },
        { "func2", l_func2 },
        { NULL, NULL },
};

static  const struct luaL_Reg lua_funcArray_m[] = {
        { "__newindex", l_setarray },   //a[100]など、新しい要素へのアクセスが生まれた場合呼ばれる
        { "__index", l_getarray },              //要素へのアクセスがあった場合呼ばれる
        { "__len", l_getsize },                 //#aなど、サイズを取得する際呼ばれる
        { "__tostring", l_tostring },   //print(a)としたとき表示する文字列を返す
        { NULL, NULL },
};


struct Data {
        char *msg;
        int val;
};


/**
 * データを生成して、そのデータを戻り値とする関数
 */
static int l_func1(lua_State *L) {
        printf("func1を実行...\n");

        //メモリの確保
        //lua_newuserdataを使う限り、こちらでメモリを開放する必要無し。luaのGC対象となる。
        struct Data *d = (struct Data*)lua_newuserdata(L, sizeof(struct Data)*5);
        for(int i=0; i<5; i++) {
                d[i].msg = (char*)lua_newuserdata(L, 64);
                sprintf_s(d[i].msg, 64, "うー☆ : %d", i);
                d[i].val = i*100+100;   //適当に値をいれておく

                //lua_newuserdataを呼び出すと、作成した値がスタックのトップに設定される。
                //戻り値としてはsruct Dataを返したいので、forループの中でスタックに
                //いれたものをpopしておく。
                lua_pop(L, 1);
        }

        //レジストリからメタテーブルを取得する(スタックの一番上にセットされる)
        luaL_getmetatable(L, "uhawwwwww");
        //今作成したユーザデータへセットする
        lua_setmetatable(L, -2);
        //struct Dataのポインタがスタックの1番目にきているのでそれを返す。
        return 1;
}

/**
 * データ(ユーザ定義型)をうけとり、そのデータの内容を表示する関数
 */
static int l_func2(lua_State *L) {
        printf("func2を実行...\n");

        //引数が渡されていることを確認する
        luaL_checkany(L, 1);
        //引数を取得
        //struct Data *d = (struct Data*)lua_touserdata(L, 1);
        //↑のようなやり方で取得可能であるが、指定したメタテーブルが設定されている
        // ユーザデータであるかのチェックがしたいなら、以下を使う
        //※指定したメタテーブルがセットされていないとabortします
        struct Data *d = (struct Data*)luaL_checkudata(L, 1, "uhawwwwww");
        //渡された値がNULLじゃないことの確認
        luaL_argcheck(L, d != NULL, 1, "一番目の引数がNULLですよ");
        
        //渡ってきた値を表示する
        for(int i=0; i<5; i++) {
                printf("index:%d\t", i);
                printf("%s / %d\n", d[i].msg, d[i].val);
        }

        return 0;
}

/**
 * struct Dataのmsgを返す
 * 
 */
static int l_getMsg(lua_State *L) {
        printf("l_getMsgを実行中...\n");    
        //オブジェクト指向風の書き方をすると、第一引数は自身のオブジェクトが指定される
        struct Data *d = (struct Data*)lua_touserdata(L, 1);
        //第二引数に何番目のデータを返すか指定される
        int pos = lua_tointeger(L, 2);
        
        //戻り値をスタックにpushする。
        lua_pushstring(L, d[pos].msg);
        return 1;
}

/**
 * struct Dataのvalを返す
 * 
 */
static int l_getVal(lua_State *L) {
        printf("l_getMsgを実行中...\n");
        //オブジェクト指向風の書き方をすると、第一引数は自身のオブジェクトが指定される
        struct Data *d = (struct Data*)lua_touserdata(L, 1);
        //第二引数に何番目のデータを返すか指定される
        int pos = lua_tointeger(L, 2);
        
        //戻り値をスタックにpushする。
        lua_pushinteger(L, d[pos].val);
        return 1;
}




/**
 * __newindexのハンドラ
 */
static int l_setarray(lua_State *L) {
        printf("l_setarrayを実行中...\n");
        //第一引数にはユーザ定義型がセットされている
        struct Data *d = (struct Data*)lua_touserdata(L, 1);
        //第二引数に何番目にデータをセットするか指定されている
        int pos = lua_tointeger(L, 2);
        //第三引数に何のデータをセットするかが設定されている
        const char *msg = lua_tostring(L, 3);
        
        //データをセットする
        d[pos].msg = (char*)lua_newuserdata(L, 64);
        sprintf_s(d[pos].msg, 64, "%s : %d", msg, pos);

        return 0;
}

/**
 * __indexのハンドラ
 */
static int l_getarray(lua_State *L) {
        printf("l_getarrayを実行中...\n");

        //第一引数にはユーザ定義型がセットされている
        struct Data *d = (struct Data*)lua_touserdata(L, 1);
        //第二引数に何番目にデータをセットするか指定されている
        int pos = lua_tointeger(L, 2);

        //戻り値を設定する(なんとなくこれを返す。理由はない。サンプルとしててきとーに作った)
        lua_pushstring(L, d[pos].msg);

        return 1;
}

/**
 * __lenのハンドラ
 */
static int l_getsize(lua_State *L) {
        printf("l_getsizeを実行中...\n");
        
        //戻り値を設定
        lua_pushinteger(L, 5);  //固定で5とする
        return 1;
}

/**
 * __tostringのハンドラ
 */
static int l_tostring(lua_State *L) {
        printf("l_tostringを実行中...\n");

        //第一引数にはユーザ定義型がセットされている
        struct Data *d = (struct Data*)lua_touserdata(L, 1);

        //文字列を連結したものを返す。
        luaL_Buffer b;
        luaL_buffinit(L, &b);
        for(int i=0; i<5; i++) {
                luaL_addstring(&b, d[i].msg);
        }
        luaL_pushresult(&b);    //結果をスタックへいれる
        return 1;
}



/**
 * luaのオブジェクトを配列風に記述できるようにします。
 */
int sample14() {
        lua_State *L = luaL_newstate();
        luaL_openlibs(L);

        try {
                //レジストリへメタテーブルを登録する
                luaL_newmetatable(L, "uhawwwwww");      //指定する名前はほかと重複しなければ何でもよい
                
                //オブジェクト指向風にアクセスさせる関数たちを登録
                luaL_register(L, NULL, lua_funcArray_m);


                //関数をluaへ登録
                luaL_register(L, "alctail", lua_funcArray);

                //.luaファイルを実行する
                char file_path[256];
                strcpy_s(file_path, LUAFILE_DIRECTORY);
                strcat_s(file_path, "luascript14.lua");
                printf(".lua file_path : %s\n", file_path);
                if(luaL_loadfile(L, file_path) || lua_pcall(L, 0, 0, 0)) {
                        //エラー
                        throw lua_tostring(L, -1);
                }

        } catch(char *msg) {
                lua_close(L);
                printf("error : %s", msg);
                printf("エラーが起きたので終了します。\n");
                exit(0);
        }

        lua_close(L);
        printf("処理が正常に完了しました。\n");
        return 0;
}

■ソース(luascript14.lua)

-- ユーザ定義型の戻り値を受け取る
obj = alctail.func1()
-- 受け取ったデータを引数に関数呼び出し
alctail.func2(obj)

-- 配列風アクセス
print("配列風アクセス")
print("obj[2] : "..obj[2])      --l_getarray関数が返した値を表示する
obj[2] = "うはww";                --l_setarray関数を呼び出す
print("obj[2] : "..obj[2])      
print("#obj : "..#obj)          --l_getsize関数を呼び出す
print(obj)                                      --l_tostring関数を呼び出す

■出力結果

.lua file_path : E:/usr/my/vc/LuaStudy/Sample1/Sample1/luascript14.lua
func1を実行...
func2を実行...
index:0 うー☆ : 0 / 100
index:1 うー☆ : 1 / 200
index:2 うー☆ : 2 / 300
index:3 うー☆ : 3 / 400
index:4 うー☆ : 4 / 500
配列風アクセス
l_getarrayを実行中...
obj[2] : うー☆ : 2
l_setarrayを実行中...
l_getarrayを実行中...
obj[2] : うはww : 2
l_getsizeを実行中...
#obj : 5
l_tostringを実行中...
うー☆ : 0うー☆ : 1うはww : 2うー☆ : 3うー☆ : 4
処理が正常に完了しました。

sample15 ライトユーザーデータについて

Cのポインタそのものです。lua上ではただの変数としてしか使えません。ライトユーザデータはメモリの管理をするのはC側です。gc対象とはなりません。

■ソース(C/C++)

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "lua.hpp"

#define LUAFILE_DIRECTORY       "E:/usr/my/vc/LuaStudy/Sample1/Sample1/"

extern int stackDump(lua_State *L);
static int counter=0;

extern int l_func1(lua_State *L);
extern int l_func2(lua_State *L);
extern int l_getMsg(lua_State *L);
extern int l_getVal(lua_State *L);
extern int l_setarray(lua_State *L);
extern int l_getarray(lua_State *L);
extern int l_getsize(lua_State *L);
extern int l_tostring(lua_State *L);


static  const struct luaL_Reg lua_funcArray[] = {
        { "func1", l_func1 },
        { "func2", l_func2 },
        { NULL, NULL },
};


/**
 * データを生成して、そのデータを戻り値とする関数
 */
static int l_func1(lua_State *L) {
        printf("func1を実行...\n");

        //メモリの確保
        char *msg = new char[64];
        strcpy_s(msg, 64, "うはwwっうぇwwっうぇwww");

        //ライトユーザデータを戻り値とする
        lua_pushlightuserdata(L, msg);
        return 1;
}

/**
 * データ(ライトユーザデータ)をうけとり、そのデータの内容を表示する関数
 */
static int l_func2(lua_State *L) {
        printf("func2を実行...\n");

        //引数が渡されていることを確認する
        luaL_checkany(L, 1);
        //引数を取得
        char *msg = (char*)lua_touserdata(L, 1);
        //渡された値がNULLじゃないことの確認
        luaL_argcheck(L, msg != NULL, 1, "一番目の引数がNULLですよ");
        
        //渡ってきた値を表示する
        printf("light user data : %s\n", msg);

        //ライトユーザデータはluaのGC対象外なので、自分でメモリを開放する
        delete []msg;
        return 0;
}




/**
 * ライトユーザーデータについて
 *
 * ライトユーザデータとは、luaのGC管理外の、Cの生のデータのことです。
 * ライトユーザデータの対義語はフルユーザデータ(luaのGC管理内のデータ)。
 * ライトユーザデータには、メタテーブルもありません。単純にCのポインタのことです。
 */
int sample15() {
        lua_State *L = luaL_newstate();
        luaL_openlibs(L);

        try {
                //関数をluaへ登録
                luaL_register(L, "alctail", lua_funcArray);

                //.luaファイルを実行する
                char file_path[256];
                strcpy_s(file_path, LUAFILE_DIRECTORY);
                strcat_s(file_path, "luascript15.lua");
                printf(".lua file_path : %s\n", file_path);
                if(luaL_loadfile(L, file_path) || lua_pcall(L, 0, 0, 0)) {
                        //エラー
                        throw lua_tostring(L, -1);
                }

        } catch(char *msg) {
                lua_close(L);
                printf("error : %s", msg);
                printf("エラーが起きたので終了します。\n");
                exit(0);
        }

        lua_close(L);
        printf("処理が正常に完了しました。\n");
        return 0;
}


■ソース(luascript15.lua)

-- ユーザ定義型の戻り値を受け取る
obj = alctail.func1()
-- 受け取ったデータを引数に関数呼び出し
alctail.func2(obj)

■出力結果

.lua file_path : E:/usr/my/vc/LuaStudy/Sample1/Sample1/luascript15.lua
func1を実行...
func2を実行...
light user data : うはwwっうぇwwっうぇwww
処理が正常に完了しました。

sample16 __gcについて

luaのgcが実施され、対象のテーブルがメモリ回収される際に、指定の関数を呼び出すように設定可能です。

■ソース(C/C++)

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "lua.hpp"

#define LUAFILE_DIRECTORY       "E:/usr/my/vc/LuaStudy/Sample1/Sample1/"

extern int stackDump(lua_State *L);
static int counter=0;

extern int l_func1(lua_State *L);


static  const struct luaL_Reg lua_funcArray[] = {
        { "func1", l_func1 },
        { NULL, NULL },
};


/**
 * データを生成して、そのデータを戻り値とする関数
 */
static int l_func1(lua_State *L) {
        printf("func1を実行...\n");

        char *msg = (char*)lua_newuserdata(L, 64);

        //メタテーブルを設定
        luaL_getmetatable(L, "nyannyannyann");
        lua_setmetatable(L, -2);

        return 1;
}


/**
 * メタテーブル("nyannyannyann")の__gcに設定する関数
 */
static int l_gcHandler(lua_State *L)  {
        printf("l_gcHandlerを実行中...\n");
        printf("----------------------------------\n");
        return 0;
}




/**
 * __gcについて
 *
 */
int sample16() {
        lua_State *L = luaL_newstate();
        luaL_openlibs(L);

        try {

                //メタテーブルを設定する
                luaL_newmetatable(L, "nyannyannyann");
                //__gcフィールドを設定する
                lua_pushstring(L, "__gc");
                lua_pushcfunction(L, l_gcHandler);
                lua_settable(L, -3);


                //関数をluaへ登録
                luaL_register(L, "alctail", lua_funcArray);

                //.luaファイルを実行する
                char file_path[256];
                strcpy_s(file_path, LUAFILE_DIRECTORY);
                strcat_s(file_path, "luascript16.lua");
                printf(".lua file_path : %s\n", file_path);
                if(luaL_loadfile(L, file_path) || lua_pcall(L, 0, 0, 0)) {
                        //エラー
                        throw lua_tostring(L, -1);
                }

        } catch(char *msg) {
                lua_close(L);
                printf("error : %s", msg);
                printf("エラーが起きたので終了します。\n");
                exit(0);
        }

        lua_close(L);
        printf("処理が正常に完了しました。\n");
        return 0;
}

■ソース(luascript16.lua)

alctail.func1()

■出力結果

.lua file_path : E:/usr/my/vc/LuaStudy/Sample1/Sample1/luascript16.lua
func1を実行...
l_gcHandlerを実行中...
----------------------------------
処理が正常に完了しました。

sample17 スレッドについて

スレッド作成のサンプルです。

■ソース(C/C++)

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "lua.hpp"

#define LUAFILE_DIRECTORY       "E:/usr/my/vc/LuaStudy/Sample1/Sample1/"

extern int stackDump(lua_State *L);
static int counter=0;

extern int l_func1(lua_State *L);
extern int l_func2(lua_State *L);


static  const struct luaL_Reg lua_funcArray[] = {
        { "func1", l_func1 },
        { NULL, NULL },
};


/**
 * スレッドを作成し、処理を行う
 */
static int l_func1(lua_State *L) {
        printf("func1を実行...\n");

        //スレッドを作成する
        lua_State *L2 = lua_newthread(L);

        /*
         * この時点でスタック上にLのスタックにスレッドがpushされる。
         * lua_pop(L, 1)
         * などとやると、スタックからL2のスレッドがなくなり、gcの回収対象
         * となります。L2の処理がまだ残っているときにこれを行うと正常に動きません
         */
        printf("スレッド作成直後 -------------------------\n");
        //Lのスタックを表示
        printf("Lのstack:\n");
        stackDump(L);
        //L2のスタックを表示(新しく作られるため空の状態)
        printf("L2のstack:\n");
        stackDump(L2);
        //Lのスレッドで関数を実行し、その戻り値をL2のスレッドに渡します。


        //L2で、luaに書いてあるf関数を呼び出し、その戻り値を、Lに渡す
        lua_getglobal(L2, "f");
        //スレッドを実行させる(問題なく終了した場合は0が帰る)
        int res;
        if ( (res=lua_resume(L2, 0)) ) {
                if(res == LUA_YIELD) {
                        //スレッドの実行中、処理が中断した場合
                        //もう一度処理を再開させる場合はlua_resumeを再度呼ぶ
                        lua_resume(L2, 0);
                } else {
                        //なんらかのエラーが起きた場合
                        printf("エラーが起きました! : %d\n", res);
                        return 0;       //とりあえず正常に終わったふりをする
                }
        }

        printf("luaスクリプトのf関数実行直後 -------------------------\n");
        //Lのスタックを表示
        printf("Lのstack:\n");
        stackDump(L);
        //L2のスタックを表示(新しく作られるため空の状態)
        printf("L2のstack:\n");
        stackDump(L2);

        lua_xmove(L2, L, 1);    //Lのスタックから1つ要素を取り出し、L2のスタックへ移動する
        
        printf("結果 -------------------------\n");
        //Lのスタックを表示
        printf("Lのstack:\n");
        stackDump(L);
        //L2のスタックを表示(新しく作られるため空の状態)
        printf("L2のstack:\n");
        stackDump(L2);


        return 0;
}


/**
 * スレッドについて
 *
 * lua_newthreadでスレッドが作成可能です。luaのスレッドは、ノンプリエンプティブです。
 * lua_State間で、メモリが独立しており、データをやり取りするには、Cのコードの中で
 * データをやりとりしてやらねばなりません。
 * lua_newthreadで作成したスレッドも、gcの対象となります。
 * luaのスクリプト上、スレッドが処理を中断する場合は、coroutine.yield関数を使います。
 *
 * このサンプルでは、2つのスレッド間でのデータのやりとりを示します。
 */
int sample17() {
        lua_State *L = luaL_newstate();
        luaL_openlibs(L);

        try {
                //関数をluaへ登録
                luaL_register(L, "alctail", lua_funcArray);

                //.luaファイルを実行する
                char file_path[256];
                strcpy_s(file_path, LUAFILE_DIRECTORY);
                strcat_s(file_path, "luascript17.lua");
                printf(".lua file_path : %s\n", file_path);
                if(luaL_loadfile(L, file_path) || lua_pcall(L, 0, 0, 0)) {
                        //エラー
                        throw lua_tostring(L, -1);
                }

        } catch(char *msg) {
                lua_close(L);
                printf("error : %s", msg);
                printf("エラーが起きたので終了します。\n");
                exit(0);
        }

        lua_close(L);
        printf("処理が正常に完了しました。\n");
        return 0;
}

■ソース(luascript17.lua)

function f()
        print("fが呼ばれました!")

        print("スレッドの実行を止めます")
        coroutine.yield();      --      スレッドの実行を止める
        print("スレッドの実行を再開します")
        return "うはwwwwwwwwwww"
end

alctail.func1()

■出力結果

----------------------------------
----------------------------------
fが呼ばれました!
スレッドの実行を止めます
スレッドの実行を再開します
luaスクリプトのf関数実行直後 -------------------------
Lのstack:
----------------------------------
:stackDump  sp : 1    → type : LUA_TTHREAD
----------------------------------
L2のstack:
----------------------------------
:stackDump  sp : 1    → type : LUA_TSTRING   value : うはwwwwwwwwwww

----------------------------------
結果 -------------------------
Lのstack:
----------------------------------
:stackDump  sp : 1    → type : LUA_TTHREAD
:stackDump  sp : 2    → type : LUA_TSTRING   value : うはwwwwwwwwwww

----------------------------------
L2のstack:
----------------------------------
----------------------------------
処理が正常に完了しました。



よくある書き方

デバッグ中、処理の途中でreturnした場合の書き方

returnは処理の最後にしかかけないようになってます。
debug中てきとうなところでreturnさせたい場合、以下のように記述すると可能です。

■ソース

--関数の定義
function func1()
        print("1番目だお!")
        print("2番目だお!")
        --do endでブロックを作成し、その中でreturnを書いてやれば処理の途中でもreturnさせることが可能
        do
                return
        end
        print("3番目だお!")
end


--関数を呼ぶ
func1()

■実行結果

1番目だお!
2番目だお!


名前つき引数っぽく使う方法

引数に名前をつけることで、関数に渡す引数の順番を気にしないでよくする方法です。
正直使いどころがわからんw

■ソース

-- この関数を介してdoWorkを呼ぶようにする
function doWorkWrap(argv) 
        doWork(argv.nyannyan, argv.wanwan)
end

-- 何か作業をする関数
function doWork(nyannyan, wanwan)
        print("猫の鳴き声:"..nyannyan)
        print("犬の鳴き声:"..wanwan)
end



-- doWorkReceptionを呼ぶ
array = {
        nyannyan="にゃ〜ん",
        wanwan="わんわん"   
}

doWorkWrap(array)

■実行結果

猫の鳴き声:にゃ〜ん
犬の鳴き声:わんわん


データ処理している気がしてくるサンプル

元データを加工し、Luaで関数を呼ぶような形にデータを書き換えてやり、Luaソース内で検索したい(・・・など複雑な条件)処理を記述し、目的のデータを抽出したりできます。
こういうソースを見ると、なんかデータ処理している気がしてきませんか?w

■ソース

-- data.luaを実行し、データをentryに追加させます

-- 実行結果を格納する配列
local result = {};

-- Sample6_1.dataに記述されている関数
function insert(data)
	-- 3番目の要素が"A"のものだけ抽出する
	if data[3]=="A" then
		result[#result+1] = data[2];
	end
end

dofile("E:/usr/my/eclipse/Lua/LuaStudy/data/data.lua");


-- data[3]が"A"だったものの一覧を出力
for i=1, #result do
	print(result[i]);
end

プログラム中で使用しているdata.luaファイルの内容(ファイルパスは適当に書き換えてください)
-- データの並び順 → { データID, タイトル, 属性 }
insert{"000001", "データその1", "A"}
insert{"000002", "データその2", "B"}
insert{"000003", "データその3", "C"}
insert{"000004", "データその4", "A"}
insert{"000005", "データその5", "B"}
insert{"000006", "データその6", "C"}

■出力結果

データその1
データその4

VisualStudio2010で、C++からluaを使う場合の環境設定

VisualStudio Expressでの説明です(ほかのでもおなじよーな感じでいけるはず)。
LuaのソースをDLして、自分でビルドしている状態を前提としています。

事前準備として、プロジェクトの作成は完了しているとします。

1.ヘッダファイルを用意
Luaソースからヘッダファイルをどこか適当なディレクトリへコピーしてきます(どのファイルが必要で、どのファイルが不要かわかりません。。。とりあえず、もってくれば間違いないはずw)
ここでは、ヘッダを"E:\usr\bin\lua\include"へコピペしてきたとします。
また、Luaのソースのetcディレクトリの中に"lua.hpp"ファイルがあるので、これも、"E:\usr\bin\lua\include"へコピーしておきます。

2.VisualStudio側の設定 その1

プロジェクトに対し、ファイルのパスを通します。
プロジェクトをクリックし、メニューから「プロパティ」を選ぶと以下の画面が表示されます。

ここで、設定するのは、「インクルードディレクトリ」と「ライブラリディレクトリ」です。

「インクルードディレクトリ」には、さきほど、1.で作成したディレクトリのパスを追加します。
「ライブラリディレクトリ」には、Luaのソースをビルドしてできたものを一式、ディレクトリを新しく作りまとめて置いておきます(全部は必要ないでしょうけどとりあえず・・・)。そのディレクトリへのパスを追加してあげます。

3.VisualStudio側の設定 その2

VSのプロジェクトへ、LIBの追加を行います(2.の続き)

「追加の依存ファイル」に、"lua51.lib"を追加します(このファイルは、ビルドしたときに生成されるはずです)

4.ためしに、以下のようなコードを書き、問題なくビルドできるか試します(インタープリターみたいなプログラムです)。

main.cpp
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "lua.hpp"



int main() {

        char buff[256];
        int error;

        lua_State *L = luaL_newstate();
        luaL_openlibs(L);

        while(fgets(buff, sizeof(buff), stdin) != NULL) {
                error = luaL_loadbuffer(L, buff, strlen(buff), "line") || lua_pcall(L, 0, 0, 0);

                if (error) {
                        fprintf(stderr, "%s", lua_tostring(L, -1));
                        lua_pop(L, 1);
                }
        }

        lua_close(L);
        return 0;
}

VisualStudio2010でDLLを作成し、そのDLLをluaから使う場合の流れ

DLLを新規作成し、それをluaから使うまでの手順を書きます。DLLの作成方法については1例です。特にこの方法に縛られる必要もないかと。。。

1.DLL用のプロジェクトを作成

VisualStudioにてDLLを作成します。
メニューより「新規作成」→「プロジェクト」で、プロジェクトの作成を行います。ここで、「インストールされたテンプレート」の中の「CLR」→「クラス ライブラリ」を選ぶと、自動的にいくつかファイルが生成されます。ここでは、プロジェクトの名前を「SampleDll」と命名することにします。

2.DLLを作成

ここで編集するファイルは以下の2とします。

■Stdafx.cpp includeするヘッダファイルを記述(luaのヘッダなどを記述)
stdafx.cpp
#include <stdio.h>
#include <string.h>
#include "lua.hpp"
#include "lstate.h"

#include "stdafx.h"

■SampleDll.cpp luaへ関数を設定する関数を定義する
SampleDll.cpp
#include "stdafx.h"
#include "SampleDll.h"

/**
 * 引数に1つの文字列をとり、その文字列の長さを返します。
 * @return 正常時:戻り値1つ (1個目)文字列の長さを返す
 *         異常時:戻り値2つ (1個目)nil (2個眼)エラーメッセージ
 */
static int sampleFunc1(lua_State *L) {
        printf("sampleFunc1が呼ばれました 処理スタート。。。!\n");

        char *msg = (char*)luaL_checkstring(L, 1);
        if(msg == NULL) {
                lua_pushnil(L);
                lua_pushstring(L, "指定された文字列がNULLでした!");
                return 2;       //戻り値の数を返す
        }

        //戻り値をセットする
        int len = strlen(msg);
        lua_pushnumber(L, len);

        printf("sampleFunc1処理完了。。。\n");
        return 1; //戻り値の数を返す
}


//lua側に渡す関数の一覧を作成
const struct luaL_Reg g_map[] = {
        {"sampleFunc1", sampleFunc1},           //1番目:lua側での関数名, 2番目:関数ポインタ

        {NULL, NULL},   //番兵用にNULLのデータをいれておく
};

/**
 * このDLLがロードされた際、呼び出される関数
 */
extern "C" __declspec(dllexport) int luaopen_SampleDll(lua_State *L) {
        printf("luaopen_alctailLibが呼ばれました!\n");

        luaL_register(L, "sampleDll", g_map);   //ここでつけた名前("sampleDll")が、luaで使用する際の名前となります
        return 1;
}
ここで、注意がいるのが、luaopen_SampleDll関数です。
この関数が、lua側から呼ばれます。ほかの関数は特にluaのDLL読み込みにおいて必要ないのでstaticをつけてます。

luaがDLLを読み込むと、このluaopen_SampleDll(名前は何でもよいかと)関数を呼びます。
処理内で、luaL_register関数にて、関数を登録しています。この結果、lua上にはsampleDllというテーブルが作成され、その中に、g_mapで指定した関数が登録されます。

ソースを記述した後、ビルドを行うとDLLが生成されるはずです(このプロジェクトの場合、SampleDll.dllというファイルができるはずです)

3.上で作成したDLLを使用するluaのスクリプトを作成する

出来上がったDLLを、luaから見えるところに配置します(ここでは、lua.exeやluac.exeがあるところへSampleDll.dllをコピーしました)。

luaソース上で、

require "SampleDll"
result = sampleDll.sampleFunc1("aaaaaaaaaa");

とやると読み込めるはずです。そして、DLLに書いてある関数も正常に呼び出せるはずです。


luaである範囲すべてをコメントかする方法(ブロックコメント)

luaのソースを指定の範囲、すべてコメント化したくなるときがあります。その場合の書き方が用意されています。

■ソース

-- その1 "--[["から"]]"までの範囲をコメントとすることができる。
print("■その1")
print("ふぁっふぁふぁふぁふぁーふぁふぁふぁふぁふぁー")
--[[
print("救いは無いんですか!")
print("もう終わりだ!")
print("すべてはチャンス!")
]]
print("歪みねぇな")


-- その2 "--[["から"--]]"までの範囲をコメントとすることができる。
print("\n■その2")
print("ふぁっふぁふぁふぁふぁーふぁふぁふぁふぁふぁー")
--[[
print("救いは無いんですか!")
print("もう終わりだ!")
print("すべてはチャンス!")
--]]
print("歪みねぇな")

print([[1文字いれるだけで、コメント状態を解除が可能です]])
print("ふぁっふぁふぁふぁふぁーふぁふぁふぁふぁふぁー")
--![[
print("救いは無いんですか!")
print("もう終わりだ!")
print("すべてはチャンス!")
--]]
print("歪みねぇな")



-- "--[==["から "]==]"までの範囲をコメントとすることができる。
-- ==をいれる方式を使うと、コメントを含むluaのコードをすべてコメントアウトできる。
-- は==の数を対応させればネストさせられる。
print("\n■その3")
--[===[
--[==[
print("ふぁっふぁふぁふぁふぁーふぁふぁふぁふぁふぁー")
--[[
print("救いは無いんですか!")
print("もう終わりだ!")
print("すべてはチャンス!")
--]]
print("歪みねぇな")
--]==]
--]===]

■実行結果

■その1
ふぁっふぁふぁふぁふぁーふぁふぁふぁふぁふぁー
歪みねぇな

■その2
ふぁっふぁふぁふぁふぁーふぁふぁふぁふぁふぁー
歪みねぇな
1文字いれるだけで、コメント状態を解除が可能です
ふぁっふぁふぁふぁふぁーふぁふぁふぁふぁふぁー
救いは無いんですか!
もう終わりだ!
すべてはチャンス!
歪みねぇな

■その3