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

Luaチュートリアル(20)。LuaがCの関数を呼び出す

2022-02-12 23:38:45

LuaがC言語の関数を呼び出せるようになると、Luaのスケーラビリティや使い勝手が大きく向上します。OSに関連する一部の機能や、高い効率が求められるモジュールについては、C関数で実装し、Luaから指定されたC関数を呼び出すことが可能です。Luaから呼び出せるC関数については、Luaが要求する形式、すなわちtypedef int (*lua_CFunction)(lua_State* L)に沿ったインタフェースが必要です。簡単に説明すると、この関数型はLua環境へのポインタを唯一の引数として持ち、実装者はこれを通じてLuaコードで渡された実際の引数にアクセスすることができます。戻り値は整数で、C関数がLuaコードに返す戻り値の数を表し、戻り値がない場合は0を返します。ここで重要なのは、C関数は実際の戻り値を直接Luaコードに返すのではなく、LuaコードとC関数の間で仮想スタックを介して呼び出しパラメータと戻り値を受け渡しする点です。ここでは、LuaでC関数を呼び出す際の2つのルールについて説明します。

1. アプリケーションの一部としてのC関数

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

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

// C registered functions to be called by Lua.
static int add2(lua_State* L)
{
    // Check that the arguments in the stack are legal. 1 means the first argument (from left to right) when Lua is called, and so on.
    //If the Lua code passes an argument that is not number when called, the function will report an error and terminate the execution of the program.
    double op1 = luaL_checknumber(L,1);
    double op2 = luaL_checknumber(L,2);
    //Press the result of the function onto the stack. If there are multiple return values, they can be pressed onto the stack multiple times here.
    lua_pushnumber(L,op1 + op2);
    // The return value is used to indicate the number of return values, i.e. the number of return values pressed onto the stack, for this C function.
    return 1;
}

// Another C-registered function to be called by Lua.
static int sub2(lua_State* L)
{
    double op1 = luaL_checknumber(L,1);
    double op2 = luaL_checknumber(L,2);
    lua_pushnumber(L,op1 - op2);
    return 1;
}

const char* testfunc = "print(add2(1.0,2.0)) print(sub2(20.1,19))";

int main()
{
    lua_State* L = luaL_newstate();
    luaL_openlibs(L);
    //register the specified function as a global function variable in Lua, where the first string argument is the Lua code
    //The name of the global function to use when calling the C function, and the second argument is a pointer to the actual C function.
    lua_register(L, "add2", add2);
    lua_register(L, "sub2", sub2);
    // After registering all the C functions, you can use them in Lua code blocks.
    if (luaL_dostring(L,testfunc))
        printf("Failed to invoke.\n");
    lua_close(L);
    return 0;
}

2. C言語関数ライブラリがLuaのモジュールになる。

Linuxならso、WindowsならDLLなど、C関数を含むコードのライブラリファイルを生成し、Luaパーサーが正しく位置を特定できるように、Luaコードが置かれているカレントディレクトリ、もしくは環境変数LUA_CPATHが指すディレクトリにコピーすることも行います。現在のWindowsシステムでは、Luaが呼び出せる全てのCライブラリを含む""C:³ Files³ Lua³ 5.1³³clibs³ "にコピーしています。以下のCコードとキーコメントをご覧ください。

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

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

// The C function to be registered, which is declared in the form given in the example above.
//It should be noted that the function must be exported as C, so extern "C" is required.
//The function code is the same as the above example, so we won't go over it here.
extern "C" int add(lua_State* L)
{
    double op1 = luaL_checknumber(L,1);
    double op2 = luaL_checknumber(L,2);
    lua_pushnumber(L,op1 + op2);
    return 1;
}

extern "C" int sub(lua_State* L)
{
    double op1 = luaL_checknumber(L,1);
    double op2 = luaL_checknumber(L,2);
    lua_pushnumber(L,op1 - op2);
    return 1;
}

// The first field of the luaL_Reg structure is a string that is used to inform Lua of the name of the function during registration.
//The first field is a pointer to a C function.
//Both fields of the last element of the structure array are NULL, to indicate that the Lua registration function has reached the end of the array.
static luaL_Reg mylibs[] = {
    {"add", add},
    {"sub", sub},
    {NULL, NULL}
};

// The only entry function for this C library. Its function signature is equivalent to the registered function above. See the following points for clarification.
//1. We can understand this function simply as a factory function for the module.



//4. It is important to emphasize that all code that requires the use of "xxx" must be consistent, regardless of C or Lua, as is the Lua convention that
// Otherwise it will not be called.
extern "C" __declspec(dllexport)
int luaopen_mytestlib(lua_State* L)
{
    const char* libName = "mytestlib";
    luaL_register(L,libName,mylibs);
    return 1;
}

以下のLuaのコードをご覧ください。

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

 require "mytestlib" -- specify package name
 -- when called, it must be package.function
 print(mytestlib.add(1.0,2.0))
 print(mytestlib.sub(20.1,19))