읽어볼만한 글 / C++ At Work: C++/CLI의 설계 원리와 발전 과정

MSIL 연산 지시어 중 하나인 modopt에 관한 정보를 구글을 통하여 검색해보다가, 눈에 띄는 자료 하나를 찾게 되어 블로그에 글을 올려봅니다. C++/CLI의 역사적인 배경을 설명하고, C++/CLI와 같이 레거시 언어에서 확장된 형태의 프로그래밍 언어를 바라보는 관점을 소개하고 있어 매우 유익하다고 생각합니다.

 

출처: http://msdn.microsoft.com/ko-kr/magazine/cc163484.aspx (MSDN Magazine)

 

목차

  • 설계 원리
  • Managed Extensions의 쇠퇴
  • 미래에 대한 기대
  • 프로그래밍의 진화
  • 최종 목표
  • 작별 인사

 

이번 달에는 그 동안 즐겨 사용해 온 질문과 대답 형식에서 벗어나 필자가 온라인에서 찾아낸 훌륭한 문서에 대해 소개하려 합니다. 몇 주 전에 필자는 C++/CLI에서 다음과 같이 const 함수를 선언할 수 없는 이유를 묻는 질문을 받았습니다.

 

// reference class
ref class A {
   void f() const; // NOT!
};

 

대답은 간단합니다. 규칙이기 때문에 어쩔 수 없다는 것입니다. CLI(Common Language Infrastructure)는 const가 무슨 의미인지도 모르는 Visual Basic®, Java, 심지어는 COBOL과 같은 다양한 언어를 지원하기 위해 설계되었습니다. 즉 CLI는 const 멤버 함수가 무엇인지 모르므로, CLI에서 이 함수를 사용할 수 없습니다.

대답을 보내고 나서 문득 기억 한구석에 있던 const와 관련하여 언어에 관계없이 컴파일러 힌트로 무언가 할 수 있었다는 내용이 떠올랐습니다. 곧바로 예전 칼럼을 찾아보니 2004년 7월에 const에 대한 질문에 대답한 적이 있었습니다. 사실 C++/CLI에서 const 멤버 함수는 선언할 수 없지만 const 데이터 멤버와 매개 변수는 가능합니다. 그림 1은 const 정적 데이터 멤버를 가진 참조 클래스를 사용하는 작은 프로그램의 예입니다. 이 프로그램을 컴파일한 다음 ILDASM을 사용하여 디스어셈블하면 다음과 같은 코드를 볼 수 있습니다.

 

field public static int32
    modopt([mscorlib]System.Runtime.CompilerServices.IsConst)
        g_private = int32(0x00000001)

 

////////////////////////////////////////////////////////////////
// To compile type:
//    cl /clr const.cpp
//
#include

ref class A {
int m_val;
// const data member allowed, will generate modopt
static const int g_private = 1;
public:
// public const member could be modified by Visual Basic or other
// programs that don’t know const — so use literal instead.
literal int g_public = 1;
A(int i) { m_val = i; }
void print(); // const; // NO!–const fn not allowed
};

void A::print()
{
printf(“Val is %dn”,m_val);
}

int main()
{
A a(17);
a.print();
}

그림 1: const.cpp

 

MSIL 선언자인 Modopt는 선택적인 한정자이므로 CLI 사용자는 이 내용을 이해할 수 있다면 좋고, 그렇지 않다면 그냥 무시해 버리면 됩니다. 그러나 modreq 함수는 필수 한정자이므로 반드시 이해해야 합니다.

 

modreq의 예로는 Volatile이 있는데, volatile 참조는 운영 체제나 하드웨어, 심지어는 다른 스레드에 의해서도 언제든 변경될 수 있으므로 CLI 사용자가 Volatile을 사용하려면 Volatile이 무엇인지 알아야 합니다. 하지만 const는 선택적인 한정자입니다. 그러나 Managed Extensions는 C++ const 개체를 CLI 리터럴로 변환하는 반면, C++/CLI는 이런 작업을 하지 않기 때문에 주의해야 합니다. 공용 const 데이터 멤버를 선언한 경우 Visual Basic과 같은 언어로 작성된 클라이언트가 그 값을 바꿀 수도 있습니다. CLI 클라이언트에서 값을 바꿀 수 없도록 하려면 그림 1과 같이 개체 리터럴을 선언해야 합니다. 그렇다면 멤버 함수는 어떨까요? const 멤버 함수는 왜 지원되지 않는 것일까요?

 

설계 원리

필자의 대답에 대해 연구하던 중 Microsoft의 Herb Sutter가 작성한 “A Design Rationale for C++/CLI”라는 훌륭한 기사를 찾아냈습니다. Herb는 C++/CLI의 설계자 중 한 사람입니다. 이 문서(앞으로는 ‘설계 원리’로 부름)는 Herb의 블로그에서 찾을 수 있습니다. URL이 너무 길고 복잡하므로 검색 엔진에서 “C++/CLI Rationale”을 찾아보기 바랍니다. 제목이 말해 주듯이 이 문서는 C++/CLI에 대한 대부분의 의문에 대해 설명해 주고 있습니다. 이 문서는 “왜 C++를 확장했는가”라는 질문에서 const 함수에 대한 필자의 궁금증에 이르기까지 모든 것을 답해 줍니다. C++/CLI의 설계 배경과 근본적인 개념을 알고 싶다면 이 문서를 반드시 읽어볼 것을 권합니다. 여기서는 모두 소개할 수 없으므로 중요한 부분만 요약해서 소개하겠습니다.

