简体   繁体   中英

Wrapping a Lua object for use in C++ with SWIG

Currently I know how to have C++ objects instantiated and passed around in Lua using SWIG bindings, what I need is the reverse.

I am using Lua & C++ & SWIG.

I have interfaces in C++ and objects in lua, that implement methods which do the same job and have the same structure. I would like to be able to instantiate these objects in lua yet pass them around in C++ using pointers to that interface which they resemble.

As such I can imagine creating a c++ implementation of the interface which would act as a handler for said lua object, yet I don't know how to do this. The class would act as the lua objects representative or proxy in the C++ world.

To clarify I shall start with the following example code used in an answer to a similar question I asked:

C++ code:

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

Lua code:

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

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

Now say that I have a class in C++ called Bank:

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

What I would like here is a mechanism for doing the following in lua:

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

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

If I need to take an extra step such as instantiating a C++ class to act as a proxy and pass it the lua table with all my lua functions etc, I can imagine it looking like this:

C++ code:

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

Lua code:

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)

The problem here being I have no idea how I would implement the ProxyAccount class, or even what the function signature of settable would look like...

I'm not familiar with SWIG (I know what it is but have never used it) so this may not be the answer you are looking for.

I've been working on a C++ project and have had success using luabind . It allows you to subclass C++ objects with Lua objects . You might want to give it a try and see if it works for you.

What I seem to gather from your examples and the discussions is that you are expecting Lua to be the primary language, and C++ to be the client. The problem is, that the Lua C interface is not designed to work like that, Lua is meant to be the client, and all the hard work is meant to be written in C so that Lua can call it effortlessly.

Now, the important question is: why don't you want to have a C representation of the object, and prefer to have it in Lua? Since C++ is a much lower level language, and object definitions must be static, and Lua dynamically defines its "objects" it is much easier to have Lua adapt to C++ objects.

Another issue I see is that you seem to be designing your Lua code in a very Object Oriented manner. Remember that even though Lua can fake Object Oriented concepts, it is not built as an Object Oriented language, and should not be used primarily as one. If you want a fully OO scripting language, use python instead.

Now if you Really want to do it the other way, and considered that the other alternatives do not work for you, then what I would recommend, is that you keep the Lua object as a coroutine, this will allow you to:

  • Keep a representation of the object in C++ (the lua_State *)
  • Have multiple individual instances of the same "object type"
  • Lua takes care of the cleanup

However, the downsides are:

  • All functions that act on an "object" need to do so through the lua API
  • There is no easy/fast way to recognize different lua types (you could use a metatable)
  • Implementation is quite cumbersome, and hard to decypher.

EDIT: Here is how you could expose the interface to an object in a script, Each object instance would be running a new lua_State, and run its script individually, thus allowing for your "Object member data" to just be globals inside the script instance. Implementing the API for the object's methods would go like this:

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*/);
}

You can bind any C function you want to Lua and call it from there. You can define in this function what you expect the contract is between your script and your C++ code. For example, the following would kind of do what you want. You'll need to add meta table information to your Lua tables so you can distinguish different Lua object types.

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.
    }
}

You can take this further with SWIG to bind any arbitrary C method, but it's basically the same.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM