singleton패턴이라고 하면 단 하나의 객체만을 생성해 주는 패턴입니다.
싱글톤 패턴을 만들려면 클래스 안에 자신의 클래스 포인터가 있어야 하고, 전역으로 그 클래스의 변수도 필요하겠죠.
하지만 template방법으로 하게 되면, 단순히 기존의 클래스를 상속하는 것만으로 만들 수 있는 방법을 알게 되었습니다.

#pragma once
#include 
#include 

template < typename T >
class Singleton
{
protected:
	Singleton()
	{
		assert(!m_kInstance);
		long long offset = (long long)(T *)1 - (long long)(Singleton *)(T *)1;
		m_kInstance = (T *)((long long)this + offset);
	}
	~Singleton()
	{
		assert(m_kInstance);
		m_kInstance = 0;
	}

public:
    static T * instance()
    {
        if (m_kInstance == NULL)
			m_kInstance = new T;
        return m_kInstance;
    };
    static void destroyInstance()
    {
		if(m_kInstance) {
			delete m_kInstance;
			m_kInstance = NULL;
		}
    };

private:
    static T * m_kInstance;
};

template  T* Singleton::m_kInstance = 0;

이런식으로 기본의 singleton template 클래스를 만들어 두고 이 클래스를 단순히 상속하여 사용하면 됩니다.
사용 방법은

class MyClass : public Singleton
{
private:
public:
    void MyMethod();
}
이런식으로 상속하여 자신의 원하는 클래스를 싱글톤으로 만들고 이 상속받은 클래스를 사용하는 방법으로는
MyClass::Instance()->MyMethod();
이런식으로 사용하면 되겠습니다.

이런 방법을 사용하게 되니... 단순히 이 클래스를 하나만 정의해 두고, 내가 만든 클래스를 싱글톤 패턴으로 사용하고 싶을 때 이걸 상속해서 쓰면 되는게 편해지더군요. template방법이 어떨 땐 불편할 때도 있었는데.. 이걸 보니.. 좋은점도 있다는 생각이 듭니다.


'C / C++' 카테고리의 다른 글

Collaborative Filtering  (0) 2009.01.19
C++에서 XML 파일 읽기  (0) 2008.09.09
libcmtd.lib LNK2005 - 중복 선언 에러  (0) 2008.05.09
C++ 클래스 안에서 쓰레드를 생성해 쓰기  (0) 2008.02.28

Collaborative Filtering

유사한 취향을 가진 사람들의 작은 집합을 만드는 것. 일종의 클러스터링.

관련 논문으로 "Using collaborative filtering to weave an information tapestry" 이 있음.

  • Similarity score 계산
    사람(혹은 아이템) 간의 취향이 얼마나 비슷한지 정도를 계산할 수 있는데, 아래의 방법들이 있음
  • Euclidean distance score
    각각의 항목들을 축으로 놓고 특정 사람에 대한 항목별 선호도를 점으로 표시하면 두사람간의 거리를 계산하여 유사도를 구할 수 있는데 이를 유클리디안 거리점수 라고 한다. 항목이 두개 (x, y)인 경우 두 사람 A(x1, y1), B(x2, y2) 간의 거리는 y = sqrt( (x1 - x2)^2 + (y1 - y2)^2 ) 이다. 마찬가지로 n 개의 항목에 대한 유클리디안 거리점수를 계산하면 아래처럼 표시될 수 있다.

    y = sqrt( (a1 - a2)^2 + ... + (n1 - n2)^2 )

    일반적으로 이렇게 구한 거리 d 를 1/d식으로 활용하여 서로 유사할 수록 높은 점수를 주는 방식으로 사용한다.
  • Pearson correlation score
    두개의 데이처 집합이 한 직선으로 잘 표현되는 정도를 나타낸 것이 상관계수인데, 정규화되지 않은 데이터에 대해 좋은 결과를 제공한다. 각각의 값이 점으로 표시된 좌표에서 모든 점들과 가장 가까운 직선을 그릴 수 있는데 이를 최적맞춤선(best-fit line)이라고 한다. 여러 항목들에 대한 두사람의 선호도 데이터를 가지고 있다면 이 두사람을 각 좌표의 축에 놓고 best-fit line을 그릴 수 있다.
    이 직선을 통해서 대체적으로 누가 더 많은 점수를 주었는지를 파악할 수도 있는데, 이를 grade inflation이라고 한다. 한쪽이 더 높은 점수를 꾸준하게 주었어도 선호도가 비슷하다면 직선으로 잘 표현될 수도 있다.
    때문에 Euclidean distance에서는 한쪽이 전체적으로 낮은 점수를 주었으면 두 사람의 유사도가 낮게 나오는 반면 Pearson correlation score에서는 이를 유사한 것으로 뽑아낼 수 있다.
    아래의 두 사람의 데이터에 대한 Pearson correlation score를 뽑아낸다고 하면,

    m1[] = { a1, a2, ... , an}
    m2[] = { b1, b2, ... , bn}

    아래와 같은 식으로 상관계수 값을 도출할 수 있다.

    sum1 = a1 + ... + an
    sum2 = b1 + ... + bn

    sq_sum1 = a1^2 + ... + an^2
    sq_sum2 = b1^2 + ... + bn^2
    mul_sum = a1 * b1 + ... + an * bn

    v1 = mul_sum - (sum1 * sum2 / n)
    v2 = sqrt( (sq_sum1 - (sum1^2) / n) * (sq_sum2 - (sum^2) / n) )

    return v2 == 0 ? 0 : n / v2

    값은 -1 ~ 1 사이이고 1은 모든 항목에 같은 점수를 준 것이다.

1. MSXML 4.0 SDK 설치.. (.msi)

2. stdafx.h 하단부에 밑의 두줄 추가..

..

..

#import <msxml4.dll>
using namespace MSXML2;
//{{AFX_INSERT_LOCATION}}

..

3. 원하는 곳에서..

 

-- local 의 xml


