How to work with tables passed as an argument to a lua C function?

11,440

When working with the Lua C API it's important to get comfortable working with the virtual stack -- all the important language boundary interactions happen there. Looking at your code snippet, it does not look like you're marshaling the data properly over to C.

When writing a lua C function you basically have to do 3 things:

  • Convert input lua data into something you can work with in C.
  • Perform the processing or whatever the function needs to do.
  • Convert and return the output result if any back to lua.

As an example, here's what your findImgProxy should look like:

static int findImgProxy(lua_State *L)
{
  // discard any extra arguments passed in
  lua_settop(L, 1);
  luaL_checktype(L, 1, LUA_TTABLE);

  // Now to get the data out of the table
  // 'unpack' the table by putting the values onto
  // the stack first. Then convert those stack values
  // into an appropriate C type.
  lua_getfield(L, 1, "imagePath");
  lua_getfield(L, 1, "fuzzy");
  lua_getfield(L, 1, "ignoreColor");
  // stack now has following:
  //   1  = {imagePath="/var/image.png", fuzzy=0.5, ignoreColor=0xffffff}
  //   -3 = "/var/image.png"
  //   -2 = 0.5
  //   -1 = 0xffffff

  const char *imagePath = luaL_checkstring(L, -3);
  double fuzzy    = luaL_checknumber(L, -2);
  int ignoreColor = luaL_checkint(L, -1);
  // we can pop fuzzy and ignoreColor off the stack
  // since we got them by value
  lua_pop(L, 2);

  // do function processing
  // ...

  return 1;
}

Note that we must keep imagePath on the stack since we're holding a const char * to it. Popping that string off would invalidate *imagePath since lua might collect it.

Alternatively, you can copy the string returned by luaL_checkstring into another buffer. Popping the string off in this case is ok since we're no longer pointing to an internal buffer owned by lua.

Edit: If some of the keys in the table are optional, you can use the luaL_opt* functions instead and provide defaults. For example, if fuzzy and ignoreColor are optional:

  // ...
  const char *imagePath = luaL_checkstring(L, -3);
  double fuzzy    = luaL_optnumber(L, -2, 0.0); // defaults to 0.0 if no fuzzy
  int ignoreColor = luaL_optint(L, -1, 0);      // defaults to 0 if no ignoreColor
  // ...

So if the calling code provides a nonsensical value for a key, this will still raise an error. OTOH, if it's absent then the value is nil and the default provided is used instead.

Share:
11,440

Related videos on Youtube

Suge
Author by

Suge

Updated on September 16, 2022

