monozuku

PC、DIY、電子工作等の情報を発信しています

LuaとLuabindでiPhoneプログラミング

LuaとLuabindを使ってiPhoneアプリケーションを作成するメモです。
ここではLua5.1.4およびLuabind0.9を使用しました。

関連ライブラリのインストール

Lua, Boost, Luabindがまだインストールできていない場合はインストールします。MacPortsを使用するのが比較的早いようです。

  1. 最新のMacPortsをダウンロードしてインストール
  2. sudo ports selfupdate
  3. sudo ports install lua
  4. sudo ports install boost

また、.bashrcと.zshrcをこのページを参考に設定しておきます。

export PATH=/opt/local/bin:/opt/local/sbin/:$PATH
export MANPATH=/opt/local/man:$MANPATH
export LIBRARY_PATH=/opt/local/lib:$LIBRARY_PATH
export LD_LIBRARY_PATH=/opt/local/lib:$LD_LIBRARY_PATH
export C_INCLUDE_PATH=/opt/local/include:$C_INCLUDE_PATH
export CPLUS_INCLUDE_PATH=/opt/local/include:$CPLUS_INCLUDE_PATH
export DYLD_FALLBACK_LIBRARY_PATH=/opt/local/lib
export BOOST_ROOT=/opt/local/include/boost:$BOOST_ROOT

つぎにLuabindをインストールします。
これもMacPortsから入れようとしたけどまだ使用できないみたいなので、サイトからダウンロードして直接luabindディレクトリを/opt/local/include/にコピーします。

% sudo cp -r luabind /opt/local/include/

iPhone (ARM) 用ライブラリの作成

iPhone用にLuaとLuabindのライブラリ(.a)を作成します。さらに、デバイス用とシミュレータ用の2種類も同時に作ります。

  1. Lua用ライブラリのプロジェクト(luaarm)をXCodeで作る

※プロジェクトの種類はCocoa Touch静的ライブラリとします。

  1. Luaのsrcフォルダの中身(lua.cとluac.c以外)をすべてClasses/に入れる
  2. シミュレータおよびデバイスの設定で、Releaseでビルドする
  3. build/ディレクトリに出力されるluaarm.aを/opt/local/lib/などにコピー
  4. Luabindも同様にビルドする。ただし、プロジェクト->プロジェクト設定を編集 から「ビルド」タブの「ヘッダ検索パス」に/opt/local/includeを追加。

lua.hppの編集

この日の記事のように、lua.hppを編集します。

#if __cplusplus
extern "C" {
#endif

#include "lua.h"
#include "lualib.h"
#include "lauxlib.h"

#if __cplusplus
}	// extern "C"
#endif

テストプロジェクトの作成

以上でインストールは完了です。テストプロジェクトを作成して試してみましょう。

プロジェクトを作成し、プロジェクトのヘッダ検索パスに/opt/local/includeを追加します。また、プロジェクトに先ほど作ったライブラリ libluabindarm.a および libluaarm.a を追加します。

以上のようにして使えるはずです。

L = luaL_newstate();
if(!L) {
    std::cout << "Failed to open lua\n";
    return;
}
luaL_openlibs(L);
luabind::open(L);

luabind::module(L) [
   ... 関数定義など
];

NSString *path = [[NSBundle mainBundle] pathForResource:[NSString stringWithCString:filename] ofTye:nil];

// ファイルを実行する
if(luaL_loadfile(L, [path UTF8Sring]) || lua_pcall(L, 0, 0, 0)) {
    std::cout << "Failed to load\n";
}

補足

  • nil.hppでエラーが出るときは、extern...の行をコメントアウトしても普通に動きました(保証はしませんが)。
  • Luabindを実行するファイルは拡張子を .mm (Objective-C++) としてください。
  • Luabindでバインドするクラス中でObjective-Cのクラスを使用したい場合は、そのクラス実装の拡張子を .mm とし、使用するObjective-Cのクラスのプロトタイプ宣言をclassキーワードで行うことで実現できます。

(MFC)リッチエディットコントロールを右端で折り返す

Windowsプログラミングに関するメモです。

MFCのリッチエディットコントロールにおいて、右端で折り返されないようにする方法を調べました。

SendMessage(hEdit, EM_SETTARGETDEVICE, 0, 1);

あるいは

CRichEditCtrl &edit = this->GetRichEditCtrl();
edit.SetTaretDevice(0, 1);

右端で折り返すようにするには、どちらも0を設定してやればいいようです。

XCodeでLuaを使う

いよいよXCodeLuaを使ってみましょう。

Luaのインストール

Luaのダウンロードページから最新版のソースをダウンロードしてきます。現時点ではlua-5.1.4.tar.gzでした。
ターミナルを開き、次のように適当なフォルダの中で解凍、インストールします。

tar -xzvf lua-5.1.4.tar.gz
cd lua-5.1.4
make macosx
make test
sudo make install

これで/usr/local/includeの中にインクルードファイルが、bin/の中にライブラリが入るはずです。

XCodeで使う

準備

XCodeを開き、新規プロジェクトを作成します。ここではC++ Toolとしました。

Luaを使うための設定をします。まず、インクルードパスの設定をしましょう。
メニューからProject->Edit Active Target "XXXX"を選択します。

"Build"タブの"Header Search Paths"の項目を、"/usr/local/include"とします。これでluaのインクルードファイルが読み込めるようになります。

次に、ライブラリを読み込みます。同じウィンドウの"General"タブをクリックし、"Linked Libraries"にliblua.aライブラリを追加します。一覧には無いのでAdd Other...でパスを指定してやる必要があります。