일단 가장 중요한 질문, 왜 C++를 확장했는가에 대해 알아보겠습니다. 간단하고 분명한 대답은 “C++를 CLI의 중요한 언어로 사용하도록 하기 위해서”입니다. Microsoft® .NET Framework는 Windows® 개발의 미래이며 COBOL 조차도 CLI를 지원합니다. 즉, C++/CLI는 C++의 지속적인 성공을 위해 설계된 것입니다.

하지만 왜 굳이 C++를 사용할까요? ^ 또는 %와 같은 새로운 개념도 있고 ref, value, property와 같은 새 키워드도 있는 다른 언어를 확장하면 어떨까요? 사실 다른 방법은 없었습니다. ‘설계 원리’에서는 Bjarne Stroustrup의 말을 인용해 이러한 질문에 대답하고 있습니다. “우리가 필요한 거의 모든 개념은 기본 속에서 찾을 수 있습니다. 라이브러리를 사용하는 방식이 정말로 실현 불가능할 경우에만 언어 확장 방식을 사용해야 합니다.”

평범한 C++ 코드의 경우 CLI를 대상으로 하는 것은 다른 프로세서를 위한 컴파일러를 만드는 것과 같습니다. 여기까지는 문제가 없으나 CLI는 특별한 코드 생성이 필요한 새로운 개념을 도입했으며 이는 C++에서 간단히 표현할 수 있는 것이 아닙니다. 예를 들어 속성에는 특별한 메타데이터가 필요합니다. 라이브러리나 템플릿 없이 속성을 구현할 수는 없습니다. ‘설계 원리’에서는 속성을 비롯하여 다른 CLI 기능을 대체할 수 있는 몇 가지 구문을 소개하고 있으며, 이러한 구문이 결국 왜 사용될 수 없었는지에 대해서도 설명하고 있습니다.

 

Managed Extensions의 쇠퇴

‘설계 원리’에서는 또한 Microsoft에서 Managed Extensions를 더 이상 사용하지 않기로 결정한 이유에 대해서도 설명하고 있습니다. Managed Extensions에서 관리된 개체와 네이티브 개체 모두에 *를 사용한 것은 C++와 CLI를 통합하기 위한 영리하고 용감한 시도였지만, 참조와 네이티브 개체의 중요한 차이점이 분명치 않았습니다. 이러한 두 종류의 포인터는 같아 보이지만 동작은 다릅니다. 예를 들어 소멸 구문, 복사 생성자, 생성자/소멸자 내에서의 가상 호출 등이 모두 포인터가 가리키는 개체가 무엇인지에 따라 다르게 동작하며 이는 끔찍한 일입니다. ‘설계 원리’에서는 “불필요한 차이점을 감추는 것도 중요하지만 필요한 차이점은 반드시 공개해야 한다.”고 설명하면서 다시 Bjarne의 말을 인용하고 있습니다. “중요한 작업은 반드시 눈에 띄어야 합니다.” 네이티브 클래스와 관리되는 클래스는 근본적으로 다른 것이므로 무시하기 보다는 그 차이점을 부각시키는 것이 나은 방법일 것입니다. 이를 위해 여러 가지 메커니즘이 고안되고 버려졌으며, 결국 C++/CLI 팀은 gcnew, ^(핸들) 및 %(참조 추적)와 같은 개념을 도입했습니다. 이렇게 관리되는 클래스와 네이티브 클래스를 분리하면 뜻밖의 효과도 누릴 수 있습니다. 예를 들어 gcnew 연산자를 사용하여 관리되는 개체를 할당하면 언젠가 관리되는 힙에서 네이티브 클래스가 할당되고, 그 반대 방향의 작업도 가능할 수 있다는 가능성을 열어두게 됩니다.

Managed Extensions는 왜 __gc나 __value와 같이 보기 흉한 밑줄 키워드를 사용하는지 궁금했던 적이 있습니까? 이는 Managed Extensions가 C++ 표준을 엄격하게 따르기 때문입니다. C++ 표준에서는 새 키워드가 필요하다면 이름 앞에 밑줄 두 개를 붙이라고 지정하고 있습니다. 꼭 그래야 할까요? Microsoft에서 __gc라는 키워드를 도입했을 때, __에 대해 프로그래머들은 강력한 항의를 보냈습니다. 뭉치면 못할 것이 없습니다. 밑줄 문자 밖에는 잃을 것도 없습니다. 밑줄을 사용하면 코드를 읽기 어려울뿐더러 마치 어셈블리 프로그램과 같이 난해하게 보이기도 합니다. 결국 C++/CLI에는 밑줄이 없는 ref 및 value가 추가되었습니다. C++에 새 키워드를 추가한 셈이지만 표준을 따르지 않아도 문제될 것은 없습니다. Bjarne은 이렇게 말합니다. “제 경험에 따르면 사람들은 자신이 고안한 매우 복잡하고 어려운 개념을 사용하기 위해 새 키워드를 만드는 것을 지나치게 좋아합니다. 그 결과, 점차 새로운 키워드들은 우리가 흔히 사용하는 용어와는 전혀 다르게 변해가고 있습니다.” 맞는 말입니다. 필자는 프로그래밍에 대한 Bjarne의 심리학적 설명을 매우 좋아합니다. C++/CLI에서 밑줄이 사라진 이유는 이것입니다. 예약된 키워드가 아닌 전후 관계에 따라 사용되는 키워드를 만듦으로써 이미 이 키워드를 변수나 함수 이름으로 사용하는 프로그램과의 충돌 문제도 방지할 수 있습니다.

 

