1. ホーム
  2. スクリプト・コラム
  3. ルア

Luaチュートリアル(6):コンパイルと実行でエラーが発生した場合

2022-02-13 11:36:33

1. コンパイルする
    Luaにはdofileという関数が用意されており、Luaのコードブロックを実行するための組み込み操作となります。しかし、実際にはdofileは単なるヘルパー関数であり、loadfileが本当のコア関数です。dofileとは対照的に、loadfileは指定されたファイルからLuaブロックをロードし、ブロックをコンパイルして、コンパイルエラーがあればnilを返し、エラーメッセージを出すだけで、コンパイルに成功しても実際にブロックを実行するわけではありません。従って、dofileは以下のように実装することができます。

コピーコード コードは以下の通りです。

 function dofile(filename)
     local f = assert(loadfile(filename))
     return f()
 end

    ここでは、assert関数はloadfileの実行が失敗した場合に直接エラーを発生させます。dofileのコードを見ると、ファイル内のLuaコードのブロックを複数回実行する場合、loadfileを一度実行し、それが返す結果を複数回実行すれば、複数回のコンパイルによるオーバーヘッドを節約できることもわかります。ここがloadfileとdofileの性能の違いです。
    Luaには、Luaのコードを動的に実行するもう1つの方法として、loadstring関数も用意されています。その名の通り、loadfileとは対照的に、loadstringのコードソースは引数の文字列から得られます。
    f = loadstring("i = i + 1")
    この時点でfは、呼び出されるたびに "i = i + 1" を実行する関数となり、以下のようになります。
コピーコード コードは以下の通りです。

 i = 0
 f() 
 print(i) - will output 1
 f()
 print(i) - will output 2

    loadstringは確かに強力な関数ですが、その結果生じるパフォーマンスのオーバーヘッドも考慮しなければなりません。つまり、多くの定数文字列に対して、上記の例のようにf = loadstring("i = i + 1")のようにloadstringのアプローチを使うことはあまり意味がないのです。後者は前者よりはるかに効率的です。後者は一度だけコンパイルされるのに対して、前者はloadstringが呼ばれるたびにコンパイルされるからです。loadstringについては、この関数は常にグローバル環境で文字列をコンパイルするので、ローカル変数をファイルすることができず、グローバル変数にのみアクセスすることに注意する必要があります、例.
コピーコード コードは以下の通りです。

 i = 32
 local i = 0
 f = loadstring("i = i + 1; print(i)")
 g = function() i = i + 1; print(i) end
 f() -- i in the f function is the global variable i, so the output is 33
 g() - the i in the g function is the local variable i, so the output is 1

    loadstringで返される関数で、式を評価する必要がある場合は、式の前にreturnを付けて、式の値を返す文にする必要があります、例.
コピーコード コードは以下の通りです。

 i = 32
 f = loadstring("i = i + 1; return i * 2")
 print(f()) -- output 66
 print(f()) -- output 68. Since loadstring returns the regular function, it can be called repeatedly.

    Luaはすべてのセパレートブロックを無名関数の関数本体として扱い、その無名関数も可変長の実引数を持つので、loadstringを呼び出す際に、次のような引数を渡すことができます。
コピーコード コードは以下の通りです。

 local i = 30
 -- The following ... indicates a variable-length real parameter that assigns the value to the local variable x.
 local f = assert(loadstring("local x = ... ; return (x + 10) * 2"))
 for i = 1, 20 do
     print(string.rep("*",f(i)))
 end

 2. Cコードです。

    前節では、Luaのコードを動的にロードすることを説明しましたが、実はLua自身もC言語のダイナミック・ライブラリからコードを動的にロードすることをサポートしています。これはLuaの組み込みシステム関数package.loadlibの助けを借りて行われ、2つの文字列引数(ダイナミック・ライブラリの完全なファイル名とライブラリに含まれる関数の名前)を持っています。

コピーコード コードは以下の通りです。

    local path = "/usr/local/lib/test.so"
    local f = package.loadlib(path,"test_func")

    loadlibは非常に底辺の関数なので、フルパス名と関数名で呼び出す必要があります。

    3. エラー

    Luaは組み込み型スクリプト言語であるため、エラーが発生しても単純に終了したり、クラッシュしたりしてはいけません。その代わり、エラーが発生したら、Luaは現在のブロックを終了し、アプリケーションに戻るようにしなければなりません。
    Luaでは、error()関数でエラーメッセージを取得することができます。

コピーコード コードは以下の通りです。

    print "enter a number:"
    n = io.read("*number")
    if not n then error("invalid input") end

    上記のコードの最後の行は、Luaが提供するもう一つの組み込み関数、assertクラスの助けを借りて、次のようにすることができます。
コピーコード コードは以下の通りです。

    print "enter a number:"
    n = assert(io.read("*number"),"invalid input")

    assert関数は、最初の引数が真であるかどうかをチェックし、真であればその引数を返し、そうでなければエラーを発生させます。第二引数にはオプションで文字列を指定します。
    エラー処理は、すべてのプログラミング言語にとって非常に重要な側面です。実際には、統一されたガイドラインはなく、問題を慎重に分析し、アプリケーションのシナリオと組み合わせて、そして自分の経験をもとに具体的なエラー処理の方法を示すしかありません。エラーコードを直接返す場合もあれば、開発者がエラーの原因となったコードの出所をすぐに突き止められるように、エラーを直接投げる必要がある場合もあります。

    4. エラー処理と例外処理。

    Luaにはエラー処理関数pcallがあり、その第一引数にprotected"されるべき関数が指定されています。関数が失敗した場合、pcallはfalseとエラーメッセージを返し、それ以外の場合はtrueと関数呼び出しの戻り値を返します。次のコードを見てください。

コピーコード コードは以下の通りです。

 function foo()
    local a = 10
    print(a[2])
end

r, msg = pcall(foo)
if r then
    print("This is ok.")
else
    print("This is error.")
    print(msg)
end
-- The output is.
--This is error.
--d:/test.lua:3: attempt to index local 'a' (a number value)

また、匿名関数を直接pcall関数に渡すこともできます。

コピーコード コードは以下の通りです。

r, msg = pcall(function() error({code = 121}) end)
if r then
    print("This is ok.")
else
    print("This is error.")
    print(msg.code)
end
-- The output is.
--This is error.
--121

 5. エラーメッセージとトレースバック。

    エラーが発生したとき、エラーが発生した場所だけでなく、より多くのデバッグ情報を得ることが望ましい場合があります。少なくとも、エラーが発生した時点と関数呼び出しの時点までさかのぼって、関数呼び出しのスタックトレースを完全に表示することが望まれます。そのためには、Luaが提供する別の組み込み関数、xpcallを使う必要があります。xpcallは、呼び出す必要のある関数に加え、エラー処理関数を第2引数に取ります。エラーが発生した場合、Luaはコールスタックが展開される前にエラーハンドラ関数を呼び出します。こうすることで、この関数内でデバッグ・ライブラリのdebug.traceback関数を使用することができ、コールスタックを元に展開されたエラーメッセージを構築することができる。例えば

コピーコード コードは以下の通りです。

function errorFunc()
    local a = 20
    print(a[10])
end

function errorHandle()
    print(debug.traceback())
end

if xpcall(errorFunc,errorHandle) then
    print("This is OK.")
else
    print("This is error.")
end

-- The output is.
--[[stack traceback:
        d:/test.lua:7: in function <d:/test.lua:6>
        d:/test.lua:3: in function <d:/test.lua:1>
        [C]: in function 'xpcall'
        d:/test.lua:10: in main chunk
        [C]: ?
This is error.
--]]