 접속하는 사람들 대부분이 제가 아는 분일 거라 생각하고 한마디 하자면, 세상에 공짜가 어디 있는가, 주저하지 말고 내게 연락해서 술 한 잔 사게! (언젠가부터 술 강요 청탁 협박 블로그가 되어 가고 있군...)

**속성 (Properties) 추가하기**

이제, GObject 속성(properties) 기능을 추가하려고 하는데, 왜 쓸데없이 일을 만들어서 하냐고 물으면 할 말이 있어야할 것 같아서, GObject 속성의 특징을 요약해 봤습니다.

1.  단일 API로 모든 속성 값을 얻어오거나 변경하기
2.  속성 변경시 자동으로 호출되는 함수 등록하기 (시그널 이용)
3.  실행 중에 속성에 대한 정보 얻어내기

물론 이미 많은 언어와 라이브러리가 그 이상의 기능을 지원하기도 하고, 일정 능력 이상의 개발자라면 직접 구현하는게 아주 어려운 것도 아닙니다. 하지만 이미 잘 구현되어 검증받은 라이브러리가 있는데 굳이 새로운 바퀴를 만들 필요는 없겠지요? 아무튼, 정확한 내용은 글을 적으면서 하나씩 설명해 나가겠습니다.

GObject 객체에 속성을 추가하려면 속성의 값(value)이 어떤 형(type)인지, 이름이 무엇인지, 값의 범위는 어떻게 되는지, 기본값은 무엇인지 등을 정의해서 알려주어야 합니다. (C++이나 Java에서 클래스 멤버 변수를 정의하는 것과 비슷합니다) 이러한 정보를 줄임말로 스펙(spec.)이라고 한다면, 속성을 추가한다는 건 다른 말로, 스펙으로 명시한 속성 정보를 클래스에 설치(install)하는 것을 의미합니다. 객체 인스턴스마다 속성의 실제 값(value)은 모두 다르겠지만, 어떤 속성이 있는지 그 속성은 어떻게 구성되어는지는 모두 동일하겠지요. (참고로 GObject 관련 API를 훑어보시면 정확히 모르더라도 지금 언급한 개념의 단어로 이루어진 API가 꽤 많은 걸 아시게 될 겁니다) 그렇기 때문에, 속성을 추가하는 작업은 클래스 초기화 함수에서 이루어집니다.

다음은 기존 예제에서 속성을 추가한 코드입니다. (변경된 부분만 보여드립니다)

**edc-host.c**

    /*...[snip]... */

    enum
    {
      EDC_HOST_PROP_0, /* ignore */
      EDC_HOST_PROP_NAME,
      EDC_HOST_PROP_ADDRESS,
      EDC_HOST_PROP_PORT,
      EDC_HOST_PROP_USER,
      EDC_HOST_PROP_PASSWORD
    };

    /*...[snip]... */

    static void
    edc_host_get_property (GObject    *object,
                           guint       property_id,
                           GValue     *value,
                           GParamSpec *pspec)
    {
      EdcHost *host = EDC_HOST (object);
      EdcHostPrivate *priv;

      priv = EDC_HOST_GET_PRIVATE (host);

      switch (property_id)
        {
        case EDC_HOST_PROP_NAME:
          g_value_set_string (value, priv->name);
          break;
        case EDC_HOST_PROP_ADDRESS:
          g_value_set_string (value, priv->address);
          break;
        case EDC_HOST_PROP_PORT:
          g_value_set_int (value, priv->port);
          break;
        case EDC_HOST_PROP_USER:
          g_value_set_string (value, priv->user);
          break;
        case EDC_HOST_PROP_PASSWORD:
          g_value_set_string (value, priv->password);
          break;
        default:
          /* We don't have any other property... */
          G_OBJECT_WARN_INVALID_PROPERTY_ID (object,
                                             property_id,
                                             pspec);
          break;
        }
    }

    static void
    edc_host_set_property (GObject      *object,
                           guint         property_id,
                           const GValue *value,
                           GParamSpec   *pspec)
    {
      EdcHost *host = EDC_HOST (object);
      EdcHostPrivate *priv;

      priv = EDC_HOST_GET_PRIVATE (host);

      switch (property_id)
        {
        case EDC_HOST_PROP_NAME:
          g_free (priv->name);
          priv->name = g_value_dup_string (value);
          break;
        case EDC_HOST_PROP_ADDRESS:
          g_free (priv->address);
          priv->address = g_value_dup_string (value);
          break;
        case EDC_HOST_PROP_PORT:
          priv->port = g_value_get_int (value);
          break;
        case EDC_HOST_PROP_USER:
          g_free (priv->user);
          priv->user = g_value_dup_string (value);
          break;
        case EDC_HOST_PROP_PASSWORD:
          g_free (priv->password);
