2013년 7월 13일 토요일

제가 Pro Git 내용을 요약한 자료를 공개합니다. 같이 일하는 동료들에게 Git 에 대해 소개하려고 만들었던 자료입니다. Git에 대한 특정 내용을 알고 싶을 때, 이 자료의 목차에서 내용을 찾아보기에 좋은 것 같아 블로그에 올립니다.

This is a slide which summarizes Pro Git. I have made this because I wanted to give the description of Git to my co-workers. I hope that it would be a great help to find a specific topic of Git. If you want to know more details than this, you can easily refer to the information of Pro Git because this slide has the same titles as it.

2013년 5월 10일 금요일

GObject - Inheritance

이번에는 GObject 상속 구조에 대해서 기술하려 합니다.

상속을 설명하기 위한 class 들은 다음과 같습니다.

ClassDataMethod
StudentInfoname, addressprint 'name' and 'address'
UniversityStudentname, address, university_nameprint 'name', 'address' and 'university_name'

UniversityStudent가 StudentInfo를 상속하고, StudentInfo로부터 UniversityStudent는 name, address를 상속받고 자신만의 데이터인 university_name을 갖습니다. method는 StudentInfo에서 상속받지만 그대로 상속받지 않고 약간 변형합니다(Method override).

이 두 클래스를 GObject로 구현해 보겠습니다.
우선, Class를 구현하기 위한 구조체는 다음과 같습니다.


StudentInfo는 GObject에서 상속받고, UniversityStudent는 StudentInfo로부터 상속받습니다. C의 구조체로 상속관계를 표현하는 가장 좋은 방법 중의 하나는 바로 구조체 중첩입니다. 이렇게 하면 UniversityStudent는 StudentInfo의 data와 method (function pointer)를 그대로 가져올 수 있습니다.

UniversityStudent* us;
us->parent.parent.g_type_instance.g_class->g_type
((GTypeInstance*)us)->g_class->g_type

위 코드는 GObject에서 어떻게 class구조에 대한 pointer를 가져오는지 보여주고 있습니다. C 구조체의 첫번째 필드는 항상 그 구조체의 첫번째 byte부터 저장되는데, 이 속성을 이용해서 우리는 UniversityStudent의 class 정보를 쉽게 가져올 수 있는 것입니다.

StudentInfo의 class 구현은 첫번째 GObject 설명에서 이미 다루었고, 여기서는 UniversityStudent class를 중심으로 설명하겠습니다.

UniversityStudent도 StudentInfo와 마찬가지로 university_student_base_init(), university_student_class_init(), university_student_init() 함수를 정의하고 g_type_register_static 함수로 type 정보를 등록합니다(university_student_get_type). 

university_student_get_property(), university_student_set_property() 함수를 만들어 private data인 university_name을 읽고 쓸수 있도록 합니다.

university_student_finalize() 함수를 만들어서 UniversityStudent 객체가 파괴될 때 memory를 정리하는데, 이 함수에서 StudentInfo의 finalize 함수를 다음과 같이 호출합니다.
line 92에서 finalize() call을 통해서 Superclass의 finalize 함수를 실행합니다. 다음은 UniversityStudent의 class 초기화 함수입니다.

university_student_parent_class 포인터는 university_student_class_init() 함수 내에서 정의할 것입니다.
line 117에서 university_student_parent_class를 정의합니다.
line 126에서 GObject의 finalize 함수를 override 합니다.
line 128에서 StudentInfo Class의 print 함수를 university_student_print 함수로 override 합니다.

line 138에서 property를 등록하는데, property 한 개를 등록할 때에는 g_object_class_install_property를 사용하고, 여러 개를 한꺼번에 등록할 때에는 g_object_class_install_properties를 사용합니다(GObject property 설명 참조). 

