Lua スクリプトからの C++ クラスの利用

Lua から C++ のクラスやテンプレートを用いる方法を紹介します。

Luabind を利用する場合

Luabind を用いることで、自分でグルーコードを記述する手間が省けます。ただし Luabind を用いるとコンパイル時間が増えます。Luabind を利用するソースコードは、頻繁に更新しないファイルに分けておきましょう。

また、Luabind についての詳細は Luabind 本家 (英語) をご覧下さい。

関数をバインド

delay() という関数を Lua スクリプトで使えるように luabindSystem() で C++ 関数をバインドしています。

delay.h

#ifndef QRK_DELAY_H
#define QRK_DELAY_H

namespace qrk
{
  extern void delay(int msec);
}

#endif /* !QRK_DELAY_H */


luabindSystem.cpp

#include "luabindSystem.h"
#include "luabindInit.h"
#include "delay.h"
#include "getTicks.h"
#include "CycleTimer.h"
#include <luabind/luabind.hpp>

using namespace qrk;
using namespace luabind;


void qrk::luabindSystem(void)
{
  lua_State* lua = luabindInit();

  module(lua)
    [
     def("delay", &delay),
     def("getTicks", &getTicks),

     class_<CycleTimer>("CycleTimer")
     .def(constructor<>())
     .def("waitMsec", &CycleTimer::waitMsec)
     .def("reset", &CycleTimer::reset)
     .def("setStrictCycle", &CycleTimer::setStrictCycle)
     .def("setCycleFps", &CycleTimer::setCycleFps)
     .def("cycleFps", &CycleTimer::cycleFps)
     .def("setCycleMsec", &CycleTimer::setCycleMsec)
     .def("cycleMsec", &CycleTimer::cycleMsec)
     .def("toNextCycleMsec", &CycleTimer::toNextCycleMsec)
     ];
}

実際に Lua スクリプト内で delay() を呼び出すには、あらかじめ luabindSystem() を C++ プログラム内で実行しておく必要があります。


クラスをバインド

以下は、GUI モジュールの各クラスを Lua にバインドした実装です。

luabindGui.cpp

#include "luabindGui.h"
#include "luabindInit.h"
#include "LuaHandler.h"
#include "Screen.h"
#include "TextInput.h"
#include "EventUtils.h"
#include "LayerManager.h"
#include "Layer.h"
#include "Font.h"
#include "TextSurface.h"
#include "ColorSurface.h"
#include "ImageSurface.h"
#include "MultiSurface.h"
#include "Label.h"
#include "Button.h"
#include "Menu.h"
#include "ChatMessage.h"
#include "CallbackEvent.h"
#include "AlignUtils.h"
#include <luabind/luabind.hpp>

using namespace qrk;
using namespace luabind;


