[C 언어] 객체 지향 C 스타일 (Object Oriented C programming style)
2020. 4. 21. 00:29개요
- 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 *this, int 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 *this, int 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 |