line 120에서 class pointer 들을 출력하는 부분이 있습니다. 실행 결과가 어떻게 될까요? 한번 생각해 보세요. 결과는 잠시 후에 설명합니다. 다음은 instance 초기화 함수입니다.
line 150에서 UniversityStudent 포인터로부터 class 구조체 포인터를 얻고, 그 값을 출력하고 있습니다. 비교를 위해서 parent class의 포인터(university_student_parent_class)도 출력합니다.
상속을 test 하기 위한 함수입니다. line 104에서 UnversityStudent type으로 GObject를 생성합니다. line 108 ~ 111까지 Instance 정보와 Class 정보를 얻기 위한 pointers를 선언합니다. uobj1은 UNIVERSITY_TYPE_STUDENT type 입니다. 따라서 line 114는 university_student_print 함수를 호출한 것과 같습니다. 또한 line 125도 uobj1의 StudentInfoClass 포인터를 얻어서 print 함수를 호출했기 때문에 line 114와 같은 함수를 호출한 것입니다.

line 118, 119에서 uobj1을 StudentInfo와 UniversityStudent type으로 각각 cast 합니다. C 문법으로 보면 STUDENT_INFO와 UNIVERSITY_STUDENT는 우리가 정의했던 macro이지만 GObject의 cast라고 생각하면 편리합니다. 따라서 line 121의 결과는 si, us 포인터들이 같은 값을 나타냅니다.

line 129 ~ 132의 출력 결과는 모두 같습니다. 즉 uobj1에 대한 class 포인터를 얻는 방법이 여러 가지라는 것을 나타냅니다. 특히 line 132의 구문은 우리가 앞에서 봤던 GObject에서 Class 정보를 얻는 방법인데, 이것은 설명을 위해 추가한 것으로 보통은 이렇게 하지는 않습니다. line 131처럼, 미리 정의한 macro를 사용하지요.

그럼 우리가 override된 함수가 아닌 부모 class의 함수만 호출하고 싶다면 어떻게 할까요? line 139가 그 방법을 알려주고 있습니다. 부모 class의 pointer를 얻고 싶을 때에는 g_type_class_peek_parent 함수를 사용합니다. 

line 142에서 uobj2를 생성하고, line 145에서 그 값을 출력하고, line 147, 148에서 생성되었던     GObject들을 해제합니다. g_object_unref의 결과 finalize 함수가 호출되고, memory는 안전하게 헤제되는 것입니다.

위 코드를 실행한 결과는 다음과 같습니다.

Start main() ...

student_info_base_init() called 1

student_info_class_init() called
gobj_class: 0x971ee30, klass:0x971ee30
student_info_parent_class: 0x971ed00

student_info_base_init() called 2
university_student_base_init() called

university_student_class_init() called
gobj_class: 0x971ef40, klass: 0x971ef40
university_student_parent_class: 0x971ee30

student_info_init() called
StudentInfoClass: 0x971ee30
student_info_parent_class: 0x971ed00

university_student_init() called
UniversityStudentClass: 0x971ef40
university_student_parent_class: 0x971ee30

university_student_print() called
   student_info_print() called
Name : 김혜진
Address : 부산
University : 부산대

Instance pointers for uobj1: 
STUDENT_INFO: 0x971f810, UNIVERSITY_STUDENT: 0x971f810

STUDENT_INFO_GET_CLASS(uobj1)->print
university_student_print() called
   student_info_print() called
Name : 김혜진
Address : 부산
University : 부산대

Class pointers for uobj1: 
STUDENT_INFO_GET_CLASS: 0x971ef40
UNIVERSITY_STUDENT_GET_CLASS: 0x971ef40
((GTypeInstance*)us)->g_class: 0x971ef40

university_student_parent_class for uobj1: 0x971ee30

STUDENT_INFO_GET_CLASS(g_type_class_peek_parent(usc))->print
   student_info_print() called
Name : 김혜진
Address : 부산

student_info_init() called
StudentInfoClass: 0x971ee30
student_info_parent_class: 0x971ed00

university_student_init() called
UniversityStudentClass: 0x971ef40
university_student_parent_class: 0x971ee30

university_student_print() called
   student_info_print() called
Name : 김수진
Address : 서울
University : 서울대
부산대 information is being deleted.
김혜진 information is being deleted.
서울대 information is being deleted.
김수진 information is being deleted.


이 출력은 첫번째 글의 StudentInfo에 대한 설명에 나온 코드와 조금 다른데, 그 이유는 제가 출력문만 보기 편하게 조금 손질했기 때문입니다. 또한 전체 코드는 여기에 있습니다.

