【问题标题】:Creating a SWIG typemap for function that returns vector of pairs为返回对向量的函数创建 SWIG 类型映射
【发布时间】:2019-02-06 04:10:19
【问题描述】:

我正在尝试创建一个在 Lua 中返回对向量的 getter 函数。

我在 C++ 中有以下矢量数据:

{{1, "a"}, {2, "b"}, {3, "c"}}

我想将此向量作为 Lua 中的表返回,以便它可以与 Lua 中的下表 t 相同:

local t = {};
t[1].value = 1
t[1].name = "a"
t[2].value = 2
t[2].name = "b"
t[3].value = 3
t[3].name = "c"

这是我的代码:

MyBindings.h

#include "main.h"

class MyClass
{
public:
    MyClass()
    :MyData({{1, "a"}, {2, "b"}, {3, "c"}}){}

    void getMyData(std::vector<pair<float, std::string>> *datap)
    {
        *datap = MyData;
    }
    std::vector<pair<float, std::string>> MyData;
};

MyBindings.i

%module my
%{
    #include "MyBindings.h"
%}

%include <stl.i>
%include <typemaps.i>
%include <std_string.i>
%include <std_vector.i>

/* convert the output std::vector<pair<float, std::string>> to lua_Table */
%typemap(in, numinputs = 0) (std::vector<pair<float, std::string>> *datap) 
(std::vector<pair<float, std::string>> *tdatap = nullptr) 
%{
%}
%typemap(argout) (std::vector<pair<float, std::string>> *datap) 
{
    lua_newtable(L);
    for (size_t i = 0; i < $1->size(); ++i)
    {
        lua_newtable(L);
        lua_pushinteger(L, static_cast<lua_Number>($1->at(i).first));
        lua_setfield(L, -2, "value");
        lua_pushstring(L, $1->at(i).second.c_str());
        lua_setfield(L, -2, "name");
        lua_rawseti(L, -2, i + 1);
    }
    SWIG_arg++;
}

%include "MyBindings.h"

main.cpp

#include "main.h"
#include "lua.hpp"

extern "C"
{
    int luaopen_my(lua_State *L);
}

int main()
{
    lua_State *L = luaL_newstate();
    luaL_openlibs(L);
    luaopen_my(L);
    lua_settop(L, 0);
    luaL_dostring(L, "local c = my.MyClass()\n"
                     "local t = c:getMyData()\n"
                     "print('Value : ' .. t[2].value)\n"
                     "print('Name : ' .. t[2].name)\n");
    lua_close(L);
}

我想要的结果:

Value : 2
Name : b

我得到的结果:

向量:线程 1:EXC_BAD_ACCESS(代码=1,地址=0x10)

我应该如何更改我的代码以获得我想要的结果?

【问题讨论】:

  • (1) *datap = MyData 泄漏内存,您可能需要datap = &amp;MyData。 (2) 一定要用argout吗?从函数返回更容易。
  • @HenriMenke 我不需要使用任何东西。只要能用就没问题。
  • 那真的很容易解决。我会尽快发布答案。
  • 顺便说一句,问题是*datap = MyData。您在此处取消引用无效指针。但是无论如何您都不应该使用此解决方案,请参阅我的答案以获取替代方案。

标签: c++ lua swig typemaps


【解决方案1】:

基本上解决方案是不使用输出参数 (argout)。这实际上是对 C++ 编程的一般建议。在 C 时代,当您无法轻松地从函数按值返回数组时,输出参数是必需的。

经过一点重构,我最终得到以下结果:

MyBindings.h

#include <string>
#include <utility>
#include <vector>

class MyClass {
    std::vector<std::pair<float, std::string>> m_data;
public:
    MyClass() : m_data({{1, "a"}, {2, "b"}, {3, "c"}}) {}

    std::vector<std::pair<float, std::string>> data() { return m_data; }
};

MyBindings.i

%module my
%{
    #include "MyBindings.h"
%}

%typemap(out) std::vector<std::pair<float, std::string>>
{
    lua_newtable(L);
    for (size_t i = 0; i < $1.size(); ++i)
    {
        lua_newtable(L);
        lua_pushinteger(L, static_cast<lua_Number>($1.at(i).first));
        lua_setfield(L, -2, "value");
        lua_pushstring(L, $1.at(i).second.c_str());
        lua_setfield(L, -2, "name");
        lua_rawseti(L, -2, i + 1);
    }
    SWIG_arg++;
}

%include "MyBindings.h"

main.cpp

#include "lua.hpp"

extern "C"
{
    int luaopen_my(lua_State *L);
}

int main()
{
    lua_State *L = luaL_newstate();
    luaL_openlibs(L);
    luaopen_my(L);
    lua_settop(L, 0);
    luaL_dostring(L, "local c = my.MyClass()\n"
                     "local t = c:data()\n"
                     "print('Value : ' .. t[2].value)\n"
                     "print('Name : ' .. t[2].name)\n");
    lua_close(L);
}

调用示例:

$ swig -c++ -lua MyBindings.i
$ clang++ -Wall -Wextra -Wpedantic -std=c++11 -I/usr/include/lua5.3 MyBindings_wrap.cxx main.cpp -llua5.3
$ ./a.out 
Value : 2
Name : b

【讨论】:

  • 非常感谢您的回答。它就像一个魅力。明天我会给你一个赏金。
  • 我发现即使在我从MyBindings.i 中删除%include &lt;stl.i&gt; %include &lt;typemaps.i&gt; %include &lt;std_string.i&gt; %include &lt;std_vector.i&gt; 之后它仍然有效。这些包括只是没有必要吗?
  • @ZackLee 这些只有在实际使用它们定义的类型映射时才需要。在这个简单的示例中,您只使用了一个您自己定义的类型映射,因此所有这些 %include 都未使用。
  • @ZackLee 这实际上适用于任何包含,无论是 SWIG 中的 %include 还是 C++ 中的 #include。如果您不使用该标题中的任何内容,只需将其删除。
  • @ZackLee 这只是从您的问题中复制和粘贴的,但是是的,应该是 lua_pushnumber
猜你喜欢
  • 1970-01-01
  • 2018-11-06
  • 2019-01-09
  • 1970-01-01
  • 2019-12-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多