[STL] 8. <algorithm>에 추가된 새로운 함수들 - partition_copy

C++0x 2011. 4. 12. 09:00 Posted by 알 수 없는 사용자

partition_copy 알고리즘은 하나의 집단에서 서로 다른 두 개의 집단으로 나눌 때 사용하는 것이라고 아주 간단하게 말할 수 있습니다.

 

partition_copy

template<class InputIterator, class OutputIterator1, class OutputIterator2, class Predicate>

    pair<OutputIterator1, OutputIterator2>

        partition_copy(

            InputIterator _First,

            InputIterator _Last,

            OutputIterator1 _Dest1,

            OutputIterator2 _Dest2,

            Predicate _Pred

        );

 데이터셋의 _First _Last 사이에 있는 각 요소 x를 조건자 _Pred에 인자를 넘겼을 때 true를 반환하면 x _Dest1, false를 반환하면 _Dest2에 복사하고 지정된 구간의 모든 요소를 다 처리하면 OutputIterator 값을 pair로 반환한다

 

그럼 좀 더 쉽게 이 알고리즘을 어떤 경우에 사용하는지 알 수 있도록 간단한 예제를 하나 보여드리겠습니다.

 ) 게임 아이템들을 팔 수 있는 것과 팔 수 없는 것으로 나누어라

#include <iostream>
#include <algorithm>
#include <vector>
#include <list>
using namespace std;

struct ITEM
{
    int nItemCode;
    bool bEnableSell;
};

int main()
{
    vector< ITEM > AllItems;
    ITEM item1; item1.nItemCode = 1;    item1.bEnableSell = false;        AllItems.push_back( item1 );
    ITEM item2; item2.nItemCode = 2;    item2.bEnableSell = true;        AllItems.push_back( item2 );
    ITEM item3; item3.nItemCode = 3;    item3.bEnableSell = true;        AllItems.push_back( item3 );
    ITEM item4; item4.nItemCode = 4;    item4.bEnableSell = false;        AllItems.push_back( item4 );
    ITEM item5; item5.nItemCode = 5;    item5.bEnableSell = true;        AllItems.push_back( item5 );
    ITEM item6; item6.nItemCode = 6;    item6.bEnableSell = false;        AllItems.push_back( item6 );
    ITEM item7; item7.nItemCode = 7;    item7.bEnableSell = true;        AllItems.push_back( item7 );

    ITEM UnItem; UnItem.nItemCode = 0;
    list< ITEM > SellItems( 7, UnItem );            
    list< ITEM > UnSellItems( 7, UnItem );
    
    pair<list< ITEM >::iterator, list< ITEM >::iterator > SeperateItems;
    SeperateItems = partition_copy( AllItems.begin(), AllItems.end(),
                                                 SellItems.begin(),
                                                 UnSellItems.begin(),
                                                   []( ITEM& item ) { return item.bEnableSell; } );

    cout << "팔 수 있는 아이템" << endl;
    for each( ITEM item in SellItems )
    {
        if( item.nItemCode <= 0 ) {
            continue;
        }
        cout << "아이템 코드 : " << item.nItemCode << endl;
    }

    cout << endl << endl;

    cout << "팔 수 없는 아이템" << endl;
    for( auto Iter = UnSellItems.begin(); Iter != UnSellItems.end(); ++Iter )
    {
        if( Iter->nItemCode <= 0 ) {
            continue;
        }
        cout << "아이템 코드 : " << Iter->nItemCode << endl;
    }
    
    getchar();
    return 0;
}

< 결과 >


partition_copy를 사용할 때 한 가지 주의할 점은 결과를 다른 컨테이너에 복사를 하므로 해당 컨테이너에 공간이

확보되어 있어 있어야 합니다. 그래서 위의 예제에도

ITEM UnItem; UnItem.nItemCode = 0;
list< ITEM > SellItems( 7, UnItem );            
list< ITEM > UnSellItems( 7, UnItem );

로 더미 값을 넣어서 복사할 공간을 확보하고 있습니다.

 

참조 : http://msdn.microsoft.com/ko-kr/library/ee384416.aspx

 



< Tessellator >

테셀레이터는 Hull Shader 의 결과를 입력으로 받아서 작업을 합니다.
이 스테이지는 프로그래머가 제어할 수 없는 영역입니다.( 정말 다행이죠? ^^ )
앞선 Hull Shader 스테이지에서 정의된 폴리곤 분할 방법과 분할 수치에 따라서
실제로 Vertex 데이터들을 생성할 수 있는 정보를 주게 됩니다. 
즉, 우리는 큰 덩어리 형태의 Vertex 데이터만 HullShader 를 통해서 전달할 뿐입니다.
테셀레이터의 정해진 연산에 의해서,
도메인 쉐이더( DomainShader )에 무게 중심 좌표( BarycentricCoordinates )들을 전달
하게 됩니다.



< 무게 중심 좌표( BarycentricCoordinates ) >

무게 중심 좌표를 언급하기 전에, 벡터의 외적의 성질에 대해서 언급할 사항이 있습니다.
우리가 이미 알고 있듯이, 두 벡터의 외적 연산으로 두 벡터에 수직인 벡터를 구할 수 있습니다.
지금부터 여기에 주목할 것은 이렇게 외적 연산을 통해서 얻어진 벡터의 길이입니다.
이렇게 구해진 벡터의 길이는 기하학적으로 두 벡터를 평행사변형을 만들었을 때, 넓이를 의미합니다.
아래의 그림이 이해에 도움이 되었으면 좋겠습니다.^^
꽤 재미있는 성질이지 않습니까? ^^
( 이미 다들 알고 계셨을 것이라 생각하지만, 처음 접했을때, 저는 꽤 재미있는 성질이라고 생각했습니다..^^ )



두 벡터의 외적으로 나온 결과 벡터의 길이가 평행사변형의 넓이라는 사실을 인지한다면,
우리는 이제 무게 중심 좌표에 대해서 얘기할 수 있습니다.
힌트를 드리면, 무게 중심 좌표는 다른 말로 면적 좌표로도 불리기도 합니다.


삼각형 내부의 임의의 점 P는 점 A,B,C를 구성하는 삼각형들의 비율로 표현할 수 있습니다.
위의 그림에서 나오는 공식과 그림은 바로 이를 보여드리고 있습니다.
w들은 가중치 상수를 의미합니다.
각 가중치들의 합은 반드시 1.0 이여야 합니다.
만약 C의 가중치인 w3 의 경우에는 삼각형 APB 의 넓이 / 삼각형 ABC의 넓이 가 되는 것입니다.
이런 식으로 서로 대응되는 각 가중치들을 삼각형을 구성하는 각각의  정점 위치에 대응시키면,
우리가 원하는 P의 위치를 구할 수 있습니다.


벡터 외적의 기하학적 특징을 이용해서 가중치를 구하는 코드는 아래와 같습니다.
이 코드에서는 삼각형 넓이를 구할 때 수행하는 2를 나누는 작업이 생략되어 있습니다.
이유는 어차피 이 코드의 결과는 비율에 대한 가중치이기 때문에, 2를 나누는 작업은 의미가 없기 때문입니다.


이처럼 무게중심좌표를 구하는 일이 DirectX11의 테셀레이터의 임무 중 하나입니다.
삼각형을 구성하는 세 정점이 주어졌을 때 세 정점의 가중치를 구할 수 있다면,
임의의 점 P를 구할 수 있습니다.
바로 이 역활을 수행하는 것이 테셀레이터의 기능 중 하나입니다.
앞선 언급했듯이 테셀레이터의 기능은 우리가 조작 할 수 있지 않습니다.
즉, 고정 기능입니다.

우리는 Hull Shader를 통해서 Patch를 정의하고,
이렇게 정의된 패치 데이터는 이후에 가공되지 않고, 바로 Domain Shader 에서도 사용됩니다.
( 테셀레이터에서도 이 데이터를 사용해서 연산을 합니다. )
테셀레이터 단계에서는 이 패치 데이터에 대응되는 가중치들을 구성해서,
바로 다음 단계인 Domain Shader 로 전달
하게 되는 것입니다.
물론 내부적으로는 더욱 복잡한 과정을 거치겠지만,
우리가 코딩관점에서 관심을 가질 수 있는 변수 정보는 이들 뿐입니다.

Domain Shader의 기본적인 형태는 다음과 같습니다.

