Search

'VS2010'에 해당되는 글 70건

  1. 2010.07.30 [Step. 07] 비관리 클래스에서 관리 클래스를 멤버로, 관리 클래스에서 비관리 클래스를 멤버로
  2. 2010.07.23 [Step. 06-2] 관리코드의 문자열과 비관리코드의 문자열 변환 3
  3. 2010.07.19 [JumpToDX11-14] DirectX9 세대의 테셀레이션( ID3DXPatchMesh 편 )
  4. 2010.07.16 [Step. 06-1] 관리코드의 문자열과 비관리코드의 문자열 변환
  5. 2010.07.09 [Step. 05] 관리 코드의 array를 비관리 코드에 포인터로 전달
  6. 2010.06.29 VS2008 과 VS2010 동시에 개발하기 : 테스트 프로젝트가 포함 될 경우 3
  7. 2010.06.25 [Step. 04] nullptr, interior_ptr, pin_ptr 2
  8. 2010.06.24 VS2008 을 VS2010 에서 동시에 개발하기 3
  9. 2010.06.18 [step.03] 배열 1
  10. 2010.06.16 [MFC/윈도우 7 멀티터치] #3 : 제스처(gesture)를 이용한 구현(下)
  11. 2010.06.15 [JumpToDX11-13] Tessellation 등장.
  12. 2010.06.11 [Step.02-2] 클래스(class), 핸들(^), 그리고 구조체(struct) 5
  13. 2010.06.04 [Step 02-1] 클래스(class), 핸들(^), 그리고 구조체(struct) 1
  14. 2010.05.28 [Step 01] ‘C++/CLI가 뭐야?’에 답하기 && 가장 많은 프로그래밍 언어로 만드는 프로그램 만들기
  15. 2010.05.21 .NET에서의 C++/CLI의 의미
  16. 2010.05.11 C++/CLI는 미운 오리새끼 or 백조 2
  17. 2010.05.10 [MFC/윈도우 7 멀티터치] #2 : 제스처(gesture)를 이용한 구현(上) 3
  18. 2010.05.02 Visual C++ 10의 변화
  19. 2010.04.26 [JumpToDX11-12] DirectCompute 의 절차.
  20. 2010.04.20 "Visual C++ 10과 C++0x" pdf 파일 4

1. 비관리 클래스에서 관리 클래스를 멤버로

 

비관리 클래스에서 관리 클래스를 멤버로 가지고 싶을 때는 ‘gcroot’라는 템플릿을 사용합니다.

 

#include "stdafx.h"

#include <iostream>

#include <vcclr.h>

 

using namespace System;

 

class TEST

{

public:

   TEST() {}

   ~TEST() {}

 

   gcroot< String^ > m_str;

};

 

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

{

    TEST test;

    test.m_str = gcnew String("Hello VSTS 2010");

 

    Console::WriteLine( test.m_str);

   

    getchar();

    return 0;

}

 



‘gcroot’를 사용하기 위해서는

#include <vcclr.h>

를 포함해야 합니다.

 

비관리 클래스에서 관리 클래스인 String을 다음과 같이 멤버로 선언합니다.

gcroot< String^ > m_str;


그리고 사용하기 위해서는 할당을 합니다.

test.m_str = gcnew String("Hello VSTS 2010");

 

 



2. 관리 클래스에서 비관리 클래스를 멤버로

 

관리 클래스에서 비관리 클래스를 멤버로 가질 때는 비관리 클래스를 포인터로 선언하여 비관리 힙에 동적할당을 합니다.

 

#include "stdafx.h"

#include <iostream>

#include <vcclr.h>

 

using namespace System;

 

class TEST

{

public:

       TEST() {}

       ~TEST() {}

};

 

ref class refTEST

{

public:

       refTEST() {}

       ~refTEST() {}

 

       TEST* test;

};

 

int main()

{

       refTEST^ refTest = gcnew refTEST();

       refTest->test = new TEST;

 

       return 0;

}

 

 

 

참고

http://msdn.microsoft.com/ko-kr/library/481fa11f%28v=VS.80%29.aspx

http://blog.naver.com/scor7910/40048083284

 


 

[Step. 06-2] 관리코드의 문자열과 비관리코드의 문자열 변환

C++/CLI 2010. 7. 23. 20:30 Posted by 알 수 없는 사용자

3) String^ C/C++ 문자열로 변환

 

1)번에서는 C/C++의 문자열을 String^로 변환하는 방법에 대해서 설명했습니다.

이번에는 String^ char* wchr_t*로 변환하는 방법에 대해서 설명합니다.

 

아래의 예제 코드를 봐 주세요

 

#include <string>

#include <msclr\marshal_cppstd.h>

 

using namespace System;

using namespace msclr::interop;

 

int main()

{

           System::String^ s0 = L"비주얼스튜디오2010 팀블로그";

          

           // 방법 1

           std::string tmp = marshal_as<std::string>(s0);

           const char* s1 = tmp.c_str();

           std::cout << "String^ -> string : " << s1 << std::endl;

 

           // 방법 2

           const char* s2;

           const wchar_t* s3;

           {

                     marshal_context ctx;

                     s2 = ctx.marshal_as<const char*>(s0);

                     s3 = ctx.marshal_as<const wchar_t*>(s0);

            

                     std::cout << "String^ -> char* : " << s2 << std::endl;

                    

                     setlocale(LC_ALL, "");

                     std::wcout << "String^ -> wchar_t : " << s3 << std::endl;

           }

 

           getchar();

           return 0;

}

 

String^ char* wchr_t*로 변환하는 방법은 두 가지가 있습니다.

 


첫 번째 std::string 사용


가장 간단한 방법입니다만 불필요한 std::string을 사용해야 단점이 있습니다.

std::string tmp = marshal_as<std::string>(s0);

const char* s1 = tmp.c_str();

std::cout << "String^ -> string : " << s1 << std::endl;

 

 

두 번째 marshal_context 사용


첫 번째 방법에서 std::string을 사용한 이유는 다름이 아니고 메모리 확보 때문입니다.

마샬링을 통해서 char* wchar_t*에 메모리 주소를 저장합니다. 문자열 그 자체를 복사하는 것이 아닙니다. 그래서 변환한 문자열을 저장할 메모리 주소를 확보하고 사용 후에는 해제를 해야 합니다. 메모리 확보와 해제를 위해서 marshal_context를 사용합니다.

marshal_context는 변환에 필요한 메모리를 확보하고, 스코프를 벗어날 때 메모리를 해제합니다.

const char* s2;

const wchar_t* s3;

{

           marshal_context ctx;

           s2 = ctx.marshal_as<const char*>(s0);

           s3 = ctx.marshal_as<const wchar_t*>(s0);

}

 

String^ C/C++ 문자열로 변환할 때는 std::string + marshal_as marshal_context 둘 중 하나를 선택하여 사용합니다.

 




참고

http://msdn.microsoft.com/en-us/library/bb384865.aspx

http://msdn.microsoft.com/ko-kr/library/bb531313%28VS.90%29.aspx

http://codezine.jp/article/detail/4774

[JumpToDX11-14] DirectX9 세대의 테셀레이션( ID3DXPatchMesh 편 )

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


DirectX11 을 통해서 가장 많은 관심을 가지고 있는 부분 중 하나인 테셀레이션( Tessellation )은
갑자기 등장한 새로운 기능이 아닙니다.


< DirectX9에서의 테셀레이션의 등장 >

DirectX9 이 처음 세상에 등장할 때, 아래와 같은 특징들을 나열했었습니다.

- 2-D support
blt, copy, fill operations, GDI dialogs
- Adaptive tessellation
- Displacement mapping
- Two-sided stencil operations
- Scissor test rect
- Vertex stream offset
- Asynchronous notifications
- VS / PS 2.0
Flow control, float pixels
- Multiple render targets
- Gamma correction


Adaptive tessellation 이 보이시죠?
저도 그냥 무심코 지났던 DirectX9 소개 자료에서 우연히 찾았습니다.^^


< Adaptive tessellation >

테셀레이션에는 몇 가지 방법이 있는데,
그 중에 가장 유명한 것이 Adaptive 형식과 Uniform 형식입니다.
아래의 이미지를 보시기 바랍니다.


< 이미지 출처 : GPU Gems 2권 >


좌측의 경우가 Adaptive 한 방식입니다.
Adaptive 한 방식을 간단히 설명드리면,
시점의 위치에 근거에서 얼마나 많은 면을 생성할 지를 판단해서,
테셀레이션 작업
을 하는 것입니다.

반면에 Uniform 한 방식은,
모두 균일한 면의 갯수로 테셀레이션 작업을 수행하는 방법
입니다.
Uniform 한 방식이 더 연산 수가 많은 것이 일반적이기 때문에,
Adaptive 한 방식이 게임 분야에서 주로 사용됩니다.



< 테셀레이션을 위해 필요한 정보 >

테셀레이션 작업을 위해서는 두 가지가 필요합니다.
그것은 제어점들( Control Points )과 테셀레이션 팩터들( Tessellation Factors ) 입니다.
제어점들은 파이프라인에 입력으로 들어감으로써 패치( Patch ) 형태로 변환되어서
최종적으로 렌더링
되게 됩니다.
이 과정에 대한 자세한 설명은 앞으로도 꾸준히 언급될 것입니다.
지금은 간단하게 이 정도로만 설명하고 넘어가겠습니다.^^



< ID3DXPatchMesh >

그러면 DirectX9 은 어떤 방식으로 테셀레이션 작업을 지원했을까요?
그것은 ID3DXPatchMesh 라는 인터페이스를 통해서 간접적으로 지원했습니다.

참고적으로 얘기드리면, DirectX 에서는 D3DX 라는 유틸리티를 통해서
메시를 관리할 수 있는 클래스를 제공했습니다.
ID3DXBaseMesh, ID3DXMesh, ID3DXSPMesh, ID3DXPMesh,
그리고 마지막으로 언급드렸던 ID3DXPatchMesh 입니다.

ID3DXPatchMesh 인터페이스는 다른 메시들을 지원하는 클래스와 다릅니다. 
일반적인 메시 인터페이스들은 ID3DXBaseMesh와 계층 관계를 이루는 반면에,
ID3DXPatchMesh 는 완전히 별도로 구성된 클래스입니다.
즉, ID3DXPatchMesh 클래스는 IUnknown 인테페이스를 상속받습니다.


ID3DXPatchMesh는 테셀레이션 작업을 위해서 각종 멤버 함수를 가지고 있습니다.
실제로 테셀레이션 작업을 하는 함수는 ID3DXPatchMesh::Tessellate() 와
ID3DXPatchMesh::TessellateAdaptive()
입니다.
이들 함수에 대한 형태는 다음과 같습니다.

HRESULT Tessellate
(
  [in]  FLOAT fTessLevel,
  [in]  LPD3DXMESH pMesh
);

HRESULT TessellateAdaptive
(
  [in]  const D3DXVECTOR4 *pTrans,
  [in]  DWORD dwMaxTessLevel,
  [in]  DWORD dwMinTessLevel,
  [in]  LPD3DXMESH pMesh
);

두 멤버함수 모두 LPD3DXMESH 형태의 테셀레이션 작업이 끝난 메시를 리턴합니다.

이들에 대한 모든 작업은 CPU 가 담당합니다.
또한 연산량도 많기 때문에 Adaptive Tessellation을 처리하기는 상당한 무리가 있습니다.
왜냐하면 Adaptive Tessellation은 시점에 근거해서 매번 폴리곤을 생성해야하기 때문입니다.
ID3DXPatchMesh::Optimize() 라는 최적화 함수를 미리 호출해 줄수도 있지만,
그래도 이는 분명 매우 부담스러운 연산입니다.

< 마치면서... >
이상으로 ID3DXPatchMesh 를 활용한 DirectX9 의 테셀레이션 작업에 대해서 살펴보았습니다.
DirectX9 에서의 테셀레이션 작업의 불편함과 성능 문제를 이해한다면,
DirectX11 에서의 테셀레이션 작업의 우수성을 알 수 있을 것이라 생각됩니다.
다음 시간에도 계속 DirectX9 에서의 테셀레이션 작업에 대해서 살펴보겠습니다.^^

[Step. 06-1] 관리코드의 문자열과 비관리코드의 문자열 변환

C++/CLI 2010. 7. 16. 08:30 Posted by 알 수 없는 사용자

관리코드와 비관리코드를 혼합해서 사용할 때 서로간에 문자열을 주고 받아야 하는 경우가 종종 있을 것입니다. 관리코드와 비관리코드간에 문자열을 서로 어떻게 변환하여 주고 받는지 알아보겠습니다.

 

 

1) C/C++ 문자열을 String^으로 변환

 

먼저 아래의 변환 예제 코드를 봐 주세요

 

#include <msclr\marshal.h>

 

using namespace System;

using namespace msclr::interop;

 

int main()

{

           const char* message = "Forever Visual C++";

           String^ result1 = marshal_as<String^>( message );

           Console::WriteLine("char -> System::String : {0}", result1);

 

           const wchar_t* Wmessage = L"Visual C++이여 영원하라";

           String^ result2 = marshal_as<String^>( Wmessage );

           Console::WriteLine("wchar -> System::String : {0}", result2);

 

           getchar();

           return 0;

}

 


관리코드와 비관리코드 간의 문자열 변환에는 msrshal_as를 사용하여 마샬링합니다.

C/C++ 문자열을 마샬링하기 위해서는

#include <msclr\marshal.h>

파일을 포함하고,


using namespace msclr::interop;

네임스패이스를 선언합니다.

 

사용 방법은 아주 간단합니다.

marshal_as< 변환할 문자 타입 >( 원본 문자열 );

 


ANSI 문자열을 관리코드의 문자열로 변환할 때는 아래와 같이 합니다.

const char* message = "Forever Visual C++";

String^ result1 = marshal_as<String^>( message );

 

유니코드를 관리코드의 문자열로 변환할 때는 아래와 같이 합니다.

const wchar_t* Wmessage = L"Visual C++이여 영원하라";

String^ result2 = marshal_as<String^>( Wmessage );

 

 



2) STL string String^간의 변환

 

이것도 marshal_as를 사용합니다.