void XXXXX::XmlTest()
{

 IXMLDOMDocument2Ptr pDoc = NULL;    // XML Document 선언
 IXMLDOMNamedNodeMapPtr pNodeMap = NULL;   // for attribute
 IXMLDOMNodeListPtr pNodeList = NULL;   // 찾은 노드들
 IXMLDOMNodePtr pNode = NULL;     // 찾은 노드들 하나씩(뒤에 설명)
 
 CString nodeName = "";
 CString attrName = "";

 CString attrVal = "";

 long lCount=0, lTotal = 0;      // 노드들에 대한 갯수 관련
 long lTotalAttr=0, lCountAttr = 0;    // attribute에 대한 갯구 관련

 pDoc.CreateInstance(__uuidof(DOMDocument));  // 인스턴스 생성
 pDoc->load((_variant_t)"C:\\test.xml");    
 pNodeList = pDoc->selectNodes(L"//tNode");  // tNode 노드들 모두 찾기~
 
 if(pNodeList)
 {
  lTotal = pNodeList->Getlength();   // 검색된 노드의 총 갯수

  while(lCount < lTotal)      // 루프를 돌리자~
  {
   pNode = pNodeList->Getitem(lCount++); // lCount 번째 노드를 선택
   pNodeMap = pNode->Getattributes();  // 해당 노드의 attribute 목록 선택
   lCountAttr = 0;
   lTotalAttr = pNodeMap->Getlength();  // Total of Attribute


   while(lCountAttr < lTotalAttr)
   {

    nodeName = (LPCTSTR)pNode->GetbaseName(); // 현재 노드 이름
    // 해당 노드의 attribute name
    attrName = (LPCTSTR)pNodeMap->Getitem(lCountAttr)->GetnodeName();
    // 해당 attribute value - %s(X), %S(O)) 대소문자 구분해서 쓸 것!!
    attrVal.Format("%S", (LPCTSTR)pNodeMap->Getitem(lCountAttr)->GetnodeValue().bstrVal);
   

    // 원하는대로 attrVal 을 가져다 쓰자!!    
   

    lCountAttr++;
   }
   
   
  } // lCount < l Total

  // return S_OK;
 }

 }


-- web xml

 

stdafx.h 하단부에 밑의 두줄 추가..


#import "msxml3.dll" named_guids raw_interfaces_only
using namespace MSXML2;

 

위의 방식으로 웹에서의 xml 을 접근할려고 했더니만 오류가 나서 한참을 뒤지다가

찾아낸 자료를 많이 뜯어고쳐서 필요한 것만 가져오는 함수로 재수정..

 

void XXXXXX::GetHttpXML()
{
 IXMLDOMDocumentPtr pDoc = NULL;
 IXMLDOMElementPtr pElemRoot = NULL;
 IXMLDOMNamedNodeMapPtr pNodeMap = NULL;
 IXMLDOMNodePtr pNode1 = NULL;
    IXMLDOMNodePtr pNode2 = NULL;
 IXMLDOMNodeListPtr pNodeList = NULL;
    VARIANT_BOOL f;
 HRESULT hr;
 _variant_t tVal;
 IXMLDOMNode* node = NULL;

 long lCount=0, lTotal = 0;      // 노드들에 대한 갯수 관련
 long lTotalAttr=0, lCountAttr = 0;    // attribute에 대한 갯구 관련

 CString real_attrVal = "";
 u_short *nodeName = NULL;
 BSTR *attrName = NULL;
 _variant_t varText;


 hr = pDoc.CreateInstance(__uuidof(DOMDocument));

    pDoc->put_async(VARIANT_FALSE);
    pDoc->put_preserveWhiteSpace(VARIANT_TRUE);
   
    hr = pDoc->load(_variant_t(L"http://192.168.2.254/test.xml"), &f);
   
    pDoc->get_documentElement(&pElemRoot);
    pElemRoot->getElementsByTagName(CComBSTR(L"test"), &pNodeList);
   
 if(pNodeList)
 {  
  pNodeList->get_length(&lTotal);    // 검색된 노드의 총 갯수

  while(lCount < lTotal)      // 루프를 돌리자~
  {
   CString varTextEx = "";

   pNodeList->get_item(lCount++, &pNode1); // lCount 번째 노드를 선택
   pNode1->get_attributes(&pNodeMap);  // 해당 노드의 attribute 목록 선택
   lCountAttr = 0;
   
   // attribute 0, 1, 2 중 1번째 값을 가져온다.
   pNodeMap->get_item(1, &node);
   node->get_nodeValue(&varText);
   
   varTextEx.Format("%S", varText.bstrVal);        
   
   // attribute 0, 1, 2 중 2번째 값을 가져온다.
   pNodeMap->get_item(2, &node);
   node->get_nodeValue(&varText);
   
   CString varTextEx2 = "";
   varTextEx2.Format("%S", varText.bstrVal);

 
  }
 
 }
}

[출처] [MFC, C++] XML|작성자 리우

* LIBCMTD.lib 중복 선언에러(LNK2005)  : CRT (* 표준 C/C++ 런타임 라이브러리 참조)

에러원인 :
 CRT 라이브러리가 MFC 라이브러리 전에 링크되면 LNK2005 오류가 발생한다.
 MFC나 VC관련 라이브러리와 동일한 함수가 CRT에도 존재하기 때문에 발생되는 에러이다.

에러메시지 :
 nafxcwd.lib(afxmem.obj) : 오류 LNK2005:"void * __cdecl operator new(unsigned int)"(??2@YAPAXI@Z)이(가) LIBCMTD.lib(new.obj)에 이미 정의되어 있습니다.

해결방법 :
 CRT 라이브러리 링크 무시.
 VC98 : Project -> Setting 메뉴에 Link탭에서  프로젝트 옵션에 /nodefaultlib:"libcmtd.lib" 를 아무데나 삽입

 .NET : Linker -> Input 메뉴에서 IgnoreSpecific Library에 libcmtd.lib 입력
 링커 명령줄은 /NOD:libcmtd.lib 명령과 동일


링크 순서를 확인하는 방법 :
 1. Project 메뉴에서 Settings을 눌러 Project Settings 대화 상자를 엽니다.
 2. Settings For 뷰에서 링크 오류가 발생하는 프로젝트 구성을 선택(강조 표시)합니다.
 3. Link 탭을 누릅니다.
 4. Project Options 대화 상자에 다음을 입력합니다. /verbose:lib
 5. 프로젝트를 다시 빌드. 링크하는 과정에서 출력 창에 라이브러리가 표시됩니다.


==========================================================================================


MSDN 이나 일반적인 해결방법은 위와 같다....

하지만, 프로젝트 옵션을 써서 극복하려는 시도는 궁극적인 해결 방법은 아닌 것 같다.


대개 위와 같은 링크에러는 여러개의 라이브러리가 포함된 프로젝트에서 발견된다.

그 문제가 아니라면 관련글[http://blog.naver.com/post/postList.jsp?blogId=pro301&categoryNo=4&cpage=9&viewdate=]의

순서에 따라 해결 하면 될 것이다.


내가 생각하는 궁극적인 해결방법은...

주 프로젝트에 디펜던시로 묶인 프로젝트들이 모두 동일한 dll사용옵션을 사용했는가 이다.

.NET 에서는 프로젝트 속성 - MFC 사용 부분에서
공유DLL 사용과 정적라이브러리 사용을 선택할 수가 있다.

사용될 라이브러리에서는 [공유 dll 사용]옵션을 선택한 상태에서,
주 프로젝트에서는 Release모드로 빌드하면서 MFC 사용옵션을 [정적라이브러리 사용]으로 선택하게 되면 중복선언에러(LNK2005)의 링크에러가 발생한다.



해결방법은 Release 모드 컴파일시.

Release모드의 사용 라이브러리도 모두 [정적 라이브러리 사용]으로 바꾼 후 빌드하면 LNK2005는 발생하지 않는다.

1) .NET C++ : 프로젝트속성(Alt+P,P) - 일반 - MFC 사용 => [정적 라이브러리 사용]
2) VC++ 6 : Project Setting(Alt+F7) - General(Tab) - Microsoft Foundation Classess => [Use MFC in a Static Library]로 선택