[domain("tri")]
DS_OUTPUT DS( HS_CONSTANT_DATA_OUTPUT input,
                    float3 UVW : SV_DomainLocation,
                    const OutputPatch<HS_OUTPUT, 3> patches
{
   DS_OUTPUT Output;
    ...
    return Output;   
}

Domain Shader의 입력으로 들어오는 인자들을 유심히 보시기 바랍니다.
( 패치 정보와 UVW 에 바로 가중치 정보가 입력됩니다. )
이들에 대해서는 다음 시간에 살펴보도록 하겠습니다.

[Step. 25] 닷넷에서 HalfNetwork를 사용하자 – 6(마지막)

C++/CLI 2011. 1. 31. 09:00 Posted by 알 수 없는 사용자

앞서 C++/CLI로 만든 클래스 라이브러리를 사용하여 아주 간단한 콘솔 환경의 서버 프로그램을 만들어 보겠습니다.

 

C#에서 Console 프로젝트를 생성합니다.

생성 후 솔루션 탐색기에서 참조에 클래스 라이브러리를 추가합니다.


 

HalfNetworkNET 클래스를 상속 받는 클래스를 생성합니다. 저는 HFServer라는 클래스를 만들었습니다.

그런 후 상속 받은 HalfNetworkNET 클래스의 일부 함수를 재정의 합니다.


 

이제 Main 부분만 남았습니다.

namespace TestServer_HFNetworkNET

{

    class Program

    {

        static void Main(string[] args)

        {

            HFServer Server = new HFServer();

 

            // 서버 초기화 및 설정 후 Port를 연다

Server.Init();

            short nRet = Server.Open();

 

            if (HalfNetwork_NET.ERRORCODE.NONE != nRet)

            {

                Console.WriteLine("Failed Server Open!");

                return;

            }

 

            Console.WriteLine("Server Start !!!");

 

            // 네트웍 처리를 한다.

            while (true)

            {

                Server.Run();

            }

 

            Console.WriteLine("End Server");

 

            // 서버 종료 처리를 한다

            Server.End();

        }

    }

}

 

 

이것으로 아주 간단한 Echo 서버가 만들어졌습니다.

자세한 코드는 첨부된 프로젝트를 보시기 바랍니다.

 

이 프로그램을 실행하면 아래와 같습니다.



프로그램이 올바르게 실행되는지 테스트 하기 위해서는 HalfNetwork 솔루션에 있는 TestClient 프로젝트를 빌드한 후 실행하면 됩니다.

오른쪽이 클라이언트 입니다. 여기서는 10개를 접속 후 0.5초마다 패킷을 보내면 서버(왼쪽)는 받은 데이터를 그대로 보냅니다.


종료하면 종료된 세션을 콘솔에 출력합니다.


닷넷 플랫폼에서 서버 프로그램을 만드는 경우 닷넷 프레임웍에서 제공하는 API를 사용해도 좋지만 기존에 만들어져 편리하고 고성능 라이브러리를 사용하는 것도 아주 괜찮은 선택입니다.

C++로 만들어진 라이브러리를 C++/CLI를 사용하여 필요한 부분만 랩핑하여 클래스를 만들면 닷넷에서 마음껏 사용할 수 있습니다.


 

 

[JumpToDX11-19] DirectX11의 테셀레이션 ( Hull Shader 역할편 )

DirectX 11 2011. 1. 25. 09:00 Posted by 알 수 없는 사용자



DirectX11의 파이프라인은 앞선 시간에서 우리는 꾸준히 보았습니다.
복습의 의미에서 이번에는 http://www.realtimerendering.com 에 정의된 Direct3D 의 파이프라인입니다.

Direct3D 10 Pipeline
< Direct3D 10 pipline >

Direct3D 11 Pipeline
< Direct3D 11 pipline >

우리가 그래픽스에서 사용하는 폴리곤은 굉장히 복잡한 방식으로 처리가 됩니다.
많은 스테이지를 통해서 결국 우리는 화면 픽셀로 변환된 최종 결과를 확인하게 되는 것입니다.
그 과정 속에서 Direct3D 9에서는 Vertex와 Pixel 을 조작할 수 있도록 변화되어 왔습니다.
Direct3D 10 은 여기에 Geometry 까지 조작할 수 있도록 프로그래머들에게 개방되었습니다.
Direct3D 11 은 무려 3개의 스테이지가 추가되었습니다.
Hull Shader, Tessellator, Domain Shader 가 바로 그것들입니다.

이 중에 프로그래머가 제어하는 부분은 Hull / Domain Shader 이며,
Tessellator 의 경우에는 하드웨어가 직접 처리하게 됩니다.

테셀레이션을 언급하면서 가장 많이 나오는 주제는 현재 LOD( Level of Detail ) 처리 이지만,
정확하게 테셀레이션이 필요한 이유는 글인 http://vsts2010.net/331 을 통해서 확인할 수 있습니다.

현재 그래픽 파이프라인에서 테셀레이션 작업은 현재 옵션으로 설정되어 있습니다.
여러분이 이 기능을 사용하기 원하지 않는다면, 이들을 활성화 시키지 않으시면 됩니다.
그렇게 된다면, 기존의 파이프라인과 동일한 방식으로 Vertex 데이터를 처리하게 됩니다.


< Hull Shader >

Hull Shader 는 테셀레이션 작업의 시작입니다.
하지만, 실제로 프로그래머의 시작은 Vertex Shader 입니다.
DirectX9에서 VertexShader 는 World-View-Projection 변환을 수행하는 것이 가장 큰 목적이였습니다.
DirectX11에서 VertexShader 의 목적은 Hull Shader 로의 데이터를 전달하는 것입니다.
즉, 테셀레이션이 목적인 경우에는
DirectX11에서 VertexShader 스테이지에서 World-View-Projection 을 수행해서는 안됩니다.
테셀레이션 작업시 VertexShader 에서 처리되는 Vertex는 실제 우리가 사용하는 데이터가 아닙니다.
우리는 VertexShader 의 입력으로 들어오는 데이터를 모아서,
많은 수의 Vertex를 새롭게 생성시켜야 합니다.
그래서 테셀레이션 작업시 VertexShader 스테이지에서는 Vertex를 월드 변환까지만 수행합니다.

Hull Shader 에서는 '폴리곤을 어떻게 분할할 것인가?' 와 '폴리곤을 얼마나 분할할 것인가?' 를 결정합니다.
가장 단순한 형태로 이 Hul Shader의 기능을 표현하면 다음과 같습니다.

Diagram of the hull-shader stage

위의 그림은 MSDN 의 그림입니다.

Hull Shader 는 두 가지의 작업을 동시에 수행합니다.
그것은 제어점( Control Point ) 를 생성하는 작업과 Patch Constant Data 를 계산하는 작업입니다.
이들 작업은 병렬적으로 수행되게 됩니다.
HLSL 코드는 실제로 드라이버 수준의 하드웨어 명령어를 생성하게 되는데,
이 때, 병렬처리가 가능한 형태로 변환되게 됩니다.
이는 Hull Shader 가 빠르게 동작할 수 있는 중요한 이유이기도 합니다. 
 
Hull Shader 의 입력으로 들어오는 제어점( Control Point )들은
낮은 차수의 면을 표현하는 정점들입니다.
이를 높은 차수의 면을 표현하는 제어점들로 만들어 내게 됩니다.
이 때 생성된 제어점들은 Tessellator 스테이지에서 사용되는 것이 아니라,
그 다음 스테이지인 Domain Shader 에서 사용됩니다.



위의 그림은 베지어(Bezier) 제어점들을 이용해서 베지어 곡면을 표현한 것입니다.

근본적으로 테셀레이션은 평면을 곡면으로 생성시키는 개념과 매우 비슷합니다.
( 굳이 평면을 많은 갯수의 폴리곤으로 표현할 필요는 없기 때문이겠죠. )
그렇기 때문에, 분할 방법으로 사용되는 알고리즘들은 베지어처럼 게임 프로그래머들에게 친숙한
개념들이 사용됩니다.

Hull Shader 의 또 하나의 중요한 역활은 불필요한 연산을 줄이기 위해
테셀레이션 단계를 스킵할지를 결정할 수 있다는 것입니다.
즉, Hull Shader 에서 Tessellation Factor 가 0 이하인 경우에
이 패치는 컬링
되어 버린 것으로 간주됩니다.
( Tessellation Factor 는 얼마나 분할할지를 나타내는 수치적 비율입니다. )
이로인해 더 이상 파이프라인 처리가 이루어지지 않음으로써,
성능 향상을 도모할 수 있습니다.
( 폴리곤을 처리하지 않는 것이 가장 큰 성능의 이득이겠죠..^^ )


그러면 과연 Hull Shader 에서의 '폴리곤을 어떻게 분할할 것인가?' 와 '폴리곤을 얼마나 분할할 것인가?'
프로그램 코드에서는 어떻게 표현해야 할까요?

현재 MSDN 에 나와있는 Hull Shader 의 가장 단순한 형태는 다음과 같습니다.
( 물론 실제로 구현되고 동작되는 내용들의 예들은 DirectX11 샘플에 있습니다. )


[domain("quad")]
[partitioning("integer")]
[outputtopology("triangle_cw")]
[outputcontrolpoints(16)]
[patchconstantfunc("SubDToBezierConstantsHS")]
BEZIER_CONTROL_POINT MainHS( InputPatch<VS_CONTROL_POINT_OUTPUT, MAX_POINTS> ip, 
                                                    uint i : SV_OutputControlPointID,  uint PatchID : SV_PrimitiveID )
{
    VS_CONTROL_POINT_OUTPUT Output;

    // Insert code to compute Output here.    
    return Output;
}

위의 Hull Shader 는 동작 방식을 설정합니다.
몇몇 정의된 값들을 셋팅해 주면, 이는 테셀레이션 작업을 하는 동안에 사용되게 됩니다.
즉, 위의 셋팅들은 '폴리곤을 어떻게 분할할것인가?' 에 준하는 프로그램 코드라 할 수 있습니다.

이제 남은 것은 '폴리곤을 얼마나 분할할 것인가?' 입니다.
이는 PatchConstantFunc 을 통해서 병렬적으로 처리된다고 앞서 설명을 했습니다.
이곳에서는 Tessellation Factor 를 계산하게 되는데, 그 결과에 따라서 컬링 작업이 실행됩니다.
( 이 값이 0 이하의 경우에는 더 이상 처리가 필요하지 않습니다. )
이 작업을 하는 함수를 우리는 직접 작성해서,
 위의 [patchconstantfunc("SubDToBezierConstantsHS")] 처럼 설정해 주면 자동적으로 동작합니다.
MSDN 에 나와있는 PatchConstantFunc의 기본적인 형태는 다음과 같습니다.

#define MAX_POINTS 32

// Patch Constant Function
HS_CONSTANT_DATA_OUTPUT
SubDToBezierConstantsHS( InputPatch<VS_CONTROL_POINT_OUTPUT, MAX_POINTS> ip,
                                         uint PatchID : SV_PrimitiveID )

    HS_CONSTANT_DATA_OUTPUT Output;

    // Insert code to compute Output here    
    return Output;
}

