1. ホーム
  2. スクリプト・コラム
  3. ルア

Luaチュートリアル(XIX)。C言語からLuaへの呼び出し

2022-02-12 01:21:43

1. 基本的なこと

    Luaの重要な用途として、設定言語としての利用が挙げられます。まずは簡単な例から見ていきましょう。

コピーコード コードは以下の通りです。

    -- Here is the configuration information for the window size defined in Lua code
    width = 200
    height = 300

    以下は、設定情報を読み込むためのC/C++のコードです。  

コピーコード コードは以下の通りです。

#include <stdio.h>
#include <string.h>
#include <lua.hpp>
#include <luxlib.h>
#include <lualib.h>

void load(lua_State* L, const char* fname, int* w, int* h) {
    if (luaL_loadfile(L,fname) || lua_pcall(L,0,0,0)) {
        printf("Error Msg is %s.\n",lua_tostring(L,-1));
        return;
    }
    lua_getglobal(L,"width");
    lua_getglobal(L,"height");
    if (!lua_isnumber(L,-2)) {
        printf("'width' should be a number\n" );
        return;
    }
    if (!lua_isnumber(L,-1)) {
        printf("'height' should be a number\n" );
        return;
    }
    *w = lua_tointeger(L,-2);
    *h = lua_tointeger(L,-1);
}


int main()
{
    lua_State* L = luaL_newstate();
    int w,h;
    load(L,"D:/test.lua",&w,&h);
    printf("width = %d, height = %d\n",w,h);
    lua_close(L);
    return 0;
}

以下は、新機能の説明です。

    lua_getglobalは、プロトタイプがマクロである。#define lua_getglobal(L,s) lua_getfield(L, LUA_GLOBALSINDEX, (s))です。

    このマクロは、呼び出されるたびに、Luaコード内の対応するグローバル変数の値をスタックに押し出します。最初に呼び出されたときは、グローバル変数 "width" の値をスタックに押し出し、再度呼び出されたときは "height" の値をスタックに押し出します。

2.テーブルの操作。

    C言語のコード内からLuaのテーブルデータを操作できるのは、非常に便利で有用な機能です。Luaコードの構造が明確になるだけでなく、Cコードで同等の構造を定義することで、可読性が格段に向上します。以下のコードを見てください。

コピーコード コードは以下の通りです。

#include <stdio.h>
#include <string.h>
#include <lua.hpp>
#include <luxlib.h>
#include <lualib.h>

void load(lua_State* L) {

    if (luaL_loadstring(L,"background = { r = 0.30, g = 0.10, b = 0 }")
        || lua_pcall(L,0,0,0)) {
        printf("Error Msg is %s.\n",lua_tostring(L,-1));
        return;
    }
    lua_getglobal(L,"background");
    if (!lua_istable(L,-1)) {
        printf("'background' is not a table.\n" );
        return;
    }
    lua_getfield(L,-1,"r");
    if (!lua_isnumber(L,-1)) {
        printf("Invalid component in background color.\n");
        return;
    }
    int r = (int)(lua_tonumber(L,-1) * 255);
    lua_pop(L,1);
    lua_getfield(L,-1,"g");
    if (!lua_isnumber(L,-1)) {
        printf("Invalid component in background color.\n");
        return;
    }
    int g = (int)(lua_tonumber(L,-1) * 255);
    lua_pop(L,1);

    lua_pushnumber(L,0.4);
    lua_setfield(L,-2,"b");

    lua_getfield(L,-1,"b");
    if (!lua_isnumber(L,-1)) {
        printf("Invalid component in background color.\n");
        return;
    }
    int b = (int)(lua_tonumber(L,-1) * 255);
    printf("r = %d, g = %d, b = %d\n",r,g,b);
    lua_pop(L,1);
    lua_pop(L,1);
    return;
}

int main()
{
    lua_State* L = luaL_newstate();
    load(L);
    lua_close(L);
    return 0;
}

void lua_getfield(lua_State *L, int idx, const char *k); 第2引数はスタック上のテーブル変数のインデックス値、最後の引数はテーブルのキー値です。この関数は正常に実行されると、フィールド値をスタックに押下します。

void lua_setfield(lua_State *L, int idx, const char *k); 第2引数はスタック上のテーブル変数のインデックス値、最後の引数はテーブルのキー名、フィールド値は前のコマンド lua_pushnumber(L,0.4) でスタック上に押し込まれており、関数が成功すると今押し込んだフィールド値をスタックからポップするようになります。
次のコード例では、Cコードでテーブル・オブジェクトを構築しながら、テーブルのフィールド値を初期化し、最後にLuaのグローバル変数にテーブル・オブジェクトを代入しています。

