Welcome to dynamic C# 외전(1) - Generate From Usage.

C# 2009. 10. 25. 09:00 Posted by 알 수 없는 사용자

- 베타2를 맞이하여.

비주얼스튜디오 2010 베타2가 드디어 나왔습니다. 모양도 초큼더 이뻐진거 같구요, 이제 슬슬 제품이 출시될거 같다는 느낌도 드는 군요. 제가 처음 비주얼 스튜디오를 접한게 비주얼스튜디오2002 베타2 버전이었는데요, 왠지 향수가 느껴지네요.-_-;;; 이번 포스트에서는 TDD의 대세를 따라서 코딩을 좀더 편리하게 해주는 기능하나를 소개해드리려 합니다.


- 이름하여 Generate From Usage!

모 이름은 '사용하는 모냥을 봐서 생성하겠다'정도가 되겠네요. 2008까지는 TDD방식으로 개발을 한다고 해도, 클래스등을 미리 만들어 놓고, 메서드 수준에 가서야 메서드 스텁은 생성하면서 TDD방식을 적용할 수 있었습니다. 물론, 편리한 툴의 지원을 받는 면에서 말이죠. 그런데 2010 베타2 부터는 TDD를 편리하게 할 수 있도록 좀 더 멋진기능이 지원됩니다. 한번 살펴보시져!


예들 들어서, Test123이라는 클래스를 TDD로 만들려고 합니다. 그러면, Test123은 당연히 현재 존재하지 않는 클래스이겠죠? TDD가 일반적으로 시나리오를 기반으로 테스트케이스를 먼저 만들고, 그 테스트를 통과하도록 구체적으로 코드를 작성해나가는 방식이니까요. 약간 귀찮은 문제가 여기서 발생합니다. 실제로 TDD를 해보면, 테스트 코드를 작성할때 대상이 되는 클래스를 열심히 타이핑하는데, 기존에 없는 클래스기 때문에 당연히 인텔리센스의 지원이 안되서 날코딩을 해야 하는거죠. 그래서 귀찮으니깐 없는 클래스를 껍데기만 미리 작성해놓고, 인텔리센스의 지원을 받습니다. 그리고 VS2008에서는 스텁생성이 메서드레벨에서만 지원됐기 때문에, 클래스를 선언해놔야 좀더 편리하기 때문이죠.

근데, 만약에 존재하지 않는 클래스라도 인텔리센스의 지원을 받을 수 있다면? 그리고 그 클래스의 스텁을 생성해줌과 동시에, 클래스 파일까지도 생성을 해준다면? 그리고 드라군이 출동한다면?? ........ 아무튼 *라게 많이 편하겠죠?

<그림1>을 보시면, 이런 멋진 기능이 이제 현실로 다가왔음을 느끼실 수 있습니다. 분명히 Test123은 없는 클래스니깐, 빨간 밑줄이 그어졌는데, new뒤에 나오는 인텔리센스에는 Test123이 버젓이 들어가 있습니다!!!



<그림1>버젓이 들어가 있다!!!!


인텔리센스에 버젓이 들어가 있었지만, 아직 정의안된 클래스니깐 <그림2>처럼 에러메세지가 나오는걸 보실 수 있습니다.


<그림2>클래스정의가 엄서요!!


하지만, 예전버전에서 없는메서드에 마우스 오른쪽 버튼을 눌러서 메서드 스텁을 생성했듯이, Test123위에 마우스를 놓고 오른쪽 버튼을 눌러보면, <그림3>처럼 클래스를 생성하는 옵션이 있습니다.



<그림3>참으로 착한 옵션이로닭!!!


그리고 결과를 보면, <그림4>처럼 Test123.cs라는 파일이 생성되었고, <그림5>처럼 껍데기가 작성된 걸 보실 수 있습니다!



<그림4>조...좋은 생성이다1



<그림5>조...좋은 생성이다2

그리고 이번엔, 없는 프로퍼티를 하나 써볼까요? <그림6>처럼 아직 정의가 안된 프로퍼티를 갖다쓰면, 어쩌구 저쩌구 하고 컴파일러가 불평을 합니다.



<그림6>어쩌구 저쩌구...


그러면, 클래스와 같이 오른쪽 버튼을 지그시 눌러주면, <그림7>처럼 필드나 프로퍼티로 생성을 할 수 있습니다. 프로퍼티를 눌러보면 <그림8>과 같이 프로퍼티가 하나 추가된걸 보실 수 있죠.



<그림7>프로퍼티 하나 추가여.



<그림8>조...좋은 생성이다3


- 마치면서