궁금증이 많은 사람이라면 밑줄을 없애는 과정에서 gc가 어떻게 ref로 바뀌었는지 궁금할 수도 있습니다. ‘설계 원리’에서는 관리되는 클래스가 관리되는(가비지가 수집되는) 힙에 있다는 것이 중요한 것이 아니라 그 참조 구문이 중요하다고 합니다. 핸들(^)이 포인터가 아닌 참조처럼 사용되는 것도 같은 원리입니다. 이와 같이 ‘설계 원리’를 읽어보면 모든 것이 분명해집니다.

분명한 차이점을 드러내는 것이 중요하다면 그렇지 않은 것을 감추는 것도 중요합니다. 예를 들어 모든 연산자 오버로딩은 개체가 네이티브인지, ref인지, 또는 value인지에 관계없이 C++ 프로그래머가 예상하는 그대로 작동합니다. C++/CLI 문서에서 차이에 대해 설명한 또 하나의 예로 다음 코드를 보겠습니다.

 

// ref class R as local variable
void f()
{
   R r; // ref class on stack???
   r.DoSomething();
   // ...
}

여기서 r은 스택 개체처럼 보이지만 누구라도 관리되는 클래스는 스택에 물리적으로 넣을 수 없다는 것을 알 것입니다. 관리되는 클래스는 관리되는 힙에 할당해야 합니다. 그렇다면 무슨 일이 벌어질까요? 컴파일러는 이 코드를 다음과 같은 의미로 해석하게 됩니다.

// how the compiler sees it.
void f()
{
   R^ r = gcnew R; // allocate on gc heap
   try
   {
      r->DoSomething();
      ...
   }
   finally
   {
      delete r;
   }
}

 

결과적으로 개체는 실제 스택에 할당되지 않습니다. 여기서 중요한 것은 로컬 변수 구문이 모든 C++ 프로그래머가 예상하는 방식으로 작동한다는 점입니다. 특히 f를 벗어나기 전에 r의 소멸자가 호출되는 것은 C++/CLI에서 결정적 소멸을 복원한 이유와 같은 이유로 설명할 수 있습니다. 소멸을 위해 모든 C++ 프로그래머가 알고 사용하는 것과 동일한 구문을 따르고 있다는 것입니다. 비결정적 소멸은 Managed Extensions의 기능 중 최악의 것으로, C++/CLI는 이 문제를 해결하기 위해 소멸자를 Dispose에 매핑하고 종료자를 위한 특별한 구문인 !를 도입했습니다. 참조 개체가 사용하는 메모리는 가비지 수집기가 활동하기까지는 그대로 남아있게 되지만 그리 큰 문제는 아닙니다. C++ 프로그래머에게는 필요할 때 소멸자가 실행되는 것이 중요하지, 개체가 파괴될 때 메모리를 회수하는 것이 중요하지는 않습니다. C++ 프로그래머는 파일 핸들, 데이터베이스 잠금과 같이 메모리 리소스가 아닌 개체를 초기화/해제하는 데 생성/소멸 패턴을 자주 사용합니다. C++/CLI를 사용하면 참조와 네이티브 클래스에 대해 여러분이 기대하는 방식 그대로 친숙한 생성/소멸 패턴을 사용할 수 있습니다. 결국 이와 같은 이유로 인해 Microsoft는 Managed Extensions의 문제를 인식하고 이를 수정하는 작업을 포기하게 된 것입니다.

미래에 대한 기대

‘설계 원리’의 흥미로운 단원 중에 “Future Unifications(미래의 통합)”이라는 부분이 있습니다. 이 단원에서는 C++/CLI가 미래에 어떤 방향으로 발전할지에 대해 약간의 힌트를 주고 있는데, 예를 들어 지금은 관리되는 클래스에서 네이티브 클래스를 파생할 수 없고 그 반대로도 마찬가지지만 “기본” 클래스를 데이터 멤버로 추가하고 컨테이너 인스턴스를 호출하는 것만 담당하는 통과 래퍼를 만들면 같은 결과를 만드는 해결책을 구현할 수 있습니다. 아주 좋은 방법으로 보이며 컴파일러가 이러한 작업을 지원하지 못할 이유가 없어 보입니다. 컴파일러는 모든 CLI 부분을 포함하는 하나의 CLI 개체와 모든 C++ 부분을 포함하는 하나의 C++ 개체로 구성된 혼합된 개체를 만들어 사용할 수 있을 것입니다.

