tags:

views:

151

answers:

2

I have a class with a method called GetEnemiesLua. I have bound this class to lua using SWIG, and I can call this method using my lua code.

I am trying to get the method to return a lua table of objects.

Here is my current code:

void CGame::GetEnemiesLua(){
 std::vector<Unit*> enemies = callback->GetEnemyUnits();
 if( enemies.empty()){
  lua_pushnil(ai->L);
  return;
 } else{
  lua_newtable(ai->L);
  int top = lua_gettop(ai->L);
  int index = 1;

  for (std::vector<Unit*>::iterator it = enemies.begin(); it != enemies.end(); ++it) {

   //key
   lua_pushinteger(ai->L,index);//lua_pushstring(L, key);

   //value
   CUnit* unit = new CUnit(callback,*it,this);
   ai->PushIUnit(unit);
   lua_settable(ai->L, -3);
   ++index;
  }
  ::lua_pushvalue(ai->L,-1);
 }
}

PushIUnit is as follows:

void CTestAI::PushIUnit(IUnit* unit){
 SWIG_NewPointerObj(L,unit,SWIGTYPE_p_IUnit,1);
}

To test this I have the following code:

 t = game:GetEnemiesLua()
 if t == nil then
  game:SendToConsole("t is nil! ")
 end

The result is always 't is nil', despite this being incorrect. I have put breakpoints in the code and it is indeed going over the loop, rather than doing lua_pushnil.

So how do I make my method return a table when called via lua?

+1  A: 

God only knows what SWIG is doing (the Lua API is so simple that I avoid SWIG and its little friends), but somewhere you need to communicate to Lua that you are not only leaving a table on the top of the stack, but that you want to return that table. If you were writing the C code yourself return 1; would do it. I don't know how to persuade SWIG to get it to return a value for you, but I bet a return type of void is not doing you any favors.

You might try working around SWIG and just create a function with prototype

int lua_get_enemies(lua_State *L);

If you can then get your inner stuff to work, just ending the routine with return 1; and the table on the top of the stack might do it.

Norman Ramsey
if I changed it to return 1, then in my teast code, t would not be a table as I want, but a number literal, and printing it would yield the string '1'
Tom J Nowell
@Tom: I feared as much. Well, I hope somebody comes along who understands the complexities of SWIG. You almost certainly need to replace that `void` return type with something else. But do consider trying the straight Lua API---it's really quite pleasant, and it's easy to figure stuff out.
Norman Ramsey
+1  A: 

Your 'GetEnemies' function returns void which SWIG will take literally, throwing away any values you attempt to return. You'll want to specify your C function to either return an array or take a pointer to one.

For example,

std::vector<Unit*>& CGame::GetEnemiesLua()
{
    return callback->GetEnemyUnits();
}

Next, tell SWIG how to interpret this out value:

// convert the return value into a Lua table
%typemap(argout, noblock=1) std::vector<Unit*>&
{
     SWIG_arg += ConvertEnemyVectorToLuaTable(*$1);
}

Your conversion should return '1' to indicate you pushed a single table onto the stack.

int ConvertEnenyVectorToLuaTable(std::vector<Unit*>& enemies)
{
    if(enemies->empty())
    {
        lua_pushnil(ai->L);
        return 1;  // you did push a NIL value
    } 
    else
    {
        lua_newtable(ai->L);
        int top = lua_gettop(ai->L);
        int index = 1;

        for (std::vector<Unit*>::iterator it = enemies.begin(); 
             it != enemies.end(); 
             ++it) 
        {
            //key
            lua_pushinteger(ai->L,index);

            //value
            ai->PushIUnit(*it);

            // set the table entry
            lua_settable(ai->L, -3);

            ++index;
         }

        // push the new table
        ::lua_pushvalue(ai->L,-1);
        return 1;
    }
}
Aaron Saarela
Now this is a far more elegant solution ^_^
Tom J Nowell