C++Builder Programming Forum
C++Builder  |  Delphi  |  FireMonkey  |  C/C++  |  Free Pascal  |  Firebird
볼랜드포럼 BorlandForum
 경고! 게시물 작성자의 사전 허락없는 메일주소 추출행위 절대 금지
C++빌더 포럼
Q & A
FAQ
팁&트릭
강좌/문서
자료실
컴포넌트/라이브러리
메신저 프로젝트
볼랜드포럼 홈
헤드라인 뉴스
IT 뉴스
공지사항
자유게시판
해피 브레이크
공동 프로젝트
구인/구직
회원 장터
건의사항
운영진 게시판
회원 메뉴
북마크
볼랜드포럼 광고 모집

C++빌더 Q&A
C++Builder Programming Q&A
[66247] Re: 컴파일러와 개발환경의 내부적인 측면을 살펴 보면...
빌더 [] 1360 읽음    2012-02-13 00:20
왕초보와코드 님이 쓰신 글 :
: C++빌더에서 사용하는 VCL컨트롤들은 델파이로 만들어 진 것으로 알고 있습니다.
:
: 그리고 추가로 설치하는 컴포넌트들도 사실 델파이로 만들어진 것이 많이 있죠.
:
: 갑자기 의문이 생기는데, 추가 설치하는 컴포넌트들은 델파이로 코드가 만들어져 있고,
:
: 가져와서 직접 컴파일한후, 인스톨해서 사용하는 경우가 많이 있는데, 이런 점으로 봐서
:
: 분명 빌더는 델파이 코드를 컴파일하는 능력이 있다는 이야기 입니다.
:
:
: 그렇다면,
:
: 1) 델파이 제품 또는 Rad Studio와 C++빌더 단일제품의 차이점은 무엇일까요?
:
: 2) 진짜 C++빌더만 가지고도 델파이 코딩이 가능한가요?
:
:
: 해보신 분 있나요?







답변...


이 문제는 델파이로 짜여진 VCL 소스코드를 C++ 로 완전하게 포팅하는게 가능하냐 하는 것과도 연관이
있는 부분이라, 먼저 이 부분 부터 짚어 보겠습니다.

델파이 개발환경의 강점은 Rapid Application Development 환경이란 거고, Form Designer 를 통해서
VCL 콘트롤을 동적으로 생성하고, Object Inspector 로 Property 값을 바꾸기도 하고, 폼에서 삭제하면
객체를 소멸 시키기도 하는 건데요.

이렇게 Design Time 시에 Dynamic 한 객체 사용이 가능한 것은 델파이 컴파일러가 생성하는 확장된
RTTI 정보 때문 입니다. 예를 들어 보죠.

Design Time Package 의 코드 프레임은 예를 들면 다음과 같은 식으로 구성 됩니다.


namespace MyDesignTimeComponent
{
    void __fastcall PACKAGE Register()
    {
         TComponentClass classes[1] = {__classid(TMyButton)};
         RegisterComponents(L"MyComponent", classes, 0);
    }
}




패키지 자체에서는 컴포넌트를 생성하는 코드 부분이 없습니다. 그저 위와 같은 식으로 TMyButton
클래스의 Type 정보만 RegisterComponents 함수로 넘겨줄 뿐인데요..

컴파일 후.. Register 함수는 .BPL 파일의 Export 섹션에 엔트리로 노출되게 됩니다.

그렇기 때문에 RAD IDE 개발환경이 Design Time Package 파일을 로드할 때... .BPL 파일에서 정의된
Register 펑션을 호출 할수 있게 되죠.

RegisterComponents 펑션은 System.Classes.pas 에 아래와 같은 식으로 정의 되어있습니다.


procedure RegisterComponents(const Page: string;
  ComponentClasses: array of TComponentClass);
//  const ComponentClasses: array of TComponentClass);
begin
  if Assigned(RegisterComponentsProc) then
    RegisterComponentsProc(Page, ComponentClasses)
  else
    raise EComponentError.CreateRes(@SRegisterError);
end;





여기서 RegisterComponentsProc 는 이 유닛에서 nil 값으로 되어있는 함수 포인터 입니다.
그런데 VCL 소스코드를 뒤져 보면... nil 값을 갖고있는 RegisterComponentsProc 에 함수의
주소를 대입해 주는 코드는 어느 곳에서도 전혀 보이지 않습니다.

사실상 RegisterComponentsProc 는 rtl160.bpl 파일에서 ord 넘버 4615 를 갖는  엔트리로
Export 되어 있습니다.(Embarcadero 애들이 그런식으로 컴파일 해서 배포를 했다는 거죠.)
그렇기 때문에 개발환경의 패키지 로더가 RegisterComponentsProc 함수 포인터의 주소를 알수 있고
Startup 과정에서 IDE 개발환경의 패키지 로더가 자신의 어느 부분의 주소 값으로  이 함수포인터를 패치
하게 됩니다.

RegisterComponentsProc 함수포인터가 호출되게 되면, 결과적으로 IDE 환경의 패키지 로더 내부함수
가 호출되게 되고, TMyButton 클래스의 ClassType 정보가 이런 식으로 패키지 로더(폼 디자이너)에
알려지게 됩니다.