university_student_class_init 함수의 출력을 보면 university_student_parent_class에서 부모의 class pointer를 출력하고 있는데, 이 값이 StudentInfoClass의 출력 값과 같습니다. 즉 university_student_parent_classs는 정확하게 부모의 class pointer 값을 가지고 있음을 알 수 있습니다.

student_info_base_init 함수의 호출 순서를 살펴보면 이 함수가 언제 쓰이는지 알 수 있습니다. 즉 student_info_class_init가 호출되기 전에 한번, university_student_base_init가 호출되기 전에 또 한번 호출되었는데, 이는 student_info_base_init 함수가 class hierachy에서 자신을 포함한 모든 자식 class가 만들어질 때마다 호출됨을 의미합니다.

uobj2를 생성할 때에는 class 생성 함수들이 호출되지 않는 것도 볼 수 있습니다. 이는 object-oriented programming의 특성으로, class는 한번만 생성되고 instance는 여러 개가 생성되므로  당연한 결과 입니다.

이상으로 GObject 상속에 대한 내용을 마칩니다. 다음에는 interface에 대한 내용을 다룰 예정인데, 글을 올리는 것이 생각보다 많은 노력이 들어가네요. 게다가 Algorithm 연구 등 개인적으로 공부하는 것도 많아서 글을 올리는 속도가 굉장히 느립니다. 그렇지만 GObject 뿐만 아니라 GLib API까지 다루어보고 싶고, 아무리 오래 걸리더라도 그 목표를 달성하고 싶습니다. 

2013년 4월 2일 화요일

Git 과 svn 비교 - svn 대비 git의 차별점

Git에 대해 생각했던 점을 정리해보려 합니다. 이 글은 git을 처음 시작하는 분이나, 혹은 git을 이미 잘 사용하고 있는 분에게도 생각해볼 만한 유용한 내용을 공유하려는 목적으로 작성되었습니다.
git을 사용하면서 스스로에게 다음과 같은 질문을 할 때가 많았습니다.
  1. git이 svn 대비 장점은 무엇일까?
  2. 왜 많은 조직들이 svn에서 git으로 옮기려고 하는가?
처음에 git을 단순히 사용하기만 할때에는 위 질문들에 답할 수 없었습니다. 그도 그럴 것이 git repository를 중앙집중식으로 운영해서 마치 svn을 사용할 때와 똑같이 운용했었으니까요. 이렇게만 사용하면 사실 git은 svn 대비 장점을 가지기 힘듭니다. 게다가 git은 개념적으로 svn보다 어렵습니다. 많은 조직들이 여전히 svn 같은 중앙집중식 소스 관리를 하는 이유는 바로 개념적으로 쉽기 때문입니다.

그런데, 많은 open source projects 들이 git을 사용하고 있고, 소위 잘나가는 개발사들은 git을 기반으로한 source control을 사용합니다. 거기에는 나름의 이유가 있고, 그 이유를 정확히 알아야 우리가 각 project에 최적인 방법으로 source를 운용할 수 있습니다. 우리가 원하는 것은 결국 소스를 최신으로 유지하면서도 개발자들이 원하는대로 원하는 순간에 수정할 수 있는 방법을 찾는 것이고, 이를 달성하기 위해서는 분산 관리 방식의 장점을 정확히 이해하는 것부터 시작해야 할 것입니다.

svn으로 작업할 때에는 소스를 중앙 저장소에 commit 하기 전에 대부분의 기능을 완성해놓고 commit 하는 경우가 많습니다. 그도 그럴 것이, commit을 한다는 자체가 중앙 저장소에 내가 만든 기능을 공개한다는 뜻이기 때문입니다. 그래서 개발자가 자신만의 version history를 가질 수 없고, commit한 내용에 실수가 있을 시에 다른 개발자에게 바로 영향을 미치게 됩니다. ( 물론, branch를 따서 관리하는 방법도 있지만, 수백명의 개발자의 branch를 따로 만들어주고 관리하는 것은 굉장히 어렵습니다. )

