2011-01-06 28 views
21

私はLuaから呼び出せるC++の機能を持っています。私はそこに文字列変数を使用する必要はありません知っているが、それは、問題を実証することがあります:Luaから関数を呼び出すときにC++の例外を処理する方法は?

int PushHello(lua_State *L){ 
    string str("Hello"); 
    lua_pushlstring(L, str.data(), str.length()); 
    return 1; 
} 

注:ここでは私の問題を示すためには、一例です。私は例外をスローすることがLuaの文字列コンストラクタからこの関数を呼び出すと

  1. :ここ

    は私の二つの問題があります。問題ありますか?ルアはそれを処理し、ルアスタックを適切に巻き戻しますか?私はそうは思わない。それをどうすれば解決できますか?そのようなコードのまわりにtry/catchを追加し、例外をlua_errorに変換する必要がありますか?より良い解決策はありませんか?

  2. longjmp関数を使用した場合はC++がlua_pushlstring()通話lua_error()文字列のデストラクタが呼び出されることはないされたときのように、私はおそらくLuaのをコンパイルすることによって解決している別の問題。問題は、C++としてコンパイルし、longjmpを使用する代わりに例外をスローすることで解決されますか?明確にするために

、私は問題1に見ることができる可能な解決策は、このようになります:

int PushHello(lua_State *L){ 
    string str; 
    try{ 
     str.assign("Hello"); 
    catch(exception &e){ 
     luaL_error(L, e.what()); 
    } 
    lua_pushlstring(L, str.data(), str.length()); 
    return 1; 
} 

しかしtry/catchは、多くの場所に追加される必要があるであろうと、それは非常に醜いとエラーが発生しやすくなります。それはマクロとして実行することができ、投げることができるすべてのコマンドを置くことができますが、これはあまりうまくいかないでしょう。

+0

私はこのスタック交換[案](HTTPと思います@Blaho ://area51.stackexchange.com/proposals/11464/code-review?referrer = aWNm_PdciyFqjFW8CUacGw2 "コードレビュー")があなたにとって興味深いかもしれません。それがあなたのサポートを示し、それをベータ版に手伝ったら! :) – greatwolf

答えて

8

私は妥当な解決策を見つけました。問題はそれが正しいかどうかです。エクスポート(またはlua_cpcall経由での呼び出し)の代わりに、元の関数int PushHello(lua_State *L)ラッパーint SafeFunction<PushHello>(lua_State *L)がエクスポート/呼び出されます。ラッパーは次のようになります。

template<lua_CFunction func> 
int SafeFunction(lua_State *L){ 
    int result = 0; 
    try{ 
     result = func(L); 
    } 
    // transform exception with description into lua_error 
    catch(exception &e){ 
     luaL_error(L, e.what()); 
    } 
    // rethrow lua error - C++ Lua throws lua_longjmp* 
    catch(lua_longjmp*){ 
     throw; 
    } 
    // any other exception as lua_error with no description 
    catch(...){ 
     luaL_error(L, "Unknown error"); 
    } 

    return result; 
} 

あなたはどう思いますか?何か問題は見えますか?

+0

catchブロックから_luaL_error_を呼び出さないでください。それは例外オブジェクトをリークします。スタック内のエラーメッセージをプッシュし、後で_lua_error_を呼び出す必要があります。 [これらの行に沿って]何か(https://github.com/ignacio/LuaCppBridge51/blob/master/lcbBaseObject.h.html)。 – Ignacio

+0

@Ignacio:例外オブジェクトがどのように漏れる可能性があるのか​​分かりません。 'catch'ブロックから例外をスローすることは安全でなければなりません。私はC++としてLuaをコンパイルしました。 –

+0

申し訳ありません。あなたのコメントを見ました。あなたが正しい。あなたがC++としてコンパイルされたLuaを持っているので、luaL_errorはスローされるので、オブジェクトは漏れません。しかし、LuaをCとしてコンパイルした場合、luaL_errorはlongjmpになります。したがって、キャッチした例外は(特に)破棄されません。 – Ignacio

1

lua_errorを使用して、lua機能の外で発生したエラーを示すことはありません。 luaの境界内で呼び出すことができる追加のlua関数を追加する場合は、lua_errorが使用されます。その関数の実行中にエラーが発生した場合、lua_errorは適切です。

また、このStack unwinding in C++ when using Lua

の複製である編集

このない理由と呼ばれている文字列のデストラクタを心配している場合:

try 
{ 
    string str("Hello"); 
    lua_pushlstring(L, str.data(), str.length()); 
} 
catch (exception& e) 
{ 
    luaL_error(L, e.what()); 
} 

を、私は、これは微妙であると認識あなたが提案したものに変更しますが、違いがあります。例外がスローされると、try{}内のスタック上の何かが破壊されます。破壊しようとしているものがその試行内にあることを確認してください。

+0

私は重複しているとは思わない。おそらく、C++スタックの巻き戻しに問題はありません。問題は、例外がスローされた場合、Luaはそれをキャッチして、スタックを正しく巻き戻しますか?そして私が言ったように、関数はLuaから呼び出されます。私は私の質問でそれを明確にします。 –

+0

@Jurajあなたは正しいですが、あなたはそれがluaスクリプト自体の中で呼び出されると言っていました。私は誤解しました。しかし、私はまだ、他の質問があなたの質問に答えると思います。 Luabindを使用する場合は、例外が発生した場合に使用することをお勧めします。 Luaはその例外を処理し、正しくリワインドします。私は主な違いは、現在の関数のスタックとは対照的に、Luaスタック自体が正しく巻き戻されるかどうかを確認することです。あなたがluabindを使用している場合、はいluaがそれを捕まえる答えによれば –

+0

はい、それは解決策ですが、私はLuaBindを使いたくありません。 –

0

C++としてLuaをコンパイルすると、エラーとしてC++例外が使用されますが、Cとしてコンパイルするとlongjmp/setjmpが使用されます。これは、基本的には、このような例外を投げる大したことはないということです。

+0

問題は投げているのではなく、捕まえることです。 C++コード(文字列コンストラクタのような)が例外をスローすると、Luaはこれをどのように処理しますか?エラーメッセージが表示されるのは何ですか? –

1

LuaはC++例外をキャッチしません。それをキャッチしないと、他のコードブロックでキャッチされるか、プログラムがクラッシュする(未処理例外)まで、コールスタックに渡されます。 Luaに公開する関数が例外をスローする関数を呼び出す場合は、その関数でそれらを処理する必要があります。

+0

私はそれを知っています(LuaがC++としてコンパイルされているときでさえ、まったく真実ではありませんが)。問題はどのようにして例外をうまくキャッチできますか? –

+0

True(私はあなたのC++コードで書かれた関数を呼び出すスクリプトとしてLuaを使用していたと仮定していました。つまり実際にはLuaコードをコンパイルしていませんでした)。 (関数がOutOfBoundsExceptionをスローし、それを正常に処理してキャッチできれば)、catch(...)を使ってすべてをキャッチすることができますそうでなければ、関数を正常に終了します(必要なクリーンアップなどを実行します)。編集:基本的に、私は常に私はスクリプト言語に 'nothrow'関数として公開する関数を扱います。 –

+0

問題は、lua_errorも例外を送出するため、catch(...)できないということです。 –

4

Juraj Blahoの答えは素晴らしいです。しかし、欠点があります:int SafeFunction<PushHello>(lua_State *L)でエクスポートする関数ごとに、コンパイラはすべてコードのコピーをマクロのように生成します。多数の小さな関数をエクスポートすると、これはフットプリントの無駄になります。

あなたは簡単にすべてのジョブを実行する共通static機能を定義することで問題を回避することができ、かつtemplate機能だけでは、共通の関数を呼び出します。

static int SafeFunctionCommon(lua_State *L, lua_CFunction func){ 
    int result = 0; 
    try{ 
     result = func(L); 
    } 
    // transform exception with description into lua_error 
    catch(exception &e){ 
     luaL_error(L, e.what()); 
    } 
    // rethrow lua error - C++ Lua throws lua_longjmp* 
    catch(lua_longjmp*){ 
     throw; 
    } 
    // any other exception as lua_error with no description 
    catch(...){ 
     luaL_error(L, "Unknown error"); 
    } 

    return result; 
} 

template<lua_CFunction func> 
int SafeFunction(lua_State *L){ 
    return SafeFunctionCommon(L, func); 
} 
+0

lua_longjmp *はどこから来たのですか?あなたのtry/catchを実装しようとしましたが、lua_longjmpはどこにも定義されていません – MintyAnt

+1

@MintyAnt 'lua_longjmp'は[' ldo.c']で定義された内部構造です(https://github.com/lua/lua/blob /5.2.4/src/ldo.c#L76)(LUA 5.2.4)。 – Lekensteyn

関連する問題