아래의 예제 코드를 봐 주세요

 

#include <iostream>

#include <string>

#include <msclr\marshal_cppstd.h>

 

using namespace System;

using namespace msclr::interop;

 

int main()

{

           std::string s0 = "비주얼스튜디오2010 팀블로그";

           std::cout << "string : " << s0 << std::endl;

 

           System::String^ s1 = marshal_as< System::String^ >(s0);

           Console::WriteLine("std::sting->System::String : {0}", s1);

 

           std::wstring s2 = marshal_as< std::wstring >(s1);

           setlocale(LC_ALL, "");

           std::wcout << "System::String->std::wstring : " << s2 << std::endl;

          

           getchar();

           return 0;

}

 

STL의 문자열과 변환하기 위해서는 다음의 헤더 파일을 포함해야 합니다.

#include <msclr\marshal_cppstd.h>

 

마샬링하는 방법은 앞에 설명한 C/C++ 문자열 변환과 같습니다.

System::String^ s1 = marshal_as< System::String^ >(s0);

 

주제와 좀 관계 없는 것으로 콘솔창에 유니코드 문자열을 출력하는 방법은 아래와 같습니다.

setlocale(LC_ALL, "");

std::wcout << "System::String->std::wstring : " << s2 << std::endl;

 setlocale로 국가를 설정하고(직접 나라를 지정할 수도 있고, 아니면 위처럼 시스템 설정에 따라가도록 할 수도 있습니다), ‘cout’ 대신 ‘wcout’를 사용합니다.

 

 



관리코드 문자열과 비관리코드 문자열간의 변환에 따른 성능


C++로 만드는 프로그램은 보통 고성능을 원하는 프로그램이므로 보통 C++ 프로그래머는 성능에 민감합니다. 마샬링은 공짜가 아닙니다만 많은 양을 아주 빈번하게 마샬링 하는 것이 아니면 성능에 너무 신경 쓰지 않아도 됩니다. 다만 기본적으로 관리코드의 문자열은 유니코드입니다. 그래서 비관리코드의 문자열이 ANSI 코드라면 유니코드를 사용했을 때 보다 더 많은 시간이 걸립니다(정확한 수치는 잘 모르지만 ANSI가 유니코드보다 3배정도 더 걸린다고도 합니다). 그래서 관리코드와 비관리코드를 같이 사용할 때는 가능한 유니코드를 사용하는 것이 훨씬 좋습니다.

 

 

아직 설명할 것이 많이 남아 있습니다. 다음을 기다려주세요^^

[Step. 05] 관리 코드의 array를 비관리 코드에 포인터로 전달

C++/CLI 2010. 7. 9. 08:30 Posted by 알 수 없는 사용자

아마 C++ 프로그래머가 C++/CLI를 사용할 때 가장 신경 쓰이는 부분이 관리 코드를 어떻게 하면 비관리 코드와 연동하는 방법이라고 생각합니다.

 

그래서 아직 C++/CLI의 델리게이트 등 C++/CLI의 특징을 설명하지 않은 것이 많지만 이런 것은 C#를 공부하면 배울 수 있는 것이므로 급하지 않다고 생각합니다. 그래서 관리 코드와 비 관리 코드의 연동에 대해서 앞으로 몇 차례에 걸쳐서 설명하려고 합니다.

 

이번은 첫 번째로 간단하게 array로 만든 배열을 비관리 코드의 포인터로 어떻게 전달하는지 설명하겠습니다.

 

먼저 코드를 봐 주세요^^

#include <Iostream>

 

using namespace System;

 

void DumpNativeArray( int* pArrNums, int length )

{

    for( int i=0; i<length; i++ )

    {

        std::cout << pArrNums[i] << std::endl;

    }

}

 

int main()

{

array< int >^ ArrNums = gcnew array<int>(3);

ArrNums[0] = 1;

ArrNums[1] = 2;

ArrNums[2] = 3;

 

     pin_ptr<int> pNative = &ArrNums[0];

     DumpNativeArray(pNative,ArrNums->Length);

     pNative = nullptr;

 

getchar();

     return 0;

}

 

위 코드의 핵심은

pin_ptr<int> pNative = &ArrNums[0];

입니다.

 

앞서 설명한 pin_ptr을 사용하였습니다. pin_ptr을 사용하여 관리 힙에 할당된 객체가 이동하지 못하도록 고정합니다. 고정하는 이유는 비관리 코드로 메모리 주소를 넘기기 때문에 관리 힙에서 이동이 되면 안되기 때문입니다.


또 여기서 자세히 봐야 되는 것이 있습니다.

pin_ptr<int> pNative = &ArrNums[0];


pNative&ArrNums이 아닌 &ArrNums[0]을 대입하였습니다. 이유는 관리 코드에서는 &ArrNums ArrNums의 요소가 아닌 ArrNums 오브젝트 자체를 가리키는 것이기 때문입니다.

&ArrNums[0]을 대입해야 ArrNums에 들어가 있는 요소의 첫 번째 주소를 비관리 코드에 주소를 넘길 수 있습니다.

 



제가 요즘 바빠서 이번은 아주 간단하게 이것으로 끝내겠습니다.^^;

다음에는 문자열 변환에 대해서 설명하겠습니다.

 

 

참조

http://mag.autumn.org/Content.modf?id=20050507224044

 

 

지난 아티클에서 Visual Studio 2008 과 Visual Studio 2010 을 동일한 소스 코드와 프로젝트로 개발하기 위한 환경을 구성하는 방법을 알아보았습니다.

문제 원인

하지만 지난 시간에 언급한 듯이 테스트 프로젝트가 포함된 경우는 VS2008 과 VS2010 을 동시에 사용할 수 없는 문제가 발생합니다. 그 이유는 Microsoft.VisualStudio.Quality 프레임워크가 개선이 되고, Microsoft.VisualStudio.TestTools 프레임워크가 도입되면서 기존의 테스트 프레임워크와 비호환적인 부분이 존재하게 됩니다.

아래와 같이 VS2008 에서 작업한 테스트 프로젝트가 있을 경우,

지난 아티클의 방법으로 프로젝트를 변환하게 되면 아래의 오류가 발생합니다.

기존 프로젝트는 기존의 .NET Framework 버전을 그대로 사용할 수 있지만, 테스트 프로젝트는 반드시 .NET Framework 4.0 으로만 사용할 수 있습니다.

만약 하위 프레임워크 버전인 .NET Framework 3.5 로 변경하고자 할 경우 아래와 같은 오류 메시지가 나타나고, 다시 .NET Framework 4.0 버전으로 변경이 됩니다.

일부 이미 완성된 테스트 프로젝트인 경우 이것보다 더 다양한 오류 메시지를 볼 수 있습니다.^^;

어쨌든, Microsoft.VisualStudio.QualiltyTools 프레임워크가 .NET Framework 4.0 버전으로 고정되어 자칫 테스트 프로젝트가 굉장히 큰 우범을 저지를 수 있는 문제가 될 수 있습니다.

반대로 Visual Studio 2010 에서 만든 테스트 프로젝트는 .NET Framework 4.0 이 기본이고 Microsoft.VisualStudio.QualityTools 프레임워크와 Microsoft.VisualStudio.TestTools 프레임워크가 기본 참조(RTM 에서는 제외 됨)이며, 이 프레임워크의 버전도 4.0.xxxxx 버전이라 하위 버전과 호환되지 않습니다.

VS2010 이든 VS2008 이든 테스트 프로젝트가 한번 VS2010 으로 업그레이드 되었다면, VS2008 에서 테스트 프로젝트를 사용하기 위해서는 다운그레이드는 그리 쉽지 만을 않습니다. 왜냐하면 VS2008 에서 테스트 프로젝트를 로드하면 프로젝트의 참조가 깨져있습니다.

그 이유는 .csproj 파일을 열어보면 답이 나오는데요. 아예 Microsoft.VisualStudio.QualityTools 프레임워크의 버전 번호를 명시하여 해당 VS2008 에서는 어셈블리 리디렉션(Assembly Redirection) 을 시키기가 좀 애매해 집니다.

   

문제 해결 기본 지식

이 문제를 해결하기 위해서는 테스트 프로젝트간의 비 호환적인 테스트 프레임워크로 인한 문제이므로, 문제를 해결하기 위한 접근 측면에 제한을 둘 수 밖에 없습니다. Visual Studio 2010 에서는 Coded UI, Test Impact 등 새로운 기능이 추가되었고, 기존 테스트 또한 비주얼한 부분이 개선이 되면서 강제적으로 테스트 프레임워크의 버전을 .NET Framework 4 로 고정을 시키는 것 같습니다.

이 문제는 MSBuild 를 통해 해결하기 위한 기본적인 지식을 알려드립니다. 여러분이 알다시피 MSBuild 는 Microsoft 의 통합 빌드 솔루션입니다. 예전에는 "빌드"라는 말 대신 "컴파일"이라는 단어를 사용했었죠. 컴파일이란 소스 코드를 목적 파일 또는 실행 파일로 변환하는 과정을 "컴파일"이라고 합니다.

컴파일과 빌드를 비교하는 아주 간단한 그림 입니다.
컴파일은 목적은 소스 코드를 목적 파일로 변환하여 실행 파일 또는 라이브러리로 만들기 위한 목적입니다.

빌드는 컴파일의 일련의 과정을 플로우(Flow) 로 처리하여 컴파일 중에 더 많은 작업을 하기 위한 목적입니다.
가장 대표적인 빌드 솔루션이 MSBuild 이며, 이 외에 Ant 또는 NAnt 등이 바로 이러한 솔루션입니다. 그리고 Team Foundation Server 의 팀 빌드도 바로 MSBuild 에 기반하고 있다는 것입니다.

필자 또한 MSBuild 를 접하면서 나의 지식의 끝을 무한하게 확장해 주었던 것이 MSBuild 입니다. MSBuild 는 정적인 컴파일 방식에서 동적인 방식의 빌드로 거듭나면서 굉장히 많은 가능성을 보여주는 부분이기도 합니다. Microsoft 의 MSBuild 의 대략적인 구조는 아래와 같습니다.

기본적으로 MSBuild 는 Task 의 집합이라고 해도 과언이 아닙니다. 그리고 이 Task 중에 빌드와 연관된 Task 도 있습니다. 이 Task 를 .NET Framework 버전에 따라 Project References(프로젝트 참조)를 변형시키는 방법입니다.

 

해결 방법

이 테스트 프로젝트를 VS2008, VS2010 양 쪽에서 사용하도록 하기 위해서는 이 어셈블리 참조를 동적으로 변화시킬 필요가 있습니다. 이 방법도 MSBuild 의 Choose 라는 조건문으로 제어를 분기할 수 있는 방법입니다.

1. 먼저 솔루션 탐색기에서 열려 있는 프로젝트를 언로드 한 후, 편집을 클릭합니다.

2. 그럼 아래와 같이 참조와 관련되어 있는 부분이 ItemGroup 요소에 있는 것을 확인할 수 있습니다.

3. 이 ItemGroup 에서 VS2008, VS2010 에서 공통적인 참조 어셈블리를 별도의 ItemGroup 으로 분리합니다. 그럼 아래와 같은 형태가 되겠지요?

4. 테스트와 관련된 ItemGroup 에 Choose 조건 분기 요소를 사용하여 조금 변형해 봅시다. .NET Framework 의 버전 별로 말이죠.

위의 $(MSBuildBinPath) 는 실제로 빌드가 수행할 때의 MSBuild 의 경로를 나타냅니다. 하지만 여기에는 한 가지 함정이 있습니다. Visual Studio 2008 에서는 <Message Text="$(MSBuildBinPath)" /> 가 아래와 같이 C:\Windows\Microsoft.NET\Framework\v2.0.50727 로 나타납니다. 하지만 내부적으로 이 MSBuild 는 v3.5 경로의 MSBuild.exe 를 실행하게 됩니다. 자세한 이유와 내막은 Microsoft.Common.targets 파일을 뒤져보시면 아실거라고 생각합니다.

그리고 Choose 조건 분기 요소는 if ~ else 와 같은 구문입니다. ItemGroup 요소는 하나의 항목을 담는 필드라고 보시면 되고, PropertyGroup 은 한 Property 에 여러 항목을 담는 속성이라고 보시면 됩니다. 이 부분은 MSBuild 를 공부해 보시면 어렵지 않는 기본적인 부분이니 자세한 설명은 여기에서 하지 않겠습니다.

5. 모두 완료 되었습니다. 각각의 VS2008, VS2010 에서 테스트 프로젝트를 모두 사용할 수 있게 되었습니다.

만약 Coded UI 와 같은 VS2010 의 새로운 기능을 사용할 경우 아래와 같이 추가적인 어셈블리를 참조하게 됩니다.

이 경우도 위의 4번과 같이 ItemGroup 의 VS2010 용 어셈블리를 아래와 같이 넣어버리면 됩니다.

그럼 VS2008 인지 VS2010 인지에 따라서 참조 어셈블리가 완벽하게 분리가 됩니다.

하지만 VS2008 에서 빌드를 할 경우 아래와 같이 오류가 발생하게 됩니다. 당연히 VS2008 에서는 Coded UI 등에서 필요한 Microsoft.VisualStudio.TestTools 프레임워크가 존재하지 않고, 이 프레임워크를 재사용하기 힘들기 때문입니다.

하지만 이 문제로 해결해 볼까요? 위에서 Property 를 재정의한 구문이 생각나실 겁니다. 전처리 지시문의 상수 값으로 사용되는 <DefineConstants> 에 NET4.0 빌드인지, NET3.5 빌드인지 알 수 있도록 상수 값을 선언하였습니다.

이 상수 값을 이용하여 CodedUI 등 VS2010 에서 새로 추가된 부분에, #if ~ #endif 지시문을 사용하여 감싸 주시면 됩니다.

   

이제 Visual Studio 2008 이든 Visual Studio 2010 이든 테스트 프로젝트를 양 쪽 어떤 도구를 사용하든 테스트가 가능하도록 구성하는 방법을 완료하였습니다.

[Step. 04] nullptr, interior_ptr, pin_ptr

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

nullptr

 

