이 컬럼은 2004년 8월, STL.NET Primer란 제목으로 MSDN에 포스팅된 글을 번역한 것입니다.

STL.NET Primer (1/3)에 이어서..


행복한 고민 : 무엇을 선택해야 하는가?

CLI 타입으로 이루어진 컬렉션을 조작하는 데 있어 Visual C++ 프로그래머가 선택 가능한 컨테이너 라이브러리에는 세 가지가 있는데, 이들 라이브러리는 세 가지의 타입 매개변수화(type parameterization) 모델에 각각 기반하여 만들어진 것입니다. 아래의 리스트는 각 모델에 해당하는 코드 샘플과 함께 이들 라이브러리를 요약한 것입니다.

오리지널 System::Collections 라이브러리는, 모든 CLI 타입의 기초 클래스(base class)인 Object를 통해 요소 타입을 저장하는 데에 기반을 둡니다. 그 예로, 아래에는 IList 인터페이스를 구현하는 ArrayList가 있습니다. 이 타입은 Object 타입의 배열을 나타내며, 이 예제에서는 String 타입의 요소를 담고 있습니다.
void objectCollection()
{
    using namespace System::Collections;

    ArrayList ^as = gcnew ArrrayList;
    as->Add( "Pooh" );   as->Add( "Piglet" );
    as->Add( "Eeyore" ); as->Add( "Rabbit" );
    as->Sort();

    Console::WriteLine( "ArrayList holds {0} elements: ",
                         as->Count );

    for ( int i = 0; i < as->Count; i++ )
          Console::WriteLine( as[ i ] );

    int index = as->IndexOf( "Pooh" );
    if ( index != -1 )
    {
        // 명시적인 다운캐스트가 필요합니다.
        String^ item = safe_cast( as[ index ]);
        as->RemoveAt( index );
    }

    as->Remove( "Rabbit" );

    Console::WriteLine( " ArrayList holds {0} elements: ",
                         as->Count );

    IEnumerator^ is = as->GetEnumerator();
    While ( is->MoveNext() )
            Console::WriteLine( is->Current );
}
새로운 컨테이너 라이브러리는 CLI Generic 메커니즘에 기반을 두고 있습니다. 이 라이브러리는 System::Collections::Generic 네임스페이스에서 찾을 수 있죠. 이 라이브러리는 Visual Studio 2005 베타 1에서 선보였는데, 최종판이 배포되기에 앞서 변경이 가해지리라 예상됩니다. Collection<T>는 구체화될 지네릭 기반 클래스(concrete generic base class)로서, 사용자들은 이 클래스를 기초 클래스로 하여 그들만의 특화된 컨테이너 클래스를 파생시킬 것입니다. 다음은 위의 예와 동일한 내용이 담긴 지네릭 버전의 예제 코드입니다.
void genericCollection()
{
    using namespace System::Collections::Generic;

    Collection<String^> ^cols = 
                         gcnew Collection<String^>;

    cols->Add( "Pooh" );   cols->Add( "Piglet" );
    cols->Add( "Eeyore" ); cols->Add( "Rabbit" );

    // 콜렉션에 관계된 정렬 메서드가 없습니다.

    Console::WriteLine( "Collection holds {0} elements: ",
                         cols->Count );

    for ( int i = 0; i < cols->Count; i++ )
          Console::WriteLine( cols[ i ] );


    int index = cols->IndexOf( "Pooh" );
    if ( index != -1 )
    {
         // 다운캐스트가 필요 없습니다 ...
         String ^item = cols[ index ];
         cols->RemoveAt( index );
    }

    cols->Remove( "Rabbit" );

    Console::WriteLine( " Collection holds {0} elements:",
                          cols->Count );

    IEnumerator<String^> ^is = cols->GetEnumerator();
    while ( is->MoveNext() )
            Console::WriteLine( is->Current );
}
STL.NET에서 사용하는 타입 매개변수화(type parameterization) 모델은 매우 다른데, 바로 이 모델이 다음 절의 주제입니다. 다음의 구현 코드는 String 컨테이너로서, 아래에서 좀더 깊게 파고들 내용입니다.
#include <cli/vector>
#include <algorithm>

void stlCollection()
{
    vector<String^> ^svec = gcnew vector<String^>;

    svec->push_back("Pooh");   svec->push_back("Piglet");
    svec->push_back("Eeyore"); svec->push_back("Rabbit");

    // 지네릭 알고리즘: sort
    sort( svec->begin(), svec->end() );

    Console::WriteLine( "Collection holds {0} elements: ",
                        svec->size() );

    for ( int i = 0; i < svec->size(); i++ )
          Console::WriteLine( svec[ i ] );

    // 지네릭 알고리즘: find
    vector::iterator iter =
           find( svec->begin(), svec->end(), "Pooh" );

    if ( iter != svec->end() )
    {
        // 다운캐스트가 필요 없습니다 ...
        String ^item = *iter;
        svec->erase( iter );
    }

    // 지네릭 알고리즘: remove ...
    remove(svec->begin(),svec->begin(),svec->end(),"Rabbit");

    Console::WriteLine( " Collection holds {0} elements: ",
                        svec->size() );

    IEnumerator ^is = svec->GetEnumerator();
    while ( is->MoveNext() )
           Console::WriteLine( is->Current );
}