이 PatchConstantFunc 의 결과에 바로 '폴리곤을 얼마나 세밀하게 분할할 것인가?' 에 대한 정보들이 있습니다.

// Output patch constant data.
struct HS_CONSTANT_DATA_OUTPUT
{
    float Edges[4]        : SV_TessFactor;
    float Inside[2]       : SV_InsideTessFactor;
    ...
};

위의 경우의 결과 구조체는 사각형을 분할한 경우이며,
우리가 주로 사용하는 삼각형 분할의 경우에는 다음과 같을 것입니다.

// Output patch constant data.
struct HS_CONSTANT_DATA_OUTPUT
{
    float Edges[3]        : SV_TessFactor;
    float Inside       : SV_InsideTessFactor;
    ...
};

지금까지 Hull Shader의 기본적인 개념과 역활에 대해서 언급해 드렸습니다.
이렇게 얻어진 결과는 테셀레이터로 전달되게 됩니다.
세부적인 Hull Shader 의 작성은 이후의 시간들을 통해서 살펴볼 예정입니다.
( 현재 본 글들은, 개념 위주의 설명에 포커스를 두고 있습니다. ^^ )

VSS 마이그레이션 전략

Team Foundation Server 2011. 1. 18. 08:30 Posted by POWERUMC

Visual Source Safe 마이그레이션 이전에

많은 분들이 예전에 Visual Source Safe(이하 VSS) 를 사용하시면서, 현재는 이 VSS가 많은 골치거리라고 느끼시는 분들이 많이 계실 겁니다. 사실 소스 제어를 떠나서 VSS는 안정성 면에서 굉장히 불리하죠. 가장 흔하게 겪는 안전성의 문제는 파일 시스템 기반의 소스 제어 데이터베이스가 꼬이는 겁니다. 왜 꼬이는지는 알고 싶지 않지만, 오래 쓰면 쓸수록 꼬입니다.

제가 겪었던 꼬이는 대표적인 문제가 체크인 상태가 다른 사람에겐 체크인 상태가 아니라는 것이죠. 아무리 다른 사람이 최신 버전을 가져와도 그 소스 코드는 예전에 체크인 되었던 소스 코드이고, 불가피하게 강제로 다시 체크인해야 하기도 합니다. 뭐, 여기까지는 정말 가벼운 일상적인 문제이죠? 더 심한 경우는 복구 불능..!

최근 들어서, VSS의 이런 문제 때문에 많이 고생하시는 분들이 다른 소스 제어 제품으로 갈아타려는 준비를 많이 하십니다.

   

왜 VSS에서 이런 문제가 발생하나…?

사실 어쩔 수 없습니다. 지금에야 VSS가 실컷 얻어터질 수 밖에 없지만, 사실 예전에도 뚜렷한 대안이 있었던 것도 아닙니다.

VSS아니면 CVS(Concurrent Versions System) 인데, 이 CVS도 그 기능 자체의 구현이 충실하지 않아 문제점을 얘기하자면 VSS나 크게 별반 다를 것이 없었습니다. 참고로 Wikipedia 의 과거 소스 제어 제품을 보면 다음과 같지요. 즉, 당시에 VSS 보다 더 뛰어난 제품도 찾기 힘들었고, 현대의 이슈인 안정성과 성능, 보안의 요소는 어디를 뒤져봐도 없었습니다. 즉, 당시에는 어떤 제품을 선택하든 똑같은 문제를 겪었을 테니까요.

   

다만, VSS 제품은 VSS 2005 버전까지 오면서 많은 부분에서 보완이 되었지만, 사용자의 요구사항에 매우 소극적으로 대응했던 점에서 아쉬움이 남습니다.

아래는 조만간 나오게 될 백서의 내용 중의 일부이니 참고하세요.

   

 

일반적으로 '형상관리'라는 의미의 소스 제어는 소스 제어(Source Control), 버전 컨트롤(Version Control), 소프트웨어 환경 관리(Software Configuration Management)라고 불립니다. 향후 소스제어는 서버/클라이언트 아키텍처로 변경되면서 개발 조직에서 소스를 공동으로 개발하고 공유할 수 있게 되었습니다.

초기 Microsoft 에서는 소스 제어를 위한 소프트웨어로 Visual SourceSafe(비주얼 소스세이프) 를 내놓게 되었습니다. Visual SourceSafe는 처음 One Tree Software 라고 불리는 회사에서 여러 운영체제를 지원하는 소스 제어 솔루션을 만들었는데, Microsoft 는 이를 1994년에 인수하여 즉시 Visual SourceSafe 3.1 버전을 내놓았습니다. 그 이후로, Visual SourceSafe 4.0, 5.0, 6.0, 2005 버전까지 지속적으로 지원을 하다가, Visual SourceSafe 2005버전을 마지막으로 이 제품의 업데이트는 이루어 지지 않고 있습니다.

Microsoft는 그 이후에 내부적으로 소스 제어 뿐만 아니라 버그 추적/품질 관리/제품 계획에 사용되는 솔루션을 만들었고, 그 이름은 "Product Studio" 라는 제품입니다. 이 제품은 Microsoft 내부적으로 사용하기 위한 제품이었고, 이 제품을 통해 노하우를 발전시켜 비즈니스 프로세스, 개발 등 전반적인 모든 개발 활동을 아우를 수 있는 "Visual Studio Team System, Team Foundation Server" 를 시장에 내놓게 되었습니다.

   

VSS to TFS2010 마이그레이션 전략

일단 아쉽지만 VSS와 같은 제품 군은 TFS(Team Foundation Server)에 100% 마이그레이션이 힘들 수 있습니다. 왜냐하면 VSS는 파일 시스템의 파일 단위 체크인 방식인데, TFS제품은 변경 집합(ChangeSet) 기반의 소스 제어 구조를 가집니다. 변경 집합은 변경이 일어난 묶음의 세트를 얘기하며, 이 변경 집합 덕분에 분기(Branch)/병합(Merge)/이력/관리가 매우 용이합니다. 덕분이 3-ways 방식의 병합이 매우 안정적으로 동작할 수 있고요.

VSS to TFS로 마이그레이션이 100% 보장할 수 없는 예를 들자면, 고객의 데이터베이스 스키마에 "주소"가 없는데, "주소" 컬럼이 생겼다고 주소를 가짜 데이터로 입력할 수 는 없는 노릇입니다. 게임을 예로 들면, 게임 시스템에 새로운 스킬이 생겼다고 종족/레벨/서버를 막론하고 모두가 이 스킬을 습득할 수 없는 것과 마찬가지입니다.

기존의 VSS는 레이블(Labeling) 방식의 이력 관리를 하였기 때문에, 이것을 변경 집합(ChangeSet) 기반으로 바꿀 수는 없습니다. 그래서 100% 마이그레이션이 힘든 한 가지 원인이기도 합니다. 그렇게 때문에 VSS to TFS로 마이그레이션을 결심하였다면, "퀑 대신 닭", "짜장면 대신 짬뽕","아이폰 대신 블랙베리" 라는 심정으로 100%를 기대하시면 오히려 독이 될 수 있답니다.^^

아래는 VSS to TFS 마이그레이션 전략을 메트릭스로 표현해 보았습니다. 물론, 이것 보다 더 많은 고려 사항이 있습니다만, 대략 아래의 정보에 답할 수 있다면 마이그레이션은 가능하다고 말씀 드리고 싶네요.

   

   

Team Foundation Server 및 .NET 플랫폼 기술 문의

언제든지 저희 Visual Studio Korea 공식 팀 블로그에 문의를 주시기 바랍니다. 저희가 모든 것을 가이드해 드릴 수는 없지만, 저희 팀의 다양한 분야의 기술 전문가들이 성의껏 여러분들을 도와드리고 있습니다. 저희 팀은 언제나 새로운 기술에 목말라있고, 먼저 고민하고 뼈저리고 값진 노하우를 경험한 컨설팅/개발/교육 및 강사 출신의 분들과 Microsoft MVP 활동을 하고 계신 많은 분들이 계십니다.

더불어, Microsoft 의 Social Forums 인 http://social.msdn.microsoft.com/Forums/ko-kr/categories/ 에 오시면 많은 전문가들의 생생한 고급 답변을 들을 수 있습니다.


Visual Studio Hotfix List

Visual Studio 2010 2010. 11. 2. 09:00 Posted by 알 수 없는 사용자
사람이 만드는 소프트웨어에 버그 ‘0’이라는 것은 무척이나 힘들고 특히 Visual Studio 같은 대형 프로그램은 자잘한 버그가 있습니다. 하지만 Visual Studio는 마이크로소프트의 주력 제품인 만큼 버그가 알려지면 패치가 재빨리 나오니 자잘한 버그에 대해서는 걱정하지 않으셔도 됩니다.