이에 대한 흥미로운 일화도 소개되어 있습니다. ‘설계 원리’의 저자가 이러한 혼합 클래스에 대한 아이디어를 Bjarne Stroustrup에게 처음 보여주었을 때, Bjarne은 서재로 가서 책을 펴 보여주면서 “C++에서 개체를 단일 메모리 블록에 연속해 저장할 필요는 없다.”고 말했다고 합니다. 이때까지 누구도 비연속적인 개체의 이점을 알지 못했으며 .NET과 CLI가 개발될 것이라고 예상하지도 못했습니다. 비연속이라는 문을 열어야 한다는 Bjarne의 고집이 결국 혼합된 개체를 가능하게 만든 것입니다. C++/CLI의 나중 버전에서 이러한 기능을 보게 되도 놀라지 마십시오. 여기서 주는 교훈은 여러분이 새 언어나 복잡한 프로그램을 설계하여 계속 사용될 것으로 기대한다면 단지 약간의 편의를 위해 절대 불필요한 가정을 해서는 안 된다는 것입니다.

이 밖에도 ‘설계 원리’에서는 재미있는 몇 가지 사실도 소개하고 있습니다. Microsoft가 C++/CLI의 원래 내부 이름으로 사용한 것은 MC^2였다고 합니다. M은 “관리되는”의 의미, C는 C++를 의미하며 ^2는 앨버트 아인슈타인의 공식에서 차용한 것입니다. 하지만 결국 이름이 너무 장난스럽다는 이유로 채택되지 못했습니다. 필자도 동의하는 부분입니다. MC^2라는 이름을 그대로 사용했다면 아마도 받아들이기 힘들었을 것입니다. 이름을 C++/CLI로 결정한 데는 Bjarne이 C++의 이름에 대해 말한 것과 같은 이유가 있었습니다. Bjarne은 “이름을 C++라고 정한 것은 짧고, 쉽게 해석이 가능하며 ‘부수적인 C’와 같은 형식이 아니었기 때문입니다”라고 말했습니다. C++/CLI 역시 C++를 먼저 넣고, “부수적인 C++”라는 표현을 피한 것뿐입니다.

‘설계 원리’에서는 속성, gcnew, 제네릭 및 const 등의 다른 C++/CLI 확장 기능에 대한 설명과 더불어 마무리에는 유용한 질문과 대답 단원을 소개하고 있습니다. 자세한 내용은 ‘설계 원리’를 다운로드하여 읽어보십시오. Const에 대해 다시 얘기해 보겠습니다. C++/CLI는 왜 const 데이터는 허용하면서 const 함수는 허용하지 않는 것일까요? 간단히 얘기하면 CLI는 modopt/modreq를 함수에 직접 넣는 것을 허용하지 않기 때문입니다. 이 정보를 메타데이터에 인코딩하는 방법이 있기는 하지만, 아직 테스트되지 않았습니다. 그리고 ‘설계 원리’에서는 언젠가 이러한 기능이 추가될 것이라고 조심스럽게 시사하고 있습니다.

프로그래밍의 진화

C++/CLI는 C++를 CLI의 중요한 언어로 만들었으며 ‘설계 원리’를 읽어보면 이러한 결정에 대해 작은 의심도 없었음을 알 수 있습니다. C++는 아직도 시스템 프로그래밍 분야에서 최고의 언어이며, 이는 다른 어떤 언어보다도 CLI에 직접적으로 액세스할 수 있고 아직까지도 널리 사용되는 예전 Win32® API를 호출해야 할 경우 C로도 확장될 수 있기 때문입니다.

C++/CLI가 얼마나 중요하며 무엇을 나타내는지 이해하기 위해서는 현재 우리가 프로그래밍의 진화 단계에 어느 지점에 있는지 생각해 보아야 합니다. 간단하게 이러한 내용에 대해 살펴보겠습니다. 예전에는 프로그래머가 토글 스위치를 사용하여 프로그램을 작성했습니다. 여기서 발전한 것이 종이 테이프였고, 각 컴퓨터가 고유한 “기계” 언어를 가지는 단계로 발전했습니다. 컴퓨터가 진화함에 따라 프로그래머도 이러한 새 기계를 대상으로 하는 프로그램을 만들어야 했고, FORTRAN, BASIC, C와 같은 고급 언어가 개발되었으며 고급 언어를 각 컴퓨터에 대한 명령으로 변환해 주는 “컴파일러”가 사용되기 시작했습니다. 그림 2에서 이러한 과정을 보여 줍니다. 이제 프로그램을 만들어 컴파일하면 다른 컴퓨터에서 쓸 수 있게 된 것입니다. C가 시스템 프로그래밍을 위한 최적의 언어로 선택된 이유는 “고급 언어 중 가장 저급 언어”였기 때문입니다. 이는 사람이 사용할 수 있는 언어 중 가장 기계와 친숙한 언어라는 의미입니다. 근래에 사용되는 대부분의 운영 체제는 C로 작성되었으며 성능이 중요하고 하드웨어 조작이 필요한 일부분만 어셈블리 언어로 코딩되어 있습니다.

