Reputation: 13451
I have a working C++ function that I am able to call from Lua. To demonstrate my problem here is an example:
int PushHello(lua_State *L){
string str("Hello");
lua_pushlstring(L, str.data(), str.length());
return 1;
}
Note: I know I don't have to use string variable there, but it is there to demonstrate the problem.
Here are my two problems:
When I call this function from Lua string constructor may throw an exception. Is that a problem? Will Lua handle it and unwind the Lua stack properly? I don't think so. How can I solve that? Do I need to add try/catch
around all such code and convert the exception to lua_error? Is not there a better solution?
Another problem that I have probably solved by compiling Lua as C++ is when lua_pushlstring()
calls lua_error()
string destructor would not be called if longjmp was used. Is the problem solved by compiling as C++ and throwing exceptions instead of using longjmp?
To clarify, possible solution I can see to problem 1 would be this:
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;
}
But that is very ugly and error prone as try/catch
would need to be added to many places. It could be done as a macro and put around every command that can throw, but that would not be much nicer.
Upvotes: 22
Views: 9131
Reputation: 6858
Juraj Blaho's answer is great. It has however a drawback: for each function you export with int SafeFunction<PushHello>(lua_State *L)
, the compiler will generate a copy of all code from the template, just as if it was a macro. When numerous small functions are exported, this will be a waste of footprint.
You can easily avoid the problem by defining a common static
function performing all the job, and the template
function just calls that common function:
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);
}
Upvotes: 4
Reputation: 13451
I have found a reasonable solution. The question is whether it is correct. Instead of exporting (or calling via lua_cpcall) the original function int PushHello(lua_State *L)
a wrapper int SafeFunction<PushHello>(lua_State *L)
is exported/called. The wrapper looks like:
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;
}
What do you think about it? Do you see any problems?
Upvotes: 9
Reputation: 13628
I wouldn't use lua_error to denote an error occuring outside of lua functionality. lua_error would be used if you are adding additional lua functions that can be invoke within the bounds of lua. Then lua_error would be appropriate if an error occurs while executing that function.
Also this is a duplicate of Stack unwinding in C++ when using Lua
Edit
If you are worried about the string destructor being called why not do this:
try
{
string str("Hello");
lua_pushlstring(L, str.data(), str.length());
}
catch (exception& e)
{
luaL_error(L, e.what());
}
I realize this is a subtle change to what you suggested but there is a difference. If an exception is thrown anything on the stack within the try{}
will destruct. Just ensure that anything you want to destruct is within that try.
Upvotes: 1
Reputation: 15872
Lua will not catch the C++ exception. If you do not catch it, it will be passed up the call stack until it is either caught by some other block of code or causes the program to crash (unhandled exception). If the functions you expose to Lua call functions that can throw exceptions, you should handle them in that function.
Upvotes: 1
Reputation: 146910
If you compile Lua as C++, then they will use C++ exceptions as errors, whereas if you compile as C then they will use longjmp/setjmp. This basically means that there's no big deal throwing such an exception.
Upvotes: 0