루아 모듈의 이름 충돌 방지(2/n)

Twitter icon류광, 2016-04-29 23:04
이번 글에서는 루아 C API를 이용한 루아 C/C++ 확장 모듈의 이름 충돌 방지를 살펴봅니다.

이전 글에서는 순수 루아 모듈의 이름 충돌 방지를 이야기했습니다. 이번 글에서는 루아 C API를 이용한 루아 C/C++ 확장 모듈의 이름 충돌 방지를 살펴봅니다.

C/C++ 확장 모듈의 이름 충돌 문제도 순수 루아 모듈의 것과 본질적으로 동일합니다. 결국은 모듈이 전역 변수(함수도 그냥 함수 형식의 값을 가진 변수임을 기억하세요)를 만드느냐, 다른 말로 하면 전역 테이블을 건드리느냐의 문제입니다.

이전 글의 처음에 등장하는 “원시적인” 함수 라이브러리의 모듈에 해당하는 C/C++ 확장 모듈은 루아 '접착제' 함수들 각각을 lua_register(또는 ua_pushcfunctionlua_setglobal의 조합)을 이용해서 직접 전역 테이블에 추가하는 형태일 것입니다. 이전 글에서도 말했듯이 이런 접근방식으로는 이름 충돌을 피할 수 없습니다.

개별 함수들의 이름 충돌을 피하기 위해서는 함수들을 하나의 테이블로 묶을 필요가 있는데, 그런 경우를을 위해 루아 5.0과 5.1은 luaL_register라는 보조 함수를 제공합니다. 아마 5.0과 5.1용 C 확장 모듈은 대부분 이 함수를 사용할 것입니다.

void luaL_register (lua_State *L,
                    const char *libname,
                    const luaL_Reg *l);

ibname 매개변수에 이름(C 문자열)을 지정해서 이 함수를 호출하면 그 이름으로 전역 변수가 만들어집니다. 예를 들어 PIL 제1판(루아 5.0 대응)과 제2판(루아 5.1 대응) §28.1에는 다음과 같은 예제가 있습니다.

static const struct luaL_Reg arraylib [] = {
  {"new", newarray},
  {"set", setarray},
  {"get", getarray},
  {"size", getsize},
  {NULL, NULL}
};

int luaopen_array (lua_State *L) {
  luaL_register(L, "array", arraylib);
  return 1;
}

이 코드는 new, set, get, size라는 필드(값은 C 함수)를 가진 테이블을 생성해서 array라는 이름의 전역 변수에 배정합니다. 결과적으로, 전역 이름공간 오염의 관점에서는 이전 글의 “require"mymp3lib"” 예제와 같습니다.

다행히 luaL_register의 libname 매개변수에 널(0)을 지정할 수 있습니다. 그러면 luaL_register는 그냥 테이블에 생성하기만 하고 전역 변수에는 배정하지 않습니다. 그 테이블을 그대로 돌려주기만 하면 이전 글의 “local my_movie_lib = require"movie_lib"” 패턴과 같은 효과가 납니다. 지금 예의 경우 luaopen_array를 다음처럼 바꾸면 됩니다.

int luaopen_array (lua_State *L) {
  luaL_register(L, 0, arraylib); // "array" 대신 널 포인터
  return 1;
}

(현대적인 C++에서는 0 대신 nullptr를 사용해야겠죠.)

루아 5.2에서는 luaL_register가 사라지고 luaL_newlib가 생겼습니다. 다음에서 보듯이 모듈 이름을 지정하는 매개변수가 아예 없습니다.

void luaL_newlib (lua_State *L, const luaL_Reg *l);

따라서 일부러 luaL_register를 만들어서 쓰지 않는 한, 5.2부터는 전역 이름공간을 오염시키지 않는 모듈 패턴이 '저절로' 적용됩니다.

이것으로 테이블 안에 담긴 함수 라이브러리 형태의 확장 모듈의 이름공간 오염 문제는 확실히 해결됩니다. 그러나 루아의 이름 충돌 문제가 모두 해결된 것은 아닙니다. 이전 글의 “local video_engine = require"my_video_engine"” 예제처럼 마치 클래스 비슷하게 사용하는 모듈을 C API로 구현하는 경우에는 겉으로 드러나는 전역 이름공간 말고도 오염될 수 있는 이름공간이 더 있기 때문입니다. 이에 대해서는 다음 글에서 살펴보겠습니다.

태그: 프로그래밍 Lua

comments powered by Disqus