반면, git은 개발자가 자신만의 commit history를 가질 수 있고, 개발자와 서버의 저장소는 독립적으로 관리 가능합니다. 여기서 독립적으로 관리한다는 말은 개발자의 commit이 바로 서버에 영향을 미치지 않는다는 것입니다. 개발자는 마음대로 commit하다가 자신이 원하는 순간에 서버에 변경 내역(commit history)을 보낼 수 있으며, 서버의 통합 관리자는 관리자가 원하는 순간에 각 개발자의 commit history를 가져올 수 있습니다.

통합 관리자가 각 개발자의 commit history를 가져온다는 것은, 각 개발자가 완성한 commit histroy에 대해서 통합 관리자가 차후에 아무때나 가져와 적용할 수 있다는 뜻입니다. 통합 관리자가 각 개발자의 commit history를 가져오기 전에 이미 각 개발자는 자신의 저장소와 서버의 저장소간 통합을 마친 상태이므로 통합 관리자는 별다른 어려움 없이 commit history를 가져올 수 있습니다. ( 여기서 별다른 어려움이 없다는 말은 merge conflicts가 일어나지 않는다는 말입니다. 다른 말로 Fast forward merge 라고 합니다. 클릭하면 커지는 아래 그림 참조. )
Figure 1. git repository의 commit history

이렇게 git은 서버 저장소와 개발자 저장소가 독립적으로 commit history를 가져갈 수 있기 때문에 매우 유연한 방식으로 source를 운영할 수 있으며, 이러한 유연성이 git의 가장 큰 장점입니다. 이 유연성을 바탕으로 바로 우리의 목적인 "소스를 최신으로 유지하면서도 개발자들이 원하는 때에 원하는 만큼 수정할 수 있는 방법"을 달성할 수 있는 것입니다.

따라서 svn과 다르게 git은 굉장히 다양한 방식으로 소스를 관리할 수 있으며, open source projects에서 많이 사용하는 방법들에는 다음과 같은 것들이 있습니다.


위 방식들에 대한 자세한 내용은 http://git-scm.com/book 을 참고하시기 바랍니다.

2013년 1월 30일 수요일

GObject example source codes

제가 예제로 작성한 코드를 공유하려 합니다.

git://github.com/seungzzang/DES-GObject.git

위 주소를 입력으로 git clone 하시면 전체 소스를 받으실 수 있습니다.

git clone git://github.com/seungzzang/DES-GObject.git

소스 내용 중, iface 라는 directory가 있는데, 여기에 있는 소스는 interface를 설명할 때 사용할 것입니다.

아주 간단한 소스이지만, GObject를 처음 공부하는 분들에게는 도움이 되리라 생각합니다.

이 소스를 compile 하려면 glib-2.0, gobject-2.0 libraries 이 필요합니다.

2012년 10월 28일 일요일

GObject의 property 정의 및 사용


처음 올린 글은 GObject의 subclass로 StudentInfo 를 정의하는 방법을 설명했습니다. 덧붙여, 각종 macro와 객체 초기화 및 정리 코드에 대한 내용도 설명했습니다.

이제 두번째로 StudentInfo 에서 선언했던 property에 대한 설명과 실제로 StudentInfo를 사용하는 예제를 보여드리려 합니다.

다음 코드는 첫번째 글의 Figure 4에서 가져온 것입니다.
Figure 2.1

위 코드는 student_info_class_init에서 property를 정의한 부분을 가져온 것입니다. 두개의 properties가 있는데, "name" 과 "address"를 정의합니다. g_param_spec_string 은 string (gchar *)값을 가지는 property를 나타냅니다. 함수의 정의는 링크를 참조하세요. 링크된 페이지를 보면, 비슷한 류의 다른 함수들도 보입니다. 

135 줄의 코드를 설명하면, 이름은 "name"이고, 별명도 "name"인 읽고 쓰기가 가능한 property를 정의하고 있습니다. 그리고 정의된 "name" 과 "address" 를 151 줄에서 등록하고 있습니다.
Figure 2.2 - StudentInfo.h

Figure 2.2는 Figure 2.1에 정의된 enum 값을 보여주고 있습니다. 그리고 props 변수는 보통 객체를 정의하는 StudentInfo.c 파일에 다음처럼 선언합니다.
GParamSpec 정의 참조

일단 위처럼 정의된 property는 set/get method를 통해서 access 하게 됩니다.
Figure 2.3

