Kgc2006 Template Metaprogramming을 이용한 LuaTinker 구현
Upcoming SlideShare
Loading in...5
×
 

Kgc2006 Template Metaprogramming을 이용한 LuaTinker 구현

on

  • 418 views

2006년 KGC에서 Nexon의 이권일님께서 발표하신 LuaTinker 구현에 대한 PPT 입니다. 인터넷에 찾아보니까 공유된 내용이 없어서 제가 저장해 두었던 것을 ...

2006년 KGC에서 Nexon의 이권일님께서 발표하신 LuaTinker 구현에 대한 PPT 입니다. 인터넷에 찾아보니까 공유된 내용이 없어서 제가 저장해 두었던 것을 공유합니다. 혹시나 문제가 된다면 삭제하겠습니다.

Statistics

Views

Total Views
418
Slideshare-icon Views on SlideShare
416
Embed Views
2

Actions

Likes
0
Downloads
6
Comments
0

1 Embed 2

https://twitter.com 2

Accessibility

Categories

Upload Details

Uploaded via as Microsoft PowerPoint

Usage Rights

© All Rights Reserved

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Processing…
Post Comment
Edit your comment
  • 이 발표의 내용은 LuaTinker를 만들면서 제가 익히고 사용한 Template Metaprogramming 에 대해 소개하는 자리입니다. <br /> 주요 내용은 Lua 와 C++ 코드를 연동함에 있어 Template Metaprogramming 을 어떻게 이용하였는지를 설명하고 따라가보는 것입니다. <br /> Lua 를 벌써 사용하고 계신 분들이나 LuaTinker 를 사용하시는 분들께는 여러모로 참고가 되리라 생각합니다. <br /> 참고로 이 자료는 KGC 2006 발표만이 아니라 나중에 다시 읽어보더라도 이해하기 좋게 텍스트가 좀 많습니다. 양해를 바랍니다. <br />
  • 요 몇년새 게임에 스크립트 언어를 적용시키는 경우가 늘어나고 있습니다. <br /> 특히 게임 로직이나 UI 같이 자주 변경이 필요한 경우에 쓰게 되곤 하는데 Lua나 Python 같은 경우가 대표적으로 많이 쓰이고 있습니다. <br /> Lua 는 C 언어와 비슷한 흡사한 문법을 갖고 있고 매우 가벼운 스크립트 언어입니다. <br /> Homeworld 나 워크래프트 같은 게임에 쓰였고 게임 개발 서적도 나와서 국내서도 이젠 한번쯤 들어봤을법한 스크립트 언어입니다. <br />
  • 템플릿 메타프로그래밍은 C++ 템플릿 사용하여 컴파일 과정에서 실제 코드가 생성되게 하는 방법입니다. <br /> 여기서 C++ 템플릿 코드는 실제 컴파일되는 것이 아니라 적절한 파라메터와 함께 컴파일러에 입력되면 임시 코드가 생성되어 실제 실행에 쓰이게 됩니다. <br /> 요 몇년새 Boost 와 같은 라이브러리가 앞서가는 개발자들에 의해 도입이 되고 있고 LuaTinker 도 그런 라이브러리와 방향을 같이하고 있습니다. <br /> Template Metaprogramming 이란 서적도 나와 있으니 Boost 라이브러에 관심이 있으신 분은 꼭 읽어보시기를 권하고 싶습니다. <br />
  • LuaTinker 는 Lua 와 C++ 코드에서 연동하기 위해서 제작되었습니다. <br /> 원래 프로젝트에서 Boost 와 LuaBind 를 사용하고 있었는데 Boost 와 LuaBind 의 컴파일 속도가 너무 느려서 파일 하나에 1분 30초가 넘어가곤 했었죠. <br /> 그래서 직접 LuaBind 와 같은 일을 하는 코드를 만들고자 했습니다. <br /> 참고로 Tinker는 땜장이 또는 어설픈 용접이란 뜻으로 Lua 와 C++ 을 땜질한다는 뜻입니다. (이름 짓는데 고생했습니다. ^_^) <br />
  • Lua 와 C 언어와 연동을 하기 위해서는 Lua 에서 C 함수를 호출하거나 C 언어에서 Lua 함수를 호출 할 수 있어야합니다. <br /> Lua 내부에서 이런 기능을 지원하는데 C Closure 라고 부르며 Lua 에서 정해놓은 함수 형식으로 Lua 전역 함수를 등록할 수 있습니다. <br />
  • 이 소스는 Lua 에 C 함수를 등록하는 코드를 표현한 것입니다. <br /> Addition 은 left, right 값을 더해서 리턴하는 간단한 C 함수입니다. <br /> 이 함수를 Lua 에 등록하기 위해서는 Lua 에서 지정한 C Closure 형식을 사용하여 코드를 작성해야 합니다. <br /> Lua 는 인자값을 넘기고 리턴값을 넘기기 위해서 Stack을 사용하는데 이 Stack 은 lua_State 핸들을 통해서 접근이 가능합니다. <br />
  • 이렇게 만들어진 함수는 몇몇 Lua API 들을 호출해서 등록할 수 있습니다. <br /> 먼저 전역으로 호출될때 사용될 함수 이름을 지정해주고 Lua 형식에 맞춘 C 함수 포인터를 전달하고 등록을 하면 Lua 내에서 실행이 가능합니다. <br />
  • 이러한 Lua C 함수 등록의 문제점은 모든 함수들을 하나하나 만들어줘야 한다는 겁니다. <br /> 또한 Lua에서 값을 전달하기 위해 Stack에 일일이 접근해서 데이터를 불러오는 바인딩 코드도 새 함수가 등록될때마다 새로 작성해 주어야 합니다. <br /> 이런 코드를 일일이 작성하는건 상당히 시간을 많이 소비하면서 실수를 하기 좋은 부분들이기도 합니다. <br />
  • 앞에서 보여드린 내용을 약간 개선하는 것이 Lua에 있는 Upvalue 란 기능을 활용하는 것입니다. <br /> Upvalue 란 Lua 에 C 함수를 등록할때 함수와 함께 등록할 수 있는 값으로 각종 파라메터나 함수 포인터등을 저장할 수 있습니다. <br /> 다음에 나오는 방법은 Lua 에서 C 함수가 호출될때 이 Upvalue 에 함수 포인터를 넣어서 사용하는 방법입니다. <br />
  • 여기서 addtiion 과 같이 subtract 라는 함수를 만들었습니다. <br /> 이 subtract 는 addition 과 같은 입력 변수들을 갖고 있지만 다른 연산 결과를 리턴합니다. <br /> Lua 에 등록할 C 함수는 여기의 arithmetic 함수와 같이 Upvalue 에 함수 포인터는 넣어서 등록하고 Lua 에서 C 함수가 불리웠을때 Upvalue 에 저장된 함수 포인터를 참조해서 실제 함수를 호출하게 됩니다. <br />
  • 함수를 Lua에 등록할때는 이전에 함수 등록할때 작업에서 Upvalue 를 Stack 에 넣는 작업이 추가되었습니다. <br /> 따라서 arithmetic_lua 함수를 추가하기 전에 addtion 함수포인터를 Lua Stack에 넣어주게 됩니다. <br />
  • 이렇게 등록된 함수는 이전 함수와 동일하게 addtion 과 subtrct 함수명으로 호출할 수 있습니다. <br />
  • 이렇게 Upvalue 를 활용함으로써 같은 형의 함수들은 하나의 함수만 작성해서 Lua 에 등록해주는 것으로 작업이 끝나게 되지만 변수가 다르거나 리턴값이 다를 경우 전부 다른 방식으로 함수들을 만들어 주지 않으면 안됩니다. <br /> 또한 모든 함수들을 함수 포인터와 그것에 적합한 Lua C 함수를 등록해주는 과정은 실수를 하기도 좋고 잘못 호출되었을때 C 언어쪽 Stack을 전부 깨부술 수도 있기 때문에 나름대로 민감한 작업이 될 수 있습니다. <br /> 특히 이미 만들어진 C 함수쪽 인자를 바꾸거나 호출 방식을 변경한 다음 Lua 쪽에 등록해주는 코드를 변경해주지 않으면 끔찍한 일이 일어날 수도 있죠. <br />
  • * Lua 를 접해보지 못하신 분들은 조금 지겨우셨을지 모르겠는데 이제 LuaTinker 가 이 문제들을 어떻게 풀었는지 보여드리겠습니다. <br /> 이제 LuaTinker 가 앞에서 Lua 에 C 함수를 어떻게 등록하는지 설명하겠습니다. <br /> 일단 LuaTinker 가 해주어야 할 부분은 간편하게 함수를 등록하고 자동적으로 Lua Stack 에 접근하여 값을 읽거나 쓸 수 있어야 합니다. <br /> 또한 C++의 클래스나 구조체들을 Lua 에서 접근할 수 있도록 하는 기능또한 필요로 합니다. <br />
  • LuaTinker 가 함수를 등록할때는 아래 코드와 같이 lua_tinker - define 함수를 호출합니다. <br /> L 값은 lua_State 핸들이고 “addition” 스트링은 Lua 내에서 호출할때 사용할 함수 이름, 그리고 맨 뒤의 addition은 실제 함수가 됩니다. <br /> LuaTinker는 이 함수 호출 하나로 함수의 등록, Lua C 함수 노출, Stack을 뒤져서 값을 읽고 리턴값을 쓰는 일까지 자동으로 생성해주게 됩니다. <br />
  • Define 함수를 살펴보면 전체 내용은 앞에서 Upvalue 를 사용해서 함수를 등록하던 것과 다르지 않습니다. <br /> 달라진 부분이 있다면 앞에서 lua_pushcclosure() 함수가 push_functor() 란 LuaTinker 함수로 바뀌었다는 점과 등록하는 define 함수 자체가 템플릿으로 만들어졌다는 것입니다. <br />
  • LuaTinker 의 push_functor 함수의 역할은 함수 이름과 같이 Lua 에 함수를 등록해주는 역할을 합니다. <br /> 내부적으로 lua_pushcclosure() 함수를 호출해주는데 이때 주어진 함수를 분해해서 Stack에 접근하고 해당 함수를 불러주는 functor 를 생성할 수 있도록 자료형 변환을 맡게됩니다. <br /> 다음 코드를 보면서 설명을 하는게 좋을 것 같습니다. <br />
  • 이 코드는 앞에 나왔던 define 코드와 define 코드에서 push_functor를 호출했을때의 내용을 설명하고 있습니다. <br /> Define 를 통해 addtion 이란 함수를 Lua에 등록하고자 할때 push_functor 함수에는 addition 함수포인터가 들어가고 이 함수 포인터는 left, right 두개의 int 형 자료를 입력받고 하나의 int 값을 리턴하는 함수형을 갖고 있습니다. <br /> 그런데 define 함수는 함수 포인터를 하나의 자료형으로 코드를 보면 typename F를 받고 있습니다. push_functor 는 이러한 함수 포인터 자료형인 F를 분석해서 리턴값과 각 인자들을 분해해주는 역할을 합니다. <br /> 밑에 push_functor 함수를 보면 위의 define 에서 넘어온 F 자료형이 Return Value 와 Type 1, Type 2 로 분리되는 것을 볼 수 있습니다. <br /> 그리고 이렇게 분리된 함수를 앞서 Lua 에서 C 함수를 등록할때 썼던 Lua API 인 lua_pushclosure 를 써서 등록해주게 됩니다. <br />
  • push_functor 가 이런 분석 작업을 위해 템플릿과 함수 오버로딩을 같이 사용하고 있습니다. <br /> 템플릿은 우리가 어떠한 자료형을 넘겨주었을때 그것을 템플릿에 선언된 typename 에 적용되어 코드가 생성되는 것입니다. <br /> 그리고 함수 오버로딩이 필요한 이유는 함수 포인터 자로형때문인데 아래 소스에 예제가 나와 있습니다. <br /> 앞에서 사용했던 addition 함수는 int 형 자료 left, right 를 인자로 하고 int 형 자료를 리턴하게 되어 있습니다. <br /> 그래서 여기 맨 위의 예제와 같이 자로형은 int 값 두개를 받는 함수 포인터로 선언되고 템플릿또한 R Value 를 리턴하고 T1, T2 두개의 타잎을 갖는 함수가 되게 됩니다. <br /> 만약 여기 increase 와 같은 int 형 자료를 하나 받고 int 를 하나 리턴하는 함수를 이 템플릿에 적용하고자 하면 두번째 인자가 void가 들어오게 됩니다. <br /> 그렇지만 두번째 인자로 void 값을 입력받는 함수는 존재하지 않기때문에 이런 template는 구현될 수 없습니다. <br /> 그래서 마지막 예제와 같이 템플릿 형태가 바뀌어야하죠. <br />
  • 그래서 나온 결과물이 이렇게 각 인자 개수만큼 push_functor 함수를 개별적으로 만들어서 C++ 에서 자동적으로 오버로딩이 가능하게 만들어 주었습니다. <br /> 조금 무식해보이지만 이건 템플릿과 C++의 규칙에 의해서 어쩔 수 없이 만들어지는 부분으로 LuaTinker 에서 이런식으로 템플릿이 해결하지 못하는 문제들을 해결하기 위해서 오버로딩된 코드들을 몇가지 찾아볼 수 있습니다. <br /> 여기서 제일 중요한 것은 왜 자료형을 Return Value 와 각 인자값을 T1, T2, T3 같은 형식으로 나눠줘야 하는가인데 이 부분은 나중 Lua Stack에 접근하는 push, pop 코드에 대해 설명하며 다시 언급하겠습니다. <br />
  • 앞의 push_functor 는 lua_tinker에 정의된 functor 함수자를 Lua C 함수의 Upvalue 로 넣어주는 역할을 하고 있습니다. <br /> 앞서 설명했었지만 Upvalue 라는 것을 통해 함수 포인터를 등록하고 사용하는 방법에 대해 언급 했었습니다. <br /> 여기서도 주어진 함수를 Upvalue에 함수 포인터로 넣어주고 functor 를 통해서 이것을 호출해주게 됩니다. <br /> 실제 코드 내용은 Upvalue 를 활용한 Lua C 함수 호출과 같지만 template 을 사용하여 자동적으로 Stack 에 접근하는 코드가 생성된다는 점이 다릅니다. <br />
  • 여기 functor 함수체의 실제 코드 부분을 보면서 설명하겠습니다. <br /> 일단 함수체의 첫번째 함수는 RVal 이라는 Return Value 값을 가지며 T1, T2 타잎의 함수 인자 두개를 갖고 있는 함수를 호출하는 코드입니다. <br /> 이 함수는 upvalue 에서 함수 포인터를 읽어온 다음 Lua Stack 에서부터 T1 타잎과 T2 타잎의 인자값을 각각 읽어와서 함수를 호출하고 그 리턴값을 다시 스택에 넣는 역할을 합니다. <br /> 그럼 이 코드를 하나하나 풀어 보겠습니다. <br />
  • 먼저 addition 함수의 인자들을 살펴보면 RVal 와 T1, T2 타잎이 있습니다. <br /> 각각 리턴값과 첫번째 두번째 인자인데 세가지 모두 int 형이 됩니다. 그럼 여기에 적절한 값을 대입해 보도록 하겠습니다. <br />
  • 각각 RVal와 T1, T2에 값을 적용하였습니다. 파란색은 RVal이 있던 자리고 빨간색은 T1, T2가 있던 자리입니다. <br /> 이제 코드를 보기 좀 편해졌는데 여기보면 Return Value 를 push 하고 있고 T1, T2 값에 해당하는 left, right 값을 pop 하고 있습니다. <br />
  • 일단 코드가 보기 좋지 않으니 pop 하는것과 push를 하는걸 조금 정리해보도록 하죠. <br /> 일단 pop 부터 하나씩 빼는걸로 해서 첫번째를 뺍니다. <br />
  • 인자 left를 함수 위에 만들고 <br />
  • 값을 할당합니다. 그리고 뒤에 pop 부분을 빼주면 <br />
  • 이렇게 정리가 됩니다. 그런데 여기서 pop 형은 실제 Lua API인 lua_tonumber()란 함수를 호출해주게 되어 있습니다. <br /> 그래서 이걸 다시 정리해주면 <br />
  • 이렇게 lua_tonumber()를 호출하는 코드로 정리됩니다. <br />
  • 다음은 두번째 pop인 right 차례로 <br />
  • Right 지역 변수를 만들어주고 <br />
  • 값을 읽어온다음 <br />
  • 아래 함수 부분을 right 로 대체합니다. <br /> 이것도 첫번째와 마찬가지로 int 형을 pop 하기 때문에 내부적으로 lua_tonumber()가 호출되어 <br />
  • 이렇게 정리가 됩니다. <br />
  • 다음은 upvalue 부분인데 여기 코드를 잘 읽어보면 push 다음 lua_State 핸들인 L을 넣어주고 upvalue 템플릿의 인자를 불러주게 됩니다. <br /> 이 Upvalue는 앞에서 Lua C 함수에 대해 설명할때 나왔던 부분의 바로 그것으로 함수를 호출할때 함수 포인터를 저장하는 장소입니다. <br /> 이 코드의 역할은 upvalue에서 함수 포인터를 받은다음 적절한 함수 포인터로 타잎 캐스팅을 한 다음 호출을 하도록 하는 코드입니다. <br /> 일단 이 코드 읽기가 지저분하니 일단 조금 정리를 해보죠. <br />
  • 위에다가 함수 포인터형 지역 변수 func 를 선언해주고 <br />
  • 인자값을 읽어오도록 합니다. 그리고 아래쪽 코드를 정리하면 <br />
  • 이렇게 정리가 됩니다. 이제 코드가 깔끔해졌으니 func 호출부분을 여러줄에 적을 필요가 없겠죠? <br />
  • 그래서 이렇게 한줄에 써주게 됩니다. <br /> 여기서 upvalue 함수는 Upvalue 에서 어떠한 값을 읽어와서 템플릿에 주어진 인자형으로 타잎캐스팅하여 리턴하는 함수입니다. <br /> 화면을 보면 타잎캐스팅 되는 타잎은 addtion 함수의 함수 포인터인 것을 쉽게 알 수 있습니다. <br /> 그럼 이 코드가 내부적으로 호출하는 내용을 풀어보면 <br />
  • 이렇게 lua_touserdata() API와 lua_upvalueindex()를 찾아오는 API를 호출하게 되어 있습니다. <br /> 그리고 까먹으면 안되니 타잎 캐스팅을 해줘야죠. <br />
  • 이렇게 lua_touserdata() API와 lua_upvalueindex()를 찾아오는 API를 호출하게 되어 있습니다. <br />
  • 다음은 push를 호출하는 부분입니다. <br /> Push는 어떠한 값을 Lua Stack 에 넣어주는 역할을 하는 함수입니다. <br /> 일단 임시 함수 포인터인 func 호출 부분을 정리하면 <br />
  • 위에 result 라는 리턴 지역변수를 만든다음 <br />
  • Func 함수 포인터 호출의 결과값을 result에 넣어줍니다. <br /> 위에서 했던 것과 같이 아래 push 쪽 코드를 정리하면 <br />
  • Push 함수가 이렇게 정리가 됩니다. <br /> 일단 보기좋게 한줄로 정리해보죠. <br />
  • Push 함수의 역할은 Rval 값으로 들어온 값, 여기서는 int 값을 Lua Stack에 넣어주는 역할을 합니다. <br /> Lua 에 int 값을 넣는 API는 lua_pushnumber() 로 템플릿 함수를 바꿔주면 <br />
  • 이렇게 정리가 됩니다. <br /> 자 여기까지 정리를 하고 나면 실제 컴파일러가 이 템플릿을 어떻게 처리하여 코드를 생성하는지 알 수 있습니다. <br /> 그리고 지금 이 최종 코드는 앞의 Upvalue를 호출하던 것과 전혀 다르지 않다는 것도 눈치가 빠른 분들은 알고 계실겁니다. <br />
  • 그럼 방금 살펴봤던 push와 pop에 대한 설명을 조금 더 붙여 보겠습니다. <br /> LuaTinker의 push와 pop은 Lua Stack 에서 값을 넣고 빼는 함수입니다. <br /> 템플릿으로 제작되었고 Lua API에서 지정한 데이터 타잎들중 몇몇가지는 명시적 전문화를 통해 미리 구현된 함수를 갖고 있습니다. <br /> 앞에서 lua_tonumber()나 lua_pushnumber()와 같은 함수가 명시적 전문화가 되어있는 코드로 다음이 그러한 코드들의 예입니다. <br />
  • Lua 에서 명시적 전문화가 되어있는 자료형은 대표적으로 숫자들과 문자열 입니다. <br /> 여기서 int 형은 lua_pushnumber() API가 호출되게 되어 있고 그 바로 밑에 const char 포인터 타잎은 lua_pushstring() 함수를 호출해서 문자열을 Lua Stack에 넣어주는 역할을 하게 됩니다. <br /> 문제는 루아와 1:1 변환이 존재하지 않는 데이터 타잎들인데 C++ 에서 다루는 구조체와 클래스들이 이러한 것에 포함됩니다. <br />
  • 이렇게 1:1 변환이 불가능한 자료형은 Lua 에서 표현이 불가능하기 때문에 userdata 타잎이라고 해서 Lua 에서 관리하는 메모리 공간을 할당받아 그 내용을 전달하게 됩니다. <br /> 간단히 설명하자면 userdata 타잎은 Lua 에서 할당해주는 빈 메모리로 임의로 그 데이터를 채워서 쓸 수 있고 Lua에서 관리하다 임의의 타이밍에 Garbage Collection 이 일어나게 됩니다. <br /> 이렇게 유저 데이터 공간을 통해 어떠한 데이터를 전달할때 주의할게 있는데 C++에서 Lua로 넘기는 값이 실제 인스턴스 자체인지, 포인터인지 아니면 리퍼렌스인지에 따라 취급 방법이 달라진다는 것입니다. <br /> 또한 C++의 enum 값도 귀찮은 문제가 생기는데 enum은 내부적으로 숫자를 갖는 별개의 자료형이지만 그 크기나 형태는 int 형과 마찬가지로 쓰이고 있습니다. 이러한 값 또한 예외처리가 되어야 했습니다. <br /> 이러한 변환을 모두 묶어서 LuaTinker 의 push 함수는 내부적으로 type2lua() 이란 함수를 호출하고 있습니다. <br />
  • type2lua 함수는 명시적 전문화가 되어있지 않은 push 함수에 의해서 불리워 집니다. <br /> 이 코드를 보면 재밌는게 있는데 바로 template if 문입니다. <br />
  • Template if는 템플릿 메타프로그래밍에서 매우 요긴하게 쓰이는 것으로 실제 이 코드에서도 type2lua 함수에 들어온 값이 enum인지 아닌지를 판단해서 각각 다른 함수를 호출할 수 있게 만들어줍니다. <br /> 밑에 있는 3줄의 템플릿 코드가 실제 구현된 if 코드의 전부입니다. <br /> 3줄의 첫번째는 선언부이고 두번째, 세번째가 각각 명시적 전문화에 의해서 첫번째 인자가 true 나 false가 됨에따라 구조체 if 내부에 선언된 type define 에 의해서 정의된 type이 A 가 되느나 B 가 되느냐에 따라 바뀌게 됩니다. <br /> 맨 밑에 그 예가 있습니다. <br />
  • 이 코드는 lua_tinker 의 push 함수의 기본형입니다. 1:1 변환이 가능한 int, char 같은 자료형이 아닌 어떠한 enum 값이나 구조체를 넣을때 이 코드가 어떻게 호출되는지 살펴보기로 합니다. <br /> 일단 type2lua 함수를 살펴보기로 하죠. <br />
  • Push 함수는 type2lua 함수만 호출하기 때문에 실제 push 함수 내부의 type2lua 함수 호출은 아래 type2lua의 바디로 대체될 수 있습니다. <br /> 그럼 내용을 바꾸면 <br />
  • 여기 파란색으로 된 부분이 <br />
  • 이 빨간 부분으로 바뀌게 되면 push 코드는 다음과 같이 됩니다. <br />
  • 이 빨간 부분으로 바뀌게 되면 push 코드는 다음과 같이 됩니다. <br /> 그럼 여기다 직접 어떠한 값을 넣어보도록 하죠. <br />
  • 먼저 enum 값을 처리해 보도록 하겠습니다. <br /> 그럼 push를 하는데 T 타잎 ret 에 APPLE 를 넣어보겠습니다. <br />
  • 먼저 APPLE 를 push 함수에 넣으면 <br />
  • Ret 값은 APPLE가 됩니다. 그리고 template의 typename T 는 FRUIT 가 되겠죠. <br /> 그럼 다음같이 바뀌어서 <br />
  • T를 전부 FRUIT로 바꿔주게 됩니다. <br /> 그런다음 push 함수의 내부를 보면 if 구문이 어떻게 처리되는지 보도록 하죠. <br />
  • If 를 보면 is_enum 템플릿의 value 값으로 FRUIT이 enum 인지 아닌지를 구별하게 되어 있습니다. <br /> 여기서 FRUIT는 enum 이므로 is_enum 의 리턴값이 true 가 되므로 <br />
  • Value는 true가 되고 <br />
  • template if 코드 여기다 펼쳐놓은 다음 보면 두번째줄 코드가 value 가 true 일때 상황이죠. <br /> 그럼 <br />
  • 일단 두번째줄을 좀 보기좋게 정리해서 <br />
  • 위로 올리고 <br />
  • 펼쳐 놓습니다. <br /> 그리고 여기 위의 enum2lua 와 ojbect2lua를 적용시키면 <br />
  • A 에는 enum2lua 가 들어가고 B 에는 ojbect2lua 가 들어가므로 각각 대입을 시키면 <br />
  • A가 이렇게 <br />
  • 또 이렇게 정리되고 <br /> B도 <br />
  • 이렇게 되어 대입이 끝납니다. <br /> 그럼 여기서 위에 template 쪽을 정리하면 <br />
  • 최종적으로 여기까지 정리가 됩니다. <br /> 그럼 이걸 위의 if 적용하기 전에 template if 내에 정의된 type 값은 enum2lua 함수체가 되고 <br />
  • 위의 if 문의 type 까지를 정리하면 type은 enum2lua 함수체이기 때문에 이것이 <br /> 싸그리 정리되어 <br />
  • 이렇게 됩니다. <br /> 그럼 실제 push 함수의 내용은 enum2lua 템플릿 함수를 호출하는 것이 되죠. <br /> 여기까지가 enum 값을 넣었을때의 push 함수의 작용입니다. <br /> 그럼 이걸 구조체로 바꿔볼까요? <br />
  • 제일 처음으로 돌아가서 어떠한 구조체가 있다고 치죠. <br />
  • 그럼 여기 Damage의 인스턴스 myDamage가 있습니다. <br /> 이걸 push 에 적용하면 <br />
  • 이렇게 정리가 되고 is_enum 값은 아까와 달리 false가 나오겠죠? <br />
  • 그럼 이걸 다시 if 문에 적용하면 <br />
  • If 코드 예제 세개중 세번째줄에 해당하는 is_enum = false 인 경우를 선택하게 되고 <br />
  • 이 세번째를 정리하면 <br />
  • 이렇게 되었다 <br />
  • 위에 들어가고 <br />
  • 아래까지 채우면 <br />
  • 마지막 type 자료형은 object2lua 함수체가 됩니다. <br /> 이걸 위의 if 문에 적용하면 <br />
  • If 문에서 type까지가 전부 적용되고 결과적으로 <br />
  • 이렇게 정리됩니다. <br /> Object2lua 가 함수체이기 때문에 이 전체 if문은 사라지고 함수체의 invoke 함수를 호출하는 코드만 남게되죠. <br />
  • 여기서 각각 함수체를 간단히 설명하면 앞의 template if 문에서 나왔던 enum2lua 함수체는 enum 값을 Lua에 넣는 함수체입니다. <br /> Template if 에 사용하기 위해 invoke 라는 공통 메소드를 갖고 있는 함수체 형태로 제작 되었고 하는 일은 enum 값을 받아서 lua_pushnumber로 넘겨주는 작업만 하고 있습니다. <br />
  • 이에 반해서 object2lua 는 조금더 복잡한 작업을 하게 됩니다. <br /> Enum이 아닌 값이 type2lua 로 넘어왔을때 object2lua 의 함수체가 불리게 되는데 object2lua는 내부적으로 값을 복사하거나 포인터나 리퍼렌스를 루아로 전달하도록 되어 있습니다. <br /> 이러한 코드들 또한 template if로 작성되어 있고 내부적으로 서로 다른 함수체가 불리게 되어 있습니다. <br />
  • 이것이 object2lua 함수체에 대한 설명인데 내부에서 또다시 template if 구문을 통해 포인터인지 여부를 구분하고 리퍼렌스인지 여부에 따라 다른 함수체를 호출해주고 있습니다. <br />
  • 인쇄된 자료에는 object2lua 코드를 조금더 깊이 들어가는 내용이 있는데 이 부분은 Lua와 객체를 활용하는데 대한 내용이기 때문에 오늘 발표에서는 건너 뛰도록 하겠습니다. <br /> 몇페이지를 조금 더 넘겨서 얘기하고 싶은 것은 이렇게 push 함수에 의해서 Lua Stack에 들어간 구조체나 클래스를 어떻게 처리하느냐에 대한 것입니다. <br /> LuaTinker는 Lua 내에서 구조체와 클래스에 접근할 수 있는 방법을 제공하기 위해서 Lua 메타테이블이란 것을 사용하고 있습니다. <br /> Lua 메타테이블은 Lua 에서 제공하는 기능으로 루아 내부에서 다루는 값들의 각종 속성을 변경할 수 있게 해줍니다. <br /> 간단히 설명하면 C++의 오퍼레이터 오버라이딩과 흡사해서 각종 오퍼레이터들도 다룰 수 잇고 객체가 테이블로서 참조되거나 삭제될때의 이벤트도 가로채서 직접 처리할 수 있습니다. <br />
  • Lua 팅커는 이러한 테이블 접근 코드를 이용해서 LuaTinker 로부터 Lua 쪽으로 넘어간 구조체나 클래스의 멤버 변수나 멤버 함수의 호출을 지원하고 있습니다. <br /> 그래서 object2lua 함수가 호출된 다음 맨 마지막에 Lua Stack에 입력된 Object에게 새로운 메타테이블을 셋팅해주는걸 볼 수 있습니다. <br />
  • 다음은 LuaTinker가 메타테이블에서 재설정하는 오퍼레이터들입니다. <br /> __index는 table에 어떤 값을 읽어올때 호출하는 함수로 여기와 같이 멤버 변수등을 읽어올때 사용합니다. <br /> 또한 이 함수를 통해 functor 를 넣어주면 어떠한 구조체나 클래스의 멤버 변수 호출도 간단히 가능합니다. <br />
  • Newindex 는 index와 반대로 어떠한 값을 새로 써 넣을때 사용합니다. 원래는 테이블에 새 키와 값을 넣을때 사용하지만 LuaTinker는 그러한 함수를 가로채서 멤버 변수에 어떠한 값을 넣고자 할때 사용합니다. <br /> 그리고 gc는 Garbage Collection 의 약자로 객체가 파괴될때 불립니다. <br /> 만약 LuaTinker가 값으로 어떠한 구조체나 클래스를 Lua로 전달하였을때는 내부적으로 Lua로부터 빈 메모리 영역을 할당받아 복사생성자의 호출을 통해 객체가 초기화 됩니다. <br /> 어떠한 시점에 Lua에서 이 객체를 지우려고 할 경우 이 gc 함수가 불리워서 적절한 소멸자가 불리울 수 있게됩니다. <br /> Lua 내에 정의되는 구조체나 클래스에 대한 내용은 프린트물로 나간 자료에 어느정도 언급되어 있으나 PT 자료로 설명하기 보다는 직접 코드를 보고 테스트를 해보면서 살펴보는게 좋습니다. <br />
  • 이제 프레젠테이션도 끝나가는데 마지막으로 LuaTinker의 목표에 대해서 언급하고자 합니다. <br /> LuaTinker는 오픈 소스로 가능하면 많은 한국 개발자들의 의견을 수렴해서 개발해 나가고자 합니다. <br /> 앞으로 계속해서 업데이트 되는 Lua 버젼에 따라 올라가고 LuaTinker 를 활용한 다른 소스들도 오픈하고자 생각하고 있습니다. <br /> 그리고 버젼도 아직 0.2이지만 앞으로 메모리 할당 문제나 테이블 접근을 좀더 직관적으로 수정하고 몇몇 보고되는 버그들도 계속적으로 고치고자합니다. <br />
  • 난해하고 설명하는 사람도 좀 어리숙해서 듣는데 고생 많이 하셨으리라 생각됩니다. <br /> 질문 있으신분은 마이크를 신청해주시고 <br /> 혹시 Boost 고수분중에 토마토나 썩은 계란을 가져오셨는데 아직 못던지신분이 계시면 여기로 갖다 주시기 바랍니다. ^_^ <br />