오랜 세월 후에 C를 개선하여 개체 지향 방식 등 많은 새로운 기능을 도입한 C++가 등장했습니다. Bjarne은 이렇게 말했습니다. “C++는 저자나 그의 친구들이 어셈블러, C 또는 다양한 근래의 고급 언어를 사용해 프로그래밍할 필요가 없도록 하기 위해 설계되었습니다. C++의 주 목적은 모든 프로그래머가 좋은 프로그램을 편하고 즐겁게 작성할 수 있도록 하는 것입니다.” C++는 훌륭한 언어이지만, 고급 언어는 서로 호환되지 않는 경우가 많습니다. C++에서 코드를 작성하면 이를 BASIC과 같은 다른 언어에서 사용할 수 없으며, 가능하다 해도 상당한 어려움이 따릅니다. 각 언어는 각자 고유한 영역 내에서 처리되기 때문입니다. 이러한 특징은 독립 실행형 응용 프로그램에는 문제가 없지만 응용 프로그램이 보다 복잡해지고 환경이 분산되면서 공유 코드에 대한 필요성이 대두되기 시작했습니다. 결국 프로그래머는 완벽히 캡슐화되어 재사용이 가능한 구성 요소를 원하게 된 것입니다. 이러한 구성 요소를 사용하면 각 조각을 만들어 결합하는 것으로 응용 프로그램을 만들 수 있을 것이며 모든 조각이 같은 언어로 작성될 필요도 없을 것입니다.

이러한 다양한 구성 요소를 통합하려는 시도는 몇 년에 걸쳐 진행되었습니다. 처음에는 C 런타임이나 printf와 같은 라이브러리를 사용한 언어가 등장했고, Windows가 사용되면서 메모리 절약을 위한 DLL의 지연 로딩(DLL에 Dynamic이란 이름이 사용된 이유임)이 제공되었습니다. 또한 Visual Basic이나 COBOL과 같은 언어에서 가져오기 구문을 사용하여 컴파일러가 C와 연계된 호출을 DLL에 보내는 방식으로 DLL 사용이 가능해짐으로써 언어의 상호 운용성이 증대되는 효과가 있었습니다. 하지만 응용 프로그램과 DLL의 연결은 너무 강했으며 부서지기도 쉬웠습니다. 각 응용 프로그램은 DLL의 각 항목에 대한 이름과 서명을 알아야 했고 DLL에서 응용 프로그램으로의 호출은 함수 포인터를 콜백으로 전달해야 했으므로 상당히 귀찮은 일이었습니다. 때문에 프로그래머들이 VBX를 개발했고, 이 것이 OCX를 거쳐 COM으로 발전했습니다. 다행히도 COM은 언어 중립적인 것으로, COM에는 “형식 라이브러리”가 있어 연결 시 함수 이름을 알 필요가 없고 런타임에 문의하는 방식이 가능했습니다. COM은 상당히 유용하지만 프로그래밍이 매우 어렵다는 단점이 있습니다. 아이러니하게도 C++에 기반을 두고 있으면서도 C++에서 프로그래밍하기가 가장 어렵습니다. COM에는 다른 문제도 있습니다. 너무 저급 언어이며 보안이나 메모리 관리에 대한 기능이 빈약하다는 것입니다.

최종 목표

현재는 2007년이며 우리에게는 .NET Framework와 그 표준 부속물인 CLI가 있습니다. CLI는 프로그래밍 언어와 컴퓨터 사이에 새로운 추상 계층을 삽입하여 재사용성 문제를 완전히 다른 방법으로 해결하였습니다. 이제 시스템 명령을 생성하는 대신 컴파일러는 MSIL 코드를 생성하며 CLI 가상 시스템/JIT(just-in-time) 컴파일러가 이를 즉시 기계어 코드로 컴파일합니다. 가상 시스템(Virtual Machine, VES 또는 Virtual Execution System)은 컴퓨터를 위한 추상 계층을 제공합니다. 가상 시스템은 새로운 개념이 아니라 사실 오랫동안 사용되어 왔습니다. Pascal이나 ZIL(필자가 한때 몸담았던 Infocom이란 회사에서 게임 개발을 위해 내부적으로 사용하던 언어, Zork Implementation Language의 약자임)과 같은 언어는 고급 언어를 P-코드(또는 Z-코드)로 컴파일하여 가상 시스템에서 해석하는 방식을 이미 사용하고 있었습니다. 하지만 CLI는 모든 언어가 사용할 수 있는 공용 가상 시스템(CLI에서 C가 의미하는 부분)을 제공한다는 점이 다릅니다. CLI는 클래스, 속성, 상속성, 리플렉션과 같은 기본적인 개념을 지원합니다. VES/VM는 메모리 관리와 보안 기능도 제공하므로 프로그래머는 악의적인 바이러스에 문을 열어 주는 버퍼 오버런이나 기타 버그를 걱정하지 않아도 됩니다. .NET Framework와 CLI가 널리 사용되면서 Visual Basic, Fortran, COBOL, C#과 같은 고급 언어는 점차 그 경계가 사라지는 분위기이며, 클래스, 상속성, 멤버 함수 등의 CLI 기반 개념을 지원해야 하기 때문에 C++ 역시 그 영향을 받고 있습니다. 각 언어는 여전히 독자적인 영역을 가지고 있으므로 프로그래머가 자신의 도구를 .NET Framework을 사용하도록 바꿀 필요는 없으며 몇 가지 새로운 개념만 배우면 됩니다.

