如何处理 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"