C/C++에서 포인터를 초기화 할 때 ‘NULL’을 사용합니다. 그러나 VC++ 10에는 C++0x에서는 포인터를 초기화 할 때 NULL 대신 새로 생긴 ‘nullptr’을 사용할 수 있게 되었습니다.


C++/CLI는 이전부터 nullptr이 있었습니다.

C++/CLI에서는 ref 클래스의 핸들을 초기화 할 때는 nullptr을 사용합니다.

C++/CLI, C++0x nullptr C/C++ 처럼 ‘0’이 아니라는 것을 잘 기억하시기 바랍니다.

 

 

 

interior_ptr

 

interior_ptr은 관리 힙(managed heap. GC겠죠) 상의 value type나 기본형을 가리키는 포인터라고 할 수 있습니다. interior_ptrvalue type나 기본형을 비관리 코드의 포인터처럼 사용하고 싶을 때 사용하면 좋습니다.

 

< 코드 1. >

ref class REFClass

{

public:

    int nValue;

};

 

void SetValue( int* nValue )

{

    *nValue = 100;

}

 

 

int main()

{

    REFClass^ refClass = gcnew REFClass;

    SetValue( &refClass->nValue );  // 에러

}

 

위 코드를 빌드 해 보면 SetValue( &refClass->nValue ); 에서 빌드 에러가 발생합니다. 매니지드 힙에 있는 것은 그 위치가 변하므로 비 관리 코드의 포인터를 넘길 수가 없습니다. 그럼 <코드 1>를 정상적으로 빌드 하기 위해서 interior_ptr를 사용해 보겠습니다.

 

< 코드 2. >

ref class REFClass

{

public:

    int nValue;

};

 

void SetValue( interior_ptr<int> nValue )

{

    *nValue = 100;

}

 

 

int main()

{

    REFClass^ refClass = gcnew REFClass;

    SetValue( &refClass->nValue );

}

 


<코드 2> SetValue의 파라미터로 비관리 코드의 참조나 포인터를 넘길 수도 있습니다.

< 코드 3. >

#include <iostream>

 

 

void SetValue( interior_ptr<int> nValue )

{

    *nValue = 100;

}

 

int main()

{

           int nValue = 50;

           SetValue( &nValue );

 

           std::cout << nValue << std::endl;

 

           getchar();

           return 0;

}

 

그리고 interior_ptr에 대신 C++/CLI의 참조(‘%’)를 사용하는 방법도 있습니다.

 

 

 

 

pin_ptr

 

pin_ptr은 관리 힙 상의 value type나 기본형을 비관리 코드에서 포인터로 사용하고 싶을 때 사용하는 기능입니다. 가장 필요한 경우가 C++/CLI에서 기존의 비관리 코드로 만들어 놓은 라이브러리를 사용할 때입니다.

 

< 코드 4. >

ref class REFClass

{

public:

    int nValue;

};

 

void SetValue( int* pValue )

{

    *pValue = 100;

}

 

 

int main()

{

    REFClass^ refClass = gcnew REFClass;

pin_ptr<int> pValue = &refClass->nValue;

    SetValue( pValue );

pValue = nullptr;

}

 

pin_ptr에 메모리 주소를 할당하는 것을 ‘pin’이라고 부르고 사용이 끝난 후 nullptr로 초기화 하는 것을 ‘unpin’ 이라고 부릅니다. pin_ptr 사용이 끝난 후 가능한 빨리 unpin 해주는 것이 좋습니다.

 

 

 

 

interior_ptr pin_ptr의 차이점

 

interipor_ptr pin_ptr은 둘 다 관리 힙 상의 value type이나 기본형을 가리키는 포인터로 사용되지만 interior_ptr은 관리 힙 상에서 인스턴스가 이동하여도 올바르게 추적할 수 있는 포인터로 런타임의 지배하에 있습니다(즉 인스턴스가 관리 힙 상에서 이동하여도 괜찮습니다).


pin_ptr은 관리 힙 상의 value type을 비관리 코드에서 사용하고 싶을 때 사용합니다. 당연히 이 때는 관리 힙에 있는 인스턴스가 이동하면 안되므로 인스턴스의 이동을 금지합니다.

 

interipor_ptr pin_ptr의 같은 점 : 포인터처럼 사용할 수 있다.

interipor_ptr pin_ptr 다른 점 : interipor_ptr은 관리 코드 상에서 포인터로 사용하고, pin_ptr는 비관리 코드에 포인터로 넘길 때 사용합니다.

 

 

interipor_ptr pin_ptr을 공부했으니 다음에는 C++/CLI에서 비관리 C++과 혼합해서 사용할 때 어떻게 해야 하는지 설명하겠습니다.

 

 

 

 

참고

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

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

http://cppcli.shacknet.nu/cli:interior_ptr%E3%81%A8pin_ptr%E3%81%AE%E9%81%95%E3%81%84

 

6월 1일 REMIX10 행사를 기점으로 Visual Studio 2010 한글판이 대중에 공개가 되었습니다. Visual Studio 2010 의 영문 버전은 그 이전에 출시가 되었지만 한글판이 출시된 이후에 더 많은 관심을 받게 되었습니다.

Visual Studio 2010 으로 개발 환경을 업그레이드를 진행하는 곳이 특히 해외에서 많습니다. 제가 그걸 어떻게 다 아냐구요? 항상 트위터 검색을 통해 해외에서 Visual Studio 2010 를 어떻게 사용하고 있는지 매일 매일 관심 있게 보고 있답니다. ^^

어쨌든 Visual Studio 2008 을 쓰고 있지만, Microsoft MVP 이거나 회사에서 MSDN Subscription 라이선스가 있다면 Visual Studio 2008, Visual Studio 2010 개발 도구가 혼합해서 사용될 경우가 있습니다. 이런 경우 두 개발 도구에서 쌍방 개발 가능하게 구성을 할 수 있습니다.

이 방법은 제니퍼소프트의 정성태 과장님의 블로그에서 예전에 소개했던 VS2005, VS2008 혼합해서 사용하는 방법과 동일합니다.

1. 간단한 예제로 Console Application 을 Visual Studio 2008 에서 생성했습니다.

   

2. 기존의 솔루션 파일의 복사본을 하나 만듭니다.

   

3. 솔루션 파일을 노트패드로 ConsoleApplication - VS2010.sln 파일을 열어 다음의 항목을 수정합니다.

   

4. 프로젝트 파일을 열어 ToolsVersion 의 '3.5' 를 '4.0' 으로 수정합니다. 'ConsoleApplication1.csproj'

  

만약 다수의 프로젝트일 경우, 위의 3번에서 수정한 솔루션 파일을 열면 프로젝트를 변환하는 마법사로 진행하면 쉽게 변경이 됩니다.

   

5. 모두 완료 되었습니다. Visual Studio 2010 와 Visual Studio 2008 에서 각각의 솔루션 파일로 동시에 작업을 할 수 있습니다.

   

위의 방법을 이용하여 Visual Studio 2008 과 Visual Studio 2010 에도 모두 개발이 가능합니다. 하지만 만약 테스트 프로젝트가 포함이 되어 있다면 두 개발 도구에서 사용할 수 없습니다. 왜냐하면 .NET Framework 4.0 에서는 테스트와 관련된 Microsoft.VisualStudio.Quality 프레임워크가 개선되고, Microsoft.VisualStudio.TestTools 프레임워크가 추가되면서 이전 Visual Studio 2008 과 프로젝트가 호환이 되지 않습니다.    

하지만 불가능할 것 같은 테스트 프로젝트도 Visual Studio 2008과 Visual Studio 2010에서 동시에 사용할 수 있는 방법이 있습니다. 이것은 다음에 알아보도록 하겠습니다.

[step.03] 배열

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

프로그래밍 할 때 가장 자주 사용하는 자료구조가 바로 배열입니다. 배열을 사용하지 않고 프로그래밍 하기는 힘들죠^^.

그래서 이번에는 C++/CLI에서의 배열에 대해서 이야기하려고 합니다.

 

 

C++/CLI에서의 배열은 ‘array’

 

비관리 C++에서는 배열은 ‘[]’을 사용합니다.

int Nums[10];

char szName[20] = {0,};

 

그러나 C++/CLI에서의 배열은 ‘array’라는 클래스를 사용합니다.

 

int 형의 3개의 요소를 가지는 배열은 아래와 같이 정의합니다.

array< int >^ A1 = gcnew array< int >(3);

array< int >^ A2 = gcnew array< int >(4) { 1, 2, 3 };

array< int >^ A3 = gcnew array< int >{ 1, 2, 3 };

 

다음은 간단한 사용 예입니다.

< 코드 1. >

int main()

{

           array< int >^ Numbers = gcnew array< int >(5);

                    

           for( int i = 0; i < 5; ++i )

           {

                     Numbers[ i ] = i;

 

                     System::Console::WriteLine( Numbers[i] );

           }

 

           getchar();

           return 0;

}

 

 

 

array에 유저 정의형 사용하기

 

array에는 기본형(int, float )만이 아닌 유저 정의형도 사용할 수 있습니다. 다만 비관리 클래스는 안됩니다. 오직 관리 클래스(ref class)만 가능합니다. 또 그냥 ref 클래스를 그대로 넣을 수는 없는 클래스의 핸들을 사용해야 합니다(ref 클래스는 GC에 동적 할당을 하기 때문이겠죠).

 

ref class refTest

{

};

 

array< refTest >^ arrTest;    // 에러

array< refTest^ >^ arrTest;   // OK

 

 

 

 

for each 사용하기

 

앞서 <코드1>의 예제에서는 배열의 모든 요소를 순환하기 하기 위해 ‘for’문을 사용했습니다. 그러나 .NET에서는 for문 보다 ‘for each’문을 사용하는 것이 성능이나 안정성 등에서 더 좋습니다(다만 for each를 사용하면 내부에서 값을 변경할 수 없다는 문제는 있습니다).

 

< 코드 2. >

#include <iostream>

 

 

int main()

{

           array< int >^ Numbers = gcnew array< int > { 10, 11, 12, 13, 14 };

                    

           for each( int nValue in Numbers )

           {

                     System::Console::WriteLine( nValue );

           }

 

           getchar();

           return 0;

}

 

 

 

다 차원 배열

 

array는 너무 당연하게 다 차원 배열도 만들 수 있습니다.

 

< 코드 3. 2차원 배열의 예 >

int main()

{

           array<int,2>^ a2 = gcnew array<int,2>(4,4);

           array<int,2>^ b2 = gcnew array<int,2>{ {1,2,3,4},{1,2,3,4},{1,2,3,4},{1,2,3,4} };

 

           getchar();

           return 0;

}

 


 

 


array의 기능은 .NET 라이브러리의 array 클래스를 사용하므로 자세한 사용방법은 MSDN에서 .NET 라이브러리 부분을 참고 하시가 바랍니다.

 

 

다음에는 nullptr, interior_ptr, pin_ptr 설명과 관리코드의 타입을 비관리 코드로 넘길 때 어떤 작업을 하는지 이야기 하겠습니다.^^

 

 

[MFC/윈도우 7 멀티터치] #3 : 제스처(gesture)를 이용한 구현(下)

MFC 2010. 6. 16. 09:00 Posted by 알 수 없는 사용자

Intro

안녕하세요~ MFC 카테고리의 꽃집총각 입니다.

이번 포스팅에서는 앞 글에서 이야기 중이었던 예제 프로그램의 나머지 부분을 마저 작성하고, MFC 기반의 프로젝트에서 제스처를 이용해 멀티터치 프로그래밍을 제어하는 방법에 대한 보충 설명을 정리해 보도록 하겠습니다. 앞에 적은 글과 이어지는 내용이니까, 앞부분을 보지 못하신 분들은 ( http://vsts2010.net/292 ) 이 글을 먼저 읽어주세요 ^^ 지난번 글에서는 멀티터치 동작을 확인하기 위한 Drawing 처리까지 함께 알아봤습니다. 앞부분에서 진행됐던 내용을 소제목만 간추려서 알아보면,

* 제스처를 인식하는 첫 번째 예제 프로그램 *

  1. MFC Application 프로젝트를 만들자
  2. 하드웨어 상태를 알아봅시다
  3. View 영역에 사각형을 그리자!

…와 같은 내용이었습니다. 두 번째 스텝에서 알아봤던 하드웨어의 멀티터치 인식 가능여부 확인방법을 제외하고는 멀티터치와 직접적으로 연관된 내용은 많이 없었군요. 이번 시간부터는 본격적으로 터치 입력을 제어하는 코드를 함께 알아보겠습니다.

 

Task 4 : 터치 인식 코드를 넣을 차례!

자, 이제 드디어 제스처를 제어하는 코드를 알아볼 시간입니다. 사용자 경험(UX;User Experience) 분야에서 가장 뜨거운 이슈인 멀티터치! 뭔가 색다르고 놀라운 구현방식이 있을 거 같지만 실망(?)스럽게도 구현방법은 기존의 MFC 프로그래밍과 별다른 바가 없습니다. 그저 적당한 함수를 오버라이딩 해서 필요한 코드를 넣어주면 그만이거든요. 바로 이 점이 새롭게 변화된 MFC 추가기능들이 마음에 드는 이유 중에 하나입니다. 바로 ‘친숙한 인터페이스’ 말입니다 :)

MFC의 윈도우 랩핑 클래스인 CWnd에는 이번에 새롭게 추가된 제스처 관련 함수들이 있습니다. 이 중에서 아래의 다섯 가지 제스처 핸들링 함수를 오버라이딩 합니다. 각각의 함수들은 이름에 명시되어 있는 제스처가 입력됐을 때 호출됩니다. 아래의 코드를 ChildView.h 파일의 class CChildView 선언에 넣어줍니다.

// Overrides
protected:
    // Gesture handlers
    virtual BOOL OnGestureZoom(CPoint ptCenter, long lDelta);
    virtual BOOL OnGesturePan(CPoint ptFrom, CPoint ptTo);
    virtual BOOL OnGestureRotate(CPoint ptCenter, double dblAngle);
    virtual BOOL OnGesturePressAndTap(CPoint ptFirstFinger, long lDelta);
    virtual BOOL OnGestureTwoFingerTap(CPoint ptCenter);