Visual Studio의 자잘한 버그로 피곤하신 분들을 위해 일본인 프로그래머인 kkamegawa씨가 개인적으로 마이크로소프트 사이트에서 수집하여 만든 Visual Studio Hotfix 링크 모음집을 소개합니다.


Visual Studio 2010 Hotfix

http://mist.clueup.org/VSHotFixLists/

 

Visual 2008 SP1 Hotfix 

http://mist.clueup.org/VSHotFixLists/default.aspx?Product=VS2008

 

사용하고 있는 Visual Studio에 버그가 있다면 당장 위의 사이트에 가서 확인해 보시기 바랍니다.

 

[JumpToDX11-17] DirectX9 세대의 테셀레이션( ATI 라이브러리편 )

DirectX 11 2010. 10. 11. 08:30 Posted by 알 수 없는 사용자

오늘은 DX9 세대의 테셀레이션 마지막입니다.
ATI 는 DirectX9를 지원하는 일부 그래픽카드들은 하드웨어 기반의 테셀레이션 작업을 지원합니다.
( HD 2000 시리즈 이후 지원되었다고 합니다. )
이 방법은 왜 DirectX11의 테셀레이션 작업이 강력한지를 이해하는 좋은 출발점이 될 수 있습니다.

이 경우에는그래픽 파이프라인 구조가 다음과 같습니다.




이는 현재 X-BOX 360 에도 동일하게 적용되는 그래픽 파이프라인 구조입니다.
주목할 만한 것은 Tessellator의 위치입니다.
즉, 버텍스 쉐이더( VertexShader ) 스테이지의 앞단계에 위치하고 있습니다.
이 위치는 DX11 세대에서는 버텍스 쉐이더 다음 단계로 변경됩니다.


아래의 그림은 ATI 카드에서 지원되는 DX9 기반의 테셀레이션 작업을 보여줍니다.



DirectX9의 테셀레이션을 위해서 총 3번의 패스를 통과해야 합니다.
즉, 3번의 렌더링 작업이 필요합니다.
이렇게 많은 패스가 필요한 이유는 테셀레이션을 위해서 인접한 정점의 정보가 필요하기 때문입니다.
DX9 의 시대에서는 VertexShader 단계에서 인접한 정점의 정보를 쉽게 확인할 수 있는 방법이 없습니다.
그래서 인접한 정보를 구성하는 단계가 첫번째 패스입니다.

첫번째 패스의 렌더타겟은 백버퍼가 아니라, 텍스쳐입니다.
이 텍스쳐에 정점 정보와 정점의 인덱스를 기록하게 됩니다.
즉, rgb 에는 위치 정보가 기록되며 a 에는 정점의 인덱스 정보가 기록됩니다.

이 때, 주의할 것은 메시가 인덱스 버퍼 기반으로 렌더링 되어지는 경우입니다.
이 경우에는 인덱스 버퍼를 모두 풀어서 새로운 버텍스 버퍼를 만들어야 합니다.
우리가 필요한 것은 폴리곤을 렌더링하는 작업이 아닙니다.
인접정보를 구성하는 일임을 잊지 말아야 합니다.
첫번째 패스에서의 렌더링은 TRIANGLELIST가 아니라, POINTLIST 로 수행하게 됩니다.

또 하나 주의할 것이 있습니다.
POINTLIST 로 렌더링을 수행할 때는 WVP( World-View-Projection ) 변환이 아니라,
World-View까지만 변환
을 해야 합니다.
이유는 간단합니다.
테셀레이션은 주로 시점에 근거해서  얼마나 많은 폴리곤을 생성할지를 판단해야 합니다.
이를 앞 시간들을 통해서 Adaptive 한 방식이라고 언급을 했었습니다.
이후의 패스에서는 이들 정점에 근거해서 LOD를 판정해서 Tessellation Factor 를 연산하게 되니다.
그래서 View 좌표계까지만 변환을 합니다.
첫번째 패스에서는 이렇게 View 공간으로 POINTLIST들을 텍스쳐에 렌더링 합니다.

이렇게 생성된 텍스쳐를 기반으로 해서 두번째 패스를 진행할 수 있습니다.
DX9 를 지원하는 모든 그래픽카드가 VertexShader 단계에서 텍스쳐 데이터를 읽어올 수 있는 것은 아닙니다.
이런 제약 사항들은 이제 큰 의미가 있는 것이 아니기 때문에,
개념적인 것에 포커스를 두시기 바랍니다.^^

두번째 패스의 목적은 Tessellation Factor를 구하는 것입니다.
즉, 얼마나 폴리곤을 세분화 할지를 결정합니다.
두번째 패스도 역시 POINTLIST 로 렌더링을 합니다.
그리고 첫번째 패스에서 생성해둔 인접 정점 정보를 가진 텍스쳐를 바인딩 합니다.
인접 정보가 있기 때문에 현재 정점을 기준으로 Tessellation Factor 를 계산할 수 있습니다.
두번째 패스에서 주의할 것은 이들 Tessellation Factor 를 저장하기 위해
R2VB 라는 일종의 버퍼에 렌더링
을 한다는 것입니다.
이는 ATI 테셀레이션 라이브러리에만 존재하는 개념입니다.

세번째 패스는 실제로 지오메트리(Geometry)를 렌더링 하는 단계입니다.
실제 렌더링 작업은 TRIANGLELIST 로 렌더링 합니다.
인덱스 기반의 렌더링이 아니라,
우리가 인덱스를 풀어서 생성한 버텍스버퍼로 렌더링 하는 것에 주의해야 합니다.
이때 스트림(Stream) 을 하나 더 연결하는데,
이것은 앞서 우리가 렌더링 했던 R2VB 라는 버퍼
입니다.

결과적으로 VertexShader 에는 Barycentric coordiate 기반의 가중치 값이 전달됩니다.
즉, 무게 중심 좌표입니다.

float3 vPosTessOS = i.vPositionVert0.xyz * i.vBarycentric.x +
                              i.vPositionVert1.xyz * i.vBarycentric.y + 
                              i.vPositionVert2.xyz * i.vBarycentric.z;


정점을 구성하는 방법은 위처럼 해야 합니다.

이상으로 DX9 세대의 테셀레이션 작업들에 대해서 아주 간단히 살펴보았습니다.
메인으로 다룰 내용이 아니라서, 쉽게 넘어간 부분이 많습니다.
아무래도 거의 사용하지 않기 때문에, 깊이있게 다루는 것은 의미가 없다고 생각합니다.

하지만, DX9 세대의 테셀레이션 작업은 이렇게 복잡한 방법과 절차를 통과해야 합니다.
DX11 의 테셀레이션 작업은 상대적으로 빠른 성능으로 구현이 됩니다.
왜냐하면 1 Pass 이기 때문입니다.


ATI 는 DX9 세대의 테셀레이션 작업을 위해서, 라이브러리를 제공하고 있습니다.
더 필요한 정보가 있으시면, 아래의 링크를 참고하시기 바랍니다.

http://developer.amd.com/gpu/radeon/Tessellation/Pages/default.aspx#d3d9

[Step. 14] 인터페이스 ( interface )

C++/CLI 2010. 10. 1. 09:30 Posted by 알 수 없는 사용자

인터페이스는 비관리코드에서는 순수가상함수만을 가진 클래스와 같습니다. ‘interface class’라는 키워드를 사용하여 정의하면 이 클래스에는 아래와 같은 형만 멤버로 가질 수 있습니다.

 

함수

프로퍼티

이벤트

 

또한 선언만 가능하지 정의는 할 수 없습니다.

 


C++/CLI ref class C#이나 Java와 같이 다중 상속은 할 수 없지만 인터페이스를 사용하면 다중 상속(즉 인터페이스를 상속)을 할 수 있습니다.

 

interface class IA {

public:

    void funcIA();

};

interface class IB {

public:

    void funcIB();

};

interface class IC {

public:

    void funcIC();

};

ref class A {

    int i;

};

ref class B : A, IA, IB, IC {

public:

    virtual void funcIA() {   }

    virtual void funcIB() {   }

    virtual void funcIC() {   }

};

int main() {

    B^ b = gcnew B;

 

    IA^ ia = b;

    IB^ ib = b;

    IC^ ic = b;

    b->funcIA();

    ia->funcIA();

    ib->funcIB();

    ic->funcIC();

    return 0;

}

 



출처

http://cppcli.shacknet.nu/cli:interface

 

 

[Step. 15] static 생성자, initonly, literal

C++/CLI 2010. 9. 24. 09:00 Posted by 알 수 없는 사용자

static 생성자

 

static 생성자는 클래스의 생성자에서 static 멤버를 초기화 하고 싶을 때 사용합니다.

ref class, value class, interface에서 사용할 수 있습니다.

 

#include "stdafx.h"

#include <iostream>

 

using namespace System;

 

ref class A {

public:

    static int a_;

    static A()

    {

        a_ += 10;

    }

};

ref class B {

public:

    static int b_;

    static B()

    {

//        a_ += 10; // error

        b_ += 10;

    }

};

ref class C {

public:

    static int c_ = 100;

    static C()

    {

        c_ = 10;

    }

};

 

int main()

{

    Console::WriteLine(A::a_);

    A::A();

    Console::WriteLine(A::a_);

 

    Console::WriteLine(B::b_);

 
    Console::WriteLine(C::c_);


     getchar();

    return 0;

}

 

< 결과 >


 