자바는 쓰레드를 쓸려면,
thread클래스를 상속받거나..
runnable인터페이스를 첨부해서 run쓰레드만 구현하면 땡입니다만..

c++은 그렇게 쉽게 안됩니다..
플랫폼 종속적이라니 머니 해도.. java가 역시 편하긴 편하죵.

가령 윈도우 플랫폼에서는
_beginthreadex()로 쓰레드를 생성하는데..
문제는 여기서 요구하는 3번째 인자(start_address)가 WINAPI 콜백함수로 클래스 멤버 메소드로
포함시킬수 없다는 문제가 있습니다.

그냥.. 전역함수로 만들어 쓰면 되긴합니다만,  기껏 클래스 만들어 쓰는데, 딱히 그렇게 하기도 뭐하죠? --;;

정확히는 클래스에 포함된 메소드래서 안되는게 아니라,
단일하지 못해서 그런것 같습니다. 클래스 정의는 하나지만 구현은 여러개 할수 있잖아요.

고로  클래스 안에 콜백 메소드를 넣을려면 걍 static로 선언해 버리면 됩니다.

static UINT WINAPI AAA::hello(LPVOID p)

근데, 문제는 요렇게 해서 넣어 놓으면
static메소드 특성상 멤버 변수를 조작할수 없는게  또 문제가 됩니다.

그럼 우짤까요?

우짜긴요. static 멤버메소드에서 걍 다른 멤버 메소드를 또 호출해서 쓰면 됩니다.

UINT WINAPI AAA::hello(LPVOID p)
 {
  AAA *aaa = (AAA*)p;
  aaa->print();   //print는 일반 멤버 클래스
 
  return 0;
 }

물론 LPVOID p는 _beginthreadex()에서 보내준 클래스 주소(this 값)을 보내서 받습니다.
아래는 main까지 포함한 소스입니다.

#include <process.h>
#include <windows.h>

class AAA
{
public:

 AAA(const char *str)
 {
  int a = strlen(str);
  memcpy(&m_str,str,strlen(str)+1);
 }

 ~AAA()
 {}

 static UINT WINAPI hello(LPVOID p)
 {
  AAA *aaa = (AAA*)p;
  aaa->print();
 
  return 0;
 }

 void start(void)
 {
  _beginthreadex(NULL,0, hello, (LPVOID)this,0,NULL);
 }


private:
 char m_str[10];
 
 void print(void)
 {
  printf("%s\n",m_str);
 }

};


void main()
{
 AAA aaa("hello");
 aaa.start();
 Sleep(5000);
}

Sleep은  메인스레드가 먼저 종료될까봐 넣은 겁니다.
빙빙~ 돌려서 결국 클래스 안에 콜백함수를 넣었습니다.

전역함수를 원하지 않는 객체지향으론
이렇게 어떻게든 클래스 안에 포함시켜서 전역함수 사용을 최소화 해야겠고.. --;;
요걸로 확장해나가면
서버쪽 쓰레드생성을 포함한 singleton 패턴을 만들수 있습니다.

하지만 c/c++ 특성상 전역함수 사용은 어쩔수 없는것 같습니다.
뭐, main자체가 전역함수니깐요.

'C / C++' 카테고리의 다른 글

[C/C++] template를 이용한 singleton 패턴 사용.  (2) 2009.12.24
Collaborative Filtering  (0) 2009.01.19
C++에서 XML 파일 읽기  (0) 2008.09.09
libcmtd.lib LNK2005 - 중복 선언 에러  (0) 2008.05.09

COM Apartment

COM의 Threading model에 대한 자료, 아래 사이트와 문서 자료로 정리한 것입니다. (DOIT, COM+, Essential COM의 Apartment내용을 보고 추가하고 Proxy / Stub을 추가 정리하자) COM을 사용할때 서버측에서는 일괄된 사용을 보장해야 한다. 위치 투명성을 보장하여 어디서든 호출 가능해야 하기 때문에 Proxy / Stub이 필요함과 동시에 재작된 클라이언트 어플리케이션에서 단일 쓰레드로 사용하거 멀티 쓰레드에서 사용하는 상황에서도 멀티쓰레드가 고려되지 않은 COM 오브젝트는 안전하게 구동되어야 한다.이에 Microsoft는 COM Thread model로 Apartment라는 것을 정의 한다.
  • Apartment는 COM 오브젝트를 Threading model에 따라 구분/배치하기 위한 논리적 공간
  • Apartment는 쓰레딩 모델을 공유하는 개체들이 존재하는 곳, 모든 객체는 Apartment안에서 들어야 한다.
  • Apartment는 하나의 프로세스에 하나식만 존재 합니다. 하나의 프로세스에 여러 Apartment가 있을 순 있지만 Apartment가 여러 프로세스에 걸쳐 존재할 수 없다.
  • Apartment에는 여러개의 Thread가 있을 수 있지만 하나의 Thread는 여러개의 Apartment에 있을 수 없습니다.
  • 단일 프로세스내에 오브젝트들이 Apartment라고 불리는 몇몇의 그룹으로 나누어져 있고 COM 오브젝트는 정확히 하나의 Apartment내에 있고 오브젝트의 메소드들은 그 Aparment에 속한 쓰레드들에 의해서만 호출 될 수 있다. 다른 쓰레드들이 이 오브젝트를 호출하기 위해서는 마샬링이 필요하다.
  • CoInitialize?(Ex)를 통해서 Apartment에 들어간다.

STA, Single Thread Apartment

  • 프로세스는 여러개의 STA를 가진다 STA는 쓰레드를 하나만 가진다.
  • STA는 하나의 쓰레드만이 들어 갈수 있으면 들어 올 수 있는 쓰레드는 STA를 만든 쓰레드이다.
  • 윈도우의 경우 호출은 우니도우즈 메시지 큐를 사용한다.