set/get 함수는 Figure 2.3에서 보는 것처럼 정의하는데, static으로 정의된 것에서 알 수 있듯이 이 set/get 함수를 직접 호출하는 경우는 없습니다. g_object_set_property(GObject *obj, const gchar *property_name, const GValue *value) 함수를 호출하면 obj에 해당하는 _set_property () 함수가 호출되는 것입니다. 따라서 여기서는 g_object_set_property를 호출하면 내부적으로 student_info_set_property가 호출됩니다. get_property 함수도 마찬가지 입니다.

이제 지금까지 만들었던 StudentInfo class를 직접 사용하는 예는 다음과 같습니다.
Figure 2.4

위 코드를 실행시키면 다음과 같은 출력을 얻을 수 있습니다.
Figure 2.5

위 출력은 이제까지 설명한 코드들에 있었던 g_print 문장의 결과입니다. 출력문을 보면, StudentInfo 객체의 name을 "다니엘"로, address를 "서울"로 만들고 곧바고 그 객체를 destroy하는 것을 보여주고 있습니다.

그럼, Figure 2.4의 코드를 봅시다. GValue는 다양한 type의 값을 담을 수 있는 container 입니다. g_value_init으로 담을 값의 type을 지정해 주고, g_value_set_ 계열의 함수로 값을 설정합니다. 물론 대응되는 g_value_get_ 계열의 함수로 값을 얻어올 수도 있습니다.

위 코드는 이렇게 설정된 val로 name과 address를 설정하고 있습니다. 그리고 80 줄에서 설정된 name 과 address를 출력하고 84 줄에서 StudentInfo 객체를 destroy하고 함수를 종료합니다.

70 줄의 g_value_reset 함수는 그 아래에 주석처리한 g_value_unset 함수와 g_value_init 함수를 연달아 부른 것과 같은 효과를 가집니다. (자세한 내용은 링크 참조.)

지금까지 GObject의 subclass로서 StudentInfo 객체를 정의하고, 그 객체에 필요한 property를 정의하고 사용하는 법을 다루어 봤습니다.

다음 세번째 글에서는 inheritance 에 대해서 다루어볼 예정입니다.

2012년 9월 16일 일요일

GObject programming

GLib, GObject ...

IBus open source project에 참여하다가 GLib을 알게 되었습니다. IBus는 linux 계열의 OS에 다국어 입력을 지원하는 Input method framework을 제공합니다. IBus 는 많은 다른 open source 기술들로 구현되어 있는데, 그 중 대표적인 것이 GLib 이었습니다. 

이 GLib에 대한 이야기를 하고 싶어 처음으로 저도 블로그를 시작하게 되었네요. ^^;


GLib은 단순한 library가 아닙니다. C 언어로 OOP style로 programming 할 수 있게 해주며, 다른 high level scripting 언어와의 연결을 지원합니다. 게다가 program에 필수적인 자료구조들과 signal, object property, event loop 같은 유용한 기능들도 지원해서 마치 programmer를 위한 종합 선물세트 같은 녀석이 GLib 입니다. 게다가 대부분의 open source들이 이녀석을 기본적으로 사용하고 있어서, 알아두면 두고두고 좋을 녀석입니다. 예를 들면 clutter, webkitGTK, GTK+, GStreamer 등이 있고 그외에도 많습니다.

그런데 안타깝게도 이 녀석에 대한 소개글이 영문으로 된 공식 홈페이지 이 외에는 찾기가 어렵다는 점입니다. 이마저도 처음에 접하기에는 상당히 어렵게 기술되어 있어서 우리나라 programmer들이 이 훌륭한 녀석을 사용하기가 어렵다는 점이 단점입니다.

그래서 제가 알고 있는 지식을 공유하고 도움을 주고자 이 블로그를 시작하게 되었습니다.

그럼 이제 본론으로 들어가겠습니다. 다음과 같은 내용들을 다룰 예정입니다.
  1. GObject 서브 클래스로 type 정의하기.
  2. class의 property 정의 및 사용
  3. Inheritance example
  4. Interface 설명
이 중에서 이글을 1번을 다룹니다. 차후에 이어지는 글에서 나머지도 다루겠습니다. 이 내용을 이해하기 위해서 필요한 지식은 C언어를 알아야 하고, OOP에 대한 기본적인 개념을 알고 있어야 합니다. 

