일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | ||||
4 | 5 | 6 | 7 | 8 | 9 | 10 |
11 | 12 | 13 | 14 | 15 | 16 | 17 |
18 | 19 | 20 | 21 | 22 | 23 | 24 |
25 | 26 | 27 | 28 | 29 | 30 | 31 |
- effective modern c++
- 상속
- operator new
- Effective c++
- 티스토리챌린지
- iterator
- 게임
- 영화 리뷰
- universal reference
- exception
- Smart Pointer
- more effective c++
- 예외
- virtual function
- std::async
- reference
- 보편 참조
- implicit conversion
- lua
- 영화
- resource management class
- 오블완
- 암시적 변환
- effective stl
- 참조자
- UE4
- 반복자
- c++
- 언리얼
- 스마트 포인터
- Today
- Total
스토리텔링 개발자
[Lua] 메타 테이블(Metatable) 본문
메타 테이블이란?
메타 테이블이 뜻하는 바는 무엇일까요?
이름만으로는 그다지 직관적이지 않네요.
뜻을 명확히 해보면 그 용도도 명확해지지 않을까요?
간단히 백과사전을 뒤져 보았습니다.
메타 테이블은 소스 코드 또는 메타 데이터 역할을 하는 데이터를 보유하도록 지정된 데이터베이스 또는 기타 데이터 보유 구조의 섹션입니다.
여전히 모호하네요.
하나씩 분할 정복해 보겠습니다.
'메타 데이터'는 뭘 의미할까요?
이번에도 찾아 보았습니다.
데이터에 관한 구조화된 데이터로, 다른 데이터를 설명해주는 데이터
데이터 구조를 정의하는 데이터라고 할 수 있겠네요.
설계도라고 비유하면 딱 맞지 않을까요?
xml의 스키마 처럼요.
요약해보면,
메타 테이블이란
데이터 구조를 정의하는 데이터(메타 데이터)들을 포함하는 데이터 테이블
을 뜻하는 것 같습니다.
메타 테이블은 메타 데이터들의 집합이구요.
루아의 메타 테이블
루아의 메타 테이블을 위의 정의의 맥락에서 생각해 봅시다.
루아의 메타 테이블은
'기본 행동(변수의 기본값, 함수의 기본 행위 등)을 정의해둔 테이블'
을 의미합니다.
예와 함께 보겠습니다.
tableA = {}
tableB = {}
tableB[a] = 10
setmetatable(tableA, tableB) -- tableB를 tableA의 메타 테이블로 설정합니다.
printA("결과 : "..tableA[a]) -- 결과 : 10
tableA는 빈 테이블이지만, 메타 테이블을 설정함으로 인해 tableB처럼 동작시킬 수 있습니다.
A의 설계도로 B가 설정된 것이라고 생각하면 편리하죠.
예제에는 할당된 값을 가져오는 간단한 기능만 포함되어 있습니다만,
당연히도 이외에 테이블이 할 수 있는 다양한 행동들을 재정의 할 수 있습니다.
재정의 가능한 함수들은 아래에서 계속 정리해 보겠습니다.
메타 테이블 기능 목록
메타 테이블은 테이블 기본값을 설정하는 것 이외에도 다양한 기능을 재정의할 수 있도록
아래 함수들을 제공합니다.
해당 이름의 함수들을 재정의하는 것으로 손쉽게 여러 기능을 원하는대로 구현할 수 있습니다.
재정의 가능한 기능들은 아래와 같습니다.
__add(+ 연산)
__sub(- 연산)
__mul(* 연산)
__div(/ 연산)
__mod(% 연산)
__pow(^ 연산)
__unm(-(부정) 연산)
ex) -t1
__idiv(//(몫) 연산)
__add 하위 연산들의 동작 순서
't1 + t2' 를 예로 들자면
1) t1의 메타테이블에 __add가 있는지 확인하고 있다면 호출
2) t1의 메타테이블에 __add가 없다면 t2의 메타테이블에 __add가 있는지 확인하고 있다면 호출
3) 둘 다 __add가 정의되어 있지 않다면 에러를 출력합니다.
의 순으로 진행됩니다.
__add의 매개변수로는 피연산자들(t1, t2)이 각각 넘어옵니다.
__band(and 연산)(Lua 5.3)
__bor(or 연산)(Lua 5.3)
__bxor(xor 연산)(Lua 5.3)
__bnot(xor 연산)(Lua 5.3)
__shl(<< 연산)(Lua 5.3)
__shr(>> 연산)(Lua 5.3)
__add 하위 연산들과 흡사하게 동작합니다.
다만, 피연산자 중 정수도 아니고 정수로 강제 변환도 불가능한 것이 하나라도 있어야 동작합니다.
__concat(..(접합) 연산)
__add 하위 연산들과 흡사하게 동작합니다.
다만, 피연산자 중 문자열도 아니고 문자열로 강제 변환이 불가능한 것이 하나라도 있어야 동작합니다.
__len(#(길이) 연산)
피연산자가 문자열이 아니어야 동작합니다.
메타메소드에 __len이 존재하지 않으면 테이블 길이 연산이 출력됩니다.
__eq(== 연산)
__add 하위 연산들과 흡사하게 동작합니다.
다만, 피연산자가 모두 테이블이거나 모두 userdata이고 단순 비교로 같지 않을때 동작합니다.
__lt(< 연산)
__le(<= 연산)
__add 하위 연산들과 흡사하게 동작합니다.
다만, 피연산자가 모두 수도, 문자열도 아닐때 동작합니다. 그리고 호출 결과는 항상 boolean으로 변환됩니다.
__index(table[key](인덱스 이용 접근) 연산)
피연산자가 테이블이 아니거나 테이블 내에 key가 없을 때 동작합니다.
이 이벤트에 할당할 수 있는 것은 [_함수, 테이블, 메타값 __index가 있는 임의 값_] 모두 포함됩니다.
함수의 경우 (table, key)가 매개변수로 넘어옵니다.
예를 들자면 아래와 같습니다.
t = {}
mt = {
__index = function(t, k)
return k.." 키로 호출됨"
end
}
setmetatable(t, mt)
print(t[1])
출력은 아래와 같습니다.
1 키로 호출됨
__newindex(table[key] = value(인덱스 할당) 연산)
피연산자가 테이블이 아니거나 테이블 내에 key가 없을 때 동작합니다.
이 이벤트에 할당할 수 있는 것은 [_함수, 테이블, 메타값 __index가 있는 임의 값_] 모두 포함됩니다.
함수의 경우(table, key, value)가 매개변수로 넘어옵니다.
유의해야 할 점은, 내부에서 일반적인 인덱스 할당을 시도하면 반복적으로 메타테이블을 검색해서 무한루프에 빠질 위험이 있습니다.
그를 회피하기 위해서는 rawset을 호출해서 메타 테이블 콜스택을 거치지 않고 할당해야 합니다.
t = {}
mt = {
__newindex = function(t, k, v)
-- t[k] = v
-- 내부적으로 위와 같은 식으로 처리하면 반복적으로 mt의 __newindex가 무한 호출된다.
-- 이를 회피하기 위해 아래와 같이 rawset() 함수를 사용해야 한다.
rawset(t, k, v)
print(k.." = "..v.." 할당 완료")
end
}
setmetatable(t, mt)
t[1] = 1
출력은 아래와 같습니다.
1 = 1 할당 완료
__call(table()(호출) 연산)
함수가 아닌 값을 함수처럼 호출하려 할 때 이 이벤트가 발생합니다.
매개변수로는 (table, 함수 호출 시의 매개변수들)이 매개변수로 넘어옵니다.
복수 개의 return을 허용하는 유일한 메타메소드입니다.
t = {}
mt = {
__call = function(t, arg)
print("함수 호출("..arg..")")
end
}
setmetatable(t, mt)
t("test")
출력은 아래와 같습니다.
함수 호출(test)
__gc(가비지 컬렉션 메타메소드)
가비지 컬렉션에서 수집될 때 소멸자로 이 함수가 호출됩니다.
해당 함수가 메타테이블에 있는 객체는 가비지 컬렉션에 즉시 수집되지 않고 특정 리스트에 따로 넣어둡니다.
__gc 메타메소드를 호출 후 다음 가비지 컬렉션 수집 주기에 객체 메모리가 해제됩니다.
리스트는 역순으로 호출되므로, 가장 나중에 __gc 표시를 한 객체부터 함수가 호출됩니다.
__close(자동 닫힘 메타메소드)
변수가 유효 범위를 벗어나는 경우(블록 종결, break/goto/return을 통한 블록 종결, 오류로 인한 블록 종결) 이 함수가 호출됩니다.
인자로는 (본인의 값, 오류로 종료 시의 오류 객체(없으면 nil)) 이 전달됩니다.
코루틴 내부에서 yield 후 다시 재개하지 않으면 내부의 일부 변수들은 절대 닫히지 않을 수 있으므로 유의해야 합니다.
__mode(약한 참조 방식 제어)
키가 약한 테이블은 "k", 값이 약한 테이블은 "v", 둘 모두가 약한 테이블 "kv"를 할당합니다.
참조)
이페머론(ephemeron) 테이블
- 키가 약하고 값이 강한 테이블
- 키가 도달 가능한 경우에만 그 값이 도달 가능하다고 봅니다.
- 어느 키에 대한 유일한 참조가 그 값을 통해서라면, 값이 강한 참조더라도 키/값 페어를 제거합니다.
__tostring, __name(tostring 및 오류 메시지에서 출력)
메타테이블에 __tostring 필드가 있으면 tostring(객체)의 결과값으로 __tostring이 출력됩니다.
만약 __tostring 필드가 없고 문자열 값을 가진 __name 필드가 있다면 그 문자열을 출력합니다.
'개발 > Lua' 카테고리의 다른 글
[Lua] __newindex 로 할당 로직 재정의 (0) | 2021.08.06 |
---|---|
[Lua] 상속 구조 만들기 (0) | 2021.07.27 |