Comments

  • Suge
    Suge over 1 year

    I'm going to implement a function with C language and which will be called by Lua script.

    This function should receive a lua table as the argument, so I should read the fields in the table.I try to do like below, but my function is crashing when I run it. Can anyone help my find the problem?

    
    /*
     function findImage(options)
        imagePath = options.imagePath
        fuzzy = options.fuzzy
        ignoreColor = options.ignoreColor;
    
    
     end
    
     Call Example:
    
      findImage {imagePath="/var/image.png", fuzzy=0.5, ignoreColor=0xffffff}
    
     */
    
    
    // implement the function by C language
    static int findImgProxy(lua_State *L)
    {
        luaL_checktype(L, 1, LUA_TTABLE);
    
        lua_getfield(L, -1, "imagePath");
        if (!lua_isstring(L, -1)) {
            error();
        }
        const char * imagePath = lua_tostring(L, -2);
        lua_pop(L, 1);
    
        lua_getfield(L, -1, "fuzzy");
        if (!lua_isnumber(L, -1)) {
            error();
        }
        float fuzzy = lua_tonumber(L, -2);
    
        lua_getfield(L, -1, "ignoreColor");
        if (!lua_isnumber(L, -2)) {
            error();
        }
        float ignoreColor = lua_tonumber(L, -2);
    
        ...
    
        return 1;
    }
    
    

    How about return a table from C to Lua:

    
    struct Point {
        int x, y;
    }
    typedef Point Point;
    
    
    static int returnImageProxy(lua_State *L)
    {
        Point points[3] = {{11, 12}, {21, 22}, {31, 32}};
    
        lua_newtable(L);
    
        for (int i = 0; i  3; i++) {
            lua_newtable(L);
            lua_pushnumber(L, points[i].x);
            lua_rawseti(L, -2, 0);
            lua_pushnumber(L, points[i].y);
            lua_rawseti(L, -2, 1);
            lua_settable(L,-3);
        }
    
        return 1;   // I want to return a Lua table like :{{11, 12}, {21, 22}, {31, 32}}
    }
    
    
  • Suge
    Suge over 10 years
    @ greatwolf thank you very much, I've never got an answer so clear and helpful like yours.Generally I've understood.But I have another problem, I should return a table from the C function to lua, but my code(added above) doesn't work, could you have a look?Thank you so much.
  • Suge
    Suge over 10 years
    And if the key's number of the argument table is not fixed, the table may be {imagePath="/var/q.png"} or may be {imagePath="/var/q.png", fuzzy=1.0} or may be {imagePath="/var/q.png", ignoreColor=0xffffff}.How can I get the values?
  • greatwolf
    greatwolf over 10 years
    @Suge if some of the keys are optional you can use luaL_opt* functions to get them.
  • greatwolf
    greatwolf over 10 years
    @Suge I've added an example for this.
  • Suge
    Suge over 10 years
    Thank you very much, I've got it.Could you take a look at the code above I use to return a table from C function?
  • greatwolf
    greatwolf over 10 years
    @Suge you're doing lua_settable on the wrong index. lua_rawseti pops a value off so the outter table is at index -2.
  • greatwolf
    greatwolf over 10 years
    @Suge try lua_rawseti(L, -2, i + 1); instead
  • lhf
    lhf over 10 years
    I think it'd be clearer to use lua_getfield(L,1, ...) for all 3 calls.
  • Suge
    Suge over 10 years
    How to use lua_getfield(L,1, ...) for all 3 calls?
  • greatwolf
    greatwolf over 10 years
    @Suge he's referring to my 'getfield' usage in my first example. using absolute index rather than relative makes it a bit clearer.
  • Suge
    Suge over 10 years
    @greatwolf, thank you very much for your help.Would you help me with another question, if the ignoreColor is also a table such as {imagePath="/var/image.png", fuzzy=0.5, ignoreColor={0xffffff, 0x2d2d2d, 0x0000ff}}, the fuzzy and ignoreColor are optional, and the element number of ignoreColor is unsure. How can I get the values in ignoreColor?Thanks for all your help:)
  • greatwolf
    greatwolf over 10 years
    @Surge I'm not sure what you mean by element number. Are you asking how to provide a default table if the ignoreColor table isn't given?
  • Suge
    Suge over 10 years
    @greatwolf, thank you for reply. I mean in this table {imagePath="/var/image.png", fuzzy=0.5, ignoreColor={0xffffff, 0x2d2d2d, 0x0000ff}}, the value of ignoreColor is an array(table), but the member count of ignoreColor is unsure. Meanwhile the fuzzy and ignoreColor are optional.I mean, if ignoreColor is given, it's value is an array(table).At this sense, how to get all the values inside {imagePath="/var/image.png", fuzzy=0.5, ignoreColor={0xffffff, 0x2d2d2d, 0x0000ff}}?
  • greatwolf
    greatwolf over 10 years
    @Suge To get the values out of ignoreColor you unpack it to the stack similar to the outer table. You can use lua_rawgeti on 'ignoreColor' table. To get the element count of 'ignoreColor' use lua_objlen.
  • greatwolf
    greatwolf over 10 years
    @Suge you might find this helpful as a reference.
  • Suge
    Suge over 10 years
    @greatwolf, thank you so much, would have a look at my code here: stackoverflow.com/questions/18637314/…
  • greatwolf
    greatwolf over 10 years
    Is there still something else you need addressed? If not you should accept this as an answer if it solves your problem satisfactory.