高速スクリプト言語「Lua」を始めよう!(5)

211 views
Skip to first unread message

sagasw

unread,
Aug 7, 2009, 5:49:18 AM8/7/09
to lu...@googlegroups.com

本稿では動作速度が高速で、非常に移植性が高い組み込み向けのプログラミング言語「Lua」の 使い方について紹介します。Lua は、JavaScript や Pascal に似ていることから、とても手に馴染みやすいのが特徴です。自作アプリケーションにちょっとしたスクリプト言語を組み込みたい場合に重宝します。そこで、 Lua のインストールから、簡単な使い方を紹介し、簡単なアプリケーションに組み込んで使うまでの過程を解説します。

本稿の目標

本稿では、Lua がどんなプログラミング言語なのかを紹介し、実際に簡単なアプリケーションに組み込んで使ってみるところまでを紹介します。

  • 第一回目は、Lua とは何か、そして、どんな風にプログラムを書くのかを紹介しました。
  • 第二回目は、Lua の便利なテーブル型や文字列の操作について紹介しました。
  • 第三回目は、Lua の関数やメタテーブルについて紹介しました。
  • 第四回目は、Lua のオブジェクト指向について紹介しました。

今回は、自作アプリケーションに、Lua を組み込んで使う方法を紹介します。

自作アプリに Lua を組み込んで使おう

C/C++言語やDelphiなどのコンパイルする言語に比べて、スクリプト言語である Lua はとても手軽に使えます。C/C++ よりも Lua の方が手軽に使えるという部分については、これまでの連載を見てきた方なら、納得していただけるのではないでしょうか。

Lua は組み込み用途を考慮されて作られているので、C/C++/Delphiなど自作のアプリケーションの補助スクリプトとして組み込むのにぴったりです。し かも、Lua の本体には、それほど機能がついていませんので、組み込み後の実行ファイルのサイズも非常に小さくてすみます。

今回は、本連載の一回目でコンパイルした Lua のソースコードを利用して自作のCアプリケーションから呼び出して使う方法を紹介します。

一番簡単な Lua プログラム

C言語で Lua のスクリプトファイルを実行する一番簡単なサンプルは以下のようになります。以下を、test.c という名前で保存します。

// file:test.c
#include <stdio.h>
#include "lua.h"

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

int main(int argc, char* argv)
{
// Lua の言語エンジンを初期化
lua_State *lua = luaL_newstate();
// Luaのライブラリを使えるようにする
luaL_openlibs(lua);
// Luaのスクリプトを読み込む
luaL_dofile(lua, "hello.lua");
return 0;
}

上のプログラムから実行される Lua のプログラム "hello.lua" は、以下のようになっています。

-- hello.lua
print("Hello, World!")

Cygwin の gcc から "test.c" をコンパイルするには次のようにします。コンパイルのためにヘッダファイル(*.h)のファイルと、lua51.dll (あるいは、lua5x.dll)を同じフォルダにコピーしておきます。

そして、コマンドラインから次のように入力します。

$ gcc -o test.exe test.c lua51.dll

ここまでの手順で、test.exe が生成されます。実行すると「Hello, World!」と表示されます。

実行するには、以下のようにします。

$ ./test.exe

C言語から Lua のプログラムを呼び出す

それでは、C言語から Lua のプログラムを呼び出す方法を紹介します。はじめに、Lua のプログラムから紹介します。

-- func1.lua
function lua_add(a, b)
return (a + b);
end

このソースファイルの関数「lua_add」を呼び出すC言語のソースが以下になります。

// file:test.c
#include <stdio.h>
#include "lua.h"
#include "lualib.h"
#include "lauxlib.h"

int main(int argc, char* argv)
{
lua_State *L = luaL_newstate();
luaL_openlibs(L);// Luaのライブラリを使えるようにする
luaL_dofile(L, "func1.lua"); // Luaのスクリプトを読み込む
//
// 関数を呼ぶためにスタックに値を積む
//
lua_getglobal(L,"lua_add"); // 関数名を積む
lua_pushnumber(L, 12); // 第1引数の値を積む
lua_pushnumber(L, 6); // 第2引数
//
// 関数を呼ぶ..引数(2つ)、戻り値(1つ)
lua_call(L, 2, 1);
//
// 関数の結果を得る
int n = lua_tointeger(L, -1);
lua_pop(L, 1);
//
printf("%d\n", n);
// 閉じる
lua_close(L);
return 0;
}

簡単に組み込めるという割には、意外と面倒なと思われたのではないでしょうか。もう少し、関数を呼ぶ部分を見てみます。

lua_getglobal(L,"lua_add"); // 関数名を積む
lua_pushnumber(L, 12); // 第1引数の値を積む
lua_pushnumber(L, 6); // 第2引数
lua_call(L, 2, 1); // 関数を呼ぶ..引数(2つ)、戻り値(1つ)

Lua の関数を呼ぶときには、スタックを意識する必要があります。関数の引数や戻り値はスタックを介して処理されるのです。

関数の呼び出し時には、はじめに、関数の名前をスタックに積み、その後、関数の引数をスタックに積みます。そして「lua_call」を呼び出すことで、実際に Lua の関数が実行されます。

実行結果を得るには、以下のように記述します。Luaでは複数の戻り値を得ることができます。関数の結果を得るには、「lua_tointeger()」や「lua_tonumber()」などの関数を利用しますが、これらは、スタックから値を取り出すのに利用します。