이제 프로그래머는 원하는 언어를 선택하여 클래스를 작성할 수 있으며 언어에 관계없이 다른 프로그래머가 작성한 클래스를 사용할 수도 있습니다. 어떤 언어를 사용해도 모든 구성 요소를 프로그래밍할 수 있고, 약간의 프로그래밍 노력만 거치면 모든 구성 요소가 매끄럽게 서로 연결됩니다. 또한 CLI의 보안, 가비지 수집 등 인프라 기능(CLI에서 I가 의미하는 부분)의 혜택을 받을 수 있습니다. 이후에 Microsoft에서 새로운 CLI 기능을 추가하면 모든 언어가 그 혜택을 받을 수 있을 것입니다. Windows Vista™와 .NET Framework 3.0(10,000개의 새 클래스와 더불어 Windows Presentation Foundation, Windows Communication Foundation, Windows Workflow Foundation 및 Windows CardSpace™와 같은 새로운 기술이 연계되어 있음)이 개발되면서 Windows는 CLI 클래스로서 다시 만들어지고 있습니다. 이제는 재사용 가능하고, 언어 중립적이며 상호 운용이 가능한 구성 요소라는 목표에 도달한 것으로 보입니다. 이는 매우 큰 패러다임의 변화로, 전진적인 진화를 거쳐 놀라운 수준에 이른 것입니다. 이제는 프로그래밍이 아주 쉬워지고 있습니다.

이러한 모든 사항을 곰곰이 생각했을 때 C++가 새로운 세계에 적응하는 것은 당연한 일로 생각됩니다. C++/CLI이 등장한 이유가 바로 이것입니다. C++/CLI가 없다면 C++는 Windows 프로그램에 사용할 수 없는 유일한 근대 프로그래밍 언어로 남았을 것입니다. 또는 점차 그 수명을 다하여 역사의 변두리로 밀려났을 수도 있습니다. 하지만 C++/CLI가 있기에 그런 일은 일어나지 않습니다. C++를 좋아하는 필자와 같은 프로그래머는 걱정 없이 계속 사용해도 됩니다. 그림 2는 프로그래밍 언어의 패러다임 변화와 그 속에서 C++가 차지하는 위치, 그리고 “/CLI”가 없다면 어떨지를 간략하게 보여 줍니다.

프로그래밍의 진화 과정

그림 2: 프로그래밍의 진화 과정 (더 크게 보려면 이미지를 클릭하십시오.)

 

작별 인사

이제 작별 인사를 하려고 합니다. 그 동안 필자가 기고해온 C++ At Work 칼럼이 이번 호를 마지막으로 여러분 곁을 떠납니다. 많은 칼럼이 중단되는 와중에서도 그 동안 꽤나 오래 이 칼럼을 기고해 온 것을 자랑스럽게 생각합니다. 칼럼이 중단되는 것은 Microsoft나 MSDN®Magazine에서 C++의 위상이 약해진 것 때문은 아니며, 14년간에 걸친 164개 칼럼(이번이 165개 째)을 기고하면서 필자가 많이 지쳤기 때문입니다. 재미있는 일화를 하나 소개하면, 편집장인 Josh Trupin은 필자를 “MSDN Magazine의 Cal Ripken”이라고 부르기도 했습니다. 필자는 야구 팬은 아니지만(축구를 더 좋아합니다) Cal Ripken이 누군지는 압니다. 그래서 Cal이 몇 경기나 출장했는지 웹에서 검색해 보았더니 2,632 경기였습니다. 그리고 그의 이력에서 “많은 팬들은 Cal이 충분히 휴식을 취했다면 더 나은 플레이어가 되었을 것이다.”라고 말한 내용도 보았습니다. 필자에게도 해당되는 말이라 생각합니다. 하지만 이것이 필자가 다시는 MSDN Magazine에 기고하지 않을 것임을 의미하지는 않습니다.

칼럼을 마치면서 그 동안 필자의 많은 오류와 실수를 지적해주고 격려를 통해 힘을 준 많은 독자들에게 감사의 말을 전하고 싶습니다. 어떤 독자는 필자가 칼럼을 게시하기 전에 솔루션을 테스트하는 수고를 해주기도 했습니다. 또한 그 동안 함께 일해온 MSDN Magazine의 많은 동료들에게도 감사의 말을 전합니다. 과거에서 현재로 순서 없이 나열해보면 Steve, Josh, Joanne, Eric, Etya, Terry, Laura, Joan(두 명 모두), Nancy, Val, 그리고 Milo가 떠오릅니다. 빠진 사람이 있는지 모르겠네요. 이들은 그 동안 필자를 여러모로 많이 지원해 주었으며 다양한 분야와 내용의 집필이 가능하도록 필자에게 자유로운 공간을 허락해 주었습니다. 그 중에서도 특히 Gretchen Bilson에게 감사하고 싶습니다. 몇 년 전 이미 MSDN Magazine을 떠난 그녀는 MSDN Magazine이 Microsoft Systems Journal이던 1993년에 필자를 고용한 고마운 분입니다.