static 생성자는 런타임에서 호출하기 때문에 클래스 A의 멤버 a_는 이미 10으로 설정되어 있습니다. 그리고 이미 런타임에서 호출하였기 때문에 명시적으로 A::A()를 호출해도 실제로는 호출되지 않습니다.

 

클래스 B의 경우 static 생성자에서 비 static 멤버를 호출하면 에러가 발생합니다.

 

클래스 C의 경우 static 멤버 c_를 선언과 동시에 초기화 했지만 런타임에서 static 생성자를 호출하여 값이 10으로 설정되었습니다.

 

 

 

initonly

 

initonly로 선언된 멤버는 생성자에서만 값을 설정할 수 있습니다. 그리고 initonly static로 선언된 멤버는 static 생성자에서만 값을 설정할 수 있습니다.

 

 

ref class C
{
public:
    initonly static int x;
    initonly static int y;
    initonly int z;
    static C()
    {
        x = 1;
        y = 2;
        // z = 3; // Error
    }
    C()
    {
        // A = 2; // Error
        z = 3;
    }
    void sfunc()
    {
        // x = 5; // Error
        // z = 5; // Error
    }
};


int main()
{
    System::Console::WriteLine(C::x);
    System::Console::WriteLine(C::y);
    C c;
    System::Console::WriteLine(c.z);
    return 0;
}

 

 

 

literal

 

literal로 선언된 멤버는 선언과 동시에 값을 설정하고 이후 쓰기는 불가능합니다. 오직 읽기만 가능합니다.

using namespace System;
ref class C
{
public:
    literal String^ S = "Hello";
    literal int I = 100;
};

int main()
{
    Console::WriteLine(C::S);
    Console::WriteLine(C::I);
    return 0;
}



참고
http://cppcli.shacknet.nu/cli:static%E3%82%B3%E3%83%B3%E3%82%B9%E3%83%88%E3%83%A9%E3%82%AF%E3%82%BF
http://cppcli.shacknet.nu/cli:initonly
http://cppcli.shacknet.nu/cli:literal





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

[Step. 16] array 클래스에 non-CLI 오브젝트 사용  (0) 2010.10.27
[Step. 14] 인터페이스 ( interface )  (1) 2010.10.01
[Step. 13] parameter array  (1) 2010.09.10
[Step. 12] for each  (1) 2010.09.03
[Step. 11] 열거형( enum )  (2) 2010.08.27

[Step. 13] parameter array

C++/CLI 2010. 9. 10. 09:00 Posted by 알 수 없는 사용자

비관리코드에서 로그 기능을 구현할 때 주로 가변 길이 인수를 사용합니다.

void LOG( char* szText, ... )

{

}

 


위와 같은 가변 길이 인수를 C++/CLI에서는 parameter array라는 것으로 구현합니다.

void LOG( … array<Object^>^ Values )

{

for each( Object^ value in Values )

{

   ….

}

}

 

int main()

{

LOG( 23 );

LOG( 2, 1, “error” );

 

return 0;

}

 


parameter array를 사용하면 이전 보다 안전하고, 하나의 형이 아닌 다양한 형을 인자로 받아 들일 수 있어서 유연성이 높습니다.

 

그러나 하나의 함수에서 parameter array는 하나 밖에 사용하지 못합니다. 하지만 parameter array가 아닌 형이라면 여러 개 사용할 수 있습니다. 다만 이 때는 parameter array는 가장 마지막 인수가 되어야 합니다.

 

void LOG( int nLogLevel, … array<Object^>^ Values )

{

}

 

 


참고

http://cppcli.shacknet.nu/cli:parameter_array

 

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

[Step. 14] 인터페이스 ( interface )  (1) 2010.10.01
[Step. 15] static 생성자, initonly, literal  (1) 2010.09.24
[Step. 12] for each  (1) 2010.09.03
[Step. 11] 열거형( enum )  (2) 2010.08.27
[Step. 10] 이벤트 ( event )  (0) 2010.08.20

안녕하세요. 지난 2010년 8월 28일, '한국 Visual Studio 공식 팀(Visual Studio Korea)' 에서 주최한 Visual Studio Camp #1 세미나의 발표 자료를 공개해 드립니다. (세미나의 후기는 [세미나 후기] Visual Studio Camp #1 를 참고하세요)    

※ 세미나 발표 자료의 저작권과 권리는 발표를 진행한 스피커와 Visual Studio Korea(한국 Visual Studio 공식 팀) 에 있으므로, 영리/비영리/변경하실 수 없습니다.

PDF 문서 다운로드

   

XPS 문서 다운로드

 

세미나 아젠다

  

Native 트랙

.NET 트랙

Enterprise 트랙

14:00 ~ 14:50

Visual Studio 2010 : C++0x와 Windows 7
최성기

그것이 알고싶다 - C# 4.0의 변화, 그 진실은 무엇인가. 희망인가? 또 다른 혼란인가?
강보람 C# MVP

VS Team Foundation Server 2010 의 새로운 변화
김병진 ALM MVP

15:00 ~ 15:50

비주얼 스튜디오 2010 의 Concurrency Runtime 을 이용한 멀티 코어 제대로 활용하기
임준환

좋은 프레임워크 있으면 소개시켜줘 - ASP.NET MVC
박세식

소프트웨어 품질 향상을 위한 다양한 테스트 기법
엄준일 ALM MVP

16:00 ~ 16:50

DirectX11 을 기다리며..
조진현

Beginnig WCF
오태겸

SharePoint 2010 Enterprise 솔루션 개발
정홍주 SQL Server MVP

   

발표 내용 소개 및 세미나 PPT 자료

Native 트랙

Visual Studio 2010 : C++0x와 Windows 7
그 동안 .NET 영역으로 적잖이 편중되었던 Visual Studio의 버전업에 비해 이번 2010 버전에서는 Native Code 개발환경에서도 많은 변화가 찾아왔다. C++0x 표준 반영에 의한 문법의 변화, 새로운 라이브러리 제공(Concurrency Runtime Library), Windows 7의 최신 기능들을 제어하기 위한 SDK의 업데이트 등이 그것이다. 본 세션을 통해 C++의 문법적인 변화와 Windows 7 기능 구현을 위한 SDK의 업데이트 사항들을 정리해본다.

 

비주얼 스튜디오 2010 의 Concurrency Runtime 을 이용한 멀티 코어 제대로 활용하기
요즘 가정의 PC 에 멀티 코어 프로세서가 많이 보급되어 있습니다. 하지만 실제로 PC 에 설치된 코어들을 모두 사용하는 애플리케이션들은 많지 않습니다. 이렇게 낭비되는 자원을 C++ 개발자가 쉽게 사용할 수 있도록 도와주는 Concurrency Runtime 을 비주얼 스튜디오 2010에서 제공합니다. 이 Concurrency Runtime 을 어떻게 시작해야 할지 알아보겠습니다.

 

DirectX11 을 기다리며...
조금씩 정보가 공개되면서 많은 변화를 예고하고 있는 DirectX11 에 대해서 살펴 볼 것입니다. 특히나 Tessellation, DirectCompute, Multi-threading 을 위한 기본 개념과 작업들에 대해서 체크해 볼 것입니다.

조진현님 PPT 는 현재 비공개이며, 추후 공개하도록 하겠습니다.

.NET 트랙

그것이 알고싶다 - C# 4.0의 변화, 그 진실은 무엇인가. 희망인가? 또 다른 혼란인가?
PDC 2008에 울려 퍼진 C# 4.0의 소식. 그 소식을 듣고 많은 사람들은 기대와 혼란을 가지게 되었다. C#은 분명히 정적 언어인데, 동적 언어에나 있을 법한 기능을 추가한다니? 이제 와서 뒷북일 수도 있는 C# 4.0의 변화에 대한 진실, 그 마지막 시리즈가 이제 시작된다. :)

   

좋은 프레임워크 있으면 소개시켜줘 - ASP.NET MVC
그 동안 아주 미묘하게 아쉬웠던 ASP.NET. 가려운 곳을 긁어줄 대안의 프레임워크가 나타났다. 웹 개발자들 한테 참~ 좋은데, 웹 개발자들 한테 정말 좋은데, 이걸 말로 그냥 할 수 없어서, 이번 기회에 소개한다.

   

Beginnig WCF
WCF는 서비스 지향 프로그래밍을 위해 마이크로소프트에서 개발 및 지원하는 기반 기술이며, 기존의 .NET 웹 서비스에 비해 유연성과 확장성이 뛰어나 최근 많은 관심을 받고 있습니다. 본 세션에서는 WCF가 무엇인지? 어떤 장점이 있는지? 그리고, WCF 를 이용하기 위해선 무엇이 필요한지? 에 대해 함께 알아보고, 마지막으로, WCF의 활용 예를 알아보도록 하겠습니다.

Enterprise 트랙

VS Team Foundation Server 2010 의 새로운 변화
V
isual Studio Team Foundation Server 2010의 혁신적인 변화와 개선 부분, 프로젝트 및 형상관리와 Agile의 Scrum 을 이용한 방법론을 알아보고, 단지 소스 체크인/아웃만 하는 Visual Source Safe에서 업그레이드 하는 방법에 대하여 알아봅니다.

   

소프트웨어 품질 향상을 위한 다양한 테스트 기법
소프트웨어는 개발 및 릴리즈 과정까지 수 많은 과정을 겪는데, 소프트웨어가 점진적으로 진화함에 따라 결함의 발생률이 증가합니다. 이를 개선하기 위한 테스트 기법 중 단위 테스트, WhiteBox 테스트, 화면 테스트, 성능 테스트, 부하 테스트 등 다양한 테스트 기법을 알아봅니다.

   