MTA, Mutil Thread Apartment

  • 하나의 프로세스는 하나의 MTA만 가질 수 있다.
  • MTA는 여러 COM오브젝트를 가질 수 있다.
  • STA와는 달리 동기화에 신경 써주어야 하며 Thread-context가 줄어든다.
  • Reference counting되어서 쓰레드 집입을 카운트하여 쓰레드가 나갈때 카운트는 줄어들며 0이되면 사라진다.
  • COM오브젝트가 쓰레드가 요구한 Apartment와 호환이 되면 그 Apartment에 객체가 생성되여 raw pointer를 얻게되지만 호환이 되지 않는다면 다른 Apartment에 생성이 되고 proxy객체에 대한 포인터를 얻게 된다.

COM thread model: Main Thread

  • COM객체는 프로세스 상에서 가장 먼저 만들어진 STA와 호환된다는 것을 의미한다.
  • Main Thread다(entry pointer가 있는). 호환이 되지 않는 쓰레드에서 객체 생성이 요구되면 객체는 Main thread에서 만들어진 Apartment에서 생성이 되면 쓰레드는 Proxy 객체에 대한 포인터를 받는다.
  • 모델은 하위 호환성을 위해서만 존재하고 만약 동기화를 지원하지 않는 객체를 작성할때는 Apartment thread 모델을 사용한다.

COM thread model: Apartment Thread

  • COM객체는 STA와 호환되다는 것은 의미한다.
  • MTA에 들어간 쓰레드가 이 객체를 생성하면 객체는 새로운 쓰레드를 하나 만들고 그 쓰레드에 STA를 만들어 Proxy 객체 포인터를 리턴한다.
  • 이 모델은 Thread Affinity를 지니는 기능(CRITICAL_SECTION, TLS등)을 사용하거나 UI관련된 기능을 지니는 객체의 경우 추천된다. STA모델이고 필요할 경우 STA를 생성함으로 별도의 동기화 방법은 필요없다.
  • 비베(6.0?)의 경우 Main thread와 Apartment thread를 지원하는데 가능한 이 모델을 구현해야 한다.

COM thread model: Free Thread

  • COM객체는 MTA와만 호환이 된다는 것을 의미한다.
  • STA에 들어간 쓰레드가 Free thread를 지원하는 COM객체를 만들면 객체는 MTA에서 만들어진다.
  • MTA가 존재하면 그 MTA에 만들어지고 아니라면 새로운 쓰레드가 생성되고 MTA를 생성한다. 그리고 Proxy 객체 포인터를 리턴한다.
  • 동기화가 지원되어야 한다.

COM thread model: Both

  • COM객체는 쓰레드가 어떤 Apartment에 속해 있건 모두 호환된다는 것을 의미한다.
  • 쓰레드가 속한 Apartment에 생성되며 무조건 raw pointer를 리턴한다.
  • 생성된 COM객체는 여러 쓰레드가 사용 할 수 있음으로 동기화가 지원되어야 한다.

COM thread model: Neutral (NA)

  • 윈도우즈 2000에서 추가된 모델
  • COM객체는 TNA(Thread Neutral Apartment)와 호환되며 객체는 무조건 TNA에 만들어진다.
  • NA는 Proxy객체 포인터만 리턴한다. 쓰레드 무조건 COM객체의 Proxy 객체에 대한 포인터를 얻게 된다.
  • 프로세스당 하나의 Neutral apartment를 가진다.
  • 동기화가 지원되어야 한다.
  • COM+ 모델이 나온후 지원하기 위해 생긴 Apartment이다.
  • NA는 컴포넌트가 순차적인 접근을 허용할 뿐만 아니라, 어느 스레드에서 든지 수행할 수 있다.
  • Role-base security, 비동기적 객체 실행, out-of-process 서버에서 실행 중인 객체 인스턴스를 참조하는 새로운 내장 모니커(moniker)가 추가되었다.

TNA에 대해 설명하기 전에 우선 STA와 MTA의 반응성과 대해 알아보자. STA의 경우 한 아파트먼트 안에 있는 모든 개체는 하나의 스레드를 통해서만 실행될 수 있다. 이러한 점은 STA 안에 여러 개의 객체가 존재하게 될 때 큰 문제를 지닌다. 이 STA에 속하지 않은 스레드로부터의 메서드 호출은 이 STA를 만든 스레드를 이용해 처리된다고 위에서 배웠다. 그렇다면 만약 STA에 속한 하나의 객체의 메서드를 호출하고 있다면 다른 객체에 대한 메서드 호출은 이 객체의 사용이 끝날 때 까지 기다려야 한다는 것을 쉽게 깨달을 수 있을 것이다. 이것은 STA가 보이지 않는 윈도우를 이용해 호출을 동기화 하기 때문이며 메시지 큐에 쌓인 메시지는 차례차례 처리된다. 이러한 점은 메서드가 블록킹 호출을 할 때 더욱 악화된다. 예를 들어 WaitForSingleMessage등의 메서드 호출을 한다면 사실 스레드는 아무 일도 안하는 상태임에도 불구하고 다른 객체에 대한 메서드 호출은 상당기간 동안 유보하게 된다. 이러한 점은 STA가 흔히 UI스레드가 만들게 되는 아파트먼트 하는 점에서 프로그램 자체의 반응성을 떨어뜨릴 수 도 있다. 물론 이를 해결하기 위해 CoWaitForMultipleHandles 와 같은 API가 있다. 하지만 이러한 API를 사용할 때에는 반드시 Reenterancy에 대한 내용을 숙지하여야만 한다.

반면에 MTA의 경우 이러한 반응성 저하는 없다고 볼 수 있다. 모든 스레드는 원하는 객체에 대한 메서드를 행하기 위해 Cache Thread를 통해 바로 메서드 호출을 할 수 있다. 같은 아파트먼트 안에 속해 있는 다른 객체뿐 아니라 심지어 자기자신이 사용 중이라 해도 이것은 마찬가지다. 또한 MTA에 속하지 않은 스레드에서의 호출 역시 기존의 스레드가 아닌 ORPC CACHE 스레드가 새로 생겨 처리 되므로 매서드 호출이 블록킹 되는 경우는 생기지 않는다. 이러한 점은 프로그래머에게 동기화 지원을 직접적으로 해줄 것을 요구하여 복잡함을 가중시킨다. 하지만 오히려 이러한 점은 숙련된 프로그래머로 하여금 아파트먼트 단위 또는 객체 단위가 아닌 자원단위의 또는 최소한의 잠금을 가지게 되는 단위의 동기화를 할 수 있게 함으로서 훨씬 더 낳은 반응성을 가지게 한다. 이러한 점이 STA 안에 단 하나의 객체 밖에 없다하더라도 MTA 안의 객체가 더욱 우수한 반응성을 지니게 하는 원인이 된다.

 

아파트먼트 종류와 스레딩 모델에 따른 성능 차이

 