여기까지입니다. 하지만 영영 떠나는 것은 아닙니다. 아놀드 슈왈츠제네거가 말했듯이 저는 돌아올 것입니다. 즐거운 프로그래밍을 계속하시기 바랍니다.

 

Gak 자료: 다양한 질문

 

딱딱한 내용은 여기서 끝내고, 그 동안 필자가 받았던 특별하고 재미있는 몇 가지 질문에 대해 소개하면서 칼럼을 마치려 합니다. 필자는 이러한 자료를 Gak라는 폴더에 수집해 두었으며 범주별로 분리해 두었습니다. 예를 들어 언어에 대한 질문은 따로 모아 두었습니다.

 

C++를 어디서 다운로드할 수 있나요?

 

많은 사람들이 C++를 높게 평가합니다. 이유가 무엇일까요? Visual Basic이나 Delphi에서는 할 수 없지만 C++에서 할 수 있는 작업은 어떤 것이 있을까요? 이에 대해서는 필자가 많은 경험을 가지고 있습니다. Visual C++®를 사용하더라도 필요한 모든 것은 항상 찾을 수 있었습니다. 중견 프로그래머들이 이 언어를 사용하는 이유가 바로 이것입니다.

 

“중견 프로그래머”에 필자는 포함되지 않았으면 좋겠네요. 다음 질문은 많은 사람이 어려워하는 포인터에 대한 것입니다.

아직도 포인터(var, object, functional)를 잘 이해하지 못했습니다. 설명을 해 주시거나 공부할 자료를 보내 주실 수 있나요?

 

C++를 처음 배우고 있으며 포인터가 무엇이고 어떻게 사용되는지 궁금합니다. 많은 책을 보았지만 너무 복잡하게 설명되어 있습니다. 포인터란 특정한 메모리 위치를 가리키는 것으로 알고 있는데 이런 개념이 왜 필요한지 모르겠습니다. 아마도 컴퓨터가 각 위치를 자동으로 추적하려는 것일까요? 답변 부탁드립니다.

 

필자는 이 질문을 동료 필자인 Matt Pietrek과 John Robbins에게 보내어 조언을 구했습니다. Matt는 이렇게 말하더군요. “그 사람의 생각이 맞다. 포인터는 오래되어 불필요한 개념이며 이제는 배울 필요가 없다.” John은 한술 더 뜨더군요. “포인터는 개 이름인줄 알았다. 뭔가 다른 의미가 있나?”

 

다음은 컴파일러에 대한 질문입니다.

 

Paul씨, 컴파일하는 동안 링크 MFC 라이브러리는 어떻게 컴파일합니까? 때로는 DLL을 컴파일하기도 합니다. 지금까지 이 소스는 컴파일하지 않았는데요. 컴파일하는 방법이나 소스를 좀 보내 주십시오. 감사합니다.

 

더 자주 받는 질문은 Windows에 대한 것입니다.

 

키보드/마우스 감지 프로그램을 만들고 있는데 문제가 있습니다. 예를 들어 “시간을 세는” 프로그램이 선택된 상태에서 작동하고 있을 때 선택이 해지된 다음에도 계속 작동하도록 하려면 어떻게 해야 할까요? 여러 가지 방법을 시도해 봤지만 실패해서 이렇게 문의 메일을 보냅니다.

 

중요한 것은, 질문에서 말하는 키보드/마우스 감지 프로그램이 무엇을 말하는 것인지 모르겠다는 점입니다. 필자는 이런 프로그램을 만들어 본 적이 없습니다. 때문에 질문의 의도도 잘 모르겠네요. 아쉬운 일입니다.
다음은 연산에 관련된 질문입니다.

 

문자를 인코딩했다가 나중에 디코딩하는 작업을 수행하는 프로그램을 만들고 있습니다. 그런데 디코딩 부분에서 % 연산을 유지해야 하기 때문에 어려운 점이 있습니다. 나누는 숫자와 나머지는 알고 있는데 이것으로는 애초에 나눈 숫자가 무엇인지 알 수 없습니다. 예를 들어 연산의 결과로 나머지가 22이고 이 연산은 (숫자)%63이었다면 이 숫자는 무엇일까요? 도움을 부탁 드립니다.

 

솔직히 어려운 점이 있다고 말한 이유를 이해할 수 없습니다. n%a=b라는 연산에서 a와 b는 아는데 n을 모르겠다는 의미 아닌가요? 수학 시간에 열심히 듣지 않았던 분인가 봅니다. 다음과 같이 가끔은 “제 숙제를 대신 해주실래요?”라는 질문도 종종 받곤 합니다.

 

프로그래밍에 대해 잘 모릅니다. 20개의 숫자를 입력하고 이를 내림차순으로 정렬해야 하는데 어떻게 해야 할까요?

 

