LuaRover 1.0.0 버전입니다.

25 views
Skip to first unread message

류광

unread,
Dec 19, 2007, 8:03:46 AM12/19/07
to luausers-kr
1.0.0 버전을 파일 란에 올렸습니다.

전역 변수 설정 버그를 잡았고, 예외 발생을 미리 피하기 위한 상태 조회 메서드들과 기본값 설정 메서드들을 추가했습니다.

(Readme.txt에서 발췌)
...

L은(그리고 LG["키"]는) 다양한 상태 조회 메서드들을 제공합니다. 예외를
발생하고 싶지 않은 경우 이 메서드들로 미리 점검을 한 후에 변수나 필드에
접근하면 됩니다.

isnil() : 대상이 nil이면 true를 반환
isnumber(): 대상이 수치형이면 true를 반환
isstring() : 대상이 문자열이면 true를 반환
istable() : 대상이 테이블이면 true를 반환

예:
LUA_ROVER_WITH_FILE("test.lua")
{
ASSERT_EQUALS(true, LG["not_exist"].isnil());
ASSERT_EQUALS(true, LG["window"]["not_exist"].isnil());
WITH_T("window")
{
ASSERT_EQUALS(true, L.istable());
ASSERT_EQUALS(false, L.isnil());
ASSERT_EQUALS(false, L.isnumber());
ASSERT_EQUALS(false, L.isstring());

ASSERT_EQUALS(true, L["controls"].istable());
WITH_T(".controls")
{
ASSERT_EQUALS(size_t(3), L.length());
ASSERT_EQUALS(true, L[1]["type"].isstring());
ASSERT_EQUALS(false, L[1]["type"].isnumber());

// 수치는 문자열로도 간주됨.
ASSERT_EQUALS(true, L[1]["font"]["size"].isnumber());
ASSERT_EQUALS(L[1]["font"]["size"].isstring(),
L[1]["font"]["size"].isnumber());
}
}
}

또한 L은(그리고 LG["키"]는) 기본값 설정을 위한 or_val() 메서드들을 제공합니다.
L["키"].or_val(기본값)은 만일 현재 테이블(또는 전역 범위)에 "키"라는
필드(또는 변수)가 존재하면(nil이 아니면) "키"의 값이되고 그렇지 않으면
"기본값"이 됩니다.

예:
LUA_ROVER_WITH_FILE("test.lua")
{
int default_value = LG["not_exist"].or_val(1234);
...
WITH_T(".controls")
{
string ff = L[1]["font"]["family"].or_val("Times New Roman");
...
}
}
-------------------
이하는 Readme.txt 전문입니다.

=================================================
C++을 위한 간단한 루아 접근 라이브러리 - LuaRover
=================================================

LuaRover는 C++에서 루아에 접근하는 것만을 염두에 둔 간단한 라이브러리입니다.

특징:
=====

* C++에서 루아에 접근하는 기능만 제공하며, 루아에서 C/C++에 접근하는
기능은 없습니다.
* 매크로와 { } 블록을 이용한 정형화된 표기법을 제공합니다. 이 표기법만
지키면 루아 스택이 깨지는 일은 (이론적으로) 생기지 않습니다.

최신 정보 및 피드백
===================

