IBM®
메인 컨텐츠로 가기
    Korea [국가변경]    이용약관
 
 
   
        제품    서비스 & 솔루션    고객지원 & 다운로드    회원 서비스    
메인 컨텐츠로 가기

한국 developerWorks  >  SOA와 웹서비스  >

XPCOM Part 2: XPCOM 컴포넌트 기초 (한글)

XPCOM 프로그래밍 소개

developerWorks
문서 옵션

JavaScript가 필요한 문서 옵션은 디스플레이되지 않습니다.

영어원문

영어원문


제안 및 의견
피드백

난이도 : 초급

Rick Parrish, Independent consultant, Freelance

2007 년 12 월 04 일

지난 기술자료에서는 XPCOM 기술을 소개했습니다. 이번 시간에는, 유형 라이브러리, xpidl 컴파일러, 인터페이스 발견에 대해 설명합니다.

XPCOM의 개념은 플랫폼 및 언어 중립적인 모듈식의 프레임웍을 제공하는 것이다. JavaScript나 기타 스크립팅 언어로 수행될 수 없는 것을 C++로 작성된 컴포넌트가 수행할 수 있는 것은 별로 없다. 이러한 기능을 수행하기 위해 XPCOM에 사용된 메커니즘이 유형 라이브러리(type library)이다.

유형 라이브러리

유형 라이브러리는 메소드, 애트리뷰트, 매개변수, 컴포넌트의 인스턴스를 기술하는데 필요한 공통 데이터 포맷이나 교환 메커니즘을 제공한다. 공통 포맷을 확립함으로써, 같은 인터페이스들이 여러 플랫폼과 여러 프로그래밍 언어들을 통해서 기술될 수 있다. 이것은 기본적인 마샬링 또는 프록시 메커니즘을 지원하기에 유용하다. 유형 정보를 사용하여, 프로그램은 인터페이스에 대한 메소드의 모든 매개변수 또는 애트리뷰트를 결정할 수 있다. 이러한 지식을 바탕으로, 인터페이스와 다른 환경 사이에 데이터를 이동시킬 수 있다. 다른 환경이란 스크립팅 엔진 또는 쓰레드, 프로세스, 네트워크 경계를 넘나드는 프록시 메커니즘이 될 수 있다. 스크립팅 엔진의 경우, 이것은 컴포넌트가 스크립팅 환경에 정의되어 스크립팅 된 코드가 컴포넌트 인터페이스에 메소드를 호출 할 수 있도록 하는 방식이다.

XPConnect는 XPCOM 라이브러리 파일을 읽음으로써 XPCOM 인터페이스를 JavaScript 엔진으로 마샬링 할 수 있는 XPCOM을 기반으로 구현된 추가 레이어이다. XPConnect는 또한 XPCOM 컴포넌트들이 JavaScript로 완전히 작성되도록 하여 C++ 코드가 JS 컴포넌트를 호출할 수 있거나, JS를 사용하여 컴파일 된 C++ 컴포넌트를 로딩 및 조작할 수 있다. JavaScript 외에도, Python 언어 역시 XPConnect와 비슷한 메커니즘을 사용하는 또 다른 스크립팅 대안으로서 추가되었다.




위로


인터페이스 디스크립션

소셜 북마크

mar.gar.in mar.gar.in
digg Digg
del.icio.us del.icio.us
Slashdot Slashdot

언어 중립적인 방식으로 인터페이스를 설정하는 것은 IDL 또는 인터페이스 디스크립션 언어를 사용하는 것이다. 인터페이스 디스크립션에서 유형 라이브러리 파일을 만드는 툴은 IDL 컴파일러이다. XPCOM에 사용되는 IDL은 OMG CORBA 또는 Microsoft IDL에 사용되는 것과 약간 다르기 때문에, 다른 컴파일러, xpidl 컴파일러가 사용된다. xpidl 컴파일러의 재미있는 기능은 인터페이스 정의에서 C++ 코드 스텁을 생성하는 옵션이다. 이 기능은 새로운 프로젝트를 시작할 때 모든 선언적인 C++ 코드를 작성하는 효과를 갖고 있다. 이것은 코딩 마법사와 같은 것이다. CORBA와 Microsoft IDL 컴파일러도 비슷한 기능을 제공한다. 다음은 쉘 프롬프트에서 xpidl을 실행하는 시놉시스이다.