왜 STL.NET인가?

STL.NET에 대하여 파고들기에 앞서, 분명히 해두어야 할 사항을 짚고 가도록 하죠. 바로 다음과 같은 질문에 관한 것입니다 : 왜 Visual C++ 프로그래머는 언어 중립적 라이브러리인 System::CollectionsSystem::Collections::Generic보다, STL.NET 컨테이너 라이브러리를 선택하는 것이 좋을까?

보통, System::Collections 라이브러리를 피하게 되는 이유는 Visual Studio 2005에서 Generic 라이브러리를 제공하는 이유와 동일합니다. 즉, 매개변수화에 대한 Object 모델은 타입 정보를 손실함으로 인하여 복잡하고도 안전하지 않다는 것이죠. 16개 혹은 그 이하의 요소를 가진 컨테이너에서는 버블 소트(bubble sort)가 문제없는 것처럼, 간단하게 사용할 때에는 이 모델도 괜찮습니다. 하지만 여러분의 애플리케이션이 실세계의 문제를 다루는 경우라면, 여러분에게는 좀더 정교한 해결책이 필요하게 될 것입니다.

따라서, Visual C++ 같은 시스템 프로그래밍 언어를 위한 대안은 STL.NET과 System::Collections::Generic 라이브러리로 좁혀집니다. 그렇다면, 왜 Visual C++ 프로그래머는 이 두 라이브러리 중 STL.NET을 선호해야 할까요? 그리고 이는 나머지 우리의 프로그램을 다른 .NET 언어로부터 격리시키는 꼴은 아닐까요? 이는 유효한 질문일 뿐만 아니라, 답변받을 만한 가치가 있는 질문입니다.

첫 번째 답변은 확장성(extensibility)입니다. Alex Stepanov가 발명한 STL의 원 디자인 패턴은 알고리즘과 컨테이너를 각기 다른 두 영역(domain space)로 나눕니다. 따라서 여러분은 모든 종류의 컨테이너에 적용될 알고리즘을 추가할 수 있거나, 그 알고리즘이 적용될 컨테이너들을 추가할 수 있습니다. 하지만 Generic 라이브러리는 좀더 제한적인 컨테이너 모델이며, 이 사실은 우리를 두 번째 답변으로 이끕니다.

두 번째 답변은 통일성(unification)입 니다. 실전의 C++ 프로그래머들은 기존의 코드 몸체뿐만이 아니라, 이 라이브러리도 함께함으로써 전문적 기술을 익혀왔습니다. 우리가 원하는 바는 (.NET으로의) 이주로(migration path)를 제공하는 데 있어 기존의 코드뿐만 아니라, 몸에 익숙해진 기존의 기술도 함께 할 수 있도록 하는 것입니다. 여러분이 C++로 프로그래밍을 할 때에 STL에 의존해왔을 경우 .NET 하에서 STL을 사용 못하게 된다면, 여러분은 이를 크나큰 손실로 여길 것입니다(적어도 저의 경험으로는 그랬습니다). 이러한 견해는 저와 의견을 나눠온 많은 고급 C++ 프로그래머들에게서 이미 제기되었던 사항이며, 이로 인해 그들은 .NET으로 이주하는 것을 보류했다고 말해왔습니다.

세 번째 답변은 성능(performance)입니다. 하지만 C++ 프로그래머는 성능에 관한 주제에 대해선... 흠, 말하자면, 짜증날 정도로 예민하기 때문에, 이 연재물 중 적절한 시점에서 다시 다루도록 하겠습니다.

마 지막으로, 남은 질문, "좋아요, Stan. 지금까지 모두 좋았다고요. 하지만 이는 C++ 프로그래머와 C++/CLI 프로그램을 나머지 다른 .NET 커뮤니티로부터 고립시키게 되는 일이 아닌가요?" 이에 대한 답변은, 제가 믿기로는, '아니오'입니다. Anson Tsao, Martyn Lovel, P.J. Plauger를 비롯한 STL.NET 아키텍트들은 이 사안에 대해 심사숙고를 해왔으며, 우리는 우리의 능력, 즉 IEnumerator, IList, 그리고 ICollection에 대한 지원을 통해 다른 .NET 언어와 상호운용을 이뤄낼 수 있다는 데에 확신을 가집니다. 이어질 저의 컬럼 중 하나에서는 이 사항에 대하여 깊게 다룰 것입니다.


STL.NET Primer (2.5/3)으로 계속됩니다.
Posted by 어쨌건간에