this adjustment
MSVC 컴파일러에서 상속시 발생할 수 있는 this 포인터 문제
Callback 인터페이스를 제공하는 클래스를 디자인하던 중 발생한 this 포인터 문제에 대해서 정리하기 위해서 작성하였음. 빌드 환경은 윈도우 + MSVC컴파일러.
this 포인터의 잘못된 참조
목표는 몇 가지 콜백함수를 등록하고, 특정 상황에 맞게 호출할 수 있는 인터페이스를 제공하는 클래스를 디자인하는 것이다. 여러 방법 중 해당 클래스를 상속하여 인터페이스를 이용하는 방식을 선택하였다.
먼저 상속관계에서 콜백을 호출하는 간단한 테스트를 진행하였고, 그 과정에서 문제를 발견하였다.
1 | class ICallback |
위 코드에서 Test
클래스는 ICallback
클래스 말고도 다른 클래스들을 상속할 수 있다. 이때 경우에 따라서 Test::foo()
의 결과가 달라진다. 어떤 경우에는 정확한 값인 10을 출력하지만 다른 경우에는 쓰레기값을 출력한다.
이러한 결과는 this
포인터를 잘못 참조할 경우 나타난다. 즉, t
의 this
와 (ICallback*)t
의 this
가 서로 다른경우 문제가 발생한다.
예상되는 문제의 원인은 ICallback::CallProc()
에서 콜백을 호출하는 과정에서 this
포인터의 implicit down casting 이 이뤄진다. 이 부분에서 문제가 발생하는 것 같다.
버그?
이 과정에서 신기한점을 발견하였다. 멤버함수 포인터를 struct
, class
, union
중 하나의 멤버로 전방선언을 하면 콜백호출과정에서 this
가 정확하게 조정된다는 것이다.
1 | class ICallback; |
왜 단순한 전방선언만으로 문제를 해결할 수 있는지는 아직도 이유를 모르겠다.
this 포인터
클래스 인스턴스의 this
포인터가 가리키는 대상은 클래스 구조에 따라 달라진다. 하위 클래스의 this
포인터는 항상 클래스 데이터의 시작 메모리 주소이지만, 클래스 구조에 따라 메모리에 데이터가 적층되는 순서와 종류가 다르기 때문에 그 대상이 달라진다. 일반적인 경우를 제외하고 상속구조에서의 this
포인터가 참조하는 메모리는 몇 가지 규칙에 의해 정해진다.
MSVC에서 실험한 결과는 다음과 같다.
1 | class BaseA |
BaseA
,BaseB
둘 다vtable
이 없는 경우에는 상속의 역순으로 메모리에 적층된다.
1 | // ┌───────────────┐ ◄──── this of d and ba |
BaseA
혹은BaseB
가vtable
을 가지고 있다면 해당 클래스는 후순위에 메모리에 적층된다. 만약 둘 다 가지고 있다면 1에서처럼 상속 순서에 영향을 받는다.
1 | // ┌─────────────────┐ ◄──── this d and ba |
이 예제에서 처럼 this
포인터를 d
와 ba
가 공유하고 bb
는 다른 위치를 참조한다. 이렇기 때문에 알맞은 vtable
을 참조할 수 있다. 그리고 만약 필요하다면 thunk
를 통해 this
위치를 조정한다.
this adjustment
다시 문제로 돌아와서, 콜백호출과정에서 this
의 잘못된 참조를 해결하기 위해서는 this
를 강제로 조정해줘야 한다는 생각이 들었다. 위에서 본것과 같이 this
를 명시적으로 캐스팅한다면 그 값이 정확하다는 점을 통해서 문제를 해결하였다.
1 | class ICallback |
이제 단순 호출에는 문제없지만, 내가 디자인하고자 했던 인터페이스 클래스는 콜백리소스를 직접 관리해야했다.
해당 콜백을 올바르게 호출하기 위한 this adjustment 정보와 콜백을 함께 관리하고, 콜백호출시 해당 정보를 이용하여 this
를 알맞게 조절하도록 하였다.
1 | class ICallback |
콜백호출 시 this
의 값이 조정되는 up/down casting 대신 값이 변하지 않는 type casting 을 사용하여 this
의 값을 직접 조정하였다.
이제 기존에 문제가 발생하던 상황에도 문제없이 콜백을 호출한다.
굳이 콜백을 호출할 때 마다 std::ptrdiff_t
로 this
를 조정하는 대신 직접 (T*)this
를 저장해서 사용해도 될것이다.
this adjustment