SharePoint 2010 Enterprise 솔루션 개발
SharePoint 2010은 기업 협업 플랫폼으로 개발자들은 VS 2010을 이용하여 더 생산성 있고 효과적인 SharePoint 2010 개발을 진행할 수 있습니다. 본 세션에서는 SharePoint 2010 개발에 대한 가장 필요한 내용을 구체적으로 알아보며 이를 통해 가장 많은 요구사항에 대한 실무 솔루션을 구성하는 방법에 대한 내용을 알아보겠습니다.

   

   

[Step. 12] for each

C++/CLI 2010. 9. 3. 09:00 Posted by 알 수 없는 사용자

데이터셋에 있는 요소를 열거할 때 비관리코드에서는 보통 for문이나 while문을 자주 사용합니다.

그러나 C++/CLI에서는 for each을 사용하여 데이터셋에 있는 요소들을 열거할 수 있습니다.

for each에서 사용할 수 있는 것은 배열 이외에도 아래의 형으로 구현한 것들을 사용할 수 있습니다.

 

1. IEnumerable 인터페이스를 구현한 클래스

2. STL의 이터레이터와 같은 것을 가지고 있는 클래스

 


참고로 VC++ 8(VS 2005)에서는 비관리코드에서도 for each 문을 지원하고 있습니다.

for each를 사용할 때 주의해야 할 점은 열거하는 요소를 변경할 수는 없다는 것입니다.

 

#include "stdafx.h"

#include <iostream>

#include <vector>

#include <map>

 

using namespace std;

using namespace System;

using namespace System::Collections;

 

 

int main(array<System::String ^> ^args)

{

     // 배열

     array< int >^ Nums = { 0, 1, 2, 3, 4, 5 };

     for each(int value in Nums)

     {

          value = 2;

          Console::WriteLine( value );

     }

    // 위에서 for each 내부에서 요소의 값을 바꾸었지만 아래의 출력 값을 보면

    // 바뀌지 않은 것을 알 수 있습니다.

    for each(int value in Nums)

    {

          Console::WriteLine( value );

    }

 

   

    // 리스트

    ArrayList^ NumList = gcnew ArrayList();

    NumList->Add(1);

    NumList->Add(2);

    NumList->Add(3);

 

    for each(int value in NumList)

    {

        Console::WriteLine(value);

    }

 

 

    // vector

    vector<int> vi;

    vi.push_back(3);

    vi.push_back(4);

   

    for each(int i in vi)

    {

        Console::WriteLine(i);

    }

 

 

    // map

    map<const char*, int> num;

    num["ten"] = 10;

    num["hundred"] = 100;

 

    for each( pair<const char*, int> c in num )

    {

        Console::WriteLine(gcnew String(c.first) + c.second.ToString());

    }

 

 

    // 해쉬 테이블

    Hashtable^ ht = gcnew Hashtable();

    ht["aaa"] = "111";

    ht["bbb"] = "222";

    for each(DictionaryEntry^ dic in ht)

    {

        Console::WriteLine(dic->Key->ToString() + dic->Value->ToString());

    }

    

 

    getchar();

    return 0;

}

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

[Step. 15] static 생성자, initonly, literal  (1) 2010.09.24
[Step. 13] parameter array  (1) 2010.09.10
[Step. 11] 열거형( enum )  (2) 2010.08.27
[Step. 10] 이벤트 ( event )  (0) 2010.08.20
[Step. 09] 델리게이트 (delegate)  (2) 2010.08.12

[Step. 11] 열거형( enum )

C++/CLI 2010. 8. 27. 09:00 Posted by 알 수 없는 사용자


비관리 코드의 열거형

 

비관리 코드에서 열거형을 정의할 때는 다음과 같습니다.

 

enum WEAPON_TYPE

{

GUN = 1,

SWORD = 2,

BOW = 3

};

 


열거형은 정수형으로 int 형에 대입할 수 있습니다.

int nUsedWeapon = GUN;

 

그런데 저는 위의 방식으로 사용할 때 ‘GUN’이라고 사용하기 보다는 ‘GUN’이 어떤 열거형에 속하는지 표시할 수 있도록 좀 더 다른 방식으로 사용하고 있습니다.

 

struct WEAPON

{

enum TYPE

{

GUN = 1,

SWORD = 2,

BOW = 3

};

};

 

int nUsedWeapon = WEAPON::GUN;

 

이렇게 저는 열거형을 조금 이상한 방법으로 사용하고 있는데 C++/CLI에서는 그럴 필요가 없어졌습니다. C++/CLI는 제가 딱 원하는 방식을 정식으로 지원하고 있습니다.

 


 

C++/CLI의 열거형

 

enum class WEAPON

{

   GUN = 1,

SWORD = 2,

BOW = 3

};

 

int nUsedWeapon = static_cast<int>(WEAPON::GUN);

 

C++/CLI의 열거형은 비관리코드와 비교해서 다른 점은 위에서 알 수 있듯이 암묵적으로 int 형에 대입할 수 없습니다. 왜냐하면 열거형은 정수형이 아니고 object이기 때문입니다.

그래서 캐스팅을 해야 합니다.

 

그리고 C++/CLI의 열거형은 정수형을 명시적으로 정할 수 있습니다.

enum class WEAPON : short

{

   GUN = 1,

SWORD = 2,

BOW = 3

};

 


< 추가 > - 2010. 12. 10

VC++10에서 열거형의 타입을 바로 위의 코드와 같이 명시적으로 지정해도 사용할 수가 없습니다.

enum을 사용할 때 타입 캐스팅을 해야합니다. 왜 이런지 저도 자세한 이유는 모르겠습니다.

C++/CLI에서는 enum 보다는 literal을 사용하는 것이 더 좋을 것 같다고 생각합니다.


public ref class WEAPON
{
public:
        literal short GUN = 1;
        literal short SWORD = 2;

        literal short BOW = 3;

};




참고

http://msdn.microsoft.com/ko-kr/library/ms235243.aspx

 

 

 



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

[Step. 13] parameter array  (1) 2010.09.10
[Step. 12] for each  (1) 2010.09.03
[Step. 10] 이벤트 ( event )  (0) 2010.08.20
[Step. 09] 델리게이트 (delegate)  (2) 2010.08.12
[Step. 08] 프로퍼티 ( property )  (8) 2010.08.06


앞선 시간을 통해서 ID3DXPatchMesh 를 이용하면
간단하게 테셀레이션이 적용된 메시를 만들 수 있음을 언급했었습니다.
실제로 D3DX 유틸리티 클래스들이 테셀레이션을 손쉽게 적용할 수 있도록 구비가 되어있습니다.
그렇다는 것은 실제로는 DirectX 내부적으로 코어한 API가 있다는 얘기입니다.

테셀레이션과 관련한 DirectX 에서 코어한 API가 바로
IDirect3DDevice9::DrawTriPatch() 와 IDirect3DDevice9::DrawRectPatch() 입니다.
API 이름에서 쉽게 이해할 수 있듯이 전자는 삼각형과 관련한 것이고 후자는 사각형과 관련한 것입니다.
두 함수의 원형은 다음과 같습니다.

HRESULT DrawTriPatch
(
  [in]  UINT Handle,
  [in]  const float *pNumSegs,
  [in]  const D3DTRIPATCH_INFO *pTriPatchInfo
);


HRESULT DrawRectPatch(
  [in]  UINT Handle,
  [in]  const float *pNumSegs,
  [in]  const D3DRECTPATCH_INFO *pRectPatchInfo
);


그런데 조금 생소한 구조체 정보를 함수 인자로 받습니다.
이 두 API들은 함수 이름에서도 알 수 있듯이 실제로 렌더링을 수행하는 API 입니다.
테셀레이션을 위해서는 테셀레이션을 위한 정보들이 존재해야 합니다.
이들에 대한 설정 작업이 이루어져야 하는데,
이를 위한 구조체가 세번째 인자인 D3DTRIPATCH_INFO와 D3DRECTPATCH_INFO 입니다.
사각형과 관련한 작업은 삼각형과 유사하기 때문에 지금부터는 삼각형에 국한에서 글을 진행하겠습니다.


D3DTRIPATCH 구조체의 원형은 다음과 같습니다.

typedef struct D3DTRIPATCH_INFO
{
  UINT          StartVertexOffset;
  UINT          NumVertices;
  D3DBASISTYPE  Basis;
  D3DDEGREETYPE Degree;
} D3DTRIPATCH_INFO, *LPD3DTRIPATCH_INFO;


이 구조체는 버텍스 버퍼처럼 오프셋과 버텍스 갯수를 먼저 설정합니다.
D3DBASISTYPE 은 고차원 패치( high-order patch )의 기본 타입을 설정합니다.
삼각형의 경우에는 D3DBASIS_BEZIER 만 설정할 수 있습니다.

D3DDEGREETYPE 는 고차원 패치의 차수 정도를 설정하게 됩니다.
즉, 곡선을 표현하는 방정식의 차수를 표현하는데,
높은 차수를 선택할 수록 당연히 연산량이 많아질 것입니다.

이들에 대한 종류는 다음과 같습니다.

종류

버텍스 갯수
D3DDEGREE_CUBIC 10 ( 3차 방정식 )
D3DDEGREE_LINEAR 3   ( 1차 방정식 )
D3DDEGREE_QUADRATIC N/A ( 지원되지 않음 ) ( 2차 방정식 )
D3DDEGREE_QUINTIC 21 ( 4차 방정식 )