최신 버전의 공지는 한국 루아 사용자 그룹
(http://groups.google.com/group/luausers-kr)에 올리겠습니다.

의견이나 질문은 그 곳이나 http://occamsrazr.net의 자유 게시판에
올려주세요.


사용 권한:
==========

이 라이브러리의 사용 권한은 Lua와 동일한 Lua License를 따릅니다.
Lua License의 내용은 COPYRIHGT 파일에 있습니다. (본질적으로 MIT License와
동일합니다.)


저작권자는 Ryu, Gwang(류광, http://occamsrazr.net/)입니다.

사용법:
=======

현재는 라이브러리 형태가 아니라 헤더 파일 하나와 소스 파일 형태로
배포됩니다. 소스 파일(lua_rover.cpp)를 프로젝트에 포함시키고,
원하는 소스 파일에서 lua_rover.hpp)를 #include하면 사용 준비가 끝납니다.

Lua C API를 직접 사용할 것이 아닌 한 lua.hpp를 따로 #include할 필요는 없습니다.
그러나 lua_rover.hpp와 .cpp는 Lua의 헤더와 라이브러리를 사용하므로 컴파일러
또는 프로젝트에 Lua 라이브러리 관련 설정이 되어 있어야 합니다.

LuaRover를 사용하기 위한 수단들은 다음과 같습니다.

매크로 LUA_ROVER_WITH_FILE(filename)
------------------------------------
루아 환경(lua_State)를 생성하고 주어진 루아 파일을 불러들입니다. 이 시점에서
LG라는 변수가 생성됩니다.

반드시 { } 블록을 사용해야 합니다.

예:
LUA_ROVER_WITH_FILE("test.lua")
{
// 1...
}

매크로 LUA_ROVER_WITH_TABLE(table_spec)
---------------------------------------
LUA_ROVER_WITH_FILE 블록 안(위의 1) 에서 사용하며, table_spec으로 주어진
테이블에 접근할 수 있는 L이라는 변수를 생성합니다.

반드시 { } 블록을 사용해야 합니다.

예:
LUA_ROVER_WITH_FILE("test.lua")
{
LUA_ROVER_WITH_TABLE("t1")
{
int x = L["x"]; // test.lua의 t1.x의 값을 int 변수에 설정
}
}

LUA_ROVER_WITH_TABLE의 table_spec은 다음과 같은 형식을 지원합니다.

LUA_ROVER_WITH_TABLE("t1") : 전역 범위의 t1이라는 테이블을 L에 배정.
LUA_ROVER_WITH_TABLE("t1.f1") : 전역 범위 t1의 필드 f1을 L에 배정.
LUA_ROVER_WITH_TABLE("t1.f1.f2.f3") : 마침표를 이용해서 필드의
필드의 필드.... 를 임의로 지정할 수 있습니다.

LUA_ROVER_WITH_TABLE(".f1") : 첫 글자가 마침표이면 자신을 감싸고 있는
다른 LUA_ROVER_WITH_TABLE의 필드를 L에 배정합니다.

예: 1과 2는 동일한 의미입니다.

// 1
LUA_ROVER_WITH_TABLE("t1")
{
LUA_ROVER_WITH_TABLE(".f1")
{
int x = L["x"]; // t1.f1.x
}
}
// 2
LUA_ROVER_WITH_TABLE("t1.f1")
{
int x = L["x"]; // t1.f1.x
}

변수 LG
-------
LUA_ROVER_WITH_FILE에 의해 자동으로 생성되는 변수입니다. 루아 환경의
전역 범위에 접근하기 위한 수단이며, LUA_ROVER_WITH_TABLE 매크로도 내부적으로
이 변수를 사용합니다. 이 변수는 LUA_ROVER_WITH_FILE 안에서 유일하게 존재하며,
블록 안의 어느 곳에서도 사용할 수 있습니다.

사용 예:

LUA_ROVER_WITH_FILE("test.lua")
{
int gX = LG["x"]; // test.lua의 전역 변수 x의 값을 int 변수에 설정

LUA_ROVER_WITH_TABLE("t1.f1")
{
int x = L["x"]; // t1.f1.x
gY = LG["y"]; // 이 LG는 처음 나온 LG와 동일한 것임.
}
}

LG는 [] 연산자와 val<T>() 템플릿 함수를 지원합니다. 또한 배정 연산자와 값 변환
연산자들도 지원합니다.

[] 연산자는 전역 변수에 접근하기 위한 것입니다. 예를 들어 전역 변수 x에 접근하기
위해서는 LG["x"]라고 표기하면 됩니다. 구체적인 형식을 가진 변수에 LG["x"]를
배정하면 내부적으로 변환 연산자에 의해 해당 형식의 값이 추출됩니다.

예:
int x = LG["x"];

[] 연산자는 연결해서 사용할 수 있습니다. 다음은 루아의 t1.f1.f2.x에 접근하는
예입니다.

int x = LG["t1"]["f1"]["f2"]["x"];

val<T>()는 템플릿 함수 등 암묵적인 변환 연산자가 적용될 수 없는, 따라서 값의
형식을 명시해야 하는 상황을 위한 것입니다.

std::cout << LG["x"] << std::endl; // 오류

std::cout << LG["x"].val<int>() << std::endl;

현재 T로 가능한 형식은 int, double, std::string 입니다.

LG는 또한 배정 연산자도 지원합니다.

LG["x"] = 10;
LG["t1"]["f1"]["f2"]["x"] = 12.3;

변수 L
------
LUA_ROVER_WITH_TABLE에 의해 자동으로 생성되는 변수로, 해당 테이블의 필드
에 접근하기 위한 수단입니다. L은 각 LUA_ROVER_WITH_TABLE마다 새로 생성됩니다.

LUA_ROVER_WITH_FILE("test.lua")
{
int gX = LG["x"]; // test.lua의 전역 변수 x의 값을 int 변수에 설정
// int gX = L["x"]; // 에러 - 현재 범위에는 L이 없음
LUA_ROVER_WITH_TABLE("t1.f1")
{
int x = L["x"]; // t1.f1.x
gY = LG["y"]; // 이 LG는 처음 나온 LG와 동일한 것임.
}

LUA_ROVER_WITH_TABLE("t1")
{
int x = L["x"]; // t1.x 위의 L과는 다른 변수임.
gY = LG["y"]; // 이 LG는 처음 나온 LG와 동일한 것임.
}

}

L도 LG처럼 [] 연산자, val<T>() 함수, 배정 연산자, 변환 연산자들을 지원합니다.
작동 방식도 동일합니다. 단, L의 경우 기준은 전역 범위가 아니라 해당
LUA_ROVER_WITH_TABLE에서 적재한 테이블입니다. 위의 코드 예제에서 LG["x"]는
전역 변수 x를 지칭하지만, 첫 L["x"]는 t1.f2 의 필드 x를 지칭합니다.

L은 length() 멤버 함수를 제공합니다. length()는 루아의 # 연산자와 동일한
값을 돌려줍니다. 즉, 테이블의 경우에는 테이블의 최대 정수 색인을 돌려주며,
문자열의 경우에는 문자열 길이를 돌려줍니다.

예:

WITH_T("window.controls")
{
// 배열 루프 및 문자열 길이
for(int i = 1; i <= L.length(); i++)
{
cout << L[i]["label"].val<string>()
<< " : " << L[i]["label"].length()
<< "\n";
}
}

L은(그리고 LG["키"]는) 다양한 상태 조회 메서드들을 제공합니다. 예외를
발생하고 싶지 않은 경우 이 메서드들로 미리 점검을 한 후에 변수나 필드에
접근하면 됩니다.

isnil() : 대상이 nil이면 true를 반환
isnumber(): 대상이 수치형이면 true를 반환
isstring() : 대상이 문자열이면 true를 반환
istable() : 대상이 테이블이면 true를 반환

예:
LUA_ROVER_WITH_FILE("test.lua")
{
ASSERT_EQUALS(true, LG["not_exist"].isnil());
ASSERT_EQUALS(true, LG["window"]["not_exist"].isnil());
WITH_T("window")
{
ASSERT_EQUALS(true, L.istable());
ASSERT_EQUALS(false, L.isnil());
ASSERT_EQUALS(false, L.isnumber());
ASSERT_EQUALS(false, L.isstring());

ASSERT_EQUALS(true, L["controls"].istable());
WITH_T(".controls")
{
ASSERT_EQUALS(size_t(3), L.length());
ASSERT_EQUALS(true, L[1]["type"].isstring());
ASSERT_EQUALS(false, L[1]["type"].isnumber());

// 수치는 문자열로도 간주됨.
ASSERT_EQUALS(true, L[1]["font"]["size"].isnumber());
ASSERT_EQUALS(L[1]["font"]["size"].isstring(),
L[1]["font"]["size"].isnumber());
}
}
}

또한 L은(그리고 LG["키"]는) 기본값 설정을 위한 or_val() 메서드들을 제공합니다.
L["키"].or_val(기본값)은 만일 현재 테이블(또는 전역 범위)에 "키"라는
필드(또는 변수)가 존재하면(nil이 아니면) "키"의 값이되고 그렇지 않으면
"기본값"이 됩니다.

예:
LUA_ROVER_WITH_FILE("test.lua")
{
int default_value = LG["not_exist"].or_val(1234);
...
WITH_T(".controls")
{
string ff = L[1]["font"]["family"].or_val("Times New Roman");
...
}
}

예외 처리
---------
LuaRover는 잘못된 접근(예를 들면 존재하지 않는 전역 변수에 접근하거나,
테이블이 아닌 값에 대해 필드 접근을 시도하는 등)이 일어나면 LuaError 형식의
예외를 던집니다.

LuaError는 다음 두 메서드를 제공합니다.

const char* LuaError::what() - 간단한 오류 메시지 문자열을 돌려줍니다.
ErrorCode LuaError::code() - ErrorCode 열거형 오류 코드를 돌려줍니다.
ErrorCode 열거형은 lua_rover.hpp에 정의되어 있습니다.

예:

// 만일 test.lua에 non_exist라는 변수가 없으면
// "non_exist is Not Defined"가 출력됨
try
{
LUA_ROVER_WITH_FILE("test.lua")
{
int n = LG["non_exist"];
}
catch(LuaError e)
{
cout << e.what() << "\n";
}
}

C++ 예외를 사용하고 싶지 않다면 L의 is*() 메서드들과 or_val() 메서드를 이용해서
예외 발생을 미리 방지할 수 있습니다.



고급 사용 팁
============

매크로 이름 커스텀화
--------------------
LUA_ROVER_WITH_TABLE이나 LUA_ROVER_WITH_FILE의 타이핑이 번거롭다면 짧은
버전을 직접 정의해서 쓰면 됩니다. 예를 들면:

#define WITH_F LUA_ROVER_WITH_FILE
#define WITH_T LUA_ROVER_WITH_TABLE

WITH_F("test.lua")
{
WITH_T("t1.f1")
{
int x = L["x"]; // t1.f1.x
gY = LG["y"]; // 이 LG는 처음 나온 LG와 동일한 것임.
}
}

변수 LG, L 이름 바꾸기
----------------------
LG이나 L이라는 이름이 마음에 들지 않는다면, lua_rover.hpp를 #include하기
'전에' LUA_ROVER_GLOBAL_ID와 LUA_ROVER_LOCAL_ID 매크로로 원하는 이름을
정의하면 됩니다.

예:

#define LUA_ROVER_GLOBAL_ID g_
#define LUA_ROVER_LOCAL_ID t_

#include "lua_rover.hpp"

WITH_F("test.lua")
{
WITH_T("t1.f1")
{
int x = t_["x"]; // t1.f1.x
gY = g_["y"]; // 이 LG는 처음 나온 LG와 동일한 것임.
}
}

다음과 같은 형태의 커스텀 헤더를 만들어 두고 사용하면 편할 것입니다.
// my_lua_rover.hpp
#ifndef MY_LUA_ROVER_HPP
#define MY_LUA_ROVER_HPP

#define LUA_ROVER_GLOBAL_ID g_
#define LUA_ROVER_LOCAL_ID t_

#include "lua_rover.hpp"

#define WITH_F LUA_ROVER_WITH_FILE
#define WITH_T LUA_ROVER_WITH_TABLE

#endif // #ifndef MY_LUA_ROVER_HPP

커스텀 헤더 사용 예:
// test.cpp
#include "my_lua_rover.hpp"

void f()
{
WITH_F("test.lua")
{
WITH_T("t1.f1")
{
int x = t_["x"];
gY = g_["y"];
}
}
}

루아 상태의 공유
----------------
LUA_ROVER_WITH_FILE 매크로는 블록의 시작에서 새로운 루아 상태를 생성하고
파일을 적재하며, 블록의 끝에서 루아 상태를 파괴합니다. 이런 방식은 스택이
잘못 되는 일을 방지할 수 있다는 점에서 안전하지만, 경우에 따라서는 비효율적
일 수 있습니다.

LUA_ROVER_WITH_TABLE이 LG라는 이름에 의존한다는 점을 이용해서 루아 상태를
공유하는 것이 가능합니다. 이를 위해서는 LUA_ROVER_WITH_FILE로 루아 상태를
여는 대신, 다음과 예처럼 LuaGlobal 형식의 인스턴스를 직접 생성하고 참조로
넘겨주면 됩니다.

#include "lua_rover.hpp"

using namespace LuaRover;

void load_graphics_options(LuaGlobal&);
void load_sound_options(LuaGlobal&);

int main()
{
LuaGlobal LG("options.lua");
load_graphcis_options(LG);
load_sound_options(LG);
}

void load_graphics_options(LuaGlobal& LG) // "LG"라는 이름이 중요함
{
// LUA_ROVER_WITH_TABLE이 작동하기 위해서는
// 이 시점에서 LG라는 이름의 LuaGlobal 인스턴스가 존재해야 함
LUA_ROVER_WITH_TABLE("graphics")
{
Graphics::set_resolution(L["res"]["w"], L["res"]["h"]);
// .. 기타 등등 ..
}
}


void load_sound_options(LuaGlobal& LG) // "LG"라는 이름이 중요함
{
// LUA_ROVER_WITH_TABLE이 작동하기 위해서는
// 이 시점에서 LG라는 이름의 LuaGlobal 인스턴스가 존재해야 함
LUA_ROVER_WITH_TABLE("sound")
{
Sound::set_main_volume(L["volume"]);
// .. 기타 등등 ..
}
}

물론, LUA_ROVER_GLOBAL_ID 매크로를 이용해서 LG를 다른 이름으로 바꾸었다면
그 이름을 사용해야 합니다.

sykim

unread,
Dec 20, 2007, 12:09:45 AM12/20/07
to luausers-kr
책만 한번 보고는 따로 적용해 볼 시간을 내지 못하고 있었는데,
좋은 라이브러리 감사합니다.

lua_rover.hpp 파일의 LUA_ROVER_WITH_TABLE define 문에서
LG.get(name) 은 LUA_ROVER_GLOBAL_ID.get(name) 으로 해야 하지 않을까요??

그리고,
luavalue.hpp 파일은 내용이 아무것도 없던데요.

이상이구요.
감사히 쓰겠습니다.
> 의견이나 질문은 그 곳이나http://occamsrazr.net의자유 게시판에
> 올려주세요.
>
> 사용 권한:
> ==========
>
> 이 라이브러리의 사용 권한은 Lua와 동일한 Lua License를 따릅니다.
> Lua License의 내용은 COPYRIHGT 파일에 있습니다. (본질적으로 MIT License와
> 동일합니다.)
>
> 저작권자는 Ryu, Gwang(류광,http://occamsrazr.net/)입니다.

류광

unread,
Dec 20, 2007, 4:31:05 AM12/20/07
to luausers-kr
On 12월20일, 오후2시09분, sykim <syki...@gmail.com> wrote:
> 책만 한번 보고는 따로 적용해 볼 시간을 내지 못하고 있었는데,
> 좋은 라이브러리 감사합니다.
>
> lua_rover.hpp 파일의 LUA_ROVER_WITH_TABLE define 문에서
> LG.get(name) 은 LUA_ROVER_GLOBAL_ID.get(name) 으로 해야 하지 않을까요??
>

고맙습니다~ 지적하신 것이 맞습니다.

게다가 더 황당한 실수가 있었습니다. 현재의 구현은 응용 코드에서 using namespace LuaRover; 를 해준 경우에
만 컴파일됩니다... LUA_ROVER_WITH_* 매크로에서 LuaGlobal과 LuaObject에 LuaRover:: 를
붙이지 않았기 때문입니다.


> 그리고,
> luavalue.hpp 파일은 내용이 아무것도 없던데요.
>

함수 호출을 지원하려고 추가해 둔 것인데 잘못 포함되었습니다. (간단하게 뚝딱 만들어서 공개할 생각으로 시작한 일인데 버전 관리
나 패키징 자동화도 도입해야 할 것 같아요... 배보다 배꼽이 커질 것 같은 불길한 예감이...;;;; )

Readme.txt에도 이상하거나 현재 구현과 맞지 않는 부분들이 있을지 모르니 살펴봐 주세요~

버그들을 수정한 1.0.1 버전을 곧 올리겠습니다!

> 이상이구요.
> 감사히 쓰겠습니다.
>
Reply all
Reply to author
Forward
0 new messages