\documentclass{howto}

\usepackage[hangul,nonfrench]{dhucs}
\usepackage{dhucs-ucshyper}
\hypersetup{%
  colorlinks=true,
  plainpages=false,
}

\title{확장 모듈 만들기}
\author{정 원교}
\authoraddress{
  \strong{지구 남쪽에서}\\
  이메일 : \email{weongyo@gmail.org}
}
\date{2006.2.25}

\begin{document}
\maketitle

\begin{abstract}
  \noindent
  이 문서는 ROVM Server 에서 작동할 수 있는 확장 모듈 (Extension
  Module) 을 작성하는 방법에 대해서 자세히 기술한 문서입니다.
\end{abstract}

\tableofcontents

\section{소개 - 확장 모듈 또한 클래스이다.}
\label{sec:introduction}

\emph{아래 문서는 완전히 완료된 것이 아니며, 여전히 공사중인 문서입니다.  이
문서에 정의되어 있는 함수 및 구조체의 prototype 은 언제든지 수정될 수
있습니다.  수정 사항에 대한 공지사항은 별도로 알려드립니다.}

ROVM Server 에서 제공되는 확장 모듈 기능의 개념은 많은 부분에서 python
에 영향을 받아 제작되었다고 할 수 있습니다.  자세한 구현 방식은 Python
소스를 직접 분석하지 않았기 때문에 정확하게 모르나, `확장 모듈'을 만드는
방식에 있어서는 약간 비슷할 수 있습니다.

확장 모듈 기능은 아래와 같은 생각을 바탕으로 제작되었습니다.
\begin{itemize}
\item \textbf{확장 모듈 또한 클래스이다.}  아직 클래스라고 불리기에는
  부족한 점이 많지만, 확장 모듈을 만들 때, 클래스를 정의하는 것 처럼
  프로그램을 짤수 있도록 하였습니다.  즉 다른 언어의 확장 모듈이 그런
  것 처럼 Method 의 선언, Field 의 선언 등등 거의 모든 요소들을 선언할
  수 있도록 구성할 생각입니다.

  ``ENVLANG File Format'' 을 읽어 내부적으로 Class 구조체를 만드는
  방식과 `확장 모듈'을 읽어 내부적으로 Class 구조체를 만드는 방식은
  거의 비슷합니다.
\end{itemize}

\section{준비 사항과 컴파일}
\label{sec:prepare}

ROVM 을 위한 확장 모듈을 만들기 위해서는 `확장 모듈 라이브러리'가
필요합니다.  이 `확장 모듈 라이브러리'는 ROVM Server 패키지를 설치할
경우 자동으로 설치가 되게 됩니다.

ROVM Server 의 설치 경로가 \texttt{/usr/local/rovm} 이라고 가정을
한다면 확장 모듈을 작성하기 위해서는 아래와 같은 파일의 존재 여부를
먼저 확인해야 합니다.  아래의 두 파일은 반드시 존재해야 합니다.

\begin{itemize}
\item \texttt{/usr/local/rovm/include/el.h} \\
  `확장 모듈 라이브러리'에서 지원하는 함수 및 구조체에 대한 prototype
  을 가지고 있는 헤더입니다.  모든 확장 모듈에는 이 헤더 파일을
  include 해야 합니다.
\item \texttt{/usr/local/rovm/lib/libel.so} \\
  `확장 모듈 라이브러리'의 구성 요소 중 실제 함수들이 포함되어 있는
  공유 라이브러리입니다.  Linking 과정에서 이 공유 라이브러리에 대한
  링크가 필요합니다.
\end{itemize}

실제 Makefile.am 파일을 예제로 든다면 아래와 같이 간단하게 작성할 수
있습니다.  아래는 str.so 확장 모듈을 작성하는 구문입니다.

\begin{verbatim}
AM_CFLAGS = -Wall -I/usr/local/rovm/include

core_LTLIBRARIES = str.la
coredir = $(prefix)/core

str_la_SOURCES = str.c
str_la_LDFLAGS = -module -L/usr/local/rovm/lib -lel
\end{verbatim}

\section{시작하기 - entry point 정의하기}
\label{sec:start}

ROVM Server 가 확장 모듈을 제대로 로드하고 읽기 위해서는 모듈의 진입
부분이 필요합니다.  이를 entry point 라고 하는데, 아래같이 정의를
하셔야 합니다.

\begin{verbatim}
int
init_<modulename> (RvClass *cls) 
{ 
  ... 
}
\end{verbatim}

위에서 \texttt{<modulename>} 는 실제 클래스 이름이랑 반드시 같아야 합니다.
예를 들어 현재 클래스 이름을 pcre 로 정의한다고 가정을 하면 파일
이름은 pcre.so (Unix 기준으로) 을 entry point 함수 이름은 \texttt{init_pcre} 로
반드시 해야 합니다.