void qrk::luabindGui(void)
{
  lua_State* lua = luabindInit();

  module(lua)
    [
     class_<Color>("Color")
     .def(constructor<float, float, float, float>())
     .def_readwrite("r", &Color::r)
     .def_readwrite("g", &Color::g)
     .def_readwrite("b", &Color::b),

     class_<Screen>("Screen")
     .def(constructor<>())
     .def("rect", &Screen::rect)
     .def("isFullscreen", &Screen::isFullscreen)
     .def("toggleFullscreen", &Screen::toggleFullscreen)
     .def("showCursor", &Screen::showCursor)
     .def("isShowCursor", &Screen::isShowCursor)
     .def("setClearColor", &Screen::setClearColor)
     .def("clear", &Screen::clear),

     class_<Event>("Event"),

     class_<CallbackEvent, Event>("CallbackEvent")
     .def(constructor<>())
     .def("clearEvent", &CallbackEvent::clearEvent)
     .def("key", &CallbackEvent::key)
     .def("click", &CallbackEvent::click)
     .def("enterCursor", &CallbackEvent::enterCursor)
     .def("moveCursor", &CallbackEvent::moveCursor)
     .def("isActive", &CallbackEvent::isActive)
     .def("setCallback", &CallbackEvent::setCallback),

     class_<TextInput, Event>("TextInput")
     .def(constructor<>())
     .def("clear", &TextInput::clear)
     .def("empty", &TextInput::empty)
     .def("text", &TextInput::text)
     .def("isChanged", &TextInput::isChanged)
     .def("setBackspaceEvent", &TextInput::setBackspaceEvent)
     .def("setDecideEvent", &TextInput::setDecideEvent),

     def("setQuitEvent", &setQuitEvent),
     def("setEnterEvent", &setEnterEvent),

     class_<LayerManager>("LayerManager")
     .def(constructor<>())
     .def("updateEvent", &LayerManager::updateEvent)
     .def("draw", &LayerManager::draw)
     .def("swap", &LayerManager::swap)
     .def("clearEvent", &LayerManager::clearEvent)
     .def("insertEvent", &LayerManager::insertEvent)
     .def("removeEvent", &LayerManager::removeEvent)
     .def("clear", &LayerManager::clear)
     .def("push_front", &LayerManager::push_front)
     .def("push_back", &LayerManager::push_back)
     .def("remove", &LayerManager::remove),

     class_<Layer>("Layer")
     .def(constructor<>())
     .def("clear", &Layer::clear)
     .def("push_front", &Layer::push_front)
     .def("push_back", &Layer::push_back)
     .def("remove", &Layer::remove)
     .def("setAlpha", &Layer::setAlpha)
     .def("draw", &Layer::draw),

     class_<Font>("Font")
     .def(constructor<const char*, size_t, bool>())
     .def("duplicate", &Font::duplicate)
     .def("isValid", &Font::isValid)
     .def("setFontSize", &Font::setFontSize)
     .def("fontSize", &Font::fontSize)
     .def("setForegroundColor", &Font::setForegroundColor)
     .def("foregroundColor", &Font::foregroundColor)
     .def("setBackgroundColor", &Font::setBackgroundColor)
     .def("backgroundColor", &Font::backgroundColor)
     .def("setTransparent", &Font::setTransparent)
     .def("transparent", &Font::transparent),

     class_<Surface>("Surface"),

     class_<TextSurface, Surface>("TextSurface")
     .def(constructor<const Font&, const char*>())
     .def("rect", &TextSurface::rect)
     .def("setAlpha", &TextSurface::setAlpha)
     .def("draw", &TextSurface::draw),

     class_<ColorSurface, Surface>("ColorSurface")
     .def(constructor<const Rect<long>&, const Color&>())
     .def("rect", &ColorSurface::rect)
     .def("setAlpha", &ColorSurface::setAlpha)
     .def("draw", &ColorSurface::draw),

     class_<ImageSurface, Surface>("ImageSurface")
     .def(constructor<const char*, bool>())
     .def("rect", &ImageSurface::rect)
     .def("setAlpha", &ImageSurface::setAlpha)
     .def("draw", &ImageSurface::draw),

     class_<MultiSurface, Surface>("MultiSurface")
     .def(constructor<Surface&>())
     .def("rect", &MultiSurface::rect)
     .def("setAlpha", &MultiSurface::setAlpha)
     .def("draw", &MultiSurface::draw)
     .def("push_front", &MultiSurface::push_front),

     class_<Component>("Component"),

     class_<Label, Component>("Label")
     .def(constructor<Surface&>())
     .def("setPosition", &Label::setPosition)
     .def("position", &Label::position),

     class_<Button, Component>("Button")
     .def(constructor<Surface&, Surface&, Surface&>()),

     class_<Menu, Component>("Menu")
     .enum_("Align")
     [
      value("Left", Menu::Left),
      value("Center", Menu::Center),
      value("Right", Menu::Right),
      value("Top", Menu::Top),
      value("Middle", Menu::Middle),
      value("Bottom", Menu::Bottom)
      ]
     .def(constructor<const Rect<long>&, Menu::Align>())
     .def("setButtonStep", &Menu::setButtonStep)
     .def("addButton", &Menu::addButton)
     .def("setEnableNoSelect", &Menu::setEnableNoSelect)
     .def("setEnableRotate", &Menu::setEnableRotate)
     .def("setIndex", &Menu::setIndex)
     .def("isClicked", &Menu::isClicked)
     .def("clickedId", &Menu::clickedId),

     class_<ChatMessage, Component>("ChatMessage")
     .def(constructor<Font&, const Rect<long>&>())
     .def("setPosition", &ChatMessage::setPosition)
     .def("position", &ChatMessage::position)
     .def("setDrawTimeout", &ChatMessage::setDrawTimeout)
     .def("setFadeoutTime", &ChatMessage::setFadeoutTime)
     .def("setOffset", &ChatMessage::setOffset)
     .def("clear", &ChatMessage::clear)
     .def("print", &ChatMessage::print)
     .def("newline", &ChatMessage::newline),

     def("left", (long(*)(const Component&, long))&left),
     def("center", (long(*)(const Component&, long))&center),
     def("right", (long(*)(const Component&, long))&right),
     def("top", (long(*)(const Component&, long))&top),
     def("middle", (long(*)(const Component&, long))&middle),
     def("bottom", (long(*)(const Component&, long))&bottom),

     def("left", (long(*)(const Surface&, long))&left),
     def("center", (long(*)(const Surface&, long))&center),
     def("right", (long(*)(const Surface&, long))&right),
     def("top", (long(*)(const Surface&, long))&top),
     def("middle", (long(*)(const Surface&, long))&middle),
     def("bottom", (long(*)(const Surface&, long))&bottom)
     ];

  LuaHandler::registerValue("SDLK_BACKSPACE", SDLK_BACKSPACE);
  LuaHandler::registerValue("SDL_KEYDOWN", SDL_KEYDOWN);
  LuaHandler::registerValue("KMOD_NONE", KMOD_NONE);
}

