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

概要

luajitで関数作成するのには

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

の2通りあります。

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

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

ダウンロード

PICTURE
更新日 2017/04/05
hmLJ.lib.zip ver 2.041

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

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

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

PICTURE

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

PICTURE

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

PICTURE

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

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

PICTURE

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

PICTURE

これで、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内で利用可能となります。

PICTURE