Listing 1. 쉘 프롬프트에서 xpidl
Usage: xpidl [-m mode] [-w] [-v] [-I path] [-o basename] filename.idl
  -w turn on warnings (recommended)
  -v verbose mode (NYI)
  -I add entry to start of include path for ``#include "nsIThing.idl"''
  -o use basename (e.g. ``/tmp/nsIThing'') for output
  -m specify output mode:
     header        Generate C++ header            (.h)
     typelib       Generate XPConnect typelib     (.xpt)
     doc           Generate HTML documentation    (.html)

C++ 코드를 생성하는 것이 보너스라면, IDL 컴파일러의 진짜 목적은 각 모듈을 위한 유형 라이브러리 파일을 만드는 것이다. C++ 코드는 IDL 생성 C++ 헤더 파일을 활용하여 C++ 클래스 상의 가상 메소드로서 인터페이스를 기술하는 것이다. 인터페이스 디스크립션(C++ 헤더 파일의 형태)이 컴파일 시 사용되기 때문에, 이른 바인딩(early binding)이라고 한다. 유형 라이브러리 파일은 전에 한번도 보지 못했던 컴포넌트를 사용해야 하는 곳에서 인스턴스를 위해 같은 기능을 제공한다. 이 사실 후에 인터페이스를 배운다. 이것을 늦은 바인딩(late binding)이라고 한다.

인터페이스를 설정하는 XPIDL 신택스는 다음과 같다; interface 키워드 다음에, 인터페이스 이름, 콜론, 기본 인터페이스의 이름(nsISupports), 중괄호 열기, 세미콜론으로 끝나는 애트리뷰트와 메소드 리스트, 중괄호 닫기와 세미콜론이다. 애트리뷰트는 attribute 키워드를 사용하여 선언된다. 메소드에 대한 매개변수는 in 또는 out 키워드를 앞에 붙임으로써 인풋 또는 아웃풋 매개변수로서 선언될 수 있다. Listing 2는 컴퓨터의 스크린을 기술하는데 사용되는 샘플 인터페이스이다.

Listing 2. 샘플 인터페이스
#include "nsISupports.idl"
[scriptable, uuid(f728830e-1dd1-11b2-9598-fb9f414f2465)]

interface nsIScreen  : nsISupports
{
  void GetRect(out long left, out long top, out long width, out long height);
  void GetAvailRect(out long left, out long top, out long width, out long height);
  readonly attribute long pixelDepth;
  readonly attribute long colorDepth;
};

위 인터페이스 디스크립션을 보면, 이 인터페이스 이름이 nsIScreen이고, 두 개의 메소드(GetRectGetAvailRect)와 두 개의 애트리뷰트(pixelDepthcolorDepth)를 갖고 있다. 인터페이스 키워드 바로 앞에 한 쌍의 대괄호 안에 구문이 있다. 이 구문은 인터페이스 디스크립션의 선택적 부분이고, 유용한 메타데이터를 제공한다. scriptable 키워드는 JavaScript와 기타 스크립팅 언어로 마샬링 될 수 있는 후보로 태그를 단다. uuid 키워드는 인터페이스의 UUID 또는 인터페이스 ID를 지정한다. nsIScreen의 기본 인터페이스는 nsISupports (콜론 바로 다음에 나타남)이고, 이것의 의미는nsISupports로 기술된 어떤 메소드와 애트리뷰트든 nsIScreen에서 찾을 수 있다는 것을 의미한다. 애트리뷰트는 attribute 키워드에 의해서 메소드와 구분된다. 이 경우, 두 개의 애트리뷰트 모두 검사될 수 있지만 설정되지 않았다. readonly 키워드가 증거이다. (참고자료)




위로


인터페이스 발견

XPCOM은 컴포넌트 핸들링에 인터페이스 기반 방식을 사용한다. 클라이언트 코드는 그 컴포넌트에 의해 제공되는 인터페이스를 통해 직접 컴포넌트와 상호 작동한다. 대부분의 컴포넌트들은 두 개 이상의 인터페이스를 지원하기 때문에, 인터페이스 분배 메커니즘(QueryInterface 메소드)는 인터페이스를 관리하는 장치를 제공해야 한다. 특히:

  • 어떤 인터페이스들이 컴포넌트에 의해 지원되는지 결정
  • 한 인터페이스에서 또 다른 인터페이스(그 반대의 경우도 포함)로 변환

