5

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

greatwolf
  • 20,287
  • 13
  • 71
  • 105
Suge
  • 2,808
  • 3
  • 48
  • 79
  • "*something wrong*" *What* is wrong? What do you expect it to do and what is it doing that isn't correct? – Nicol Bolas Aug 28 '13 at 02:21
  • I want to read the lua table in C function, but it's crashed, I'm still finding the problem.So I want someone can help me check if the way right I used on. – Suge Aug 28 '13 at 02:26

1 Answers1

13

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.

greatwolf
  • 20,287
  • 13
  • 71
  • 105
  • @ 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 Aug 28 '13 at 05:23
  • 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? – Suge Aug 28 '13 at 06:28
  • @Suge if some of the keys are optional you can use `luaL_opt*` functions to get them. – greatwolf Aug 28 '13 at 07:08
  • @Suge I've added an example for this. – greatwolf Aug 28 '13 at 07:15
  • 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? – Suge Aug 28 '13 at 07:17
  • @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 Aug 28 '13 at 07:21
  • @Suge try `lua_rawseti(L, -2, i + 1);` instead – greatwolf Aug 28 '13 at 07:29
  • I think it'd be clearer to use `lua_getfield(L,1, ...)` for all 3 calls. – lhf Aug 28 '13 at 10:51
  • How to use `lua_getfield(L,1, ...)` for all 3 calls? – Suge Aug 28 '13 at 11:27
  • @Suge he's referring to my 'getfield' usage in my first example. using absolute index rather than relative makes it a bit clearer. – greatwolf Aug 28 '13 at 21:21
  • @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:) – Suge Sep 05 '13 at 02:52
  • @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? – greatwolf Sep 05 '13 at 05:40
  • @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}}? – Suge Sep 05 '13 at 13:02
  • @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 Sep 05 '13 at 13:09
  • @Suge you might find **[this helpful](http://pgl.yoyo.org/luai/i/_)** as a reference. – greatwolf Sep 05 '13 at 13:19
  • @greatwolf, thank you so much, would have a look at my code here: http://stackoverflow.com/questions/18637314/why-cant-i-read-a-nested-lua-table-as-argument-from-c-function – Suge Sep 05 '13 at 13:21
  • Is there still something else you need addressed? If not you should accept this as an answer if it solves your problem satisfactory. – greatwolf Sep 06 '13 at 21:19