인자로 넘어오게 되는 CLS 는 클래스 구조체에 대한 포인터이며, 오직
이름만 설정되어 있는 상태입니다.  이후로 CLS 변수를 사용할 경우,
구조체 구성요소에 직접 접근을 하지 않으며, 모두 지원 함수를
실행함으로써 이루어지게 됩니다.

이제 \texttt{init_<modulename>} 내에 클래스의 내용을 채워 넣음으로써
확장 모듈의 기능을 더할 수 있습니다.

이 함수의 return 값은 로드 실패시 -1 을, 성공시 0 을 반환해야 합니다.

\subsection{메모리 관리}
\label{sec:handlememory}

이 섹션에서는 확장 모듈을 작성할 때 알아두면 용의한 메모리 관리에
대해서 살펴보도록 하겠습니다.

`확장 모듈 라이브러리'에서 사용되는 함수들의 경우, 대부분의 경우 GC
메모리를 이용하여 메모리 관리가 이루어지기 때문에 해당 함수들을
이용함으로써 발생할 수 있는 메모리 릭은 신경을 쓰지 않으셔도 됩니다.

하지만 개발자께서 직접 malloc 와 같은 함수를 이용하여 메모리 할당을
하였다면 그것은 반드시 해제를 해주셔야 메모리 문제가 발생하지
않습니다.

Garbage Collector 때문에 Reference Count 를 계산해 줘야 하는 불편함이
없는 것이 현재 ROVM Server 확장 모듈의 특징입니다.

\section{클래스 채워넣기}
\label{sec:fillclass}

\subsection{Field 정의하기}
\label{sec:definefield}

클래스 내부에 Field 을 정의하기 위해서는 미리 정의해 놓은 함수
RvDefineFields () 를 사용해야 합니다.

\begin{verbatim}
int RvDefineFields (RvClass *cls, RvFieldDef *fds);
\end{verbatim}

이 함수를 \texttt{init_<modulename>} () 함수 내부에서 사용함으로써
field 들을 정의할 수 있는데, 첫번째 인자는 \texttt{init_<modulename>}
함수에 건내진 cls 인자이며, 두번째 인자는 Field 에 대해 정의해 놓은
구조체 포인터입니다.

\begin{verbatim}
typedef struct efielddef RvFieldDef;

struct efielddef
{
  const char *name;
  const char *type;
};
\end{verbatim}

현재 RvFieldDef 구조체는 위와 같이 선언되어 있습니다.  위에서 type 의
경우, ``ENVLANG File Format''에 정의되어 있는 field type
형식을 그대로 따라야 합니다.

예를 들어 설명한다면, 아래와 같이 구조체를 정의할 수 있습니다.

\begin{verbatim}
static RvFieldDef field[] =
  {
    { "value",  "[" },
    { "len",    "I" },
    { NULL, NULL },
  };
\end{verbatim}

\subsection{Method 정의하기}
\label{sec:definemethod}

위 \texttt{init_<modulename>} 의 인자로 클래스 구조체가 들어 왔기 때문에,
클래스 내부에 메쏘드를 정의하기 위해서는 RvDefineMethods () 함수를
사용해야 합니다.

\begin{verbatim}
int RvDefineMethods (RvClass *cls, RvMethodDef *mds);
\end{verbatim}

이 함수는 ROVM Server 측에서 기본적으로 제공해 주는 함수입니다.
RvMethodDef 구조체에 대한 모습은 아래와 같습니다.

\begin{verbatim}
typedef struct emethoddef RvMethodDef;

struct emethoddef
{
  const char *name;
  const char *type;

  int (*func) (RvObject *cls, RvValue *arg, RvValue *ret);
};
\end{verbatim}

위의 구조체를 보면 알겠지만, `확장 모듈'에서 정의하는 함수들은 항상 

\begin{verbatim}
int 
<funcname> (RvObject *cls, RvValue *arg, RvValue *ret)
{
  ...
}
\end{verbatim}

와 같은 형태를 가집니다.  이렇게 하는 이유는 ROVM Server 상에서의
구현을 단순하게 하며, 이런 방식이 아닌 다른 방식을 사용할 경우, 각 OS
마다 다른 함수 호출 방식을 assembly 수준에서 모두 작성해 주어야 하기
때문입니다.

Method 의 이름과 type 은 항상 UTF-8 encoding 방식이어야 하며,
특히 type 의 경우, ``ENVLANG File Format''에 정의되어 있는 method type
형식을 그대로 따라야 합니다.  이렇게 하는 이유는 저희는 확장 모듈을
만들고 있으며, 그 확장 모듈은 내부적으로 ROVM Server 에서 클래스를
만드는 일과 동일하기 때문입니다.

