리눅스 GNU C++에서 타이머 컴포넌트를 만들기

[목차(도우미)]
리눅스(Linux)에서 단순히 GNU C++(흔히 gcc, GNU Compiler Collection 라고 부르는 C++)에서 인용되는 시스템 함수(System call)를 호출하여 타이머(Timer)를 사용하는 것도 좋은 방법이다.

타이머 컴포넌트

한번 만든 소스코드를 재활용하기 위한 객체 지향 기법(Object-oriented design)을 좋아하는 사람이라면 타이머 컴포넌트(Component)를 만들어 두고 싶어진다.

GNU C++에서 사용되는 함수는 signal(SIGVTALRM, handler)를 적용해서 이벤트 핸들러(Event handler)를 등록해 두면 되는데 handler 함수를 클래스의 멤버 함수로 선언하면 나중에 계승(inherit)처리를 하려고 설계한다면 제대로 컴파일이 통하지 않는다.

기본 샘플 : 소스를 복사해서 사용하는 방식

복사해서 개발하는 경우에 자주 보는 샘플 소스.
방식 자체가 나쁜 것은 아니지만 재활용하려면 여전히 소스코드를 복사해서 처음부터 써나가야 한다.

  1. void handler(int signo){ printf("handler called-----"); }
  2. int main(){
  3.     // ...
  4.     // this is OK typical sample source
  5.     signal(SIGALRM, handler);
  6.     struct itimerval delay;
  7.     delay.it_value.tv_sec = 5;
  8.     // ...
  9.     int ret = setitimer(ITIMER_REAL, &delay, NULL);
  10.     // ...
  11. }

클래스를 선언한 방식 : 컴파일 에러

안타깝게도 컴파일 조차 안된다.
정적 메소드(static) 로 선언해 주어야 된다는 에러가 발생한다. 따라서 해결 방법은 함수 포인터를 사용하여 호출처에서 직접 함수를 넘겨 주는 것이다. 예를 들어  생성자에서 함수 포인터를 넘겨 주는 것이 가능하다.
  1. class Timer {
  2.   public:
  3.     Timer();
  4.     ~Timer();
  5.     void TickEvent(int signo){printf("called...")};
  6.     // ...
  7. };

  8. Timer::Timer(){
  9.     // ...
  10.     // this is compile NG
  11.     signal(SIGALRM, TickEvent);
  12.     struct itimerval delay;
  13.     delay.it_value.tv_sec = 5;
  14.     // ...
  15.     int ret = setitimer(ITIMER_REAL, &delay, NULL);
  16.     // ...
  17. }

클래스 생성시에 함수 포인터를 넘겨주는 방식

클래스의 인스턴스 생성시에 미리 준비한 이벤트 핸들러를 인수로 넘겨 주어야 한다.
생성시에 핸들러를 넘겨주어야 한다는 점에서 사용시 제약이 있다. 독특한 사용법이라고나 할까.
  1. class Timer {
  2.   public:
  3.     Timer(void (*TickHandler)(int));
  4.     ~Timer();
  5.     // ...
  6. };

  7. Timer::Timer(void (*TickHandler)(int)){
  8.     // ...
  9.     // this is nice to compile but...
  10.     signal(SIGALRM, TickHandler);
  11.     struct itimerval delay;
  12.     delay.it_value.tv_sec = 5;
  13.     // ...
  14.     int ret = setitimer(ITIMER_REAL, &delay, NULL);
  15.     // ...
  16. }


클래스의 이벤트를 선언한 방식

이벤트를 정의하는 방법이 C++에서는 파스칼(Delphi, Pascal)에서 처럼 깔끔하게 보이지 않지만, 이번 경우는 이벤트로 정의하는 것이 낫다.
  1. class Timer {
  2.   public:
  3.     Timer();
  4.     ~Timer();
  5.     // event property declaration
  6.     void (*TickEvent)(int);
  7.     // ...
  8. };

  9. Timer::Timer(){
  10.     // ...
  11.     // this calles event procedure when signal occurs
  12.     signal(SIGALRM, TickEvent);
  13.     struct itimerval delay;
  14.     delay.it_value.tv_sec = 5;
  15.     // ...
  16.     int ret = setitimer(ITIMER_REAL, &delay, NULL);
  17.     // ...
  18. }

함수 포인터로 구현한다는 것 자체가 델파이식으로 말한다면 이벤트 속성(Event Property)를 구현하는 것이다. 계승에 의한 처리 구현과 함수 포인터에 의한 처리 구현의 방식의 차이는 있지만 본질적으로 처리를 클래스에서 따로 분리하는 것은 같다고 할 수 있다.
GNU C 에서 구현하는 예제
(단지 손으로 책에 나온 내용을 다시 입력하기가 번거로와서 기존 공개 페이지를 연결해 둔다.)
http://www.codeguru.com/forum/showthread.php?t=356101

by 금메달.아빠 on 2011. 4. 13. 02:26