Lua 5.3を素早く把握する-あなたのプログラムを拡張する(2)


Q:Luaの関数をCで呼び出すには?
A:1、Lua関数をスタックに入れる.2、パラメータをパラメータ順にスタックに順次入れる.3、関数を呼び出します.呼び出しが完了すると、関数のパラメータとLua関数がスタックされ、関数の戻り値がスタックされます.4、関数の戻り値を取得します.「config.lua」ファイル:
function f(x, y)
    return (x ^ 2 * math.sin(y)) / (1 - x)
end

main.cファイル:
#include 
#include 
#include 
#include 
#include 
#include 

void error(lua_State *L, const char *fmt, ...)
{
    va_list argp;
    va_start(argp, fmt);
    vfprintf(stderr, fmt, argp);
    va_end(argp);
    lua_close(L);
    exit(EXIT_FAILURE);
}

void load(lua_State *L, char *filename)
{
    if(luaL_loadfile(L, filename) || lua_pcall(L, 0, 0, 0))
        error(L, "cannot run configuration file: %s", lua_tostring(L, -1));
}

//       Lua     。
double f(lua_State *L, double x, double y)
{
    double z = 0.0;

    lua_getglobal(L, "f");    //     Lua         Lua  ,     。
    //      Lua            。
    lua_pushnumber(L, x);
    lua_pushnumber(L, y);

    if(lua_pcall(L, 2, 1, 0) != 0)    //          ,2   ,1    。
        error(L, "error running function `f': %s", lua_tostring(L, -1));

    if(!lua_isnumber(L, -1))    //         。
        error(L, "function `f' must return a number");
    z = lua_tonumber(L, -1);
    lua_pop(L, 1);    //      ,               。

    return z;
}

int main(void)
{
    lua_State *L = luaL_newstate();
    luaL_openlibs(L);

    load(L, "config.lua");
    printf("%f
"
, f(L, 0.5, 0.5)); lua_close(L); return 0; }
prompt> gcc main.c -llua -ldl -lm -Wall
prompt> ./a.out
0.239713

Q:CでLua関数を呼び出す形で、C++のような関数のリロードを実現するにはどうすればいいですか?
A:上記の例のプログラムを拡張し続けます(この例ではJNIの呼び出しに似ているものもあります)、「main.c」ファイルでは、
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

void error(lua_State *L, const char *fmt, ...)
{
    va_list argp;
    va_start(argp, fmt);
    vfprintf(stderr, fmt, argp);
    va_end(argp);
    lua_close(L);    //   Lua   。
    exit(EXIT_FAILURE);    //      。
}

void load(lua_State *L, char *filename)
{
    if(luaL_loadfile(L, filename) || lua_pcall(L, 0, 0, 0))
        error(L, "cannot run configuration file: %s", lua_tostring(L, -1));
}

/* "func":     Lua     。
 * "sig":           。
   'd': double。
   'i': integer。
   's': string。
   '>':             。         ,    。
 * "...":          。
 */
void call_va(lua_State *L, const char *func, const char *sig, ...)
{
    va_list vl;
    int narg = 0, nres = 0;    //              。

    va_start(vl, sig);
    lua_getglobal(L, func);    //  Lua    。

    narg = 0;
    while(*sig)    //          ,         。
    {
        switch(*sig++)
        {
            case 'd':  /* double argument */
                lua_pushnumber(L, va_arg(vl, double));
                break;

            case 'i':  /* int argument */
                lua_pushinteger(L, va_arg(vl, int));
                break;

            case 's':  /* string argument */
                lua_pushstring(L, va_arg(vl, char *));
                break;

            case '>':    //         ,  "while"。
                goto endwhile;

            default:
                error(L, "invalid option(%c)", *(sig - 1));
        }
        narg++;
        /*       1。      (          ),   。
         *                    ("NULL"       )。
         *            ,           ,            。
         */
        luaL_checkstack(L, 1, "too many arguments");
    }
endwhile:

    nres = strlen(sig);    //         。
    if(lua_pcall(L, narg, nres, 0) != 0)
        error(L, "error running function `%s': %s", func, lua_tostring(L, -1));

    nres = -nres;    //        ,                  。
    while(*sig)    //      。
    {
        switch(*sig++)
        {
            case 'd':  /* double result */
                if(!lua_isnumber(L, nres))
                    error(L, "wrong result type");
                *va_arg(vl, double *) = lua_tonumber(L, nres);
                break;

            case 'i':  /* int result */
                if(!lua_isinteger(L, nres))
                    error(L, "wrong result type");
                *va_arg(vl, int *) =(int)lua_tointeger(L, nres);
                break;

            case 's':  /* string result */
                if(!lua_isstring(L, nres))
                    error(L, "wrong result type");
                *va_arg(vl, const char **) = lua_tostring(L, nres);
                break;

            default:
                error(L, "invalid option(%c)", *(sig - 1));
        }
        nres++;
    }
    va_end(vl);
}

int main(void)
{
    double z = 0.0f;
    lua_State *L = luaL_newstate();

    luaL_openlibs(L);

    load(L, "config.lua");
    //   Lua "f"  ,         ,         ,          "z"  。
    call_va(L, "f", "ds>d", 0.5, "0.5", &z);
    printf("%f
"
, z); lua_close(L); return 0; }
prompt> gcc main.c -llua -ldl -lm -Wall
prompt> ./a.out
0.239713

追加:
1、call_va()で指定された「func」がLuaの有効な関数であるか否かを判断する必要はなく、lua_pcall()でこのエラーをキャプチャすることができる.2.Lua関数の戻り値は文字列である可能性があるため、call_va()では戻り値をスタックから出すことができない.この作業では、呼び出し元が戻り値を転送した後、手動で結果をスタックから出す必要があります.