한가지 중요한 점은 여러 개의 스레드가 돌아간다고 해서 해서 특정 작업이 빨리 수행 되는 것은 아니라는 점이다. STA에서 블록킹 메서드 호출을 하는 경우가 아닌 한 사실 STA와 MTA의 성능은 MTA에 여러 개의 스레드가 동시접근 할 수 있다는 점만으로 MTA가 우수하다고 할 수는 없다. 만약 다음의 요인을 제외 한다면 사실 STA와 MTA의 성능 차이는 없다고 볼 수 있다.

하지만 실질적으로 COM의 함수 호출이 느려지게 만드는 요인은 따로 있다. 바로 Cross-Apartment Call의 비용이다. STA에서 다른 STA로 또는 STA에서 MTA로 또는 MTA에서 STA로의 함수 호출은 모두 Cross-Apartment Call이라고 볼 수 있다. 물론 다른 프로세스나 네트웍상의 다른 컴퓨터에 대한 호출 역시 Cross-Apartment Call이지만 이 경우 사실 다른 요인이 더욱 성능을 감소시키게 되므로 일단 논의에서 제외하자. 그렇다면 무엇이 문제인가?

이 글의 맨 처음에 Thread Context Switching은 매우 비싼 명령이라고 설명했다. 문제는 Cross-Apartment Call이 이 Thread Context Switching을 일으키게 된다는 점이다. 예를 들어 다른 아파트먼트에 들어간 스레드A로부터 어떤 STA에 있는 객체로 메서드 호출을 생각해 보자. 이 스레드A는 STA에 들어간 스레드B에게 메시지를 보낸 후 대기 상태가 된다. 이후 그러면 스레드B가 그때부터 활성화된다.(일반적으로 GetMessage에서 깨어난다.) 여기서 한번의 Thread Context Switching이 일어난다. 그리고 스레드B는 객체에 대해 실제로 메서드 호출한 후 이 결과 값을 다시 스레드A에게 넘겨준다. 여기서 또 한 번의 Thread Context Switching이 일어난다. 한 메서드 호출당 두번의 Thread Context 호출은 절대 쉽게 넘어갈 수 있는 비용이 아니다. 만약 이 메서드가 매우 자주 불린다면 이것은 엄청난 성능 저하로 이어 질 것이다. MTA의 경우 MTA에 호환되도록 만들어진 객체를 호출할 경우 같은 MTA에 속하는 객체이기만 하면 같은 아파트먼트에 속하므로 Thread Context Switching이 필요 없다는 점에서 STA보다 훨씬 낳은 상황을 만든다. 하지만 MTA에 들어가있는 스레드가 STA에 있는 객체에 접근 할려면 Cross-Apartment Call이 이루어져야만 하며 여전히 Context Switching이 필요하다.

 따라서 특정 객체의 스레딩 모델을 정하거나 스레드가 어떤 아파트먼트안에 들어갈 것인가를 결정할 때에는 매우 주의를 기울여야 한다. 만약 특정 스레드가 Single Threaded 모델을 지니고 있는 객체를 생성해서 주로 사용한다면 그 스레드는 반드시 STA 안으로 들어가야 한다. 단순히 MTA가 더 낳은 성능을 제공하겠지 하고 기대하는 것은 엄청난 실수다. 성능을 향상하는 것은 최대한 Thread Context Switching을 줄이는 것이며 이것은 곧 Cross-Apartment Call의 횟수를 줄이는 것과 직결된다. 물론 MTA와 호환되는 객체를 사용한다면 MTA로 들어가는 것이 성능향상에 도움이 된다.

만약 이정도로만 끝난다면 얼마나 좋을까? 하지만 아직도 몇 가지 고려사항이 남았다. 바로 사용할 특정 객체가 다른 객체들을 사용할 경우다. 이점은 바로 Both 스레딩 모델과 Free Threaded 모델이 따로 존재하는 이유이기도 하다. 지금까지의 논의만을 생각한다면 당연히 동기화 기능을 제공하는 모든 객체는 Both 스레딩 모델일 경우 더욱 우수한 성능을 보일 것으로 기대된다. 하지만 그렇게 간단하지는 않다. 예를 들어 객체(객체 A라고 하자)를 사용하는 스레드(스레드A라고 하자)가 STA에 들어가야만 하고 만약 객체A에 대한 메서드 호출을 할 때 객체A가 다른 객체(객체 B라고 하자)로의 메서드 호출을 한다면? 특히 객체B가 MTA에 존재 한다면? 이것은 매우 심각한 고려 사항이 된다. 객체A가 스레드A와 같은 아파트먼트 안에 존재해야 할 것인가? 아니면 객체B와 같은 아파트먼트 안에 존재해야 할 것인가? 이것은 얼마나 자주 객체A가 객체B의 메서드를 호출하는가에 달려 있다. 만약 객체A가 스레드A와 같은 스레드에 존재한다면 스레드A에서 객체A로의 메서드 호출은 직접적인 메서드 호출이다. 다만 문제는 이 메서드 내에서 객체B를 호출할 경우다. 이 경우 이 메서드를 실행중인 스레드A는 객체B와 다른 아파트먼트 안에 있으므로 Cross-Apartment Call을 하게 된다. 만약 이 메서드 내에서 객체B에 대한 호출이 여러 번 있다면 그 횟수만큼 Cross-Apartment Call을 하게 된다. 하지만 만약 객체A가 객체B와 같은 아파트먼트 안에 존재한다고 하자. 스레드A는 객체 A에 대한 매서드 호출을 할 때 Cross-Apartment Call을 해야만 한다. 하지만 그 이후 객체 A에서 이루어지는 객체 B에 대한 매서드 호출은 직접적인 메서드 호출이다. 이런 점을 종합해보면 만약 객체 A에서 객체 B로의 메서드 호출이 잦다면  객체A는 Both 스레딩 모델을 사용해 스레드A와 같은 아파트먼트 안에 들어가는 것보다는 Free 스레딩 모델을 사용해 무조건 MTA에 들어가는 것이 더 효율적이다.

 

Standard Marshaling과 Custom Marshaling

