C++アプリにluaスクリプトエンジンを組み込む


この記事について

この記事ではC++のプログラムにluaを使ったスクリプトエンジンを埋め込む方法を説明します。

スクリプトエンジンとは?

C++アプリケーションから他の言語で書かれたスクリプトを呼び出します。
以下のような場合に使われます。

  1. 仕様がころころ変わる
  2. ユーザにカスタマイズする手段を提供したい

言語としてはPythonやRubyが使われます。
本記事ではluaを用いたスクリプトエンジンの実装方法を解説します。

luaとは?

プログラミング言語の一つです。Cのプログラムに対して埋め込むことを考えて設計されています。
スクリプトエンジンに最適な言語の一つです。
詳細はWikipediaを見てください。

深く知りたい場合は公式サイトみると良いでしょう。

環境

本記事で紹介する方法はubuntu linux 18.04を使います。
WindowsやMacでも同様な方法でできるかもしれません。(ところどころ修正が必要ですが…)

ビルド構成

早速自分のプログラムにluaを埋め込んで見ましょう。
あなたのCプログラムにluaを埋め込むにはluaライブラリをリンクする必要があります。
ここではCMakeを使って

  1. luaのソースを取得
  2. luaライブラリをコンパイル
  3. 生成されたluaライブラリをリンクする

方法を順に説明します。

luaのソースを取得するにはCMakeのExternalProjectモジュールを使います。

###################################################################
# enable ExternalProject
include(ExternalProject)
set(EXTERNAL_INSTALL_LOCATION ${CMAKE_BINARY_DIR}/external)
include_directories(${EXTERNAL_INSTALL_LOCATION}/include)
link_directories(${EXTERNAL_INSTALL_LOCATION}/lib)

上記のコードは

  1. ExternalProjectをインクルード
  2. luaのダウンロード先の設定
  3. 生成されたluaライブラリの置き場
  4. 生成されたluaライブラリの置き場をインクルード先に設定

を指定します。

続いて、luaのビルド設定をcmakeに書きます。

###################################################################
# download and build lua
ExternalProject_Add(
    lua-ext
    GIT_REPOSITORY https://github.com/LuaDist/lua.git
    GIT_TAG master
    CMAKE_ARGS -DCMAKE_INSTALL_PREFIX=${EXTERNAL_INSTALL_LOCATION} -DBUILD_SHARED_LIBS=OFF
)

ADD_LIBRARY(lua IMPORTED STATIC)
SET_TARGET_PROPERTIES(lua PROPERTIES IMPORTED_LOCATION ${EXTERNAL_INSTALL_LOCATION}/lib/liblua.a)
ADD_DEPENDENCIES(lua lua-ext)

上記のコードは

  1. luaのコードのダウンロード元のgitレポジトリ
  2. リンク時に参照されるcmakeのシンボル定義

を行っています。

luaのインターフェースはC言語で書かれています。
使いづらいこともあるのでC++のラッパーを用意すると良いでしょう。
いろんなラッパーがあるのではここではsol3ライブラリを使います。

###################################################################
# download and build sol2
ExternalProject_Add(
    sol2-ext
    GIT_REPOSITORY https://github.com/ThePhD/sol2.git
    GIT_TAG develop
    CMAKE_ARGS -DCMAKE_INSTALL_PREFIX=${EXTERNAL_INSTALL_LOCATION}
)

solはヘッダオンリーのライブラリです。コンパイルが必要ないので、luaのときのようにcmakeが参照するシンボル設定は必要ありません。

最後に自分のプログラムにluaライブラリを自分のアプリケーションにリンクします。

##################################
# Define a Main
file(GLOB_RECURSE MAIN_SRC main/*.cpp)
add_executable(MyApp ${MAIN_SRC})
target_link_libraries(MyApp m dl lua) # luaをリンク
target_include_directories(Main PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/main/ ${Boost_INCLUDE_DIR})

luaをリンクします。luaは、Posixのmライブラリとdlライブラリに依存しているので、これらのライブラリも一緒にリンクされるように指定します。

ScriptEngineクラス

luaをC++アプリに組み込むには最低限以下のステップが必要です。

  1. luaのエンジンを初期化する
  2. C++の型をluaから参照できるようにする
  3. C++の変数の参照をluaに渡す
  4. luaを実行する

それでは順に見ていきましょう。

solライブラリを使えばsol::stateクラスにラップされたluaエンジンのハンドラが取得できます。

myapp.cpp
#include <sol/sol.hpp> // ライブラリをインクルード

...c++:myapp.cpp
sol::state lua;
lua.open_libraries(sol::lib::base);

取得したハンドラを使ってC++のアプリで使っている型をおしえます。

myapp.cpp
struct Command
{
    double turn;
    double throttle;
};

上記のような構造体を以下のコードでラップすることによりluaエンジンから参照できるようになります。

myapp.cpp
lua.new_usertype<Command>("Command",
    "turn", &Command::turn,
    "throttle", &Command::throttle);

luaエンジンにc++の変数の参照を渡すのは簡単です。
以下のコードによりluaエンジンにcommandというグローバル変数を定義します。
そのグローバル変数はc++のcommand変数の参照となるので、luaのコードからcommand変数を操作するとc++のコードのcommand変数もかわります。

myapp.cpp
Command command;
lua["command"] = &command;

luaのスクリプトを書くときがきました。
以下のスクリプトではcommandグローバル変数のメンバに1と2を設定します。

script.lua
command.turn = 1
command.throttle = 2

c++側ではパスを指定して上記のスクリプトを実行します。

myapp.cpp
lua.script_file("script.lua");

スクリプトを実行するとc++のコードのcommand変数のメンバが1と2に変わります。

最後に

c++アプリへのluaスクリプトの組み込み方法を紹介しました。
luaのスクリプトエンジンをc++に組み込んだアプリを作ってみたので良かったら参考にしてみてください。

以下のようなスクリプトで

brain.lua
command.throttle = 1.0
command.turn = 1.0

白い玉の動作を定義できます。