아래의 그림은 Cubic Bézier 방식의 삼각형 패치를 보여주고 있습니다.


Diagram of a triangular high-order patch with nine vertices


중간 중간에 생성된 정점을 기준으로 테셀레이션 작업이 수행될 것입니다.^^
이런 테셀레이션과 관련한 API들이 DirectX9 에 있었지만, 사실 거의 사용되지는 못했습니다.
왜냐하면, 정말이지 많은 연산을 필요로 하기 때문이겠죠? ^^
즉, DirectX9의 테셀레이션 작업은 소프트웨어적으로 에뮬레이션 되는 테셀레이션입니다.



이번 시간에 해 볼 내용은 동적으로 컨트를들을 생성하는 작업입니다.




모두 이렇게 아무것도 없는 상태로 프로젝트를 만들어 주시기 바랍니다.
지금부터 아무 것도 없는 화면에서 동적으로 컨트롤들을 생성해서 배치할 것입니다.
즉, 애플리케이션 실행 중에 컨트롤을 만들 것입니다.

가장 단순한 것이 버튼 컨트롤일 것입니다.
동적으로 버튼 컨트롤을 생성하기 위해서는 당연한 얘기지만,
폰 페이지가 생성되는 시점에 컨트롤을 만들어주는 코드를 추가해 주면 됩니다.
*.cs 파일을 열어서 다음과 같이 내용을 추가해 줍니다.




이들은 블렌드 툴에서 컨트롤 속성을 설정하는 부분을,
프로그램 소스 상에서 속성을 제어하는 부분입니다.
속성 정보는 중에 제가 현재 이벤트를 연결한 부분이 있습니다.
pStartCtrl.Click += OnStartButtonClick 와 pEndCtrl.Click += OnEndButtonClick 부분입니다.
이벤트도 코드 상에서 이렇게 연결해 줄 수 있습니다.


현재 예제에서는 간단히 메시지 박스를 출력하는 것은 마무리 했습니다.
대부분의 컨트롤들은 위의 방법과 같이 동적으로 생성해 줄 수 있습니다.
여기까지가 예제 샘플 작업의 끝입니다.^^






그런데 한가지 생소한 부분이 있을 것이라 생각을 합니다.
바로 ContentPanel.Children.Add( XXX ) 부분입니다.
어딘가에 현재 우리가 생성한 컨트롤들을 추가해주고 있습니다.
과연 이 패널은 무엇일까요?

프로그램 작성을 마치고, 다시 디자인 폼 수정 화면으로 돌아옵니다.



위의 화면을 확인해 보실 수 있겠죠?
앞에서 언급한 낯선 이름이 보입니다.
'ContentPanel' 보이시죠?

여기에 버튼을 하나 만들어 봅니다.
이번에는 소스 코드 레벨이 아니라, 디자인 폼 상에서 버튼을 바로 배치해 봅니다.




디자인 폼 상에서 버튼을 배치시키면,
바로 ContentPanel 의 하위 계층에 추가되어지는 것을 확인할 수 있습니다.
즉, 앞선 언급했던 ContentPanel.Children.Add( XXX ) 소스 코드는 바로
이 작업을 해준 것입니다.

조금 더 정확히 얘기하자면,
윈도우폰7의 레이아웃은 크게 TitlePanel과 ContentPanel 로 나눠져 있습니다.
그래서 이들 패널에 컨트롤을 추가했던 것입니다.

패널( Panel )이 무엇인지 모르시겠다구요?
패널은 이후의 시간에 언급될 기회가 있을 것입니다만,
지금은 단순히 컨트롤들을 그룹화 시켜서
자동으로 관리시켜주는 것 정도로 생각하시면 될 것입니다.^^


샘플 파일 첨부해드립니다..^^

[Step. 10] 이벤트 ( event )

C++/CLI 2010. 8. 20. 09:00 Posted by 알 수 없는 사용자
이벤트는 그 이름처럼 이벤트를 처리할 때 사용하는 것으로 델리게이트와 조합하여 사용합니다.

보통 이벤트를 설명할 때 button 클래스를 예제로 사용하므로 저도 이것을 사용하여 간단하게 설명하겠습니다.^^

button 클래스의 여러 이벤트 중 clicked 이벤트를 구현할 때 이벤트로 불러질 클라이언트는 OnClick()이라는 멤버함수를 정의한 후 이것을 button 클래스의 clicked에 등록합니다.

 

delegate void ClickHandler();
ref class button {
public:
    event ClickHandler^ Clicked;
    button() {
        btnForTest = this;
    }
public:
    void someoneclicked() {
        Clicked();
    }
    static button^ btnForTest;
};

ref class Client {
private:
    button^ btn;
public:
    Client() {
        btn = gcnew button;
        btn->Clicked += gcnew ClickHandler(this, &Client::OnClick);
    }
private:
    void OnClick() {
        Console::WriteLine("someone clicked.");
    }
};
int main()
{
    Client^ client = gcnew Client;
   
button::btnForTest->someoneclicked();
    return 0;
}
< 출처 : http://cppcli.shacknet.nu/cli:event >
 
 
event는 몇 개의 제약이 있습니다. 
1. Clicked 호출은 button 밖에 할 수 없습니다. 
2. 클라이언트가 Clicked에 대한 조작은 += -=만 할 수 있으며 Clicked() Clicked = nullptr 같은 
것은 할 수 없습니다.
 
 
참고
http://cppcli.shacknet.nu/cli:event






[Step. 09] 델리게이트 (delegate)

C++/CLI 2010. 8. 12. 09:30 Posted by 알 수 없는 사용자
델리게이트는 비관리코드에서는 함수 포인터와 같은 기능을 합니다.

 

#include <iostream>

delegate void TestDelegate();

ref class TEST

{

public:

void TestMethod()

{

  System::Console::WriteLine(“TEST::TestMethod()”);

}

};

 

int main()

{

TEST^ test = gcnew TEST();

TestDelegate^ testDelegate = gcnew TestDelegate( test, &TEST::TestMethod );

 testDelegate();


 getchar();

    return 0;

};

 

 

정적 멤버함수의 델리게이트

 

#include <iostream>

 delegate void TestDelegate();

 ref class TEST

{

public:

static void TestMethod()

{

  System::Console::WriteLine(“TEST::TestMethod()”);

}

};

 

int main()

{

TestDelegate^ testDelegate = gcnew TestDelegate( &TEST::TestMethod );

 

testDelegate();

 

getchar();

return 0;

};

 

 

복수 개의 델리게이트

 

델리게이트는 하나가 아닌 여러 개를 설정할 있습니다.

 

#include <iostream>

delegate void TestDelegate();

 

ref class TESTA

{

public:

void TestMethod()

{

  System::Console::WriteLine(“TESTA::TestMethod()”);

}

};

 

ref class TESTB

{

public:

static void TestMethod()

{

  System::Console::WriteLine(“TESTB::TestMethod()”);

}

};

 

ref class TESTC

{

public:

void operator()()

{

  System::Console::WriteLine(“TESTC::operator()”);

}

};

 

int main()

{

TestDelegate^ testDelegate;

TESTA testA;

TESTC testC;

 

testDelegate = gcnew TestDelegate( testA, &TESTA::TestMethod );

testDelegate += gcnew TestDelegate(&TESTB::TestMethod );

testDelegate += gcnew TestDelegate( testC, &TESTC::operator );

 

testDelegate();

 

getchar();

return 0;

};

 

 

델리게이트의 비교와 삭제

 ==으로 델리게이트를 비교하면 이것은 핸들의 비교가 아닌 델리게이트가 가지고 있는 함수를 비교하는 것입니다. -=을 사용하여 설정한 델리게이트를 제거할 수도 있습니다.

using namespace System;

delegate void MyDele(int);


void func1(int i)

{

    Console::WriteLine("func1");

}


void func2(int j)

{

    Console::WriteLine("func2");

}


int main()

{

    MyDele^ dele;

    dele += gcnew MyDele(&func1);

    dele += gcnew MyDele(&func2);

    MyDele^ dele2 = gcnew MyDele(func1);

    dele2 += gcnew MyDele(&func2);

    if ( dele == dele2 )  {

        Console::WriteLine("TRUE");

    } else {

        Console::WriteLine("FALSE");

    }

   

    dele -= gcnew MyDele(&func1); 

   

    dele(1); 

   

   return 0;

}

< 예제 출처 : http://cppcli.shacknet.nu/cli:delegate >

 

위의 예의 델리게이트들은 모두 void를 반환하고 파라미터가 없는 것인데 당연하듯이 반환 값이나 파라미터를 가질 수 있습니다.




델리게이트의 비동기 실행

 

델리게이트는 비동기 실행을 지원합니다. 비동기 실행은 처리를 요청한 후 종료를 기다리지 않은 호출한 곳으로 제어를 넘겨줍니다. 델리게이트 함수가 긴 시간을 필요로 하는 작업인 경우 비동기 실행을 이용하면 프로그램의 응답성을 높일 수 있습니다.

 

델리게이트의 비동기 실행은 스레드를 사용합니다. 이런 경우 비동기 실행을 할 때마다 스레드의 생성과 소멸에 부담을 느낄 수도 있지만 델리게이트는 닷넷의 기능을 잘 활용하여 스레드를 생성/삭제하지 않고 스레드 풀에 있는 스레드를 사용하므로 스레드 사용에 대한 부담이 작습니다.

 

