如何使用 SWIG 从 lua 中的 C++ 类继承

Posted

技术标签:

【中文标题】如何使用 SWIG 从 lua 中的 C++ 类继承【英文标题】:How to inherit from C++ class in lua using SWIG 【发布时间】:2016-09-27 09:55:16 【问题描述】:

例如,有一个用 C++ 编写的类:

//Say.h
#pragma once

#include <iostream>

class Say

public:
    Say() 
    virtual ~Say() 
    virtual void SaySomething()  std::cout << "It should not be show..\n"; ;
;

inline void CallCppFun(Say& intf) 
    intf.SaySomething();

我写了 Say.i:

//Say.i
%module Test

%
#include "Say.h"
%

%include "Say.h"

%inline %
inline void CallCppFun(Say& intf);
%

和 main.cpp:

//main.cpp
#include <iostream>

extern "C"

#include <lua.h>
#include <lauxlib.h>
#include <lualib.h>


/* the SWIG wrappered library */
extern "C" int luaopen_Test(lua_State*L);

using namespace std;

int main()

    lua_State *L;
    L = luaL_newstate();
    luaL_openlibs(L);
    printf("[C] now loading the SWIG wrapped library\n");
    luaopen_Test(L);
    if (luaL_loadfile(L, "Test.lua") || lua_pcall(L, 0, 0, 0)) 
        printf("[C] ERROR: cannot run lua file: %s", lua_tostring(L, -1));
        exit(3);
    

    return 0;

然后运行命令:

swig -c++ -lua say.i

我编译自动生成的文件example_wrap.cxx和其他cpp文件并成功链接。

我想在Test.lua中做的是从lua中的C++Say类继承:

-- Test.lua
Test.Say.SaySomething = function(self)
    print("Inherit from C++ in Lua")
end

my = Test.Say()

my:SaySomething() -- doesn't appear to inherit successfully in lua call

Test.CallCppFun(my) -- doesn't appear to inherit successfully in c++ call

在 lua 调用和 c++ 调用中,print 的结果似乎都没有成功继承:

[C] now loading the SWIG wrapped library
It should not be show..
It should not be show..

我知道它支持从 Java 中的 C++ 继承:generating-java-interface-with-swig

我知道这里有类似的问题,但没有回答我面临的具体问题:implementing-and-inheriting-from-c-classes-in-lua-using-swig

Lua 是否支持使用 SWIG 从 lua 中的 C++ 类继承,甚至只使用纯 lua?请显示一些代码示例。 如果 SWIG 无法完成这项工作,是否有一些第三方库支持可以轻松完成?

【问题讨论】:

【参考方案1】:

似乎SWIG with Lua doesn't actually support directors 是跨语言多态工作所必需的。但这不是世界末日,看起来您的 SaySomething 方法是 Say 类中唯一的虚拟方法,因此您可以替换为单个回调函数。 (如果是我,我会在 C++ 接口设计中使用std::function,这样可以简化剩下的工作)。

为了展示这可能如何工作(并在此过程中向我学习一些 Lua!)我整理了一个演示,该演示对问题中的测试用例进行了略微简化。

本质上,我最终所做的是将Callback 类的“in”类型映射修改为使用luaL_ref 来保留对匿名函数的“引用”,前提是给出的输入是“ t 开头的回调类型。因此,我的类型映射会检查它被赋予了什么类型并构造一个临时的、本地类型的实例,该类型继承自 Callback 类型,持有并使用对一些 Lua 定义的代码的引用,直到我们用 lua_pcall 调用它。由于我们只在调用期间保留该实例,因此我们稍后在 argfree 类型映射中对其进行清理。 (我从a Lua mailing list post得到这个想法)

所以我在一个地方演示这一切的界面如下所示:

%module test

%
#include <iostream>
%

%typemap(in) const Callback& (Callback *tmp=NULL) %
  if(lua_isuserdata(L,$argnum)) 
    if (!SWIG_IsOK(SWIG_ConvertPtr(L,$input,(void**)&$1,$descriptor,$disown)))
      SWIG_fail_ptr("$symname",$argnum,$descriptor);
    
  
  else if (lua_isfunction(L,$argnum)) 
    struct Lua$basetype : $basetype 
      Lua$basetype(lua_State* L, int idx) : L(L) 
        lua_pushvalue(L, idx); // This gets popped by luaL_ref
        // retain our argument
        ref = luaL_ref(L, LUA_REGISTRYINDEX);
      
      virtual ~Lua$basetype() 
        // release our reference
        luaL_unref(L, LUA_REGISTRYINDEX, ref);
      
      virtual void run(const std::string& str) const 
         // push our 'reference' onto the stack
         lua_rawgeti(L, LUA_REGISTRYINDEX, ref);
         // manually prepare the function arguments
         lua_pushstring(L, str.c_str());
         // make the actual calll
         lua_pcall(L, 1, 0, 0);
         // TODO: catch error and throw as C++ exception?
      
    private:
      lua_State* L; // Uh, is keeping this around bad?
      int ref;
    ;
    tmp = new Lua$basetype(L,$input);
    $1 = tmp;
  
  else 
    SWIG_fail_arg("$symname",$argnum,"$1_type");
  
%

%typemap(freearg) const Callback& %
  delete tmp$argnum; // Fine, even if NULL remember
%

%inline %
  struct Callback 
    virtual ~Callback() 
    virtual void run(const std::string& str) const  std::cout << "Got string: " << str << "\n"; 
  ;

  void call_me(const Callback& cb) 
    std::cout << "Hello World:\n";
    cb.run("DO IT NOW");
  
%

这已经足够了,我可以将它与以下 Lua 代码一起使用:

require("test")
cb=test.Callback()
test.call_me(cb)
cb=function(s) print("Lua got string: "..s) end
test.call_me(cb)

我可以编译和运行:

swig3.0 -lua -c++ test.i
g++ -Wall -Wextra test_wrap.cxx -shared -o test.so -I /usr/include/lua5.1/
lua run.lua 
Hello World:
Got string: DO IT NOW
Hello World:
Lua got string: DO IT NOW

请注意,这个示例几乎使我编写的 Lua 总量翻了一番,所以你真的应该在生产中使用之前检查我所做的是否合理。如果您更熟悉 Lua,则可能可以使用类似的方法将适当的导向器支持添加到 SWIG,或者更好地模拟它(即一种类型中的多个虚拟函数)。

【讨论】:

以上是关于如何使用 SWIG 从 lua 中的 C++ 类继承的主要内容,如果未能解决你的问题,请参考以下文章

SWIG:Lua - 将 c++ 实例作为 lua 函数参数传递

如何使用 SWIG 从 C++ 调用 Java?

Swig + Lua:调用 m(int argv, char **argc)

如何使用 swig 将 const 字符串参数从 perl 传递到 c++

Swig 和 Lua:如何将 Lua 文件映射到 FILE*

SWIG:如何将 C++ 对象数组从 C# 传递到 C++?