2006-06-08

Lua Tips

LUA는 현존하는 가장 가볍고 빠른 스크립트 언어로, 발더스게이트1과 월드 오브 워크래프트에서 사용되고 있다.

LuaPack

바이너리 데이터를 pack/unpack 하는 라이브러리. 일단 require 해주면 string.pack, string.unpack 의 2개 함수가 추가된다. 레이옷은 '''순수 루아 테스트 클라이언트'''를 만들기 위해서 LuaSocket 과 함께 사용했다. 단, 리눅스에서 사용하던 놈이라, 윈도우 환경에서 dll 로 사용하기 위해서는 약간의 수고가 필요하다.

  • 기존 루아 프로젝트를 dll 기반으로 수정해줘야 한다. 또한, 같이 배포되는 pack.lua 은 동작하지 않으며, LuaSocket 의 compat-5.1.lua 에서 제공하는 LUA_CPATH 및 새 require() 를 이용해야 한다.
  • lpack.c 의 luaopen_pack() 앞에 __declspec(dllexport) 달아줄 것
  • dll 이름과 luaopen_xxx() 을 동일하게 해줄 것. pack.dll & luaopen_pack() 또는 lpack.dll & luaopen_lpack()을 권장함
  • 다음과 같은 배치 파일 및 테스트용 루아를 실행, 2개 함수가 function:xxxxx 이라고 나오면 성공~
# test_luapack.bat
set LUA_CPATH=?.dll
lua test_luapack.lua
pause

-- test_luapack.lua
dofile('compat-5.1.lua')
require("lpack")
print(string.pack)
print(string.unpack)

변환표








type string 의미 z zero-terminated string p string preceded by length byte
P string preceded by length word
a string preceded by length size_t
A string
f float
d double
n Lua number
c char
b byte = unsigned char
h short
H unsigned short
i int
I unsigned int
l long
L unsigned long
< little endian
> big endian|
= native endian

see also:

ipairs()

lua table 을 array 로 쓰는 경우, 정수 인덱스 기반의 iterating 을 할 때 사용한다. 정수 인덱스에 관련된 값이 nil 일 때까지 루프를 돌게 된다. 반면에 lua table 을 map 으로 사용할 경우, 즉 key 와 value 를 명시해서 사용할 경우에는 pairs() 를 사용해야 한다.

t = { ["a"] = 1, ["b"] = 2, ["c"] = 3 }
for i,v in ipairs(t) do print(i,v) end

tt = {[1]=1,[3]=3,[5]=5}
for i,v in ipairs(tt) do print(i,v) end
1 1
ttt = {[1]=1,[2]=3,[3]=3,[5]=5}
for i,v in ipairs(ttt) do print(i,v) end
1 1
2 3
3 3
tttt = {"a","b","c"}
for i,v in ipairs(tttt) do print(i,v) end
1 a
2 b
3 c

luabind : param 개수 제한

C++ 메써드를 바인딩할 때, 파라미터의 개수가 제한되어 있는 듯하다. 좀더 테스트가 필요할 듯.

luabind : C++에서 루아 테이블 객체 생성해서 functor 로 넘기는 법

C++에서 루아 함수를 호출할 때, 파라미터의 개수가 상황에 따라서 동적으로 바뀔 경우 루아 테이블에 넣어서 보내면 깔끔하다. 그런데, 어떻게 C++ 스코프에서 루아 테이블을 만들 수 있을까? 생각보다 꽤 간단했다.

-- lua
function f(a) return a.x+a.y end

// C++
luabind::object table = luabind::newtable(L);
table["x"] = 1;
table["y"] = 2;
functor f(L,"f");
f(table);

테이블 안에 테이블을 넣는 것도 가능하다.

// C++
luabind::object table = luabind::newtable(L);
luabind::object subtable = luabind::newtable(L);
table["sub"] = subtable;
functor f(L,"f");
f(table);

luabind : virtual member function

class Connection {
virtual int handle_input();
virtual int handle_output();
virtual int handle_msg();
};
class TestConnection : public Connection {
virtual int handle_msg();
};