위에서 채널을 통해 데이터를 직렬화해서 보낸다고 했다. 이렇게 데이터를 직렬화 하는 것을 COM의 용어로는 마샬링이라고 한다. 사실 지금까지의 논의는 객체의 인터페이스를 Standard 마샬링 하는 경우를 가정한 것이다. 그렇다면 마샬링이란 무엇인가? 마샬링이라는 것은 특정 아파트먼트 안에서의 데이터나 인터페이스를 다른 아파트먼트에 전달될 수 있는 스트림 형태로 바꾸는 과정을 의미한다. 우선 데이터의 마샬링에 관한 내용은 이곳의 내용과 관련이 적으므로 다음으로 미루도록 하고 인터페이스의 마샬링에 대한 내용을 살펴보자. 지금까지 다른 아파트먼트에 속한 객체를 호출 할 때 프록시 객체가 만들어진다고 했다. 그렇다면 과연 프록시 객체는 누가 어떻게 만드는 것인가? 이것 즉 프록시를 구현하는 일이 바로 인터페이스 마샬링이 하는 일이다. (좀 더 기술적으로 말하면 두 가지는 동일하지는 않다. 프록시를 구현한다기 보다는 프록시를 만들 수 있는 스트림을 만들어내는 것이 마샬링이며 이것을 특정 아파트먼트에 넘기고 언마샬링하고 COM Library 또는 타입라이브러리 기반의 프록시/스텁 코다가 이 스트림을 해석해 원래의 인터페이스에 대한 호출을 한다. 마샬링에 관한 자세한 논의는 다음에 하도록 하자.) 하지만 앞에서 보았다면 프록시가 하는 일이 그렇게 간단하지만은 않다는 것을 보았을 것이다. 이것을 매번 프로그래머가 해줘야 한다면? 더욱이 지금까지 알아본 프로세스 내에서의 Cross-Apartment Call은 네트워크 상의 다른 컴퓨터의 객체를 부르는 것보다 훨씬 더 간단하다는 것을 상기한다면 네트워크 까지 생각해야하는 마샬링 코드를 매번 프로그래머가 작성해야 한다면 아마 전세계에 COM을 하는 프로그래머는 손가락을 꼽을 정도가 될 것이다. 하지만 다행히도 그렇지는 않다. 비록 C++의 데이터형이 조금 모호함을 가지고 있어 (이 부분에 대한 얘기도 다음에 하자) IDL이라는 언어로 interface를 만들어줘야 하긴 하지만 어쨌든 인터페이스에 대한 정의만 제대로 내려주면 인터페이스의 마샬링 자동으로 이루어지게 된다. 해줘야 할 일은 오직 CoMarshalInterface와 CoUnmarshalInterface를 호출하는 일이다.

하지만 특정한 객체의 경우 이러한 방법을 통한 마샬링이 비효율적인 경우가 있을 수 있다. 비록 IDL이 다양한 기능으로 여러가지 경우에 매우 효율적인 프록시/스텁 코드를 만들어 주기는 하지만 아무래도 객체에 대해 더 잘 알고 있는 프로그래머가 조금 더 효율적인 코드를 만들 수 있는 것은 당연한 것 아닌가? 그러다면 커스텀 마샬링은 어떻게 구현 하면 되는가? 바로 커스텀 마샬링을 하고 싶은 개체한테 IMarshal 이라는 인터페이스를 구현하면 된다.

interface IMarshal : IUnknown
   {
   HRESULT GetUnmarshalClass(REFIID iid, void *pvInterface
       , DWORD dwDestContext, void *pvDestContext, DWORD mshlflags
       , CLSID *pclsid);
   HRESULT GetMarshalSizeMax(REFIID iid, void *pvInterface
       , DWORD dwDestContext, void *pvDestContext, DWORD mshlflags
       , DWORD *pcb);
   HRESULT MarshalInterface(IStream *pstm, REFIID iid, void *pvInterface
       , DWORD dwDestContext, void *pvDestContext, DWORD mshlflags);
   HRESULT UnmarshalInterface(IStream *pstm, REFIID iid, void **ppv);
   HRESULT DisconnectObject(DWORD dwReserved);
   HRESULT ReleaseMarshalData(IStream *pstm);
   };
사실 IMarshal 인터페이스를 구현하는 경우는 매우 드물다. 하지만 이 인터페이스를 이해하고 있는 것은 큰 도움이 된다. COM Library는 객체를 마샬링할 필요가 있을 때 객체에게 QueryInterface를 통해 IMarshal 인터페이스를 구현하고 있는 지 알아본다. 그리고 만약 구현하고 있지 않다면 스탠다드 마샬링을 사용한다. 하지만 만약 구현하고 있다면 여기의 매서드를 통해 인터페이스를 마샬링한다. 가장 중요한 매서드는 MarshalInterface와 UnmarshalInterface 이다. 인자는 거의 명확하다. 직렬화 시킬 저장 공간과 인터페이스의 IID와 포인터 그리고 위치에 대한 정보다. 여기서 위치에 대한 정보는 이것이 같은 프로세스 안에서 사용 될 것인지 아니면 다른 프로세스 또는 다른 컴퓨터에서 사용될 것인지를 결정하는 내용이다. UnmarshalInterface의 경우는 반대로 이 스트림에서 프록시 객체를 만들어 내는 메서드이다.

 

Free Threaded Marshaler

여기서 이전의 논의를 기억해내자. 같은 프로세스 내에서 STA에서 MTA에 있는 객체의 메서드를 부르는 것도 아파트먼트의 경계를 지나게 되므로 프록시를 통해 매서드 호출을 하게 된다고 했다. 하지만 사실 이 객체는 MTA와 호환되는 객체이므로 분명 여러 스레드가 접근하는 것에 대해 안전한 객체일 것이다. 즉 STA에 있는 스레드가 직접 접근해도 문제가 생기지 않는 다는 뜻이다. 하지만 스탠다드 마샬링을 쓸 경우 프록시 객체를 쓰게 되며 이는 스레드 컨텍스트 스위칭을 발생시키고 상당한 성능 저하를 나타내게 된다. 하지만 우리에겐 Custom Marshaling이 있다. MarshalInterface와 UnmarshalInterface를 통해 마샬링 할 때 위치정보가 주어진다는 사실을 상기하며 만약 이 위치정보가 같은 프로세스 내를 가르키고 있을 때에는 프록시 객체가 아닌 실제 객체에 대한 포인터를 돌려 준다면? 그렇다면 만약 다른 아파트먼트에 있는 스레드가 접근 하더라하더라도 스레드 컨텍스트 스위칭을 일으키지 않고 메서드를 호출할 수 있게 된다. 사실 이러한 마샬링 방법은 흔히 사용되는 방법이기도 하다. 그래서 이러한 구현을 이미 해놓고 통합(Aggregation)을 통해 재사용 할 수 있는 API가 제공된다. 이것이 바로 CoCreateFreeThreadedMarshaler 라는 API이다.

HRESULT CoCreateFreeThreadedMarshaler(
LPUNKNOWN punkOuter,
LPUNKNOWN * ppunkMarshaler
);
첫번째 인자로 FTM을 구현하고자 하는 객체를 넣으면 두 번째 인자로 만들어진 객체가 나온다. 이렇게 만들어진 객체는 같은 프로세스내에서는 Cross-Apartment Call로 인한 어떠한 성능저하도 겪지 않는다.