GObject : object-oriented C-based APIs

GLib에서는 새로운 object type을 정의하기 위해서 GType library를 사용합니다. gtype.h에는 다음과 같은 구조체가 정의되어 있습니다.
typedef struct _GTypeInfo               GTypeInfo;
struct _GTypeInfo
{
  /* interface types, classed types, instantiated types */
  guint16                class_size;
  
  GBaseInitFunc          base_init;
  GBaseFinalizeFunc      base_finalize;
  
  /* classed types, instantiated types */
  GClassInitFunc         class_init;
  GClassFinalizeFunc     class_finalize;
  gconstpointer          class_data;
  
  /* instantiated types */
  guint16                instance_size;
  guint16                n_preallocs;
  GInstanceInitFunc      instance_init;
  
  /* value handling */
  const GTypeValueTable *value_table;
}; // Figure 1. GTypeInfo structure
위 구조체 필드에 정보를 채우고 g_type_register_static 함수를 호출해서 새로운 type을 등록합니다. 구조체 필드가 복잡해 보이는데, 써보면 별 것 아닙니다. ^^;

각 필드에 대해서는 차차  설명하기로 하고, 여기서는 위 구초체를 정의하고 g_type_register_static 계열의 함수로 type을 등록한다 정도만 알아두면 됩니다.
Figure 2

위 코드는 StudentInfo 객체를 정의하기 위해 필요한 구조체들을 선언한 것입니다. 먼저 StudentInfo 구조체를 보면, 여기에는 class의 멤버 변수(member variable)를 선언합니다. StudentInfoPrivate 구조체는 멤버 변수 중에서 이 클래스에서만 사용가능한 변수를 선언해 놓은 것으로, 자바의 private 변수와 개념적으로 같습니다.

StudentInfoClass는 class의 멤버 함수 (member function)을 정의하는 곳입니다. 위에는 학생 정보를 출력하는 함수를 담을 "함수 포인터" 변수를 선언했습니다.

이 정의에서 살펴볼 점은 구조체를 선언할 때 _StudentInfo 와 StudentInfo를 이용했다는 점입니다. _를 이용한 정의는 실제 구조체 내용을 볼 수 있는 부분이고, StudentInfo 라고 typedef 한 부분은 실제 코드에서 사용할 type 이름이 됩니다. 이렇게 하면 StudentInfo의 정의를 참조할 때 쉽게 찾을 수 있다는 장점이 있습니다. _StudentInfo만 검색하면 되니까요.

이제 이 StudentInfo를 GLib type 시스템에 등록하는 방법을 살펴보겠습니다.
Figure 3

Figure 1에서 보았던 GTypeInfo 구조체에 정보를 채우고, 이를 g_type_register_static 함수에 넘겨주는 방식으로 object type을 등록합니다. GTypeInfo 구조체의 각 필드를 보면 다음과 같습니다.
  1. class_size 첫번째 필드는 StudentInfoClass 구조체의 크기를 나타냅니다.
  2. base_init 두번째 필드는 class를 초기화하는 함수 중 하나인 base_init 함수를 등록하는데, 사실 이 함수는 거의 쓰이지 않습니다. 대부분을 이 값을 NULL로 설정하지만, 여기서는 차후 간단 설명을 위해서 student_info_base_init 함수를 등록했습니다.
  3. base_finalize세번째 필드는 base_finalize 함수를 등록하는데, 이 역시 거의 쓰이지 않으므로 사용하지 않습니다.
  4. class_init네번째 필드는 진정으로 class를 초기화하는 함수인 class_init 함수를 등록합니다. 이 함수는 class member function을 정의하고, class property를 설정하고 private 구조체를 등록합니다. 
  5. class_finalize : class가 destroy 될 때 호출되는 함수를 정의합니다. 해당 클래스의 마지막 instance가 사라질 때 class가 destroy되는데, 이때 필요한 작업을 정의하는 함수가 class_finalize 함수입니다.
  6. class_data : class 전체에서 사용할 필요가 있는 데이터를 선언합니다. C++의 정적 멤버와 비슷한 역할을 합니다.
  7. instance_size : StudentInfo의 크기를 설정합니다. 즉, instance 구조체의 크기를 설정합니다.
  8. n_preallocs : GLib 2.10 이전에 사용되던 필드입니다. GLib 2.10 이후부터는 무시되는 필드이기 때문에 0으로 설정합니다.
  9. instance_init : class_init 함수 호출 이후, instance를 초기화할 때 호출되는 함수입니다. 