// 関数の結果を得る
int n = lua_tointeger(L, -1); // 戻り値の一つ目を得る
lua_pop(L, 1);

「lua_toXXX()」系の関数は、スタックから値を取り出すものですが、関数の第1引数には lua_State* 型の第2引数には、スタックの位置を指定します。値が0以下の場合、スタックの後ろからn個目の値を返すようになっています。

エラー処理を行う

上記のプログラムは、まったくエラー処理を行っていませんので、何か問題があったときにその原因が分からない状態になります。実は、上記のようなプ ログラムをはじめての作ったときには、Luaのプログラムを読み込む「luaL_dofile()」が失敗しているのに気づかず、ずいぶん悩んでしまいま した。

そこで、ファイルのロードや関数の呼び出しがうまく行っているかどうかチェックするように書き換えたのが以下になります。

// file:test.c
#include <stdio.h>
#include "lua.h"
#include "lualib.h"
#include "lauxlib.h"

void show_error(lua_State* L)
{
const char* err = lua_tostring(L, -1);
printf("ERROR: %s\n", err);
}

int main(int argc, char* argv)
{
lua_State *L = luaL_newstate();
luaL_openlibs(L);// Luaのライブラリを使えるようにする
// Luaのスクリプトを読み込む
if (luaL_loadfile(L, "func1.lua") || lua_pcall(L,0,0,0)) {
printf("func1.luaが読めませんでした。\n");
show_error(L);
return -1;
}
// 関数を呼ぶためにスタックに値を積む
lua_getglobal(L,"lua_add"); // 関数名を積む
lua_pushnumber(L, 12); // 第1引数の値を積む
lua_pushnumber(L, 6); // 第2引数
// 関数を呼ぶ..引数(2つ)、戻り値(1つ)
if (lua_pcall(L, 2, 1, 0) != 0) {
printf("lua_add()の呼び出しに失敗しました。\n");
show_error(L);
return -1;
}
// 関数の結果を得る
int n = lua_tointeger(L, -1);
lua_pop(L, 1);
//
printf("%d\n", n);
// 閉じる
lua_close(L);
return 0;
}

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

次に、その逆で、Lua から C言語の関数を呼び出す方法を紹介します。C言語側では「c_add(a,b)」という関数を定義し、これをLuaスクリプトから呼び出してみます。

まずは、fnuc2.lua というスクリプトを保存します。

-- func2.lua
print(c_add(3,4))

次に C言語側で、c_add() という関数を定義してみます。以下は「lua_register()」を使って、C言語で定義した関数「c_add()」を Lua に登録します。

// file:test.c
#include <stdio.h>
#include "lua.h"
#include "lualib.h"
#include "lauxlib.h"

/** 今回作成した足し算を行う関数 */
int c_add(lua_State* L)
{
// 引数を得る
int a1 = lua_tointeger(L, 1);// 第一引数を得る
int a2 = lua_tointeger(L, 2);// 第二引数を得る
// 計算
int r = a1 + a2;
printf("[%d + %d]\n", a1, a2);
// 結果をスタックに戻す
lua_pushinteger(L, r);
return 1; // 戻り値の数を返す
}

/** エラーを表示するための関数 */
void show_error(lua_State* L)
{
const char* err = lua_tostring(L, -1);
printf("ERROR: %s\n", err);
}

/** メイン */
int main(int argc, char* argv)
{
lua_State *L = luaL_newstate();
luaL_openlibs(L);// Luaのライブラリを使えるようにする
// Luaに関数を登録する
lua_register(L, "c_add", c_add);
// Luaのスクリプトを読み込む
if (luaL_loadfile(L, "func2.lua") || lua_pcall(L,0,0,0)) {
printf("func2.luaが読めませんでした。\n");
show_error(L);
return -1;
}
// 閉じる
lua_close(L);
return 0;
}

こちらも、スタックの概念さえ分かっていれば、それほど難しいものではないでしょう。スタックから値を取り出して、関数の処理結果をスタックに積むという仕組みです。

具体的には、呼び出された C言語側の関数で、「lua_tointeger()」や「lua_tonumber()」などを使ってスタックに積まれている引数を取り出して、計算を 行い「lua_pushinteger()」や「lua_pushdouble()」などを使って計算結果をスタックに返します。

まとめ

以上、今回は、Lua を C言語から利用する方法を紹介しました。スタックの仕組みさえ分かっていれば、理解しやすいと思います。以上、Lua を組み込み言語として、C言語から使う基本的な方法を紹介しました。次回はもう少し複雑な例を見ていきたいと思います。お楽しみに。

Series Navigation«高速スクリプト言語「Lua」を始めよう!(4)高速スクリプト言語「Lua」を始めよう!(6)»

Posted by クジラ飛行机 | 2008年11月26日 10:56 | permalink

このサイトについて

八角研究所
株式会社八角研究所のWEBサイトですよー。 いろんなものを創り出すことのできる環境をコツコツ構築中。 いったい、いつになったらできるのか。 この技術情報サイトもそのための活動の一環のつもり。

執筆者紹介

クジラ飛行机

クジラ飛行机

くじらはんど(http://kujirahand/)にて、日本語プログラミング言語「なでしこ」(IPA未踏ユース採択)、テキスト音楽「サク ラ」(OSPオンラインソフト大賞入賞)など多くのオンラインソフトを開発。著書に「Flexプロフェッショナルガイド」「なでしこ公式バイブル」、「一 週間でマスターするActionScript3.0」など。
Reply all
Reply to author
Forward
0 new messages