lua尝试调用对象方法并出现 lua 错误时绑定中止
luabind abort when trying to call object method with lua errors
我使用 http://www.rasterbar.com/products/luabind/docs.html#deriving-in-lua 中的示例在 c++ 中定义了一个 class,我可以从 lua:
中派生
class base
{
public:
base(const char* s)
{ std::cout << s << "\n"; }
virtual void f(int a)
{ std::cout << "f(" << a << ")\n"; }
};
struct base_wrapper : base, luabind::wrap_base
{
base_wrapper(const char* s)
: base(s)
{}
virtual void f(int a)
{
call<void>("f", a);
}
static void default_f(base* ptr, int a)
{
return ptr->base::f(a);
}
};
...
module(L)
[
class_<base, base_wrapper>("base")
.def(constructor<const char*>())
.def("f", &base::f, &base_wrapper::default_f)
];
然后我在 lua 中创建了一个派生的 class:
class 'base_derived' (base)
function base_derived:__init(str)
base.__init(self,str)
end
function base_derived:f()
this_function_doesnt_exist()
end
任何对 'f' 的调用都应该抛出 lua 错误,如果我在 lua:
中这样做,它会正常工作
local x = base_derived("Test")
x:f() -- Throws "attempt to call a nil value..." error
我想做同样的事情,但在 C++ 中:
auto g = luabind::globals(l);
auto r = g["base_derived"];
if(r)
{
luabind::object o = r("Test");
auto gm = luabind::object_cast<base_wrapper*>(o);
if(gm != nullptr)
{
try
{
luabind::call_member<void>(o,"f",5);
}
catch(luabind::error &e)
{
std::cout<<"[LUA] Error: "<<e.what()<<std::endl;
}
}
o.push(l);
}
但是 'luabind::call_member'-调用导致 'luabind/detail/call_member.hpp' 中止,第 258 行:
// Code snippet of luabind/detail/call_member.hpp
~proxy_member_void_caller()
{
if (m_called) return;
m_called = true;
// don't count the function and self-reference
// since those will be popped by pcall
int top = lua_gettop(L) - 2;
// pcall will pop the function and self reference
// and all the parameters
push_args_from_tuple<1>::apply(L, m_args);
if (pcall(L, boost::tuples::length<Tuple>::value + 1, 0))
{
assert(lua_gettop(L) == top + 1);
#ifndef LUABIND_NO_EXCEPTIONS
////////////////////////////////////////////
throw luabind::error(L); // LINE 258
////////////////////////////////////////////
#else
error_callback_fun e = get_error_callback();
if (e) e(L);
assert(0 && "the lua function threw an error and exceptions are disabled."
"If you want to handle this error use luabind::set_error_callback()");
std::terminate();
#endif
}
// pops the return values from the function
stack_pop pop(L, lua_gettop(L) - top);
}
该行中的异常实际上并未抛出,但它是导致中止的原因。
但是,仅当 lua 函数导致 lua 错误时才会发生中止。如果我评论 'this_function_doesnt_exist()'-调用,lua- 和 c++-版本 运行 都很好。
为什么 'throw luabind::error(L);' 会导致中止?即使存在潜在的 lua 错误,我该怎么做才能安全地从 C++ 调用该函数?
// 编辑:
这是中止时的调用堆栈(调用'luabind::call_member(o,"f",5);'时):
> vcruntime140d.dll!__CxxFrameHandler(EHExceptionRecord * pExcept, unsigned __int64 RN, _CONTEXT * pContext, _xDISPATCHER_CONTEXT * pDC) Line 213 C++
ntdll.dll!RtlpExecuteHandlerForException() Unknown
ntdll.dll!RtlDispatchException() Unknown
ntdll.dll!KiUserExceptionDispatch() Unknown
KernelBase.dll!RaiseException() Unknown
vcruntime140d.dll!_CxxThrowException(void * pExceptionObject, const _s__ThrowInfo * pThrowInfo) Line 136 C++
server.dll!luabind::detail::proxy_member_void_caller<boost::tuples::tuple<int const * __ptr64,boost::tuples::null_type,boost::tuples::null_type,boost::tuples::null_type,boost::tuples::null_type,boost::tuples::null_type,boost::tuples::null_type,boost::tuples::null_type,boost::tuples::null_type,boost::tuples::null_type,boost::tuples::null_type,boost::tuples::null_type,boost::tuples::null_type,boost::tuples::null_type,boost::tuples::null_type,boost::tuples::null_type,boost::tuples::null_type,boost::tuples::null_type> >::~proxy_member_void_caller<boost::tuples::tuple<int const * __ptr64,boost::tuples::null_type,boost::tuples::null_type,boost::tuples::null_type,boost::tuples::null_type,boost::tuples::null_type,boost::tuples::null_type,boost::tuples::null_type,boost::tuples::null_type,boost::tuples::null_type,boost::tuples::null_type,boost::tuples::null_type,boost::tuples::null_type,boost::tuples::null_type,boost::tuples::null_type,boost::tuples::null_type,boost::tuples::null_type,boost::tuples::null_type> >() Line 258 C++
这是中止消息:
参见第 254 行:
if (pcall(L, boost::tuples::length<Tuple>::value + 1, 0))
在这里,pcall
要求 lua 执行您的 x:f(5)
等效调用。显然这段代码 returns 是一个错误,因为 pcall() returns 不同于零。这是预料之中的,因为您 确实 通过调用 this_function_doesnt_exist()
在 lua 中创建了一个错误。这也解释了为什么在您评论 this_function_doesnt_exist()
调用时不会发生中止。
然后,到C++代码:
throw luabind::error(L);
这个错误是从析构函数中抛出的:~proxy_member_void_caller()
,结果是throwing an exception from a destructor is bad practice. This problem is known for luabind (see this question)甚至没有抛出就触发了abort的调用。
解决方法是在~proxy_member_void_caller()
的签名中加上noexcept(false)
,像这样:
~proxy_member_void_caller() noexcept(false)
我使用 http://www.rasterbar.com/products/luabind/docs.html#deriving-in-lua 中的示例在 c++ 中定义了一个 class,我可以从 lua:
中派生class base
{
public:
base(const char* s)
{ std::cout << s << "\n"; }
virtual void f(int a)
{ std::cout << "f(" << a << ")\n"; }
};
struct base_wrapper : base, luabind::wrap_base
{
base_wrapper(const char* s)
: base(s)
{}
virtual void f(int a)
{
call<void>("f", a);
}
static void default_f(base* ptr, int a)
{
return ptr->base::f(a);
}
};
...
module(L)
[
class_<base, base_wrapper>("base")
.def(constructor<const char*>())
.def("f", &base::f, &base_wrapper::default_f)
];
然后我在 lua 中创建了一个派生的 class:
class 'base_derived' (base)
function base_derived:__init(str)
base.__init(self,str)
end
function base_derived:f()
this_function_doesnt_exist()
end
任何对 'f' 的调用都应该抛出 lua 错误,如果我在 lua:
中这样做,它会正常工作local x = base_derived("Test")
x:f() -- Throws "attempt to call a nil value..." error
我想做同样的事情,但在 C++ 中:
auto g = luabind::globals(l);
auto r = g["base_derived"];
if(r)
{
luabind::object o = r("Test");
auto gm = luabind::object_cast<base_wrapper*>(o);
if(gm != nullptr)
{
try
{
luabind::call_member<void>(o,"f",5);
}
catch(luabind::error &e)
{
std::cout<<"[LUA] Error: "<<e.what()<<std::endl;
}
}
o.push(l);
}
但是 'luabind::call_member'-调用导致 'luabind/detail/call_member.hpp' 中止,第 258 行:
// Code snippet of luabind/detail/call_member.hpp
~proxy_member_void_caller()
{
if (m_called) return;
m_called = true;
// don't count the function and self-reference
// since those will be popped by pcall
int top = lua_gettop(L) - 2;
// pcall will pop the function and self reference
// and all the parameters
push_args_from_tuple<1>::apply(L, m_args);
if (pcall(L, boost::tuples::length<Tuple>::value + 1, 0))
{
assert(lua_gettop(L) == top + 1);
#ifndef LUABIND_NO_EXCEPTIONS
////////////////////////////////////////////
throw luabind::error(L); // LINE 258
////////////////////////////////////////////
#else
error_callback_fun e = get_error_callback();
if (e) e(L);
assert(0 && "the lua function threw an error and exceptions are disabled."
"If you want to handle this error use luabind::set_error_callback()");
std::terminate();
#endif
}
// pops the return values from the function
stack_pop pop(L, lua_gettop(L) - top);
}
该行中的异常实际上并未抛出,但它是导致中止的原因。
但是,仅当 lua 函数导致 lua 错误时才会发生中止。如果我评论 'this_function_doesnt_exist()'-调用,lua- 和 c++-版本 运行 都很好。
为什么 'throw luabind::error(L);' 会导致中止?即使存在潜在的 lua 错误,我该怎么做才能安全地从 C++ 调用该函数?
// 编辑: 这是中止时的调用堆栈(调用'luabind::call_member(o,"f",5);'时):
> vcruntime140d.dll!__CxxFrameHandler(EHExceptionRecord * pExcept, unsigned __int64 RN, _CONTEXT * pContext, _xDISPATCHER_CONTEXT * pDC) Line 213 C++
ntdll.dll!RtlpExecuteHandlerForException() Unknown
ntdll.dll!RtlDispatchException() Unknown
ntdll.dll!KiUserExceptionDispatch() Unknown
KernelBase.dll!RaiseException() Unknown
vcruntime140d.dll!_CxxThrowException(void * pExceptionObject, const _s__ThrowInfo * pThrowInfo) Line 136 C++
server.dll!luabind::detail::proxy_member_void_caller<boost::tuples::tuple<int const * __ptr64,boost::tuples::null_type,boost::tuples::null_type,boost::tuples::null_type,boost::tuples::null_type,boost::tuples::null_type,boost::tuples::null_type,boost::tuples::null_type,boost::tuples::null_type,boost::tuples::null_type,boost::tuples::null_type,boost::tuples::null_type,boost::tuples::null_type,boost::tuples::null_type,boost::tuples::null_type,boost::tuples::null_type,boost::tuples::null_type,boost::tuples::null_type> >::~proxy_member_void_caller<boost::tuples::tuple<int const * __ptr64,boost::tuples::null_type,boost::tuples::null_type,boost::tuples::null_type,boost::tuples::null_type,boost::tuples::null_type,boost::tuples::null_type,boost::tuples::null_type,boost::tuples::null_type,boost::tuples::null_type,boost::tuples::null_type,boost::tuples::null_type,boost::tuples::null_type,boost::tuples::null_type,boost::tuples::null_type,boost::tuples::null_type,boost::tuples::null_type,boost::tuples::null_type> >() Line 258 C++
这是中止消息:
参见第 254 行:
if (pcall(L, boost::tuples::length<Tuple>::value + 1, 0))
在这里,pcall
要求 lua 执行您的 x:f(5)
等效调用。显然这段代码 returns 是一个错误,因为 pcall() returns 不同于零。这是预料之中的,因为您 确实 通过调用 this_function_doesnt_exist()
在 lua 中创建了一个错误。这也解释了为什么在您评论 this_function_doesnt_exist()
调用时不会发生中止。
然后,到C++代码:
throw luabind::error(L);
这个错误是从析构函数中抛出的:~proxy_member_void_caller()
,结果是throwing an exception from a destructor is bad practice. This problem is known for luabind (see this question)甚至没有抛出就触发了abort的调用。
解决方法是在~proxy_member_void_caller()
的签名中加上noexcept(false)
,像这样:
~proxy_member_void_caller() noexcept(false)