バインド対象の各クラスは、以下のリポジトリに配置されています。
svn checkout http://svn.sourceforge.jp/svnroot/qrobosdk/trunk/libs/gui


テンプレートをバインド

x, y を公開メンバ変数に持つテンプレート Point と、x, y, w, h を公開メンバ変数に持つ Rect をバインドした例です。


luabindGeometry.cpp

#include "luabindGeometry.h"
#include "luabindInit.h"
#include "Point.h"
#include "Rect.h"
#include <luabind/luabind.hpp>

using namespace qrk;
using namespace luabind;


void qrk::luabindGeometry(void)
{
  lua_State* lua = luabindInit();

  module(lua)
    [
     class_<Point<long> >("Point")
     .def(constructor<long, long>())
     .def_readwrite("x", &Point<long>::x)
     .def_readwrite("y", &Point<long>::y),

     class_<Point<double> >("fPoint")
     .def(constructor<double, double>())
     .def_readwrite("x", &Point<double>::x)
     .def_readwrite("y", &Point<double>::y),

     class_<Rect<long> >("Rect")
     .def(constructor<long, long, long, long>())
     .def_readwrite("x", &Rect<long>::x)
     .def_readwrite("y", &Rect<long>::y)
     .def_readwrite("w", &Rect<long>::w)
     .def_readwrite("h", &Rect<long>::h),

     class_<Rect<double> >("fRect")
     .def(constructor<double, double, double, double>())
     .def_readwrite("x", &Rect<double>::x)
     .def_readwrite("y", &Rect<double>::y)
     .def_readwrite("w", &Rect<double>::w)
     .def_readwrite("h", &Rect<double>::h)
     ];
}

テンプレート引数が異なる場合は、複数登録が必要な点に注意しましょう。


シングルトンクラスのバインド

シングルトンのクラスを Lua にバインドするには、2つの方法があります。

以下に、両方の方法でシングルトンクラスのメンバメソッドを呼び出しているサンプルを示します。


SingletonClass.h

#ifndef SINGLETON_CLASS_H
#define SINGLETON_CLASS_H

#include <iostream>


class SingletonClass
{
  SingletonClass(void)
  {
  }


public:
  static SingletonClass* object(void)
  {
    static SingletonClass singleton_object;
    return &singleton_object;
  }


  void hello(void)
  {
    std::cout << "hello" << std::endl;
  }
};

#endif /* !SINGLETON_CLASS_H */


registerSingleton.cpp

#include "SingletonClass.h"
#include "LuaHandler.h"
#include "luabindInit.h"
#include <luabind/luabind.hpp>

using namespace qrk;
using namespace luabind;


namespace
{
  void luabindSingleton(void)
  {
    lua_State* lua = luabindInit();

    module(lua)
      [
       class_<SingletonClass>("SingletonClass")
       .def("hello", &SingletonClass::hello),

       // シングルトンを取得する関数として登録
       def("SingletonClass_object", &SingletonClass::object)
       ];

    // Lua に直接登録する例
    object lua_global = globals(lua);
    lua_global["SingletonClass"] = SingletonClass::object();
  }
}


int main(int argc, char *argv[])
{
  static_cast<void>(argc);
  static_cast<void>(argv);

  luabindSingleton();
  return LuaHandler::doFile("use_singleton.lua");
}


use_singleton.lua

-- シングルトンの利用サンプル
-- Satofumi KAMIMURA
-- $Id: use_singleton.lua 772 2009-05-05 06:57:57Z satofumi $

-- 関数越しにシングルトンを作成して利用
local singleton_class = SingletonClass_object()
singleton_class:hello()

-- Lua に直接登録したオブジェクトを利用
SingletonClass:hello()

簡単ですね。
Luabind は他にも色々なことができます。詳しくは Luabind 本家 のサイトをご覧下さい。


コメントページ


参考



Generated on Mon May 18 11:11:10 2009 by  doxygen 1.5.7.1