如何在 Lua 中处理 C++ 类构造的失败

我使用 SWIG 来绑定 C++ 类,以便在 Lua 中使用它们。

我想知道是否可能在 Lua 中处理 C++ 类构造失败。

例如,我有以下 Test 类,在构造时尝试获取用户数据。

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() 返回 nullptr,调用 setDataNum() 将使我的应用程序崩溃。

我想知道是否有一种方法来检测和处理构造失败(在这种情况下,data 变成了 nullptr),以便该类不会在 Lua 中创建。

点赞
用户1944004
用户1944004

第一步是进行错误处理,需要检查是否存在错误。从Lua参考手册中可以得到以下信息:

void *lua_touserdata (lua_State *L, int index);

如果给定索引处的值为完全 userdata,返回其块地址。如果该值为 light userdata,则返回其指针。否则,返回 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 提供了 支持

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) { $1 = L; }
%include "test.hpp"

Lua 没有异常处理和 try- catch 块的概念。相反,Lua 采用了保护调用的概念,并提供了 pcall 函数。这将返回一个标志,指示调用是否成功以及调用的结果或错误信息。

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"
2018-07-27 06:32:43