이러한 두 개의 아이템들을 하나로 그룹핑 하고, 이들을 인터페이스 발견(interface discovery)이라고 하겠다. XPCOM 컴포넌트의 핵심 요구 사항은 이것이 표준 인터페이스를 지원하여 인터페이스 발견을 핸들하고, 이 표준 인터페이스가 다른 XPCOM 인터페이스가 확장하여 추가 메소드와 기능을 제공할 수 있는 기본 인터페이스가 되어야 한다는 것이다. 이 표준 인터페이스 이름은 nsISupports이고, Listing 3의 IDL에 나타나 있다.

Listing 3. 표준 인터페이스, nslSupports
interface nsISupports
{
    void QueryInterface(in nsIIDRef uuid, out nsQIResult result);
    nsrefcnt AddRef();
    nsrefcnt Release();
};

첫 번째 메소드인 QueryInterface는 인터페이스 발견을 실제로 관장하는 메소드이다. 다른 두 개의 메소드, AddRefRelease는 레퍼런스 카운팅을 통해 컴포넌트의 수명 관리(컴포넌트가 얼마나 오래 존재할 수 있는지)를 제공한다.

QueryInterface에 대한 첫 번째 매개변수는 UUID에 대한 레퍼런스 또는 128 비트 (16 바이트)인 고유 ID 넘버이다. 예를 들어, nsISupports에 대한 인터페이스 ID는 00000000-0000-0000-c000-000000000046이다.

UUID는 하이픈이 삽입된 형태로 16진수를 사용하여 작성된다. 이 ID 넘버는 쿼리되고 있는 컴포넌트에 의해 지원 또는 지원되지 않는 인터페이스를 지정한다. 컴포넌트는 에러 결과 코드를 리턴하거나, 요청된 인터페이스의 주소에 대한 제 2 매개변수를 설정하고 성공 결과 코드를 리턴한다. XPCOM 소프트웨어 디자이너는 새로운 인터페이스에 고유 인터페이스 ID를 할당하여 새로운 인터페이스가 생성할 때 이를 관리하도록 한다.

Listing 4에는 컴포넌트의 같은 인스턴스에 대한 다른 인터페이스들간 변환을 위해 QueryInterface를 사용하는 JavaScript 샘플을 포함하고 있다.

Listing 4. 인터페이스 변환하기

// first, we create an instance of something...
var file = components.classes["@mozilla.org/file/local;1"].createInstance();
// second, we specify which interface we actually want to use.
file = file.QueryInterface(Components.interfaces.nsIFile);
// do something generic with the nsIFile interface here.
file.create(NORMAL_FILE_TYPE, 0377);
var size = file.fileSize;
// later on, we check to see if an extended interface is supported.
var local = file.QueryInterface(Components.interfaces.nsILocalFile);
if (local)
{
   // do something specific to the nsILocalFile interface...
   local.initWithPath('/usr/tmp/scratch.txt');

   // suppose we're now in some scope where the file variable is no longer
   // visible to use but we want to call some function that absolutely
   // insists on only accepting an nsIFile and not an nsILocalFile.
   // no problem, just QI over to the other interface like so ...

   var insists = local.QueryInterface(Components.interfaces.nsIFile);
   if (insists)
   {
      // at this point we can call our hypothetical function
      // to do some generic file processing...
      hypothetical(insists);
   }
}

nsISupports


위로


컴포넌트 생성

위 JavaScript 예제에서 재미있게 보이는 스트링, "@mozilla.org/file/local;1" 을 보았는가? 이것을 콘트랙트 ID라고 한다. 어떤 컴포넌트를 생성해야 할지를 컴포넌트 매니저에 지정하는 수단으로서 두 가지 형태의 구분들 중 하나를 필요로 한다. 한 가지 폼은 128 bit 넘버인 컴포넌트의 클래스 ID이다. 다른 폼은 텍스트 스트링인 콘트랙트 ID이다. 두 가지 모두 컴포넌트 매니저에서 컴포넌트를 요청하기에 충분하다. 콘트랙트 ID의 의도는 컴포넌트를 사용하고 싶어 하는 클라이언트에게 작동과 관련 인터페이스를 약속하는 것이다. 콘트랙트 ID의 권장 포맷은 한 줄 스트링이다:

@<internetdomain>/module[/submodule[...]];<version>[?<name>=<value>[&<name>=<value>[...]]]

