Archive for the ‘프로그래밍’ Category
Programming in Lua 2E 야매요약본
[옛날 블로그 글입니다. 2007/06/15]
[여전히 일본어 잘하고 싶음 -_-;;]
programminginlua2e_summary-muscly
Programming in Lua 2E 를 읽고나서 대략 정리한 내용.
팀에 PT 하려고 정리한 거라서 coroutine이나 thread, 객체지향프로그래밍 등등은 생략했다.
1시간 반 시간 잡고 했는데 반도 설명 못한듯 -_-;;
일본어 잘하게 되면 일본 개발자들 대상으로 해봐야지 ㅎㅎ
undname.exe
[옛날 블로그 글입니다. 2007/09/20]
[요새는 에러 메시지가 친절해져서 필요없을 수도..]
Visual Studio와 함께 설치되는 유틸 undname.
데코레이팅된 C++ 심볼의 이름을 언데코레이트 해주는 유틸리티.
[오늘의 문제]
Q. 분명히 제대로 lib 파일을 링크해주었는데, 왜 심볼을 찾을 수 없다는 링크에러가 나는 것일까?
[풀이 과정]
1. 링크에러 메시지를 살펴보니 다음의 심볼을 못찾고 있다.
?CreatePane@VSIDE@vsautomation@@YAPAUHVSOutputPane__@VSOutputPane@2@PAUHVSIDE__@12@PB_W@Z
2. lib 파일을 열어서 CreatePane으로 검색해보니 비슷한 심볼이 보인다. (아주 살짝 틀리다)
?CreatePane@VSIDE@vsautomation@@YAPAUHVSOutputPane__@VSOutputPane@2@PAUHVSIDE__@12@PBG@Z
3. 두 심볼을 undname 을 통해 원래의 모습을 확인해보니 차이가 명확하다.
아래는 실행 화면. 노란 부분이 각 심볼의 원래 모습.
|
C:\Documents and Settings\jp90637>undname ?CreatePane@VSIDE@vsautomation@@YAPAUH Undecoration of :- "?CreatePane@VSIDE@vsautomation@@YAPAUHVSOutputPane__@VSOutpu
Undecoration of :- "?CreatePane@VSIDE@vsautomation@@YAPAUHVSOutputPane__@VSOutpu |
4. 보아하니 lib를 만들때는 'wchar_t를 unsigned short로 다루기' 옵션으로 빌드되었고,
사용하는 쪽은 'wchar_t를 내장타입으로 다루기' 옵션으로 빌드되었다. (굵은 부분 참조)
[오늘의 교훈]
undname으로 사소한 링크 문제를 해결하자.
VC 2003에서 Win32 static library를 만드니 wchar_t를 unsinged short으로 다루더라.
반면에 MFC App는 wchar_t를 내장 타입으로 다루더라.
RTTI 관련 정보
[옛날 블로그 글입니다. 2007/11/29]
사내 C++ 강좌를 준비하던 중에 RTTI 구현을 좀 파고 들어봤다.
정리하고 나니 사내 강좌용으로는 좀 어려울 것 같아서
그냥 여기에 정래해 둬야 겠다 -_-;;
-
typeid() 연산자의 내부 코드의 일부 발췌 (VS7.1)
| 004283AE mov ecx,dword ptr [inptr] // intptr이 객체의 주소 004283B1 mov edx,dword ptr [ecx] // vfptr의 값을 꺼낸다. 즉, vftable의 주소 004283B3 mov eax,dword ptr [edx-4] // vftable 바로 앞의 4바이트를 읽는다. RTTI 정보 주소 004283B6 mov dword ptr [pCompleteLocator],eax // RTTI 정보 주소 얻기 성공!! |
결론은, vftable 바로 위에 RTTI 정보 (RTTI Complete Object Locator)의 주소가 보관된다.
-
RTTI 정보의 구조 (VS7.1)
아래에서 Mac 은 클래스의 이름이다.
그리고 pTypeDescriptor ( RTTI Type Descriptor)가 바로 type_info 클래스가 된다.
결론은 RTTI 정보에서 12바이트 옵셋에 type_info 클래스의 주소가 보관된다.
-
이 정보를 토대로 나만의 type_id 연산자 만들기.
확인을 위한 것 뿐이므로 범용적이지는 않다. (포인터 타입 안됨 ㅎ )
|
#include <typeinfo>
typedef unsigned int __w64 DWORD; using namespace std;
class Computer {
class Mac : public Computer {};
template < class T > const type_info& my_typeid( T& obj )
template < class T > const type_info& my_typeid( T* ptr )
cout << typeid( pm ).name() << "\n";
cout << my_typeid( *pm ).name() << "\n"; return 0; >> 결과 class Mac * |
가상함수 호출시 this 포인터의 조절
[옛날 블로그 글입니다. 2007/11/29]
멤버함수 포인터를 조사하다가 생각치 못한 점을 발견했다.
가상함수를 호출할 때도 this 포인터를 적절히 조절해야 한다는 점이다.
그런데, MSVC는 도대체 어디서 this 포인터를 조절할까?
|
class Base1 { public: virtual void Func1(); int base1; };
class Base2 { public: virtual void Func2(); int base2; }
class Derived : public Base1, public Base2 { public: virtual void Func2() { ++ derived; } int derived; }
Derived d; Base2* pb2 = &d; pb2->Func2(); |
pb2와 &d 는 다른 값이니까, 마지막 줄에서 this 포인터를 전달할 때 pb2에서 8바이트를 빼서
넘겨줘야 한다고 생각했지만, 어디에도 그런 코드는 없었다.
조사해보니, 가상함수의 코드를 생성할 때 아예 this 포인터가 부모기준으로 넘겨오겠거니..
생각하고 어셈블리 코드를 만들어준다.
-
하지만 항상 그렇게 할 수 있는 건 아니다.
아래의 예라면 위의 수법이 통하지 않는다.
|
class Base1 { public: virtual void Func(); int base1; };
class Base2 { public: virtual void Func(); int base2; }
class Derived : public Base1, public Base2 { public: virtual void Func() { ++ derived; } int derived; }
Derived d; Base2* pb2 = &d; pb2->Func(); |
Derived::Func() 를 Base1의 this를 기준으로 어셈블해주면
Base2를 통해서 호출할 때는 필히 this 포인터의 조절이 필요하다.
MSVC는 이런 경우 this포인터값을 필요한 만큼 조절해서 Derived::Func()를 호출하는
코드 청크를 만들고, 이 청크의 주소를 Derived::Base2::'vftable'에 넣어둔다.
|
@ILT+2080(?Func@Derived@?9??main@@9@W7AEXXZ): |
청크 코드를 보면 this포인터에서 8을 빼고 실제 함수를 호출한다.
|
[thunk]:`main'::Derived::Func`adjustor{8}' (void): |
사원수 Quaternions
[옛날 블로그 글입니다. 2007/12/19]
출처 : Tricks of the 3D game programming gurus p.325
[사원수란?]
복소수(Complex Numbers)처럼 실수부와 허수부로 구성된 수.
허수부가 i, j, k 세개다.
q = q0 + q1*i + q2*j + q3*k
( q는 사원수 )
( i^2 = j^2 = k^2 = i*j*k = -1 )
( q0, q1, q2, q3 은 실수 Real Numbers )
이렇게도 표현
q = q0 + qv, where qv = q1*i + q2*j + q3*k
[사원수의 켤레(Conjugate)]
켤레수는 이차방정식의 두 근을 부르는 이름 인 것 같다.
무리수(Irrational Numbers) 부분이 +/- 로 부호만 다르게 된다.
복소좌표라면 허수 부분(Imaginary part)이 +/-로 부호만 다르게 된다.
다음은 사원수와 그의 켤레수.
q = q0 + q1*i + q2*j + q3*k = q0 + qv
q* = q0 - q1*i - q2*j - q3*k = q0 - qv
다음은 켤레수의 곱. 실수부만의 제곱이 된다.
q * q* = … 중간 생략 = q0^2 + q1^2 + q2^2 + q3^2
[놈(Norm)]
놈이란 벡터 공간 안의 모든 벡터에 양수의 길이나 크기를 부여하는 함수를 말한다.
2차원 유클리드 공간(Euclidean space) R^2에서의 유클리드 놈(Euclidean norm)이 좋은 예.
왜, 2차원 데카르트 좌표계(Cartesian coordinate system)에 벡터를 매핑 시키고 그 길이를
재는 방식 말이다.
위에 말을 정리하려고 열심히 웹서핑을 했지만, 위키피디아가 제일 나은 듯.
http://en.wikipedia.org/wiki/Norm_%28mathematics%29
사원수의 놈은 4차원 유클리드 공간 R^4에서의 벡터의 길이를 재는 것처럼 할 수 있다.
( 실수부, i, j, k 를 4차원의 축에 매핑시킨다고 생각하면 될 듯.. )
다음은 사원수와 놈.
q = q0 + q1*i + q2*j + q3*k = q0 + qv
|q| = sqrt(q0^2 + q1^2 + q2^2 + q3^2)
= sqrt( q * q* ) <- 이렇게 되는 이유는 위의 켤레수 참조
|q|^2 = (q0^2 + q1^2 + q2^2 + q3^2) = ( q * q* )
[켤레와 역원(Inverse)]
역원의 정의는 당근 이렇게. q-1 은 q 빼기 1 아님 -_-;;
q * q-1 = q-1 * q = 1
위 식의 양변에 켤레를 곱하면 재밌는 결과가 나온다.
q-1 * q = 1
=> ( q-1 * q ) * q* = 1 * q*
=> q-1 * ( q * q* ) = q*
=> q-1 * |q|^2 = q* <– 위에 놈(?) 참조
=> q-1 = q* / |q|^2
그래서 q 가 단위 사원수(unit quaternion)가 되면 |q|^2 = 1 이 되어서
아래와 같은 식이 나온다.
q-1 = q*, where |q|^2 = 1
[사원수를 사용한 회전]
사원수를 사용하는 것이 회전 행렬을 사용하는 것 보다 빠르고,
짐볼락(Gimbal lock)이 없는 것으로 유명.
그리고 회전시의 보간에도 유용하다는데, 아직 여기까지는 공부가 부족 -_-;;
벡터 v1을 기준으로 θ만큼 회전하는 사원수를 만들고 싶다면 이렇게.
q = cos(θ/ 2 ) + sin(θ/ 2 ) * v1
이렇게 준비한 사원수를 가지고 벡터 v2를 회전시키려면 이렇게.
우선은 벡터를 사원수 형식으로 바꿔야 하는데, 실수부는 0으로 두면 된다.
v2 = <x,y,z> 라면 vq = <0,x,y,z> 처럼 말이다.
왼손 좌표계라면
시계방향 회전은, vres = q * vq * q*
반시계방향은 , vres = q* * vq * q
오른손 좌표계라면 위의 식을 반대로 써주면 된다.
오일러(Euler) 회전각을 알고 있는 경우라면,
각 축에 대한 회전 사원수를 만들어서 곱해주면 된다.
qfinal = qx * qy * qz <- qx, qy, qz의 순서는 바뀌어도 무관
[DirectX 와 사원수]
뭐 다양한 사원수 지원이 있겠지만, 위에서 해본 것을 DX로 구현해본다면.
x, y, z가 각 축의 회전각(라디안)이라고 가정한다면
사원수는 이렇게 구할 수 있다.
var quat = Quaternion.RotationYawPitchRoll( y, x, z )
( VS 2008에서 Managed DirectX를 쓴 경우이다. )
바로 사원수를 행렬로 바꿀 수도 있다.
var mat = Matrix.RotationQuaternion( quat );
그런데 Matrix.RotationYawPitchRoll()을 써도 같은 행렬이 나오는 듯 -_-;;