VS2008에서 TDD를 아주 허접하게 해본 경험으로 미뤄볼때, 확실히 좀더 잔손질을 줄여주는 기능이 될 거 같습니다. 뭐 꼭 TDD가 아니더라도 기능을 활용할 일은 많은 거라는 생각이 드네요. 전 이래서 비주얼 스튜디오가 좋습니다. ㅋㅋㅋ....-_-;;;
이 글은 MSDN 글, "Solving The Dining Philosophers Problem With Asynchronous Agents"를 참고하여 작성되었습니다.

Asynchronous Agents Library로 Dining Philosophers 문제 해결하기 - 1
Asynchronous Agents Library로 Dining Philosophers 문제 해결하기 - 2

오래 기다리셨습니다; 그간 일이 바빠서;; 어쨌든 지난번에 Concurrecy::agent 에서 상속받은 Philosopher 클래스를 살펴봤었죠. 아래 두 함수만 제외하고 말입니다.

자 먼저 젓가락을 집는 함수입니다. 젓가락 한쌍을 동시에 집어야지 하나만이라도 먼저 집으려고 하다간 서로 젓가락 하나씩 잡고 기다리는 데드락 상황이 발생할 수 있습니다. 이를 위해 쓰이는 것이 지난 회에 잠깐 언급했든 join 메시지 블록입니다. 그 중에서도 non_greedy 버전을 사용해야 합니다. non_greedy 버전은 명시된 타겟을 모두 얻을 수 있을 때에만 실제 획득을 시도합니다. gready 버전을 사용하면 전술한 것처럼 데드락이 발생할 수 있습니다.

   73     std::vector<Chopstick*> PickupChopsticks()

   74     {

   75         //join 생성

   76         Concurrency::join<Chopstick*,Concurrency::non_greedy> j(2);

   77         m_LeftChopstickProvider->link_target(&j);

   78         m_RightChopstickProvider->link_target(&j);

   79 

   80         //젓가락 한쌍을 집습니다.

   81         return Concurrency::receive (j);

   82     } 


젓가락을 내려놓은 것은 간단합니다. 비동기 메시지 전송 함수인 Concurrency::asend()를 사용하여 젓가락이 이용가능함을 알리면 끝입니다.

   83     void PutDownChopsticks(std::vector<Chopstick*>& v)

   84     {

   85         Concurrency::asend(m_LeftChopstickProvider,v[0]);

   86         Concurrency::asend(m_RightChopstickProvider,v[1]);

   87     }


마지막으로 철학자들과 젓가락, 젓가락제공자를 가지고 이들 모두를 셋업하는 역할을 하는 Table 클래스입니다. 주석을 참고하시면 쉽게 이해하실 수 있을 겁니다.

  100 template<class PhilosopherList>

  101 class Table

  102 {

  103     PhilosopherList & m_Philosophers;

  104     std::vector<ChopstickProvider*> m_ChopstickProviders;

  105     std::vector<Chopstick*> m_Chopsticks;

  106 

  107     //이 생성자는 Table 클래서에서 유일한 public 메소드로 vector 변수들을 초기화하고 각 철학자에게 젓가락제공자를 할당합니다:

  108 public:

  109     Table(PhilosopherList& philosophers): m_Philosophers(philosophers)

  110     {

  111         //젓가락 및 젓가락제공자 vector를 채웁니다

  112         for(size_t i = 0; i < m_Philosophers.size();++i)

  113         {

  114             m_ChopstickProviders.push_back(new ChopstickProvider());

  115             m_Chopsticks.push_back(new Chopstick("chopstick"));

  116             //젓가락제공자에 젓가락을 놓습니다

  117             send(m_ChopstickProviders[i],m_Chopsticks[i]);

  118         }

  119         //철학자들을 식탁 자리에 앉힙니다

  120         for(size_t leftIndex = 0; leftIndex < m_Philosophers.size();++leftIndex)

  121         {

  122             //rightIndex 계산

  123             size_t rightIndex = (leftIndex+1)% m_Philosophers.size();

  124 

  125             //왼쪽,오른쪽 제공자를 해당 철학자에 부여합니다

  126             Concurrency::asend(& m_Philosophers[leftIndex].LeftChopstickProviderBuffer,

  127                 m_ChopstickProviders[leftIndex]);

  128             Concurrency::asend(& m_Philosophers[leftIndex].RightChopstickProviderBuffer,

  129                 m_ChopstickProviders[rightIndex]);

  130         }

  131     }

  132     ~Table(){

  133         m_ChopstickProviders.clear();

  134         m_Chopsticks.clear();

  135     }

  136 

  137 };