각 필드에 대한 설명을 간략히 했습니다만, 사실 이는 object를 직접 선언하고 사용해보면 더 명확해질 것입니다. 

StudentInfo object를 사용하기 위해서 남은 것은 위에 선언한 함수들을 구현하는 작업입니다.
Figure 4

student_info_base_init 함수는 거의 사용되지 않는 함수이고, 이 함수는 나중에 inheritance 설명할 때 나올 것이므로 일단 넘어갑니다. student_info_class_init은 중요한 함수입니다. class에 관한 다양한 설정을 할 수 있는 곳이죠. 이 함수는 class 가 생성될 때 GLib에 의해 자동으로 호출됩니다. 

123 줄은 StudentInfoClass 타입 포인터를 GObjectClass 타입 포인터로 cast 하는 것입니다. 여기서 G_OBJECT_CLASS macro가 사용되었는데, 이는 superclass와 subclass간 cast 연산을 수행하기 위해서 GLib에서 제공하는 macro 입니다. StudentInfo class에 대해서도 비슷한 macro를 정의할 수 있습니다. 
Figure 5

STUDENT_TYPE_INFO는 StudentInfo class의 type 정보를 얻기 위한 macro 입니다. 위에서 우리가 student_info_get_type() 함수는 구현했었죠? 


STUDENT_INFO와 STUDENT_INFO_CLASS는 GObject와 GObjectClass 구조체를 StudentInfo와 StudentInfoClass로 각각 cast 하기 위한 macro 입니다. 그리고 STUDENT_IS_INFO는 obj가 StudentInfo type인지 알아보는 macro 이며, STUDENT_IS_INFO_CLASS는 klass가 StudentInfoClass인지 알아보는 macro 입니다.

마지막으로 STUDENT_INFO_GET_CLASS는 obj의 class 구조체 포인터를 얻는 macro 입니다. 이런 macro들의 사용 예는 후에 설명하겠습니다. 어쨌든 이런 macro들은 모두 GLib에서 제공하는 G_TYPE_ 계열의 함수를 사용해서 정의하게 됩니다. 이에 대해 자세히 알아보려면 Type information API를 정의한 공식 문서를 참고하기 바랍니다. 

다시 위 코드(figure 4)로 돌아와서, 125줄은 StudentInfoClass의 부모 클래스 구조체 포인터를 얻기 위한 문장입니다. StudentInfoClass의 부모 클래스 구조체는 figure 3, 31줄의 g_type_register_static 함수의 첫번째 인자로 넘겨준 type입니다. 바로 G_TYPE_OBJECT 즉, GObjectClass가 StudentInfoClass의 부모 클래스 구조체라고 할 수 있습니다. 

125줄의 g_type_class_peek_parent는 인자로 받은 class의 부모 클래스 구조체 포인터를 얻을 수 있는 함수입니다. StudentInfoClass가 처음으로 메모리에 생성될 때, 그 부모 클래스인 GObjectClass가 먼저 생성되게 됩니다. StudentInfoClass 구조체는 첫번째 멤버로 GObjectClass를 포함하고 있는데, 이 GObjectClass의 내용은 앞서 생성된 GObjectClass의 내용을 copy해서 채워지게 됩니다. 즉, 메모리에는 GObjectClass가 어딘가에 생성되어 있고, 이것의 내용을 copy해서 StudentInfoClass의 첫번째 멤버인 GObjectClass 구조체를 채우게 되는 것입니다. 따라서 g_type_class_peek_parent로 얻어지는 포인터는 메모리에 어딘가에 생성되어 있는 GObjectClass의 포인터를 얻어오는 것이며, StudentInfoClass 안의 첫번째 멤버의 포인터를 얻어오는 함수는 아닙니다. (사실 StudentInfoClass의 첫번째 멤버에 대한 포인터를 얻으려고 함수를 호출할 필요도 없습니다. StudentInfoClass의 주소가 첫번째 멤버에 대한 포인터가 될 테니까요.) 이것을 이렇게 자세히 설명하는데에는 이유가 있습니다. 이 내용이 C로 Inheritance 및 Polymorphism 을 구현하는 핵심적인 내용이기 때문입니다. 

