如何处理 C++ class 在 Lua 中构造失败
How to handle C++ class construct failure in Lua
我使用 SWIG 绑定 C++ classes 所以我可以在 Lua.
中使用它们
我想知道是否可以在 Lua 中处理 C++ class 的构造失败。
例如,我有以下 Test
class,它在构造时尝试获取用户数据。
void *getUserdata(lua_State *L, const char *key)
{
lua_pushstring(L, key);
lua_gettable(L, LUA_REGISTRYINDEX);
return lua_touserdata(L, -1);
}
class Test
{
public:
Test(lua_State *L)
:data(static_cast<MyData *>(getUserdata(L, "my_name"))){};
void setDataNum(int num)
{
data->num = num;
}
private:
MyData *data;
};
但是,如果 getUserdata()
returns nullptr
,调用 setDataNum()
会使我的应用程序崩溃。
我想知道是否有任何方法可以检测和处理构造失败(data
在这种情况下变为 nullptr
)所以 class 不会在 [=32= 中创建].
错误处理的第一步是检查错误。来自 Lua reference manual:
void *lua_touserdata (lua_State *L, int index);
If the value at the given index is a full userdata, returns its block address. If the value is a light userdata, returns its pointer. Otherwise, returns NULL
.
这意味着我们可以通过检查 data
是否为 NULL
来轻松检查调用是否成功。然后我们就可以照办了,我选择抛出异常
test.hpp
#pragma once
#include <stdexcept>
#include <lua.hpp>
struct MyData {
int num;
};
void *getUserdata(lua_State *L, const char *key) {
lua_pushstring(L, key);
lua_gettable(L, LUA_REGISTRYINDEX);
return lua_touserdata(L, -1);
}
class Test {
public:
Test(lua_State *L)
: data(static_cast<MyData *>(getUserdata(L, "my_name"))) {
if (data == nullptr) {
throw std::runtime_error("invalid userdata at \"my_name\"");
}
};
void setDataNum(int num) { data->num = num; }
private:
MyData *data;
};
此异常无法被 Lua 消化,默认情况下解释器将因 terminate called after throwing an instance of 'std::runtime_error'
而崩溃。这不太好,我们宁愿将异常转换为 Lua 错误。 SWIG 带有 support for that.
test.i
%module example
%{
#include "test.hpp"
%}
%include <exception.i>
%exception {
try {
$action
} catch (std::exception const &e) {
SWIG_exception(SWIG_RuntimeError, e.what());
}
}
%typemap(default) (lua_State *L) { = L; }
%include "test.hpp"
Lua 没有例外,因此没有 try
-catch
块。相反,Lua 具有使用 pcall
进行受保护调用的概念。这将 return 标记调用是否成功以及调用的结果或错误。
local example = require("example")
local success, c = pcall(example.Test)
if (success) then
c:setDataNum(1)
else
print(c)
end
调用示例:
$ swig -c++ -lua test.i
$ clang++ -Wall -Wextra -Wpedantic -I /usr/include/lua5.2 -fPIC -shared test_wrap.cxx -o example.so -llua5.2
$ lua5.2 test.lua
SWIG_RuntimeError:invalid userdata at "my_name"
我使用 SWIG 绑定 C++ classes 所以我可以在 Lua.
中使用它们我想知道是否可以在 Lua 中处理 C++ class 的构造失败。
例如,我有以下 Test
class,它在构造时尝试获取用户数据。
void *getUserdata(lua_State *L, const char *key)
{
lua_pushstring(L, key);
lua_gettable(L, LUA_REGISTRYINDEX);
return lua_touserdata(L, -1);
}
class Test
{
public:
Test(lua_State *L)
:data(static_cast<MyData *>(getUserdata(L, "my_name"))){};
void setDataNum(int num)
{
data->num = num;
}
private:
MyData *data;
};
但是,如果 getUserdata()
returns nullptr
,调用 setDataNum()
会使我的应用程序崩溃。
我想知道是否有任何方法可以检测和处理构造失败(data
在这种情况下变为 nullptr
)所以 class 不会在 [=32= 中创建].
错误处理的第一步是检查错误。来自 Lua reference manual:
void *lua_touserdata (lua_State *L, int index);
If the value at the given index is a full userdata, returns its block address. If the value is a light userdata, returns its pointer. Otherwise, returns
NULL
.
这意味着我们可以通过检查 data
是否为 NULL
来轻松检查调用是否成功。然后我们就可以照办了,我选择抛出异常
test.hpp
#pragma once
#include <stdexcept>
#include <lua.hpp>
struct MyData {
int num;
};
void *getUserdata(lua_State *L, const char *key) {
lua_pushstring(L, key);
lua_gettable(L, LUA_REGISTRYINDEX);
return lua_touserdata(L, -1);
}
class Test {
public:
Test(lua_State *L)
: data(static_cast<MyData *>(getUserdata(L, "my_name"))) {
if (data == nullptr) {
throw std::runtime_error("invalid userdata at \"my_name\"");
}
};
void setDataNum(int num) { data->num = num; }
private:
MyData *data;
};
此异常无法被 Lua 消化,默认情况下解释器将因 terminate called after throwing an instance of 'std::runtime_error'
而崩溃。这不太好,我们宁愿将异常转换为 Lua 错误。 SWIG 带有 support for that.
test.i
%module example
%{
#include "test.hpp"
%}
%include <exception.i>
%exception {
try {
$action
} catch (std::exception const &e) {
SWIG_exception(SWIG_RuntimeError, e.what());
}
}
%typemap(default) (lua_State *L) { = L; }
%include "test.hpp"
Lua 没有例外,因此没有 try
-catch
块。相反,Lua 具有使用 pcall
进行受保护调用的概念。这将 return 标记调用是否成功以及调用的结果或错误。
local example = require("example")
local success, c = pcall(example.Test)
if (success) then
c:setDataNum(1)
else
print(c)
end
调用示例:
$ swig -c++ -lua test.i
$ clang++ -Wall -Wextra -Wpedantic -I /usr/include/lua5.2 -fPIC -shared test_wrap.cxx -o example.so -llua5.2
$ lua5.2 test.lua
SWIG_RuntimeError:invalid userdata at "my_name"