확장 모듈에 정의되는 함수의 첫번째 인자 CLS 는 `현재 처리되는 객체'에
대한 정보를 가지고 있습니다.

두번째 인자 ARG 는 이 메쏘드의 Type 에
따라 그에 맞는 값이 설정되어 있는 값 공간을 의미합니다. 함수의 각 인자에 접근한다거나 반환값을
설정에 대해서는 \ref{sec:handleargument} 절에 자세히 나와 있으니 그것을
참조하십시오.

세번째 인자 RET 는 이 메쏘드가 반환하게 되는 값을 설정하는 곳입니다. 

마지막으로 실제 <funcname> 가 반환하게 되는 `int' 형의 경우, 함수 처리
중 오류가 발생하였을 경우 -1 을 반환하시면 됩니다.  정상일 경우 0 을
반환하시면 됩니다.  만약 -1 이 반환하게 되면, 해당 request 에 대한
처리가 즉시 종료되며, 그 동안 쌓인 로그 메세지들을 `ERROR' 예약어에
실어 client 측에 보내게 됩니다.

로그 남기는 부분의 경우, RvLogError () 함수가 사용되는데, 이에 대한
자세한 설명은 \ref{sec:uselogging} 절에 자세히 설명되어 있습니다.

\section{필드 다루기}
\label{sec:controlfield}

\subsection{필드값의 설정 및 획득}
\label{sec:setupoffiled}

필드값을 설정하기 위해서 제공하는 함수들은 아래와 같은 것이 있습니다.

\begin{verbatim}
int RvSetField (RvObject *self, const char *name, const char *type, ...);
int RvGetField (RvObject *self, const char *name, const char *type, ...);
RvValue *RvGetFieldValue (RvObject *self, const char *name, const char *type);
\end{verbatim}

각 함수에 대한 설명을 한다면

\begin{itemize}
\item \texttt{RvSetField} \\
  필드의 이름이 NAME 이고, 타입이 TYPE 인 필드를 찾아 그에 대한 값을
  ... 에 온 인자로 설정하게 됩니다. Type 에 대해서는
  \ref{sec:controlreturn} 에 나와 있는 표를 기반하여 작성하셔야 합니다.
\item \texttt{RvGetField} \\
  필드의 이름이 NAME 이고, 타입이 TYPE 인 필드를 찾아 해당 값을
  가져오는 함수입니다.  `...' 에는 값이 들어갈 공간에 대한 포인터를
  넘겨주셔야 합니다.
\item \texttt{RvGetFieldValue} \\
  위의 \texttt{RvGetField} 함수와 비슷한 역활을 하지만, 이 함수는
  필드에 설정된 실제 값을 가져오는 것이 아니라, 실제 값이 설정되어
  있는 RvValue 구조체에 대한 포인터를 반환하게 됩니다.  이 함수의
  경우, ArrayRef 혹은 StringRef 를 다룰 때 사용됩니다.
\end{itemize}

\subsection{문자열 (String)}
\label{sec:fieldstring}

ROVM 에서 다루는 문자열 (정확하게 이야기한다면 StringRef) 을 쉽게
추출하기 위해서 제공하는 함수들입니다.

\begin{verbatim}
RvObject *RvStrNew (RvObject *self, char *str, size_t len);
RvObject *RvStrSubstr (RvObject *self, char *str, int offset, size_t len);
int RvStrGetInfo (RvObject *str, char **ptr, int *len);
char *RvStrGetPointer (RvObject *str);
int RvStrSize (RvObject *str);
\end{verbatim}

각 함수에 대한 설명을 한다면 아래와 같습니다.

\begin{itemize}
\item \texttt{RvStrNew} \\
  길이가 LEN 인 문자열 STR 을 새롭게 생성하여 그에 대한 StringRef 를
  반환하게 됩니다.