기본적으로 터치가 인식되면 애플리케이션으로 WM_GESTURE 메세지가 날아옵니다. Win32 API만을 이용해 제스처를 제어하려면 WndProc 프로시저 함수에서 WM_GESTURE를 잡아서 처리해야 하지만, MFC 애플리케이션의 경우 자체적으로 각각의 제스처에 대한 전용 핸들링 함수들이 나뉘어져 있기 때문에, 제어가 필요한 제스처에 해당하는 함수를 용도에 맞게 따로따로 오버라이딩 해서 사용하면 됩니다.

Note : 그런데 CWnd의 선언을 살펴보면 (afxwin.h 파일에 있습니다.) 위에 나열된 함수 말고도 OnGesture 함수가 정의되어 있는 것을 확인하실 수 있습니다. 함수의 원형은 아래와 같습니다.

afx_msg LRESULT OnGesture(WPARAM wParam, LPARAM lParam);

오호라~ 이것은 WM_GESTURE 메세지가 넘어올 때마다 해당 메세지를 직접 제어할 수 있는 함수인가 보군요~ 함수의 인자로 wParam, lParam이 모두 날아오니 win32 프로젝트에서 하는 것처럼 코딩하고 싶을 땐 이걸 상속받아서 작업하면 되겠구나 ~ … 라고 생각하고, 실제로 테스트 코드도 만들어 봤었지만 안되더군요 @.@… 똑똑한 여러분들은 이유가 무엇인지 바로 찾으셨을 거라고 생각합니다만… 저 함수는 가상 함수가 아닙니다 ^^;… 그저 이름만 보고 상속받아 써야지 했는데, 나중에 보니 재정의 할 수 없는 일반 멤버함수더라고요 ㅎㅎ 아마도 CWnd가 자체적으로 처리하는 코드를 구현한 부분이 아닌가 생각됩니다. 일반적으로는 각각의 제스처마다 독립적으로 호출되는 위의 다섯 가지 함수를 이용하면 되고요, 경우에 따라 부득이하게 WM_GESTURE 메세지를 직접 제어하고 싶을 때엔… 이전에 그랬던 것처럼 WndProc을 직접 제어하도록 하고 그곳에서 WM_GESTURE를 받은 경우에 대한 switch-case 문을 넣어주면 되겠죠 ^^

이제 위에 소개된 다섯 개의 함수들에서 view 영역에 그려지고 있는 사각형을 제어하는 코드들을 넣어줍니다. 아래에 함수들의 본문 코드가 있습니다.

BOOL CChildView::OnGesturePan(CPoint ptFrom, CPoint ptTo)
{
    int dx = ptTo.x - ptFrom.x;
    int dy = ptTo.y - ptFrom.y;
    
    if (dx != 0 || dy != 0)
    {
        m_drawingObject.Move(dx, dy);
        RedrawWindow();
    }
    return TRUE;
}
 
BOOL CChildView::OnGestureZoom(CPoint ptCenter, long lDelta)
{
    if ((m_pCurrentGestureInfo->dwFlags & GF_BEGIN) == GF_BEGIN)
    {
        m_dblZoomRatioStart = m_dblZoomRatioTotal = lDelta;
    }
    else if (lDelta != 0)
    {
        m_dblZoomRatioTotal += lDelta;
        double zoomFactor = (double)m_dblZoomRatioTotal / m_dblZoomRatioStart;
        
        m_drawingObject.Zoom(zoomFactor, ptCenter.x, ptCenter.y);
        
        m_dblZoomRatioStart = m_dblZoomRatioTotal;
        RedrawWindow();
    }
    return TRUE;
}
 
BOOL CChildView::OnGestureRotate(CPoint ptCenter, double dblAngle)
{
    if ((m_pCurrentGestureInfo->dwFlags & GF_BEGIN) == GF_BEGIN)
    {
        // Make the first center, the rotating one
        m_ptCenter = ptCenter;
    }
    else if (dblAngle != 0.)
    {
        m_drawingObject.Rotate(dblAngle * PI / 100.0, m_ptCenter.x, m_ptCenter.y);
        RedrawWindow();
    }
    
    return TRUE;
}
 
BOOL CChildView::OnGesturePressAndTap(CPoint ptFirstFinger, long lDelta)
{
    if ((m_pCurrentGestureInfo->dwFlags & GF_BEGIN) != 0)
    {
        m_drawingObject.ShiftColor();
        RedrawWindow();
    }
    
    return TRUE;
}
 
BOOL CChildView::OnGestureTwoFingerTap(CPoint ptCenter)
{
    m_drawingObject.TogleDrawDiagonals();
    RedrawWindow();
    
    return TRUE;
}
 

함수의 이름과 전달되는 인자들 모두 직관적입니다. 함수 본문의 예제 코드들도 그리 어렵지 않군요. 위의 코드들을 복사해 적어준 뒤 빌드하고 프로그램을 실행해 봅니다. 애플리케이션이 뜨면 사각형을 손으로 움직여 보세요. 터치 입력에 따라 사각형은 이동하고, 늘어나고, 회전할겁니다 :)

네…? 근데 뭔가가 안 된다고요?

 

Task 5 : 다 되는 거 같은데 회전만 안되네요 ㅡㅜ…

여기까지 진행하고 테스트를 해보면 다른 제스처는 다 인식을 하는데, 회전(rotate)만 제대로 안 되는 현상을 겪으실 겁니다. 참고로 이번 예제에서 확인하실 수 있는 다섯 가지 제스처에 대한 동작을 간략히 설명 드리면 아래와 같습니다.

panning 손가락을 스크린에 대고 이동한다.
zoom 두 손가락을 스크린에 대고 벌리거나 모은다.
rotate 두 손가락을 스크린에 대고 회전시킨다.
press and tab 한 손가락을 스크린에 댄 상태에서 다른 손가락으로 스크린을 빠르게 터치한다.
two finger tab 두 손가락을 동시에 스크린에 붙였다 뗀다.

그런데 다른 건 다 잘되는데 아마 rotate 제스처만 동작하지 않을 거예요. 그리고 다른 제스처들은 다 인식할 테지만… 사각형 내부에서 일어난 제스처인지를 판단하는 처리가 없었기 때문에, 꼭 사각형 내부가 아니더라도 client 영역 내에서 발생한 유효한 제스처라면 모두 다 인식하는 걸 확인할 수 있으실 겁니다.

그 이유는 코드가 잘못된 것이 아니라, 윈도우 자체에서 다른 제스처들은 모두 기본적으로 활성화 되어있는데, rotate만은 기본적으로 비활성화 되어있기 때문입니다. 활성화 하려면 다 해두든가 아님 말든가 할 것이지 왜 rotate만 천대(?)하는지 자세한 내막은 모르겠습니다만… 그것이 현실이군요. rotate를 활성화하기 위해, 제스처의 설정(config)를 제어하는 방법을 알아보도록 합시다.

MFC에서는 제스처의 설정을 쉽게 컨트롤 할 수 있도록 CGestureConfig라는 클래스를 제공합니다. CChildView의 선언( in ChildView.h )에 CGestureConfig 타입의 멤버변수를 하나 추가해줍니다.

class CChildView 
{
    // ...
 
// Fields
protected:
    // Holds gesture configuration
    CGestureConfig m_gestureConfig;
    
    // ...
}

그리고 새롭게 부활한 반가운 인터페이스, 마법사 중의 마법사 MFC Class Wizard를 띄워서 OnCreate 함수를 오버라이딩 합니다. (단축키 Ctrl + Shift + X 입니다 ^^)

그리고 아래의 코드를 넣어주세요.

 
int CChildView::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
    if (CWnd::OnCreate(lpCreateStruct) == -1)
        return -1;
 
    // TODO: Add your specialized creation code here
    GetGestureConfig(&m_gestureConfig);
    
    // Only rotation is not enabled by default
    m_gestureConfig.EnableRotate();
    
    SetGestureConfig(&m_gestureConfig);
 
    return 0;
}
 
 
 
그리고 나서 다시 한번 빌드하고 실행해 봅니다. 이제 rotate 제스처도 제대로 인식하는 첫 번째 예제 프로그램이 완성 되었습니다 ^^*
 
자 드디어 첫 번째 예제의 완성입니다~ !! 짝짝짝 ~
 

Task 6 : panning이 왜 이렇게 뻑뻑하지? 아이폰은 안 그런뎅..

 
task5 까지가 channel9에서 소개한 MFC gesture 기능의 첫 번째 예제입니다. 필요한 부분과 불필요한 부분을 적절하게 구분해 잘 설명해 두어서, 한번 설명을 따라 작성해 보시는 것만으로 제스처 프로그래밍에 대한 대략적인 흐름을 손쉽게 파악하셨을 거라고 생각합니다.
 
이대로 끝내기엔 좀 아쉬운 감이 있어서… 예제 코드에 대한 추가 설명을 드리고자 합니다. 사실 글 중간중간에도 제가 추가로 적은 글이 제법 있었지만… 마땅히 말씀드릴 타이밍을 못 찾았던 요것 한 가지만 더 말씀 드리도록 할게요 ㅎㅎ
 
일단 예제를 조작해 보면 제일 먼저 거슬리는 부분이 바로 panning 제스처에 대한 조작감(?) – 게임개발만 오래 하다 보니 이런 표현이…^^; – 인데요. 아이폰에서 느껴지는 부드러운 반응성에 비해 사각형 움직임이 무척이나 둔하고 불만족스럽다는 걸 느끼실 겁니다. 사각형이 한 번 움직이고 나면 괜찮은데, panning 제스처를 인식해 움직여지기 시작할 때 까지가 뭔가 걸리는 느낌입니다. x, y 한 방향으로만 움직이고, 다른 방향으로는 안 움직이는 경우도 아마 겪으셨을 거예요. 왜 이럴까요? 기기의 한계? 아님 OS 자체의 한계?
 
이런 현상은 하드웨어의 문제나 OS 기능 자체의 문제가 아닙니다. 이것도 역시 panning 제스처의 옵션에서 GC_PAN_WITH_GUTTER 플래그가 기본적으로 켜져 있기 때문에 나타나는 현상입니다. 저도 처음에 제스처(gesture)를 이용한 멀티터치 예제들을 실행해 보다가 panning의 무딘 반응성이 너무 눈에 거슬려서, 부드러운 움직임을 보려면 결국 WM_GESTURE에서는 한계가 있고, WM_TOUCH를 써야 하는가 보다 했었는데, 간단히 플래그만 조절해주면 좀 더 부드러운 움직임을 느낄 수가 있게 됩니다.

GC_PAN_WITH_GUTTER에서 gutter는 홈통이나 배수로 등의 뜻으로, panning 제스처가 발생했을 때 가장 메인이 되는 방향으로의 움직임 이외의 방향성에 대한 반응은 특정 임계값을 넘기지 않는 이상 무시하게 하는 효과를 줍니다. 이 때문에 손가락에서의 미세한 움직임들이 무시되고, 반응성이 안좋다는 느낌을 받게 되죠. 이를 해결하기 위해서는 rotate 제스처의 활성화를 위해 오버라이딩 했었던 OnCreate 함수에서 panning에 대한 아래의 설정도 함께 처리해 주면 됩니다.

 
config.EnablePan( TRUE, 
    GC_PAN_WITH_SINGLE_FINGER_VERTICALLY | 
    GC_PAN_WITH_SINGLE_FINGER_HORIZONTALLY |
    GC_PAN_WITH_INERTIA );
 
pan 제스처를 활성화 하되, GC_PAN_WITH_GUTTER 플래그만을 제외한 나머지 플래그들을 활성화 해주는 코드입니다. 위 코드를 넣고 나서 빌드하고 다시 실행해보면 이전보다 훨씬 더 부드럽게 사각형이 이동되는 것을 느낄 수 있으실 겁니다.

 
 

Outro

이것으로 MFC를 이용해서 제스처를 이용한 멀티터치 프로그래밍 방법의 첫 번째 예제를 소개와 설명을 모두 마쳤습니다. 제스처의 설정을 컨트롤 하는 방법, 각각의 제스처에 대한 핸들링 코드를 넣어주는 방법 등을 알아보았는데, 대부분 예전 MFC 프로그래밍의 마우스/키보드 이벤트 처리 등과 비슷한 방식이었기 때문에 그리 어렵지는 않은 난이도였습니다.

다음 포스팅 에서는 이제 가장 자유롭고 확장성 있는 (… 하지만 대신 좀 더 까다로운 ) WM_TOUCH를 이용한 멀티터치 프로그래밍 방법을 알아 보도록 하겠습니다. 혹시 그 전에 질문 사항들이나, 제스처 프로그래밍 방법들에 대한 추가 학습사항이 있다면 다시 한 번 정리하는 기회를 갖도록 하겠습니다.

그럼 다음 포스팅에서 다시 인사 드리도록 하겠습니다. 그 때까지 더운 날씨에 모두들 건강하시고 공부 열심히 하세요 ~ ^^*

감사합니다 ~ 꾸벅 ~

Reference

[JumpToDX11-13] Tessellation 등장.

DirectX 11 2010. 6. 15. 09:00 Posted by 알 수 없는 사용자


요즘 vs2010 에 아티클이 많아져서 너무 좋습니다..^^


< Tessellation 개념 잡기 >

지금부터 언급할 내용은 Tessellation 입니다.
Tessellation을 간단히 정의하자면, 적은 수의 폴리곤이 그래픽 파이프라인을 통과했을 때,
많은 수의 폴리곤들을 생성해서 최종 화면에서는 훨씬 정밀한 결과를 나타내는 기술
이라고
할 수 있습니다.
간단히 이미지를 보시면 쉽게 개념화 할 수 있을 것입니다.






감동적이신가요?
저는 꽤 큰 감동을 받기는 했는데, 어마어마한 연산량이 걱정이 되었습니다.
물론 여러 분들도 저와 같은 생각일 것이라고 생각합니다.



< Tessellation 의 등장 배경 >

오늘 날의 컴퓨터 그래픽스의 발전은 정말이지 급격하게 변화했습니다.



유명한 파이날 판타지7 과 최신작을 비교해 보았습니다.
그래픽적인 큰 변화가 느껴지시나요?
아마도 느껴지실 것입니다.( 안느껴지시면 곤란합니다..^^ )