드디어 대망의 main() 함수입니다. 상태표시를 위한 call 블록과 C++0x 람다의 사용 이외에는, 전술할 클래스들을 사용하고 있을 뿐입니다.

  206 int main()

  207 {

  208     //tr1 array를 사용해 철학자들을 생성합니다

  209     std::tr1::array<Philosopher,5> philosophers = {"Socrates", "Descartes", "Nietzche", "Sartre", "Amdahl"};

  210     Table<std::tr1::array<Philosopher,5>> Table(philosophers);

  211     //상태표시에 이용할 call 블록들의 목록을 생성합니다

  212     std::vector<Concurrency::call<PhilosopherState>*> displays;

  213     //철학자 에이전트를 구동하고 상태표시 항목을 생성합니다

  214     std::for_each(philosophers.begin(),philosophers.end(),[&displays](Philosopher& cur)

  215     {

  216         //상태표시용 call 블록을 하나 만듭니다

  217         Concurrency::call<PhilosopherState>* consoleDisplayBlock = new Concurrency::call<PhilosopherState>([&](PhilosopherState in){

  218             //cout은 각 출력 사이의 스레드안정성을 보장하지 않습니다

  219             if(in == Eating)

  220                 std::cout << cur.m_Name << " is eating\n";

  221             else

  222                 std::cout << cur.m_Name << " is  thinking\n";

  223         });

  224         //상태표시 블록을 연결하고 벡터에 저장해둡니다

  225         cur.CurrentState.link_target(consoleDisplayBlock);

  226         displays.push_back(consoleDisplayBlock);

  227         //그리고 에이전트를 구동합니다

  228         cur.start();

  229     });

  230     //모두 완료되기를 대기

  231     std::for_each(philosophers.begin(),philosophers.end(),[](Philosopher& cur)

  232     {

  233         cur.wait(&cur);

  234     });

  235 

  236     displays.clear();

  237 };


이상을 실행하면 다음과 유사한 결과를 확인하실 수 있습니다.


주석에도 나와있듯이 스레드에 안전하지 않은 cout 출력으로 가끔 상태 메시지가 섞여였음을 확인할 수 있습니다. 그것 이외에는 철학자들이 사이좋게 식사를 하고 있음을 알 수 있습니다.

이렇듯 AAL을 사용하면 저수준의 스레드 함수나 동기화 개체들을 직접 다루지 않고도 쉽게 병렬 수행 작업을 작성할 수 있습니다. 병렬화에 고민하지 않고, 해당 응용프로그램의 도메인 문제에만 집중할 수 있는 것이죠.


이상입니다. 이제 새로운 로고와 함께 VS2010의 베타2도 나왔으니, 새로운 주제로 다시 찾아뵙지요. ^^

