使用SWIG包装Lua对象以在C ++中使用

时间:2009-11-24 20:21:55

标签: c++ scripting lua swig

目前我知道如何使用SWIG绑定在Lua中实例化和传递C ++对象,我需要的是反过来。

我正在使用Lua& C ++& SWIG。

我在C ++中有接口,在lua中有对象,它实现了执行相同工作并具有相同结构的方法。我希望能够在lua中实例化这些对象,然后使用指向它们类似的接口的指针在C ++中传递它们。

因此我可以想象创建一个接口的c ++实现,它将充当所述lua对象的处理程序,但我不知道如何做到这一点。该类将充当C ++世界中的lua对象代表或代理。

为了澄清我将从以下示例代码开始,该代码用于回答我提出的类似问题:

C ++代码:

// Represents a generic bank account
class Account {
    virtual void deposit(double amount) = 0;
};

Lua代码:

SavingsAccount = { balance = 0 }
SavingsAccount.deposit = function(amount)
    SavingsAccount.balance = SavingsAccount.balance + amount
end

-- Usage
a = SavingsAccount
a.balance = 100
a.deposit(1000)

现在说我在C ++中有一个名为Bank的课程:

class Bank {
    void AddAccount(Account* a);
};

我想要的是在lua中执行以下操作的机制:

SavingsAccount = { balance = 0 }
SavingsAccount.deposit = function(amount)
    SavingsAccount.balance = SavingsAccount.balance + amount
end

-- Usage
a = SavingsAccount
bank:AddAccount(a)

如果我需要采取额外的步骤,例如实例化C ++类充当代理并将lua表传递给我所有的lua函数等,我可以想象它看起来像这样:

C ++代码:

// Represents a generic bank account
class ProxyAccount : public Account {
    virtual void deposit(double amount);
};

Lua代码:

SavingsAccount = { balance = 0 }
SavingsAccount.deposit = function(amount)
    SavingsAccount.balance = SavingsAccount.balance + amount
end

-- Usage
a = SavingsAccount
a.balance = 100
a.deposit(1000)

proxy = program.ProxyAccount()
proxy.settable(a)
bank:AddAccount(p)

这里的问题是我不知道如何实现ProxyAccount类,甚至是settable的函数签名看起来像......

3 个答案:

答案 0 :(得分:3)

我不熟悉SWIG(我知道它是什么但从未使用过)所以这可能不是你想要的答案。

我一直致力于C ++项目,并且使用luabind取得了成功。它允许你subclass C++ objects with Lua objects。您可能想尝试一下,看看它是否适合您。

答案 1 :(得分:2)

我似乎从您的示例和讨论中收集的内容是您希望Lua成为主要语言,而C ++将成为客户端。问题是,Lua C界面的设计并不像那样,Lua意味着是客户端,而所有的努力工作都是用C语言编写的,这样Lua可以毫不费力地调用它。

现在,重要的问题是:为什么你不想拥有该对象的C表示,并且更喜欢在Lua中拥有它?由于C ++是一种低级语言,并且对象定义必须是静态的,并且Lua动态定义其“对象”,因此Lua更容易适应C ++对象。

我看到的另一个问题是,您似乎正在以非常面向对象的方式设计Lua代码。请记住,即使Lua可以伪造面向对象的概念,它也不是作为面向对象的语言构建的,不应该主要用作一个。如果您需要完全OO脚本语言,请改用python。

现在,如果你真的想要以另一种方式做到这一点,并认为其他选择不适合你,那么我建议你将Lua对象保存为协程,这将允许你:

  • 保留对象的表示 在C ++中(lua_State *)
  • 拥有相同“对象类型”的多个单独实例
  • Lua负责清理工作

然而,缺点是:

  • 所有作用于“对象”的函数 需要通过lua API
  • 这样做
  • 没有简单/快速的方法来识别不同的lua类型(你 可以使用metatable)
  • 实施非常繁琐,而且很难解除狡猾。

修改 以下是如何将接口公开给脚本中的对象,每个对象实例将运行一个新的lua_State,并单独运行其脚本,从而允许您的“对象成员数据”只是脚本实例中的全局变量。为对象的方法实现API将如下所示:

int move(lua_State * L)
{
  int idx = lua_getglobal(L, "this");
  assert(!lua_isnull(-1));
  AIObject * obj = static_cast<AIObject *>(lua_touserdata(L, -1));
  lua_pop(1);
  //Pop the other parameters
  obj->move(/*params*/);
}

答案 2 :(得分:1)

你可以将你想要的任何C函数绑定到Lua并从那里调用它。您可以在此函数中定义您希望在脚本和C ++代码之间签订合同的内容。例如,以下内容可以满足您的需求。您需要将元表信息添加到Lua表中,以便区分不同的Lua对象类型。

int lua_AddBankAccount(lua_State* L, int pos)
{
    // Assume you've created metadata for your Lua objects.
    if (IsAccount(L, pos))
    {
       // process your 'Account' Lua instance.
    }
    else
    {
       // error - tried to add a non-Account.
    }
}

您可以使用SWIG进一步绑定任何任意C方法,但它基本相同。