이와 같이, 하위 클래스에서는 몇 개의 virtual member function 만을 override 한 경우, 바인딩할 때에는 주의가 필요하다.

// 컴파일 에러 발생
module(L)
[
class("TestConnection")
.def("handle_input",&TestConnection::handle_input)
.def("handle_output",&TestConnection::handle_output)
.def("handle_msg",&TestConnection::handle_msg)
];

// 컴파일 성공
module(L)
[
class("Connection")
.def("handle_input",&Connection::handle_input)
.def("handle_output",&Connection::handle_output)
.def("handle_msg",&Connection::handle_msg)
,
class("TestConnection")
.def("handle_msg",&TestConnection::handle_msg)
];

단 virtual 이 잘 동작하는지는 확인 요망...

luabind : functor

루아 함수를 C++ 에서 호출할 수 있게 해준다. 템플릿으로 구현되어 있어서 컴파일 속도가 장난 아니게 느리다.

-- lua
function f(a,b) return a+b end

// C++
functor f(L,"f");
f(1,2);

어떤 루아 함수를 사용하려 한다는 것은, 이미 루아 스택에 그 함수가 로딩되어 있어야 한다는 의미이다. 그런데, 만약 런타임에 함수를 정의하고 그 함수를 C++ 에서 사용하려면 어떻게 해야 할까? 이를 위해서는 functor 등록 함수를 luabind 를 이용해서 다시 루아에서 호출해줘야 한다. 즉

-- lua
function f(a,b) return a+b end
register_f()

// C++
void register_f()
{
functor * pFunc = new functor(L,"f");
}

module(L)
[
.def("register_f",&register_f)
];

까다로운 점은 register_f() 에서의 루아스택을 어떻게 알아내느냐인데, [레이옷]은 SingletonPattern 으로 해결해버렸다. -_-;;

끝으로 주의사항: functor 의 파라미터로 C++ 클래스를 사용하려면 필히 먼저 이 클래스를 luabind 로 등록해두어야 한다. (물론 루아에서 사용하는 메써드만 달랑 해주면 끝)

luabind : __tostring()

luabind 로 C++ 클래스 인스턴스를 생성, print(x) 를 해보고 싶다면, ostream operator << 를 implement 하고, tostring(self)를 바인딩해주면 된다.

class number {};

std::ostream& operator<<(ostream&, number&);

module(L)
[
class_("number")
.def(tostring(self))
];

반면 루아 클래스 객체를 출력하려면 다음과 같이 함수를 정의해야 한다. '''이때 self 파라미터를 수동으로 넘긴다는 점에 유의할 것'''. (다른 함수에서 self 는 자동적으로 인스턴스 자신을 가리키지만, metatable 관련 함수들은 수동으로 넘겨야 하는 듯. 당연히 굳이 self 라는 이름일 필요는 없다)

class 'XXX'
function XXX:__init()
self.v = 1
end
function XXX:__tostring(self)
return 'XXX('..self.v..')'
end

lua list

table 을 list 처럼 사용할 수 있다.

-- create
list = nil
for line in io.lines() do
list = { next=list, value=line }
end

-- iterate
l = list
while l do
print(l.value)
l = l.next
end

see also:

  • [http://www.lua.org 루아 공식 홈페이지]
  • [http://www.redwiki.net/wiki/wiki.php/Lua 레드픽셀님의 루아 페이지]
  • [http://lua-users.org/wiki/] - 루아 위키
  • [http://www.workspacewhiz.com/LuaPlus/index.html LuaPlus] - 가장 OO 스러운 루아 확장 라이브러리
  • [http://sourceforge.net/projects/lua-users/ lua-users.org at SF] - 윈도우용 루아 바이너리 및 매뉴얼 등등을 배포하는 곳.
  • [http://www.sjbrown.co.uk/lualite.html LuaLite] - Lua Syntax Highlight plugin for VS.NET. 단, 이것을 띄워 놓은 상태에서 검색을 하면 먹통이 되므로 조심할 것
  • [http://luaforge.net/ LuaForge]
  • [http://www.icynorth.com/forums/index.php LuaForum]
  • http://www.thomasandamy.com/projects/CPB/

comments powered by Disqus