비동기 실행을 할 때는 주의해야 할 점이 있습니다. 비동기 실행을 하는 경우 델리게이트에는 꼭 하나의 함수만 등록해야 합니다. 만약 2개 이상 등록하였다면 예외가 발생합니다.

 

비동기 실행은 BeginInvoke()를 사용하고, 만약 종료를 기다리고 싶다면 EndInvoke()를 사용합니다.

 

#include "stdafx.h"

#include <iostream>

 

using namespace System;

 

delegate void MyDele(void);

 

void myfunc(void)

{

    System::Threading::Thread::Sleep(3000);

}

 

 

int main(array<System::String ^> ^args)

{

     MyDele^ dele = gcnew MyDele(&myfunc);

            Console::WriteLine(L"1");

     IAsyncResult^ result = dele->BeginInvoke(nullptr,nullptr);

            Console::WriteLine(L"2");

    dele->EndInvoke(result);

            Console::WriteLine(L"3");

   

    getchar();

    return 0;

}

 

위 코드를 실행하면 '2'가 찍힌 이후 3초가 지난 이후에 3이 찍힙니다.





참고
http://cppcli.shacknet.nu/cli:delegate
http://cppcli.shacknet.nu/cli:delegate%E3%81%9D%E3%81%AE2



VS2010 C++ 프로젝트의 디렉토리 설정

Visual C++ 10 2010. 8. 9. 09:00 Posted by 알 수 없는 사용자

VS2008까지는 도구’ – ‘옵션메뉴를 통해서 VC++ 디렉토리를 설정하였습니다.

이렇게 설정한 정보는 모든 VC++ 프로젝트에 적용됩니다.

 

그러나 VS2010에서는 각 프로젝트 별로 VC++ 디렉토리를 설정합니다.

 


각 프로젝트 마다 독립적으로 설정을 할 수 있어서 편한 부분도 있지만 때로는 모든 프로젝트에 공통적으로 적용되어야 하는 경우는 매번 설정하는 것이 귀찮을 수 있습니다.

( 예로 DirectX boost 라이브러리 등 )

 

이런 경우 속성 매니저를 통해서 VC++ 디렉토리를 설정하면 모든 프로젝트에 공통적으로 적용할 수 있습니다.

 

1. 일단 아무 프로젝트 하나를 열어 놓습니다.

2. 메뉴의 보기’ -> ‘속성 관리자를 선택합니다.



3. 속성 관리자에서 ‘Microsoft.Cpp.Win32.user’를 더블 클릭해서 열어 놓습니다.


 

여기서 설정한 정보는 모든 프로젝트에 공통적으로 적용됩니다.

 

 

[Step. 08] 프로퍼티 ( property )

C++/CLI 2010. 8. 6. 09:30 Posted by 알 수 없는 사용자

보통 비관리 클래스를 정의할 때 캡슐화를 위해서 멤버 변수는 최대한 private 접근으로 한 후 외부에서의 접근을 위해서 get set 멤버 함수를 정의합니다.

 

class Character

{

public:

…….

   void SetCharCd( const int nCharCd ) { m_nCharCd = nCharCd; }

   int GetCharCd() { return m_nCharCd; }

…….

private:

   int m_nCharCd;

   …….

};

 


클래스의 멤버를 하나 정의할 때마다 그에 대응하는 get, set 멤버 함수를 정의하는 것은 좀 귀찮은 일이기도 합니다. 그래서 관리코드에서는 이 작업을 쉽게 해주는 property가 생겼습니다.

 

ref class Character

{

public:

…….

    property int CharCd

{

   void set( int nCharCd ) { m_nCharCd = nCharCd; }

   int get() { return m_nCharCd; }

}  

…….

private:

   int m_nCharCd;

   …….

};

 

위 코드에서는 get set을 둘 다 정의 했는데 둘 중 하나만 정의 해도 괜찮습니다.

또 위의 set은 아주 간단하게 그냥 대입만 하고 있는데 좀 더 로직을 넣을 수도 있습니다.

 

ref class Character

{

public:

…….

    property int CharCd

{

   void set( int nCharCd )

{

  if( nCharCd < 0 ) {

     m_nCharCd = 0;

  } else {

     m_nCharCd = nCharCd;

  }

}

 

   int get() { return m_nCharCd; }

}  

…….

private:

   int m_nCharCd;

   …….

};

 

 


property 선언과 정의 나누기


일반적으로 클래스의 멤버 선언은 헤더 파일에 정의는 cpp 파일에 하듯이 property도 선언과 정의를 나눌 수 있습니다.

 

ref class Character

{

public:

…….

    property int CharCd

{

   void set( int nCharCd );

   int get();

}  

…….

private:

   int m_nCharCd;

   …….

};

 

 

void Character::CharCd::set( int nCharCd )

{

if( nCharCd < 0 ) {

m_nCharCd = 0;

} else {

   m_nCharCd = nCharCd;

  }

}

 

void Character::CharCd::get()

{

return m_nCharCd;

}

 



 

get set의 접근 지정자 다르게 하기

 

위에서 property를 정의할 때 get set은 모두 public 였습니다.

이것을 각각 다르게 접근 지정자를 정할 수 있습니다.

 

ref class Character

{

public:

…….

    property int CharCd

{

protected:

   void set( int nCharCd )

{

  if( nCharCd < 0 ) {

     m_nCharCd = 0;

  } else {

     m_nCharCd = nCharCd;

  }

}

 

    public:

   int get() { return m_nCharCd; }

}  

…….

private:

   int m_nCharCd;

   …….

};

 

 

 

 

참고

http://cppcli.shacknet.nu/cli:property

http://vene.wankuma.com/prog/CppCli_Property.aspx

 

 

[Upgrade to VC++ 10] _WIN32_WINNT 버전 문제

Visual C++ 10 2010. 8. 2. 09:00 Posted by 알 수 없는 사용자

VS.NET(VS2002)에서 MFC 프로젝트로 만들었던 프로그램을 VC++10 프로젝트로 변환하여 컴파일 했더니 에러가 발생하면서 아래의 경고가 나왔습니다.

 

C:\Program Files\Microsoft Visual Studio 10.0\VC\atlmfc\include\atlcore.h(35):#error This file requires _WIN32_WINNT to be #defined at least to 0x0403. Value 0x0501 or higher is recommended.

 

에러 내용은 프로젝트에서 정의된 _WIN32_WINNT 버전이 0x403인데 atlcore.h 버전이 최소 0x0501 이상이 되어야 한다는 것입니다.

 

그래서 _WIN32_WINN 정의한 stdafx.h 파일을 열어보니

#define _WIN32_WINNT 0x0400

되어 있었더군요. 그래서 일단 이것을 최신이 좋다라는 생각에 아래와 같이 했습니다. ^^;;

#define _WIN32_WINNT 0x0600

 

그랬더니 이제는 아래와 같은 에러가 나오더군요. -_-;

c:\program files\microsoft visual studio 10.0\vc\atlmfc\include\afxcmn3.inl(29): error C2065: 'CCM_SETWINDOWTHEME' : 선언되지 않은 식별자입니다.

 

그래서 바로 구글링 들어갔습니다.

쉽게 저와 같은 에러가 나와서 질문을 올린 글을 찾았고 답변도 보았습니다.

문제 해결은 stdafx.h 파일에 정의된 버전의 숫자를 아래와 같이 하면 된다고 하더군요

// Modify the following defines if you have to target a platform prior to the ones specified below.
// Refer to MSDN for the latest info on corresponding values for different platforms.
#ifndef WINVER // Allow use of features specific to Windows 95 and Windows NT 4 or later.
#define WINVER 0x0501 // Change this to the appropriate value to target Windows 98 and Windows 2000 or later.
#endif

#ifndef _WIN32_WINNT // Allow use of features specific to Windows NT 4 or later.
#define _WIN32_WINNT 0x0501 // Change this to the appropriate value to target Windows 98 and Windows 2000 or later.
#endif

#ifndef _WIN32_WINDOWS // Allow use of features specific to Windows 98 or later.
#define _WIN32_WINDOWS 0x0501 // Change this to the appropriate value to target Windows Me or later.
#endif

#ifndef _WIN32_IE // Allow use of features specific to IE 4.0 or later.
#define _WIN32_IE 0x0500 // Change this to the appropriate value to target IE 5.0 or later.
#endif

 

이렇게 하니 문제 없이 빌드가 성공 되었습니다.

 

주위에서 VC++의 새로운 버전이 나와도 쉽게 사용하지 못하는 경우가 오래 전에 만들었던 프로젝트를 포팅 할 수 없어서 이전 버전을 어쩔 수 없이 사용한다는 이야기를 종종 듣습니다.

그러나 저는 운이 좋아서인지 2002버전부터 순차적으로 새 버전의 VC++을 사용할 수 있어서 VC++6에서 VS2002로 넘어갈 때만 빌드 문제를 겪었습니다.

그래서 이런 포팅에 대한 문제는 잘 알지 못합니다. 이번에는 예전에 만들었던 코드를 C++0x 코드로 바꾸고 싶어서 오래 전에 만들었던 프로젝트를 VC++ 10로 포팅하면서 정말 정말 오랜만에 이런 문제를 겪어 보게 되고 해결 방법을 포스팅 할 수 있었습니다.

 

혹시 앞으로 또 이런 경우가 발생하면 바로 바로 공유하도록 하겠습니다.