본문 바로가기

C

[GLIB2] GObject 개념 익히기

glib2 라이브러리는 gobject라는 라이브러리를 포함하고 있다. 해당 라이브러리는 C에서 객체를 생성하여 다룰 수 있도록 해준다.

C에서 객체라니... C는 절차적 언어 아닌가? 라고 생각이 들 수 있지만 객체 지향 프로그래밍 자체는 프로그래밍을 개발하는 프로그래밍 사고 방식이며 어떤 프로그램 언어이든 적용은 가능하다. 그러나 객체지향의 스펙을 지원하기 위해 개발된 OOP 언어가 아니기 때문에 제약은 있다.

 

https://onurmark.tistory.com/10

 

[GLIB2] 개발 환경 구축

GLIB는 GTK+ 프로젝트의 일부분에서 UI를 제외한 부분이 독립적으로 떨어져 나온 라이브러리로 다양한 소프트웨어 라이브러리를 포함하고 있다. 많은 운영체제에 이미 포팅되어있어 크로스플랫폼

onurmark.tistory.com

위 포스트에서 설명한 개발환경 위에서 진행한다. 먼저 위의 포스트를 따라 환경을 구축한다음 진행하도록 하자

 

GObject header

src/exam-car.h

#ifndef EXAM_CAR_H_BAQCYW7H
#define EXAM_CAR_H_BAQCYW7H

#include <glib-object.h>

G_BEGIN_DECLS

#define EXAM_TYPE_CAR exam_car_get_type()
G_DECLARE_DERIVABLE_TYPE(ExamCar, exam_car, EXAM, CAR, GObject)

struct _ExamCarClass {
    GObjectClass parent_class;
};

void
exam_car_print(ExamCar *self);

ExamCar *
exam_car_new(void);

G_END_DECLS

#endif /* end of include guard: EXAM_CAR_H_BAQCYW7H */

gobject는 {prefix}-{name}.h 형태로 네이밍한다. 이 예제에서는 prefix는 exam을 사용하고 car라는 object를 생성하도록 한다.

해당 코드가 복잡해 보이지만 템플릿이라고 생각하고 접근하자

 

먼저 glib의 object를 사용하기 위해서는 glib-object.h 헤더를 인클루드 한다. G_BEGIN_DECLS, G_END_DECLS는 C++ 에서도 해당 파일을 사용할 수 있도록 해주는 "extern C {}"와 동일한 구문이다. 

EXAM_TYPE_CAR와 G_DECLARE_DERIVABLE_TYPE은 해당 객체를 선언하는 부분이다. derivable type으로 선언하였기 때문에 나중에 해당 객체를 상속하여 자식 객체를 생성할 수도 있다. 그러기 위해 _ExamCarClass를 외부로 노출시켜준다. 해당 객체는 GObject로 부터 상속을 받는다. GObject는 기본이되는 오브젝트이다.

 

exam_car_print()은 해당 자동차의 정보를 stdout으로 출력하기 위한 함수이다. exam_car_new()는 해당 객체를 생성하는 함수이다.

 

이 모든걸 반드시 하나하나씩 이해하기 보다는 객체를 선언하기 위해서는 위와 같은 템플릿을 사용한다로 이해하자.

 

GObject source

src/exam-car.c

#include "exam-car.h"

typedef struct {
    gchar *model;
    gint year;
} ExamCarPrivate;

G_DEFINE_TYPE_WITH_PRIVATE(ExamCar, exam_car, G_TYPE_OBJECT);

static void
exam_car_class_init(ExamCarClass *klass)
{

}

static void
exam_car_init(ExamCar *self)
{

}

void
exam_car_print(ExamCar *self)
{
    ExamCarPrivate *priv =
        exam_car_get_instance_private(self);

    if (!priv->model) {
        g_print("Unknown model\n");
        return;
    }

    g_print("%d %s", priv->year, priv->model);
}

ExamCar *
exam_car_new(void)
{
    return g_object_new(EXAM_TYPE_CAR, NULL);
}

자 또한 복잡해 보이지만 어렵지 않다. ExamCarPrivate는 외부로 노출되지 않는 객체가 가지고 있는 변수들이다. G_DEFINE_TYPE_WITH_PRIVATE는 해당 객체가 private 변수를 가지고 있고 G_TYPE_OBJECT로 부터 상속을 받는 다는 의미이다.

 

exam_car_class_init() 에서는 상위 객체를 오버라이딩할 수 있다 exam_car_init()에서는 객체 생성시 초기화를 수행하는 부분이다. exam_car_new()는 객체를 생성하는 부분이다.

 

클라이언트 코드

src/main.c

#include <glib.h>

#include "exam-car.h"

int main(int argc, char *argv[])
{
    ExamCar *my_car;

    my_car = exam_car_new();

    exam_car_print(my_car);

    return 0;
}

my_car 객체를 생성하고 출력하는 클라이언트 코드이다.

 

빌더에 해당 코드를 같이 컴파일 하도록 수정

src/Makefile.am

bin_PROGRAMS = glibexam

glibexam_SOURCES = \
        main.c \
        exam-car.h \
        exam-car.c

glibexam_CFLAGS = \
        $(AM_CFLAGS) \
        $(GLIB_CFLAGS) \
        $(GIO_CFLAGS) \
        $(GOBJECT_CFLAGS)

glibexam_LDADD = \
        $(GLIB_LIBS) \
        $(GIO_LIBS) \
        $(GOBJECT_LIBS)

방금 생성한 exam-car.h exam-car.c 를 포함하여 빌드하도록 수정한다.

 

$ autoreconf -i

을 수행하여 방금 변경된 빌드를 추가한다. 매번 소스 파일을 추가할때마다 이 작업을 반복하기가 번거롭기 때문에 configure시 automake의 변경 사항을 검사하여 자동으로 추가하는 설정을 해주는게 좋다.

./configure --enable-maintainer-mode

maintainer-mode가 활성화되면 Makefile.am 의 변경사항을 자동으로 추적해 빌드에 포함시켜 주기 때문에 더이상 autoreconf를 수행할 필요 없이 make 명령으로 컴파일 할 수 있게 해준다.

 

~/Workspaces/glibexam ❯ ./src/glibexam
Unknown model

make 로 컴파일 후 실행하면 다음과 같이 정상적으로 출력되는 것을 확인 할 수 있다.

 

지금까지 객체를 생성하고 호출 하는 방법에 대해 간략히 개발을 하였다. 다음 포스트에서는 해당 object의 private 변수에 있는 property에 접근하여 값을 설정하고 얻어오는 방법이랑 상속에 대해서 좀 더 자세히 들어가 볼 예정이다.