\item \texttt{RvStrSubstr} \\
  문자열 STR 에서 시작점을 OFFSET 으로 해서 길이 LEN 만큼의 문자열을
  잘라내어, 이를 StringRef 로 변환하여 반환하도록 하는 함수입니다.
  이에 대한 사용 예제를 참조한다면 \texttt{/core/str} 클래스의 \texttt{split (T)[}
  메쏘드의 소스를 참조하면 많은 도움이 될 것입니다.
\item \texttt{RvStrGetInfo} \\
  StringRef 에서 문자열과 길이를 추출하여 각각 PTR 과 LEN 에 기록하는
  역활을 하는 함수입니다.  성공시 0 을, 실패시 -1 을 반환합니다.
\item \texttt{RvStrGetPointer} \\
  문자열 객체 STR 에서 실제 값이 저장되어 있는 공간인 value 의 실제 포인터
  값을 반환합니다.
\item \texttt{RvStrSize} \\
  문자열 객체 STR 의 길이를 계산하여 반환합니다.
\end{itemize}

\subsection{배열 (Array)}
\label{sec:fieldarray}

현재 `확장 모듈 라이브러리'에서는 배열의 생성 및 삽입과 관련하여
아래와 같은 함수들을 제공하고 있습니다.

\begin{verbatim}
RvObject *RvArrayNew (RvObject *self);
int RvArrayPush (RvObject *array, const char *type, ...);
int RvArrayPop (RvObject *array, RvValue **poped_value);
int RvArrayLength (RvObject *array);
RvValue *RvArrayLastValue (RvObject *array);
RvValue *RvArrayGetItem (RvObject *array, size_t idx);
int RvArraySetItem (RvObject *array, size_t idx, RvValue *val);
int RvArrayResize (RvObject *array, int total);
int RvArrayInsert (RvObject *array, int where, const char *fmt, ...);
int RvArraySliceAdvanced (RvObject *array, int lo, int hi, RvObject *v);
int RvArrayExtend (RvObject *self, RvObject *b);
int RvArrayReverse (RvObject *array);
int RvArrayPopWithIndex (RvObject *array, int i, RvValue **poped_value);
RvValue *RvArrayGetEntry (RvObject *array);
\end{verbatim}

각 함수에 대해서 설명을 하면
\begin{itemize}
\item \texttt{RvArrayNew} \\
  새로운 배열을 생성하는 함수로써, 첫번째 인자에는 메쏘드의 첫번째
  인자 self 를 주면 됩니다.  이 함수의 반환값으로는 새롭게 생성된
  ArrayRef 가 반환되게 됩니다.
\item \texttt{RvArrayPush} \\
  새로운 항목을 배열에 PUSH 할 때 사용되는 함수입니다.  첫번째
  인자로는 생성된 배열 ArrayRef 포인터를 주면 되고, 두번째 인자로는
  현재 삽입하고자 하는 값의 Type 을 주면 됩니다.  이 Type 에 대해서는
  \ref{sec:controlreturn} 에 나와 있는 표를 기반으로 기입하시면
  됩니다.

  만약 실행중 오류가 발생하였다면 반환값으로 -1 을 반환하게 되며,
  성공하였다면 0 을 반환하게 됩니다.
\item \texttt{RvArrayPop} \\
  배열 ARRAY 에서 가장 최상위 (맨뒤) 에 존재하는 항목을 POP 합니다.
  그 때 POP 된 항목이 두번째 인자 POPED_VALUE 에 설정되게 됩니다.
\item \texttt{RvArrayLength} \\
  배열 ARRAY 의 길이를 계산하여 그에 대한 결과값을 반환합니다.
\item \texttt{RvArrayLastValue} \\
  배열 ARRAY 에서 가장 최상위 (맨뒤) 에 존재하는 값을 계산하여 그에
  대한 RvValue 구조체 포인터 (값을 저장하는 공간) 를 반환합니다.
\item \texttt{RvArrayGetItem} \\
  배열 객체 ARRAY 의 index IDX 에 존재하는 값 구조체를 반환합니다.
\item \texttt{RvArraySetItem} \\
  배열 객체 ARRAY 의 index IDX 에 값 VAL 를 설정하는 함수입니다.
\item \texttt{RvArrayResize} \\
  배열 객체 ARRAY 를 길이 TOTAL 로 크기를 재조정합니다.  이에 대한 결과값으로
  배열 객체의 필드 LEN 값이 변경되게 됩니다.
\item \texttt{RvArrayInsert} \\
  배열 객체 ARRAY 의 index WHERE 에 값 FMT 을 넣게 됩니다.  FMT 을 바로
  넣는 것이 아니라 ... 에 맞게 해석한 뒤 알맞게 들어가게 됩니다.
\item \texttt{RvArraySliceAdvanced} \\
  배열 객체 ARRAY 에서 LO 와 HI 값을 처리할 때 사용됩니다.  만약 V 가 NULL
  일 경우는 del ARRAY[LO:HI] 와 같은 의미가 되며, 그렇지 않을 경우 
  ARRAY[LO:HI] = v 의 의미가 됩니다.
\item \texttt{RvArrayExtend} \\
  Array 관련 함수로써 SELF 와 B 는 모두 ArrayRef 객체입니다.  이 함수는
  B 에 들어 있는 항목들을 모두 SELF 에 추가하는 일을 합니다.
\item \texttt{RvArrayReverse} \\
  배열 객체 ARRAY 의 순서를 반대로 만듭니다.
\item \texttt{RvArrayPopWithIndex} \\
  배열 객체 ARRAY 에서 인덱스 I 의 값을 POP 하고 그 POP 된 값을 POPED_VALUE
  에 설정하게 됩니다.  만약 배열의 중간 부분이 POP 될 경우, 자동으로 그 공간이
  줄어들게 됩니다.
\item \texttt{RvArrayGetEntry} \\
  배열 객체 ARRAY 에서 실제 값이 저장되는 공간인 필드 VALUE 의 주소 포인터를
  반환하게 됩니다.
  
  배열 객체와 문자열 객체의 필드 VALUE 는 둘다 모두 특별하게 취급해줘야하는
  부분이 존재합니다.
\end{itemize}

\subsection{RvValue (값을 담는 공간) 직접 다루기}
\label{sec:fieldrvvalue}

RvValue 구조체는 값을 직접 담는 공간입니다.  이를 직접 제어할 수 있는
함수를 제공하고 있습니다.

\begin{verbatim}
int RvValueIsStringRef (RvValue *value);
RvObject *RvValueStringRef (RvValue *value);
char *RvValue2Str (RvObject *self, RvValue *val);
RvObject *RvValueArrayRef (RvValue *value);
int RvValueRichCompare (RvValue *o1, RvValue *o2);
RvValue *RvValueCopy (RvObject *self, RvValue *value);
RvValue *RvValueEntryItem (RvValue *entry, int idx);
\end{verbatim}

위에서 정의된 각 함수의 prototype 에 대한 설명입니다.

\begin{itemize}
\item \texttt{RvValueIsStringRef} \\
  VALUE 가 StringRef 를 담고 있는 공간일 경우 True 을 반환합니다.
  그렇지 않다면 False 를 반환합니다.
\item \texttt{RvValueStringRef} \\
  VALUE 가 StringRef 를 담고 있는 공간일 경우, 해당 StringRef 포인터를
  반환합니다.
\item \texttt{RvValue2Str} \\
  VALUE 를 직접적인 문자열로 변환할 때 사용되는 함수입니다.  이것은
  StringRef 의 실제 문자열을 다룰 때 사용되는데, str 클래스의 변수
  value 는 특별히 취급해줘야 하기 때문에, 이 함수가 존재합니다.  만약
  StringRef 를 위해 사용할 경우, SELF 는 반드시 StringRef 여야
  하며 VAL 는 해당 StringRef 의 변수 value 값 RvValue 구조체여야 
  합니다.
\item \texttt{RvValueArrayRef} \\
  값 VALUE 가 ArrayRef 일 경우, 값에 저장되어 있는 ArrayRef 포인터를 
  반환합니다.
\item \texttt{RvValueRichCompare} \\
  값을 담는 공간인 O1 과 O2 를 비교하여 만약 같다면 1 을 반환하고 그렇지
  않다면 0 을 반환합니다.  만약 중간에 오류가 발생하였다면 -1 을 반환하게
  됩니다.  만약 배열 비교일 경우, 내부 항목들을 재귀 호출로써 비교를 하게
  되며, 문자열 객체일 경우, 문자열의 길이 및 내용을 비교하게 됩니다. 
  만약 ObjectRef 일 경우는 주소값을 기준으로 비교를 하게 됩니다.  그 외의
  값은 실제 해당 값을 비교함으로써 이루어지게 됩니다.
\item \texttt{RvValueCopy} \\
  값 VALUE 의 복사본을 만들어 그 값을 반환하게 됩니다.  이 함수가 존재하는 이유는
  확장 모듈 작성시 GC 메모리 포인터를 스택상에 보관하고 있을 그 순간에 GC 가 
  실행될 경우, 해당 객체가 증발해 버리는 사고를 방지하기 위해서 존재합니다.
  
  아래의 RvMemMalloc () 에서 할당되는 메모리는 Delayed Stack Slot 에 자동으로
  등록되게 됩니다.
\item \texttt{RvValueEntryItem} \\
  값 ENTRY 가 RvValue 구조체로 이루어진 배열이라고 가정을 하고, 해당 IDX 에
  맞는 포인터값을 반환하게 됩니다.
\end{itemize}

\section{메쏘드 다루기}
\label{sec:controlmethod}

\subsection{Argument 다루기}
\label{sec:handleargument}

메쏘드의 argument 를 `확장 모듈'에서 사용하기 위해서는 해당 언어에
맞게 값을 설정해줘야 하는데, 그에 대한 일을 기본으로 제공되는
RvParseArg () 함수를 통해서 이루어지게 됩니다.

\begin{verbatim}
int RvParseArg (RvObject *self, const char *fmt, ...);
\end{verbatim}

현재 지원되고 있는 FMT (format) 의 경우 아래와 같습니다.

\begin{itemize}
\item `\texttt{[}' \\
  해당 값에 설정되어 있는 ArrayRef 를 가져올 때 사용됩니다.
  \texttt{RvObject **} 를 넘겨야 합니다.
\item `\texttt{b}' \\
  Boolean 값을 가져올 때 사용되며, \texttt{unsigned char *} 를 넘겨야
  합니다.
\item `\texttt{c}' \\
  Char 값을 가져올 때 사용되며, \texttt{char *} 를 넘겨야 합니다.
\item `\texttt{d}' \\
  Double 값을 가져올 때 사용되며, \texttt{double *} 를 넘겨야 합니다.
\item `\texttt{f}' \\
  Float 값을 가져올 때 사용되며, \texttt{float *} 를 넘겨야 합니다.
\item `\texttt{h}' \\
  Short 값을 가져올 때 사용되며, \texttt{short *} 를 넘겨야 합니다.
\item `\texttt{i}' \\
  정수형을 가르키며, int 형태의 값을 가져올 때 사용됩니다.  \texttt{int *} 를
  넘겨야 합니다.
\item `\texttt{s}' \\
  문자열을 가르키며, \texttt{char *} 형태의 값을 가져올 때
  사용됩니다.  \texttt{char **} 를 넘겨야 합니다.
\item `\texttt{s\#}' \\
  `\texttt{s}' 와 마찬가지로 문자열을 가르키지만, 길이값도 같이 가져올 때
  사용됩니다.  \texttt{char **} 와 \texttt{int *} 를 넘겨야 합니다.
\item `\texttt{S}' \\
  StringRef 를 가져올 때 사용됩니다.  \texttt{RvObject **} 를 넘겨야 합니다.
\item `\texttt{t}' \\
  ObjectRef 를 가져올 때 사용됩니다.  \texttt{RvObject **} 를 넘겨야 합니다.
\item `\texttt{x}' \\
  해당 값을 담은 공간에 대한 포인터를 원할 경우 이것을 사용할 수
  있습니다.  즉 RvValue 포인터를 구하게 되는 것입니다.  \texttt{RvValue **} 를
  넘겨야 합니다.
\end{itemize}

예를 들어 아래와 같이 사용할 수 있습니다.

\begin{verbatim}
char *pat;
int patlen, lim;

if (RvParseArg (self, args, "s#i", &pat, &patlen, &lim))
  return -1;
\end{verbatim}

\subsection{반환값 제어하기}
\label{sec:controlreturn}

확장 모듈에 정의되는 모든 메쏘드는 항상 return 값에 대한 포인터를
가지고 있는데, 이 값을 설정함으로써 반환값을 제어할 수 있습니다.

이를 쉽게 설정하기 위해서 확장 모듈 라이브러리에는 반환값을 설정하는
함수를 제공하며, 그에 대한 prototype 모형은 아래와 같습니다.

\begin{verbatim}
int RvSetReturn (RvObject *self, RvValue *ret, const char *type, ...);
int RvSetReturnValue (RvValue *ret, RvValue *value);
\end{verbatim}

각 함수에 대한 설명이 아래에 있습니다.

\begin{itemize}
\item RvSetReturn \\
  이 값의 SELF 는 메쏘드의 첫번째 인자를 가르키며, RET 는 세번째 인자를
  가르킵니다.  TYPE 은 이 메쏘드의 타입중 return 값과 관련된 문자열을
  넣으면 됩니다.
  
  예를 들어, 메쏘드의 타입이 \texttt{(TSI)[} 였다고 했을 때, TYPE 에 넣을 문자는
  ``\texttt{[}'' 입니다.  해당 return 타입에 따라, 인자를 뒤에 붙여
  넣으면 됩니다.
  
  각 return 타입에 따라 알맞은 `...' 항목의 C 언어 type 에 대한 표가
  아래에 있습니다.
\item RvSetReturnValue \\
  이 함수는 두번째 인자인 VALUE 를 그대로 RET 에 복사하고자 할 때
  사용할 수 있는 함수입니다.
\end{itemize}

\begin{tabular}{|c|l|}
  \hline
  \textbf{타입}          & \textbf{알맞은 C 언어 Type} \\
  \hline
  \texttt{V}             & 인자 필요 없음 \\
  \texttt{[}             & \texttt{RvObject *} \\
  \texttt{B}             & \texttt{unsigned char} \\
  \texttt{C}             & \texttt{char} \\
  \texttt{H}             & \texttt{short} \\
  \texttt{I}             & \texttt{int} \\
  \texttt{F}             & \texttt{float} \\
  \texttt{D}             & \texttt{double} \\
  \texttt{T}             & \texttt{RvObject *} \\
  \texttt{S}             & \texttt{RvObject *} \\
  \hline
\end{tabular}

\section{기타}
\label{sec:etc}

\subsection{메모리 할당}
\label{sec:malloc}

현재 확장 모듈 라이브러리에서는 아래와 같은 메모리 할당 함수를
제공합니다.

\begin{verbatim}
void *RvMemMalloc (RvObject *self, size_t size);
\end{verbatim}

설명을 한다면

\begin{itemize}
\item RvMemMalloc \\
  이 함수에 의해 할당되는 메모리는 GC 메모리에서 할당된 메모리입니다.
  이렇게 할당된 메모리는 해제할 필요가 없으며 자동으로 해제가 됩니다.
  할당된 메모리 공간은 자동으로 Delayed Stack Slot 에 등록되어 메쏘드가
  완료될 때까지 해제가 되지 않음을 보장합니다.

  하지만 메쏘드가 끝난 후에도 할당된 메모리를 사용하고자 한다면 그것은
  보장할 수 없는 일입니다.  즉 RvSetUserData () 함수를 이용해서 다른
  메쏘드에서도 사용하고자 한다면 그것은 불가능합니다.

  RvSetUserData () 에 설정되는 값은 반드시 malloc 과 같이 확장 모듈 개발자가
  직접 제어할 수 있는 메모리여야 합니다.
\end{itemize}

\subsection{사용자 정의값 설정}
\label{sec:defineuserdata}

확장 모듈을 작성하게 되면, 특정 메쏘드에서 설정한 값을 다른 메쏘드에서
사용할 수 있도록 할 필요가 있습니다.  이를 위해서 사용할 수 있는 기본
함수가 존재합니다.

\begin{verbatim}
int RvSetUserData (RvObject *self, int index, void *data);
void *RvGetUserData (RvObject *self, int index);
\end{verbatim}

이 두 함수를 이용하여 특정 값을 SELF 에 임시적으로 저장해 놓을 수가
있습니다.  위의 함수 선언 중 INDEX 값은 배열의 index 번호로 기억하시면
됩니다.

내부적으로 사용자 데이타를 저장하기 위해서 배열을 사용하고 있는데,
이에 대한 index 번호인 것입니다.

RvMemMalloc () 함수과 같이 GC 메모리를 할당하는 공간은 RvSetUserData
() 함수를 이용해서 설정해 놓는 일이 없기를 바랍니다.
GC thread 가 메쏘드의 실행이 끝난 후에도 해당 메모리를 유지한다는
보장이 없습니다.

\subsection{Logging 기능}
\label{sec:uselogging}

현재 ROVM Server 는 ``ENVLANG File Format v0.2'' 를 처리할 수 있는
상태입니다만, 현재 포맷에는 Exception 에 대한 처리 기능이 없습니다.

그렇기 때문에 사용자의 Debugging 을 편리하게 하기 위해서 Logging
기능을 제공하고 있습니다.

만약 Log 가 설정되고 메쏘드의 반환값이 -1 로 설정될 경우, `ERROR'
예약어가 처리되게 되는데, 이 때 그 메세지의 일부로 client 측에
데이타가 전송되게 됩니다.

\begin{verbatim}
#ifndef ERRLOG_EMERG
#define ERRLOG_EMERG        0 /* system is unusable */
#define ERRLOG_ALERT        1 /* action must be taken immediately */
#define ERRLOG_CRIT         2 /* critical conditions */
#define ERRLOG_ERR          3 /* error conditions */
#define ERRLOG_WARNING      4 /* warning conditions */
#define ERRLOG_NOTICE       5 /* normal but significant condition */
#define ERRLOG_INFO         6 /* informational */
#define ERRLOG_DEBUG        7 /* debug-level messages */

#define ERRLOG_LEVELMASK    7 /* mask off the level value */

#define ERRLOG_WITHERRNO    (ERRLOG_LEVELMASK + 1)
#define ERRLOG_MARK         __FILE__,__LINE__
#endif

int RvLogError (RvObject *, int, const char *, int, const char *, ...);
\end{verbatim}

첫번째 인자의 경우, 반드시 SELF 객체에 대한 포인터를 줘야 합니다.
아무 \texttt{RvObject *} 포인터를 주는 것이 아닙니다.

두번째 인자의 경우, 로그의 레벨을 의미합니다.  위에서 정의된 매크로 중
적당한 레벨을 선정하시면 됩니다.

세번째 인자와 네번째 인자의 경우, \texttt{ERRLOG_MARK} 가 차지하는 공간입니다.
만약 로그 레벨이 \texttt{ERRLOG_DEBUG} 일 경우, 해당 파일의 이름과 위치 정보가
찍히게 됩니다.

그 이후 인자 부터는 vprintf 형식의 포맷이 이어지게 됩니다.  위 함수에
대한 예로써 아래와 같이 사용할 수 있을 것입니다.

\begin{verbatim}
RvLogError (self, ERRLOG_ERR, ERRLOG_MARK, 
            "cannot add more objects to list");
\end{verbatim}

위에서 기록한 메세지를 client 에서 받아 보기 위해서는 반드시 확장 모듈
함수의 반환값이 -1 이여야 한다는 사실을 기억 바랍니다.

\subsection{타입 관련 함수들}
\label{sec:relatetype}

\begin{verbatim}
const char *RvGetTypeName (RvValue *val);
\end{verbatim}

VAL 의 타입 이름을 반환하는 함수입니다.

\section{예제들}
\label{sec:examples}

\subsection{어슬픈 pcre 모듈 만들기}
\label{sec:example1}

예제로써 위에서 정의된 각각의 기능을 사용하여 PCRE (Perl Compatible
Regular Expressions) 라이브러리에 대한 `확장 모듈'을 만들어 보도록
하겠습니다.

우선 확장 모듈을 만들기 위해서 반드시 include 해야 하는 header 파일이
있습니다.  ``el.h'' 파일로써 내부적으로 사용하는 함수와 구조체 등등이
선언되어 있는 파일입니다.  이 파일은 \$prefix/src/module 디렉토리에
선언되어 있습니다.

클래스 이름을 `pcre' 로 정의를 하고, \texttt{init_<modulename>} 을
\texttt{init_pcre} 로 정하였습니다.  그리고 클래스 내부에 들어갈 method 에 대한
정보들을 RvMethodDef 구조체 형식에 맞게 채워 넣었습니다.

\begin{verbatim}
#include "el.h"

RvMethodDef pcre_funcs[] =
{
  {"compile", "(TSI)I", m_pcre_compile},
  {"__del__", "(T)V",   m_pcre_free},
  {0}
};

int
init_pcre (RvClass *cls)
{
  if (RvDefineMethods (cls, pcre_funcs))
    return -1;

  return 0;
}
\end{verbatim}

PCRE 확장 모듈의 method 로써 두 개의 method 를 정의하였는데, 하나는
컴파일 메쏘드이며, 다른 하나는 해제 메쏘드입니다.

그럼 각각에 대한 method 를 정의를 하도록 하겠습니다.  우선
m_pcre_compile 함수입니다.  아래에서 주목해서 봐야할 부분은 RvParseArg
() 함수를 사용한 부분과 RvSetUserData () 함수를 사용한 부분입니다.

ARGS 변수로부터 pattern 과 option 을 가져오도록 구성되어 있습니다.
\begin{verbatim}
enum pcre_userdata
{
  PCRE_RE
};

int
m_pcre_compile (self, args, ret)
     RvObject *self;
     RvValue *args, *ret;
{
  pcre *re;
  char *pattern;
  const char *error;
  int rv, erroffset, option;

  rv = RvParseArg (args, "si", &pattern, &option);

  re = pcre_compile (pattern,              /* the pattern */
                     0,                    /* default options */
                     &error,               /* for error message */
                     &erroffset,           /* for error offset */
                     NULL);                /* use default character tables */

  if (re == NULL)
    {
      printf ("PCRE compilation failed at offset %d: %s\n", erroffset, error);
      return -1;
    }

  RvSetUserData (self, PCRE_RE, (void *) re);

  return 0;
}
\end{verbatim}

PCRE 라이브러리 함수인 pcre_compile () 에서 반환되는 re 변수의 경우,
다른 라이브러리 함수에 반드시 사용할 필요가 있기 때문에, 이를 저장해
두어야 합니다.  이 저장해 높는 방식이 바로 RvSetUserData () 함수를
이용한 방법입니다.  아래의 m_pcre_free () 함수 부분을 보시면 위에서
저장해 놓은 값을 가져다 쓰는 부분을 볼 수 있습니다.

\begin{verbatim}
int
m_pcre_free (self, args, ret)
     RvObject *self;
     RvValue *args, *ret;
{
  pcre *re;

  re = (pcre *) RvGetUserData (self, PCRE_RE);

  if (re)
    free (re);

  return 0;
}
\end{verbatim}

이제 컴파일하는 단계입니다.  해당 파일을 공유 라이브러리로 작성해야
합니다.  아래에서 \texttt{pcre-6.3} 의 경우, pcre 라이브러리의 위치이며, \texttt{-lel}
옵션의 경우 \$prefix/src/module 에서 생성되는 libel 라이브러리입니다.

\begin{verbatim}
CFLAGS=-g -O0 -I../../rovm/src/module -fPIC -DPIC -Wall -c -I./pcre-6.3
LDFLAGS=-L../../rovm/src/module/.libs -Wl,-soname -Wl,pcre.so.0
all:
        gcc $(CFLAGS) rovm_pcre.c
        gcc -shared rovm_pcre.o pcre-6.3/.libs/libpcre.a $(LDFLAGS) \
            -o pcre.so -lel
\end{verbatim}

\subsection{어슬픈 str 모듈 만들기}
\label{sec:example2}

아래에서는 \texttt{\$rovmlib/root/core/str.c} 파일의 내용으로써 field 들에
대해서 매우 간단히 사용한 예제가 되겠습니다.

\begin{verbatim}
#include <stdio.h>
#include "el.h"

static RvFieldDef field[] =
  {
    { "value",  "[" },
    { "len",    "I" },
    { NULL, NULL },
  };

/** entry point.  */
int
init_str (RvClass *cls)
{
  if (RvDefineFields (cls, field))
    return -1;

  return 0;
}
\end{verbatim}

위 예제를 통해서 RvDefineFields 의 사용 방법과 RvFieldDef 구조체의
정의 부분을 중점으로 보시면 될 것입니다.

\subsection{가장 자세한 예제는?}
\label{sec:mostdetailed}

실게 ROVM Library 의 코드를 살펴보는 것입니다.  현재 core 클래스가 계속
개발 중이고 업데이트가 이루어지고 있습니다.  

\texttt{\$rovmlib/root/core} 디렉토리에 있는 *.c 파일들을 살펴보길 바랍니다.
가장 현실적이고 자세한 예제입니다.

\end{document}

%%% Local Variables: 
%%% mode: latex
%%% TeX-master: t
%%% End: 