대괄호 ([])는 선택적인 것을 포함하고 있다. 다음은 예제이다.

  • @mozilla.org/file/directory_service;1
    

  • @mozilla.org/file/local;1
    

  • @mozilla.org/file;1
    

  • @mozilla.org/filelocator;1
    

  • @mozilla.org/filepicker;1
    

  • @mozilla.org/filespec;1
    

각 예제에는 버전 넘버 1이 포함되어 있다. 버전 넘버 2가 있는 후속 콘트랙트 ID가 백워드 호환을 내포하지 않을 수도 있다. 다른 버전 넘버를 가진 콘트랙트 ID에는 다른 콘트랙트의 작동과 인터페이스가 원래의 작동과 인터페이스의 일부로 포함될 수 있다.




위로


수명 관리

컴포넌트는 얼마나 많은 인터페이스들이 실행되었는지를 계속해서 카운트 해야 한다. 파괴 될 컴포넌트에게는 이것은 안 좋은 일이지만, 다른 코드 조각들은 이 인터페이스들 중 하나를 사용한다. 객체가 또 다른 인터페이스 카피를 분배하면, 내부 레퍼런스 카운트가 증가한다. 객체의 인터페이스가 릴리스 되면, 레퍼런스 카운트는 감소한다. 객체의 레퍼런스 카운트가 0으로 떨어지면, 자멸한다. 이것이 바로 레퍼런스 카운팅이다.

일반적으로, QueryInterface 메소드는 유효 인터페이스 포인터를 리턴할 때 쿼리되는 컴포넌트에 대해 임의의 AddRef를 수행한다. 한 조각의 클라이언트 코드가 인터페이스를 사용하여 수행될 때, Release 메소드를 호출하여 이 인터페이스를 사용하여 수행되는 컴포넌트를 가리킨다. 이는 모든 XPCOM 클라이언트 소프트웨어에게는 큰 부담이다. 컴포넌트에 대한 QueryInterface 또는 AddRefRelease가 되어야 하기 때문이다. 소실된 또는 여분의 Release에 대해 XPCOM 버그의 거의 대부분이 트레이싱 된다.




위로


매크로와 스마트 포인터

이러한 유형의 에러를 해결하기 위해, XPCOM에는 스마트 인터페이스 포인터를 선언할 수 있는 C++ 템플릿이 포함되어 있다. 이 템플릿은 "set and forget" 포인터를 제공한다. 인터페이스에 포인터를 설정하고 인터페이스를 릴리스 할 것을 기억한다. 또 다른 인터페이스에 포인터를 설정하고, 이전 것을 릴리스 한다. 간단해 보인다. 필요한 범위 안에 스마트 포인터를 선언하고, 이것을 인터페이스에 할당한다. 플레인 바닐라 인터페이스 포인터로서 스마트 포인터를 사용한다. 스마트 포인터가 범위를 벗어나면, 빌트인 파괴자가 Release 메소드를 호출한다.

nsCOMPtr 또는 nsIPtr를 사용하는 Mozilla 코드에 대해 알아보자. (Listing 5)

Listing 5. nsCOMPtr 또는 nslPtr를 사용하는 Mozilla
nsresult nsExample::DoSomething(void)
{
  nsresult rv;
  nsCOMPtr<nsIManager> pManager;
  *aResult = nsnull;
  pManager = do_GetService("Some contract ID goes here");
  if (pManager == nsnull)
    return NS_ERROR_NOT_AVAILABLE;
  rv = pManager->ManageSomething(); // do some more work here ...
  return rv;
}


Listing 5에서, 가상의 nsIManager 인터페이스에 대한 스마트 포인터는 pManager라는 이름으로 선언된다. ("nsCOMPtr .."로 시작되는 라인 참조). 이 스마트 포인터는 일부 서비스로 할당된다. 유효 포인터가 정말로 리턴되었는지를 테스팅 한 후에, 위 코드는 포인터에 대한 참조를 해제하여 ManageSomething() 메소드를 호출한다. 위 함수가 리턴할 때, pManager 스마트 포인터가 파괴된다. 하지만, 안에 보유된 인터페이스 포인터에 Release를 호출하기 이전에는 아니다.

XPCOM은 C 매크로를 사용하여 많은 선언적인 작업들을 처리한다. 대부분의 인터페이스는 nsresult를 리턴한다. 대부분의 경우, nsresult에서 체크되는 매직 값은 NS_OK이다. (nsresult의 리스트는 nsError.h를 참조하라.)