これで完了です。

テスト実行

次のようなソースでテストしてみましょう。

// main.cpp
#include <iostream>
#include <lua.hpp>

int main(int argc, char * const argv[]) {
    lua_State *L = luaL_newstate();
    luaL_openlibs(L);
    luaL_dofile(L, "/.../hello.lua");
    
    return 0;
}
-- test.lua
print("Hello, World!")

正しく実行できたでしょうか。

Objective-C++でLuaを使うときの注意点

Objective-C++を使ってLuaを扱うとき、そのままをインポートするとエラーが出た。
この場合、を次のように書き換えるといいようだ。

#if __cplusplus
extern "C" {
#endif

#include "lua.h"
#include "lualib.h"
#include "luaxlib.h"

#if __cplusplus
}   // extern "C"
#endif

ちなみに、Objective-C++を扱うときは.mとしていたソースをすべて.mmという拡張子に変えたほうがいいようだ。この2点を変更することによってObjective-C++からLuaを扱えるようになった。

glpngはどこにあるのか

GLpngはOpenGLpng画像を読み込むのに便利なライブラリ。公式ダウンロードリンクが切れているのでどこにあるかわからない。
と、ネットを調べていたら以下の場所にあるとのこと。

ftp://ftp.usa.openbsd.org/pub/OpenBSD/distfiles/glpng-1.45/glpng.zip

とりあえずメモしておきます。

Visual C++でluabindを使ってみる

引き続いて、Visual C++でLuabindを使ってLuaスクリプトを実行してみたいと思います。

Luabindとは

Luabindとは、C++Luaスクリプトをうまく結びつけるためのライブラリで、これを使うことでクラスの実装や関数のオーバーロードが簡単に出来ます。
ドキュメントを読んでいると英語を読まないでもなんとなく使い方がわかりますが、実際に使ってみましょう。
実行にはBoost 1.30以上が必要なようです。Visual C++をインストールしていれば既にあるはずですが。

Luabindの準備

最新のソースが上記のホームページからダウンロードできます。解凍フォルダを適当な場所に置き、Visual C++からインクルードディレクトリ(luabindを解凍したフォルダ)を指定してやると準備完了です。
新規プロジェクトをつくり、ソースファイルにluabindのsrcフォルダ以下のソースファイルを追加してやり、

#include <luabind/luabind.hpp>

としてやれば準備完了というやつです。

サンプルプログラム

それではサンプルプログラムをつくってみましょう。

#include <iostream>
#include <lua.hpp>
#include <luabind/luabind.hpp>

#pragma comment(lib, "lua5.1.lib")

void greet(const char* name)
{
    std::cout << "My name is " << name << std::endl;
}

class MyClass {
private:
    int val;
public:
    MyClass(int num) {
        val = num;
    }
    void show(void) {
        show(0);
    }
    void show(int num) {
        std::cout << val + num << std::endl;
    }
    void show(const char *str) {
        std::cout << str << val << std::endl;
    }
};

int main(int argc, char* argv[])
{
    // Luaを開く
    lua_State* L = luaL_newstate();
    luaL_openlibs(L);

    // luabindを開く
    luabind::open(L);
    luabind::module(L) [
        luabind::def("greet", (void(*)(const char*)) &greet),
        luabind::class_<MyClass>("MyClass")
            .def(luabind::constructor<int>())
            .def("show", (void(MyClass::*)(void))&MyClass::show)
            .def("show", (void(MyClass::*)(int))&MyClass::show)
            .def("show", (void(MyClass::*)(const char*))&MyClass::show)
    ];

    // Luaスクリプトを開く
    if(luaL_loadfile(L, "C:\\Lua\\test\\sample2.lua") || lua_pcall(L, 0, 0, 0)) {
        perror(lua_tostring(L, -1));
    }

    // Luaを閉じる
    lua_close(L);

    getchar();
    return 0;
}

このluabind::となっているところでluabindを初期化、およびクラスや関数をバインドしています。詳しくは後で書くことにして、Luaスクリプトの方を書いていきましょう。

-- sample.lua
--------------------------------------------------------------------------------

Name = "ちゃーちゃん"

greet(Name)

obj = MyClass(100)
obj:show()
obj:show(100)
obj:show("The value is ")

ビルドにはかなり時間がかかりましたが、おそらく最適化を行っているためでしょう。成功すればつぎのようになるはずです。

クラスの実装および関数オーバーロードLua上でできていることがわかります。

Luabindを開く

Luabindは全体が"luabind"名前空間に属しています。そのため、スコープ解決子::を使って次のようにアクセスします。

luabind::open(L)

これによってLuabindをオープンします。

関数を登録するには次のようなコードを書きます。

luabind::module(L) [
    luabind::def("greet", (void(*)(const char*)) &greet)
];

見た目は気持ち悪いけど、見ていてなんとなくわかります。関数ポインタの前の型キャストは、関数名がダブるほかの関数がなければ書かなくても問題ないです。他の関数を登録するにはコンマ(,)で区切って複数書きます。

クラスを登録するには次のようにします。

luabind::module(L) [
    luabind::class_<MyClass>("MyClass")
        .def(luabind::constructor<int>())
        .def("show", (void(MyClass::*)(void))&MyClass::show)
];

メンバ関数を.def関数で記述していきます。コンストラクタやデストラクタ、演算子オーバーロードなども実装できるということで柔軟性が高いです。このあたりは後にまた付け加えるかもしれません。