반응형

개요

  • C에서는 객체지향적으로 구현할 수 없을까?
  • 대표적인 객체 지향언어(C++, C#, Java) 스타일이 C에서는 어떻게 적용할 수 있는지 알아본다.
  • 객체 지향적인 C program을 구현해본다.
  • 대표적인 Object Oriented C style을 알아본다. (Strongswan, xine project 기반)

 


개념

  • 객체 지향 디자인으로 설계하려면 함수 포인터들을 사용해야함
  • Class의 Interface들은 함수포인터를 멤버로 가진 구조체로 정의되어야한다.
  • 구현은 구조체를 확장하여 이루어짐. (그 Interface를 첫 번째 구조체 멤버로 포함하여)

즉, 함수포인터들을 이용해서 class의 interface를 구현하고.

Class의 구현은 그 함수포인터들이 담긴 구조체를 첫번째 멤버로 포함시킨다.

따라서 구조체 2개가 필요하다!

 

(참고 : 이 코딩스타일은 Strongswan, xine project 스타일 기반으로 작성했습니다)

 

 

타입 보호

  • 각각의 method에 this 파라미터의 캐스팅 없이 타입을 맞추기 위해 특별한 매크로를 사용할 것임. (METHOD, INIT 매크로)
  • METHOD 매크로는 public과 private 인터페이스를 자동적으로 호환되는 메서드 구현을 가능하게함.
  • INIT 매크로는 객체를 초기화함. (읽을 수 있는 지정된 Initializer를 사용하여) 이것은 명시적으로 초기화하지않은 모든 멤버들은 0으로 초기화해줌. (그냥 구조체 선언하면 쓰레기값이 들어감)

 

 

예제

아래의 나오는 설명과 예시는 xine docs 기반이면서 Strongswan SW에서 더 좋게 변형함.

    Class

첫번째, Interface의 기능을 하는 구조체.

이 구조체는 함수포인터들과 public member를 가지게 된다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
typedef struct my_stack_t my_stack_t;
 
struct my_stack_t {
    /**
     * Method "push" with one parameter and no return value
     *    
     * @param i    element to push
     */
    void (*push)(my_stack_t *thisint i);
 
    /**
     * Method "add" with no parameters and no return value
     */
    void (*add)(my_stack_t *this);
 
    /**
     * Method "pop" with no parameters (except "this") and a return value
     *
     * @return    popped element
     */
    int (*pop)(my_stack_t *this);
};
 
/**
 * Constructor
 *
 * @return    instance of my_stack_t
 */
my_stack_t *my_stack_create();

 

 

두번째, 실질적인 Class 역할을 하는 구조체.

이 구조체는 private member 변수 및 함수를 추가할 수 있다.
( 구조체의 public 멤버 변수는 method 안에 들어가 있다. (엄밀히 말하면 interface들의 method) )

 

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
typedef private_my_stack_t private_my_stack_t;
 
struct private_my_stack_t {
 
    /**
     * Public interface
     */
    my_stack_t public;
 
    /**
     * Internal stack items
     */
    int values[MAX_STACK_SIZE];
 
    /**
     * Number of items
     */
    int stack_size;
};

 


    메서드

METHOD 매크로를 사용함으로써 각각의 메서드는 static method의 성질로 구현되어진다. 그 메서드들은 _[method_name] 이름으로 정의된다. (생성자에서 사용하기 위해 public 성질로 정의됨)

void push(i) 함수는 다음과 같이 구현된다.

my_stack_t -> 종속된 인터페이스 이름

push -> 함수명

void -> 반환형

private_my_stack_t -> private한 멤버를 저장하는 객체

int i -> 전달된 인자

여기서 실제적인 전달된인자는 i, 즉 1개 뿐인걸 기억하자.

1
2
3
4
5
METHOD(my_stack_t, push, void,
    private_my_stack_t *thisint i)
{
    this->values[MAX_STACK_SIZE - ++this->stack_size] = i;
}

 

 

 

 

    생성자

마지막으로 생성자를 구현할텐데 생성자는 INIT 매크로를 사용한다.

이 INIT 매크로는 private한 struct을 할당한다. 그리고 함수 포인터들을 채우고 default 값들을 채운다. (지정된 initializers를 사용함으로써)

여기서 생성자는 public성질을 갖게된다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
my_stack_t *my_stack_create()
{
    private_my_stack_t *this;
 
    INIT(this,
        .public = {
            .push = _push,
            .add = _add,
            .pop = _pop,
        },
        /* uninitialized fields are automatically set to zero */
    );
 
    /* return public part */
    return &this->public;
}

 

이제부터

my_stack_t stack = my_stack_create();

stack.[public한 함수명]();

을 통해서 객체처럼 쓸 수 있게된다.

 

 

 

매크로 METHOD, INIT을 알고 싶다면 아래의 링크를 들어가보자..

(참고로 저것을 구현하기 위해선 METHOD, INIT의 매크로가 정의 되어있어야 한다!, 아래에서 소스 복사붙여넣기 할 것, 하지만 METHOD와 INIT매크로의 대략적인 개념과 어떻게 쓰는지만 알아도 충분히 구현가능하다. 자세하게 어떻게 정의되었는지 알고 싶다면 아래의 링크 클릭)

https://github.com/strongswan/strongswan/blob/master/src/libstrongswan/utils/utils/object.h

 

 

다음 시간엔 이것을 활용하여 STACK을 구현해보자.

 


Strongswan 관련한 소프트웨어 다루다가 흥미로워서 글을 써봅니다.

 

참고

https://wiki.strongswan.org/projects/strongswan/wiki/ObjectOrientedC

strongswan OOC 참고문서

https://sourceforge.net/projects/xine/#id324430

xine project 참고문서

 

반응형

'C > C' 카테고리의 다른 글

[C] Error 상수 error.h  (0) 2021.06.08
Char형 문자열 배열을 HEX dump 뜨기  (0) 2021.05.12
정리  (0) 2017.10.23
초간단 static 변수 이해하기  (0) 2017.10.19
전역변수와 void함수 이해하기  (0) 2017.10.19