XPCOM에는 nsCom.h, nsDebug.h, nsError.h, nsIServiceManager.h, nsISupportsUtils.h 파일들이 포함되어 있다. 테스팅, 디버깅, 구현을 위한 추가 매크로를 제공한다.

다양한 C++ XPCOM 컴포넌트의 헤더 파일을 검색하면, 클래스 정의의 일부로 NS_DECL_ISUPPORTS를 보게 될 것이다. 이 매크로는 nsISupports 인터페이스에 대한 정의를 제공한다.

Listing 6. nsISupports의 정의
public:
    NS_IMETHOD QueryInterface(REFNSIID aIID void** aInstancePtr);
    NS_IMETHOD_(nsrefcnt) AddRef(void);
    NS_IMETHOD_(nsrefcnt) Release(void);
    nsrefcnt mRefCnt;


컴포넌트의 상응하는 구현 파일을 검색하면, NS_IMPL_ISUPPORTS1이라는 이름의 한 줄의 매크로를 보게 된다. 이 매크로는 nsISupports 인터페이스의 실제 구현을 제공한다. 매크로의 끝에 있는 숫자 "1"은 컴포넌트가 구현하는 (nsISupports 외의) 인터페이스의 수를 가리킨다. 클래스가 두 개의 인터페이스를 구현한다면, 이것은 NS_IMPL_ISUPPORTS2를 사용할 수 있다.

mRefCnt 데이터 멤버를 기억하라. -- 이것을 다시 참조할 것이다.

다음은 NS_IMPL_ISUPPORTS1 매크로가 nsISupportsUtils.h에 정의되는 방식이다.:

Listing 7. NS_IMPL_ISUPPORTS1
#define NS_IMPL_ISUPPORTS1(_class, _interface) \
  NS_IMPL_ADDREF(_class)                       \
  NS_IMPL_RELEASE(_class)                      \
  NS_IMPL_QUERY_INTERFACE1(_class, _interface)

여러분도 보다시피, 세 개의 다른 매크로의 관점에서 정의될 뿐이다. NS_IMPL_ADDREF정의부터 시작하도록 하자.:

