2016-09-27 4 views
1

Например, есть класс, написанный на C++:Как наследовать от класса C++ в Lua, используя SWIG

//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, чтобы наследовать от C++ Say класса в Lua:

-- 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++ называют:

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

Я знаю, это поддержка в наследоваться от C++ в Java: generating-java-interface-with-swig

Я знаю, что есть подобный вопрос здесь, но не г Ответ на конкретную проблему, с которой я сталкиваюсь: implementing-and-inheriting-from-c-classes-in-lua-using-swig

Поддерживает ли Lua наследование класса C++ в lua с помощью SWIG или даже просто использует чистую lua? Пожалуйста, покажите пример кода. Если SWIG не может выполнить эту работу, есть ли у нее поддержка сторонней библиотеки, чтобы сделать это легко?

ответ

0

Кажется, что SWIG with Lua doesn't actually support directors, который необходим для полиморфизма на разных языках. Это еще не конец света, похоже, что ваш метод SaySomething является единственным виртуальным методом в классе Say, поэтому вместо него можно заменить одну функцию обратного вызова. (И если бы это был я, я использовал бы std::function в дизайне интерфейса C++, что упростит часть остальной части этой работы).

Чтобы показать, как это может работать (и узнайте, что у меня есть Lua в этом процессе!), Я собрал демо, которое немного упрощено по вашему тестовому сценарию в вопросе.

В сущности, то, что я закончил делать, это модификация 'in' typemap для класса Callback, который будет использовать luaL_ref, чтобы сохранить ссылку на анонимную функцию, если введенный ввод не относится к тип обратного вызова для начала. Поэтому моя типовая карта проверяет, какой тип она была предоставлена, и создает экземпляр временного локального типа, который наследуется от типа 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, которую я написал когда-либо, поэтому вы действительно должны проверить, что я сделал, разумно перед использованием в производстве. Вероятно, можно было бы добавить соответствующую поддержку директора в SWIG, используя что-то вроде этого, или эмулировать его лучше (т. Е. Более чем одну виртуальную функцию в типе), если вы более знакомы с Lua.

Смежные вопросы