127번부터 129번까지의 줄은 GObject의 멤버 함수들을 override 하고 있습니다. GObject 포인터가 StudentInfo 객체를 가리키고 있을 때, 그 포인터로 호출된 get_property 함수는 student_info_get_property 함수를 호출하게 됩니다.

131번 줄은 StudentInfo 객체의 멤버 함수를 정의하고 있습니다. 여기서는 print 함수 하나만 정의하고 있네요. 

133번 줄은 StudentInfo의 멤버 변수들을 등록하고 있습니다. 이렇게 등록된 멤버 변수들은 나중에 정의할 STUDENT_INFO_GET_PRIVATE 마크로를 통해서 access 하게 됩니다.

135번부터 151번 줄까지는 객체의 속성(property)들을 정의하고 있습니다. 이렇게 속성을 정의하고 멤버 변수와 연관시켜서 동작하게 되는데, 이는 차후에 살펴볼 예정입니다.
Figure 6

Figure 3 에서 gtype_info에 등록한 함수 중에 마지막 함수입니다객체의 instance를 초기화하는 함수인 student_info_init 함수입니다. 157줄에 STUDENT_INFO_GET_PRIVATE가 보이는데요. 이는 다음처럼 정의합니다.
student_info_init 함수에서 StudentInfo의 priv 멤버를 이 마크로로 초기화하고, priv가 가리키는 변수들을 access 함으로써 instance의 초기화를 수행합니다. 여기서는 그냥 NULL로 멤버 변수를 초기화했는데, NULL값 초기화는 자동으로 이루어지므로 사실 필요없습니다. 멤버 변수를 동적으로 메모리를 할당한다거나 그외 초기값을 줄 필요가 있을 때에는 여기서 초기화를 하면 됩니다.

이제 StudentInfo 객체를 만들기 위한 기본적인 함수들이 정의되었습니다. 남은 함수는 멤버함수인 student_info_print 함수와 객체가 파괴될 때 호출되는 student_info_finalize 함수, 그리고 property 관련 set/get 함수들이 있습니다. 
Figure 7

student_info_print 함수부터 살펴봅시다. 108줄과 109줄은 인수로 받은 포인터가 StudentInfo type인지를 check합니다. g_return_if_fail 함수는 인수로 받은 식이 거짓이면 함수를 return하는 GLib의 Debugging 함수입니다. 이런 종류의 또다른 debugging 함수가 보고 싶다면 여기를 참고하세요. 109줄에서 GObject 포인터가 StudentInfo 객체를 가리키고 있는지 검사하는 macro가 사용되었습니다. 이 macro는 figure 5에 정의되어 있습니다. 

111줄에서 GObject 포인터를 StudentInfo 포인터로 cast합니다. 109줄에서 인수로 들어온 ob가 StudentInfo를 가르킨다는 사실을 알았으므로 이는 유효한 cast가 됩니다. 

113 ~ 114줄은 cast한 StudentInfo 포인터를 이용해서 멤버 변수를 출력하고 있습니다.

student_info_finalize 함수는 객체가 파괴될 때 GLib에 의해서 자동으로 호출되는 함수입니다. 이 함수를 우리는 figure 4에서 등록했습니다. 객체가 파괴될 때 호출되는 함수이므로, 여기서는 메모리 누수가 발생되지 않도록 정리하는 코드가 들어가야 합니다. 따라서 프로그램 실행 중에 동적으로 할당되었던 멤버 변수를 해제합니다. StudentInfo의 해제 코드를 모두 수행한 후, 102줄에서 부모 클래스의 finalize 함수를 호출합니다. 이렇게 해야 상위 클래스의 해제까지 가능하기 때문입니다.

여기까지 기본적인 객체의 생성 코드를 살펴봤습니다. 다음 글에는 property 관련 설명과 실제로 StudentInfo를 사용하는 예제를 보여드릴 예정입니다.