[DX11_#3]기본적인 설정

DirectX 11 2009. 10. 22. 23:58 Posted by 알 수 없는 사용자

 DirectX SDK(August 2009)에 포함된 'EmptyProject11'을 기준으로 기본 적인 DirectX세팅에 대해서 설명 하겠습니다.

 아래와 같이 DXUT의 일반적인 설정을 하면 상황에 이벤트에 따라 해당 함수를 호출합니다.
키보드가 눌리면 Onkeyboard가 호출되고, DXUTCreateDevice로 디바이스를 생성하면 'ModifyDeviceSettings'함수가 호출되는 등 기본적으로 필요한 골격을 제공하고 있습니다.

DXUTSetCallbackFrameMove( OnFrameMove );
DXUTSetCallbackKeyboard( OnKeyboard );
DXUTSetCallbackMouse( OnMouse );
DXUTSetCallbackMsgProc( MsgProc );
DXUTSetCallbackDeviceChanging( ModifyDeviceSettings );
DXUTSetCallbackDeviceRemoved( OnDeviceRemoved );

 DX9까지는 DX에 디파인된 버젼으로 어플리케이션에서 실행이 가능한 버젼인지 체크 후 버젼이 맞지 않으면 프로그램을 종료해 버렸습니다.
if( NULL == ( g_pD3D = Direct3DCreate9( D3D_SDK_VERSION ) ) )
     return E_FAIL;

 그러나 아래와 같이 초기화를 하면 어렵지 않게 각 버젼에 대해 깔끔하게 처리가 가능합니다.
여러개의 버젼을 세팅하는 것에 대해 저처럼 의아하게 생각하시는 분도 계실 것 입니다.
그러나 그저 DX9과 DX11을 같이 소스에 위치를 시키면, 하위 버젼에 대한 것들은 모두 알아서 삭제되고,
DX11의 콜백은 정상적으로 설정이 됩니다.(위 아래 순서를 바꿔도 상관 없이 이와 같이 동작 합니다.)

DXUTSetCallbackD3D9DeviceAcceptable( IsD3D9DeviceAcceptable );
DXUTSetCallbackD3D9DeviceCreated( OnD3D9CreateDevice );
DXUTSetCallbackD3D9DeviceReset( OnD3D9ResetDevice );
DXUTSetCallbackD3D9FrameRender( OnD3D9FrameRender );
DXUTSetCallbackD3D9DeviceLost( OnD3D9LostDevice );
DXUTSetCallbackD3D9DeviceDestroyed( OnD3D9DestroyDevice );

DXUTSetCallbackD3D11DeviceAcceptable( IsD3D11DeviceAcceptable );
DXUTSetCallbackD3D11DeviceCreated( OnD3D11CreateDevice );
DXUTSetCallbackD3D11SwapChainResized( OnD3D11ResizedSwapChain );
DXUTSetCallbackD3D11FrameRender( OnD3D11FrameRender );
DXUTSetCallbackD3D11SwapChainReleasing( OnD3D11ReleasingSwapChain );
DXUTSetCallbackD3D11DeviceDestroyed( OnD3D11DestroyDevice );

 첫번째 인자를 true로 설정하면 커맨드를 설정해 아래와 같은 기능을 사용 할 수 있습니다.
(각 버젼에 특화된 커맨드도 몇 가지 있습니다.)
'-fullscreen'=> 전체화면으로 설정
'-width:800 -height:600' => AppWindow의 넓이, 높이를 800x600으로 설정
(이 커맨드를 설정하면 DXUTCreateDevice의 화면 사이즈는 무시됩니다.)
 두번째 인자는 에러 메세지 박스 출력
 세번째 인자는 여분의 커맨드라인 파라메타세팅(default:NULL)
DXUTInit( true, true, NULL );

첫번째 인자가 true이면 전체 화면 모드에서 커서가 보이도록 설정.
두번째 인자가 true이면 전체 화면 모드에서 커서가 화면을 벗어나지 못하도록 설정.
(테스트 했을때 두개의 인자 모두 true와 false일때의 차이점을 느끼지 못했습니다.)
DXUTSetCursorSettings( true, true );

설정한 타이틀로 윈도우 생성
DXUTCreateWindow( L"EmptyProject11" );

 '하드웨어 레벨10'으로 디바이스를 생성
(Feature level관련은 조진현님의 글을 참고하시기 바랍니다.)
두번째 인자가 true이면 윈도우모드, false이면 풀스크린모드 입니다.
(테스트 했을때 false일때도 풀스크린 모드로 변경이 되지 않았습니다.)
세번째, 네번째 인자로 화면의 넓이 높이를 설정할 수 있습니다.
DXUTCreateDevice( D3D_FEATURE_LEVEL_10_0, true, 640, 480 );

DXUT 메인루프 실행
DXUTMainLoop();

 리턴되는 값으로 D3D초기화, 디바이스 상태 등을 알 수 있습니다.
'0'이면 정상이고, 그 이외의 값일경우 DX help파일에서 에러 내용을 확인하시기 바랍니다.
DXUTGetExitCode();

샘플을 실행하면, 예상대로 그저 윈도우안에 세팅된 타이틀과, 파란 화면만 덩그라니 출력됩니다.

첫 샘플 분석이기 때문에 최대한 간단한 함수까지 설명을 적었습니다.
다음 글 부터는 특이사항이 없는한 이번에 설명했던 함수에 대해서는 넘어가도록 하겠습니다.

 DXUTSetCursorSettings, DXUTCreateDevice함수의 설명과 테스트 결과가 다른 이유는 DirectX Help의 설명을 적었으나 테스트 결과 문서대로 동작하지 않았던 것 입니다.
 문제가 있는 것이 맞다면 다음 버젼에는 수정될 것으로 예상됩니다.

다음 글은 좀 더 화면에 보이는 것이 많은 것으로 올리도록 하겠습니다.

'DirectX 11' 카테고리의 다른 글

[JumpToDX11-6] 커맨드(Command)...  (0) 2009.11.09
[JumpToDX11-5] 새로운 시대를 여는 DirectX11...  (6) 2009.11.02
[DX11_#2]D3D Buffer( 2 / 2 )  (0) 2009.10.13
[DX11_#1]D3D Buffer( 1 / 2 )  (0) 2009.09.22
[JumpToDX11-4] ID3D11View  (0) 2009.09.07