U E D R S I H C RSS
ID
Password
Join
교육의 비결은 학생들을 존중하는 데 있지요. ―랠프 월도 에머슨

 * 4.0이전 버젼에서는 테그 메소드가 5.0에서는 메타테이블로 변경되었습니다. 당연히 문법도 다소 바뀌었기에 이렇게 따로 정리해봅니다.
  • 레퍼런스 메뉴얼을 참조했고요. 내용이 쪼개져 있는 것같아 붙여서 번역+편집해보았습니다.
  • 위키에서는 밑줄문자를 연달아 두번쓰면 밑줄긋기가 되어서 메타테이블 필드는 메타메소드로 지칭합니다.

Contents

1 개요
2 이벤트
2.1 add, sub, mul, div, pow
2.2 unm
2.3 lt, le
2.4 concat
2.5 index, gettable, newindex, settable
2.6 call
3 개비지 컬렉션

1 개요 #

루아내에서 테이블과 유저데이타는 메타테이블을 가지는 것이 가능하다. 지정한 객체의 메타테이블내의 특정 필드를 설정하는 것에 의해 그 객체의 특정 경우(이벤트)의 처리를 변경하는 것이 가능하다. 예를 들어, 특정 객체가 더하기 연산를 하고 있다면, 루아는 그 객체의 add 메타메소드에 설정된 함수가 있는지 검사한다. 만일 있다면, 더하기 연산에 대한 처리로서 그 함수를 실행한다. add메타메소드와 같은 특별한 키값과 연관이 되어있는데 이것을 루아에서는 "이벤트"라고 부른다. (다시 말하면, 해당 연산이 실행될때 이벤트에 연관된 함수가 실행된다.)

메타테이블은 산술연산, 순서비교, 병합 및 인덱싱처리시 어떻게 객체가 동작하는지를 정의한다. 또한 유저데이타가 개비지 컬랙션처리될 때 실행될 함수를 등록하는 것이 가능하다.

각 연산은 각각의 고유한 키값으로 명칭이 예약이 되어있다. 이 명칭은 _ _(밑줄문자 연달아적은것)으로 시작된다. (예를 들면, 앞서 얘를 들었듯이 더하기 연산은 add 메타메소드로 지정된다.) 아래에 예로 든 코드들은 그저 구조를 설명하기 위한 것이고, 실제로는 루아내에 하드코딩되어있으며, 훨씬 더 효율적으로 되어있다.

2 이벤트 #

2.1 add, sub, mul, div, pow #

(이 연산자들은 각각 +, -, *, /, ^(덧셈, 뺄셈, 곱셈, 나눗셈, 거듭제곱) 연산을 나타내는 이벤트들이다. 아래는 덧셈에 대한 부분만 설명한 내용이며, 나머지 연산들은 이와 유사하므로 생략한다.)

아래의 getbinhandler함수는 루아가 어떻게 이진 연산을 위해 핸들러를 선택하는지를 보여준다. 먼저, 루아는 좌변의 연산자 이벤트 함수를 사용하는 것을 시도한다. 만약 좌변에 대한 핸들러가 정의되어있지 않다면, 우변에 대해서도 시도한다.
function getbinhandler(op1, op2, event)
    return metatable(op1)[event] or metatable(op2)[event]
end

이 함수를 사용해서 "add"이벤트 함수를 기술해보면 다음과 같다.
function add_event(op1, op2)
   local o1, o2 = tonumber(op1), tonumber(op2)
   if o1 and o2 then  -- 좌변, 우변이 모두 숫자이다.
      return o1 + o2  -- "add"에 대한 기본 연산을 수행한다.
   else               -- 한개이상의 변수가 숫자가 아니라면
      local h = getbinhandler(op1, op2, "__add")
      if h then
         -- 좌변, 우변에 대한 핸들러를 호출한다.
         return h(op1, op2)
      else
         -- 핸들러가 없다. 기본 실행을 수행한다.
         error("수치연산을 실행할 수 없는 타입입니다.");
      end
   end
end

2.2 unm #

"-" 연산을 할때 실행되는 이벤트. (음수처리하는 연산자)
function unm_event (op)
   local o = tonumber(op)
   if o then -- 피연산자가 숫자라면 is numeric
      return -o -- 단일 연산인 "-"연산을 수행한다.
   else -- 피연산자가 숫자가 아니면,
      -- 피연산자로부터 핸들러를 얻는 것을 시도한다.
      local h = metatable(op).__unm
      if h then
         -- 피연산자와 nil을 사용하여 핸들러를 실행시킨다.
         return h(op, nil)
      else -- 핸들러가 없다. 기본 처리.
         error("수치연산을 실행할 수 없는 타입입니다.")
      end
   end
end

2.3 lt, le #

