luaから使える関数をC/C++で作成(hmLJ用)

  • 概要

    luajitで関数作成するのには

    • 「lua」で記述する方法
    • 「C/C++など」で記述する方法

    の2通りあります。

    ここでは、躓きやすいC/C++でluaの関数を作成する方法が説明されています。

    全体像を把握する上でも、まずは、一度一通りの簡単な例を実装してみしょう
    非常に簡単です。

  • ダウンロード

    hmLJ.lib.zipファイル。ver 2.041
    └更新日 2017/04/05

    このhmLJ.lib.zipファイルの中身は、「ヘッダーファイル」と「hmLJ.lib」ファイルであり、
    後述のdllをコンパイルする上で必要となります。
    (ヘッダーファイルは、hmLJ.dllを構成するLuaJITのソースファイルのうち、必要なものだけをピックアップしたものです)

  • C/C++でwin32プロジェクトを作成

    プロジェクトを作成しましょう。

    空のプロジェクトとします。

    文字セットはどちらでも良いのですが、秀丸の性質を考慮して、cp932としておきましょう。

  • ヘッダーファイルやhmLJ.libをコピー

    プロジェクトファイルと同じ場所に「****.h」と「hmLJ.lib」をコピーします。

    先ほどコピーしたファイルを、以下のようにプロジェクトファイルに追加しましょう。

    これで、luajitのヘッダーファイルや、hmLJ.dll内の関数が利用できるようになりました。

  • 引数の数値を全て足した合計を返す関数の例

    上図のようにソースを記述していきます。

    今回は「mysum.dll」を作成することになりますので、
    「luaopen_mysum」という名前の関数をdllからdllexportしておく必要があります。

    extern "C" __declspec(dllexport) int luaopen_mysum(lua_State *L) {
    

    の部分がそれに相当します。
    dllの名前(****.dll)とdllexportする関数名「luaopen_****」の****部分は一致している必要があります。

    他の部分は、Luaの関数をC/C++で作る上での基本的な方法となります。
    ネットや書籍にあふれていますので、検索してください。

    「luaL_register」などをキーワードとして検索するとよろしいかと思います。

    #include "lua.hpp"
    
    static int myfunc1(lua_State *L) {
        int n = lua_gettop(L);  /* 引数の数 */
        lua_Number dsum = luaL_checknumber(L, 1); // 一番上の値
        int i;
        for (i = 2; i <= n; i++) {
            lua_Number d = luaL_checknumber(L, i);
            dsum += d;
        }
        lua_pushnumber(L, dsum);
        return 1; // 返り値の個数(スクリプト言語は通常「複数」の値を返すことが出来る)
    }
    
    static const luaL_Reg my_funcs[] = {
        { "myfunc1", myfunc1 },
        { NULL, NULL }
    };
    
    extern "C" __declspec(dllexport) int luaopen_mysum(lua_State *L) {
        luaL_register(L, "mysum", my_funcs);
        return 1;
    }
    
  • 出来上がったmysum.dllをマクロファイルと同じ場所に配置

    出来上がったmysum.dllは、今回のdllの呼び出し元となるマクロやlua(今回はtest.macやtest.lua)と同じ場所に置いておきましょう。

  • hmLJから利用するためには

    hmLJ.dllとマクロファイル(***.mac)やluaファイル(***.lua)は、
    異なるディレクトリに置いているのが一般的だと思います。

    hmLJ.dllはhidemaru.exeと同じ場所ですが、各マクロファイルは、
    「マクロの機能単位」でサブディレクトリ等各々別の場所にあることでしょう。

    しかし、そのような状態では、hmLJ.dllは今回作成したmysum.dllを発見することが出来ません。

    なぜなら秀丸は現在実行中のマクロ(*.mac)自体のディレクトリはちゃんと管理していますが、
    lua層にはそのことが伝達されないためです。

    そこで、currentmacrodirectoryをlua層に伝達して、lua層では .dllのパス検索の範囲を広げる必要があります。

  • .mac側からマクロが存在するパス(=mysum.dllが存在するパス)を伝達する

    以下のものをtest.macとして保存しましょう。

    現在のマクロのパスをlua層の変数へと伝達しておきます。

    #L = loaddll( hidemarudir + "\\hmLJ.dll");
    
    #_ = dllfunc( #L, "SetStrVar", "currentmacrodirectory", currentmacrodirectory );
    
    #_ = dllfunc( #L, "DoFile", currentmacrodirectory + "\\test.lua" );
    
    freedll( #L );
    
  • .lua側で伝達されたパスを検索対象に加える

    以下のファイルを「test.lua」としましょう。
    伝達された「currentmacrodirectory」をdll検索に加えてから、requireします。

    -- カレントディレクトリがどこにあったとしても、
    -- マクロファイルと同じフォルダにある自分で追加したdllを実行可能とするために、
    -- cで作成したdllのパスを追加する。
    package.cpath = package.cpath .. ";" .. currentmacrodirectory .. "\\?.dll"
    -- 検索パスを秀丸デバッグモニターに表示
    hm.debuginfo(package.cpath)
    
    -- 今回作成したライブラリの読み込み
    require("mysum")
    -- 引数の数値の合計を秀丸デバッグモニターに表示
    hm.debuginfo(mysum.myfunc1(1,2,3,4,5))
    

    このようにすることで、ディレクトリの場所に依存することなく、
    自分で制作した独自のdllをLuaJIT内で利用可能となります。