프로그래밍
Lua Binding
smilejp
2010. 5. 30. 02:29
루아를 공부하면서 궁금했던 부분인 어떻게 Lua와 C++사이에서 데이터 교환이 이뤄지는가에 대한 인터넷 조사 결과를 정리해보았다.
Lua Binding을 왜 해야하는가?
- 루아와 C++의 차이 때문에 직접적으로 데이터를 교환할 수 없다
- 동적 데이터 타입 시스템인 Lua와 정적 데이터 타입 시스템인 C++
- 자동 메모리관리 하는 Lua와 수동 메모리 관리하는 C++
그래서 루아에서는 C++과 값을 교환하는데 추상적인 스택의 개념을 사용하고 있다.
즉, Lua로부터 값을 요청할 때는 Lua를 호출하여 스택에 요청하는 값을 push하며, 루아로 값을 전달할때는 스택에 값을 push한 후 루아 함수 호출하는 방식으로 사용한다.
루아 기본 API에서 C++과 데이터 교환을 위한 수단을 제공하고 있다. 복잡하게 살펴볼것도 없이 간단하게만 다음의 경우에 대해서만 파악해보자.
1. C++에서 루아 변수를 사용하는 방법
2. 루아에서 C++변수를 사용하는 방법
3. C++에서 루아 함수를 사용하는 방법
4. 루아에서 C++함수를 사용하는 방법
1. C++에서 루아 변수를 사용하는 방법
루아에서 선언한 변수를 C++에서 사용하는 것이기 때문에 Lua에서는 변수를 선언해야 한다.
-- sample.lua lua_int = 3 // main.cpp // sample2.lua를 읽어들여 실행한다. luaL_dofile( L, "sample.lua" ); // lua_int 값을 찾기 위해 키를 Stack에 Push lua_pushstring( L, "lua_int" ); // Global 테이블에서 stack에 넣어진 키에 해당하는 값을 꺼내 // Stack의 맨 위에 push 한다. lua_gettable( L, LUA_GLOBALSINDEX ); // stack의 가장 윗부분의 값을 가져온다. int lua_int = luaL_checknumber( L, -1 ); // stack에서 1개 pop한다. lua_pop( L, 1 );
2. 루아에서 C++변수를 사용하는 방법
// main.cpp int cpp_int = 100; // 테이블에서 사용할 키를 Stack에 push lua_pushstring( L, "cpp_int" ); // 값을 Stack에 push lua_pushnumber( L, cpp_int ); // Global 테이블에 "appoint“를 키(key)로하여 cpp_int를 값으로 추가 lua_settable( L, LUA_GLOBALSINDEX ); luaL_dofile( L, "sample.lua" ); -- sample.lua print("cpp_int = ".. cpp_int) // 잘 쓴다......
3. C++에서 루아 함수를 사용하는 방법
-- sample.lua function LuaFunc() print( "in LuaFunc" ) return 3 end // main.cpp luaL_dofile( L, "sample.lua" ); // sample.lua를 읽어 들여 실행 한다. // Global 테이블에서 키가 LuaFunc인 것을 찾아 Stack의 맨 위에push lua_getfield( L, LUA_GLOBALSINDEX, "LuaFunc" ); // stack에 push된 함수를 호출, // 인자는 0이며, 반환 값은 1개, 에러 처리는 하지 않음 lua_pcall( L, 0, 1, 0 ); int luaFuncResult = (int)luaL_checknumber( L, -1 );
4. 루아에서 C++함수를 사용하는 방법
// main.cpp int cpp_func( lua_State *L ) { // 매개변수가 2개 필요하므로 Stack에서 두개를 가져온다. int arg1 = (int)luaL_checknumber( L, -2 ); int arg2 = (int)luaL_checknumber( L, -1 ); lua_pushnumber( L, arg1+arg2 ); // 결과값을 스택에 넣는다. return 1; // 결과 값의 개수를 return } // 루아 테이블에서 키로 사용할 이름을 넣는다. lua_pushstring(L, "Func" ); // 값으로는 함수의 포인터를 넘겨준다. lua_pushlightuserdata(L, cpp_func); lua_pushcclosure( L, cpp_func, 1 ); // 전역테이블에 키,값을 저장한다. lua_settable(L, LUA_GLOBALSINDEX); luaL_dofile( L, "sample.lua" ); // sample.lua를 읽어 들여 실행 한다. -- sample.lua a = Func( 1, 2 ) // 잘 쓴다..
Lua API만을 이용하여 루아와 C++간의 데이터 교환은 가능하지만 복잡하기 때문에 바인딩 코드를 작성하는 사람은 Lua의 가상 스택과 바인딩을 위해 제공하는 API에 대해서 정확하게 파악하고 있어야만 한다.
바인딩할때마다 복잡하게 API를 사용하지 않고 좀더 편하게 할 수 있는 방법은 당연히 있다.
사용하기 편하도록 라이브러리를 배포하고 있는데 간단하게 몇가지만 정리해보면..
- LuaBind( http://www.rasterbar.com/products/luabind.html )
- 오픈소스
- boost를 사용하여 개발하였기 때문에 빌드를 위해서 boost 헤더 파일이 필요하다.
- 사용하는 boost 라이브러리 때문에 빌드 시간이 오래걸리는 단점이 있다.
- 읽어볼만한 글
- LuaTinker
- 최신버전은 LuaTinker 0.5d(http://dexgame.com/zbxe/?mid=download)
- LuaBind가 boost를 사용하기 때문에 boost를 사용하지 않는 프로젝트에서는 LuaBind 때문에 boost를 써야만 하는 문제가 있고, 빌드 시간이 오래 걸리는 문제를 해결하고자 개발한듯
- 장점
- boost를 사용하지 않아서 빠르다. 템플릿으로 작성되어 있다.
- 개발자가 한국인이기 때문에 물어볼곳이 있다.
- http://zupet.tistory.com (하지만 바쁘신듯.. )
- lua_tinker.cpp, lua_tinker.h 파일두개로 구성되어있다.
- 배포된 라이브러리에 LuaTinker 사용법을 보여주는 샘플 코드가 여러개 포함되어 있어 샘플코드만 봐도 사용법을 손쉽게 파악할 수 있다.
- 메뉴얼 - http://www.gpgstudy.com/gpgiki/LuaTinker
- Luainterface (http://luaforge.net/projects/luainterface/)
- .net에서 Lua를 사용할 수 있도록 바인딩 해준다.
- 유용한 글 - http://serious-code.net/moin.cgi/LuaCSharpBinding
- XNua( http://xnua.com/xna_lua_xnua )
- XNA에서 Lua를 사용하기 위한 목적으로 만들어 졌다.
- 유용한 글 - http://blog.naver.com/PostView.nhn?blogId=fly33499&logNo=120074871979&viewDate=¤tPage=1&listtype=0
LuaBind
1. C++에서 루아 변수를 사용하는 방법
-- sample.lua -- Lua쪽 변수에 값을 할당한다. lua_int = cpp_int // main.cpp luaL_dofile(L, "sample.lua"); // global 테이블에서 lua_int 키를 찾아서 값을 가져온다. size_t lua_int = luabind::object_cast(luabind::globals(L)["lua_int"]);
2. 루아에서 C++변수를 사용하는 방법
// main.cpp int cpp_int = 100; // Global 테이블에 cpp_int 추가 luabind::globals(L)["cpp_int"] = cpp_int; luaL_dofile(L, "sample.lua"); -- sample.lua print("Get cpp cpp_int :", cpp_int) // 잘쓴다...
3. C++에서 루아 함수를 사용하는 방법
-- sample.lua function LuaFunc() print( "In Lua LuaFunc()" ) return 3 end // main.cpp luaL_dofile(L, "sample.lua"); // 반환값이int 이고 이름이 LuaFunc인 루아함수를 실행 int luaFuncResult = luabind::call_function(L,"LuaFunc");
4. 루아에서 C++함수를 사용하는 방법
// main.cpp int func( int a, int b ) { return a + b; } // c함수를 lua로 연결 luabind::module(L) [ luabind::def("Func", &func) ]; luaL_dofile(L, "sample.lua"); -- sample.lua print( Func( 2, 2 ) )
LuaTinker
1. C++에서 루아 변수를 사용하는 방법
-- sample.lua -- Lua쪽 변수에 값을 할당한다. lua_int = cpp_int // main.cpp // sample1.lua 파일을 로드/실행 한다. lua_tinker::dofile(L, "sample.lua"); // sample1.lua의 변수를 호출한다. int lua_int = lua_tinker::get(L, "lua_int");
2. 루아에서 C++변수를 사용하는 방법
// main.cpp int cpp_int = 100; // LuaTinker를 이용해서 cpp_int를 Lua에 전달 lua_tinker::set(L, "cpp_int", cpp_int); lua_tinker::dofile(L, "sample.lua"); -- sample.lua print( cpp_int )
3. C++에서 루아 함수를 사용하는 방법
-- sample.lua function LuaFunc() print( "In Lua LuaFunc()" ) return 3 end // main.cpp // sample.lua 파일을 로드/실행한다. lua_tinker::dofile(L, "sample.lua"); // sample1.lua의 LuaFunc함수를 실행, 반환값은 int int luaFuncResult = lua_tinker::call(L, "LuaFunc");
4. 루아에서 C++함수를 사용하는 방법
// main.cpp int cpp_func(int arg1, int arg2) { return arg1 + arg2; } // Func 이름으로 Lua에서 사용할수있도록 함수를 등록 lua_tinker::def(L, "Func", cpp_func ); -- sample.lua print( Func( 2, 2 ) )
LuaBind와 LuaTinker를 사용하면 Lua API나 스택에 대해서 세심하게 신경쓰지 않아도 Lua와 C++ 사이의 값교환이 가능함을 알 수 있다. 물론 LuaBind와 LuaTinker는 위에서 작성한 것이외에도 클래스, 상속관계... 여러가지 많은 기능을 제공한다.
LuaBind의 느린 빌드 속도를 줄이기 위해서 Pre-Compiled Header를 사용하는 방법에 대해서도 조사해보았다.
- LuaBind 라이브러리를 include하는 코드를 미리 컴파일된 헤더로 만드는 경우 최초 미리 컴파일 헤더를 만들때를 제외하면 빌드 속도가 향상된다.
- 하지만 리빌드를 할 경우에는 여전히 오랜 시간이 걸린다.
- GPGStudy 게시판의 글을 보면 LuaBind와 미리컴파일된 헤더를 사용하다 빌드 시간이 오래 걸림으로 LuaTinker를 만들게 된듯한 글이 보임
Lua Binding 관련해서 볼만한 Game Programming gems 부분들..
- 5권 - 1.10 루아를 게임에 통합
- 6권 - 4.2 C/C++ 객체와 루아의 바인딩