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) endtt = {[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(table);
테이블 안에 테이블을 넣는 것도 가능하다.
// C++
luabind::object table = luabind::newtable(L);
luabind::object subtable = luabind::newtable(L);
table["sub"] = subtable;
functor
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
.def("handle_input",&TestConnection::handle_input)
.def("handle_output",&TestConnection::handle_output)
.def("handle_msg",&TestConnection::handle_msg)
];
// 컴파일 성공
module(L)
[
class
.def("handle_input",&Connection::handle_input)
.def("handle_output",&Connection::handle_output)
.def("handle_msg",&Connection::handle_msg)
,
class
.def("handle_msg",&TestConnection::handle_msg)
];
단 virtual 이 잘 동작하는지는 확인 요망...
luabind : functor
루아 함수를 C++ 에서 호출할 수 있게 해준다. 템플릿으로 구현되어 있어서 컴파일 속도가 장난 아니게 느리다.
-- lua
function f(a,b) return a+b end
// C++
functor
f(1,2);
어떤 루아 함수를 사용하려 한다는 것은, 이미 루아 스택에 그 함수가 로딩되어 있어야 한다는 의미이다. 그런데, 만약 런타임에 함수를 정의하고 그 함수를 C++ 에서 사용하려면 어떻게 해야 할까? 이를 위해서는 functor 등록 함수를 luabind 를 이용해서 다시 루아에서 호출해줘야 한다. 즉
-- lua
function f(a,b) return a+b end
register_f()
// C++
void register_f()
{
functor
}
module(L)
[
.def("register_f",®ister_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_
.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/