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

カスタムCモジュールへのLuaコール

2022-01-06 14:50:32

これはLuaプログラミングでも紹介されていますが、Luaの初心者がうまく実行するには、実はそんなに簡単なことではありません。ダイナミックリンクライブラリのsoファイルを生成する方法です。Lua 5.2では、エクスポート関数がLuaL_registerからLuaL_newlibに変更されました。詳細は後述します。ここでのモジュール名はhello_libで、Luaインタープリタは対応するモジュールを名前から探し、luaopen_XXXメソッドを実行します。コードは

#include <math.h>
#include <lua5.2/lua.h>
#include <lua5.2/lauxlib.h>
#include <lua5.2/lualib.h>
static int hello_sin(lua_State *L){
double d = luaL_checknumber(L, 1);
lua_pushnumber(L, sin(d));
return 1;
}
static const struct luaL_Reg hello_lib[] = {
{"hello_sin" , hello_sin},
{NULL, NULL}
};
int luaopen_hello_lib(lua_State *L){
luaL_newlib(L, hello_lib);
// luaL_register(L, "hello_lib",hello_lib); // lua 5.1
return 1;
}



Luaで呼び出される。

local hello = require "hello_lib"
print(hello.hello_sin(1))



実行過程と結果。 

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

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

// C registered function to be called by Lua.
static int add2(lua_State* L)
{
  //Check if the arguments in the stack are legal, 1 means the first argument (from left to right) in the Lua call, and so on.
  //If the Lua code passes an argument that is not NUMBER at the time of the call, 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);
  //return value to indicate the number of return values of the C function, i.e. the number of return values pushed onto the stack.
  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 C functions, 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 the registered C functions 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, the form of declaration of this function is given in the above example.
 //It should be noted that the function must be exported in C form, so extern "C" is required.
 //The function code is the same as in 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, which 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, used to inform Lua that the registered 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.
 //The function name must be luaopen_xxx, where xxx is the name of the library. lua code require "xxx" needs to correspond to it.
 //3. In the luaL_register call, its first string parameter is the module name "xxx" and the second parameter is the array of functions to be registered.
 // 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" -- パッケージ名を指定します。

-- 呼び出されるときは、package.function でなければなりません。

print(mytestlib.add(1.0,2.0))
print(mytestlib.sub(20.1,19))