2009.07.24 22:05 책 정리/Modern C++ Design

이 테크닉은 여러 책에서도 소개 되어 있다. 쉽게 말해서 "컴파일 타임 상속 여부 체크" 기능이다.

어떻게 형변환과 상속 가능 여부를 알 수 있는가?
일반적으로 사람의 눈으로 "형변환과 상속 가능 여부" 를 알기 위해선 해당 소스코드를 직접 보면 된다. 마찬가지로 컴파일러도 "볼수 있게" 만들어주면 되는데, 형변환 또는 상속 가능 여부 컴파일러가 "볼 수 있게"를 할려면, 몇가지 트릭이 필요하다. 결론은 알 수 있다는 것이다.

이 방법은 객체가 복사 될 수 있다면 컴파일러가 알아서 복사 하는 원리를 이용한 것이다. 하지만 이것을 일일이 컴파일 해보면서 알아 갈려면 정말 힘든 작업 이다. 그래서 여러 선구자들은 sizeof 트릭을 개발하였고, 이 트릭이 나오게 된다.

참고로 sizeof 연산자는 컴파일 타임에 객체의 사이즈나 타입의 사이즈를 계산하는 연산자이다. 그래서인지 함수의 선언만 있어도, 리턴타입을 알 수 있기에, 함수의 리턴 타입의 사이즈를 알 수 있다.

만약 위에서 말한 함수가 "형변환, 상속 가능성" 여부를 판단 하여, 여부가 있다면 사이즈 1짜리 객체를 리턴하고, 다르다면 사이즈 2짜리 객체를 리턴한다면, 컴파일 타임에 "형변환, 상속 가능성"을 체크 할 수 있을 것이다.

하지만 아직까지 "어떻게 형변환과 상속 가능성을 체크 할 수 있을까?"의 원리는 설명하지 않았는데, ... 두구두구두구두궁!

그 원리는 C++에서 오버로딩된 함수들을 호출하는 원리를 이용하는 것이다.

C++ 에서 오버로딩된 함수들을 호출 할 때, 인자의 타입에 의하여, 가장 알맞는 함수가 선택되어 호출 된다.

C++ 에선 클래스가 서로 상속관계에 있을 때, 하위 클래스의 포인터는 상위 클래스의 포인터로 자동형변환 된다. 그래서 함수가 f( base* ) 이렇게, 상위 클래스의 포인터의 파라미터를 가진 함수라면, 알아서 하위 클래스의 포인터로 f(base *)를 호출 할 수 있다.

그러므로 다음의 시나리오가 완성 된다.

  1. 두개의 템플릿 파라미터를 받아, 첫번째 타입 T1의 포인터를 갖는 f( T1 *) 를 생성하고 T2 *를 넘기어서 호출한다. 이 함수의 리턴 타입의 사이즈는 1로 한다.
  2. C++ 함수 호출 매카니즘으 1번을 호출하지 못한다면 리턴 사이즈 2를 리턴하는 함수를 호출 한다.

음.. 여기서 다시 막히는 것이 있는데, 바로 2번이다., 1번은 C++ 오버로딩된 함수 호출 메카니즘에 의하여 알아서 된다지만, 2번은 함수 템플릿이면 되겠군 이라고 하고 구현해 보면, 전부 함수 템플릿만 호출 되어 진다. 그 이유는 함수 템플릿은 "가장 알맞는 함수"를 호출하기 때문인데, 무조건(지금은 무조건이란 표현이 맞다) f( T2 * )로 만들어 지기 대문에 절대 f( T1 * ) 이 호출 되지 않는 것이다.

결국 템플릿은 안된다. 하지만 아직 포기하기는 이르다. 왜!? C++ 에도 어떠한 인자도 받는, 가변 파라미터 함수 있기 때문이다. 이 가변 파라미터 함수는 오버로딩으로 쓸 수 잇으며, 오버로딩시 가장 낮은 수준의 선택순위(C++ 함수 호출 메카니즘이 함수군(오버로드된)에서 찾다 찾다 못찾으면 이녀석이라도 호출해 주는 순위)를 갖는다. 

가변 파라미터 함수는 이렇게 선언 할 수 있다.

자, 시나리오를 다시 짲으니, 코드로 만들어 보자.

음~ 잘 동작 한다. 원래 상속가능성을 체크 하려 했던 것이므로 상속에 대해서 몇가지 테스트를 해봤더니, 한가지 문제점이 있었다. 바로 void* 타입일 경우 메타함수(클래스)가 제대로 동작하지 않았던 것, 그래서 책을 펼처보니, #define 으로 void* 이 아닐 경우에만 동작하도록 되어 있었다.


휴~ 이것으로 상속가능 여부를 손쉽게 컴파일 타임에 알수 있게 되었다. 생큐! Modern C++ Design!




posted by 농사를 짓는 게임 프로그래머 최익필

댓글을 달아 주세요