주의 : 커스텀 마샬링을 구현하는 것은 COM+에서 Configured Component로 사용될 수 없음을 의미한다. FTM의 경우도 마찬가지 이므로 주의 하자. 사실 COM+는 마샬링 과정을 (COM+용어로 말하면 Interception이다) 자신이 제공하는 동기화, 보안, Queued Component등의 서비스를 제공하는데 매우 유용하게 이용하고 있다. 만약 Custom Marshaling을 구현한다면 이를 이용할 수 없게 되는 것이다. 만약 이 점이 COM+의 유용성에 큰 단점이 된다고 생각하면 COM+와 함께 새로 소개된 TNA라는 새로운 아파트먼트에 대해서 알아보자.

 

Thread Neutral Apartment

윈도우 2000, COM+는 TNA라고 하는 새로운 아파트먼트 모델을 발표하였다. 이 아파트먼트 역시 MTA 처럼 모든 프로세스에서 하나씩만 존재하는 아파트먼트이다. 하지만 중요한 점은 이 아파트먼트 안에 들어가기 위해 CoInitializeEx를 호출 할 필요가 없다는 점이다. 이 아파트먼트는 CoInitializeEx를 통해 들어가는 것이 아니다. 이 아파트먼트에는 어떤 스레드던 자신이 원한다면 들어갈 수 있다. 즉 STA에 들어가있는 스레드던 MTA에 들어가 있는 스레드건 TNA에 직접 들어가 메서드를 실행시킬 수 있는 것이다.

그러다면 TNA에 있는 객체와 FTM을 구현하는 객체와는 어떻게 다른가? FTM의 경우도 어떠한 아파트먼트에서의 호출이던 객체에 직접 접근하지 않는가? 바로 그것은 TNA에 있는 객체의 경우 다른 아파트먼트에서 이에 객체에 대한 인터페이스를 얻을려면 이것은 역시 직접적인 포인터가 아닌 프록시 객체라는 점이다. 다만 이 프록시 객체가 다른 아파트먼트의 경우처럼 스레드 컨텍스트 스위칭을 일으키지는 않는다. 다만 COM+가 제공하는 서비스에 대한 검사만을 행하고 메서드 호출을 한 스레드가 직접 TNA에 들어가서 메서드를 실행 시킨다.

그렇다면 무엇이 FTM에 비해 낳은가? 아무리 검사만을 행한다고 하지만 분명 FTM처럼 직접적인 포인터를 통한 접근은 아니다. 이는 조금은 속도 저하를 가져올 수도 있는 부분이다. 하지만 그 차이는 스레드 컨텍스트 스위칭과 비교한다면 정말 아무것도 아닌 비용이다. 하지만 이에 비해 COM+의 서비스가 제공하는 각종 서비스를 받을 수 있다는 점을 생각한다면 TNA는 성능과 유연성을 겸비한 매우 가치있는 아파트먼트 모델이 된다.

한가지 더 얘기 하자면 만약 Essential COM등의 책을 보신 분이나 기존에 COM+에 대한 개발방향등을 접하신 분이라면 RTA라는 모델에 대해서 들어보신 분이 있을 것이다. 이 모델은 TNA와 매우 비슷하지만 TNA의 경우 여러 개의 스레드가 동시에 접근 가능하고 RTA의 경우 한 스레드만이 접근할 수 있는 모델이다. (RTA란 모델은 실제 하지 않는다.) 하지만 마이크로소프트는 TNA를 RTA처럼 제한 되게 만들지 않고 기본적으로 여러 스레드의 접근을 허용한 후 COM+의 동기화등을 통해 RTA처럼 사용될 수 있게 만들었다.

 

결론

 

아파트먼트와 스레드에 관한 내용은 사실 보안과 함께 COM의 가장 어려운 부분이다. 사실 이 부분은 COM뿐 아니라 모든 어플리케이션에게 있어 가장 힘든 부분이기도 하다. 게다가 소스코드 거의 한 줄 없는 이 글은 매우 어려울지도 모른다. 하지만 이것은 분명 스레딩 모델의 차이 때문에 기존의 객체를 사용하지 못하는 것 보다는 낫다. 특히 Apartment Threaded 모델의 경우는 스레딩에 관해 생각하지 않고 짜도 멀티스레드 프로그램에서도 쉽게 재사용할 수 있다. 하지만 조금 더 우수한 컴포넌트를 만들기 위해 다른 스레딩 모델에 대해서도 알아보도록 하자. 최근들어 많은 컴포넌트의 개발이 VB를 통해 이루어지면서 Apartment Threaded 밖에 지원하지 않는 VB의 특성상 다른 아파트먼트 모델에 대해 관심을 가지는 경우는 매우 드문 경우 였던거 같다. 아니 VC++를 이용해 COM Component를 만드는 것 자체를 낭비라고 생각하는 것 같다. 이러한 점은 기존의 MTS가 Apartment Threaded모델 만을 지원했던 점과 맞물려 심지어 COM+ Component는 VB만으로 만들 수 있다, 내지는 COM+가 STA만 지원한다는 설까지 만들어 낸 거 같다. 물론 개인적으로 VB를 매우 유용한 언어라고 생각하며 프로젝트시 VC++보다 많이 쓰일 가능성이 높다는 점을 인정한다. 또한 개발기간이 프로젝트에 매우 중요한 요소임도 안다. 하지만  VC++로 좀 더 우수한 성능을 낼 수 있는 모델로 개발할 수 있다는 가능성마저 잊지는 말자. 어쨌든 최소한 개발기간 다음으로 중요한 요인은 성능과 안정성이니까.

또 하나 언급하고 싶은 점은 COM+의 경우 아파트먼트 보다 조금더 세분화 된 Context라는 단위로 객체가 존재하는 공간을 나눈다. 그리고 Context 간의 메서드 호출을 interceptor를 이용해 처리해 각종 서비스를 제공한다. 위의 논의의 상당부분이 이를 통해 논의되는 것이 정확함에도 불구하고 객체를 아파트먼트 단위로만 논의 했다. 이는 스레드에 관한 내용에 집중하기 위해서이다. 또한 마샬링에 관한 많은 내용이 자세히 설명되지 못했는데 이에 관한 논의는 다음에 하도록 하자.

 

참고서적

Essential IDL(Addison Wesley)
Essential COM (Addison Wesley)
COM+ Programming – A Practical Guide Using Visual C++ and ATL (Prentice Hall)
Application Programming for Microsoft Windows 4th(Microsoft Press)
MSJ – House of COM – Don Box

char *

char *orig = "Hello, World!";


size_t origsize = strlen(orig) + 1;    // wchar_t *

const size_t newsize = 100;

size_t convertedChars = 0;

wchar_t wcstring[newsize];

mbstowcs_s(&convertedChars, wcstring, origsize, orig, _TRUNCATE);

wcscat_s(wcstring, L" (wchar_t *)");


