[C++] Template Summary – 1/3

이 문서는 제가 개인적으로 템플릿을 이용해 코딩할때 참고할 만한 자료를 기재해 놓은 것입니다. 템플릿에 대한 전반적인 내용이 아닌 정리라는 점을 염두해 주시길 바랍니다.

먼저 함수 템플릿의 예이다. Type에 상관 없이 두개의 인자를 받아 이 중 최대값을 반환하는 함수 템플릿의 정의.

tpl_decl.h 라는 파일안에 다음과 같은 코드를 정의 한다.

template T max_Tpl(T a, T b)
{
    return (a>b)?a:b;
}

typename 대신에 class를 써도 상관없지만, Type이라는 분명한 의미를 제공한다는 점에서 개인적으로 typename을 선호한다. 이 max_Tpl이라는 템플릿 함수는 헤더파일만…. 존재해야한다는 점이… 늘 걸리긴 했지만, 이제 이런 걸림은 그냥 포기하고 받아 들이기로 했다. 구현을 별도의 cpp 파일로 분리하기 위해 export 라는 예약어가 제공된다고 하나.. Visual C++ 2008에서도 예약어로만 선언되어 있을 뿐 아직 구현되어 지원하지 않는다.

위의 max_Tpl이라는 함수의 활용은 아래와 같다.

#include "tpl_decl.h"

int _tmain(int argc, _TCHAR* argv[])
{
    double max = max_Tpl(100.0, 200.0);

    ...

위의 코드에서 인자가 실수라는 점이 명확하므로 max_Tpl을 간단히 max_Tpl이라고 해도 되나, 템플릿 함수라는 점을 명확하게 하기 위해 을 붙이는 것을 개인적으로 선호한다.

정의한 max_Tpl이라는 함수에 대해 모든 Type에 대해 작동한다고 보장할 수는 없다. 그 하나의 경우로 문자열 타입에 대한 경우인데, 이 경우 문자열 타입에 대해서는 좀더 특별하게 그 구현을 제공해야한다. 그 구현은 마찬가지로 tpl_decl.h 파일에 아래의 코드를 추가한다.

template<> const char* min_Tpl<>(const char* a, const char *b)
{
    return strcmp(a, b)
}

이를 함수 템플릿 특수화라 한다.

정의되지 않은 클래스 맴버 함수 호출하기

당연이 인스턴스로 생성된 클래스의 맴버함수를 호출하기 위해서는 해당 클래스를 정의하고 있는 헤더 파일을 포함해야 하지만, 헤더 파일을 포함하지 않고 클래스의 맴버함수를 호출해야할 경우가 있다. 즉 정의되지 않는 클래스 인스턴스의 맴버 함수를 호출해야한다.

어떻게 할 수 있을까? 해답은 클래스 맴버 함수 포인터를 사용하는 방법이다.

먼저 사용하는 코드부분에서 호출해야하는 클래스를 선언하고 그 클래스에서 사용해야할 맴버 함수의 포인터를 선언한다.

C사용하는클래스의 헤더파일에…

class C사용될클래스;
typedef void (C사용될클래스::*맴버함수)(void);

그리고 C사용하는클래스의 맴버변수로써 C사용될클래스의 인스턴스와 맴버함수에 대한 변수를 추가한다.

private:
    맴버함수 funcPtr;
    C사용될클래스* p사용될클래스인스턴스;

public:
    void Set(맴버함수 funcPtr, C사용될클래스* pInstance) {
        this.funcPtr = funcPtr;
        p사용될클래스인스턴스 = pInstance;
}

이제 실제로 C사용될클래스의 멤버함수를 사용하는 방법은 아래와 같다.

((*p사용될클래스인스턴스).*funcPtr)();

여기서 주목할 것은 앞에서도 언급했지만 어디서도 C사용될클래스에 대한 헤더파일을 포함하지 않았다는 점이다. 즉, C사용하는클래스는 단지 C사용할클래스를 모르며, 단지 C사용할클래스에서 꼭 필요한 것만 알고 있다는 것이다.

꼼꼼한 독자라면 직감했겠지만, C사용하는클래스의 funcPtr과 p사용될클래스인스턴스는 어떤식으로 값을 설정해야하는가라는 문제가 생긴다.

이것은 C사용될클래스에서 C사용할클래스의 Set함수를 호출해주면 된다. 즉 C사용될클래스의 정의부 어디에선가 다음과 같은 형식의 코드(한가지 예일뿐..)를 호출한다.

p사용하는클래스 = new C사용하는클래스();
p사용하는클래스->Set(&C사용될클래스::Function, this);

사실, 이런 기술(정확히, 솔직이 말한다면 편법)이 필요한 이유는 두개의 클래스가 서로 상호참조를 하는 경우이다. 이런 경우는 서로의 헤더파일을 포함해서 쉽게 구현할 수 있는 경우가 대부분이지만, 만약…. 이 두개의 클래스가 서로 완전이 다른 개념의 개발 프로젝트인 경우에는 예외인데, 예를 들어서 하나의 클래스는 ATL 프로젝트에 있고, 다른 하나는 일반적인 Generic C/C++ 프로젝트인 경우, 단순이 헤더파일을 포함할 경우 서로의 개발환경을 이해하지 못하므로 엄청나게 많은 에러 메세지를 쏟아 낼 것이고, 머리속이 하얗게  되는 것을 경험할 것이다. 이 편법을 사용하지 않는 것이 제대로 개발하고 있다는 증거인지라, 사용하지 않기를 바라지만 꼭 필요한 경우라면 요긴할 것으로 판단되어 글로 정리하여 남긴다.