C++이야기 스물아홉번째: Portable C++ Timer Class #1

Posted at 2008. 12. 8. 22:37 // in S/W개발/C++ 이야기 // by 김윤수


If you are an English speaker, then follow this link.
C++ Tips 29th: Portable C++ Timer Class #1

우리네 생활이 온통 시간에 둘러쌓여 있듯이, 응용프로그램부터 펌웨어에 이르는 프로그램도 시간에 둘러쌓여 있지요. 예를 들자면, 달력 프로그램은 시간과 연관되어 있는 약속과 할일들을 처리하고, 검색 로봇은 주기적으로 페이지를 긁어 오고, 멀티미디어 재생기는 제 시간에 맞춰서 데이터 저장소로부터 열심히 데이터를 끌어 오고, TCP/IP 위에서 동작하는 통신 미들웨어는 데이터 교환이나 요청한 연산에 대해 타임아웃을 처리하고, 디바이스 드라이버는 주기적으로 디바이스에 상태 체크 요청을 보냅니다. 이렇게 시간은 워낙 근본적인 개념이라 아주 작은 프로그램을 개발하는 경우를 제외한다면 대부분 타임아웃이나 주기적인 이벤트를 처리할 수 있는 라이브러리가 필요하게 됩니다.

그래서 이번과 앞으로 몇 번에 걸쳐 boost 라이브러리를 활용하여 타임아웃이나 주기적인 이벤트를 처리할 수 있는 타이머 클래스를 구현하는 방법에 대해 얘기하려고 합니다.

타이머와 연관된 기본적인 개념을 먼저 한 번 생각해 보죠.
  1. 타이밍 이벤트는 주기적으로 발생하거나 딱 한 번만 발생한다.
  2. 타이밍 이벤트가 발생하면 특정 작업을 처리해야 한다.
  3. 정확한 시간에 타이밍 이벤트가 발생하도록 해주는 일종의 구동기가 존재한다.
저는 이 정도 개념이 우선 떠오르네요.

그럼 먼저 첫번째 개념에서 어떤 클래스를 정의해야 하는지부터 시작해 볼까요? 다음과 같이 enum 값들로 두 가지 타입을 다 지원하는 클래스 하나를 정의할 수도 있을 겁니다.

class Timer {
public:
  typedef enum {
    PERIODIC_TIMER,
    ONE_SHOT_TIMER
  } Type;

private:
  Type m_type;
};


서로 다른 두 개념을 클래스 하나로 묶어 놓은 설계가 과연 좋은 설계일까요? 좀 더 주의깊게 생각해 보시죠. 이 설계대로 하면 생성자는 어떻게 작성해야 할까요?

#include <boost/date_time.hpp> // for time_duration

class Timer {
public:
  typedef enum {
    PERIODIC_TIMER,
    ONE_SHOT_TIMER
  } Type;

  Timer(Type type, const boost::posix_time::time_duration& td);

private:
  Type m_type;
};

'type' 인자의 의미는 분명하지만, 'td' 인자는 어떤 것을 뜻할까요? 보통 주기적인 이벤트는 주기(== 'td')라는 게 필요하고, 타임아웃 이벤트는 그 이벤트가 발생할 시간(== 현재 시간 + 'td')을 필요로 할테니... 'td' 인자가 뜻하는 바는 'type'에 따라 달라지겠죠? 그렇다면,  Timer::Timer() 을 구현한 코드가 아무래도 다음 코드에서 보시는 것처럼 'type' 값에 의존적인 코드가 될 것 같군요.

Timer::Timer(Type type, const boost::posix_time::time_duration& td)
{
  switch (type)
  {
  case PERIODIC_TIMER:
    ......
    break;

  case ONE_SHOT_TIMER:
    ......
    break;

  default:
    break;
  }
}

이렇게 되면 새로운 타입의 타이머를 추가할 때마다 이 생성자를 수정해야할 필요가 있다는 건 쉽게 눈치 채실 수 있으시겠죠? 상당히 심각한 유지보수 문제네요. 이것 말고도 다른 문제점들도 있습니다. 세번째 개념에서 드라이버 클래스를 정의할 필요성이 있다는 걸 알 수 있습니다.

class TimerDriver { ...... };

TimerDriver가 서로 다른 타입의 타이머 인스턴스들을 제대로 처리하려면 타이머의 타입을 알아야할 것입니다. 그러니 TimerDriver의 구동 멤버 함수는 당연히 타이머의 타입 정보를 필요로 하게 됩니다. 생각이 여기에 이르면 새로운 타입의 타이머를 추가할 때마다 TimerDriver의 구동 멤버 함수도 수정해야 한다는 걸 알 수 있게 됩니다.

마지막 문제점으로 주기적인 이벤트는 주기를 저장해야 할테고, 타임아웃 이벤트는 이벤트가 발생해야할 시간을 저장해야 한다는 점이 떠오르네요. 이걸 저장하기 위해 하나의 클래스에 두 개의 멤버 변수를 정의한다면 타이머 인스턴스의 타입에 따라서 낭비되는 공간이 생길 것입니다.

이상의 문제점을 고려해 보건데, 주기적인 이벤트와 타임아웃 이벤트를 위해 따로 클래스를 정의하는 게 더 좋은 설계라는 게 명백해 보입니다. 그렇지만 한편으로는 TimerDriver는 여전히 이 클래스들을 일관되게 처리할 필요가 있을 것입니다. 따라서 저는 공통의 부모 클래스로 Timer를 두고, 파생 클래스로 PeriodicTimer와 OneShotTimer를 두려고 합니다.

class Timer { ...... };

class PeriodicTimer : public Timer { ...... };

class OneShotTimer : public Timer { ...... };

그럼 이제 Timer, PeriodicTimer, OneShotTimer 각각 클래스에는 어떤 것들을 정의해야 할까요? 이 문제는 다음 글에서 알아보도록 하겠습니다. ^^