LuaのC言語インターフェース

いちごパック > Luaの解説 > LuaのC言語インターフェース

LuaとC/C++言語

LuaはC/C++言語から制御できるように作られています。 C/C++言語からLuaを制御する方法は次の2通りあります。
  • C/C++言語で開発するソフトの一部としてLua本体を埋め込み、C/C++言語側で追加機能を実装
  • Luaを共有ライブラリ(DLL)の形で利用し、追加機能をLuaプラグインとして実装
  • どちらの方法でも、その大半は共通です。
    LuaのAPIはC言語の関数として実装されています。
  • lua_ではじまるAPIは、lua.hを#includeすることでC言語から使えます(標準ライブラリ相当の機能を初期化する関数を除きます)。
  • luaL_ではじまるAPIは、lua_を使った補助関数で、lauxlib.hを#includeすることでC言語から使えます(luaL_openlibsを除きます)。
  • print()などLuaの標準ライブラリ相当の機能は、lualib.hを#includeし、C言語からluaL_openlibs()などの初期化関数を呼び出すことで、Luaから使えるようになります。
  • 以下、LuaのC言語インターフェースの詳細について説明していきます。

    Lua本体の埋め込み

    C/C++言語で開発するソフトの一部としてLua本体を埋め込む場合、 処理の流れを図示すると次のようになります。

    Lua本体にはグローバル変数がありません。 Luaのスクリプト実行状態や、Luaスクリプトから呼び出せるC/C++言語側の機能は、 lua_Stateという構造体(オブジェクト)に保存されます。
    Lua本体を埋め込む場合、その処理は、まずlua_State構造体を作り、 次に利用したい機能を追加し、その後スクリプトをluaL_loadfile()などで読み込んでから、 その関数をlua_pcall()などで呼び出す、という流れになります。
    Luaでは、標準ライブラリ相当の機能を外すこともできるようになっています。 したがって、標準ライブラリ相当の機能を利用したい場合は、luaL_openlibs()を呼び出す必要があります。
    Lua本体を埋め込む場合は、Luaはスタティックリンク、 ダイナミックリンクのいずれの形でリンクしても問題ありません。

    Lua本体を埋め込む基本実装

    はじめに、Lua本体を埋め込み、C言語からLua本体を呼び出す方法を説明します。 Lua本体を埋め込んで動作させる基本実装は、次の関数を利用すればできます。 Luaスタックについては後で説明します。
    関数名機能の説明
    luaL_newstate()Cライブラリのrealloc()を使うlua_Stateを作り、そのポインタを返します。
    luaL_loadfile(L,filename)filenameで指定されたLuaのファイルを読み込み、引数なしの関数としてLuaスタックにpushします。読み込みエラーが起きた場合は、エラーメッセージをpushします。
    luaL_loadstring(L,str)メモリ上の文字列strをLuaのスクリプトを読み込み、引数なしの関数としてLuaスタックにpushします。読み込みエラーが起きた場合は、エラーメッセージをpushします。
    lua_pcall(L,0,LUA_MULTRET,0)Luaスタックにpushされた引数なしの関数を呼び出す際に使います。この関数の詳細は後述します。
    luaL_dofile(L,filename)luaL_loadfile()を呼び出し、成功した場合はlua_pcall()を呼び出します。
    lua_close(L)Lを解放します。
    luaL_openlibs(L)Lに標準ライブラリ相当の機能を読み込ませます。
    luaL_newstate()などで確保したlua_State*は、Luaの状態を必要とする関数の第1引数になります。この説明ではLと呼びます。
    スクリプトを読み込んで実行する、C言語側のコードの1例を示します。 このコードは、プロジェクトにLuaのincludeディレクトリを追加し、またliblua53.libをリンクすることでビルドできます。
    #include <lua.h> /* lua_ */
    #include <lauxlib.h> /* luaL_ */
    #include <lualib.h> /* luaL_openlibs() */
    #include <stdio.h>
    
    #define SCRIPT_NAME "ichigo1.lua"
    
    int main()
    {
        lua_State* L;
        int result;
    
        L = luaL_newstate();
        if (L == NULL) {
            puts("luaL_newstate() error");
            return 1;
        }
        luaL_openlibs(L);
        result = luaL_loadfile(L, SCRIPT_NAME);
        if (result != LUA_OK) {
            puts("luaL_loadfile() error");
            return 1;
        }
        result = lua_pcall(L, 0, LUA_MULTRET, 0);
        if (result != LUA_OK) {
            puts("lua_pcall() error");
            return 1;
        }
        lua_close(L);
    
        return 0;
    }
    
    このコードから読み込むLuaスクリプトの1例を示します。ファイル名はichigo1.luaとします。
    print('ichigopack')
    
    実行結果は次のようになります。Luaスクリプト内のprint()が実行されていることがわかります。
    ichigopack
    

    Luaプラグイン

    Luaでは、Lua本体が別の実行ファイルとして用意されている場合に、 Luaのプラグインとして追加機能を組み込むこともできるようになっています。
    本体を埋め込む場合と違いは、Luaをプラグインとして利用する場合は、 LuaやC言語のライブラリをダイナミックリンクの形でリンクする必要があります。 図に示すと次のようになります。

    LuaやC言語のライブラリをスタティックリンクの形でリンクすると、 実行中に2つ以上のLuaあるいはC言語ライブラリのコピーが存在する状態になります。 例として、Luaをスタティックリンクの形でリンクした場合を図に示します。

    この場合、2つのLuaライブラリの挙動が完全に一致していれば動作することもありますが、 違いがあると原因不明の不具合に悩まされることがあります。
    注意してプログラミングすれば2つのライブラリのコピーを共存させることは可能ですが、 不具合を避けて実装するのが簡単でしょう。 Luaプラグインを作る、あるいは使う場合は、不具合を避けるために、 ダイナミックリンク(Windowsであれば.dll、Linuxであれば.so)の形でリンクすることにご留意ください。
    なお、Lua本体が備えるAPIには、C言語ライブラリのデータをやりとりするAPIは存在しません。 そのため、LuaのAPIを利用してLuaを使うだけでしたら、 C言語ライブラリが一致していなくても、問題は起こらないと考えられます。
    今回の例ではMinGWと呼ばれるコンパイラでビルドされたLuaをVisual C++でリンクしています。 したがって、2つのコンパイラはそれぞれ別のC言語ライブラリを使っています。 そのため、例えばLuaのuserdata変数であるFILE構造体に直接C言語からアクセスするなど、 同一のC言語ライブラリが必要なコードを書きたい場合は、LuaのDLLをVisual C++でビルドしなおす必要があります。

    Lua plug-inの基本実装

    Lua向けのplug-inは、Lua向けに次の関数を実装した.dllあるいは.soです。 Luaスクリプトでrequire(modulename)を実行することで読み込めます。
    C言語の標準ライブラリにはDLLを読み込む機能がありませんので、 この機能はWindowsやLinux、Macなど、Luaが対応している環境でのみ使えます。 なお、require()はC言語で書かれたDLLだけでなく、Luaスクリプトを読み込むこともでき、 読み込みに成功したモジュールはキャッシュされます。
    require()がC言語のDLLを読み込む場合、DLLや初期化関数名は引数modulenameに依存して決められます。
  • package.cpathに検索すべきパスをセミコロン(;)で区切って書いておきます。パスに書かれた?はmodulenameで置き換えられます。
  • Windowsの場合、package.cpathに'./?.dll'を追加しておけば、dllを読み込むことができます。
  • Windows用LuaRocksで設定したLUA_PATHを使う場合、カレントディレクトリにあるファイルは読み込まれません。
  • 初期化関数名はluaopen_の後にmodulenameを追加した名前になります。追加の際、modulename内のドット(.)をアンダースコア(_)に変換し、また、ハイフン(-)以降は無視します。
  • WindowsでDLLを作った場合、関数はデフォルトではDLL外から見えません。関数の前に__declspec(dllexport)を入れるなどの方法で、DLL外から見えるようにする必要があります。
  • 例えば、require('ichigopack')であればichigopack.dllを読み込み、 DLLの初期化関数としてluaopen_ichigopack()を呼びだします。
    初期化関数は変数値を1つ返します。 変数値を返さなかったり、変数値がnilである場合は、trueが返されたとして扱います。 この変数値はキャッシュされ、2回目以降のrequire()では単にキャッシュされた変数値が返されます。 したがって、DLLの初期化関数は、キャッシュされた場合でもその動作が変わらないように、 関数やテーブルなどを返す、グローバル変数に値を設定するといった形で実装すると良いでしょう。
    初期化関数はLuaから呼び出せる関数型(lua_CFunction)で実装する必要があります。 関数の戻り値は、後述のLuaスタックにpushされた変数値の数になります。
    typedef int (*lua_CFunction)(lua_State* L);
    
    package.cpathに'./?.dll'を追加し、 require()を使ってDLLを読み込むLuaスクリプトの1例を示します。 ファイル名はichigolua1.luaとします。
    package.cpath = './?.dll;' .. package.cpath
    
    print('require() #1')
    result = require('ichigolua1')
    print(result)
    print('require() #2')
    result = require('ichigolua1')
    print(result)
    
    require()により読み込まれるDLLのコードの1例を示します。
    #include <lua.h> /* lua_ */
    #include <stdio.h>
    
    __declspec(dllexport)
    int luaopen_ichigolua1(lua_State* L)
    {
        puts("ichigolua init");
        fflush(stdout);
        return 0;
    }
    
    
    実行結果は次のようになります。Luaスクリプト内のprint()が実行されていることがわかります。
    require() #1
    ichigolua init
    true
    require() #2
    true
    
    DLLの読み込みは初回のみ行われ、2回目はキャッシュされています。 また、初期化関数が値を返さないと、trueとして扱われています。 plug-inの関数はLua本体とは独立に動作しますので、 関数の終わりでfflush(stdout)を呼び出し、標準出力を同期しています。
    コードのビルド方法は、EXEではなくDLLを作る点以外はLua本体を埋め込む場合と同じです。 プロジェクトにLuaのincludeディレクトリを追加し、またliblua53.libをリンクすればLuaのplug-inが作れます。 Linux環境の場合は、スクリプト中の'./?.dll'の部分を'./?.so'あるいは'./lib?.so'とすると良いでしょう。

    Lua関数のC言語からの呼び出し

    Luaの関数は、lua_State構造体を用意すれば任意のC/C++コードから呼び出せます。 一方、LuaスクリプトからはLua呼び出し専用のC/C++関数を呼び出せます。 図に示すと次のようになります。

    関数パラメータ用スタック

    Luaでは、関数呼び出しの際にやりとりするパラメータ(関数、引数、戻り値)のために、 lua_State構造体の中に、関数パラメータ用のスタック(以下、Luaスタック)を用意しています。 Luaバージョン5.3のデフォルトでは、Luaスタックは引数や戻り値を最低20個以上持てるようになっています。
    Luaスタックは1から始まるインデックスでアクセスします。 正の数値は1から始まる順方向の絶対位置を、負の数値は現在のLuaスタックの最高位置を-1としたときの逆方向の位置を表します。 図に示すと次のようになります。

    Luaスタックにデータを追加(push)したい場合は、 lua_pushnumber()やlua_pushstring()などのpush関数を使います。 Luaスタックに積まれたデータを削除(pop)したい場合はlua_pop()を使います。
    すでにパラメータを積んでいることも考えられますので、 Luaスタックを20個ぶん使えるとは限らないことにご留意ください。

    データ型

    Luaはデフォルトで整数(lua_Integer)、浮動小数点数(lua_Number)とも64ビット型を用います。 64ビットの型ではなく32ビットの型を使いたい場合は、 luaconf.hでマクロLUA_32BITSを定義すれば32ビットになります。
    Luaのデータは、C言語からはLuaのAPIを通して操作します。 スタックにpushする関数、スタックにあるデータのデータ型を確認する関数、スタックにあるデータを指定データ型として取得する関数が用意されています。 各データ型は、次の関数で操作できます。
    データ型C言語用の関数
    nillua_pushnil(L), lua_isnil(L,stackindex)
    booleanlua_pushboolean(L,val), lua_isboolean(L,stackindex), lua_toboolean(L,stackindex)
    number(integer)lua_pushinteger(L,num), lua_isinteger(L,stackindex), lua_tointeger(L,stackindex), lua_tointegerx(L,stackindex,presult)
    number(float)lua_pushnumber(L,num), lua_isnumber(L,stackindex), lua_tonumber(L,stackindex), lua_tonumberx(L,stackindex,presult)
    stringlua_pushstring(L,str), lua_pushlstring(L,str,len), lua_pushliteral(L,str), lua_pushfstring(L,fmt,...), lua_pushvfstring(L,fmt,va), lua_isstring(L,stackindex), lua_tostring(L,stackindex), lua_tolstring(L,stackindex,plen)
    functionlua_pushcfunction(L,func), lua_pushcclosure(L,func,nvars), lua_iscfunction(L,stackindex), lua_isfunction(L,stackindex), lua_tocfunction(L,stackindex)
    userdata(full)lua_newuserdata(L,numbytes), lua_isuserdata(L,stackindex), lua_setuservalue(L,stackindex), lua_getuservalue(L,stackindex), lua_touserdata(L,stackindex)
    userdata(light)lua_pushlightuserdata(L,ptr), lua_islightuserdata(L,stackindex), lua_isuserdata(L,stackindex), lua_touserdata(L,stackindex)
    threadlua_pushthread(L), lua_isthread(L,stackindex), lua_tothread(L,stackindex)
    tablelua_pushglobaltable(L), lua_istable(L,stackindex), lua_settable(L,stackindex), lua_seti(L,stackindex,val), lua_getfield(L,stackindex,name), lua_next(L,tableindex), lua_setmetatable(L,stackindex)
    userdata(full)はLua側でメモリブロックを確保する型、userdata(light)はLua側ではポインタのみ管理する型です。 各関数の概要は次の通りです。 説明文中、stackはLuaスタック、pushはデータをLuaスタックの最上位への追加、popはLuaスタック最上位データからの削除を表します。
    関数名機能の説明
    lua_pushnil(L)nilをnil型でpushします。
    lua_pushboolean(L,val)int値valをboolean型でpushします。
    lua_pushinteger(L,val)int値valをnumber型でpushします。
    lua_pushnumber(L,val)float値valをnumber型でpushします。
    lua_pushstring(L,str)'\0'を終端とするstrのコピーを作成し、string型でpushします。
    lua_pushlstring(L,str,len)長さlenを持つstrのコピーを作成し、string型でpushします。
    lua_pushliteral(L,str)strがC言語リテラルと仮定して、strをstring型でpushします。
    lua_pushfstring(L,fmt,...)printf形式に似た整形を行い、string型でpushします。%s、%d、%c、、%%、%U(long int値をUTF-8文字に変換)、%f(Luaの浮動小数点値)、%I(Luaの整数値)、%p(ポインタを16進数表記に変換)が使えます。精度指定などはできません。
    lua_pushvfstring(L,fmt,va_args)vprintfのように、可変長引数をva_argsとしてlua_pushfstring()と同様の整形を行い、string型でpushします。
    lua_pushcfunction(L,func)lua_CFunction型の関数funcへのポインタをpushします。
    lua_pushcclosure(L,func,num)lua_CFunction型の関数funcを、与えられたnum個の変数値と一体化させてpushします。一体化された変数は、関数とともにLuaスタックの外で管理されます。lua_pushcclosure()を呼び出す前に、num個の変数値をpushしておきます。lua_pushcclosure()の後、pushされていたnum個の変数値は削除され、関数がpushされた状態になります。
    lua_newuserdata(L,datasize)datasizeバイトのメモリをuserdata(full)として確保し、それをpushするとともにvoid*として返します。
    lua_pushlightuserdata(L,ptr)void*型のポインタptrをuserdata(light)としてpushします。
    lua_newtable(L)空のtableを作成し、それをpushします。
    lua_newtable(L,seqcount,keycount)空のtableを作成し、ヒントにしたがってメモリを確保してから、それをpushします。seqcountはkeyを省略して追加する要素数のヒント、keycountはkeyを与えて追加する要素数のヒントです。
    lua_pushvalue(L,index)Luaスタックの位置indexにある値のコピーをpushします。
    lua_replace(L,index)最後にpushされた要素をLuaスタックのindex番目の要素に上書き設定し、最後にpushされた要素をpopします。
    lua_getglobal(L,name)グローバル変数名nameの値をpushします。
    lua_setglobal(L,name)最後にpushされた要素をグローバル変数名nameの値として設定し、最後にpushされた要素をpopします。
    lua_geti(L,stackindex,arrayindex)Luaスタックのstackindex番目をtable、arrayindexをkeyとするとき、table[key]をpushします。
    lua_gettable(L,stackindex)Luaスタックのstackindex番目をtable、最後にpushされた要素をkeyとするとき、keyをpopしてからtable[key]をpushします。
    lua_seti(L,stackindex,arrayindex)Luaスタックのstackindex番目をtable、arrayindexをkey、最後にpushされた要素をvalueとするとき、valueをpopしtable[key]=valueを実行します。
    lua_settable(L,stackindex)Luaスタックのstackindex番目をtableとして、key、valueがこの順でpushされたとき、valueとkeyをこの順でpopしtable[key]=valueを実行します。
    lua_pop(L,num)num個の要素をpopします。
    lua_remove(L,index)Luaスタックからindex番目の要素を削除します。
    なお、lua_CFunctionは次の型を持つC言語の関数で、その戻り値はスタックにpushされた関数の戻り値の総数です。
    typedef int (*lua_CFunction)(lua_State* L);
    
    また、関数lua_type(L,stackindex)を呼び出すことで、Luaスタックにある変数の型を取得できます。
    データ型返される値(int値)
    nilLUA_TNIL
    booleanLUA_TBOOLEAN
    numberLUA_TNUMBER (内部的にはintegerもfloatとして扱われます)
    stringLUA_TSTRING
    functionLUA_TFUNCTION (C関数とLua関数は区別されません)
    userdata(full)LUA_TUSERDATA
    userdata(light)LUA_TLIGHTUSERDATA
    threadLUA_TTHREAD
    tableLUA_TTABLE
    各データ型をpushし、その型を判定し表示した後でpopする、C言語のコードの1例を示します。
    #include <lua.h> /* lua_ */
    #include <lauxlib.h> /* luaL_ */
    #include <stdio.h>
    
    void print_and_pop(lua_State* L)
    {
        printf("stackcount %d, ",lua_gettop(L));
        switch (lua_type(L,-1)) {
        case LUA_TNIL:
            printf("nil\n");
            break;
        case LUA_TBOOLEAN:
            printf("bool %d\n",lua_toboolean(L,-1));
            break;
        case LUA_TNUMBER:
            printf("number %f\n",(float)lua_tonumber(L,-1));
            break;
        case LUA_TSTRING:
            printf("string %s\n",lua_tostring(L,-1));
            break;
        case LUA_TFUNCTION:
            printf("function\n");
            break;
        case LUA_TUSERDATA:
            printf("userdata (full)\n");
            break;
        case LUA_TLIGHTUSERDATA:
            printf("userdata (light)\n");
            break;
        case LUA_TTHREAD:
            printf("thread\n");
            break;
        case LUA_TTABLE:
            printf("table\n");
            break;
        }
    
        lua_pop(L,1);
    }
    
    int func_ichigo(lua_State* L)
    {
        puts("ichigopack function");
        return 0;
    }
    
    int main()
    {
        lua_State* L;
        int dummyuserdata;
        int* fulldata;
    
        L = luaL_newstate();
        if (L == NULL) {
            puts("luaL_newstate() error");
            return 1;
        }
    
        lua_pushnil(L);
        print_and_pop(L);
        lua_pushboolean(L,0);
        print_and_pop(L);
        lua_pushnumber(L,15);
        print_and_pop(L);
        lua_pushstring(L,"ichigo");
        print_and_pop(L);
        lua_pushcfunction(L,func_ichigo);
        print_and_pop(L);
        fulldata = (int*)lua_newuserdata(L, sizeof(int));
        print_and_pop(L);
        lua_pushlightuserdata(L,&dummyuserdata);
        print_and_pop(L);
        lua_pushglobaltable(L);
        print_and_pop(L);
    
    
        lua_close(L);
    
        return 0;
    }
    
    コード中、lua_gettop(L)はLuaスタックにpushされたデータの数を返す関数です。 実行結果は次のようになります。
    stackcount 1, nil
    stackcount 1, bool 0
    stackcount 1, number 15.000000
    stackcount 1, string ichigo
    stackcount 1, function
    stackcount 1, userdata (full)
    stackcount 1, userdata (light)
    stackcount 1, table
    

    関数の呼び出し

    C/C++言語コードからLua関数を呼び出す場合は、 関数とその引数をLuaスタックにpushしてからlua_pcall()などを使って呼び出します。 この機能を使えば、例えば、Luaスクリプトの変更だけで実行時の振る舞いを変えられるようになります。
    最初に関数をpushし、次に呼び出す引数は左からpushしたうえで、lua_pcall()などを呼び出します。 呼び出し後は戻り値を取り出したうえで、できる限りLuaスタックを元の状態に戻したほうが良いでしょう。
    Lua関数を呼び出す場合は、次のいずれかの関数を用います。 これらの関数はlua_CFunctionも呼び出せます。
    関数名機能の説明
    lua_call(L,numargs,numrets)スタックからnumargs個の引数と関数を取り出し、関数を呼び出します。Luaスタックを戻り値の数がnumrets個となるように調整して制御を返します。numretsをLUA_MULTRETとすると、戻り値の調整は行いません。lua_call()には関数としての戻り値はありません。
    lua_pcall(L,numargs,numrets,hander_index)lua_call()と同様ですが、エラーが起きた場合にはそれをスタックにpushして返します。エラー状況をlua_pcall()の戻り値として返します(LUA_OKは成功、それ以外は失敗)。hander_indexが0以外の場合は、それをエラーハンドラ関数のLuaスタック内インデックスと解釈し、エラーが起きた場合にはエラーハンドラ関数を呼び出します。
    luaL_loadfile()はスクリプトを引数なしの関数として読み込むため、 スクリプトに書かれた内容を実行するにはluaL_loadfile()の直後にlua_pcall()を呼び出す必要があります。 スクリプトでreturnを実行した場合、その戻り値はLuaスタックにpushされます。
    LuaスクリプトをC言語から複数回実行したい場合は、 例えば、読み込んだLuaスクリプトをLuaのグローバル変数などに保存しておき、 lua_pcall()を使うとスクリプトを実行したいタイミングで実行できます。
    例として、Luaスクリプトを3回実行するコードを示します。
    #include <lua.h> /* lua_ */
    #include <lauxlib.h> /* luaL_ */
    #include <lualib.h> /* luaL_openlibs() */
    #include <stdio.h>
    
    #define SCRIPT_NAME "ichigo3.lua"
    #define FUNCTION_NAME "ichigofunc"
    
    int main()
    {
        lua_State* L;
        int result;
        int i;
    
        L = luaL_newstate();
        if (L == NULL) {
            puts("luaL_newstate() error");
            return 1;
        }
        luaL_openlibs(L);
        result = luaL_loadfile(L, SCRIPT_NAME);
        if (result != LUA_OK) {
            puts("luaL_loadfile() error");
            return 1;
        }
        /* 関数をLuaのglobal変数として保存し、pop */
        lua_setglobal(L,FUNCTION_NAME);
    
        /* 読み込んだLuaスクリプトを3回実行 */
        for (i = 0; i < 3; ++i ) {
            /* Luaのglobal変数から関数をpush */
            lua_getglobal(L,FUNCTION_NAME);
            /* pushされた関数を呼び出す */
            result = lua_pcall(L, 0, LUA_MULTRET, 0);
            if (result != LUA_OK) {
                puts("lua_pcall() error");
                return 1;
            }
            /* 戻り値を表示 */
            while (lua_gettop(L) > 0) {
                printf("lua_pcall(): %s\n",lua_tostring(L,-1));
                lua_pop(L,1);
            }
        }
    
        lua_close(L);
    
        return 0;
    }
    
    このコードから読み込むLuaスクリプトの1例を示します。ファイル名はichigo3.luaとします。
    print('ichigo3.lua')
    return 'ichigo'
    
    実行結果は次のようになります。 Luaスクリプト内のprint()と、Luaから返された文字列をC言語で表示するコードが交互に実行されていることがわかります。
    ichigo3.lua
    ichigo3.lua
    ichigo3.lua
    lua_pcall(): ichigo
    lua_pcall(): ichigo
    lua_pcall(): ichigo
    

    関数の実装

    標準ライブラリで提供される機能では足りない場合には、 C言語側で追加機能を実装し、Luaで利用するのが良いでしょう。
    LuaスクリプトからC/C++言語コードを呼び出す場合は、 C/C++言語側でlua_CFunction型の関数を実装したうえで、 Luaの変数としてC言語の関数を渡して、Luaスクリプト側でその関数を呼び出します。
    引数の数が必要な場合は、lua_gettop()で取得できます。 Luaスタックのインデックスは、最初の引数が1、2番目の引数が2のようになります。 戻り値がある場合は、Luaスタックに左から順にpushし、戻り値の数を返します。 Luaの関数と同じように、複数の戻り値を返せます。
    関数内でエラーが起きた場合は、luaL_error(L,fmt,...)を呼び出すとLuaの例外処理を実行できます。 luaL_error()の引数はpushfstring()と同じです。
    例として、ichigofuncという名前の関数をグローバル変数に設定しておき、 LuaスクリプトからC言語の関数を実行するコードを示します。
    #include <lua.h> /* lua_ */
    #include <lauxlib.h> /* luaL_ */
    #include <lualib.h> /* luaL_openlibs() */
    #include <stdio.h>
    
    #define SCRIPT_NAME "ichigo4.lua"
    #define FUNCTION_NAME "ichigofunc"
    
    /* 引数1と引数2を足して返す関数 */
    int func_ichigo(lua_State* L)
    {
        lua_Number v1, v2;
    
        if (lua_gettop(L) < 2)
            return luaL_error(L, "func_ichigo() error");
    
        v1 = lua_tonumber(L,1);
        v2 = lua_tonumber(L,2);
        lua_pop(L,2);
        lua_pushnumber(L,v1 + v2);
        return 1;
    }
    
    int main()
    {
        lua_State* L;
        int result;
    
        L = luaL_newstate();
        if (L == NULL) {
            puts("luaL_newstate() error");
            return 1;
        }
        luaL_openlibs(L);
    
        /* C言語の関数をpush */
        lua_pushcfunction(L,func_ichigo);
        /* Luaのglobal変数として保存し、pop */
        lua_setglobal(L,FUNCTION_NAME);
    
        /* Luaスクリプトを読み込んで実行 */
        result = luaL_loadfile(L, SCRIPT_NAME);
        if (result != LUA_OK) {
            puts("luaL_loadfile() error");
            return 1;
        }
        result = lua_pcall(L, 0, LUA_MULTRET, 0);
        if (result != LUA_OK) {
            puts("lua_pcall() error");
            return 1;
        }
        lua_close(L);
    
        return 0;
    }
    
    このコードから読み込むLuaスクリプトの1例を示します。ファイル名はichigo4.luaとします。
    print('ichigo4.lua')
    print('1 + 3 -> ' .. ichigofunc(1,3))
    
    実行結果は次のようになります。
    ichigo4.lua
    1 + 3 -> 4.0
    
    Luaスクリプトからichigofunc()を呼び出すことで、C言語の関数が実行されていることがわかります。