저 변화의 중심에 서 있는 기법 혹은 기술은 어떤 것일까요?
다양한 의견이 있을 수 있지만, 개인적인 견해를 전제로 제가 언급하면 텍스쳐링이라고 생각합니다.
오늘 날의 하나의 폴리곤에 적용되는 텍스쳐의 갯수는 하나가 아닙니다.
노말맵이 거의 표준적으로 사용되고 있는 현 세대에서는
각종 라이팅 처리를 위해서 많은 갯수의 텍스쳐가 사용되고 있습니다.
그래서 우리는 현실감 있는 게임을 즐길 수 있습니다.
이러한 발전의 방향은 폴리곤 갯수를 증가시키는 것보다,
텍스쳐링을 활용하는 것이 성능적인 측면에서 더욱 효과적이기 때문입니다.

그러던 과정에서 이제는 GPU의 성능이 급격히 발전하기 시작했습니다.
많은 사람들이 GPU의 활용에 대해서 고민하기 시작했고,
DirectX9 부터 이런 GPU을 활용한 Tessellation 위한 기법들이 공개적으로 소개되기 시작했습니다.
특히나 ATI 쪽에서는 DirectX9 을 위한 Tessllation SDK 를 제공했었습니다.
여담이지만, 엔비디아쪽에서는 자사의 GPGPU 인 CUDA 를 DirectX9 에서 지원했었습니다.
두 회사의 발전 방향이 이때부터 사실 조금씩 차이가 나기 시작했었다고 볼 수 있습니다.



위의 그림은 ATI 사에서 Tessellation의 필요성을 표현하고 있는 그림입니다.
텍스쳐링을 아무리 많이해도, 폴리곤 갯수가 적으면 더 큰 현실감을 느끼는데는 제한이 있다는 정도로 정리할 수 있을 것입니다.
( 그림(c) 에서 몬스터의 부자연스러운 손가락이 보이시죠? )

그래서 조금 더 큰 현실감을 위해서 폴리곤을 증가시키는 방법을 고안하게 되었고,
급기야 이것이 현 DirectX11 의 정식 그래픽 파이프라인 스테이지로 추가되었습니다.
즉, 공부할 것이 훨씬 더 많아졌습니다...T.T


< 왜 Tessellation 인가? >

조금 과장된 표현을 해서, 게임에서 폴리곤을 많이 사용하는 것은 범죄(?) 행위에 해당합니다.
그래픽 카드가 놀라운 속도로 발전을 하고 있지만,
아직도 게임 개발자들은 비디오 메모리의 부족을 호소하고 있습니다.
당연한 얘기지만, 이는 폴리곤 갯수와 퀄리티의 증가에 의한 것입니다.



위의 그림처럼 그래픽 카드는 약간 독특한 성능을 가지고 있습니다.

첫번째로 대역폭입니다.
CPU쪽 대역폭보다 훨씬 크기 때문에, 대량의 데이터를 전송할 수 있습니다.

두번째는 비디오 메모리가 시스템 메모리 보다 훨씬 작다는 것입니다.

세번째는 수치 연산과 병렬연산에 강한 GPU 라는 것입니다.
실제로 Tessellation 파이프라인 스테이지는 병렬적으로 처리됩니다.
( 다음 시간에 이에 대한 언급이 있을 것입니다. )

결과적으로 Tessellation 의 이점은
폴리곤 갯수를 줄임으로써 비디오 메모리 사용량을 감소시킵니다.
이는 결국 적은 데이터 전송으로 인해 대역폭을 절약할 수 있습니다.
하지만, Tessellation 은 GPU 의 성능에 좌우된다고 할 수 있습니다.
연산량이 실제로 많기 때문에, 정말이지 빠른 성능이어야 한다는 것입니다.
다행스러운 것은 GPU 의 성능이 비디오 메모리의 확장보다는 더 빨라지고 있다는 것입니다.

사실 Tessellation 에 대한 가장 큰 의구심은 '과연 빠를까?' 입니다.
이것에 대한 정답은 아직은 없습니다.
적절한 곳에서 사용한다면 유용할 수도 있을 것이고, 그렇지 않을 수도 있을 것입니다.
다만, 현재 DirectX 의 새로운 패러다임으로 Tessellation 이 선택되어졌으며,
좋은 성능을 위해서 꾸준히 노력할 것이라는 것입니다.^^

[Step.02-2] 클래스(class), 핸들(^), 그리고 구조체(struct)

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

 

gcnew로 생성하지 않기

 

C++/CLI는 클래스를 생성할 때 ‘gcnew’를 사용하지 않고 생성할 수도 있습니다.

 

< 리스트 1. ‘gcnew’를 사용하지 않고 클래스 생성하기 >

#include "stdafx.h"

#include <stdio.h>

 

using namespace System;

 

ref class ManagedTest

{

public:

           ManagedTest() { Console::WriteLine(L"New ManagedTest"); }

           ~ManagedTest() { Console::WriteLine(L"delete ManagedTest"); }

          

           void func() { Console::WriteLine(L"Call func() - {0}", nNumber ); }

 

           int nNumber;

};

 

void foo1()

{

           ManagedTest MTest;

           MTest.nNumber = 1;

           MTest.func();

}

 

void foo2()

{

           ManagedTest^ MTest = gcnew ManagedTest();

           MTest->nNumber = 2;

           MTest->func();

}

 

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

{

           foo1();

           foo2();

          

           getchar();

           return 0;

}


< 결과 >


<리스트 1> ManagedTest MTest; 는 비 관리 C++처럼 GC가 아닌 스택 영역에 생성하는 것으로 착각할 수도 있겠지만 전혀 아닙니다. 클래스를 생성하면 언제나 GC 영역에 만들어집니다.

위의 코드는 그냥 ‘gcnew’ 사용을 우리가 생략하고 컴파일러가 대신 써 준다고 생각하시면 됩니다.

 

void foo1()

{

           ManagedTest MTest;

           MTest.nNumber = 1;

           MTest.func();

}

은 컴파일러에 의해서 아래의 코드로 바뀝니다.

void foo1()

{

           ManagedTest^ MTest = gcnew ManagedTest();

           MTest->nNumber = 1;

           MTest->func();

           delete MTest;

}

 

위의 코드를 보시면 아시듯이 gcnew를 사용하지 않고 클래스를 생성하면 우리가 직접 delete를 쓰지 않아도 되는 편리함을 얻을 수 있습니다.

gcnew를 사용하지 않는 것은 C#에서 ‘using’을 간략화 시킨 것으로 생각하면 좋습니다.


< C# using 문 사용 예 >

using (Graphics g = this.CreateGraphics())

{

    g.DrawLine(Pens.Black, new Point(0,0), new Point(3,5));

}

 

 

 

 

value 클래스

 

관리 클래스는 복사 생성자와 대입 연사자를 가지지 못하므로 아래의 코드는 컴파일 에러가 발생합니다.

 

< 리스트 2. >

#include "stdafx.h"

#include <stdio.h>

 

using namespace System;

 

ref class C {

    int i;

};


void func(C c) {}


int main()

{

    C c;

    C d;

    func(c);   // 에러

    d = c;     // 에러

}

 

그러나 클래스를  ‘ref’가 아닌 ‘value’ 클래스로 정의하면 위의 코드는 컴파일 할 수 있습니다.

 

#include "stdafx.h"

#include <stdio.h>

 

using namespace System;

 

value class C {

    int i;

};

void func(C c) {}

int main()

{

    C c;

    C d;

    func(c);   // 에러

    d = c;     // 에러

}

 

value 클래스는 클래스간 복사를 할 수 있는 능력이 있습니다. 이 복사는 비트 단위의 복사로 이른바 ‘memcpy’와 같은 복사입니다.

value 클래스는 복사를 할 수 있으므로 value 클래스의 멤버는 절대 복사 가능한 멤버만 가질 수 있습니다. 그래서 아래와 같은 value 클래스 C는 컴파일 에러가 발생합니다.

 

ref class A

{

  int i;

};

 

value class C

{

  A a;

};

 

 

value 클래스의 특징으로는 ref 클래스가 GC에서 만들어지는 것과 달리 스택에 만들 수 있습니다.

value class C

{

};

 

C c;

 

위 코드에서 C 클래스는 스택에 만들어집니다(물론 gcnew를 사용하면 GC에 만들어집니다)

 

value 클래스의 특징은 좀 더 있는데 위에 설명한 것들과 포함해서 아래와 같이 정리할 수 있습니다.

 


value 클래스의 특징

 

1. 기본 생성자를 가질 수 없다.

2. 복사 생성자를 가질 수 없다.

3. 대입 연산자를 가질 수 없다.

4. 소멸자를 가질 수 없다.

5. finalize를 가질 수 없다.

6. 클래스간 복사를 할 수 있다.

7. 복사 불가능한 것을 멤버로 가질 수 없다(ref 클래스 등).

8. 스택에 생성할 수 있다.

9. 다른 클래스를 계승할 수 없다( interface는 가능하다)

 

 

 


관리 클래스를 파라메터로 넘기기

 

ref class A

{

           int i;

};

 

void foo1( A a )

{

}

 

함수 foo1의 파라미터 정의는 클래스 A value 클래스일 때만 사용할 수 있습니다. 그러므로 아래와 같이 foo1의 파라미터를 정의해야 합니다.

void foo1( A^ a )

{

}

 

 

 

 

그 외….

 

C++/CLI에서 참조는 ‘%’을 사용합니다. 이것은 비 관리의 ‘&’와 구별이 됩니다.

%는 아래와 같은 경우에 유용하게 사용할 수 있습니다.

void foo( A^ a )

{

}

 

A a;

// foo( a );  // 에러

foo( %a ); // 성공


참조는 C#에서는 'ref', VB.NET에서는 'ByRef'와 같다고 생각하시면 됩니다.


// 값을 참조로 넘기는 경우

void valuebyref(int%i)
{
   i=5;
}

// 참조형을 참조로 넘기기
void refbyref(String^%s)
{
   s="newstring";
}

