うたカモ技術ブログ

Lua C

Luaプログラミング   LuaスクリプトからC言語の関数を呼び出す方法

post: 

今回はLuaスクリプトからC言語関数を呼び出す方法について紹介します。

前回の記事「Luaプログラミング C言語ソフトウェアからLuaを呼び出す方法」ではLuaステートを使用してC言語からLuaスクリプトやその中の関数を呼び出す方法について紹介しました。

詳細としては、共有オブジェクト(ライブラリ)のliblua.soを利用して自作のC言語ソフトウェアにLuaインタプリタ(実行環境)を組み込むことで Luaスクリプトを読み込んで実行しました。

この時、実行環境のインスタンスとしてLuaステートというオブジェクトを扱いました。

今回は、このLuaステートの中にC言語関数のエントリを追加することでLuaスクリプトから特定のC言語関数を呼び出せるようにしてみます。

#実行環境OS
ubuntu 22.04 LTS 64bit
#Lua
Ver 5.4.7

前提条件:Luaライブラリ(liblua.so)のインストール

C言語からLuaスクリプトを実行するには、予め開発環境にLuaライブラリ(liblua.so)をインストールする必要があります。 以下にインストール方法について掲載します。これはLua公式サイトのダウンロードページに掲載されているものです。 まだインストールしていない方はここで実施しましょう。

user:~$ curl -L -R -O https://www.lua.org/ftp/lua-5.4.7.tar.gz
user:~$ tar zxf lua-5.4.7.tar.gz
user:~$ cd lua-5.4.7
user:~$ make all test
user:~$ sudo make install

このLuaライブラリは次節で紹介するC言語バイナリ用のLua実行環境(Luaステート)を構築するための実装です。 ライブラリを適切に使用することで、指定したLuaスクリプトやその中の関数を明示して実行することが可能になります。

sudo make installで/usr/local/libに共有オブジェクトのliblua.soが格納されます。 これでコンパイル時にgcc -o test test.c -llua -lm -ldlとすることで、C言語ソースのtest.cliblua.so(-llua)をリンクすることができます。

※なお、5.4.4以降のどこかで数学系とダイナミックリンク系の共有オブジェクト(ライブラリ)のlibm.so(-lm)libdl.so(-ldl)もリンクしないといけなくなったようです。

それでは、次節から説明に入ります。

LuaスクリプトからC言語関数を呼び出す

「LuaスクリプトからC言語関数を呼び出す」にはLuaインタプリタを組み込んだC言語ソースコード側で C言語関数のエントリをLuaステート上にプリロードする必要があります。

C言語関数のプリロードはlua_register関数を実行することで実施します。

次はC言語関数のgreetとaddをLuaステート内にプリロードし、Luaスクリプト側で c_greet、c_addとして呼び出せるようにしたソースコード(test.c)です。

//test.c
#include <lauxlib.h>
#include <lua.h>
#include <lualib.h>
#include <stdio.h>
#include <stdlib.h>

int greet(lua_State *);
int add(lua_State *);

int main() {
    int status, result;
    lua_State *L;
    L = luaL_newstate();    //Luaステートの構築
    luaL_openlibs(L);         //Lua標準ライブラリの導入(インポート)

    lua_register(L, "c_greet", greet);
    lua_register(L, "c_add", add);

    status = luaL_dofile(L, "test.lua");     //Luaスクリプトをロードして実行

    if(status) {
        fprintf(stderr, "Couldn't load file: %s", lua_tostring(L, -1));
        return 1;
    }

    lua_close(L);   //Luaステートのクローズ

    return 0;
}

int greet(lua_State *L) {
    const char *str = lua_tostring(L, 1);
    printf("Hello!! %s!\n", str);
    return 0;
}

int add(lua_State *L) {
    int x = lua_tointeger(L, 1);
    int y = lua_tointeger(L, 2);
    lua_pushinteger(L, x + y);
    return 1; // returnの値で戻り値が1つ存在することを認識
}

上記のgreet関数とadd関数をそれぞれc_greet、c_addで呼び出して実行する Luaスクリプト(test.lua)は以下の通りです。

-- test.lua
io.write("Your Name: ")
local name = io.read()
c_greet(name)

io.write("x: ")
local x = io.read()
io.write("y: ")
local y = io.read()

local sum = c_add(x, y)

print(x .. ' + ' .. y .. ' = ' .. sum)

それではコンパイルして動作確認をします。

今回、私の環境ではホームディレクトリ直下にtest-luaというディレクトリを作り、そこに上記掲載のソースファイルを保存した上でtest.cをコンパイルしました。 このとき、Luaライブラリ(liblua.so)とリンクするために、-lluaを指定します。

kamo@kamo:~/test-lua$ ls
test.c test.lua
kamo@kamo:~/test-lua$ gcc -o test test.c -llua -lm -ldl

これでコンパイルが完了しました。さっそく実行してみます。

kamo@kamo:~/test-lua$ ./test
Your Name: utakamo
Hello!! utakamo!
x: 1
y: 2
1 + 2 = 3

このようにLuaスクリプトはC言語の連携が可能なスクリプト言語です。

用途に応じた使い方をすることでソフトウェアの拡張性が高まります。

参考文献