Listing 8. NS_IMPL_ADDREF
#define NS_IMPL_ADDREF(_class)                               \
NS_IMETHODIMP_(nsrefcnt) _class::AddRef(void)                \
{                                                            \
  NS_PRECONDITION(PRInt32(mRefCnt) >= 0, "illegal refcnt");  \
  NS_ASSERT_OWNINGTHREAD(_class);                            \
  ++mRefCnt;                                                 \
  NS_LOG_ADDREF(this, mRefCnt, #_class, sizeof(*this));      \
  return mRefCnt;                                            \
}

마침내 실제 코드가 생겼다. 다섯 줄의 코드 중에서, 세 개는 우리가 무시해도 좋을 디버깅 매크로이다. 이 코드의 마지막 라인은 리턴 문이다. AddRef를 호출하는 코드는 리턴된 값을 버리도록 되어 있기 때문에, 우리는 리턴 문을 무시할 수 있다.

우리의 관심을 끄는 라인은 ++mRefCnt 문이다. 이것이 하는 일은 카운터를 늘려서, 특정 인터페이스에 대해 AddRef를 호출할 때마다, 일부 내부 카운터를 증가시키는 것이다. 이제, NS_IMPL_RELEASE 매크로를 보자:

Listing 9. NS_IMPL_RELEASE 매크로
#define NS_IMPL_RELEASE(_class)                              \
NS_IMETHODIMP_(nsrefcnt) _class::Release(void)               \
{                                                            \
  NS_PRECONDITION(0 != mRefCnt, "dup release");              \
  NS_ASSERT_OWNINGTHREAD(_class);                            \
  --mRefCnt;                                                 \
  NS_LOG_RELEASE(this, mRefCnt, #_class);                    \
  if (mRefCnt == 0) {                                        \
    mRefCnt = 1; /* stabilize */                             \
    NS_DELETEXPCOM(this);                                    \
    return 0;                                                \
  }                                                          \
  return mRefCnt;                                            \
}

우리가 무시해도 좋을 디버깅 매크로가 포함된 세 개의 문장과, 앞서 설명한 이유대로 무시해도 좋을 두 개의 리턴 문이 있다.

우리가 신경 써야 할 두 문장은 객체의 카운터를 줄이는 --mRefCnt와, 카운터가 0의 값에 도달했는지 여부를 테스트 하는 if (mRefCnt == 0)이다. 다음 두 라인은 내부 카운터가 0에 다다르면 이 객체가 스스로를 삭제한다는 것을 알려준다.

요약하면, AddRef는 카운터를 늘리고, Release는 카운터를 줄인다. AddRef에 대한 호출의 수가 Release의 호출 수와 같아지면, 순 레퍼런스 카운트는 0이 되고, 컴포넌트는 자멸한다. 이러한 전체적인 레퍼런스 카운팅 개념은 이제 매우 단순해 보인다. 이제, Listing 10에 정의된 NS_IMPL_QUERY_INTERFACE1에 대해 알아보자.

Listing 10. NS_IMPL_QUERY_INTERFACE1
#define NS_IMPL_QUERY_INTERFACE1(_class, _i1)            \
  NS_INTERFACE_MAP_BEGIN(_class)                         \
    NS_INTERFACE_MAP_ENTRY(_i1)                          \
    NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, _i1)   \
  NS_INTERFACE_MAP_END

앗! 매크로가 더 많아졌다. 지금까지, 모든 MFC 코더들은 이러한 종류의 매크로 인다이렉션이 넌센스라고 생각했기 때문에 웃고 있을 것이다. 이러한 매크로들은 컴포넌트를 위한 인터페이스 맵을 구현하기 때문에, 이들의 순서와 위치는 QueryInterface 구현을 생성하는데 있어서 중요하다. 이러한 인다이렉션에 압도되지 말고, NS_INTERFACE_MAP_BEGIN을 분석해 보자. 이것은 Listing 11의 코드로 확장되는 NS_IMPL_QUERY_HEAD의 앨리어스일 뿐이다.

Listing 11. NS_INTERFACE_MAP_BEGIN
#define NS_IMPL_QUERY_HEAD(_class)                                       \
NS_IMETHODIMP _class::QueryInterface(REFNSIID aIID, void** aInstancePtr) \
{                                                                        \
  NS_ASSERTION(aInstancePtr, "QueryInterface requires a non-NULL destination!"); \
  if ( !aInstancePtr )                                                   \
    return NS_ERROR_NULL_POINTER;                                        \
  nsISupports* foundInterface;

이 매크로에 있는 코드는 어떤 일도 하지 않는다. 이 함수의 전문과, 무효 리턴 포인터에 대한 테스트의 형태로 가벼운 에러 체크만 제공할 뿐이다. 이 함수를 완료하는 닫기 중괄호도 없기 때문에, 이 매크로 뒤에 코드의 나머지를 채우는 다른 매크로가 있어야 할 것만 같다. 다음 매크로는 몇 가지 작업을 수행한다.NS_INTERFACE_MAP_ENTRYNS_IMPL_QUERY_BODY의 앨리어스일 뿐이다.

Listing 12. NS_IMPL_QUERY_BODY
#define NS_IMPL_QUERY_BODY(_interface)                  \
  if ( aIID.Equals(NS_GET_IID(_interface)) )            \
    foundInterface = NS_STATIC_CAST(_interface*, this); \
  else


이것은 인터페이스 맵을 위해 매칭을 수행하는 중요한 코드 스니펫이다. if/else 문이 구축된 방식 때문에, 다중 NS_IMPL_QUERY_BODY 매크로들을 쌓아서 어떤 인터페이스 ID에도 답할 인터페이스 맵을 구현할 수 있다. 다음 매크로인, NS_INTERFACE_MAP_ENTRY_AMBIGUOUSNS_IMPL_QUERY_BODY_AMBIGUOUS에 대한 앨리어스일 뿐이다.

Listing 13. NS_IMPL_QUERY_BODY_AMBIGUOUS
#define NS_IMPL_QUERY_BODY_AMBIGUOUS(_interface, _implClass)             \
  if ( aIID.Equals(NS_GET_IID(_interface)) )                             \
    foundInterface = NS_STATIC_CAST(_interface*, NS_STATIC_CAST(_implClass*, this)); \
  else


NS_IMPL_QUERY_BODY_AMBIGUOUSNS_IMPL_QUERY_BODY와 같은 일을 하는 것처럼 보인다. 여기에서 행해지는 유일한 추가 작업은 nsISupports에서 파생된 두 개 이상의 지원 인터페이스들이 있을 때, nsISupports용 인터페이스 포인터를 리턴할 때 컴파일러 에러를 피하는 것이다. 이들 중 하나가 유효 nsISupports 인터페이스 포인터가 될 수 있기 때문에, C++ 컴파일러의 딜레마는 어떤 것을 선택하는가 이다. XPCOM 인터페이스-파견 메커니즘에 대한 한 가지 요구 사항은 언제나 같은 인터페이스 ID에 대한 같은 포인터를 리턴하는 것이다. 따라서, 이 매크로는 어떤 nsISupports-파생 인터페이스 포인터가 nsISupports 인터페이스 포인터로서 사용되는지를 지정함으로써 이 규칙에 순응한다. Listing 14에서 설명하듯이, NS_INTERFACE_MAP_ENDNS_IMPL_QUERY_TAIL_GUTS에 대한 앨리어스일 뿐이다.

Listing 14. NS_IMPL_QUERY_TAIL_GUTS
#define NS_IMPL_QUERY_TAIL_GUTS    \
    foundInterface = 0;            \
  nsresult status;                 \
  if ( !foundInterface )           \
    status = NS_NOINTERFACE;       \
  else                             \
    {                              \
      NS_ADDREF(foundInterface);   \
      status = NS_OK;              \
    }                              \
  *aInstancePtr = foundInterface;  \
  return status;                   \
}

드디어, QueryInterface 구현의 끝에 다다랐다. 이 코드의 마지막 스니펫은 콜러의 인터페이스 ID가 맵의 어떤 ID와도 매치되지 않을 때 NS_NOINTERFACE의 에러 코드를 리턴한다. 콜러의 인터페이스 ID가 객체의 지원 인터페이스들 중 하나와 매치하면, 이 코드는 객체의 AddRef 메소드를 호출하고, 인터페이스에 대한 포인터와 NS_OK의 결과 코드로 리턴한다.

지금까지 살펴본 코드는 하나의 인터페이스를 가진 컴포넌트를 위한 nsISupports의 구현이다. 여러 인터페이스를 지원하는 구현은 비슷하다. 실제 컴포넌트들은 구현들 중 하나를 사용하여 작성되거나, 고유의 것을 제공한다. 대부분의 경우, nsISupportsUtils.h에서 찾은 매크로를 사용한다. 지금까지, mozilla/XPCOM 코드 베이스를 읽고 이해하기를 원한다면, 특히 중첩된 정의의 경우, C 스타일 매크로를 해부하는데 이것이 왜 중요한지를 보았다.




위로


결론

지금까지의 자료는 Mozilla 검색에 도움이 될 것이다. 컴포넌트가 인터페이스 포인터를 어떻게 분배하는지, 수명 관리를 어떻게 수행하는지를 자세히 살펴보았다. 앞으로는, 컴포넌트 관리에 사용할 수 있는 고급 장치에 대해 설명하겠다. 다음 단계는 Mozilla 브라우저와 실제 XPCOM 프레임웍을 실제로 구현하는 개발 툴과 요구 사항에 대해 알아보겠다. XPCOM 개발 환경 생성이 가장 큰 효과를 볼 것이다.



참고자료



필자소개

Rick Parrish는 고등학교 시절부터 컴퓨터에 관심을 갖고 있었다. 전기 엔지니어링을 전공했지만 하드웨어와는 다른 소프트웨어에 눈을 돌리게 되었다. Rick은 A LONG TIME(tm)에 C/C++로 프로그래밍을 수행했고, VB, Delphi(Pascal) 및 어셈블리 언어도 사용하고 있다. 두 개의 프로젝트도 이끌고 있다. Linux 2.4 커널에 매료되어 있다. 현재 소프트웨어 모델링 디자인용 툴 개발을 하는 오픈 소스 프로젝트를 시작했다. (rfmobile@swbell.net)




기사에 대한 평가


보다 나은 서비스를 제공하기 위함이오니 잠시 짬을 내어 이 양식을 제출하여 주십시오.



아니오잘 모르겠음
 


 


12345
 



위로


developerWorks 콘텐트를 다른 사이트에 전재하기:
developerWorks 콘텐트에 대한 저작권은 IBM에 있습니다. IBM의 서면 허가나 원본 저자의 허락이 없이는 전재를 금합니다. 저희 콘텐트를 전재하시려면 IBM developerWorks 담당자 에게 문의하십시오.
    IBM 소개 개인정보 보호정책 문의