void main()
{
   int i=1;

   valuebyref(i);

   String^s="basestring";
   refbyref(s);
}
( 위 코드는 http://adversaria-june.blogspot.com/2006/08/ccli_26.html 에서 인용했습니다 )

 



구조체


C++/CLI에서의 구조체가 클래스와 다른 점

1. 'value class'와 같습니다. 물론 비 관리코드에서는 기존의 C++과 같다

2. 구조체는 상속 받을 수 없다.

 

 

 

 

[Step 02-1] 클래스(class), 핸들(^), 그리고 구조체(struct)

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

C C++의 큰 차이점의 하나가 바로 C++에만 있는 클래스입니다. 또 클래스는 객체 지향 프로그래밍 언어에서 자주 볼 수 있습니다.

 

C++/CLI에서 관리 클래스는 ‘ref’라는 키워드를 사용하여 만듭니다.

 

비 관리 클래스

class Test

{

};

 

관리 클래스

ref class Test

{

};

 

관리 클래스는 ‘ref’를 제외하고는 외관상으로는 비 관리 클래스와 비슷하지만 관리와 비 관리가 많이 다르듯이 관리 클래스는 비 관리 클래스와 다른점이 있습니다. 그러므로 이것을 잘 파악하고 있어야 합니다.

 

 

 

관리 클래스의 특징

 

1. 정적 할당은 안 된다. 무조건 가비지컬렉션(GC)에 동적으로 생성한다.

 

2. 복사 생성자를 만들 수 없다.

 

3. ‘^’(핸들이라고 부른다)‘gcnew’를 사용하여 클래스를 생성한다. 당근 메모리 해제는 GC에서 관리한다.

 

4. 핸들은 네이티브의 ‘*’(포인터) ‘&’(참조)와 비슷한 것으로 더 안정스럽다.

 

5. ‘delete’는 명시적으로 클래스에서 사용하고 있는 리소스를 해제할 때 사용하는 것으로 소멸자가 호출 되는 것이지 메모리에서 해제하는 것은 아니다. delete GC에 있는 메모리를 해제하는 것은 아니다.

 

6. delete로 소멸자를 호출하면 GC에 의해서 진짜 파괴될 때 소멸자는 호출되지 않는다.

(소멸자를 선언한 클래스는 자동적으로 IDisposable 인터페이스를 구현한다. 소멸자는 컴파일러에 의해서 Dispose() 메소드로 치환된다).

 

7. 소멸자 이외에 'finalize'를 선언할 수 있다. finalize GC에서 인스턴스가 파괴될 때 호출된다. delete로 소멸자를 호출한 경우에는 finalize는 호출되지 않는다.

 

8. finalize‘!’ 키워드를 사용한다.

 

 

 

관리 클래스 사용해 보기

 

< 코드 1. 관리 클래스 정의 및 사용 >

#include "stdafx.h"

 

using namespace System;

 

ref class ManagedTest

{

public:

           ManagedTest() { Console::WriteLine(L"New ManagedTest"); }

           ~ManagedTest() { Console::WriteLine(L"delete ManagedTest"); }

           !ManagedTest() { Console::WriteLine(L"finalize ManagedTest"); }

};

 

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

{

           Console::WriteLine(L"1.");

          ManagedTest^ MTest1 = gcnew ManagedTest();

           delete MTest1;

 

           Console::WriteLine(L"2.");

           ManagedTest^ MTest2 = gcnew ManagedTest();

          

           return 0;

}

 

< 결과 >





<코드 1> 설명



< 그림 1. <코드 1>의 소스 코드와 결과 >

 

<그림 1> 코드를 보면 3번의 ‘gcnew’, 4번의 ‘^’를 사용하여 ManagedTest를 생성하였습니다.

<코드 1>에는 없지만 핸들은 포인터와 같은 것으로 관리 클래스의 멤버를 사용할 때는 ‘->’를 사용합니다.

5 delete를 사용하면 소멸자가 호출되어서 6번이 출력됩니다.

MTest2는 소멸자를 호출하지 않아서 8번에서 정의한 finalize가 프로그램이 종료할 때 호출됩니다(7).


 

<코드 1>은 아주 짧고 단순한 코드이지만 관리 클래스의 대부분의 특징을 다 나타내고 있습니다. 관리 클래스에 대한 설명은 아직 남아 있습니다. 이것은 다음 회에 또 설명하겠습니다.



앞서 두 번을 걸쳐서 C++/CLI에 대해서 잘 모르는 분과 싫어하는 분을 위해서 제 생각이나 MSDN에 있는 글을 정리해서 포스팅 했습니다.

 

이제 본격적으로 C++/CLI에 대해서 설명해 나가겠습니다(이 글을 보는 분들은 C++을 알고 있다고 가정을 하겠습니다).

 

 

 

1. ‘C++/CLI가 뭐야?’

 

라고 질문을 하면 가장 초 간단한 답은 ‘.NET에서 C++를 사용하기 위한 언어라고 말할 수 있습니다. 그런데 이 답은 너무 간단하고 없어 보이죠? ^^;

그래서 좀 유식하게 보일 수 있도록 고급스럽게 답해 보겠습니다(또는 복잡하게).

 

C++/CLI에서 CLI‘Common Language Infrastructure’의 약자입니다.

 

C++/CLI CLI 환경에서 돌아가는 프로그램을 만들기 위한 언어입니다. C++/CLI는 마이크로소프트(이하 MS)가 만들었지만 공업 표준화 단체인 ECMA에 의해서 표준 언어로 제정 되어 있습니다.

 C++/CLI MS가 만들었기 때문에 현재까지는 실행 환경이 Windows .NET 플랫폼이지만 언어 사양 상으로는 Windows .NET 플랫폼에만 사용할 수 있는 것이 아닙니다. 이론적으로는 Windows 이외의 Unix Linux, Mac에서도 실행할 수 있습니다(누구라도 Windows 이외서도 사용할 수 있도록 언어 사양을 따라서 구현만 하면 됩니다).

 

C++/CLI C++로 만든 프로그램을 거의 그대로 컴파일 할 수 있습니다.

C++의 표준 기능에 CLI를 위한 추가 기능이 더해져 있습니다.

 

 

 

2. 가장 많은 프로그래밍 언어로 만드는 프로그램 만들기

 

가장 많은 프로그래밍언어로 만드는 프로그램은 무엇일까요? 제가 생각하기에는 그 유명한 ‘Hello World’라고 생각합니다. 제가 공부한 대부분의 프로그래밍 언어 책에는 첫 번째 예제 프로그램이 Hello World였습니다.

 

그래서 C++/CLI도 관례(?)에 맞추어서 ‘Hello World’ 프로그램을 만들어 보겠습니다.

(VS의 마법사 기능에 의해서 코딩을 하나도 하지 않고 프로그램이 만들어집니다.)


< 그림 1. CLR 콘솔 어플리케이션을 선택합니다 >


< 리스트 1. ‘Hello World’ >

#include "stdafx.h"

 

using namespace System;

 

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

{

    Console::WriteLine(L"Hello World");

    return 0;

}

 

<리스트 1>의 코드는 <그림 1>에서 ‘OK’ 버튼을 누른 후 자동으로 생성되는 코드입니다. 이것을 빌드 후 실행을 하며 아래와 같은 결과가 나옵니다.



 

그런데 문제는 실행과 동시에 종료되어서 결과를 볼 틈이 없습니다.

예전에 C++에서는 이런 경우 코드의 아래에 ‘getchar()’를 사용하여 결과를 보고 종료시켰습니다.

그런데 다들 아시겠지만 ‘getchar()’라는 함수는 .NET 라이브러리에 있는 함수가 아닌 네이티브의 함수입니다. C#이라면 바로 사용하지 못하겠지만 C++/CLI는 이런 네이티브의 함수를 바로 사용할 수 있습니다.

 

<리스트 2. getchar() 사용 >

#include "stdafx.h"

#include <stdio.h>

 

using namespace System;

 

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

{

    Console::WriteLine(L"Hello World");

                 

getchar();

    return 0;

}

 

<리스트 2>.NET 라이브러리와 네이티브가 자연스럽게 공존하고 있습니다.

이런 것이 C++/CLI이기 때문에 가능한 것입니다.

 

C++/CLI로 만들어진  'Hello World' 소스 코드를 보면 C++ 프로그래머라면 몇개 처음보는 것이 있지만 이름만 봐도 대충 어떤 의미를 가지고 있는지 쉽게 파악할 수 있어서 소소 코드가 전혀 어렵지 않을 것입니다.

C++/CLI는 기존의 C++에서 CLI가 더해진 것으로 간단하게 말하면 이 더해진 'CLI'만 공부하면 C++/CLI는 마스터합니다.



앞으로 더해진 'CLI' 부분에 대해서 설명해 나가겠습니다.

다음에는 C++의 트레이드마크인 클래스 C에서부터 친숙한 struct C++/CLI에서는 어떤 의미를 갖고 어떻게 사용되는지 알아 보겠습니다.

.NET에서의 C++/CLI의 의미

C++/CLI 2010. 5. 21. 08:30 Posted by 알 수 없는 사용자

C++/CLI에 대한 기술 아티클이 별로 없는 편입니다. 좀 오래 되었지만 MSDN 매거진 2005 1월에 연재된 'Visual C++ 의 프로그래밍 모델과 컴파일러 최적화를 사용한 어플리케이션의 강화'( http://msdn.microsoft.com/ko-kr/magazine/cc163855%28en-us%29.aspx )라는 글을 통해서 간단하게 정리해 보겠습니다.

이 글을 통해서 C++/CLI의 특징이나 .NET 언어 중에서 어떤 특징을 가지고 있는지 알 수 있습니다.

 

 

 

유연한 프로그래밍 모델

 

C++ C#에 비해 언어적 표현력이나 라이브러리가 부족하다는 단점이 있지만 C#에 비해서 프로그래머가 가질 수 있는 자유도가 무척 높습니다(때로는 이것 때문에 많은 문제를 일으키지만).

C#으로 프로그래밍을 할 때는 무조건 객체 지향 프로그래밍 모델만 사용해야 합니다. 그러나 C++는 절차형 프로그래밍, 객체 지향 프로그래밍, 제너릭 프로그래밍, 메타 프로그래밍 등 프로그래머가 원하는 모델을 선택하여 프로그래밍 할 수 있습니다.

.NET에서 C++/CLI를 사용하면 이런 C++의 장점을 바로 얻을 수 있습니다.

 

 

 

어떤 .NET 언어보다도 뛰어난 성능

 

보통 .NET에서 프로그래밍 할 때 어떤 언어를 사용하나 동일한 성능을 낸다고 생각하지만(즉 언어는 달라도 컴파일 결과로 나오는 MSIL은 동일하다고) 이것은 잘 못된 생각입니다.

C++ 컴파일러 팀은 수년에 걸쳐 네이티브 코드의 최적화로부터 얻었던 지식을 C++/CLI의 최적화에 적용할 수 있도록 많은 노력을 했습니다. 그 결과 다른 .NET 언어보다 최적화된 MSIL를 만들어냅니다.

 

VC++은 어느 컴파일러보다 최상의 최적화를 제공하고 이것은 네이티브 뿐만이 아닌 매니지드 코드에 대해서도 같습니다. VC++ 컴파일러는 네이티브 코드의 모든 최적화 방법을 MSIL에도 적용하여 다른 .NET 언어보다 더 뛰어난 최적화를 할 수 있습니다.

 

.NET에서 가장 최적화된 .NET 코드를 만들어 내는 것은 C++/CLI 입니다.

 

 

 

네이티브 코드와 상호 운용 가능

 

사실 C++/CLI .NET에서 가장 큰 의미를 갖는 것이 바로 이 부분이라고 생각합니다.

.NET의 다른 언어에서 네이티브 코드를 사용하려면 네이티브 코드를 DLL로 만들어서 P/Invoke로 호출해야 합니다. 그러나 C++/CLI는 네이티브 코드와 매니지드 코드를 혼합하여 사용할 수 있습니다. 네이티브 함수로부터 매니지드 함수를 호출하는 경우 특별한 구문을 기술할 필요가 없습니다.

 

네이티브에서 매니지드 호출 또는 매니지드에서 네이티브를 호출하는 경우 서로간의 경계를 넘어가야 하므로 비용이 발생하는데 이런 호출을 최대한 줄여야 성능에 좋습니다. C#의 경우에는 서로간의 호출을 줄여야 하는 경우 인터페이스 변경 등이 필요하나 C++/CLI /clr 스위치를 사용하는 것으로 쉽게 변경할 수 있습니다. 이러한 결과로 네이티브와 매니지드 간의 호출에 발생하는 비용을 최소화 할 수 잇습니다.

 

매니지드 코드와 네이티브 코드의 상호 운용에서 가장 비싼 비용은 마샬링입니다. C#의 경우 P/Invoke를 호출할 때 CLR에 의해서 암묵적으로 마샬링이 실행됩니다. 그러나 C++/CLI는 프로그래머가 필요에 따라서 명시적으로 마샬링을 할 수 있어서 한번 마샬링한 데이터를 복수로 사용할 수 있어서 마샬링에 의한 비용을 줄일 수 있습니다.

 

 

 

.NET 프로그램의 처음 실행 시의 딜레이

 

.NET으로 만든 프로그램과 네이티브로 만든 프로그램의 차이 중의 하나가 .NET으로 만든 프로그램은 처음 실행 시에 CLR을 읽기 위해 딜레이가 발생하는 것입니다. 그러나 C++/CLI는 이런 문제를 회피할 수 있습니다.

VC++에는 DLL 딜레이 로딩 이라는 기능이 있습니다. 링커 옵션에서 /DELAYLOAD:dll에 딜레이 로딩을 할 .NET 어셈블리를 지정하면 네이티브 프로그램과 동일한 정도의 속도록 실행 시킬 수 있습니다.

 

 

 

좀 더 C++/CLI가 다른 .NET 언어보다 좋은 점이 있지만 이것으로 줄이겠습니다.

C++/CLI를 아시는 분들은 언어적 위치의 애매함에 의해서 좋지 않은 인상을 가진 분들이 많으리라 생각하는데 제가 소개한 장점을 통해서 조금이나마 좋은 인상을 얻으셨는지 모르겠네요^^.

 


C++/CLI가 다른 .NET 언어보다 어떤 특징을 가지고 있는지 좀 더 자세하게 알고 싶다면 위에 소개한 MSDN 매거진의 원문(영어)을 보시던가 또는 제가 발 번역한 글을 보시기 바랍니다.

 

Visual C++ 의 프로그래밍 모델과 컴파일러 최적화를 사용한 어플리케이션의 강화

http://jacking75.cafe24.com/MSDN_MagaZine/C++Rule_200501.htm

 

그리고 아래의 글도 참고하시기 바랍니다.

C++/CLI: .NET 프레임워크 프로그래밍을 위한 가장 강력한 언어

http://anyflow.net/entry/CCLI-NET-%ED%94%84%EB%A0%88%EC%9E%84%EC%9B%8C%ED%81%AC-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D%EC%9D%84-%EC%9C%84%ED%95%9C-%EA%B0%80%EC%9E%A5-%EA%B0%95%EB%A0%A5%ED%95%9C-%EC%96%B8%EC%96%B412


http://anyflow.net/entry/CCLI-NET-%ED%94%84%EB%A0%88%EC%9E%84%EC%9B%8C%ED%81%AC-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D%EC%9D%84-%EC%9C%84%ED%95%9C-%EA%B0%80%EC%9E%A5-%EA%B0%95%EB%A0%A5%ED%95%9C-%EC%96%B8%EC%96%B422

 

 

C++/CLI 의 설계 원리와 발전 과정

http://msdn.microsoft.com/ko-kr/magazine/cc163484.aspx

C++/CLI는 미운 오리새끼 or 백조

C++/CLI 2010. 5. 11. 08:30 Posted by 알 수 없는 사용자

안녕하세요. 저는 작년에 스터디에 합류하면서 C++0x VC++ 10 Concurrency Runtime에 대해서 글을 적고 강연을 했었습니다. 아직 C++0x Concurrency Runtime에 대한 모든 것을 다 다루지 못했지만 이번에 새로운 멤버들이 합류하여 이 분들이 제가 이전에 맡았던 부분을 맡아서 할 예정이므로 저는 새로운 것을 하려고 합니다.

 

제가 하려는 것은 C++/CLI에 대한 것입니다.

사실 원래 작년부터 저는 C++/CLI을 공부하면서 정리해 보려고 했는데 작년 4월에 스터디에 합류하면서 계속 미루어졌습니다.

 

 

보통 .NET에서 프로그래밍을 한다고 하면 C#으로 프로그래밍 한다고 생각합니다. 그리고 네이티브 프로그래밍을 한다고 하면 C++입니다. 그럼 C++/CLI는 무엇일까요?

 

이름으로 유추 해 볼까요? ‘C++/CLI의 첫 단어는 ‘C++’입니다. ‘C++’ 이라고 하니 딱 네이티브 냄새가 나는군요. 두 번째 단어는 ‘CLI’입니다. C#으로 프로그래밍 하시는 분들은 ‘CLI’가 무엇을 뜻하는지 아시죠? ‘CLI’ .NET의 냄새를 풀풀 풍깁니다. ‘C++/CLI’라는 단어는 네이티브와 .NET의 냄새가 동시에 나지 않나요? 만약 그렇다고 생각하신 분들은 냄새를 잘 맡은 것입니다.^^

 

‘C++/CLI’가 네이티브와 .NET의 냄새를 동시에 풍기듯이 프로그래밍 측면에서도 ‘C++/CLI’는 네이티브와 .NET의 중간 지점에 있는 언어라고 할 수 있습니다.

 

 

 

폭 넓은 또는 이도 저도 아닌 어중간한

 

어떤 영역에서 중간 정도의 위치에 있으면 좋은 말로는 한쪽에 쏠리지 않으면서 양쪽 모든 것을 다 할 수 있다고 들을 수 있지만, 나쁜 말로는 이도 저도 아니라는 말도 들을 수 있습니다.

 

C++/CLI도 그런 것 같습니다.

 

먼저 C++/CLI의 나쁜 점은 .NET 프로그래머와 네이티브 프로그래머 양쪽 모두가 썩 마음에 들어하지 않는 언어라는 것입니다. .NET 프로그래머가 보기에는 이미 C#이라는 좋은 언어가 있어서 다른 언어는 눈에 잘 들어오지도 않고, 네이티브에는 관심이 없는 분들도 많습니다.

 

네이티브 프로그래머에게는 C++라는 걸출한 언어가 있고 일의 특성상 .NET으로는 하기 힘들어서 .NET 프로그래머가 사랑하는 C#도 들어올 공간이 없을 정도입니다. 그런데 C++/CLI를 보니 ‘C++’이라는 단어에 반가움을 가지고 봤더니 C++와 비슷한 것 같으면서 문법이 미묘하게 다른 것이 꽤 마음에 들지 않습니다(C++ 프로그래머가 보기에는 해괴하게 생겼습니다).

 

C#, C++ 어느 하나도 제대로 마스터하기 힘든데 C++/CLI를 제대로 사용하기 위해서는 .NET 프로그래머는 C++을 알아야 하고, C++ 프로그래머는 .NET을 알아야 합니다. 이 바쁜 세상에 하나도 제대로 마스터하기 힘든데 두 개나 알아야 합니다. -__-

 

혹시 여기까지 읽고 “C++/CLI 나쁜 언어군. 이런 잉여 언어라고 생각하고 창을 닫으려고 하는 건 아니겠죠? 조금만 더 기다려 보세요. 이제 좋은 말을 좀 할 테니 이것도 보세요^^

 

그럼 이제 좋은 점을 말해 보겠습니다.

.NET C++(네이티브) 둘 다 각각의 장점을 많이 가지고 있지만 완벽하지는 않습니다.

.NET 프로그래머 입장에서는 아직 세상에는 C++ C로 만들어진 라이브러리가 많고, .NET C++에 비해서 아직은 성능이 낮기 때문에 .NET만으로는 부족한 경우가 있습니다. C++ 프로그래머 입장에서는 요즘 같이 생산성을 추구하는 환경에서 .NET에 비해 낮은 생산성 때문에.NET 환경이 부럽습니다. C++/CLI는 이런.NET 프로그래머와 C++(네이티브) 프로그래머의 아쉬운 부분을 해결해 줄 수 있습니다.

 

물론 C++/CLI를 사용하지 않아도 .NET에서 네이티브 라이브러리를 사용하고, C++ 프로그래머는 .NET으로 만들어진 라이브러리를 사용할 수 있지만 규모가 작지 않은 경우라면 C++/CLI를 사용하는 것보다 까다롭습니다.

 

 

제가 일하고 있는 게임 업계에서는 주력 언어는 C++이고, 예전이나 지금 만들고 있는 대 부분의 라이브러리는 C++을 사용하여 만들고 있습니다. 그러나 게임 개발을 위한 회사에서 사용할 인하우스 툴을 만들 때는 C++만큼의 고 성능을 필요로 하지 않으면서 C++로 만든 라이브러리를 사용하고, 빠르게 개발하고 유지보수가 간편하기를 원하므로 .NET과 네이티브가 결합되기를 바랍니다. 바로 이럴 때가 ‘C++/CLI’가 적격입니다.

 

C++/CLI의 가장 큰 단점은 C# 프로그래머가 보기에도 이상하고, C++ 프로그래머가 보기에도 생김새가 이상해서 흥미를 일으키지 못한다는 점과, C++/CLI를 올바르게 알기 위해서는 .NET C++을 모두 알아야 된다는 점이라고 생각합니다.

그러나 이 단점이라는 부분이 .NET C++ 양쪽 모두를 아는 프로그래머에게는 어느 한쪽만을 아는 프로그래머에 비해 양쪽의 장점을 적절하게 다 가져갈 수 있습니다.

 

.NET이나 C++ 한쪽만을 아는 사람에게는 C++/CLI는 미운 오리새끼이지만 양쪽 모두 아는 사람에게는 백조가 될 수 있습니다.

이 블로그를 통해서 아직까지는 미운 오리새끼로 취급 받고 있는 C++/CLI를 백조가 될 수 있도록 해보겠습니다.

 

 

적다 보니 생각보다 글이 길어졌네요. 첫 글을 너무 쓸데 없이 주절댄 것은 아닌가 모르겠습니다.^^;

 

다음부터는 .NET에서의 C++/CLI의 의미에 대해서 정리하고, C++/CLI에 대한 설명, C++/CLI를 사용한 .NET과 네이티브의 결합, C++/CLI .NET Framework 4 사용 이라는 순서로 글을 올려보겠습니다.

 

 

ps : C++/CLI .NET이 처음 나올 때는 ‘Managed C++’이라는 이름으로 불렸습니다. 그러다가 VS 2005가 나오면서 C++/CLI로 이름이 바뀌었습니다. 또한 문법적인 면에서도 차이가 있습니다.

[MFC/윈도우 7 멀티터치] #2 : 제스처(gesture)를 이용한 구현(上)

MFC 2010. 5. 10. 08:30 Posted by 알 수 없는 사용자

Intro

안녕하세요. MFC 카테고리의 꽃집총각입니다.

한동안 여러 가지 다른 행사와 게으름으로(;;) 포스팅이 많이 늦어졌습니다 ^^; 지난 글에서는 ( [윈도우 7 멀티터치] #1 : 멀티터치 UX를 적용하는 3단계 전략 ) 멀티터치 프로그래밍을 내 애플리케이션에 적용하는 방법을 3단계로 나누어 설명하였습니다. 이 중 첫 번째 단계인 ‘추가 코딩 없이도 기본적으로 얻을 수 있는 기능들’에 대해 자세히 알아보았고, 나머지 두 단계는 기본적인 내용만 소개해 드렸습니다. 이번엔 이중에서 두 번째 단계인 제스처(gesture) 이용한 프로그래밍 방법에 대해 자세히 알아보도록 하겠습니다.

note : 지난번 까지는 프로그래밍 이야기 보다는 개념적인 설명이 위주였기 때문에 ‘Windows 7 Development’ 카테고리에 글을 올렸습니다만… 이제부터는 본격적으로 구현에 대한 이야기가 시작되니까 ‘MFC’ 카테고리에서 연재하도록 하겠습니다. 앞부분은 어느 언어를 사용하든 멀티터치 프로그래밍을 위해 알아두어야 할 공통적인 내용들이었고요, 제가 앞으로 말씀드릴 부분은 주로 MFC를 위한 구현방법이니까, 이게 맞는 거겠지요. :)

 

제스처를 인식하는 첫 번째 예제 프로그램

이제까지는 제가 주로 개념설명이나 이론적인 부분들을 모두 말씀 드리고 나서 예제 코드를 알아봤었는데, 이런 쌍팔년도식 학습법은 요즘은 별로 어울리지 않는 듯 합니다 ^^;… 일단은 돌아가는 예제 프로그램을 간단하게 한 번 작성해 보기로 하지요. 그게 자질구레한 설명도 줄여주고, 공부하기도 좀 더 쉬울 것 같네요 ㅎㅎ

첫 번째로 구현해볼 예제는 view 영역에 그려진 box를 터치로 조작, 변환하는 프로그램 입니다.

view 영역에 box를 하나 표시하고, 손가락으로 사각형을 이동, 회전, 확대/축소 할 수도 있고, Press  & Tap 제스처를 통해 색상을 변경할 수도 있고, Twi Finger Tap 제스처로 X표시를 하거나 지울 수도 있는 프로그램입니다. 예제를 따라서 작성해 보면 MFC 애플리케이션에서 어떻게 제스처를 다룰 수 있는지 쉽게 이해하실 수 있습니다. 해당 예제는 Windows 7 SDK에 들어있는 MFC 샘플 중에 하나입니다. 영어에 부담이 없으신 분들은 직접 Windows 7 SDK를 확인해 보셔도 좋을 것 같군요 ^^ ( http://channel9.msdn.com/learn/courses/Windows7/Multitouch/Win7MultiTouchGestureMFC/Exercise-1-Build-a-Multitouch-Gesture-Application/ )

 

Task 1 : MFC Application 프로젝트를 만들자

우선 Visual Studio 2010에서 MFC Application 프로젝트를 하나 만드세요. Application Wizard에서는 고전 MFC 스타일의 SDI 타입을 지정해 줍니다. Wizard의 설정 화면을 활자 설명 대신 스크린샷으로 대신하겠습니다.

 

Task 2 : 하드웨어 상태를 알아봅시다

본격적인 멀티터치 UX를 구현하기에 앞서, 현재 구동중인 실행환경이 멀티터치가 가능한 상태인지를 확인해야 합니다. 멀티터치를 인식 가능한 하드웨어가 연결되어 있는지, 그리고 사용 가능한 상태인지를 알 수 있어야겠지요. 이런 정보를 얻고자 할 때는 예전부터 익히 사용해오던  ::GetSystemMetrics(…) 함수를 이용하면 됩니다.

int WINAPI GetSystemMetrics( __in  int nIndex );
http://msdn.microsoft.com/en-us/library/ms724385(VS.85).aspx

GetSystemMetrics는 운영체제의 여러 가지 설정 정보들을 얻어올 수 있는 API로 예전부터 자주 사용되던 함수입니다. 윈도우 캡션 영역, 메뉴 영역 등의 너비를 얻을 수 있기 때문에 UI 작업할 때 많이 사용해 보셨을 겁니다. MFC/Win32 API에 새롭게 추가되는 기능들은 상당 부분 이렇게 이전 인터페이스를 그대로 사용하고 있기 때문에 공부하기가 특히 어렵지 않은 느낌이어서 마음에 듭니다 ㅎㅎ 이 함수로 터치인식 정보를 얻고 싶을 땐 아래의 새로운 flag들을 사용합니다.

  • SM_DIGITIGER : 입력장치 상태 확인
  • SM_TABLETPC : 타블렛 pc 여부 확인
  • SM_MAXIMUMTOUCHES : 최대 동시입력 가능한 터치 개수

이번에 공부하면서 보니, 윈도우 XP가 타블렛 PC edition이란게 따로 있었더군요… SM_TABLETPC는 이런 정보를 확인할 때 쓰는 값이고, 나머지 두 flag는 Vista 이하 OS에서는 인식하지 않습니다. 각 항목의 자세한 값은 MSDN을 참고하시면 되고요, 기본적인 경우라면 아래의 코드를 가져다 사용하시면 되겠습니다. 현재 멀티터치 입력 가능 여부와 동시 입력 개수를 알아보는 코드입니다.

   1: BYTE digitizerStatus = (BYTE) GetSystemMetrics(SM_DIGITIZER);
   2: if ((digitizerStatus & (NID_READY | NID_MULTI_INPUT)) == 0) //Stack Ready + MultiTouch
   3: {
   4:     AfxMessageBox(L"현재 터치 입력이 불가능한 상태입니다.");
   5:     return FALSE;
   6: }
   7:  
   8: BYTE nInputs = (BYTE) GetSystemMetrics(SM_MAXIMUMTOUCHES);
   9:  
  10: CString str;
  11: str.Format(L"현재 %d개의 터치를 동시 인식할 수 있습니다.", nInputs);
  12: AfxMessageBox(str);

그리고… 멀티터치 프로그래밍 공부가 하고 싶은데 하드웨어가 마땅치 않을 때… 제가 예전에 말씀 드렸던 시뮬레이션 환경 설정 ( http://multitouchvista.codeplex.com/ ) 기억하고 계시죠? 기억이 안 나시는 분은 ( [멀티터치]멀티터치 프로그래밍 환경 구축하기 ) 링크를 참고해 주세요 ^^

위에 있는 코드를 그대로 복사해서 CTouchGestureDemoApp::InitInstance() 함수에 넣어주세요. 그리고 애플리케이션을 실행하면 아래와 같은 창이 뜹니다.

아따, 많기도 하여라 @.@… 동시에 255개의 터치를 인식할 수 있군요. 저도 터치 입력 환경을 시뮬레이션 해서 공부하고 있는데, 이런 경우 동시 입력 개수가 255개로 나오게 됩니다 ㅎㅎ

 

Task 3 : View 영역에 사각형을 그리자!

샘플을 따라 해보기 위해서 화면에 box를 그려주는 코드를 우리가 일일이 직접 작성할 필요는 없겠죠. 지금은 제스처의 인식 방법을 알아보고 싶은 거니까요. Windows 7 SDK에서 box 드로잉을 해주는 코드를 제공하고 있으니, 이 코드를 사용하도록 하겠습니다. 그리고 우리는 멀티터치 제스처를 이용하는 방법에만 집중하기로 합시다. box를 그려주는 CDrawingObject라는 클래스의 소스는 아래에서 다운 받으실 수 있습니다.

위의 파일을 다운 받으셔서 프로젝트에 포함하고, stdafx.h 파일의 아래쪽에 헤더파일 인클루드 구문을 넣어줍니다. 기본적으로 작성되어 있는 상태에서 #include <afxcontrolbars.h> 아래에 넣어주면 됩니다.

   1: #include "DrawingObject.h"

그리고 ChildView.h를 열고 View 클래스의 멤버 변수로 CDrawingObject 객체를 하나 선언해 줍니다.

   1: // The drawing object
   2: CDrawingObject m_drawingObject;

마찬가지로 View 클래스의 멤버 변수로, box의 확대/축소와 이동을 처리하기 위해 필요한 변수들을 몇 가지 더 추가해 줍니다.

   1: // Needed for drawing object position and size calculations
   2: double m_dblZoomRatioStart;
   3: double m_dblZoomRatioTotal;
   4: CPoint m_ptCenter;

그리고 box를 그려주기 위해서, CChildView.cpp에 있는 CChildView::OnPaint() 함수의 제일 아래쪽에 box drawing 코드를 넣어줍니다.

   1: m_drawingObject.Paint(&dc);

그다음, 윈도우 크기를 변경할 때 box의 크기와 위치를 기본값으로 초기화 해주는 코드를 넣어줍니다. box가 화면영역을 벗어나거나 한 경우 초기화 코드가 있으면 좋겠지요. 그러기 위해서 먼저 WM_SIZE 메세지 핸들러를 추가합니다.

Ctrl + Shift + X 키를 눌러 MFC 클래스 위자드(MFC Class Wizard)를 띄웁니다. MFC 클래스 위자드는 VC++ 6.0에 있던 툴인데, Visual Studio .NET 2002 통합 환경으로 변경되면서 사라졌지만 이번에 Vusial Studio 2010에서 새롭게 부활했습니다. 거의 10년 만이라고 할 수 있군요. 예전에 사용해 보신 분들이라면 아주 익숙한 인터페이스 일겁니다. 여기에서 CChildView의 WM_SIZE 메세지를 선택해서 핸들러를 추가해 줍니다. 아래 스크린샷을 참고하세요.

MFC 클래스 마법사의 부활은 VS2010의 핵심적인 변화 요소중에 하나입니다. 클래스 마법사의 활용 부분은 다음 기회에 별도의 포스팅으로 자세하게 설명하도록 하겠습니다.

생성된 CChildView::OnSize 함수에 아래의 코드를 넣어줍니다.

   1: m_drawingObject.ResetObject(cx, cy);

여기까지 진행했다면 이제 drawing 처리의 완성입니다. 프로젝트를 빌드하고 애플리케이션을 실행하면 아래와 같이 빨간 box가 drawing 되는 모습을 확인하실 수 있습니다.

 

Outro

한 번의 포스팅에서 예제의 완성과 추가설명까지 모두 다루려고 했지만 그러기엔 글이 너무 길어질 것 같네요. 이쯤에서 한 번 호흡을 끊고, 다음 포스팅에서 실질적인 제스처 인식 코딩 방법과 추가설명을 정리하도록 하겠습니다. (下편은 바로 올릴 예정이니 조금만 기다려 주세요)

이번 글에서는 제스처를 인식하는 예제 프로그램의 작성 방법을 단계적으로 소개하고, 하드웨어의 멀티터치 인식 여부 확인 방법을 알아보았고, 아주 짧게나마 VS2010에서 새롭게 부활한 MFC 클래스 마법사에 대한 언급이 있었고, 첫 번째 멀티터치 예제의 작성 방법을 절차적으로 알아보았습니다. 제스처를 이용한 멀티터치 구현 하(下)편에서는 제스처를 인식해 box를 이동, 변환 시키는 코드의 작성과 샘플을 통해 알아볼 제스처 인식 프로그래밍의 부가 정보 및 팁들을 소개해 드리도록 하겠습니다.

그럼 곧바로 하(下)편으로 찾아 뵙겠습니다.
감사합니다 ^^*

Reference

Visual C++ 10의 변화

Visual C++ 10 2010. 5. 2. 22:08 Posted by 알 수 없는 사용자

영문판 VS 2010(Visual C++ 10)은 나왔고, 한글판은 61일에 나온다고 합니다.

 

Visual C++ 10을 도입하려고 하시는 분들은 전체적으로 어떤 변화가 있는지 알고 싶을 것이고, 특히 구매를 위해 회사 윗 분들에게 보고를 해야 되는 분들은 관련된 문서를 만들어야 하는 분들도 있으리라 생각합니다.

 

VC++ 10 도입이나 내부 스터디에 사용하도록 간단하게 VC++ 10의 달라진 점을 정리하여 문서를 만들어 보았습니다.

 

이 문서는 러프하게 만들어진 문서이니 참고로 사용하여 더 좋은 문서를 만드시기를 바랍니다. 특히 윗 분들에게 보고 하기 위해서 PT문서를 만들 때는 이 문서에 왜 필요한지에 대해서 좀 더 강력한 메시지를 넣기를 바랍니다.^^






[JumpToDX11-12] DirectCompute 의 절차.

DirectX 11 2010. 4. 26. 09:00 Posted by 알 수 없는 사용자


앞선 시간에서 우리는 GPGPU 의 실행에 대한 간단한 개념에 살펴보았습니다.
이제 실제적으로 GPGPU 를 활용하는 절차를 살펴볼 차례입니다.
큰 절차는 다음과 같습니다.


 

< DirectCompute 의 초기화 >

가장 먼저 DirectCompute 를 초기화 해야 합니다.
 

hr = D3D11CreateDevice

(

     NULL,     // default gfx adapter

  D3D_DRIVER_TYPE_HARDWARE,  // use hw

     NULL,               // not sw rasterizer

     uCreationFlags,     // Debug, Threaded, etc.

     NULL,               // feature levels

     0,                  // size of above

     D3D11_SDK_VERSION,  // SDK version

     ppDeviceOut,        // D3D Device

     &FeatureLevelOut,   // of actual device

     ppContextOut );     // subunit of device

);

어디서 많이 본 API 라고 생각이 드시죠.
DirectCompute 를 초기화하는 작업은 바로 전통적인 CreateDevice() API 를 사용하는 것입니다.
즉, DirectX 를 사용하는 것입니다.
이로 인해서 DirectX 는 더욱 더 넓은 범위에서 활용되어 질 것입니다.


< HLSL 의 로드 >

그 다음은 실제적으로 GPU 가 실행을 하게될 로직을 로드할 차례입니다.
이것은 워낙 다양할 수 있는 부분이기 때문에, 여기서는 간단하게 예를 들겠습니다.


#define BLOCK_SIZE 256

StructuredBuffer   gBuf1;
StructuredBuffer   gBuf2;
RWStructuredBuffer gBufOut;

[numthreads(BLOCK_SIZE,1,1)]
void VectorAdd( uint3 id: SV_DispatchThreadID )
{
  gBufOut[id] = gBuf1[id] + gBuf2[id];
}


보통 이를 두고 ComputeShader 라고 합니다.
ComputeShader 를 위한 여러종류의 버퍼가 존재합니다.
더 많은 종류의 버퍼는 차후에 설명드리기로 하겠습니다.

StructuredBuffer 라고 정의된 키워드는 C언어의 구조체와 같은 구조를 가집니다.
즉, 개발자가 정의한 구조체입니다.
그런데 앞에 식별자가 없으면 읽기 전용이라는 의미입니다.
반면에 앞에 'RW' 라고 명시된 버퍼는 읽기/쓰기 가 가능한 버퍼라는 의미입니다.
우리는 GPU 가 처리한 결과는 읽기/쓰기가 가능해야 하기 때문에,
결과를 저장하는 버퍼는 'RW" 가 명시되어 있습니다.
최적화를 위해서 각 목적에 맞게 버퍼를 사용해야 할 것입니다.^^


< ComputeShader 의 생성 >

pD3D->CreateComputeShader( pBlob->GetBufferPointer(),
                                             pBlob->GetBufferSize(),
                                             NULL,
                                             &pMyShader );  // hw fmt


CreateComputeShader() API 를 통해서 간단히 ComputeShader 를 생성할 수 있습니다.


< 입력을 위한 GPU 버퍼 만들기 >

우리가 GPGPU 를 활용하는 것은 CPU 를 활용하는 것보다 빠르게 결과를 도출하기 위해서입니다.
이를 위해서는 GPU 가 빠르게 액세스할 수 있는 버퍼가 있어야 할 것이며,
당연히 이것은 비디오 메모리에 존재해야 할 것입니다.
그래서 우리는 DirectX 인터페이스를 통해서 비디오 메모리를 생성을 합니다.


D3D11_BUFFER_DESC descBuf;
ZeroMemory( &descBuf, sizeof(descBuf) );
desc.BindFlags = D3D11_BIND_UNORDERED_ACCESS;
desc.StructureByteStride = uElementSize;
desc.ByteWidth = uElementSize * uCount;
desc.MiscFlags = D3D11_RESOURCE_MISC_BUFFER_STRUCTURED;

pD3D->CreateBuffer( &desc, pInput, ppBuffer );

주의해야 할 것은 바로 'BindFlags' 입니다.
'D3D11_BIND_UNORDERED_ACCESS' 라는 플래그를 주고 있습니다.
이것은 PixelShader 나 ComputeShader 에서 병렬적으로 실행하는 버퍼를 의미
합니다.


< 뷰를 만들자!! >

버퍼 리소스를 만들었으면, 이제 이를 실제 파이프라인에서 액세스할 수 있는 매커니즘을 만들어야 합니다.
즉, ShaderResourceView 를 만들어야 합니다.
DirectX10 부터는 아래와 같이 리소스들을 다루어야 합니다.



앞선 시간들을 통해서 View 라는 개념을 충분히(?) 숙지하셨을 것이라 생각합니다.^^
대체로 ShaderResourceView 는 파이프라인 스테이지에서 읽기 전용입니다.
그런데, 아래 UnorderedAccessView 는 양방향 화살표로 되어있습니다.
읽기/쓰기가 가능한 형태입니다.
이를 통해서 결과를 비디오 메모리에 있는 버퍼에 결과를 기록할 수 있음을 보여주고 있는 것입니다.


실제 API 를 통한 View 생성은 다음과 같습니다.

D3D11_UNORDERED_ACCESS_VIEW_DESC desc;
ZeroMemory( &desc, sizeof(desc) );
desc.ViewDimension = D3D11_UAV_DIMENSION_BUFFER;
desc.Buffer.FirstElement = 0;
desc.Format = DXGI_FORMAT_UNKNOWN;
desc.Buffer.NumElements = uCount;

pD3D->CreateUnorderedAccessView( pBuffer, // Buffer view is into
                                                      &desc,  // above data
                                                      &pMyUAV ); // result


중요한 부분은 ViewDimension 부분입니다.
'D3D11_UAV_DIMENSION_BUFFER' 를 설정하고 있는데,
이는 ComputeShader 상에서 이 버퍼를 일반적인 버퍼로 보겠다
는 의미입니다.
즉, 샘플링 작업을 전혀하지 않습니다. 
이는 어떠한 수정도 없이 데이터를 있는 그대로 보존합니다.


< 실행 단계 >

이제까지는 모두 준비 단계였습니다.
이제는 실제 실행 단계에 대해서 언급해 보겠습니다.

먼저, ComputeShader 를 현재 파이프라인 스테이지에 아래와 같이 바인딩 해주어야 합니다.
pD3D->CSSetShader( pMyShader, NULL, 0 );


그 다음에는 뷰를 바인딩해야 합니다.
pD3D->CSSetUnorderedAccessViews( 0,
                                                        1,
                                                       &pMyUAV,
                                                       NULL );


이제 마지막으로 GPU 에게 현재 바인딩된 내용을 바탕으로 연산해 줄 것을 요청합니다.
pD3D->Dispatch( GrpsX, GrpsY, GrpsZ );


이제 실행의 단계가 모두 끝났습니다.
이 단계까지 끝나면, 실행 결과가 비디오 메모리에 존재합니다.
우리가 결과를 확인하기 위해서는 CPU 가 액세스할 수 있는 버퍼로 결과를 복사
해야 합니다.


< 결과 복사해 오기 >

결과를 CPU 가 액세스 하기 위해서는 어떻게 해야 할까요?
이전 시간을 통해서 언급드렸듯이,
DX10 부터는 리소스에 대한 세부적인 액세스 권한에 대한 플래그를 설정할 수 있습니다.
그래서 다음과 같은 설정으로 버퍼를 만듭니다.

D3D11_BUFFER_DESC desc;
ZeroMemory( &desc, sizeof(desc) );
desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
desc.Usage = D3D11_USAGE_STAGING;
desc.BindFlags = 0;
desc.MiscFlags = 0;
pD3D->CreateBuffer( &desc, NULL, &StagingBuf );


'D3D11_CPU_ACCESS_READ' 라는 플래그를 통해서,
이 버퍼는 CPU 가 액세스 할 수 있는 형태로 만듭니다.
그리고 'D3D11_USAGE_STAGING' 라는 플래그를 통해서
단순히 비디오 메모리에 있는 내용을
CPU 도 접근 할 수 있는 메모리로 복사해오는 버퍼임을 명시
합니다.


그리고 아래와 같이, 준비해둔 버퍼에 실제로 메모리를 복사해 옵니다.
pD3D->CopyResource( StagingBuf, pBuffer );

마침내 우리는 GPU 에 의해서 처리된 결과를 확인할 수 있게 되었습니다.


< 마치며...>

지금까지 DirectCompute 를 활용하는 일련의 절차에 대해서 살펴보았습니다.
DirectX11 의 API 가 생소해서 어려워 보일 수 있지만,
실제로 DirectCompute 의 절차는 그리 복잡하지는 않습니다.
현재 DirectCompute 의 활용은 SDK 샘플에 'NBodyGravity' 라는 이름으로 들어있습니다.
제가 여기에 대해서 자세히 언급드리면 좋겠지만,
그것은 차후로 미루기로 하겠습니다.^^


참고 자료
http://microsoftpdc.com/Sessions/P09-16
본 내용은 위의 PDC 를 참고해서 만들었습니다.

"Visual C++ 10과 C++0x" pdf 파일

C++0x 2010. 4. 20. 13:18 Posted by 알 수 없는 사용자

C++0x 관련 책 "Visual C++ 10 C++0x"가 오늘 한국 MSDN 사이트에 올라왔습니다.

e-book으로 보기를 원하는 분이나 책을 얻지 못한 분들은 다운로드 해서 보세요

 

MSDN : http://msdn.microsoft.com/ko-kr/default.aspx


 


Visual Studio의 시작 페이지에도 다운로드 링크가 표시됩니다.

 

 

그리고 책에 오타가 있습니다.

48페이지 decltype 설명에서 오타가 있습니다.




팀블로그에 예전에 RValue Reference lambda 관련 글을 올린 적이 있는데 책을 만들 때 제가 올린 글을 다시 보니 설명에 미흡한 부분이 많아서 RValue Reference lambda는 새로 적었습니다. 그러니 RValue Reference lambda를 공부할 때는 꼭 블로그에 올라와 있는 글보다 이 책의 글을 보시기 바랍니다.


'C++0x' 카테고리의 다른 글

[Plus C++0x] 람다(Lambda) 이야기 (2)  (1) 2010.05.27
[Plus C++0x] 람다(Lambda) 이야기 (1)  (0) 2010.05.27
C++0x 관련 책 "Visual C++ 10과 C++0x"  (9) 2010.04.17
VC++ 10에 구현된 C++0x의 코어 언어 기능들  (1) 2010.04.12
nullptr  (2) 2010.01.28