views:

431

answers:

3

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

A: 

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.

Aaron Saarela
This doesn't really help me since I don't want to bind C functions, I'm working in C++ with objects. Also I would not know how to obtain the 'pos' variable, how to implement IsAccount, or how to actually use the table at 'pos'.Thus how do I actually process my 'Account' lua instance?
Tom J Nowell
From your original question, you seems to be creating an interface class in C++ and attempting to create "subclasses" of this interface in Lua. Your question suggests that subclasses do not have C++ implementations but only Lua implementations. You will need at least one C interface to bind in Lua that you can call from Lua and pass your object back into your C++ application. The above assumes you're using SWIG to generate this method, which will bind C arguments to Lua stack positions (as specified by pos). Dig into the SWIG Lua binding implementation; there's a lot there that you can use.
Aaron Saarela
I keep getting pointed back to the same document over and over again as people misunderstand what I want and assume I'm wrapping a c++ object for use in lua, when I actually want it the other way around.
Tom J Nowell
Lua 'objects' do not have C++ equivalents. What you need to do is defined the protocol used when passing Lua objects into C++. This should be pretty straight forward using some Lua metatable information for your Lua 'classes'. Once you define the metatable information, you need some code on the C++ side to identify these classes.
Aaron Saarela
the 'equivilants' are conceptual, aka a lua table representing A and an associated C++ class/structure representing A. The person answering the question kind of has free reign in suggesting the protocol and mechanics of how it would work, since the question is primarily a 'how' question.
Tom J Nowell
+1  A: 

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.

Zack Mulgrew
this looks interesting, Ill look into it, do you have any notes on the performance of subclassing in lua via luabind?
Tom J Nowell
I am currently using it to create all of the "entities" in a game I'm working on. I haven't got everything up and running yet, but so far I have been happy with the performance. That being said, I'm not at a point where I've been measuring performance too much so it's hard to say. I have read that there is definitely overhead using luabind vs. the Lua C API, but at this point in my project it's not an issue.
Zack Mulgrew
A: 

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*/);
}
Ramon Zarazua
In one project I have I could move a lot of stuff in lua, and not have to wrap them with C++ objects. I would loose some flexibility but I would be able t move the necessary components into lua and regain some of what I've lost
Tom J Nowell
HOWEVER, I have other projects that do not have this luxury, for example I have an RTS AI project where I intend to implement 'widgets' or 'plugs' in lua to extend a units behaviour. As such I need to be able to associate this lua code with that object, so there would be tables filled with methods and code I would need to associate with my various behaviour and task classes, as well as work with them in my existing infrastructure
Tom J Nowell
I will give you the question since you have provided a solution, though I'm curious as to what better ways you could suggest especially for the AI situation
Tom J Nowell
Game programming Gems 6 has two good articles on this, I would strongly recommend you take a look at them. Unfortunately I do not have the book with me at the time. I used a very different approach for my implementation, so I don't quite remember how the book did it.
Ramon Zarazua
Off hand however, I would recommend that you use a C++ object for your AI object, and expose the interface into lua by keeping the this pointer of the objects as light user data, for example://Moved to the post for code formatting
Ramon Zarazua