__classid(TMyButton) 은 컴파일러가 생성해준 해당 클래스에 대한 TypeInfo 테이블에 대한 포인터를
리턴하는데(테이블의 다양한 섹션 중에서 VMT를 zero 베이스로 사용 합니다)... TypeInfo 테이블에 대한
구체적인 자료구조와 RTTI, VMT 테이블 과의 연관 관계는 언제 기회가 되면 강좌를 통해서 다뤄 보도록
하고요.

Form Designer 가 다이나믹 하게 VCL 콘트롤을 생성하는 실제적인 내부 코드를 슈도 코드로 나타내
보면 다음과 같습니다.

classType은 Dynamic Function Table, VMT, Class Name, Size, Property Name 등의 각종 정보
로 구성되어있고, TObject::NewInstance, TObject::FreeInstance 등의 델파이에서 Root Class 로
사용되는 TObject 에서 정의된 함수들에 대한 주소 정보도 갖고 있습니다. __classid는 이와 같은
정보 테이블의 위치를 알려줍니다. (이 부분도 다음에 강좌를 통해서 자세하게 다뤄보는 기회를 가져
보도록 하죠)

간략하게 폼디자이너의 내부코드를 슈도코드로 나타내 보면...


// 패키지 로딩과정에서 얻은 클래스 타입 정보.
classType = __classid(TMyButton)   
                         
// 클래스 객체를 생성하기 위해 메모리를 할당하고, Self 포인터를 VMT 테이블로 
// 디퍼런싱 하게 되지만 아직 constructor가 호출되지는 않은 단계

TComponent *instance = classType.NewInstance 


// TForm을 포함해서 모든 Visual 또는 Non Visual VCL 콘트롤들은 TComponent 로 부터 
// 상속 받게 되어 있기 때문에, 다음과 같이 constructor를 호출 합니다.

// 생성자의 실제 호출은 VMT 테이블을 통해서 결정되기 때문에, 컴파일러는 VMT 테이블의 
// Index를 갖고, constructor를 호출하는 코드를 생성 하게 됩니다.

instance.Create(Self)  





여기서 델파이와 C++ 과의 극명한 차이가 드러나는 데요. 델파이에선 construcor 네임을 사용자가
마음대로 정할 수도 있고, 또 일반 함수처럼 명시적으로 호출하는 것도 가능 합니다. 그러나 C++에선
이게 허용되지 않죠.

그리고 클래스 상속계층에서 끝단에 있는 생성자를 호출해서 객체를 초기화 해야하기 때문에...
델파이는 consturctor를 Virtual 함수처럼 다룰 수 밖에 없습니다.

C++과 달리 델파이가 NewInstance 로 객체를 위한 메모리를 할당 하고.. 바로 Virtual Method Table의
Context를 결정하는 이유도... ClassType 정보 만으로 객체를 다이나믹하게 생성하기 위한 과정에서...
Late Binding이 필요하고, 그래서 constructor를 일반 Virtual 함수처럼 동작하게 만들 수 밖에 없기
때문 입니다.


C++에선 명시적으로 constructor 를 사용자가 호출하는 것을 허용하지 않기 때문에, 위와 같은 식의
코드패턴을 C++로 바로 포팅할 수는 없습니다. (VCL 소스에선 위와 같은 코드패턴이 곳곳에서 발견
됩니다.)

C++로 포팅하기 위해선...

컴파일러가 생성한 클래스 타입정보 테이블을 갖고, constructor 에 해당하는 VMT 테이블의 인덱스
를 찾아서 해당 생성자의 주소, 그리고 전달되는 파라미터의 갯수 그리고 속성을(이런 정보도 테이블
에 포함되어 있습니다.) 알아내서.. 런타임 중에 스텍 프레임을 구성해서 간접적으로 호출해주는 코드를
구현해 줘야 합니다.

__classmethod  키워드로 정의되는 클래스 메소드 함수도  델파이에선 가능한 record 타입의
class method는 아직 C++빌더가 지원하지 않고 있습니다. 

C++ 빌더로는 델파이로 컴파일 되어있는 바이너리를 투명하게 사용할 수 있다는 거고...

델파이로 작성되어 있는 VCL 또는 RTL 소스코드를 C++로  포팅하는 것은 가능은 하지만...
노가다가 많이 필요 합니다. C++빌더 컴파일러가 파스칼 소스코드를 컴파일 할수 있는 능력도 갖고 있는
것은 아닙니다. 파스칼 소스의 컴파일은 DCC32가 하게 되는 거죠.

어떻게 적다 보니까... 두서없이 적었네요.. --;

+ -

관련 글 리스트
66245 C++빌더에서 델파이 코드 사용 관련 의문 왕초보와코드 970 2012/02/12
66247     Re: 컴파일러와 개발환경의 내부적인 측면을 살펴 보면... 빌더 1360 2012/02/13
66253         Re:Re: 빌더님 강좌 좀 부탁 드립니다 ^^ 대박왕 1297 2012/02/13
66246     Re:C++빌더에서 델파이 코드 사용 관련 의문 Lyn 1140 2012/02/12
Google
Copyright © 1999-2015, borlandforum.com. All right reserved.