コピーコード コードは以下の通りです。

#include <stdio.h>
#include <string.h>
#include <lua.hpp>
#include <luxlib.h>
#include <lualib.h>

void load(lua_State* L)
{
    lua_newtable(L);
    lua_pushnumber(L,0.3);
    lua_setfield(L,-2,"r");

    lua_pushnumber(L,0.1);
    lua_setfield(L,-2,"g");

    lua_pushnumber(L,0.4);
    lua_setfield(L,-2,"b");
    lua_setglobal(L,"background");

    lua_getglobal(L,"background");
    if (!lua_istable(L,-1)) {
        printf("'background' is not a table.\n" );
        return;
    }
    lua_getfield(L,-1,"r");
    if (!lua_isnumber(L,-1)) {
        printf("Invalid component in background color.\n");
        return;
    }
    int r = (int)(lua_tonumber(L,-1) * 255);
    lua_pop(L,1);
    lua_getfield(L,-1,"g");
    if (!lua_isnumber(L,-1)) {
        printf("Invalid component in background color.\n");
        return;
    }
    int g = (int)(lua_tonumber(L,-1) * 255);
    lua_pop(L,1);

    lua_getfield(L,-1,"b");
    if (!lua_isnumber(L,-1)) {
        printf("Invalid component in background color.\n");
        return;
    }
    int b = (int)(lua_tonumber(L,-1) * 255);
    printf("r = %d, g = %d, b = %d\n",r,g,b);
    lua_pop(L,1);
    lua_pop(L,1);
    return;
}

int main()
{
    lua_State* L = luaL_newstate();
    load(L);
    lua_close(L);
    return 0;
}

 上記のコードでは、先ほどのコードと同じ結果が出力されます。

    lua_newtableはマクロで、プロトタイプは以下の通りです。#define lua_newtable(L) lua_createtable(L, 0, 0)です。このマクロが呼ばれると、Luaは新しいテーブル・オブジェクトを生成し、スタックに押し付けます。

    lua_setglobal は、プロトタイプがマクロです。#define lua_setglobal(L,s) lua_setfield(L,LUA_GLOBALSINDEX,(s)) です。このマクロを呼び出すと、Luaは第2引数で指定されたグローバル変数名に、現在のスタックの先頭の値を代入する。このマクロが正常に実行されると、割り当てられたばかりの値がスタックの先頭からポップアップします。

    3. Luaの関数を呼び出す。

    関数を呼び出すためのAPIも非常にシンプルです。まず呼び出す関数をスタックに押し出し、次に関数の引数を押し、次にlua_pcallを使って実際に呼び出し、最後に呼び出しの結果をスタックからポップさせます。次のコードを見てください。

コピーコード コードは以下の通りです。

 #include <stdio.h>
#include <string.h>
#include <lua.hpp>
#include <luxlib.h>
#include <lualib.h>

const char* lua_function_code = "function add(x,y) return x + y end";

void call_function(lua_State* L)
{
    //luaL_dostring is equivalent to luaL_loadstring() || lua_pcall()
    //Note: you must execute the Lua script before you can call the Lua function, otherwise you will get an error when you actually call the Lua function later.
    //The error message is: "attempt to call a nil value."
    if (luaL_dostring(L,lua_function_code)) {
        printf("Failed to run lua code.\n");
        return;
    }
    double x = 1.0, y = 2.3;
    lua_getglobal(L,"add");
    lua_pushnumber(L,x);
    lua_pushnumber(L,y);
    // The second argument below indicates the presence of two arguments to the lua function with the call.
    //The third argument means that even if the function with the call has multiple return values, then only one will be pressed onto the stack after execution.
    //After the lua_pcall call, the function arguments and function names are popped out of the virtual stack.
    if (lua_pcall(L,2,1,0)) {
        printf("error is %s.\n",lua_tostring(L,-1));
        return;
    }
    //At this point the result has been pressed onto the stack.
    if (!lua_isnumber(L,-1)) {
        printf("function 'add' must return a number.\n");
        return;
    }
    double ret = lua_tonumber(L,-1);
    lua_pop(L,-1); //pop the return value.
    printf("The result of call function is %f.\n",ret);
}

int main()
{
    lua_State* L = luaL_newstate();
    call_function(L);
    lua_close(L);
    return 0;
}