다음 두 프로그램을 만들어야 하는데 분석을 좀 부탁드립니다. 내일 정오까지 완료해야 하는데 도와주세요!

  • 사용자의 최초 잔액과 예금 인출액을 입력 받아 사용자가 -1을 입력하거나 잔액이 0이하가 될 때까지 반복하는 프로그램을 작성하시오. 사용자의 최초 잔액, 최후 잔액, 인출 횟수와 평균 금액을 출력하시오.
  • 임금 지불 내역 프로그램을 작성하고 직원의 격주 순수 임금을 출력하시오. 직원의 근무 시간과 시간당 임금을 입력 받고 초과 근무에 대해서는 가중치를 적용해야 합니다. 공제액은 총 급여의 18%로 책정하여 계산하십시오. 사용자의 급여와 연봉을 출력하십시오.

 

세 번째 것도 있는데 생략하겠습니다. 때로는 아주 긴 소스 코드가 포함된 질문도 받곤 하는데, 읽지도 않고 지워버립니다. 또는 필자의 칼럼과 전혀 관계없는 Visual Basic, Microsoft Access™, Excel® 프로그래밍 등에 대해서도 질문을 받는 경우가 있으며 “컴퓨터가 망가졌는데 어떻게 해야 할까요?” “시작 단추를 안 누르고 컴퓨터를 끄는 방법은 없나요?”와 같은 매우 사려 깊은 질문도 간혹 있습니다. 제발 이런 질문은 Raymond Chen의 블로그에 올려 주시기 바립니다. 다음과 같이 특정한 범주로 분류하기 힘든 질문도 있습니다.

 

Paul씨, 일반적인 개발자가 하루에 작성하는 Visual C++ 코드의 평균 줄 수에 대한 통계 자료가 있나요? 도움을 바랍니다.

 

존경하는 DiLascia씨, 어떻게 하면 당신처럼 될 수 있을까요?

 

안녕하세요. 그 동안 MSDN을 많이 구매했고 거기서 당신 이름을 보았습니다. “Microsoft에 취직하려면 어떻게 해야 할까요?” 저는 Visual C++ 프로그래밍을 좋아하며 기술과 가치, 미래를 위해 Microsoft에서 일하고 싶습니다. 저는 MFC로 멀티미디어 프로그램을 만들 수 있으며 학교 성적도 좋은 편입니다. 그러나 영어 실력은 그저 그렇습니다. 프로그래밍이 너무 재미있어서 5년간 여자 친구도 사귀지 않았습니다. 엉뚱한 질문이라 생각하실지 모르지만 답장을 주셨으면 좋겠습니다. Microsoft에서 일하려면 어떻게 해야 하는지 알려 주십시오. 감사합니다.

 

영어 실력은 그저 그렇지만 무엇보다도 이 청년의 열정적인 자세에 박수를 보내고 싶습니다. 필자는 “죄송합니다. 모르겠네요.”라고 보낼지라도 모든 질문에 대답하려 노력합니다. 실제로 필자가 답변을 보내면 놀라는 독자들이 많았습니다.

 

Paul씨, 당신 같은 유명한 저자가 모르는 사람을 지속적으로 도와주리라고는 생각지 못했습니다. 당신은 지식이 뛰어난 전문가일 뿐만 아니라 아주 좋은 사람이군요. 감사합니다.

 

저 역시 감사합니다. 마지막으로 재미있는 질문 하나를 소개합니다.

 

Windows 프로그래밍에서 심령의 창 (Psychic Window)이란 무엇인지요? 철자를 정확히 모르겠네요. 친구가 물어본 것이라서요. 부탁 드립니다.

 

이 질문에 대한 대답은 찾을 수 없었습니다. 심령의 창을 찾으신다면 초능력자에게 물어보시는 것이 나을 것 같네요. 누구 아시는 분이 있으면 필자에게 텔레파시를 보내 주십시오.

 

저자 소개

Paul DiLascia는 프리랜서 소프트웨어 컨설턴트이자 저명한 웹/UI 디자이너입니다. 저서로는 Windows++: Writing Reusable Windows Code in C++(Addison-Wesley, 1992)가 있습니다. Paul은 여가 시간을 활용해 MFC 클래스 라이브러리인 PixieLib을 만들어 그의 웹 사이트인 www.dilascia.com에 공개하고 있습니다.

읽어볼만한 글 / C++ At Work: C++/CLI의 설계 원리와 발전 과정”에 대한 6개의 생각

    • C++/CLI 역시 닷넷 프레임워크에서 사용할 수 있는 일반적인 프로그래밍 언어로 보고 개인적으로 많은 관심을 두고 있던 중에 찾게 된 기사라서 퍼오게 되었습니다. 궁금하신 점은 언제든 질문해 주세요. 🙂

  1. ㅇㅅㅇ 저는 C++는 그냥 C++쓰지 .net이랑 짬뽕된 C++/CLI는 좀 안땡기더군요.
    이왕 할거면 첨부터 .net을 위해 설계된 C#이 젤 잘맞는듯해요.
    역시 플랫폼에 맞춰 설계된 언어를 쓰는게 최고인듯. =ㅅ=

    • C++/CLI는 닷넷 프레임워크를 활용하기 위한 하나의 도구에 지나지 않습니다. C++과 COM 기술만을 이용하여 닷넷 프레임워크를 호스팅 하는 방법도 있으니 이 또한 좋은 선택이 될 수 있을 것 같습니다.

댓글 남기기