Friday, May 6, 2011

How do I pass a list of objects from C++ to Lua?

I'm the lead dev for Bitfighter, and am adding user-scripted bots using Lua. I'm working with C++ and Lua using Lunar to glue them together.

I'm trying to do something that I think should be pretty simple: I have an C++ object in Lua (bot in the code below), and I call a method on it that (findItems) which causes C++ to search the area around the robot and return a list of objects it finds (TestItems and others not shown here). My question is simply how do I assemble and return the list of found items in C++, and then iterate over them in Lua?

Basically, I want to fill in the <<<< Create list of items, return it to lua >>>> block below, and make any corrections I may need in the Lua code itself, included below that.

I've tried to keep the code simple but complete. Hope there's not too much here! Thanks!

C++ Header file

class TestItem : public LuaObject
{

public:
   TestItem();     // C++ constructor

   ///// Lua Interface

   TestItem(lua_State *L) { } ;             //  Lua constructor

   static const char className[];
   static Lunar<TestItem>::RegType methods[];

   S32 getClassID(lua_State *L) { return returnInt(L, TestItemType); }
};


class LuaRobot : public Robot
{
   LuaRobot();     // C++ constructor

   ///// Lua Interface

   LuaRobot(lua_State *L) { } ;             //  Lua constructor

   static const char className[];
   static Lunar<LuaRobot>::RegType methods[];

   S32 findItems(lua_State *L);
}

C++ .cpp file

const char LuaRobot::className[] = "Robot";      // Class name in Lua
// Define the methods we will expose to Lua
Lunar<LuaRobot>::RegType LuaRobot::methods[] =
{
   method(LuaRobot, findItems),
   {0,0}    // End method list
};


S32 LuaRobot::findItems(lua_State *L)
{
   range = getIntFromStack(L, 1);    // Pop range from the stack
   thisRobot->findObjects(fillVector, range);  // Put items in fillVector

   <<<< Create list of items, return it to lua >>>>

   for(int i=0; i < fillVector.size(); i++)
      do something(fillVector[i]);    // Do... what, exactly?

   return something;
}


/////
const char TestItem::className[] = "TestItem";      // Class name in Lua

// Define the methods we will expose to Lua
Lunar<TestItem>::RegType TestItem::methods[] =
{
   // Standard gameItem methods
   method(TestItem, getClassID),
   {0,0}    // End method list
};

Lua Code

bot = LuaRobot( Robot ) -- This is a reference to our bot

range = 10
items = bot:findItems( range )

for i, v in ipairs( items ) do
    print( "Item Type: " .. v:getClassID() )
end
From stackoverflow
  • So you need to fill a vector and push that to Lua. Some example code follows. Applications is a std::list.

    typedef std::list<std::string> Applications;
    

    I create a table and fill it with the data in my list.

    int ReturnArray(lua_State* L) {
        lua_createtable(L, applications.size(), 0);
        int newTable = lua_gettop(L);
        int index = 1;
        Applications::const_iterator iter = applications.begin();
        while(iter != applications.end()) {
            lua_pushstring(L, (*iter).c_str());
            lua_rawseti(L, newTable, index);
            ++iter;
            ++index;
        }
        return 1;
    }
    

    This leaves me with an array in the stack. If it were returned to Lua, then I could write the following:

    for k,v in ipairs( ReturnArray() ) do
        print(v)
    end
    

    Of course so far, this just gets me a Lua array of strings. To get an array of Lua objects we just tweak your method a bit:

    S32 LuaRobot::findItems(lua_State *L)
    {
        range = getIntFromStack(L, 1);    // Pop range from the stack
        thisRobot->findObjects(fillVector, range);  // Put items in fillVector
    
        // <<<< Create list of items, return it to lua >>>>
    
        lua_createtable(L, fillVector.size(), 0);
        int newTable = lua_gettop(L);
        for(int i=0; i < fillVector.size(); i++) {
            TestItem* item = fillVector[i];
            item->push(L);  // put an object, not a string, in Lua array
            lua_rawseti(L, newTable, i + 1);
        }
        return 1;
    }
    
  • This works perfectly. To clarify to others who are reading this, the method

    item->push(L)
    

    is

    void push(lua_State *L) {  Lunar<TestItem>::push(L, this); }
    

    By encapsulating this in a method, it's possible to make the findItems agnostic to what it's finding.

    Thank you for the help!

    Watusimoto : Wow --it's possible to be a totally different user on a different computer with the same user name! Who knew?

0 comments:

Post a Comment