"lt"이벤트는 "<" 연산자에 해당되는 이벤트이다. gt같은 이벤트는 없다. (a>b는 b<a로 비교할 수 있으므로 구현하지 않은듯...)
function lt_event (op1, op2)
   if type(op1) == "number" and type(op2) == "number" then
      return op1 < op2 -- 피연산자가 숫자라면 숫자 비교
   elseif type(op1) == "string" and type(op2) == "string" then
      return op1 < op2 -- 피연산자가 문자열이면 문자열 비교
   else
      local h = getbinhandler(op1, op2, "__lt")
      if h then
         return h(op1, op2)
      else
         error("비교할 수 없는 타입입니다.");
      end
   end
end

"le"는 "<=" 연산자에 해당되는 이벤트이다. 만일 "le" 이벤트를 찾을 수 없으면 루아는 자동적으로 "lt"로 대체하여 검사한다. (다시말하면, a<=b는 not (b<a)와 같다.)
function lt_event (op1, op2)
   if type(op1) == "number" and type(op2) == "number" then
      return op1 < op2 -- 피연산자가 숫자라면 숫자 비교
   elseif type(op1) == "string" and type(op2) == "string" then
      return op1 < op2 -- 피연산자가 문자열이면 문자열 비교
   else
      local h = getbinhandler(op1, op2, "__le")
      if h then
         return h(op1, op2)
      else
         h = getbinhandler(op1, op2, "__lt")
         if h then
            return not h(op2, op1)
         else
            error("비교할 수 없는 타입입니다.");
         end
      end
   end
end

2.4 concat #

".." 병합 연산자에 대한 이벤트처리.
function concat_event (op1, op2)
   if (type(op1) == "string" or type(op1) == "number") and (type(op2) == "string" or type(op2) == "number") then
      return op1..op2 -- 피연산자들이 숫자나 문자열이라면 문자열 병합연산을 수행한다
   else
      local h = getbinhandler(op1, op2, "__concat")
      if h then
         return h(op1, op2)
      else
         error("병합연산을 수행할 수 없는 타입입니다.")
      end
   end
end

2.5 index, gettable, newindex, settable #

"index" 이벤트는 테이블내에 존재하지 않는 인덱스의 값을 얻으려고 시도할 때 발생한다. "gettable" 이벤트는 테이블내의 적법한 인덱스에 해당되는 값을 얻으려고 할때 발생한다. 다음은 gettable 이벤트의 처리과정을 나타내는 간략한 코드이다.
function gettable_event (table, key)
   local h
   if type(table) == "table" then
      local v = rawget(table, key)
      if v ~= nil then return v end
      h = metatable(table).__index
      if h == nil then return nil end
   else
      h = metatable(table).__gettable
      if h == nil then
         error("테이블이 아닌 값에 인덱스 구문을 사용했습니다.");
      end
   end
   if type(h) == "function" then
      return h(table, key) -- 핸들러를 호출한다.
   else return h[key] -- 또는 연산을 반복한다.
end

"newindex" 이벤트는 테이블내에 존재하지 않는 인덱스의 값을 삽입하려고 할때 발생한다. "settable" 이벤트는 테이블내의 적법한 인덱스에 해당되는 위치에 값을 설정할때 발생한다. 다음은 settable 이벤트의 처리과정을 나타내는 간략한 코드이다.

function settable_event (table, key, value)
   local h
   if type(table) == "table" then
      local v = rawget(table, key)
      if v ~= nil then rawset(table, key, value); return end
      h = metatable(table).__newindex
      if h == nil then rawset(table, key, value); return end
   else
      h = metatable(table).__settable
      if h == nil then
         error("테이블이 아닌 값에 인덱스 구문을 사용했습니다.");
      end
   end
   if type(h) == "function" then
      return h(table, key,value) -- 핸들러 호출.
   else h[key] = value -- 또는 같은 연산 반복
end

2.6 call #

함수값이 호출되면 실행되는 이벤트.
function function_event (func, ...)
   if type(func) == "function" then
      return func(unpack(arg)) -- 일반적인 호출
   else
      local h = metatable(func).__call
      if h then
         tinsert(arg, 1, func)
         return h(unpack(arg))
      else
         error("함수가 아닌 값에 호출을 실행했습니다.")
      end
   end
end

3 개비지 컬렉션 #

메타테이블은 또한 유저데이타를 위한 소멸자를 정의하는 것이 가능하다. 각 유저데이타가 개비지 컬렉션 처리될 때마다, 루아는 "gc"라는 이벤트를 발생시키고, 그에 해당되는 핸들러를 실행한다.
function gc_event (obj)
   local h = metatable(obj).__gc
   if h then
      h(obj)
   end
end
개비지 컬렉션 내부의 처리 사이클에서, 유저데이타의 소멸자들은 생성된 순서의 역순으로 호출된다. 다시말하면, 프로그램내에서 가장 마지막 유저데이타와 연결되어있는 핸들러가 가장 먼저 실행되는 소멸자가 된다.

Valid XHTML 1.0! Valid CSS! powered by MoniWiki
last modified 2010-10-28 12:42:52
Processing time 0.4835 sec