Kgc2006 Template Metaprogramming을 이용한 LuaTinker 구현 Kgc2006 Template Metaprogramming을 이용한 LuaTinker 구현 Presentation Transcript

  • Template metaprogramming 을 통한 LuaTinker 의 구 현 이권일 Nexon
  • 발표 대상  현재 LuaTinker 를 사용하고 있는 사람  C++ Template 을 사용한 Metaprogramming 을 배우고자 하는 사람  STL 이외의 Template 을 유용하게 사용할 방 법을 찾고 있는 사람
  • Lua 에 대한 소개  Lua 는 가볍고 유연한 스크립트 언어  브라질 Computer Graphics Technology Group 소속 3 명의 멤버가 시작  현재 5.1 까지 발표되어 있고 Python 과 함께 많은 게임들에서 사용되고 있음  reflective, imperative and procedural language  C 언어와 매우 비슷한 문법을 갖고 있다 .
  • Template Metaprogramming  metaprogramming = 코드를 만드는 코드로 C++ Template 을 이용하여 만들어진다 .  최근 Boost Metaprogramming Libary 를 사용 하는 개발자들이 늘어나고 있다 .  C++ Template Metaprogramming 서적 추천
  • LuaTinker 의 소개  Lua 와 C++ 간의 원활한 통신을 하기 위해 제작된 Open Source 라이브러리  기존에 사용하던 LuaBind 를 사용하던 상태 에 바로 적용 가능  template metaprogramming 으로 구현됨  빠른 컴파일 시간 !!
  • Lua 스크립트와 와 C 함수  Lua 와 C 언어간 통신 방법중 하나로 Lua 스크립트에서 미리 등록된 C 언어의 함수를 호출할 수 있다 .  Lua 는 아래 한가지 함수형만 등록이 가능하 고 함수 호출에 사용된 인자들은 Lua API 와 lua_State 핸들을 통해 접근하게 된다 . typedef int (*lua_CFunction) (lua_State *L);
  • Lua C 함수의 구현 (1) /* 다음은 left + right 값을 리턴해주는 간단한 C 함수이다 . */ int addition(int left, int right) { return left + right; } /* addition 을 Lua C 함수로 등록한다 . */ int addition_lua(lua_State *L) { /* Lua Stack 에서 left, right 값을 읽어온다 */ int left = lua_tonumber(L, 1); int right = lua_tonumber(L, 2); /* addtion() 을 실행한다 . */ int result = addition(left, right); /* Lua Stack 에 left + right 값을 넣고 1 을 리턴한다 */ lua_pushnumber(L, result); return 1; }
  • Lua C 함수의 등록 (1) /* lua 에서 호출할 함수 이름을 Stack 에 넣는다 . */ lua_pushstring(L, "addition"); /* Lua C 함수 포인터를 Stack 에 넣는다 . */ Lua_pushcclosure(L, addition_lua, 0); /* 전역으로 addition 함수를 등록한다 . */ lua_settable(L, LUA_GLOBALSINDEX); -- Lua 에서 실행예 result = addition(10, 20) print(result) >30
  • Lua C 함수의 문제점  Lua 에서 호출되기를 원하는 모든 함수를 만 들어 주어야 한다 .  Stack 에 접근하기 위해서 일일이 Lua Stack 접근 함수를 사용해야한다 .  Lua 와 호환되지 않는 자료형은 사용할 수 없 기 때문에 추가적인 코드를 작성하지 않고 class 나 struct 를 사용할 수 없다 .
  • Upvalue 의 활용  Upvalue 는 Lua 에서 C 함수를 등록할때 Lua C 함수 이외에 별도의 정보를 저장할 수 있는 영역이다 .  Upvalue 에는 Lua 자료형을 넣을 수 있는데 void* 형이 존재하며로 함수 포인터를 넣어서 Lua C 코드 작성량을 줄일 수 있다 .
  • Lua C 함수의 구현 (2) int addition(int left, int right) { return left + right; } Int subtract(int left, int right) { return left - right; } /* Upvalue 를 이용하는 을 Lua C 함수로 등록한다 . */ int arithmetic_lua(lua_State *L) { /* Lua Stack 에서 left, right 값을 읽어온다 */ int left = lua_tonumber(L, 1); int right = lua_tonumber(L, 2); /* Upvalue 에서 함수 포인터를 얻어온다 . */ void* func = lua_touserdata(L, lua_upvalueindex(1)); /* C 함수 포인터를 실행한다 . */ int result = ((int(*)(int,int))func)(left, right); /* Lua Stack 에 계산 결과값을 넣고 1 을 리턴한다 */ lua_pushnumber(L, result); return 1; }
  • Lua C 함수의 등록 (2) /* lua 에서 호출할 함수 이름을 Stack 에 넣는다 . */ lua_pushstring(L, "addition"); /* addition 함수 포인터를 Upvalue 로 추가한다 . */ lua_pushlightuserdata(L, (void*)&addition); /* int(*)(int,int) 함수를 호출하는 Lua C 함수를 넣는다 . */ Lua_pushcclosure(L, arithmetic_lua, 1); /* 전역으로 addition 함수를 등록한다 . */ lua_settable(L, LUA_GLOBALSINDEX); /* lua 에서 호출할 함수 이름을 Stack 에 넣는다 . */ lua_pushstring(L, “subtract"); /* subtract 함수 포인터를 Upvalue 로 추가한다 . */ lua_pushlightuserdata(L, (void*)&subtract); /* int(*)(int,int) 함수를 호출하는 Lua C 함수를 넣는다 . */ Lua_pushcclosure(L, arithmetic_lua, 1); /* 전역으로 subtract 함수를 등록한다 . */ lua_settable(L, LUA_GLOBALSINDEX);
  • Upvalue 를 사용한 Lua 실행 -- Upvalue 를 활용한 Lua 실행예 result = addition(10, 20) print(result) >30 result = subtract(10, 20) print(result) >-10
  • Upvalue 활용의 한계  다른 인자값을 갖는 경우 모든 형태의 함수를 만들어 줘야 한다 .  함수 포인터를 쓰기 때문에 등록된 함수와 호 출해주는 Lua C 함수가 정확히 같은 자료형 및 함수 포인터호출을 해줘야 한다 .
  • LuaTinker 가 제시하는 해결책  Lua C 함수 등록을 간편하게 해주고 중간 호 출 코드를 자동으로 생성해 준다 .  Stack 에 접근하는 함수를 제공하여 자료형에 따른 적절한 변환을 지원한다 .  class/struct 의 멤버 함수 및 멤버 변수 Stack 에 접근하는 함수를 제공하여 자료형에 따른 적절한 변환을 지원한다 .
  • LuaTinker 와 Lua C 함수 등록  lua_tinker::def() 함수에 함수 명과 함수 포인 터를 넘겨준다 .  내부에서 자동적으로 template 를 통해 functor 함수를 생성해주고 stack 접근코드까 지 생성해준다 . /* int addition(int, int), subtract(int, int) 함수 등록 예 */ lua_tinker::def(L, “addition”, addtion); lua_tinker::def(L, “subtract”, subtract);
  • lua_tinker::def() 코드 /* F 자료형은 함수 포인터형이다 . */ template<typename F> void def(lua_State* L, const char* name, F func) { /* 함수명을 Stack 에 넣기 */ lua_pushstring(L, name); /* Upvalue 에 함수 입력 */ lua_pushlightuserdata(L, func); /* template 함수로 F 자료형을 분석 후 functor 등록 함 수 */ push_functor(L, func); /* Lua 전역으로 함수 등록 */ lua_settable(L, LUA_GLOBALSINDEX); }
  • lua_tinker::push_functor()  lua_tinker::def() 에서 typename F 로 선언된 함수 변수형을 typename RVal, T1, T2… T6 등이 조합된 함수형으로 분리  C++ 에서 함수 인자로 void 선언이 불가능하 기 때문에 각각 함수 인자 종류에 맞춰 overloading 을 구현  분리된 RVal, typename T1, T2… T6 들은 Stack 접근을 위해서 사용됨
  • push_functor() 와 함수 뽀개기 /* def(“addition”, addtion) 호출시 typename F = int (*)(int,int) 자료형이 된다 . */ template<typename F> void def(lua_State* L, const char* name, F func) { … push_functor(L, func); … } /* push_fuctor 에 int (*)(int,int) 함수 포인터가 넘어오면 typename F 였던 함수자료형이 분석되어 각 typename Rval=int, T1=int, T2=int 을 채워 준다 */ template<typename RVal, typename T1, typename T2> void push_functor(lua_State *L, RVal (*func)(T1,T2)) { lua_pushcclosure(L, functor<T1,T2>::invoke<RVal>, 1); }
  • push_functor() overloading  typename F 를 분리하기 위해 함수 포인터형을 선언 하는데 void 사용 불가  C++ overloading 을 활용해서 함수가 0~5 개의 인자 릴 받을때에 적용하여 push_functor 를 구현 int addition(int,int) -> RVal (*func)(T1,T2) RVal=int, T1=int, T2=int -> int (*func)(int,int) int increase(int) -> RVal (*func)(T1,T2) RVal=int, T1=int, T2=void -> int (*func)(int,void) int increase(int) -> RVal (*func)(T1) RVal=int, T1=int, T2=void -> int (*func)(int) YES!! NO!! YES!!
  • push_functor() 선언 template<typename RVal> void push_functor(lua_State *L, RVal (*func)()); template<typename RVal, typename T1> void push_functor(lua_State *L, RVal (*func)(T1)); template<typename RVal, typename T1, typename T2> void push_functor(lua_State *L, RVal (*func)(T1,T2)); template<typename RVal, typename T1, typename T2, typename T3> void push_functor(lua_State *L, RVal (*func)(T1,T2,T3)); template<typename RVal, typename T1, typename T2, typename T3, typename T4> void push_functor(lua_State *L, RVal (*func)(T1,T2,T3,T4)); template<typename RVal, typename T1, typename T2, typename T3, typename T4, typename T5> void push_functor(lua_State *L, RVal (*func)(T1,T2,T3,T4,T5));
  • lua_tinker::functor 객체  lua_tinker::functor 는 template 로 구성되어있 고 Lua 에 등록 가능한 Lua C 함수 functor::ivoke() 를 갖고 있는 함수체이다 .  호출시 stack 에서 함수 인자를 적절한 형태 로 읽어오고 Upvalue 에 들어있는 함수 포인 터를 읽어와 실행한다 .  리턴값이 없는 경우의 예외 처리를 위해 template 에 대한 명시적 전문화를 사용한다 .
  • lua_tinker::functor 풀이 /* int addition(int left, int right) 를 넣는다고 했을때 */ template<typename T1, typename T2> struct functor<T1,T2> { template<typename RVal> static int invoke(lua_State *L) { push_<RVal> ( L, upvalue_< RVal(*)(T1,T2) >(L)( pop_<T1>(L,1), pop_<T2>(L,2) ) ); return 1; } };
  • lua_tinker::functor 풀이 /* int addition(int left, int right) 를 넣는다고 했을때 */ template<typename T1, typename T2> struct functor<T1,T2> { template<typename RVal> static int invoke(lua_State *L) { push_<RVal> ( L, upvalue_< RVal(*)(T1,T2) >(L)( pop_<T1>(L,1), pop_<T2>(L,2) ) ); return 1; } };
  • lua_tinker::functor 풀이 /* int addition(int left, int right) 를 넣는다고 했을때 */ template<typename int, typename int> struct functor<int,int> { template<typename int> static int invoke(lua_State *L) { push_<int> ( L, upvalue_< int(*)(int,int) >(L)( pop_<int>(L,1), pop_<int>(L,2) ) ); return 1; } };
  • lua_tinker::functor 풀이 /* int addition(int left, int right) 를 넣는다고 했을때 */ template<typename int, typename int> struct functor<int,int> { template<typename int> static int invoke(lua_State *L) { push_<int> ( L, upvalue_< int(*)(int,int) >(L)( pop_<int>(L,1), pop_<int>(L,2) ) ); return 1; } };
  • lua_tinker::functor 풀이 /* int addition(int left, int right) 를 넣는다고 했을때 */ template<typename int, typename int> struct functor<int,int> { template<typename int> static int invoke(lua_State *L) { int left; push_<int> ( L, upvalue_< int(*)(int,int) >(L)( pop_<int>(L,1), pop_<int>(L,2) ) ); return 1; } };
  • lua_tinker::functor 풀이 /* int addition(int left, int right) 를 넣는다고 했을때 */ template<typename int, typename int> struct functor<int,int> { template<typename int> static int invoke(lua_State *L) { int left = pop_<int>(L,1); push_<int> ( L, upvalue_< int(*)(int,int) >(L)( pop_<int>(L,1), pop_<int>(L,2) ) ); return 1; } };
  • lua_tinker::functor 풀이 /* int addition(int left, int right) 를 넣는다고 했을때 */ template<typename int, typename int> struct functor<int,int> { template<typename int> static int invoke(lua_State *L) { int left = pop_<int>(L,1); push_<int> ( L, upvalue_< int(*)(int,int) >(L)( left, pop_<int>(L,2) ) ); return 1; } };
  • lua_tinker::functor 풀이 /* int addition(int left, int right) 를 넣는다고 했을때 */ template<typename int, typename int> struct functor<int,int> { template<typename int> static int invoke(lua_State *L) { int left = lua_tonumber(L,1); push_<int> ( L, upvalue_< int(*)(int,int) >(L)( left, pop_<int>(L,2) ) ); return 1; } };
  • lua_tinker::functor 풀이 /* int addition(int left, int right) 를 넣는다고 했을때 */ template<typename int, typename int> struct functor<int,int> { template<typename int> static int invoke(lua_State *L) { int left = lua_tonumber(L,1); push_<int> ( L, upvalue_< int(*)(int,int) >(L)( left, pop_<int>(L,2) ) ); return 1; } };
  • lua_tinker::functor 풀이 /* int addition(int left, int right) 를 넣는다고 했을때 */ template<typename int, typename int> struct functor<int,int> { template<typename int> static int invoke(lua_State *L) { int left = lua_tonumber(L,1); int right; push_<int> ( L, upvalue_< int(*)(int,int) >(L)( left, pop_<int>(L,2) ) ); return 1; } };
  • lua_tinker::functor 풀이 /* int addition(int left, int right) 를 넣는다고 했을때 */ template<typename int, typename int> struct functor<int,int> { template<typename int> static int invoke(lua_State *L) { int left = lua_tonumber(L,1); int right = pop_<int>(L,2); push_<int> ( L, upvalue_< int(*)(int,int) >(L)( left, pop_<int>(L,2) ) ); return 1; } };
  • lua_tinker::functor 풀이 /* int addition(int left, int right) 를 넣는다고 했을때 */ template<typename int, typename int> struct functor<int,int> { template<typename int> static int invoke(lua_State *L) { int left = lua_tonumber(L,1); int right = pop_<int>(L,2); push_<int> ( L, upvalue_< int(*)(int,int) >(L)( left, right, ) ); return 1; } };
  • lua_tinker::functor 풀이 /* int addition(int left, int right) 를 넣는다고 했을때 */ template<typename int, typename int> struct functor<int,int> { template<typename int> static int invoke(lua_State *L) { int left = lua_tonumber(L,1); int right = lua_tonumber(L,2); push_<int> ( L, upvalue_< int(*)(int,int) >(L)( left, right, ) ); return 1; } };
  • lua_tinker::functor 풀이 /* int addition(int left, int right) 를 넣는다고 했을때 */ template<typename int, typename int> struct functor<int,int> { template<typename int> static int invoke(lua_State *L) { int left = lua_tonumber(L,1); int right = lua_tonumber(L,2); push_<int> ( L, upvalue_< int(*)(int,int) >(L)( left, right, ) ); return 1; } };
  • lua_tinker::functor 풀이 /* int addition(int left, int right) 를 넣는다고 했을때 */ template<typename int, typename int> struct functor<int,int> { template<typename int> static int invoke(lua_State *L) { int left = lua_tonumber(L,1); int right = lua_tonumber(L,2); int(func*)(int,int); push_<int> ( L, upvalue_< int(*)(int,int) >(L)( left, right, ) ); return 1; } };
  • lua_tinker::functor 풀이 /* int addition(int left, int right) 를 넣는다고 했을때 */ template<typename int, typename int> struct functor<int,int> { template<typename int> static int invoke(lua_State *L) { int left = lua_tonumber(L,1); int right = lua_tonumber(L,2); int(func*)(int,int) = upvalue_< int(*)(int,int) >(L); push_<int> ( L, upvalue_< int(*)(int,int) >(L)( left, right, ) ); return 1; } };
  • lua_tinker::functor 풀이 /* int addition(int left, int right) 를 넣는다고 했을때 */ template<typename int, typename int> struct functor<int,int> { template<typename int> static int invoke(lua_State *L) { int left = lua_tonumber(L,1); int right = lua_tonumber(L,2); int(func*)(int,int) = upvalue_< int(*)(int,int) >(L); push_<int> ( L, func( left, right, ) ); return 1; } };
  • lua_tinker::functor 풀이 /* int addition(int left, int right) 를 넣는다고 했을때 */ template<typename int, typename int> struct functor<int,int> { template<typename int> static int invoke(lua_State *L) { int left = lua_tonumber(L,1); int right = lua_tonumber(L,2); int(func*)(int,int) = upvalue_< int(*)(int,int) >(L); push_<int> ( L, func(left, right) ); return 1; } };
  • lua_tinker::functor 풀이 /* int addition(int left, int right) 를 넣는다고 했을때 */ template<typename int, typename int> struct functor<int,int> { template<typename int> static int invoke(lua_State *L) { int left = lua_tonumber(L,1); int right = lua_tonumber(L,2); int(func*)(int,int) = lua_touserdata(L,lua_upvalueindex(1)); push_<int> ( L, func(left, right) ); return 1; } };
  • lua_tinker::functor 풀이 /* int addition(int left, int right) 를 넣는다고 했을때 */ template<typename int, typename int> struct functor<int,int> { template<typename int> static int invoke(lua_State *L) { int left = lua_tonumber(L,1); int right = lua_tonumber(L,2); int(func*)(int,int) = (int(func*)(int,int) ) lua_touserdata(L,lua_upvalu…); push_<int> ( L, func(left, right) ); return 1; } };
  • lua_tinker::functor 풀이 /* int addition(int left, int right) 를 넣는다고 했을때 */ template<typename int, typename int> struct functor<int,int> { template<typename int> static int invoke(lua_State *L) { int left = lua_tonumber(L,1); int right = lua_tonumber(L,2); int(func*)(int,int) = (int(func*)(int,int) )lua_touserdata(L,lua_upvalu…); push_<int> ( L, func(left, right) ); return 1; } };
  • lua_tinker::functor 풀이 /* int addition(int left, int right) 를 넣는다고 했을때 */ template<typename int, typename int> struct functor<int,int> { template<typename int> static int invoke(lua_State *L) { int left = lua_tonumber(L,1); int right = lua_tonumber(L,2); int(func*)(int,int) = (int(func*)(int,int) )lua_touserdata(L,lua_upvalu…); int result; push_<int> ( L, func(left, right) ); return 1; } };
  • lua_tinker::functor 풀이 /* int addition(int left, int right) 를 넣는다고 했을때 */ template<typename int, typename int> struct functor<int,int> { template<typename int> static int invoke(lua_State *L) { int left = lua_tonumber(L,1); int right = lua_tonumber(L,2); int(func*)(int,int) = (int(func*)(int,int) )lua_touserdata(L,lua_upvalu…); int result = func(left, right); push_<int> ( L, func(left, right) ); return 1; } };
  • lua_tinker::functor 풀이 /* int addition(int left, int right) 를 넣는다고 했을때 */ template<typename int, typename int> struct functor<int,int> { template<typename int> static int invoke(lua_State *L) { int left = lua_tonumber(L,1); int right = lua_tonumber(L,2); int(func*)(int,int) = (int(func*)(int,int) )lua_touserdata(L,lua_upvalu…); int result = func(left, right); push_<int> ( L, result ); return 1; } };
  • lua_tinker::functor 풀이 /* int addition(int left, int right) 를 넣는다고 했을때 */ template<typename int, typename int> struct functor<int,int> { template<typename int> static int invoke(lua_State *L) { int left = lua_tonumber(L,1); int right = lua_tonumber(L,2); int(func*)(int,int) = (int(func*)(int,int) )lua_touserdata(L,lua_upvalu…); int result = func(left, right); push_<int> (L, result); return 1; } };
  • lua_tinker::functor 풀이 /* int addition(int left, int right) 를 넣는다고 했을때 */ template<typename int, typename int> struct functor<int,int> { template<typename int> static int invoke(lua_State *L) { int left = lua_tonumber(L,1); int right = lua_tonumber(L,2); int(func*)(int,int) = (int(func*)(int,int) )lua_touserdata(L,lua_upvalu…); int result = func(left, right); lua_pushnumber(L, result); return 1; } };
  • lua_tinker 와 push_(), pop_()  lua_tinker::push_(), lua_tinker::pop_() 함수는 Lua Stack 에 접근하여 데이터를 교환하는 함 수들이다 .  push_() 와 pop_() 모두 template 으로 제작 되었으며 각 타잎에 따른 명시적 전문화으로 적합한 데이터 교환이 이뤄진다 .  명시적 전문화가 되지 않은 구조체 , 클래스의 경우 Lua 내의 metatable 을 붙여서 객체로 취급하고 특별한 처리를 한다 .
  • lua_tinker::push_() 코드 /* template 으로 선언되고 1:1 변환이 가능한 것은 명시적 전문화가 되어 있다 . */ template<typename T> void push_(lua_State *L, T ret); /* 명시적 전문화 : int 는 number 자료형으로 변환이 가능하다 . */ template<> void push_(lua_State *L, int ret) { lua_pushnumber(L, ret); } /* 명시적 전문화 : const char* 는 string 자료형으로 변환이 가능하다 . */ template<> void push_(lua_State *L, const char* ret) { lua_pushstring(L, ret); } /* 변환 존재하지 않을 경우 userdata 로 변환하는 type2lua() 를 호출한 다 . */ template<typename T> void push_(lua_State *L, T ret) { type2lua<T>(L, ret); }
  • 1:1 변환이 불가능한 자료형  Lua 와 1:1 변환이 불가능한 자료형은 Lua 에서 할당받은 메모리에 복사해 넣는다 .  Value, Pointer, Reference 여부에 따라 userdata 에 전체를 복사하거나 포인터만 넣어 주게 된다 .  enum 는 별개의 데이터 형이지만 Lua 내에 서 값에 접근하기 좋게 숫자로 취급한다 .  변환에는 lua_tinker::type2lua() 함수를 호출 하고 이 함수는 template if 문이 사용되었다 .
  • type2lua() 코드 template<typename T> void type2lua(lua_State *L, T val) { if_<is_enum<T>::value /* T 자료형이 enum 인가 ? */ ,enum2lua<T> /* enum2lua functor */ ,object2lua<T> /* object2lua fuctor */ >::type::invoke(L, val); /* functor 를 호출한다 . */ } template<typename T> struct enum2lua { static void invoke(lua_State *L, T val); } template<typename T> struct object2lua { static void invoke(lua_State *L, T val); }
  • template if 구문  3 개의 인자를 받는 template 구조체  첫번째 bool 조건에 따라 구조체 내의 type 자료형이 A 또는 B 형으로 정의됨  type 은 ‘자료형’ 이기 때문에 함수 포인터나 인스턴스가 못오고 functor 를 사용한다 . template<bool C, typename A, typename B> struct if_ {}; template<typename A, typename B> struct if_<true, A, B> { typedef A type; }; template<typename A, typename B> struct if_<false, A, B> { typedef B type; }; if_<true, int, char>::type == int if_<false, int, char>::type == char
  • Template if 구문 풀이 /* lua_tinker::push() 함수의 기본형 */ template<typename T> void push_(lua_State *L, T ret) { type2lua<T>(L, ret); }
  • Template if 구문 풀이 /* lua_tinker::push() 함수의 기본형 */ template<typename T> void push_(lua_State *L, T ret) { type2lua<T>(L, ret); } /* lua_tinker::type2lua() 함수 */ template<typename T> void type2lua(lua_State *L, T val) { if_<is_enum<T>::value ,enum2lua<T> ,object2lua<T> >::type::invoke(L, val); }
  • Template if 구문 풀이 /* lua_tinker::push() 함수의 기본형 */ template<typename T> void push_(lua_State *L, T ret) { type2lua<T>(L, ret); } /* lua_tinker::type2lua() 함수 */ template<typename T> void type2lua(lua_State *L, T val) { if_<is_enum<T>::value ,enum2lua<T> ,object2lua<T> >::type::invoke(L, val); }
  • Template if 구문 풀이 /* lua_tinker::push() 함수의 기본형 */ template<typename T> void push_(lua_State *L, T ret) { type2lua<T>(L, ret); } /* lua_tinker::type2lua() 함수 */ template<typename T> void type2lua(lua_State *L, T val) { if_<is_enum<T>::value ,enum2lua<T> ,object2lua<T> >::type::invoke(L, val); }
  • Template if 구문 풀이 /* lua_tinker::push() 함수의 기본형 */ template<typename T> void push_(lua_State *L, T ret) { if_<is_enum<T>::value ,enum2lua<T> ,object2lua<T> >::type::invoke(L, val); }
  • Template if 구문 풀이 /* lua_tinker::push() 함수의 기본형 */ template<typename T> void push_(lua_State *L, T ret) { if_<is_enum<T>::value ,enum2lua<T> ,object2lua<T> >::type::invoke(L, val); } /* 테스트 enum 데이터 */ enum FRUIT { APPLE, BANANA, GRAPE };
  • Template if 구문 풀이 /* lua_tinker::push() 함수의 기본형 */ template<typename T> void push_(lua_State *L, T ret) { if_<is_enum<T>::value ,enum2lua<T> ,object2lua<T> >::type::invoke(L, val); } /* 테스트 enum 데이터 */ enum FRUIT { APPLE, BANANA, GRAPE };
  • Template if 구문 풀이 /* lua_tinker::push() 함수의 기본형 */ template<typename T> void push_(lua_State *L, T ret=APPLE) { if_<is_enum<T>::value ,enum2lua<T> ,object2lua<T> >::type::invoke(L, val); } /* 테스트 enum 데이터 */ enum FRUIT { APPLE, BANANA, GRAPE };
  • Template if 구문 풀이 /* lua_tinker::push() 함수의 기본형 */ template<FRUIT> void push_(lua_State *L, FRUIT ret=APPLE) { if_<is_enum<FRUIT>::value ,enum2lua<FRUIT> ,object2lua<FRUIT> >::type::invoke(L, val); } /* 테스트 enum 데이터 */ enum FRUIT { APPLE, BANANA, GRAPE };
  • Template if 구문 풀이 /* lua_tinker::push() 함수의 기본형 */ template<FRUIT> void push_(lua_State *L, FRUIT ret=APPLE) { if_<is_enum<FRUIT>::value ,enum2lua<FRUIT> ,object2lua<FRUIT> >::type::invoke(L, val); } /* 테스트 enum 데이터 */ enum FRUIT { APPLE, BANANA, GRAPE };
  • Template if 구문 풀이 /* lua_tinker::push() 함수의 기본형 */ template<FRUIT> void push_(lua_State *L, FRUIT ret=APPLE) { if_<is_enum<FRUIT>::value = true ,enum2lua<FRUIT> ,object2lua<FRUIT> >::type::invoke(L, val); } /* 테스트 enum 데이터 */ enum FRUIT { APPLE, BANANA, GRAPE };
  • Template if 구문 풀이 /* lua_tinker::push() 함수의 기본형 */ template<FRUIT> void push_(lua_State *L, FRUIT ret=APPLE) { if_<is_enum<FRUIT>::value = true ,enum2lua<FRUIT> ,object2lua<FRUIT> >::type::invoke(L, val); } /* 테스트 enum 데이터 */ enum FRUIT { APPLE, BANANA, GRAPE }; /* template if 코드 */ template<bool C, typename A, typename B> struct if_ {}; template<typename A, typename B> struct if_<true, A, B> { typedef A type; }; template<typename A, typename B> struct if_<false, A, B> { typedef B type; };
  • Template if 구문 풀이 /* lua_tinker::push() 함수의 기본형 */ template<FRUIT> void push_(lua_State *L, FRUIT ret=APPLE) { if_<is_enum<FRUIT>::value = true ,enum2lua<FRUIT> ,object2lua<FRUIT> >::type::invoke(L, val); } /* 테스트 enum 데이터 */ enum FRUIT { APPLE, BANANA, GRAPE }; /* template if 코드 */ template<bool C, typename A, typename B> struct if_ {}; template<typename A, typename B> struct if_< true, A, B> { typedef A type; }; template<typename A, typename B> struct if_<false, A, B> { typedef B type; };
  • Template if 구문 풀이 /* lua_tinker::push() 함수의 기본형 */ template<FRUIT> void push_(lua_State *L, FRUIT ret=APPLE) { if_<is_enum<FRUIT>::value = true ,enum2lua<FRUIT> ,object2lua<FRUIT> >::type::invoke(L, val); } /* 테스트 enum 데이터 */ enum FRUIT { APPLE, BANANA, GRAPE }; /* template if 코드 */ template<typename A, typename B> struct if_< true, A, B> { typedef A type; };
  • Template if 구문 풀이 /* lua_tinker::push() 함수의 기본형 */ template<FRUIT> void push_(lua_State *L, FRUIT ret=APPLE) { if_<is_enum<FRUIT>::value = true ,enum2lua<FRUIT> ,object2lua<FRUIT> >::type::invoke(L, val); } /* 테스트 enum 데이터 */ enum FRUIT { APPLE, BANANA, GRAPE }; /* template if 코드 */ template<typename A, typename B> struct if_<true, A, B>{ typedef A type; };
  • Template if 구문 풀이 /* lua_tinker::push() 함수의 기본형 */ template<FRUIT> void push_(lua_State *L, FRUIT ret=APPLE) { if_<is_enum<FRUIT>::value = true ,enum2lua<FRUIT> ,object2lua<FRUIT> >::type::invoke(L, val); } /* 테스트 enum 데이터 */ enum FRUIT { APPLE, BANANA, GRAPE }; /* template if 코드 */ template<typename A=enum2lua<FRUIT>, typename B=object2lua<FRUIT>> struct if_<true, A, B>{ typedef A type; };
  • Template if 구문 풀이 /* lua_tinker::push() 함수의 기본형 */ template<FRUIT> void push_(lua_State *L, FRUIT ret=APPLE) { if_<is_enum<FRUIT>::value = true ,enum2lua<FRUIT> ,object2lua<FRUIT> >::type::invoke(L, val); } /* 테스트 enum 데이터 */ enum FRUIT { APPLE, BANANA, GRAPE }; /* template if 코드 */ template<typename A=enum2lua<FRUIT>, typename B=object2lua<FRUIT>> struct if_<true, enum2lua<FRUIT>, B>{ typedef A type; };
  • Template if 구문 풀이 /* lua_tinker::push() 함수의 기본형 */ template<FRUIT> void push_(lua_State *L, FRUIT ret=APPLE) { if_<is_enum<FRUIT>::value = true ,enum2lua<FRUIT> ,object2lua<FRUIT> >::type::invoke(L, val); } /* 테스트 enum 데이터 */ enum FRUIT { APPLE, BANANA, GRAPE }; /* template if 코드 */ template<typename A=enum2lua<FRUIT>, typename B=object2lua<FRUIT>> struct if_<true, enum2lua<FRUIT>, B>{ typedef enum2lua<FRUIT> type; };
  • Template if 구문 풀이 /* lua_tinker::push() 함수의 기본형 */ template<FRUIT> void push_(lua_State *L, FRUIT ret=APPLE) { if_<is_enum<FRUIT>::value = true ,enum2lua<FRUIT> ,object2lua<FRUIT> >::type::invoke(L, val); } /* 테스트 enum 데이터 */ enum FRUIT { APPLE, BANANA, GRAPE }; /* template if 코드 */ template<typename A=enum2lua<FRUIT>, typename B=object2lua<FRUIT>> struct if_<true, enum2lua<FRUIT>, object2lua<FRUIT>>{ typedef enum2lua<FRUIT> type; };
  • Template if 구문 풀이 /* lua_tinker::push() 함수의 기본형 */ template<FRUIT> void push_(lua_State *L, FRUIT ret=APPLE) { if_<is_enum<FRUIT>::value = true ,enum2lua<FRUIT> ,object2lua<FRUIT> >::type::invoke(L, val); } /* 테스트 enum 데이터 */ enum FRUIT { APPLE, BANANA, GRAPE }; /* template if 코드 */ template<> struct if_<true, enum2lua<FRUIT>, object2lua<FRUIT>>{ typedef enum2lua<FRUIT> type; };
  • Template if 구문 풀이 /* lua_tinker::push() 함수의 기본형 */ template<FRUIT> void push_(lua_State *L, FRUIT ret=APPLE) { if_<is_enum<FRUIT>::value = true ,enum2lua<FRUIT> ,object2lua<FRUIT> >::type::invoke(L, val); } /* 테스트 enum 데이터 */ enum FRUIT { APPLE, BANANA, GRAPE }; /* template if 코드 */ template<> struct if_<true, enum2lua<FRUIT>, object2lua<FRUIT>>{ typedef enum2lua<FRUIT> type; };
  • Template if 구문 풀이 /* lua_tinker::push() 함수의 기본형 */ template<FRUIT> void push_(lua_State *L, FRUIT ret=APPLE) { enum2lua<FRUIT>::invoke(L, val); } /* 테스트 enum 데이터 */ enum FRUIT { APPLE, BANANA, GRAPE }; /* template if 코드 */ template<> struct if_<true, enum2lua<FRUIT>, object2lua<FRUIT>>{ typedef enum2lua<FRUIT> type; };
  • Template if 구문 풀이 /* lua_tinker::push() 함수의 기본형 */ template<typename T> void push_(lua_State *L, T ret) { if_<is_enum<T>::value ,enum2lua<T> ,object2lua<T> >::type::invoke(L, val); } /* 테스트 struct 데이터 */ Struct Damage { int value; int count; }
  • Template if 구문 풀이 /* lua_tinker::push() 함수의 기본형 */ template<typename T> void push_(lua_State *L, T ret) { if_<is_enum<T>::value ,enum2lua<T> ,object2lua<T> >::type::invoke(L, val); } /* 테스트 struct 데이터 */ Struct Damage { int value; int count; } Damage myDamage;
  • Template if 구문 풀이 /* lua_tinker::push() 함수의 기본형 */ template<typename Damage> void push_(lua_State *L, T ret=myDamage) { if_<is_enum<Damage>::value ,enum2lua<Damage> ,object2lua<Damage> >::type::invoke(L, val); } /* 테스트 struct 데이터 */ Struct Damage { int value; int count; } Damage myDamage;
  • Template if 구문 풀이 /* lua_tinker::push() 함수의 기본형 */ template<typename Damage> void push_(lua_State *L, T ret=myDamage) { if_<is_enum<Damage>::value = false ,enum2lua<Damage> ,object2lua<Damage> >::type::invoke(L, val); } /* 테스트 struct 데이터 */ Struct Damage { int value; int count; } Damage myDamage;
  • Template if 구문 풀이 /* lua_tinker::push() 함수의 기본형 */ template<typename Damage> void push_(lua_State *L, T ret=myDamage) { if_<is_enum<Damage>::value = false ,enum2lua<Damage> ,object2lua<Damage> >::type::invoke(L, val); } /* 테스트 struct 데이터 */ Struct Damage { int value; int count; } Damage myDamage; /* template if 코드 */ template<bool C, typename A, typename B> struct if_ {}; template<typename A, typename B> struct if_<true, A, B> { typedef A type; }; template<typename A, typename B> struct if_<false, A, B> { typedef B type; };
  • Template if 구문 풀이 /* lua_tinker::push() 함수의 기본형 */ template<typename Damage> void push_(lua_State *L, T ret=myDamage) { if_<is_enum<Damage>::value = false ,enum2lua<Damage> ,object2lua<Damage> >::type::invoke(L, val); } /* 테스트 struct 데이터 */ Struct Damage { int value; int count; } Damage myDamage; /* template if 코드 */ template<bool C, typename A, typename B> struct if_ {}; template<typename A, typename B> struct if_<true, A, B> { typedef A type; }; template<typename A, typename B> struct if_<false, A, B> { typedef B type; };
  • Template if 구문 풀이 /* lua_tinker::push() 함수의 기본형 */ template<typename Damage> void push_(lua_State *L, T ret=myDamage) { if_<is_enum<Damage>::value = false ,enum2lua<Damage> ,object2lua<Damage> >::type::invoke(L, val); } /* 테스트 struct 데이터 */ Struct Damage { int value; int count; } Damage myDamage; /* template if 코드 */ template<typename A, typename B> struct if_<false, A, B> { typedef B type; };
  • Template if 구문 풀이 /* lua_tinker::push() 함수의 기본형 */ template<typename Damage> void push_(lua_State *L, T ret=myDamage) { if_<is_enum<Damage>::value = false ,enum2lua<Damage> ,object2lua<Damage> >::type::invoke(L, val); } /* 테스트 struct 데이터 */ Struct Damage { int value; int count; } Damage myDamage; /* template if 코드 */ template<enum2lua<Damage>, object2lua<Damage>> struct if_<false, A, B> { typedef B type; };
  • Template if 구문 풀이 /* lua_tinker::push() 함수의 기본형 */ template<typename Damage> void push_(lua_State *L, T ret=myDamage) { if_<is_enum<Damage>::value = false ,enum2lua<Damage> ,object2lua<Damage> >::type::invoke(L, val); } /* 테스트 struct 데이터 */ Struct Damage { int value; int count; } Damage myDamage; /* template if 코드 */ template<enum2lua<Damage>, object2lua<Damage>> struct if_<false, enum2lua<Damage>, object2lua<Damage>> { typedef object2lua<Damage> type; };
  • Template if 구문 풀이 /* lua_tinker::push() 함수의 기본형 */ template<typename Damage> void push_(lua_State *L, T ret=myDamage) { if_<is_enum<Damage>::value = false ,enum2lua<Damage> ,object2lua<Damage> >::type::invoke(L, val); } /* 테스트 struct 데이터 */ Struct Damage { int value; int count; } Damage myDamage; /* template if 코드 */ template<enum2lua<Damage>, object2lua<Damage>> struct if_<false, enum2lua<Damage>, object2lua<Damage>> { typedef object2lua<Damage> type; };
  • Template if 구문 풀이 /* lua_tinker::push() 함수의 기본형 */ template<typename Damage> void push_(lua_State *L, T ret=myDamage) { if_<is_enum<Damage>::value = false ,enum2lua<Damage> ,object2lua<Damage> >::type::invoke(L, val); } /* 테스트 struct 데이터 */ Struct Damage { int value; int count; } Damage myDamage; /* template if 코드 */ template<enum2lua<Damage>, object2lua<Damage>> struct if_<false, enum2lua<Damage>, object2lua<Damage>> { typedef object2lua<Damage> type; };
  • Template if 구문 풀이 /* lua_tinker::push() 함수의 기본형 */ template<typename Damage> void push_(lua_State *L, T ret=myDamage) { object2lua<Damage>::type::invoke(L, val); } /* 테스트 struct 데이터 */ Struct Damage { int value; int count; } Damage myDamage; /* template if 코드 */ template<enum2lua<Damage>, object2lua<Damage>> struct if_<false, enum2lua<Damage>, object2lua<Damage>> { typedef object2lua<Damage> type; };
  • enum2lua {} functor  enum 값을 Lua 의 number 타잎으로 변환하 여 Stack 에 넣어준다 .  Template if 문에 사용되기 위해서 함수체로 만들어졌고 is_enum<T> 평가자와 함께 쓰이 게 된다 . template<typename T> struct enum2lua { static void invoke(lua_State *L, T val) { /* number 타잎으로 값을 Lua Stack 에 밀어넣는다 . */ lua_pushnumber(L, (int)val); } };
  • object2lua {} functor  template if 문에 사용될 수 있게 만들어진 functor 로 invoke() 함수를 갖고 있다 .  어떠한 object 를 인자로 받아 Lua 로부터 할 당받은 메모리에 복사하는 역할을 한다 .  넘어온 object 가 value, pointer, reference 에 따라서 객체 전체 복사 , 포인터 복사 , 리 퍼렌스 복사중 한가지 작업을 하게된다 .
  • lua_tinker::object2lua 코드 template<typename T> struct object2lua { /* template if 에 사용하게 만들어진 functor */ static void invoke(lua_State *L, T val) { /* 다중 if 문이 사용되어 포인터 , 리퍼렌스 , 값 여부를 체크한다 . */ if_<is_ptr<T>::value ,ptr2lua<base_type<T>::type> /* 기본 자료형을 찾는다 */ ,if_<is_ref<T>::value ,ref2lua<base_type<T>::type> ,val2lua<base_type<T>::type> >::type >::type::invoke(L, val); }; } /* 생성된 개체의 metatable 을 설정한다 . */ class_<class_type<T>::type>::push_meta(L); lua_setmetatable(L, -2);
  • Lua 에서의 객체  lua_tinker 는 struct/class 는 멤버 함수 , 멤버 변수의 접근이 가능을 제공해야한다 .  Lua 는 객체지향 언어가 아니지만 table 이란 자료형을 통해서 구조체와 흡사한 기능을 제공 하고 있다 .  Lua 에서 제공하는 metatable 이란 것을 통해 서 table 접근용 operator 들을 재정의해서 struct/class 에 대한 지원을 구현하고 있다 .
  • lua_tinker 와 메타테이블  Lua 메타테이블은 어떠한 값이나 가질 수 있 고 메타테이블을 통해 기본적으로 정해진 속성 들을 변경할 수 있다 .  lua_tinker 는 메타테이블의 여러 기능중 테이 블 접근에 사용하는 . 오퍼레이터와 Garbage Collecting 함수를 오버라이드 한다 .
  • 메타테이블 __index  __index 를 재정의하면 table 에서 어떠한 값 을 읽어오는 함수를 재정의한다 . -- Lua 에서는 [] 와 . 오퍼레이터가 동일하게 작동한다 . value = character[“hp”] value = character.hp  __index 에서 멤버 함수 functor 를 리턴하면 멤버 함수로 호출이 가능하다 . -- functor 는 this 포인터를 모르므로 첫번째 인자로 개체 자체를 넘겨준다 . character.run(character) -- : 오퍼레이터는 이러한 작업을 단순화 시켜준다 . character:run()
  • 메타테이블 __newindex, __gc  __newindex : table 에 접근하여 어떠한 값을 갱신하거나 써 넣을때 사용된다 . character[“hp”] = value character.hp = value  __gc : 메모리가 해지될때의 이벤트로 lua_tinker::destroyer() 가 불린다 . template<typename T> int destroyer(lua_State *L) { /* user 객체로 타잎캐스팅 후 삭제하므로 virtual 함수가 호출된 다 . */ ((user*)lua_touserdata(L, 1))->~user(); return 0; }
  • LuaTinker 의 목표  Lua 버젼에 따라 계속적인 업데이트  동적 할당되는 메모리를 줄이고 포인터로 작동 하여 가벼움 유지  Table 접근이나 전역 변수 접근 기능 강화 .
  • LuaTinker Q&A