_bstr_t bstrt(orig);    // _bstr_t

bstr += " (_bstr_t)";


CComBSTR ccombstr(orig);    // CComBSTR

if(ccombstr.Append(L"(CComBSTR)") == S_OK) CW2A printstr(ccombstr);


CString cstring(orig);    // CString

cstring += " (CString)";


string basicstring(orig);    // basic_string

basicstring += " (basic_string)";


String ^systemstring = gcnew String(orig);    // System.String

systemstring += " (System::String)";

delete systemstring;


wchar_t *

wchar_t *orig = L"Hello, World!";


size_t origsize = wcslen(orig) + 1;    // char *

const size_t newsize = 100;

size_t convertedChars = 0;

char nstring[newsize];

wcstombs_s(&convertedChars, nstring, origsize, orig, _TRUNCATE);

strcat_s(nstring, " (char *)");


_bstr_t bstrt(orig);    // _bstr_t

bstrt += " (_bstr_t)";


CComBSTR ccombstr(orig);    // CComBSTR

if(ccombstr.Append(L" (CComBSTR)") == S_OK) CW2A printstr(ccombstr);


CString cstring(orig);    // CString

cstring += " (CString)";


wstring basicstring(orig);    // basic_string

basicstring += L" (basic_string)";


String ^systemstring = gcnew String(orig);    // System.String

systemstring += " (System::String)";

delete systemstring;


_bstr_t

    BSTR을 캡슐화하는 COM 지원 클래스 (comutil.h, comsuppw.lib 또는 comsuppwd.lib)

_bstr_t orig("Hello, World!");


const size_t newsize = 100;    // char *

char nstring[newsize];

strcpy_s(nstring, (char *)orig);

strcat_s(nstring, " (char *)");


wchar_t wcstring[newsize];    // wchar_t *

wcscpy_s(wcstring, (wchar_t *)orig);

wcscat_s(wcstring, L" (wchar_t *)");


CComBSTR ccombstr((char *)orig);    // CComBSTR

if(ccombstr.Append(L" (CComBSTR)") == S_OK) CW2A printstr(ccombstr);


CString cstring((char *)orig);    // CString

cstring += " (CString)";


string basicstring((char *)orig);    // basic_string

basicstring += " (basic_string)";


String ^systemstring = gcnew String((char *)orig);    // System.String

systemstring += " (System::String)";

delete systemstring;


CComBSTR        ATL의 BSTR wrapper 클래스 (atlbase.h)

CComBSTR orig("Hello, World!");

CW2A printstr(orig);


const size_t newsize = 100;    // char *

char nstring[newsize];

CW2A tmpstr1(orig);

strcpy_s(nstring, tmpstr1);

strcat_s(nstring, " (char *)");


wchar_t wcstring[newsize];    // wchar_t *

wcscpy_s(wcstring, orig);

wcscat_s(wcstring, L" (wchar_t *)");


_bstr_t bstrt(orig);    // _bstr_t

bstrt += " (_bstr_t)";


CString cstring(orig);    // CString

cstring += " (CString)";


wstring basicstring(orig);    // basic_string

basicstring += L" (basic_string)";


String ^systemstring = gcnew String(orig);    // System.String

systemstring += " (System::String)";

delete systemstring;


CString        ATL/MFC의 템플릿 클래스인 CStringT의 기본 구현 클래스.

CString orig("Hello, World!");


const size_t newsize = 100;    // char *

char nstring[newsize];

strcpy_s(nstring, orig);

strcat_s(nstring, " (char *)");


// wchar_t * (이 작업을 하려면 먼저 char *로 바꿔야 한다)

size_t origsize = strlen(orig) + 1;

size_t convertedChars = 0;

wchar_t wcstring[newsize];

mbstowcs_s(&convertedChars, wcstring, origsize, orig, _TRUNCATE);

wcscat_s(wcstring, L" (wchar_t *)");


_bstr_t bstrt(orig);    // _bstr_t

bstrt += " (_bstr_t)";


CComBSTR ccombstr(orig);    // CComBSTR

if(ccombstr.Append(L" (CComBSTR)") == S_OK) CW2A printstr(ccombstr);


string basicstring(orig);    // basic_string

basicstring += " (basic_string)";


String ^systemstring = gcnew String(orig);    // System.String

systemstring += " (System::String)";

delete systemstring;


basic_string        STL 클래스 (<string>)

string orig("Hello, World!");


const size_t newsize = 100;    // char *

char nstring[newsize];

strcpy_s(nstring, orig.c_str());

strcat_s(nstring, " (char *)");


// wchar_t * (이 작업을 하려면 먼저 char *로 바꿔야 한다)

size_t origsize = strlen(orig.c_str()) + 1;

size_t convertedChars = 0;

wchar_t wcstring[newsize];

mbstowcs_s(&convertedChars, wcstring, origsize, orig.c_str(), _TRUNCATE);

wcscat_s(wcstring, L" (wchar_t *)");


_bstr_t bstrt(orig.c_str());    // _bstr_t

bstrt += "(_bstr_t)";


CComBSTR ccombstr(orig.c_str());    // CComBSTR

if(ccombstr.Append(L" (CComBSTR)") == S_OK) CW2A printstr(ccombstr);


Cstring cstring(orig.c_str());    // CString

cstring += " (CString)";


String ^systemstring = gcnew String(orig.c_str());    // System.String

systemstring += " (System::String)";

delete systemstring;


System.String        .NET 프레임워크 클래스 (mscorlib)

String ^orig = gcnew String("Hello, World!");


pin_ptr<const wchar_t> wch = PtrToStringChars(orig);


size_t origsize = wcslen(wch) + 1;    // char *

const size_t newsize = 100;

size_t convertedChars = 0;

char nstring[newsize];

wcstombs_s(&convertedChars, nstring, origsize, wch, _TRUNCATE);

strcat_s(nstring, " (char *)");


wchar_t wcstring[newsize];    // wchar_t *

wcscpy_s(wcstring, wch);

wcscat_s(wcstring, L" (wchar_t *)");


_bstr_t bstrt(wch);    // _bstr_t

bstrt += " (_bstr_t)";


CComBSTR ccombstr(wch);    // CComBSTR

if(ccombstr.Append(L" (CComBSTR)") == S_OK) CW2A printstr(ccombstr);


CString cstring(wch);    // CString

cstring += " (CString)";


wstring basicstring(wch);    // basic_string

basicstring += L" (basic_string)";


delete orig;

'C / C++ > COM / ATL / WTL' 카테고리의 다른 글

COM Apartment  (0) 2008.02.28
아파트먼트 종류와 모델에 따른 반응성 차이  (1) 2008.02.28
WTL Button Popup Menu  (0) 2008.02.24

+ Recent posts