Welcome to Dynamic C#(21) - 인덱스의 힘.

C# 2010. 5. 21. 09:00 Posted by 알 수 없는 사용자

- 인덱스는 왜 나오는 고냐...?

인덱스는 방대한 정보를 특정한 기준으로 잘 분류해 놔서 정보를 금방 찾을 수 있도록 해주는 고마운 장치이죠. 아무리 두꺼운 사전이 있다고 해도, 그 사전이 가나다 순이나 알파벳순으로 잘 인덱싱이 되어있지 않으면 쓸모없겠죠. C#도 그래서, 대화하기 여러운 COM과 잘 지내기 위해서 인덱스 가능한 프로퍼티를 사용하기 시작했쬬!


- 인덱스 가능한 프로퍼티가 몬데?

이름 그대로입니다. 그냥 이름 그대로 프로퍼티인데, 프로퍼티의 값을 가져올 때 인덱스로 특정 요소에 접근가능한 프로퍼티죠. 아래와 같은 모양이 인덱스로 접근하는 프로퍼티의 모습입니다.

myObject.MyIndexedProperty[index];

인덱스 가능한 프로퍼티는 내부적으로 그냥 간단한 접근자 메서드와 일반적으로 쓰이는 인덱서와 유사한 인덱서로 구성됩니다. 그냥 일반적으로 쓰는 인덱서처럼 생각할 수도 있죠. 그러면 일반적인 인덱서와의 차이점을 예제를 통해서 한번 확인해 보겠습니다. 우선 일반적인 인덱서의 예제를 보시죠.

class D
{
    private int[] nums = new int[] { 1, 2, 3, 4, 5, 6, 7, 8 };

    public int this[int x]
    {
        get
        {
            return nums[x];
        }
    }
}

class C
{
    static void Main()
    {
        D d = new D();
        for (int i = 0; i < 8; i++)
        {
            Console.WriteLine(d[i]);
        }
    }
}


그리고 인덱스 가능한 프로퍼티의 사용예제입니다.

using System;
using Excel = Microsoft.Office.Interop.Excel;
using System.Collections.Generic;

namespace OfficeInteropExam2
{
    public class Account
    {
        public int ID { get; set; }
        public double Balance { get; set; }       
    }

    class Program
    {
        static void DisplayInExcel(IEnumerable<Account> accounts)
        {
            var excelApp = new Excel.Application();
            excelApp.Visible = true;
                       
            excelApp.Workbooks.Add();

            Excel._Worksheet workSheet = (Excel.Worksheet)excelApp.ActiveSheet;

            workSheet.Cells[1, "A"] = "ID Number";
            workSheet.Cells[1, "B"] = "Current Balance";

            var row = 1;
            foreach (var acct in accounts)
            {
                row++;
                workSheet.Cells[row, "A"] = acct.ID;
                workSheet.Cells[row, "B"] = acct.Balance;
            }

            workSheet.Range["A1", "B3"].AutoFormat(
                Excel.XlRangeAutoFormat.xlRangeAutoFormatClassic2);
        }

        static void Main(string[] args)
        {
            var bankAccounts = new List<Account> {
                new Account {
                  ID = 345678,
                  Balance = 541.27
                },
                new Account {
                  ID = 1230221,
                  Balance = -127.44
                }
            };

            DisplayInExcel(bankAccounts);
        }
    }
}


전자는 간단하게 인덱서를 사용하는 예제이구요, 두번째는 엑셀에 데이터를 표시하는 COM 프로그래밍 예제입니다. 두번째 예제에서 밑줄친 부분이 바로 인덱스 가능한 프로퍼티를 사용한 부분입니다. 겉으로 보기에는 별 차이가 없어 보이는데요. 내부적으로는 어떨까요? 리플렉터로 우선 첫번째 예제를 보겠습니다.


위에서 붉은 선으로 표시된 대로, 일반적인 인덱서 앞에는 'Item'이라는 이름이 붙습니다. 그리고 이 인덱서를 사용하는 부분의 코드는,

private static void Main()
{
    D d = new D();
    for (int i = 0; i < 8; i++)
    {
        Console.WriteLine(d[i]);
    }
}

위와 같이 소스코드와 큰 변화가 없는 걸 볼 수 있습니다. 그러면 두번째 예제는 어떨까요? 인덱스 가능한 프로퍼티가 사용된 부분만 리플렉터로 보면,

workSheet.get_Range("A1", "B3").AutoFormat(XlRangeAutoFormat.xlRangeAutoFormatClassic2, Missing.Value, Missing.Value, Missing.Value, Missing.Value, Missing.Value, Missing.Value);

'Range["A1", "B3"]'이라고 썼던 부분이 'get_Range'라는 메서드 호출로 바뀐걸 보실 수 있습니다. 그러면 저 get_Range를 한번 찾아볼까요?


위에서 보시듯이 'Item'이라는 표시가 없습니다. 즉 일반적인 인덱서가 아니라는 이야기죠. 그리고 접근자 메서드를 가지고 있는 걸 보실 수 있습니다. 위에서 인덱스 가능한 프로퍼티가 '접근자 메서드와 일반적으로 쓰이는 인덱서와 유사한 인덱서로 구성'된다고 이야기 한 부분이 바로 이걸 가리키는 말이었던 거죠.


- 찝찝하지만..

인덱스 가능한 프로퍼티를 그동안 추가하지 않았던 이유는 C#팀의 디자인 철학과 맞지 않아서 였다고 합니다. C#팀에서는 'C라는 타입안에 있는 P라는 프로퍼티에 접근해서 값을 가지고 온다. 그리고 그 결과값에 대해서 인덱스로 접근한다'는 시나리오가 더 옳다고 생각한다고 하네요.

하지만, 인덱스 가능한 프로퍼티를 사용하는 COM이 무척이나 많이 퍼져있다는 게 문제였다고 합니다. 그래서 이전 포스트에서 언급 해드렸던 이유와 같은 이유로 COM 프로그래밍을 지원하기 위해서 이런 기능이 추가되었다고 하네요.

비록 인덱스 가능한 프로퍼티를 사용할 수 있긴 하지만, C#에서는 인덱스 가능한 프로퍼티를 선언할 수는 없습니다. COM 라이브러리가 tlbimp라는 툴을 이용해서 만들어 지기 때문에, C#에서는 만들수 없다고 하네요. 즉 C#에서는 COM 라이브러리에 대해서만 인덱스 가능한 프로퍼티를 사용할 수 있는 것이죠.

컴파일러는 COM 타입에서 인덱스 가능한 프로퍼티로 보이는 걸 모두 임포트해둔다고 합니다. 그래서 해당 프로퍼티에 대해서는 이름을 붙인 인덱서 문법을 이용해서 사용할 수 있도록 한다고 하네요. 즉, '컴파일러는 이름을 붙인 인덱서 문법을 통해서 인덱스 가능한 프로퍼티를 사용할 수 있도록 한다'는 것이죠. 위의 예제에서 보여드렸듯이 일반적인 프로퍼티는 접근자 메서드를 통해서 바로 접근할 수 없습니다. 하지만 문법적으로는 매우 유사한 형태를 취하면서 내부적으로는 다른 일이 벌어지고 있는 것이죠.


- 오버로드 판별도 해보자.

인덱스 가능한 프로퍼티가 추가되면서 또 오버로드 판별에 변수가 생겼습니다. 아래의 예제를 가지고 설명을 해보면요,

myObject.MyIndexedProperty[index];

우선 컴파일러가 이 구문을 보게 되면, '.'왼쪽을 그 객체의 타입과 연결시킨다고 합니다. myObject의 타입이 예를 들어서 MyType이라고 하면, 그 둘을 연결시키는 것이죠. 그 다음에는 MyIndexedProperty라는 이름을 MyType에서 쭉 훑어보면서 찾습니다.

하위 호환성을 유지하기 위해서, 컴파일러가 만약 검색중에 같은 이름을 가진 평범한 프로퍼티를 보더라도 무조건 그 프로퍼티와 호출을 바인딩하게 됩니다. 하지만, MyType에서 MyIndexedProperty라는 프로퍼티를 발견했다고 해도, MyType에 인덱서가 없다면 이 프로퍼티에 호출을 바인딩하지는 않습니다.

이렇게 인덱스 가능한 프로퍼티를 제외한 일반적인 바인딩이 실패 했다면, 컴파일러는 MyType이 ComImport타입인지 확인합니다. ComImport타입이 맞다면, MyType에서 사용가능한 인덱스 가능한 프로퍼티의 리스트를 만들어서, 후보군에 대해서 오버로딩 판별을 하는데, 이때의 판별 알고리즘은 평범한 프로퍼티와 동일하게 진행됩니다. 그저 이름으로 인덱서를 취급하듯이 하는 것이죠.

그리고 인덱스 가능한 프로퍼티를 사용했다고 해서 특별한 일이 일어나는 건 아닙니다. 그저 예전버전에서 값을 가져오려면 적어야 했던 내용을 그대로 생성해주는 syntactic sugar일 뿐이기 때문이죠. 이 부분은 아래의 예제를 보시면 좀 더 명확해 집니다.

//인덱스 가능한 프로퍼티를 사용한 경우
workSheet.Range["A1", "B3"].AutoFormat(
    Excel.XlRangeAutoFormat.xlRangeAutoFormatClassic2);

//기존버전에서 작업해야 했던 경우
workSheet.get_Range("A1", "B3").AutoFormat(Excel.XlRangeAutoFormat.xlRangeAutoFormatClassic2,
    Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing,
    Type.Missing);

위에서 보여드렸던 예제중에서 인덱스 가능한 프로퍼티를 사용한 코드를 리플렉터에서 보면 아래와 같았습니다.

workSheet.get_Range("A1", "B3").AutoFormat(XlRangeAutoFormat.xlRangeAutoFormatClassic2, Missing.Value, Missing.Value, Missing.Value, Missing.Value, Missing.Value, Missing.Value);

똑같죠? 넵. syntatic sugar일 뿐이니까요. 낄낄낄.


- 마치면서

이번 포스트는 내용을 정리하면서 좀 힘들었습니다. 내공의 부족 때문인지 내용이 제대로 이해가 안됐기 때문이죠. 그런데 예제를 만들고 확인해보고 하니깐 조금씩 이해가 되더군요. 정리가 잘 된건지 모르겠습니다. 그럼 오늘은 여기까지~~!!!


- 참고자료

1. http://blogs.msdn.com/samng/archive/2009/11/03/com-interop-in-c-4-0-indexed-properties.aspx
2. http://msdn.microsoft.com/en-us/library/dd264733%28VS.100%29.aspx

Welcome to Dynamic C#(20) - 어르신과 대화하는 법.

C# 2010. 5. 17. 09:00 Posted by 알 수 없는 사용자
- 어르신과 대화하려면 어케 해야 되는거임?

간단합니다. 말씀하시는 내용을 잘 경청하고, 대꾸는 딱 필요한 만큼만 하면 되는 거죠. COM은 아마 그동안 C#이 못마땅 했을 겁니다. VB.NET은 공손하게 필요한 말만 딱 하는데, C#은 'Type.Missing'이 어쩌고 저쩌고 주저리주저리 말이 많았기 때문이죠. C#도 이제 99년도 부터 출발했다고 볼때, 11년이나 되었으니 꽤 성숙한 셈이죠. 이제 예의를 갖추기 시작한 겁니다. 낄낄낄.


- C#의 버릇없던 옛 시절.

그러면 C#얼마나 버릇이 없었는지 아주 간단한 예제를 통해 알아보시죠.

using System;
using Word = Microsoft.Office.Interop.Word;

namespace ConsoleApplication3
{
    class Program
    {
        static void CreateIconInWordDoc()
        {
            var wordApp = new Word.Application();
            wordApp.Visible = true;

            object useDefaultValue = Type.Missing;

            wordApp.Documents.Add(ref useDefaultValue, ref useDefaultValue,
                ref useDefaultValue, ref useDefaultValue);
        }

        static void Main(string[] args)
        {
            CreateIconInWordDoc();
        }
    }
}


위 예제는 그냥 오피스 워드 창을 하나 띄우는 예제입니다. 그런데 평소에 오피스 COM 프로그래밍을 접해보지 못한 분이라면, 뭔가 특이한 점을 발견하셨을 겁니다. 'Type.Missing'같은 독특한 걸 object형 변수에 넣고, Add메서드에 여러번 반복해서 써넣기 때문이죠. 딱히 값을 리턴받아서 뭘 하는 것도 아닌 것 같고, Missing이라면 뭔가 없다는 것 같은데, 없는 값을 왜 저렇게 반복해서 넣어야 할까... 하는 생각이 드는 것이죠. 이제 COM이 왜 C#을 싫어했는지 아시겠나요? 말이 많았거든요.

지난 포스트에서 보셨듯이, C# 4.0은 다른 런타임과의 상호운용에 신경을 무척 많이 썼으며, 그중의 하나가 COM과의 상호운용이었습니다. C# 4.0에서는 COM과 대화할 때 좀더 말을 적게 하면서 예의를 갖춰서 대화를 하게 된 것이죠. 컴파일러가 COM 객체를 대상으로 작업하고 있다는 걸 눈치채는 순간, 컴파일러는 매개변수에 'ref'키워드를 안붙이고 메서드나, 인덱서, 프로퍼티에 넘길 수 있도록 해줍니다.

using System;
using Word = Microsoft.Office.Interop.Word;

namespace OfficeInteropExam2
{
    class Program
    {
        static void CreateIconInWordDoc()
        {
            var wordApp = new Word.Application();
            wordApp.Visible = true;

            object useDefaultValue = Type.Missing;

            wordApp.Documents.Add(useDefaultValue,
                useDefaultValue, useDefaultValue, useDefaultValue);
        }

        static void Main(string[] args)
        {
            CreateIconInWordDoc();
        }
    }
}


즉 C# 4.0에서는 위 처럼 ref를 빼고 작업할 수 있도록 도와줍니다. 그리고 컴파일러가 나중에 각 매개변수 앞에 ref를 붙여서 컴파일 하는 것이죠. 일종의 syntactic sugar인 것입니다. 그런데 여기에 지난 포스트까지 설명드렸던 Named and Optional Arguments를 이용하면 아예 매개변수를 생략할 수 있습니다.

using System;
using Word = Microsoft.Office.Interop.Word;

namespace OfficeInteropExam2
{
    class Program
    {
        static void CreateIconInWordDoc()
        {
            var wordApp = new Word.Application();
            wordApp.Visible = true;

            wordApp.Documents.Add();
        }

        static void Main(string[] args)
        {
            CreateIconInWordDoc();
        }
    }
}


지난 포스트까지 설명드렸듯이 아예 매개변수를 생략하면 기본값으로 설정된 값이 넘어가게 됩니다. 그러면, Add메서드의 각 파라미터에는 기본값이 어떻게 설정되어 있을까요? Add메서드의 정의를 보면 아래와 같습니다.

Document Add(ref object Template = Type.Missing, ref object NewTemplate = Type.Missing, ref object DocumentType = Type.Missing, ref object Visible = Type.Missing);

위에서 보시듯이, 기본값은 'Type.Missing'으로 설정되어 있습니다. 즉 매개변수를 생략하고 Add메서드를 호출하면, 기본값으로 Type.Missing이 넘어가고 컴파일러는 거기에 자동으로 ref를 붙여서 호출을 완성시켜 주는 것이죠. 지금은 겨우 파라미터가 4개정도라서 감흥이 없으실지도 모르겠지만, 파라미터가 30개정도 되는 메서드들을 자주만나다보면 아마 이 기능이 너무나도 고맙게 느껴지시겠죠? ㅋㅋㅋㅋ


- 마치면서

오늘은 아주 짧게 향상된 COM 프로그래밍에 대해서 알아봤습니다. COM이 안쓰이길 바랬음에 불구하고 많이 쓰이니 어쩔 수 없이 C# 4.0에서 COM에 대한 지원이 많이 들어갔다고 하는걸 보니, 역시 기술의 생명은 벤더에게만 달린게 아니라는 생각을 해봅니다. 그럼 오늘은 여기까지 하고 다음에 또 뵙죠!


- 참고자료

1. http://blogs.msdn.com/samng/archive/2009/06/16/com-interop-in-c-4-0.aspx
2. http://msdn.microsoft.com/en-us/library/dd264733%28VS.100%29.aspx

Hello Windows Azure / Windows Azure Platform의 이해

Cloud 2010. 5. 16. 15:00 Posted by 알 수 없는 사용자

안녕하세요. Visual Studio 2010 팀 블로그에 새로 참여하는 새내기, Visual C# MVP 남정현입니다. 데브피아 C# 포럼 SYSOP으로 활동 중이기도 하고, 데브피아 오프라인 세미나를 통해서 .NET 커뮤니티 개발자 여러분들과도 몇 번 인사드렸던 적이 있었는데, 이번에 VSTS 2010 팀 블로그에서 인사를 드리게 되었습니다. 반갑습니다. :-)

요즈음 클라우드 컴퓨팅에 관해서 이곳 저곳에서 이야기도 많이 회자되고 있는 것을 볼 수 있습니다. 2008년 후반에 Buzz Keyword로 급부상했던 아이템이 점차 트렌드로 바뀌어가고 있는 추세인데, 여러 유수 IT 기업들이 클라우드 컴퓨팅에 대한 다양한 해석과 아이디어를 더하여 자신들만의 플랫폼 구축에 열과 성을 다하고 있습니다. Microsoft에서도 지난 2월, 미국, 유럽, 일부 아시아 국가를 대상으로 Windows Azure Platform을 정식으로 런칭하였습니다.

VSTS 2010 팀 블로그를 통하여 제가 연재하게 될 Article Series인 "Hello Windows Azure"의 내용은, Windows Azure Platform에서 실행되는 응용프로그램을 Visual Studio 2010을 통하여 개발하는 과정을 주로 소개하는 내용으로, Visual Studio 2010 이외의 다른 IDE를 활용한 개발 방법 및 Windows Azure Platform 기반의 상호 운용성에 대한 주제는 저의 블로그 (http://www.rkttu.com/) - 또는 - 저의 개인 메일 (rkttu nospam rkttu dot com)을 통하여 질문, 토론, 의견 등을 받고자 합니다. 많은 관심 부탁드립니다.

Windows Azure Platform 개요

Windows Azure Platform은 Microsoft Data Center 위에서 실행되는 Public Cloud Platform으로, 응용프로그램을 호스팅하고 실행하는 기능을 제공하는 Windows Azure, 관계형 데이터베이스 (RDBMS) 데이터를 호스팅하고 제공하는 SQL Azure, 분산 컴퓨팅 기술의 구현과 Hybrid Cloud Platform의 구현을 가능하게 하는 AppFabric, 실시간으로 제공되는 동적 데이터 카탈로그를 구현할 수 있는 SQL Data Services (Codename: Dallas) 등으로 구성됩니다.

그 외에 부수적으로는 Windows Azure Platform과 조합하였을 때 훨씬 더 효과적으로 사용할 수 있는 Microsoft의 다른 Web SDK 및 온라인 서비스들 (예: Facebook SDK, Windows Live ID 및 Windows Live 관련 SDK)을 접목한다면 더욱 사용성이 편리하고 우수한 클라우드 기반 응용프로그램을 만드실 수 있습니다.

위의 그림에서 보여지는 것처럼, Windows Azure Platform은 핵심 인프라를 기반으로하여, End User들에게는 실제 클라우드 응용프로그램이 제공하는 서비스를 활용할 수 있게 하고, 개발자들에게는 Visual Studio 및 기존에 자신이 사용하던 IDE를 이용하여 Cloud Application을 개발할 수 있게 하고, 호스팅되는 Cloud Application의 재량이지만, SOAP, REST, XML-RPC 등의 통신 프로토콜을 통하여 기존의 On-Premise 기반 소프트웨어에서도 클라우드 응용프로그램의 이점을 누릴 수 있게함을 보여줍니다.

특히, Visual Studio는 이 블로그를 찾아주시는 여러분들께서 제일 많이 애용하시는 개발 도구일 것입니다. 익숙한 프로그래밍 언어 (C#, VB.NET, F#)들을 활용해서 최신의, 그리고 강력한 컴퓨팅 환경 위에서 구동 가능한 응용프로그램을 자유롭게 개발하실 수 있다는 것을 뜻합니다. 아직까지 국내에 정식 출시되지 않은 서비스이지만, Windows Azure가 실제로 어떻게 동작하고, 어떤 절차를 거쳐서 개발이 이루어지는지에 대해서 먼저 아실 수 있다면 그 또한 좋은 이득이 되지 않겠는가 하는 생각입니다.

Windows Azure에 대하여

아래 그림은 Windows Azure Platform을 이루는 주요 구성 요소들을 Layer별로 표현한 모식도입니다. 아래 그림에서 몇 가지 핵심적인 요소들을 살펴보겠습니다.


Windows Azure는 Windows Azure Platform에서 운영 체제에 해당하는 인프라 구성 요소입니다. 내부적으로 Windows Server 2008 R2 x64를 Guest OS로 사용하여, Hypervisor 및 Load Balancer를 기반으로 외부에서 발생하는 요청을 항상 고성능 / 실시간으로 처리하고 결과를 내보낼 수 있도록 돕습니다. Windows Azure는 응용프로그램을 게시하고 실행할 수 있는 Compute 서비스와, 인스턴스 및 응용프로그램의 개수나 종류에 관계없이 동일한 영속성을 부여하기 위한 Storage 서비스를 제공합니다.

Compute 서비스를 통하여 실행되는 Windows Azure 응용프로그램은 보통 여러 개의 역할 (Role) 구성 요소가 동시에 실행됩니다. Role 하나에는 하나 이상의 Virtual Machine Instance가 복제되어 서비스를 담당하게 됩니다. 이 때, Load Balancer가 하는 일은, 실행되는 Virtual Machine Instance의 네트워크 부하, CPU 부하 등을 측정하여 현 시점에서 제일 최적의 성능을 낼 수 있는 Instance가 처리를 대행하도록 연결해주는 일입니다. Compute 서비스를 통하여 실행할 수 있는 Role의 종류는 크게 두 가지인데, Web Role과 Worker Role이 있습니다.

Web Role은 IIS를 내장하고 있는 OS 템플릿을 바탕으로 구축됩니다. IIS를 이용하여 기본적으로는 ASP.NET 응용프로그램을 호스팅할 수 있게 되어있습니다. 여기에 IIS 7.x 이후부터는 Fast CGI를 지원할 수 있게 되어 PHP, Perl, Python 등 대중의 인기를 많이 받는 새로운 웹 프로그래밍 언어까지 더할 수 있고, 특별히 Windows Azure SDK for Visual Studio에서는 Windows Azure용 Fast CGI를 개발할 수 있는 템플릿도 제공하고 있어서 더욱 편리하게 웹 응용프로그램을 개발할 수 있는 것이 특징입니다.

Worker Role은 OS의 핵심 기능을 템플릿으로 하여 구축됩니다. 별도의 기본 서비스 없이, 최소한의 운영 체제의 기능을 이용하여 고속의 연산 처리나 백그라운드 응용프로그램 처리 작업을 개발할 때에는 유용하게 쓰일 수 있습니다.

012345678910


동일하게 복제된 여러 Virtual Machine Instance는, 장애나 Load Balancer의 판단으로 내부적으로 외부의 응답을 처리하는 Instance가 계속 바뀌게 됩니다. 이에 따라, 여러 Instance들이 동일한 내용을 보고 처리할 수 있게 하기 위해서는 외부적인 저장소가 필요한 데, 이 때 사용할 수 있는 것이 바로 Storage 서비스를 통하여 제공되는 Table, Queue, BLOB Storage입니다. Table Storage는 행과 열로 구성된 2차원 행렬 타입의 테이블 자료 구조를 구현하고, Queue Storage는 응용프로그램간 실시간 메시지 교환을 목적으로 사용하는 FIFO (First In First Out) 타입의 큐 자료 구조를 구현하며, BLOB Storage는 파일 크기에 관계없이 대용량의 BLOB (Binary Large Object)를 저장하고 로드할 수 있도록 한 것입니다. 특별히 BLOB Storage의 경우, BLOB Storage의 파일을 외부에서 안정적으로 다운로드할 수 있도록, CDN (Contents Delivery Network) 서비스와 연계하는 기능을 제공합니다. 이 글을 작성하는 시점에서 CDN 서비스와의 연동은 아직 CTP (Community Technology Preview) 단계에 머물러 있습니다.


그리고 최근에는 Windows 7, Windows Server 2008 R2 이후부터 본격적으로 Native Format으로 자리잡은 VHD File을 이용하여 Win32 I/O API를 통한 파일 입/출력을 가능하게 하는 Windows Azure Drive API가 새롭게 런칭되었습니다. Windows Azure Drive API를 이용하여 VHD를 생성하고, 마운트하는 기능을 제공하며, VHD 파일 내부적으로는 NTFS 파티션을 자동으로 생성합니다. Windows Azure Drive와 BLOB Storage 사이의 가장 큰 차이점을 꼽는다면, Windows Azure Drive는 CreateFile 같은 Win32 API로 파일 입/출력이 가능한 저장소이고, BLOB Storage는 외부에 파일을 직접 노출하거나, 서로 다른 시스템간에 파일을 교환할 수 있다는 것입니다. 상황과 조건에 따라서 적절한 방법을 택할 수 있을것입니다.

다음 시간에는

이번 첫 Article에서는 Windows Azure Platform에 대한 개요와 Windows Azure의 기본적인 구성 요소에 대해서 살펴보는 내용을 다루었습니다. 다음 시간에는 Windows Azure Platform 서비스를 신청하고 활용하는 방법을 소개하고, 몇 가지 실제 Windows Azure 기반 응용프로그램 및 개발 사례들을 소개하도록 하겠습니다.

감사합니다. 즐거운 하루 되세요. :-)

이미지 파일 인용: Microsoft Cloud Platform - David Chou / Microsoft

Welcome to Dynamic C#(19) - 위너 고르기.

C# 2010. 5. 13. 09:00 Posted by 알 수 없는 사용자

- 위너를 고르는 방식!

위너라니, 무슨 위너이야기 일까요? 넵. 메서드 오버로딩에서 호출에 맞는 메서드를 고를때, 어떤 경우에 어떤 메서드가 더 적합한지 고르는, 즉 메서드 오버로딩 중에서 위너를 고르는 거에 대한 이야기 입니다. 아마도 이런 경우는 생각보다 자주일어날 것 같은데요. 과연 컴파일러는 어떤 방식으로 위너를 고를까요? 키로? 얼굴로? 능력으로? 한번 알아보시죠.


- 첫번째 경우

일단 두 메서드의 파라미터 개수가 같다고 할때는 매개변수가 어떤 타입의 파라미터로 형변환하는게 더 나은것인지를 기준으로 판단합니다.

class C
{
    public void Foo(object o) { Console.WriteLine("object"); }
    public void Foo(int x) { Console.WriteLine("int"); }

    static void Main(string[] args)
    {
        C c = new C();
        c.Foo(10);
    }
}


그럼 위의 예제에서는 누가 위너가 될까요? 10은 정수니까, object로 형변환도 가능하고, int로도 형변환이 가능합니다. 하지만 정수는 object보다는 int로 형변환 되는게 더 적합한 형변환이죠. 그래서 여기서는 'Foo(int x)'가 위너가 됩니다. 'Foo(int x)' ㅊㅋㅊㅋ


- 두번째 경우

두번째 경우는 파라미터 배열이 끼어듭니다.

class C
{
    public void Foo(int x, int y) { Console.WriteLine("int twins"); }
    public void Foo(params int[] x) { Console.WriteLine("params"); }

    static void Main(string[] args)
    {
        C c = new C();
        c.Foo(10, 20);
    }
}


첫번째 예제를 약간 변형시킨 건데요. 컴파일러는 파라미터 배열을 보자마자 이 파라미터 배열을 확장시켜서 메서드의 시그니처를 'Foo(int x_1, int x_2)'같이 만들고 이 시그니처를 가지고 오버로딩의 후보군에 끼워넣는다고 합니다. 그런데 컴파일러가 파라미터 배열을 확장만 시키는게 아니라, 파라미터 배열에서 확장되었음을 표시한다고 하네요. 그리고 파라미터에서 확장된 시그니처의 경우는 오버로드 판별에서 2등급으로 취급된다고 합니다. 즉 메서드 시그니처가 동일할 경우에 파라미터 배열은 2등급이기 때문에, 다른 일반적인 1등급 파라미터에 우선순위에서 밀리게 된다고 합니다. 위의 예제에서는 'Foo(int x, int y)'가 위너네요. ㅊㅋㅊㅋ


- 세번째 경우

이제 드디어 지금까지 열심히 이야기 해온 옵셔널 파라미터가 끼어들 차례입니다.

class C
{
    public void Foo(int x) { Console.WriteLine("int"); }
    public void Foo(int x, int y = 0, int z = 10)
    { Console.WriteLine("optionals"); }

    static void Main(string[] args)
    {
        C c = new C();
        c.Foo(10);
    }
}


위 예제는 쫌 난감합니다. 'c.Foo(10)'라는 호출만 보자면, 둘다 똑같이 해당되기 때문이죠. 첫번째 Foo는 시그니처가 완전히 일치하고, 두번째 Foo도 x를 제외한 값은 모두 기본값이 사용될 수 있으니까요. 이 경우에는 파라미터 배열과 같이 옵셔널 파라미터를 2등급으로 취급한다고 합니다. 즉 첫번째 Foo메서드가 옵셔널 파라미터가 하나도 없기 때문에 첫번째 Foo가 위너가 되는 것이죠. 그런데 만약에 첫번째 메서드에도 옵셔널 파라미터가 있다면 어떻게 될까요?

class C
{
    public void Foo(int x, int y = 0) { Console.WriteLine("optional1"); }
    public void Foo(int x, int y = 0, int z = 10)
    { Console.WriteLine("optional2"); }

    static void Main(string[] args)
    {
        C c = new C();
        c.Foo(10);
    }
}


위와 같이 작성된 경우 말이죠.


이런 에러를 보게됩니다. 즉, 둘간의 차이를 분별해낼 수 없기 때문에 모호한 호출이라는 것이죠.


- FAQ!

옵셔널 파라미터는 그동안 아주 꾸준히 C#에 추가해달라고 요청하던 기능입니다. 특히 오피스같은 COM과 연동하는 작업을 하는 프로그래머들이 많이 요청을 했었습니다. 왜 이런 기능을 예전에 안하고 지금하느냐? 하는 질문이 있을 법한데요. C#개발팀의 Sam Ng가 답변한 내용을 옮겨볼까 합니다.

1. 왜 이걸 좀 더 일찍하지 않았냐?

- 왜 이걸 좀 더 일찍 하지 않았느냐 하면 말이죠. 우린 진짜 이 기능이 C#에 포함되지 않았으면 했습니다. 이걸 그동안 계속해서 미뤄온 건, 이건 우리가 원했던 패러다임이 아니었기 때문이었죠.

2. 그럼 왜 지금은 이걸 추가했느냐?

- 이게 다 COM 때문이죠. 진짜 이건 사라지지를 않더라구요! 이걸 없앨려고 노력했지만, 사람들은 계속 이걸 사용하고 있고, 앞으로도 계속 사용하려고 하더군요. C#이랑 COM이랑 무슨 관계냐구요? 오피스. 오피스 PIAs때문이죠. 오피스 PIAs는 대부분 30개정도의 파라미터를 갖는 메서드로 이루어져 있죠. 그 파라미터의 대부분은 옵션이구요. 대부분의 경우에는 한개정도의 매개변수만 적어주고 나머지는 다 기본값을 사용하면 되는거죠.

이제 Named and Optional Parameters를 통해서 옵션인 파라미터는 안적고도 메서드를 호출할 수 있죠. 오피스 메서드를 호출할때도 모든 매개변수마다 Type.Missing같은거 안적어주고도 호출할 수 있는거죠. 그리고 매개변수에 해당하는 파라미터 이름을 적을 수 있기 때문에, 딱 필요한 거만 매개변수로 넘겨주고, 나머지는 생략할 수 있죠.

그리고 ref 없이 COM을 사용할 수 있도록한 기능과 조합해서 사용하면 COM 코드가 더 간결해지고 지루한 작업은 매우 줄어들겁니다. 컴파일러가 옵션인 ref 파라미터에 넘겨줄 임시값을 만들어서 매개변수를 넘겨주거든요.

제가 예전에 이야기 했듯이 C# 4.0의 큰 테마중의 하나가 다른 런타임(COM, 동적언어 등)과의 상호운용이에요. 그런 테마가 이런 기능을 반드시 갖추도록 했던거죠.


- 마치면서

아~ 이제 Named and Optional Parameters에 대해서 할말은 다 한것 같습니다. 자료를 읽으면서 저도 많이 배우고 재밌는 내용도 많이 읽었네요. 여러분은 어떠셨나요? 호호호호-_- 그럼 오늘은 여기까지 하고~ 다음에 또 다른 이야기 가지고 오겠습니다!


- 참고자료

1. http://blogs.msdn.com/samng/archive/2009/04/17/named-and-optional-arguments-ties-and-philosophies.aspx

Welcome to Dynamic C#(18) - 이름을 붙이면서 벌어진 일들.

C# 2010. 5. 10. 09:00 Posted by 알 수 없는 사용자
- 무슨일인데 그러냥?

네. 언제나 기존의 질서안에서 새로운 변화를 가져오려고 하면 새로운 문제들이 생기기 마련이죠. 오늘은 매개변수에 이름을 붙이면서 생겨난 문제와 내용에 대해서 설명을 드려보려고 합니다. 우끼끼끼!!


- 이름은 다 어디서 가져오놔?

우선, CLR이 파라미터 이름은 메서드 시그니처의 일부로 보지 않기 때문에 A라는 메서드를 오버라이드해서 B라는 메서드를 작성한다고 할때, A와 B의 파라미터 이름이 다르다고 해도 전혀 문제되지 않습니다.

public class Animal
{
    public virtual void Eat(string foodType = "Rice")
    {
    }
}

class Monkey : Animal
{
    public override void Eat(string bananaType = "Rainbow Banana")
    {           
    }

    static void Main(string[] args)
    {
        Monkey m = new Monkey();
        Animal a = m;

        m.Eat(bananaType: "Black Banana");
        a.Eat(foodType: "Hamburger");
    }
}


위의 예제를 보시면, Animal을 Monkey가 상속해서 Eat을 오버라이드 하고 있습니다. 하지만 메서드 간의 파라미터 이름은 전혀 문제가 되지 않습니다. 그런데요, 이름이 틀리게 되면 또 하나의 의문점이 생길 수 있습니다. 언제 어떤 이름이 쓰이는 걸까요? 해답은 생각보다 간단합니다. 수신자를 중심으로 생각하는 것이죠.

여기서 수신자란 메서드의 호출대상이 되는 객체를 말하는데요. 'm.Eat()'에서는 m이 수신자가 되는 것이죠. 즉, 'm.Eat'에서 m은 정적타입인 Monkey타입의 객체입니다. 그래서 m.Eat에서는 Monkey클래스에 정의된대로, 'bananaType'을 파라미터의 이름으로 가져옵니다. 그리고 a.Eat에서는 Animal클래스에 정의된대로, 'foodType'을 파라미터의 이름으로 가져오는 것이죠. 주의깊게 보셨다면, m에서 생성한 객체를 그대로 a에 넣어주는 걸 보실 수 있습니다. 즉, 동일한 객체라는 의미인데요. 동일한 객체에 대해서라도, 수신자를 중심으로 파라미터의 이름을 가져온다는 이야기가 되는거죠.


- 이름을 가져올 때 안에서 벌어지는 일.

class Calc
{
    static double CalcRatio(double source,
        double factor1 = 0.87,
        double factor2 = 1.0)
    {
        return source * factor1 * factor2;
    }

    static void Main(string[] args)
    {
        CalcRatio(92.1, factor2:1.11);
    }
}


위와 같은 코드를 가지고 생각을 해보겠습니다. 실제로 위 코드가 실행될 때까지 어떤 일이 벌어지는지 말이죠. 우선 컴파일러가 'CalcRatio'를 호출하는걸 보게되면, 이름을 붙이지 않은 매개변수가 이름을 붙인 매개변수보다 앞쪽에 있는지 확인을 합니다. 그리고 매개변수에 붙인 파라미터의 이름이 중복되지 않는지 확인합니다. 같은 파라미터에 두개의 매개변수를 넘길수는 없으니 말이죠. 그리고는 적용가능한 모든 후보메서드군을 생성합니다. 위의 예제에서는 딱 한개뿐이죠. 그 후에는 각각의 후보메서드에 대해서 몇가지 검사를 합니다.

일단 메서드 호출에 적혀있는 파라미터의 이름이 후보군에 있는 메서드의 파라미터 이름과 동일한지 검사합니다. 여기서는 'factor2'의 이름을 각 후보메서드가 파라미터로 가지고 있는지 확인하겠죠. 그리고 이름이 붙은 모두 매개변수와 파라미터가 일치하는지 확인합니다. 즉, CalcRatio의 파라미터 중에 이름이 붙지 않은 매개변수를 받지 못한 factor1, factor2는 이름이 붙은 매개변수를 받아야 한다는 것이고, 같은 파라미터에 중복되는 매개변수가 없어야 한다는 것이죠.

만약에 이름이 붙은 매개변수나 이름이 붙지않은 매개변수 어느 것도 받지 못한 파라미터가 있다면, 컴파일러는 그 파라미터가 옵셔널 파라미터인지 검사합니다. 만약에 그 파라미터가 옵셔널 파라미터라면, 기본값을 파라미터에게 넘겨줄 매개변수로 사용하게 됩니다.

이런과정을 겨처서 매개변수 목록이 정리되면, 컴파일러는 늘 하듯이, 각 매개변수가 형변환에 문제가 없는지 확인을 합니다. 위의 예제에서 정리된 매개변수의 목록은 ['92.1', '0.87', '1.11']가 되겠죠.

이 모든 과정은 철저하게 컴파일 시점에서 벌어지는 'syntactic sugar'입니다. syntactic sugar는 그저 프로그래머의 수고를 덜어주는 역할을 하는 기능을 뜻하는 데요, 지금까지 설명드린 'Named and Optional Parameters'는 새로운 참조를 요구하지도 않고, 새로운 호환성 문제를 만들지도 않습니다. 생성된 IL을 보면, 그냥 일반적으로 호출하는 모양과 차이가 없기 때문이죠. 즉, 컴파일러가 위에서 설명드린 과정을 거쳐서 정리된 매개변수의 목록을 만들고 나면, 프로그래머가 원래 똑같은 매개변수목록으로 메서드를 호출한 것 처럼 처리를 합니다. 그래서 컴파일이 되고 난 후에, 메서드의 파라미터 이름이 바뀌거나 새로운 옵셔널 파라미터가 추가되어도 아무문제 없이 동작하는 것이죠.


- 중요한 거 한가지만 더!! 캬캬캬

class Calc
{
    static int GetNum1()
    {
        Console.WriteLine("GetNum1");
        return 1;
    }

    static int GetNum2()
    {
        Console.WriteLine("GetNum2");
        return 1;
    }

    static int GetNum3()
    {
        Console.WriteLine("GetNum3");
        return 1;
    }

    static void DoSth(int num1, int num2, int num3)
    {
    }

    static void Main(string[] args)
    {
        DoSth(num3: GetNum3(),
        num1: GetNum1(),
        num2: GetNum2());
    }
}


위와 같은 예제가 있다고 했을때요, 아마도 컴파일러는 매개변수의 순서를 재정렬해서 num1, num2, num3의 순서로 각 파라미터에 넘겨줄 것 같은데요. 메서드안의 GetNum시리즈는 어떤 순서로 평가될까요? 써있는 순서대로 앞에서 뒤로 할 것같다고 생각하셨다면 정답! 입니다. 처음에 GetNum3, GetNum1, GetNum2의 순서로 말이죠. 결과를 보시면 명확합니다.


내부적으로는 각 파라미터에 대한 표현식의 결과를 저장할 공간을 임시로 만들고, 각 표현식의 결과를 저장한 후에, 그 임시값들을 순서에 맞게 재정렬해서 파라미터에게 넘겨준다고 합니다. 재밌지 않나요? 저만 그런가효? 호호호호-_-;;;;


- 마치면서

이제 Named and Optional Parameter(도대체 한글로 뭐라고 써야할지 감이 안잡히네요-_-)에 대해서 기본적인 이야기는 한 것 같은데요. 처음에는 '그냥 파라미터에 기본값을 줄 수 있고, 매개변수를 넘겨줄 때 순서를 바꿔서 줄 수도 있다' 이정도 인줄 알았는데, 공부를 하다보니 생각보다 복잡하기도 하고 재미있는 내용이 많아서 글로 정리하면서도 즐거웠습니다. 오늘은 여기까지 하죠~~~~~!


- 참고자료

1.
http://blogs.msdn.com/samng/archive/2009/04/01/named-arguments-and-overload-resolution.aspx

Welcome to Dynamic C#(17) - 필요한 말만 하고 삽시다.

C# 2010. 5. 7. 09:00 Posted by 알 수 없는 사용자
- 이젠 dynamic을 벗어나서!

문득 제가 거의 1년 가까이 dynamic만 이야기를 했다는 걸 깨달았습니다.-_-;;;; C# 4.0에 dynamic만 추가된게 아닌데 말이죠;;; 반성을 하면서! 이제 dynamic말고 다른 이야기를 좀 하겠습니돠.


- 파라미터에 기본값을 설정하는거지.

프로그래밍을 하다보면, 여러가지 파라미터를 가지는 메서드를 작성하는 경우가 많은데요. 호출시에 꼭 매번 넘겨줘야 하는 파라미터가 있는가 하면, 대부분의 경우 그냥 한가지 값으로만 쭉~ 사용하는 파라미터도 있습니다. 그래서 C#에서는 늘~ 메서드 오버로딩을 통해서 파라미터가 축약된 메서드를 정의해주고, 그 메서드 안에서 원래의 메서드를 호출하면서, 기본값을 매개변수로 넘겨주는 형태를 취했습니다. 그래서 많은 프로그래머들이 메서드를 정의할 때, 파라미터에 기본값을 정의할 수 있게 해달라고 요청했습니다. 그리고 그 메서드를 호출할 때, 기본값을 그대로 쓰는 경우에는 매개변수를 생략할 수 있게 해달라고 말이죠.

C# 4.0에서 파라미터에 기본값을 설정하는데는 두가지 방법이 있는데요,

class C
{
    static int Add(
        [DefaultParameterValueAttribute(10)] int num1,
        int num2 = 30)
    {
        return num1 + num2;
    }

    static void Main(string[] args)
    {
        Console.WriteLine(Add(15));
    }
}


위의 코드를 보시면, num1에는 DefaultParameterValueAttribute라는 어트리뷰트가 붙어있는 걸 보실 수 있는데요. 이건 그냥 하위호완성과 COM, VB와의 상호운용을 위해서 만들어진거라고 합니다. 이 방법은 권장되지 않는데요, 왜냐하면 이 어트리뷰트는 특정 파라미터에 기본값이 있다는 건 명시하지만, 그 파라미터가 옵션인지 아닌지는 명시할 수 없기 때문입니다. 즉 컴파일러 조차 이 파라미터가 옵션으로 생략가능한지 인식하지 못합니다. 그래서 결국에는 명시된 기본값은 아예 사용되지도 못하는 거죠.

그리고 'num2' 파라미터를 보시면 이 부분은 두가지 의미가 있습니다. 첫째는 컴파일러에게 이 파라미터가 옵션으로 생략가능하다는 걸 알려주는 거구요, 둘째는 프로그래머가 이 파라미터에 아무 값도 안 넘겨주때 사용할 수 있는 기본값을 컴파일러에게 알려주는 것입니다. Add메서드를 호출할 때 인텔리센스를 보면요,


위에서 말씀드린대로, 'num1' 파라미터에 대해서는 기본값은 설정이 되어있지만, 옵션이라는 표시가 없기 때문에 생략할 수 없습니다. 그렇기 때문에 어트리뷰트에 명시된 기본값은 아예 사용되지 않는 것이구요. 그리고 'num2' 파라미터를 보시면, 기본값이 30이라고 표시되는 걸 보실 수 있습니다. 그리고 이렇게 표시된 파라미터는 옵션으로 생략가능한 것이구요. 위의 코드를 실행하면, 15와 'num2' 파라미터의 기본값인 30이 더해져서 45라는 결과가 나오게 됩니다.


- 파라미터에 기본값을 설정하면 어케 되는고얌~?

위의 Add메서드를 리플렉터에서 보면 아래와 같습니다.

private static int Add([DefaultParameterValue(10)] int num1,
                              [Optional, DefaultParameterValue(30)] int num2)
{
    return (num1 + num2);
}

'num1' 파라미터는 위에서 선언해준 어트리뷰트가 그대로 설정되어 있는 걸 보실 수 있구요. 'num2' 파라미터를 보시면, 기본값을 설정하는 게 실제로는 두가지 일을 한다는 걸 알 수 있습니다. 파라미터에 DefaultParameterValue 어트리뷰트를 통해서 기본값을 설정하고, 이 파라미터가 옵션으로 생략가능함을 나타내는 Optional이라는 표시도 하게 되는 것이죠. 이런 두가지 어트리뷰는 이미 CLR에 존재했다고 합니다. VB.NET에서는 이미 제공되던 기능이니깐 당연한 이야기겠죠.

여담이지만, VB.NET과 C#개발팀이 통합되었다고 합니다. 서로 같이 크면서 동일한 기능을 제공하고자 하는 'coevolution'전략을 위함이라고 하는데요. 그 덕분일까요? C#에도 오늘 소개해드리는 기본값을 설정하는 기능이 추가되었고, VB.NET에도 C# 3.0에서 추가되었던 '컬렉션 이니셜라이저'나 '자동으로 구현된 속성'같은 기능이 추가되었습니다. 자세한 내용은 여기를 참고하시면 되겠네요.

그런데 기본값을 설정할 때, 몇가지 규칙이 있는데요. 첫째로는 옵션으로 생략가능한 파라미터는 생략불가능한 파라미터를 모두 선언한 뒤에 나와야 합니다.


위와 같이 옵션인 파라미터가 먼저 나오고, 뒤에 반드시 필요한 파라미터를 선언하면, 옵셔널 파라미터는 반드시 꼭 필요한 파라미터들 뒤에 나와야 한다고 경고메세지가 뜨는 걸 볼 수 있습니다. 그리고 ref나 out으로 설정된 파라미터는 기본값을 설정할 수 없습니다. 왜냐하면, ref나 out에 대응되는 상수값이 존재하지 않기 때문이죠.

그리고 호출시에 옵셔널 파라미터로 설정된 파라미터에 매개변수를 넘겨주지 않으면, 컴파일러는 DefaultParameterValue 어트리뷰트에 설정된 기본값을 가지고 와서, 그 값을 매개변수로 해서 메서드를 호출하는데 사용하게 됩니다.


- 매개변수에게 이름을 허 하여뢋!

형을 형이라고 부르지 못하고, 아버지를 아버지라 부르지 못한 건 아니지만, 매개변수는 늘 이름없는 설움을 겪어야 했습니다. 파라미터는 이름이라도 갖고 있었죠. 태어나서 이름하나 세상에 남기지 못하는게 얼마나 슬픈....여기까지 하고 이야기 계속 하겠습니다.-_-;

이제 이름붙인 매개변수를 사용하게 되면, 이 모든 장점을 제대로 활용할 수 있게 됩니다. 만약에 모든 파라미터가 옵셔널 파라미터 라면, 그 중에 값을 넘겨주고 싶은 것들만 이름을 붙여서 매개변수를 넘겨줄 수 있는 것이죠. 기존의 오피스등의 COM 프로그래밍을 할 때, 대부분의 값들이 옵션으로 생략하능한 파라미터지만, C#에서는 그것들을 생략할 방법이 없어서 의미도 없는 값을 반복해서 넘겨줘야 했던 걸 생각하면 굉장히 편해질거라는 생각도 드네요.


이름붙인 매개변수는 메서드를 호출할 때 사용되는 데요. 예제를 보시면요,

class C
{
    static double CalcRatio(
        double fact1 = 90.0,
        double fact2 = 0.9887,
        double fact3 = 33.211)
    {
        return fact1 * fact2 * fact3;
    }

    static void Main(string[] args)
    {
        Console.WriteLine(
            CalcRatio(fact1:40.12, fact3:13.11)
            );
    }
}


위의 예제를 보면, 세상에 이런 엉터리 계산식이 있을진 모르겠지만 아무튼 뭔가의 비율을 계산하는 메서드인 CalcRatio가 있습니다. 계산할 때 일반적으로 고정된 상수들이 있을 수 있는데요, 그런 경우를 위해서 기본값을 설정해두었습니다. 그리고 호출하는 부분은 보시면, 'fact1', 'fact3' 파라미터에 넘겨질 값들에 각각 이름을 붙인 것을 볼 수 있습니다. '40.12'는 'fact1'파라미터에 넘겨질 매개변수이고, '13.11'은 'fact3'파라미터에 넘겨질 매개변수 인 것이죠.

매개변수에 이름을 붙이는 건, 꼭 기본값이 있는 옵셔널 파라미터에만 한정되는 않습니다.

class C
{
    static double CalcRatio(
        double superfactor,
        double fact1 = 90.0,
        double fact2 = 0.9887,
        double fact3 = 33.211)
    {
        return superfactor * (fact1 * fact2 * fact3);
    }

    static void Main(string[] args)
    {
        Console.WriteLine(
            CalcRatio(fact1:(40.12 / 2), fact3:13.11, superfactor:100)
            );
    }
}


위의 예제와 같이 기본값이 없는 일반 파라미터에 넘겨줄 매개변수에도 이름을 붙일 수 있으며, 매개변수의 순서는 파라미터의 순서와는 전혀 상관없이 배열할 수 있습니다. 그리고 'fact1'의 매개변수처럼 원래 매개변수로 넘겨줄 때 할 수 있는 것 처럼 아무 표현식이든지 올 수 있습니다.


- 마치면서

그동안 아주 오랫동안.... 게으름과 겹치면서 너무 오랫동안 dynamic에 대해서만 이야기를 해왔는데요. 문득 돌아보니 dynamic외에도 다룰 내용이 좀 더 있다는 걸 깨달았습니다.-_-;;;; 머리가 둔하면 이렇죠. 하하하하하>ㅇ<


- 참고자료

1. http://blogs.msdn.com/samng/archive/2009/02/03/named-arguments-optional-arguments-and-default-values.aspx

Welcome to Dynamic C#(16) - dynamic이라도 이건 안되는 거임.

C# 2010. 5. 5. 09:00 Posted by 알 수 없는 사용자
- Long time no see~

오랜만이죠~? 다행히 기다려주신 분이 없는 거의 없는 관계로 마음은 불편하지 않았습니다. 그런데 왜 눈물이 아흙.... 아직 못한 이야기가 조금 있는거 같아써! 조금 더 이야기를 하도록 할께영~ 호호호호-_-


- 상황1. dynamic타입의 변수에 들어있는 값을 변환시키기

다음과 같은 코드가 있다고 가정을 했을 때요,

static void Main()
{
    dynamic d = 10;
    d++;
}

어떤 일이 벌어질까요? d에는 11이라는 값이 있어야 할 것 같지만, dynamic타입은 실제로는 object타입이기 때문에 다른 결과가 나옵니다. dynamic타입이 실제로는 object라는 건 이전에 이야기 했던 내용인데요, 자세한 내용은 이전포스트를 참고하시면 되겠습니돠. 아무튼 처음에 d에 10을 넣을 때, int에서 object로 박싱이 일어나구요, 두번째 줄에서 d에서 언박싱한 값을 가지고 ++연산을 수행합니다. 하지만 이 값은 다시 박싱되어서 저장되지는 않는다는 게 문제입니다. 그러면 결과는 여전히 10을 가리키겠죠.

그런데 이런 문제는 런타임 바인더의 구조 덕분에 해결이 가능했다고 하는데요. 이전에 말씀드렸듯이 런타임 바인더는 동적인 구문을 적절한 객체와 연산으로 바인딩하고 그 결과를 Expression Trees의 형태로 DLR에게 리턴해줍니다. Expression Trees의 장점은 목표로 하는 형태로 변환되기 전에, 여러가지 변환이나 최적화가 용이하다는 점이 었는데요, Expression Trees에 박싱된 값을 언박싱하고 값을 변화시키는 요소가 있고, 그 값을 다시 박싱해서 저장하는 것도 있다고 합니다. 그래서 이런형태의 동적인 표현식을 제대로 처리할 수 있다고 하네요.


- 상황2. 중첩된 구조체 연산

이번 문제는 조금 더 알쏭달쏭한데요. '.'으로 여러번 연결된 표현식을 생각해보면요, 각각 부분별로 쪼개셔서 바운딩이 됩니다. 즉 A.B.C.D같은 표현식이 있다고 하면요, A.B에 대한 사이트를 만들고, 다시 그 결과를 .C의 수신자로 하는 사이트를 만들고, 다시 그 결과를 .D의 수신자로 하는 사이트를 만듭니다. 꽤나 현명하게 잘 만든거 같다는 생각이 들긴하는데요. 원래 컴파일러가 하는거랑 같은 방식이기도 하구요. 그런데 문제는 런타임의 구조상 ref형식으로 값을 리턴할 수 없다는 제약때문에 생깁니다. 물론 이런 제약은 CLR의 제약은 아닙니다. 다만 닷넷의 언어중에서 ref 리턴을 지원하는 언어가 없기 때문인데요. 그 말은 만약에 값형 변수에 대해서 연속적으로 '.'으로 연결된 표현식이 있다면, 대상이 되는 변수의 값은 박싱이 되면서 복사본이 생깁니다. 그리고 이후에 '.'로 연결된 것들은 그 복사본을 대상으로 연산이 수행된다는 것이죠. 예제를 보시면요,

public struct S
{
    public int i;
}

class D
{
    public S s;
    static void Main(string[] args)
    {
        dynamic d = new D();
        d.s = default(S);
        d.s.i = 10;
        Console.WriteLine(d.s.i);
    }
}


10이 결과로 찍힐거라고 예상할 수 있지만, 결과는 아래와 같습니다.


앞에서 설명드린대로, 'd.s.i = 10'에서 구조체 S가 박싱되면서 복사본이 생겼고, 그 복사본의 i에 10을 대입했기 때문에, 원래의 'd.s'의 i값에는 변화가 없는 것이죠. 이 문제는 참고하고 있는 Sam Ng의 2008년 12월 15일자 글에서 어떻게 할지 고민중이라고 적혀있었는데요. 출시된 VS2010에서 확인해본 결과 아무런변화가 없어서, 그대로 두기로 결정한 것으로 보입니다. 뭐 결국 핵심은 dynamic은 object랑 비슷하기 때문에 박싱이 일어난다는 점입니다.


- 상황3. 명시적으로 구현된 인터페이스의 메서드

우선 명시적으로 구현된 인터페이스의 메서드가 뭔지 부터 이야기를 해야 할 것 같습니다.



위 그림을 보면, S가 IEnumerable를 구현한다고 선언을 한 상태인데요. 인터페이스를 구현하는 방식에 'Implement Interface'와 'Implement Interface Explicitly'가 있는 걸 볼 수 있습니다. 전자가 우리가 흔히 인터페이스를 구현할 때 써온 암시적 인터페이스 구현이구요. 후자가 여기서 말씀드릴 명시적 인터페이스 구현입니다. 우선 위의 두 경우에 코드 모양이 어떻게 틀린지 확인해보도록 하지요.

public class S : IEnumerable
{
    public int i;

    #region IEnumerable Members

    public IEnumerator GetEnumerator()
    {
        throw new NotImplementedException();
    }

    #endregion
}

- 암시적 인터페이스 구현의 경우

public class S : IEnumerable
{
    public int i;

    #region IEnumerable Members

    IEnumerator IEnumerable.GetEnumerator()
    {
        throw new NotImplementedException();
    }

    #endregion
}

- 명시적 인터페이스 구현의 경우

틀린 점을 발견하셨나요? 첫째로 명시적 인터페이스 구현의 경우, 메서드 이름인 GetEnumerator앞에 인터페이스의 이름이 '.'과 함께 붙어있습니다. 이 메서드가 어떤 인터페이스를 통해 구현된 건지 명시적으로 보여주는 것이죠. 그리고 한정자가 없으므로 private입니다. 그러므로 이 메서드는 인터페이스를 구현했지만 밖으로 노출이 되지 않습니다. 이렇게 명시적인 인터페이스 구현을 하는 경우에는 몇가지가 있을 수 있는데요. 이에 대한 더 자세한 설명은 유경상 수석님의 글을 참고하시면 매우 자세하게 아실 수 있습니다. 그럼 명시적 인터페이스 구현에 대한 설명은 여기까지로 하구요, 이게 dynamic과 무슨 관련이 있는지 알아보도록 하겠습니다. 아래와 같은 예제가 있다고 할때 말이죠,

public interface IFoo
{
    void M();
}

class C : IFoo
{
    #region IFoo Members

    void IFoo.M()
    {
        Console.WriteLine("C.M()!!");
    }

    #endregion
}

class D
{       
    static void Main(string[] args)
    {
        dynamic d = new C();

        d.M();
    }
}


예상으로는 "C.M()!!"이라는 메세지가 출력될 것 같기도 한데요. 그런데 앞서 말씀드렸던 명시적 인터페이스 구현의 특성 때문에, 런타임에 클래스C에서는 M이라는 이름의 메서드를 찾을수가 없다고 합니다. 그래서 런타임 바인더가 호출에 대해서 묶을 수 있는 IFoo라는 타입을 찾을 수 없다고 하네요. 이런 문제 때문에 위의 코드는 아래와 같은 에러를 내게 됩니다.


C라는 타입에 M의 정의가 없다는 에러메세지를 내면서 호출은 실패하게 됩니다.


- 마치면서

이제 비주얼 스튜디오 2010이 정식으로 출시되면서 C# 4.0에 대한 이야기도 현실과 매우 가까운 이야기가 되었습니다. 다만, 제 능력부족으로 글의 내용은 비현실적인거죠-_-;; 아무쪼록 도움이 되셨기를 바라면서 오늘은 여기서 끗~!


- 참고자료

1. http://blogs.msdn.com/samng/archive/2008/12/15/dynamic-in-c-vi-what-dynamic-does-not-do.aspx
2. http://www.simpleisbest.net/archive/2008/06/23/2423.aspx

Welcome to Dynamic C#(15) - A/S for dynamic.

C# 2010. 5. 3. 09:00 Posted by 알 수 없는 사용자

- 긴급 A/S 출동!

제가 글을 쓰면서 공부했던 내용들이 많이 바뀌었습니다-_- 그래서 저도 좀 뒤늦게 그 업데이트를 확인했구요~ 그래서 바뀐 내용에 대해서 A/S들어갑니다~~!


- 오버로드 판별 A/S!

아마 이 시리즈를 관심있게 읽어주신 분이라면!... 아흙 왜 또 눈물이... 아무튼 읽어주신 분이라면 유령메서드라는 걸 보셨을텐데요. 그게 너무 복잡하다보니, 유령메서드랑 관련한 내용은 하나도 채택되지 못했다고 합니다. 그리고 굉장히 간단한 규칙으로 정리를 했습니다. 만약에 어떤 메서드를 호출할 때 dynamic타입의 매개변수가 끼어있다면, 그 메서드에 대한 호출은 동적으로 디스패치된다는 것입니다. 그리고 런타임에서 dynamic타입의 매개변수의 실제값에 맞는 타입을 선택한다고 합니다.

class C
{
    static void M(dynamic d) { Console.WriteLine("dynamic"); }
    static void M(string s) { Console.WriteLine("string"); }
    static void M(int i) { Console.WriteLine("int"); }

    static void Main(string[] args)
    {
        dynamic d = "test";
        M(d);
    }
}


위와 같은 예제를 보면, M에 대한 호출은 런타임에 디스패치 됩니다. 그리고 어떤 M이 선택될지는 d의 실제값에 달여있는데요, 여기서는 d의 실제값이 string이기 때문에, 'M(string)'이 선택됩니다. 그러면, M(dynamic)은 어떻게 되는 걸까요? 바뀐 규칙에서 파라미터에서 dynamic타입이 있는 건, object타입이 있는것과 동일한 의미를 갖습니다. 즉 적합한 오버로드가 없을 때, 가장 나중에 선택이 되게 되겠죠.


- 대입형변환 A/S!

dynamic이 끼어있는 형변환 규칙도 약간의 변화가 있는데요, 그 규칙은 아래와 같습니다.

1. 모든 타입에서 dynamic타입으로 암시적 형변환이 가능하다. 기본적으로 object로 암시적 형변환이 가능한 타입이라면, dynamic으로도 암시적 형변환이 가능하다.

2. dynamic타입에서는 dynamic과 object를 제외한 다른 어떤 타입으로도 암시적 형변환이 불가능하다.

3. 하지만, 모든 '동적 표현식'은 다른 모든 타입으로 암시적 형변환이 가능하다.

4. 만약에 어떤 타입의 차이점이 dynamic과 object뿐이라면, 서로간에 암시적 형변환이 가능하다.

2, 3번에 대해서 좀 더 들여다 보면요, 타입의 형변환과 표현식의 형변환은 과연 뭐가 틀린걸까요? 일단 표현식의 형번환은 이때까지 '대입 형변환'이라고 불렀던 것을 가능하게 해줍니다. 예제를 하나 보면요.

string s = d;

이 형변환은 3번 규칙 때문에 성립합니다. 이 형변환은 동적 표현식에서 string으로의 형변환이지, dynamic타입에서 string으로의 형변환이 아니기 때문이죠. 이외에도 return, foreach, 프로퍼티에 값설정하기 등의 대부분의 경우는 이렇게 처리됩니다.

그렇다면 dynamic타입에서 string타입으로 형변환이 없어야 하는 이유는 뭘까요? covariance를 통해서 컴파일러가 이 형변환을 못하게 막는 걸 한번 확인해보겠습니다.

class C
{
    static void Main()
    {
        IEnumerable<dynamic> ied = null;
        IEnumerable<string> iei = null;
        var x = M(ied, iei);           
    }
    static T M<T>(IEnumerable<T> x, IEnumerable<T> y) { return default(T); } 
}

위 예제는 문제없이 컴파일 됩니다. 그리고 메서드의 타입유추 과정을 통해서 T를 dynamic이라고 골라내는 과정을 거칠텐데요, 그 과정은 아래와 같습니다.

1. 첫번째 매개변수는 IEnumerable<dynamic>이다. 그러므로 dynamic은 T의 후보가 된다.

2. 두번째 매개변수는 IEnumerable<string>이다. 그러므로 string은 T의 후보가 된다.

3. T의 후보군인 { dynamic, string }을 가지고 고민하는 과정에서 둘 사이의 관계를 보는데. string에서 dynamic으로의 형변환은 있지만, 그 반대는 성립하지 않는다. 그러므로 좀 더 일반적인 타입인 dynamic을 고르게 되는 것이다.

위 과정의 3번 단계에서 만약 dynamic에서 string으로 형변환이 가능했다면, 둘 중에 어떤 타입이 더 일반적인 타입인지를 결정할 수 없게 됩니다. 그렇다면 메서드 타입유추는 모호환 둘의 관계 때문에 실패하게 되겠죠. 그게 바로 dynamic에서 다른 타입으로의 형변환이 금지되어야 하는 이유입니다.

그리고 대입형변환이라는 용어는 아마 살아남지 못한 것 같습니다. C# 4.0 명세서에서 그 용어를 발견할 수 없었기 때문입니다. 그래도 혹시나 싶어서 Chris Burrows에게 질문을 남겨놨으니, 답이오면 반드시 알려드리겠습니다.


- 마치면서

앞서서 열심히 공부하고 적었던 내용이 바뀌고, 또 어떤 내용은 아예 통채로 날아가 버리니 아주 상쾌하네요~! 하하하-_- 그런데 바뀐 내용을 보니깐, 훨씬 간단하고 깔끔해지고, 더 이해하기 쉬워진 것 같아서 좋네요. 그럼 오늘은 여귀까쥐!


- 참고자료

1. http://blogs.msdn.com/cburrows/archive/2010/04/01/errata-dynamic-conversions-and-overload-resolution.aspx

[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 를 참고해서 만들었습니다.

Welcome to Dynamic C#(14) - 철지난 만우절에 낚여서 파닥파닥.

C# 2010. 4. 8. 09:00 Posted by 알 수 없는 사용자
- 뭔소리여

Eric Lippert가 만우절에 쓴 글에 아주 제대로 낚였습니다. C# 4.0이 정식으로 나오기 직전인데, 급하게 추가된 연산자가 있다더군요. 자바에 얼마전에 이런 기능이 추가되어서, C#의 우위를 유지하기 위해서 급하게 추가해서 가장 최근버전의 CTP버전에서 확인할 수 있다는 말이었습니다. 추가된 연산자는 '-->'랑 '<--'인데, 전자는  '~로 향해가는'이라는 의미이고, 후자는 '~로 접근하는'이라는 의미라고 합니다. 무슨 말인고 하니....

int x = 10;
// this is read "while x goes to zero"
while (x --> 0)
{
    Console.WriteLine("x = {0}", x);
}

위의 코드에서 while문안의 식은 x가 0에 도달할때까지라는 의미를 갖습니다.

int x = 10;
// this is read "while zero is approached by x"
while (0 <-- x)
{
    Console.WriteLine("x = {0}", x);
}

위의 코드에서는 0이 x에 의해서 접근되어질때까지(즉, x가 0으로 접근할 때까지)의 의미를 갖습니다.


제가 귀가 얇아서 일까요? 순간 '혹'해서, '좋은데? 낄낄낄'하고 생각하면서 글을 읽어나갈 무렵, 마지막 줄이 눈에 들어왔습니다. '만우절인데 얼레리꼴레리 속았지? 속았지? 우헤헤헤헤헤헤'. 저는 낚인줄도 모르고 신나게 파닥파닥 거린셈이죠. 호호호호호호. 글을 보니, C# QA팀의 테스터한명도 낚여서 파닥거렸다고 합니다. 그리고 참고로, '-->'이 연산자는 몇년 전부터 돌아다니는 쫌된 유머라고 하네요. 즉, 'x-- > 0'은 'x --> 0'과 동일하다는 거죠..... 아흙.

namespace Console1
{
 class Program
 {
  public static void Main(string[] args)
  {
   int x = 10;
   
   while(x --> 0)
   {
    Console.WriteLine("{0}",x);
   }
   
   Console.Read();
  }
 }
}

위의 코드는 아래와 같은 결과가 나오고, while문 안의 조건을 'x-- > 0'으로 바꿔도 결과는 동일합니다.




- 마치면서


사실 저도 이글을 가지고 여러분을 낚아볼려는 생각을 가지고 글을 쓰기 시작했는데, 그랬다가 많은 분들의 호응(?)을 받아서 교훈을 얻을까봐, 소개해드리는 정도로 마쳤습니다. 제가 참 좋아하는 블로거이고, 또 배우러 자주가는 블로그에서 낚일줄은 생각도 못했네요-_- 


- 참고자료

1. http://blogs.msdn.com/ericlippert/archive/2010/04/01/SomeLastMinuteFeatures.aspx

똑똑한 검색을 지원하는 VSTS 2010의 "Navigate To" 검색

Visual Studio 2010 2010. 3. 15. 08:30 Posted by 알 수 없는 사용자

VSTS 2010에는 이전보다 더 지능적인 코드 검색을 위해 “Navigate To”라는 강력한 기능을 제공합니다.

 

“Navigate To”를 사용하려면 “Ctrl 키 + , 키”를 누르면 아래와 같은 다이얼로그 창이 나옵니다.

 

 


“Clear”을 입력하면 아래와 같은 결과가 나옵니다.

단순하게  “Clear”이 있는 위치만 알려주는 것이 아니고 Type, 메소드/프로퍼티 이름, 필드 선언, 파일 이름을 포함한 모든 것을 보여줍니다.

 

Result에서 표시된 항목에서 찾기를 원했던 것을 마우스 클릭을 하면(아니면 Tab 키로 이동하여 선택) 해당 코드를 보여줍니다.

 

 

 

 

기억이 안 나는 단어는 “fuzzy 검색으로 찾기

 

“fuzzy 검색이라는 것은 완전한 단어를 알지 못하지만 일부 단어만을 사용하여 검색 하는 것을 가리킵니다.

 

GetStreamID 라는 함수를 찾아야 하는데  “Stream” 이라는 단어만 생각난다면 이것을 입력하면 아래와 같이 출력됩니다.

 “Stream”이라는 단어를 사용한 모든 것을 다 보여줍니다.

 

 

 

“Pascal Casing” 규약으로 검색

 

닷넷 프레임워크에서는 type이나 메소드의 이름을 “Pascal Casing” 규약으로 짓기를 권유합니다. “Pascal Casing” 방식이라는 것은 여러 단어가 합쳐서 하나의 이름이 되는 것은 해당 단어의 첫 글자를 대문자로 하는 것입니다. “get”“stream”, “id”라는 단어를 붙여서 하나의 메소드 이름을 만든다면 “GetStreamID”로 됩니다.

 

“GetStreamID”라는 단어를 “Pascal Casing” 패턴으로 찾을 때는 “GSI”라는 단어만 입력하여 찾을 수 있습니다.

 

그런데 현재 RC 버전에서는 VC++의 경우는 제대로 지원되지 않습니다. VC++의 경우는 조금 더 입력을 해야 찾아집니다.

“GetStreamID”를 예를 들면 “GStrame”으로 검색을 하면 “GetStreamID”를 찾습니다.

[JumpToDX11-11] DirectCompute 를 위한 한걸음!

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


앞선 시간을 통해서 GPGPU 를 위해서 마이크로소프트가 제공하는 플랫폼이
DirectCompute 라는 것이라고 말씀드렸습니다.
앞으로 DirectX11 을 지원하는 모든 그래픽카드들은 이 DirectCompute 를 지원할 것입니다.
그 이외에도 일부 DirectX10 을 지원하는 그래픽카드들도 지원을 하고 있습니다.


GPGPU 를 위해서 가장 기본적이고 핵심이 되는 기능은 무엇일까요?
저는 GPU 에서 처리된 메모리를 CPU 쪽의 메모리로 보내는 것이라고 생각합니다.
( 이는 개인 의견입니다.^^ )
즉, 그래픽카드에 있는 메모리를 메인메모리로 보내는 작업입니다.
DirectX9 세대까지는 이 작업이 불가능 했습니다.
예를 들면, 그래픽스 파이프라인 중간에 처리된 결과를 다시 가공할 수 있는 방법은
VertexShader 나 PixelShader 같은 쉐이더 스테이지 정도 뿐이였습니다.

하지만 DirectX10 부터는 이들에 대한 중간 결과를 메인메모리로 보내는 기능이 추가되어지면서,
GPGPU 의 시작을 알렸다고 생각합니다.
이 단순한 Copy 작업이 앞으로도 얼마나 유용하게 사용될 수 있을지는 기대가 상당합니다.



< DirectCompute 를 위한 ComputeShader >

DirectCompute 를 위해서 개발자가 할 일은 ComputeShader 를 작성하는 일입니다.
ComputeShader 는 HLSL 이라는 기존 DirectX 의 쉐이더 문법 구조로 작성을 합니다.




HLSL 코드는 DirectX 쉐이더 컴파일러인 FXC 나 API 를 통해서 컴파일 됩니다.
HLSL 은 결국 최적화된 IL 코드를 생성하게 되고,
이 IL 코드를 기반으로 런타임에 각각의 하드웨어에 최적화된 명령어들로 변환
되어져서 실행됩니다.


< GPGPU 에게 실행이란? >

GPGPU 를 활용해서 실행한다는 것은 하드웨어 내부적으로 어떻게 동작하도록 할까요?
앞선 시간에 GPU 는 병렬 처리에 최적화된 많은 SIMD 형태로 구성되어져 있다고 언급했었습니다.
결국 이들은 스레드들의 그룹으로써 실행합니다.
스레드들을 얼마나 많이 생성할 것인지를 개발자가 정해주면, 그에 맞게 연산을 수행합니다.

API 에서는 이들을 큰 그룹으로 나누어 줍니다.
큰 그룹으로 나누어 주는 API 는 ID3D11DeviceContext::Dispatch() 입니다.

ipImmediateContextPtr->Dispatch( 3, 2, 1 );

이렇게 큰 블럭 단위로 나누고 난 후에
ComputeShader HLSL 에서는 이들을 세부적인 스레들로 분할하는 문법을 지정합니다.

[numthreads(4, 4, 1)]
void MainCS( ... )
{
        ....
}




결과적으로 위의 그림처럼 스레드들이 생성되어서 병렬적으로 실행이 됩니다.
위에 나열된 숫자들은 스레드 ID 로써의 역활을 합니다.
즉, 어떤 스레드의 ID 가 MainCS 함수에 파라메터로 넘오오면,
그 ID 를 통해서 해당 버퍼에 값을 작성하게 됩니다.

아래에 간단한 예가 있습니다. 

[numthreads( 256,1,1) ]

void VectorAdd( uint3 id: SV_DispatchThreadID )
{

  gBufOut[id] = gBuf1[id] + gBuf2[id];

}


아무리 스레드들이 복잡하게 동작하더라도, 위와 같이 ID 를 통해서 제어한다면
그 어떤 작업도 문제없이 할 수 있습니다.

일단 먼저 어떻게 DirectCompute 가 실행되어지는지에 대해서 살펴보았습니다.
실행까지 가기 위해서는 일련의 절차를 거쳐야 합니다.
이들에 대해서는 앞으로 차근차근 살펴보겠습니다.



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

[MFC] 리스타트 매니저(Restart Manager) - (3/3) : 활용하기

MFC 2010. 2. 1. 10:00 Posted by 알 수 없는 사용자

Intro
안녕하세요. MFC 카테고리의 꽃집총각 입니다.
지난번 [MFC] 리스타트 매니저(Restart Manager) - (2/3) : 사용하기 편에 이어서 오늘은 리스타트 매니저 시리즈의 마지막인 ‘활용하기’ 편입니다. 이번 시간에는 실제로 리스타트 매니저의 동작을 확인해 볼 수 있는 샘플을 만드는 과정을 정리하고, 실제 동작에 관련된 이슈들을 몇 가지 정리해 보겠습니다. 


예제 프로그램작성 – 1. MFC Application Wizard 설정 하기.

자, 우선은 VIsual Studio 2010을 열어서 Ctrl + Shift + N 을 힘차게 누질러서 새 프로젝트 생성 창을 띄우고, MFC Application을 선택해 MFC 어플리케이션 위자드(Application Wizard;응용 프로그램 마법사)를 띄웁니다.

(그림 1) MFC Application을 선택해 새 프로젝트를 생성합니다.

어플리케이션 위자드가 뜨면 왼쪽 메뉴에서 Application Type을 선택하고, 기본 비주얼 스튜디오(Visual Studio) 형식으로 되어 있는 프로젝트 스타일 항목을 오피스(Office) 형식으로 변경해 줍니다. 비주얼 스튜디오 스타일의 여러 가지 도킹 pane들은 이번 예제에는 크게 쓸모가 없으니까, 괜히 걸리적 거리기만 하거든요 ^^;


(그림 2) MFC Application Wizard > Application Type 항목 설정.

Document Template Properties 항목을 선택하고, 파일 확장자(File extension) 란에 임의의 확장자를 입력해줍니다. 파일 확장자를 명시하게 되면 별도의 코딩을 추가하지 않아도 생성된 프로젝트에 자동 생성되는 소스코드 부분에 Document의 저장/로딩 처리가 추가됩니다. 파일 확장자를 입력하면 오른쪽에 있는 필터 이름(Filter name)칸도 자동으로 채워집니다. 저는 custom-document-format의 뜻으로 cdf라고 적어봤어요.

( 그림 3) MFC Application Wizard > Document Template Properties 항목 설정.

그리고 가장 중요한 부분! Advanced Features 항목에 가서 리스타트 매니저 지원 사항들을 확인합니다. 리스타트 매니저와 관련된 세 개의 체크사항들이 기본적으로 체크되어 있기 때문에 수정을 할 필요는 없지만 그래도 올바로 체크되어 있는지 확인해야 겠지요. 


(그림 4) MFC Application Wizard > Advanced Features 항목 설정.

자, 이제 마지막으로, Generate Class 페이지로 가서 뷰 클래스의 부모 클래스를 CView –> CEditView로 변경해 줍니다. CEditView를 상속받은 클래스는 기본적으로 뷰 영역이 메모장 프로그램처럼 캐럿이 깜박이는 에디트 컨트롤 형식으로 되어있어서, 별다른 처리 없이도 문서의 저장을 확인할 수 있는 문자열 형식의 데이터를 입력할 수 있게 됩니다.


(그림 5) MFC Application Wizard > Generated Class 항목 설정.

이제 어플리케이션 마법사의 모든 설정이 끝났습니다. Finish를 눌러 프로젝트를 생성합니다.


예제 프로그램작성 – 2. 크래시 발생 버튼 추가하기.

리스타트 매니저가 올바르게 동작하는지 알아보기 위해서는 프로그램이 비정상 종료 되어야 합니다. 샘플 프로그램의 리본 UI에 간단하게 패널 하나와 버튼 하나를 추가하고, 핸들링 함수를 추가해서 아래와 같이 잘못된 포인터 연산을 하는 코드를 넣어줍니다. 어플리케이션 위자드에서 오피스 형식의 Application Type을 선택하셨다면 아마 기본적으로 리본 UI가 붙어있을겁니다. 그렇지 않다면 툴바든, 마우스 입력이든 상관 없이 아무 이벤트나 받아서 고의적으로 예외를 발생시키는 코드를 넣어주면 됩니다.

  
(그림 6) 리본에 누르면 터지는 버튼을 넣어줍니다.

 

 예제 프로그램 작성 – 3. 문서 자동 저장 간격 조절하기.
지난번에 리스타트 매니저 사용 팁을 정리하면서, 문서 데이터 자동 저장 기능의 기본 저장 간격 시간은 5분이라고 말씀 드린 적이 있습니다. 우리는 매우 바쁜 사람이니까, 이 시간을 당겨보죠. 이왕이면 프로그램을 띄우고 나서 바로 확인할 수 있도록 아주 짧게 잡아보겠습니다. 한 10초 정도면 나쁘지 않겠죠?

(그림 7) CWinApp 파생클래스의 생성자에서 m_nAutosaveInterval의 값을 10000 미리세크(=10초)로 설정해줍니다.

시간 설정에 대한 내용은 지난회 포스팅이었던 [MFC] 리스타트 매니저(Restart Manager) - (2/3) : 사용하기 편을 참고하시면 됩니다.

 

예제 프로그램 실행!
이제 빌드를 하고 프로그램을 실행시켜서 에디트 뷰에 블라블라 테스트용 잡담을 늘어놓은 후, 10초가 지나 임시 문서가 만들어졌을 만한 충분한 시간을 제공한 뒤에 ‘누르면 폭발!’ 버튼을 야심차게 눌러주면 프로그램이 크래시가 나고 리스타트 매니저가 동작하면서 프로그램을 다시 띄워 주겠지요? 하지만 실제로 실행해보시면 아마 리스타트 매니저는 커녕 그냥 프로그램만 죽어버리고, 프로그램을 닫든지 디버깅을 하든지 니 맘대로 하라는 쓸쓸한 대화상자만 출력되고 말 겁니다.


(그림 8) 크래시가 났지만, 재시작도 문서 복구도 아무 것도 일어나지 않았습니다.
우리는 대 사기극에 휘말린 걸까요?


(그림 9) ‘그래, 내컴은 닷넷 디버거가 깔려 있어서 그럴 거야! 일반 사용자들은 이렇지 않겠지!’
라는 일말의 희망도 부질없습니다. 닷넷 미설치 PC에서는 위와 같은 창이 출력됩니다.

이 시점에서 몇 가지 더 알아두어야 할 것이 있습니다. 우리의 샘플 프로그램에서 리스타트 매니저가 동작하지 않는 이유와 함께, 실제로 리스타트 매니저를 사용하려고 할 때 알아두면 좋은 몇가지 정보들을 함께 정리해 보도록 하겠습니다.

  

(중요*) 리스타트 매니저 사용시 고려사항.

  1. 우리가 살펴보고 있는 MFC 10.0의 리스타트 매니저 기능이 실은 첫 번째 포스팅에서 설명 드렸던 것처럼 윈도우 비스타 시스템에서 선보인, OS 차원에서 제공되는 기능입니다. MFC 10.0에서는 이 기능을 좀 더 쉽게 사용할 수 있게끔 추가처리를 지원하는 것입니다. 예를 들자면 win32 GDI의 DC(Device Context)와 MFC의 CDC 클래스 관계 정도가 되겠네요. MFC의 리스타트 매니저도 내부 구현으로 들어가면 비스타 이후 OS에서 지원하는 윈도우 API인 RegisterApplicationRestart, RegisterApplicationRecoveryCallback 등의 함수를 사용해 재시작 및 문서 복구 기능을 제공하고 있습니다. MFC를 사용하지 않은 일반 Win32 윈도우 프로그램이나, 콘솔 프로그램에서 까지도 재시작 및 복구 기능을 사용할 수 있습니다. 하지만 MFC를 이용하면 보다 쉽게 사용할 수 있게 되는 것이지요.
  2. 프로그램 재시작 기능의 핵심이 되는 RegisterApplicationRestart 함수는 사용시 한가지 주의해야 할 사항이 있습니다. 만약 프로그램의 초기화 코드에 오류가 있어서 인스턴스가 실행되자마자 크래시를 내는 상황인데 리스타트 매니저가 동작한다면 어떻게 될까요? 아마 프로그램은 뻗고, 실행되고, 다시 뻗고, 다시 실행되는 무한 재실행을 반복하게 될겁니다. 이런 경우를 피하기 위해서 비스타의 응용 프로그램 재시작 기능은 초기 재시작 후 60초 동안은 크래시로 인한 비정상 종료가 있었다고 해도 동작하지 않습니다. 우리가 실행시킨 샘플 프로그램은 구동한지 60초가 지나지 않았기 때문에, 재실행 기능이 실행되지 않았던 겁니다. 보다 자세한 내용은 MSDN(http://msdn.microsoft.com/en-us/library/aa373347(VS.85).aspx)에서 확인하실 수 있습니다.
  3. 자동 저장되는 문서 파일의 경로는 CDataRecoveryHandler::GetAutosavePath 함수로 알아낼 수 있습니다. 프로그램을 실행하고 해당 경로에 가보면 실제로 자동 저장되는 버전의 문서파일을 확인하실 수 있습니다. 우리의 샘플 프로그램은 10초마다 착실하게 문서를 잘 저장하고 있네요 :) 임시 파일 저장 경로의 변경은 CDataRecoveryHandler::SetAutosavePath 함수로 가능합니다.

 

 이제 예제를 통해 리스타트 매니저 동작을 직접 확인해 봅시다.
이제는 정말로 리스타트 매니저의 놀라운 동작을 우리 눈으로 직접 확인할 시간입니다. 준비는 이미 다 끝났습니다. 단지 리스타트 매니저가 실행될 수 있도록 예제 프로그램 구동 후 60초의 시간을 기다려 주기만 하면 됩니다.


(그림 10) 리스타트 매니저의 동작을 한 눈에 파악하기 위해
 모두 저장된 파일, 반만 저장된 파일, 저장 안 된 파일을 준비.

60초의 시간이 흐르는 동안 저는 위의 (그림 10)과 같이 세 개의 문서 파일을 준비했습니다. 저장한 문서창 하나, 저장한 후 추가로 내용을 추가한 문서창 하나, 그리고 저장하지 않고 내용만 적은 문서창 하나. 곧 리스타트 매니저가 이들을 각각 제대로 복구해 주는지 실제로 확인해 보겠습니다.
그리고 실제로 우리가 설정한 10초의 간격으로 문서 파일이 저장되는지 확인해 보겠습니다. 저는 윈도우 7을 사용하고 있는데, 임시 문서 기본 저장 경로가 C:\Users\(윈도우계정명)\AppData\Local 으로 잡혀있네요. 윈도우 탐색기로 해당 경로를 열어보면 실제 10초 단위로 갱신되고 있는 임시 저장 파일들이 보입니다.


(그림 11) 착실히 저장되고 있는 자동 임시 저장 파일들.

이제 미리 만들어 두었던 리본 UI의 크래시 버튼을 눌러 프로그램을 비정상 종료 시킵니다. 프로그램이 비정상 종료된 후, 자동으로 재시작 되면서 문서를 임시 저장된 버전으로 복구할 것인지를 묻는 대화상자가 출력됩니다.


(그림 12) 크래시 발생후 리스타트 매니저가 자동으로 응용 프로그램을 재시작.


(그림 13) 응용 프로그램 재시작 후, 임시 저장된 버전의 문서를 복구할 것인지 묻는 대화상자 출력

그림 13의 문서 복구 여부 확인창이 떴을 때 ‘자동 저장 문서 복구’ 항목을 선택하면, 크래시가 나기 전에 저장해 두었던 문서의 텍스트들이 그대로 모두 복구되는 것을 확인할 수 있습니다. 임시 버전으로 복구된 파일들은 파일명이 출력되는 탭 부분에 [복구됨] 이라는 표기가 붙어서 출력됩니다.


(그림 14) 리스타트 매니저의 '자동 복구 기능'으로 복구된 문서파일의 모습.

 

Outro
이번 포스팅 에서는 리스타트 매니저를 직접 동작시키고 확인해 볼 수 있는 예제 작성 방법과 몇 가지 주의 사항을 짚어 보았습니다. 첨부된 이미지들이 많아서 괜히 길어 보이긴 하지만, 어려운 내용은 없었으리라 생각합니다.
이것으로 3회에 걸친 리스타트 매니저 강좌를 마치고, 다음에는 MFC 10.0에서 새롭게 선보이는 CTaskDialog 클래스에 대한 강좌를 가지고 다시 찾아 뵐 예정입니다.
그럼 다음 강좌에서 뵙도록 할게요.
감사합니다 ^^*

 

Reference

[JumpToDX11-10] GPGPU 를 위한 DirectCompute.

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


아주 오래 전 컴퓨터에는 GPU 라는 개념이 특별히 존재하지 않았습니다.
그저 화면에 얼마나 많은 픽셀을 나타낼 수 있는가 정도가 그래픽 카드의 성능을 나타내는 기준이였습니다.
그랬던 상황이 오늘 날에 이르게 된 것입니다.( 굳이 자세히 언급할 필요가 없을 것 같습니다.^^ )

오늘날의 GPU 의 성능은 가히 놀라울 정도입니다.
하지만 이런 놀라운 성능을 가진 GPU의 processing unit 들이 대부분의 시간을 놀면서 있다는 것이
우리의 신경에 거슬렸던 것입니다.
그래서 이들에게 일감을 분배시키기 위한 방안을 생각하게 되었고,
이를 배경으로 등장한 것이 바로 GPGPU 입니다.

GPU 를 활용한 일반적인 처리 방식을
GPGPU( General-purpose computing on graphics processing uints ) 라고 합니다.
범용성 있게 GPU 를 활용해서 처리하겠다는 것이지만,
사실 CPU 와 GPU 의 목적은 엄연히 다릅니다.

CPU 는 광범위한 영역에서도 효율적으로 이용될 수 있도록 설계를 된 것이지만,
GPU 는 그래픽 처리를 위한 산술 연산에 특화된 processing unit 입니다.
오늘 날 PC 는 멀티코어 형식이 많아지고 있는 추세인데,
하나의 CPU 는 기본적으로 특정 시간에 하나의 연산만 수행할 수 있습니다.
GPU 의 경우에는 병렬처리 형식에 완전히 특화된 형태입니다.
오늘날 GPU의 코어는 32개라고 합니다.
즉 32개가 연산이 동시에 실행될 수 있다는 얘기입니다.
아래 그림을 한번 보실까요?




GPU 에는 SIMD 라는 것이 굉장히 많은 것을 볼 수 있습니다.
SIMD( Single Instruction Multiple Data ) 라는 것은 병렬 프로세서의 한 종류입니다.
벡터 기반의 프로세서에서 주로 사용되는데,
하나의 명령어를 통해서 여러 개의 값을 동시에 계산할 수 있도록 해줍니다.
( http://ko.wikipedia.org/wiki/SIMD  --> 여기서 참고 했습니다^^ )

벡터 기반이라는 사실에 우리는 주목할 필요가 있습니다.
GPU 는 광범위한 목적으로 설계된 processing unit 이 아닙니다.
즉, GPGPU 를 활용하는 목적은 주로 수치 연산에만 국한된 이야기 입니다.
일반적인 로직으로 GPGPU 를 활용하는 것은 그리 좋은 선택이 아니라는 것입니다.
현재 GPGPU 가 활용되고 있는 영역은 이미지 프로세싱, 비디오 프로세싱, 시뮬레이션 등과 같이
많은 수학 연산이 필요한 영역입니다.
분명한 것은 이들 수치 연산에 국한된 모델이라 할지라도, 그 성능이 무척 매력적이라는 것입니다.

이런 GPGPU 활용을 위해서 마이크로소프트는 어떤 준비물을 가지고 등장했을까요?
그것이 바로 'DirectCompute' 라는 것입니다.^^
아래 그림을 한번 보실까요?



DirectCompute 외에도 친숙한 이름이 보이시나요?
개인적으로 현재 GPGPU 분야에서 가장 앞서 있다고 보여지는 CUDA 가 있습니다.
이것들에 대한 우열을 가리기는 어려운 문제입니다.
여러분이 처한 상황에서 최선의 선택을 하면 되는 것입니다.
그 중에 DirectCompute 도 하나의 선택지일 뿐입니다.
CUDA 도 굉장히 훌륭한 GPGPU 모델입니다.
( 사실 저도 CUDA 를 공부하면서 GPGPU 의 개념을 잡았습니다.^^ )
CUDA 는 제가 지금 언급하지 않아도 될 정도로 많은 정보들이 공개되어 있습니다.

DirectCompute 는 마이크로소프트에서 가지고 나온 GPGPU 모델입니다.
앞으로 OS 의 강력한 지원을 가지고 등장하게 될 것입니다.

사실 GPGPU 와 DirectCompute 는 매우 혼란스럽게 사용될 수 용어들입니다.
그래서 오늘은 이들 두 용어를 확실히 구분하는 것으로 마무리 하겠습니다.^^
다음 시간부터는 DirectCompute 에 대해서 조금씩 살펴보겠습니다.


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

Welcome to Dynamic C#(13) - 아직도 가야할 길.

C# 2010. 1. 20. 09:00 Posted by 알 수 없는 사용자
- 제목이 표절인거 같은데...?

넵. 존경해 마지 않는 스캇 펙의 아직도 가야할 길을 요즘 감명깊게 읽고 있기 때문만은 아니구열. dynamic키워드로 아직도 써야 할 내용이 남아 있기 때문에, 한번 써봤습니당. 역시, 프로그래밍 언어의 현대적인 패러다임을 따라잡는 건, 단순히 사용하는 패턴만 익히는 게 아니라는 걸 다시한번 깨닫게 되네요. 그럼그럼~ 계속해서 한번 가보시져!


- 프로퍼티

d.Foo를 예로들면, d는 dynamic객체이고, Foo는 d속에 살고 있는 멤버 변수나 프로퍼티입니다. 컴파일러가 이런 구문을 만나면, 우선 Foo라는 이름을 payload속에다가 기록합니다. 그리고 런타임에게 d의 실제 타입을 찾아서 연결(바인드)해달라고 요청합니다.
payload : 캡슐화를 통해서 제공되는 컴퓨터 프로그램이나, 데이터 스트림속에서 사용자의 정보등을 나타내는 부분(출처 : http://en.wikipedia.org/wiki/Payload). 여기서는 C# 런타임 바인더가 해당 구문을 제대로 바인드하기 위해서 필요한 정보를 기록해 놓는 데이터 구조를 뜻합니당.

그리고 이런 프로퍼티는 항상 3가지경우 중 한가지경우에서 쓰이는데요. 값을 읽어오거나, 값을 대입하거나, 둘다 하거나(+=같이). 컴파일러가 사용된 모양을 보고, 어떻게 사용하려고 하는지도 payload에 같이 기록합니다. 즉, 읽기만 하는 경우에는 해당 프로퍼티는 읽기전용으로 기록을 하는 식으로 말이죠.

그리고 컴파일러는 이런 접근이 필드에 접근하는건지, 프로퍼티에 접근하는건지 딱히 구분하지 않습니다. 그건 나중에, 런타임이 구분을 하게됩니다. 그리고 컴파일할때, 이런 구문의 리턴 타입은 dynamic으로 설정됩니다.


- 인덱서

인덱서는 두가지로 생각해볼 수 있습니다. 첫번째는 매개변수가 있는 프로퍼티, 두번째는 배열이나 리스트같은 집합의 이름을 통한 메서드 호출. dynamic과 연관지어서 생각할때는 후자가 훨씬 도움이 됩니다. 메서드의 경우와 같이 인덱서도 정적으로 바운드 될 수 있지만, dynamic타입의 매개변수가 주어지고, 그 매개변수가 dynamic타입을 받는 인덱서로 정적 바운드가 되지 않는 경우, 오버로드 판별 과정에 유령이 끼어들게 됩니다. 그래서 인덱서의 수신자(receiver)는 정적타입이지만, 매개변수가 dynamic타입이라서 런타임에 늦은 바인딩이 일어나게 됩니다. 말로 설명하니깐, 깝깝하시죠? 실력부족으로 더 이상 말로는 깔끔하게 설명을 못드리겠네요-_-;; 예제로 설명을 드리면요.

public class C
{
    public int this[int i]
    {
        get
        {
            return i;
        }
    }

    public int this[dynamic d]
    {
        get
        {
            return d;
        }
    }
   
    static void Main(string[] args)
    {
        C c = new C();
        Console.WriteLine(c[5]);
        dynamic d = 7;
        Console.WriteLine(c[d]);
    }
}


위와 같은 코드를 보시면, C에 인덱서가 두개가 있습니다. 하나는 int를 매개변수로, 하나는 dynamic을 매개변수로 받죠. 그리고 Main메서드 안에서 하나는 int를, 하나는 dynamic타입의 매개변수를 넘겨주고 있습니다. 이 경우에 두번째 인덱서는 언제 어떻게 바인드될까요? 이경우는 비록 d가 타입이 dynamic이지만, 인덱서의 오버로드중에, 매개변수를 dynamic타입으로 받는 인덱서가 있습니다. 그래서 컴파일하는 시점에 "c[d]"이 인덱서 호출은 "public int this[dynamic d]"이 인덱서로 바인드 됩니다. 정적바인드가 되는거죠.

그런데, 만약에 dynamic을 받는 오버로드가 없다고 한다면 어떻게 될까요? 인덱서 호출을 받는 수신자는 c이고 c의 타입은 정적 타입인 C입니다. 하지만, 매개변수가 dynamic이죠. 그런데, dynamic과 일치하는 오버로드가 없습니다. 그래서 이때, 지지난 포스트에서 설명드렸던, 유령이 끼어들게 되는거죠.

메서드 처럼 생각하는게 편하다는 말씀은 드렸지만, 사실 프로퍼티와 유사한 면도 있습니다. 인덱서호출 역시 payload에다가 읽기, 쓰기등을 어떻게 하는지 기록합니다. 그래서 C# 런타임 바인더가 그 정보를 바탕으로 바인드할 수 있도록 말이죠. 그리고 인덱서의 리턴타입 역시 컴파일하는 시점에서는 dynamic으로 간주됩니다.


- 형변환

지지난 포스트에서 설명을 드릴때, dynamic은 다른 타입으로 암시적 형변환은 안되지만 되는 경우가 있다고 설명을 드렸었습니다. 그리고 지난 포스트에서 사실 그런 형변환이 대입 형변환이라는 설명도 드렸구요~. 형변환의 경우는 payload가 매우 단순해집니다. 왜냐면, 컴파일러는 이미 어떤 타입으로 형변환을 하려고 하는지 알고 있기 때문이죠. 그래서 컴파일러는 그냥 payload에 형변환 하려고 하는 타입을 기록하고, 런타임 바인더에게 가능한 모든 대입 형변환(형변환 연산자를 쓰는 경우에는 명시적 형변환도 같이)을 시도해보라고 이야기 해줍니다. 물론, dynamic타입이 아니라, 런타임에 결정될 실제 타입에서 목표 타입으로 시도해보겠죠.

형변환의 경우는 다른 모든 경우와 다르게 컴파일하는 시점에서 dynamic이 아닌 형변환의 목표타입을 리턴합니다. 위에서 말씀드렸듯이 이미 어떤 타입으로 형변환하려고 하는지 알 수 있게 때문이죠.


- 연산자

연산자는 초큼 특이합니다. 그냥 아무생각없이 훑어보면, 동적인 뭔가가 일어난다고 느끼기 힘들기 때문이죠. 그런데, d+1 같은 간단한 구문도 런타임에 바인드 되어야 합니다. 그 이유는 사용자정의 연산자가 끼어들 수 있기 때문입니다. 그래서, dynamic 매개변수를 갖는 모든 연산은 런타임에 바인드됩니다. +=나 -=같은 연산자도 포함해서 말이죠.

컴파일러는 연산자를 보면, d.Foo += 10 같이 멤버에 대입하는 연산이 있는지 혹은, d += 10 같이 변수에 대입하는 연산이 있는지 확인합니다. 그리고 그 과정에서 d를 ref를 통해 넘겨서 변경된 값이 유지되어야 하는지 확인합니다.

그리고 마지막으로 d.Foo += x 같은 구문이 있을 때, d.Foo가 바인드결과 delegate나 event타입이라면, 앞의 구문은 이벤트 수신자 추가 같은 적절한 메서드를 호출하도록 컴파일러가 연결해줍니다.


- 델리게이트 호출

데일게이트 호출은 메서드와 굉장히 유사합니다. 딱 한가지 틀린 점이 있다면, 호출되는 메서드의 이름이 명시되지 않는다는 것 뿐이죠. 그래서, 아래 예제의 두 호출은 모두 런타임에 바인드됩니다.

public class C
{
    static void Main(string[] args)
    {
        MyDel c = new MyDel();
        dynamic d = new MyDel();

        d();
        c(d);
    }
}


첫번째 호출은 매개변수가 없는 호출을 런타임에 바인드하게 됩니다. 런타임 바인더가 런타임에 호출의 수신자가 델리게이트 타입이 맞는지 확인하고 해당 델리게이트 시그니처와 일치하는 호출이 있는지 오버로드 판별을 통해서 찾게 됩니다.

두번째 호출은 매개변수가 dynamic타입이기 때문에, 런타임에 바인드됩니다. 컴파일러가 컴파일시점에서 c의 타입이 델리게이트라는 걸 확인할 수 있지만, 실제 오버로드 판별은 런타임에 가서 끝나게 됩니다.


- 마치면서

이제야 저는 dynamic에 대한 내용들이 머리속에서 아주 조금 자리를 잡은 듯한 느낌이네요. 저도 이런데 혼란스러웠던 지난 포스트를 보신 분들은 더 하시겠죠-_- 최대한! 최대한! 앞으로도 열심히 적겠습니다. 그럼 다음포스트에서 뵙죠~.

- 참고자료

1. http://blogs.msdn.com/samng/archive/2008/12/11/dynamic-in-c-v-indexers-operators-and-more.aspx

Welcome to Dynamic C#(12) - dynamic은 외로운 아이.

C# 2010. 1. 18. 09:00 Posted by 알 수 없는 사용자

- 뭐시 외로운 아이여? 스타아닌겨?

네. 확실히 C# 4.0의 가장 큰 키워드는 Dynamic이기 때문에, dynamic은 스타일지도 모르겠습니다. 하지만, 화려한 모습뒤에 감쳐진 그들의 일상사는 때때로 자살같은 비극적인 사건을 통해서 세간에 알려지곤 하죠. 그럴때마다 세삼스럽게 사람들은 화려한 일상뒤의 모습은 변비때문에 우울해하는 것 같이, 보통사람과 전혀 다르지 않음을 재확인 합니다..... 왜 이런 헛소리를 또 하고 있을까요-_-;;; 아무튼. dynamic은 초큼 외로운 아이입니다. 증거를 제시해드리죠.



그림1. 출처 : http://blogs.msdn.com/cburrows/archive/2008/11/06/c-dynamic-part-iv.aspx

네. 다른 타입들은 System.Object로 부터 아주 사이좋게 이리저리 연결되어 있습니다만, dynamic은 천상천하유아독존입니다. 그저 혼자 있을 뿐이지요. 어린이집에서도 유별난 애들은 꼭 걔네들 기분에 잘 맞춰줘야 해서 선생님들이 고생을 하기도 하는데요. dynamic역시 독특한 면을 갖고 있습니다. 지난 포스트에 이어서 dynamic의 형변환 룰에 대해서 알아보면 아래와 같습니다.

1. dynamic에서 dynamic으로 동일한 형변환이 가능
2. 모든 참조형 타입에서 dynamic으로 암시적인 참조형변환이 가능
3. 모든 값형 타입에서 dynamic으로 암시적인 박싱형변환이 가능
4. dynamic에서 모든 참조형 타입으로 명시적인 참조형변환이 가능
5. dynamic에서 모든 값형 타입으로 명시적인 언박싱 형변환이 가능
리스트1. dynamic에서 다른 타입으로 형변환 가능여부.

  아래부분에 줄로 그어 버린 부분은 정식버전이 출시되면서 개념이 바뀐 부분들입니다. 그리고 밑에서 설명드리는 '대입형변환'이라는 용어도 여전히 의미가 있는지 불분명합니다. 다만, C# 4.0 명세서에 보면 '대입형변환'이라는 개념이 없는 걸로 봐서는 설명을 위해서 도입한 개념이 아닌가 싶은데요, 이에 관해서 Chris Burrows에게 질문을 남겨놨는데요, 답이 오면 바로 업데이트 하겠습니다. 바뀐 내용에 대해서는 이 글을 참조하시기 바랍니다.

매우 직관적으로 보이긴 하지만, 좀 생각해보면 이상한 점들이 발견됩니다. 그 첫번째가 바로 dynamic에서 object로 암시적인 형변환이 없다는 사실인데요. 위에서 1, 4번에서 언급했듯이 dynamic에서 dynamic을 제외한 모든 참조형타입으로 암시적인 형변환이 없다고 하고 있습니다. 그 이유는 연산을 하는 도중에 둘을 구분해내기가 매우 어렵기 때문이라고 합니다. 근데, 아래와 같은 코드가 컴파일 되고 실행되는 걸 확인할 수 있습니다.

dynamic d = null;
object o = d;

이건 분명히 암시적 형변환 처럼 보이는데, 왜 이게 컴파일이 되는 걸까요? 사실, 두번째줄은 암시적 형변환이 아니라 대입 형변환입니다. 대입 형변환은 또 뭘까요?

그림 2. 출처 : http://blogs.msdn.com/cburrows/archive/2008/11/11/c-dynamic-part-v.aspx

위 그림을 보시면, 형변환이 총 3가지로 분류가 되는 걸 확인하실 수 있습니다. 대입 형변환은 명시적 형변환과 암시적 형변환의 사이에 위치해 있는데요. 모든 대입 형변환은 사실 명시적 형변환이며, 모든 암시적 형변환은 대입 형변환 인거죠. 왜 이런 걸 새로 도입했어야 할까요? 사실 C# 4.0작업을 하면서 dynamic에서 다른 타입으로 암시적 형변환 도입을 검토했었다고 합니다. 그런데, 이렇게 하게 되면 문제가 생기는데요. 바로, dynamic을 통해서 아무타입에서 아무타입으로 형변환이 가능하기 때문입니다. 이 문제를 오버로드 판별을 예로 들어서 설명해보겠습니다.

public class C
{
    public static void M(int i){}

    public static void M(string s){}
   
    static void Main(string[] args)
    {
        dynamic d = GetSomeDynamic();
        C.M(d);
    }
}

코드1. 만약 암시적 형변환이 가능하다면?

위와 같은 코드가 있다고 할때요, 과연 어떤 메서드가 실행되어야 할까요? dynamic에서 모든 타입으로 암시적 형변환이 있다면, dynamic에서 int도 dynamic에서 string도 가능한 상황이 됩니다. 물론, dynamic에서 object같이 dynamic에서 int보다 더 나은 걸 찾을 수도 있겠지만, 그렇지 않은 경우가 훨씬 많이 발생하게 됩니다. 이런 모호함 때문에 dynamic에서 다른 타입으로 형변환을 할때는 명시적으로 선언을 하게 제한을 둔 거죠.

그렇다면, 대입 형변환은 또 뭘까요?

1. dynamic에서 모든 참조형 타입으로 대입 참조 형변환이 가능
2. dynamic에서 모든 값형 타입으로 대입 언박싱 형변환이 가능
리스트2. 대입 형변환의 설명

[리스트1]에서 4,5번을 보면 명시적 형변환에 대해서 이야기 하고 있죠? 사실은 그 명시적 형변환이 바로 이 대입 형변환을 말하는 겁니다. 모든 대입 형변환은 명시적 형변환이라고 말씀드렸던 걸 떠올리시면 고개가 절로 끄덕끄덕....교회 다니시는 분들은 교회로 끄덕끄덕 하실겁니다. 그리고 리스트2의 모든 형변환과 암시적 형변환을 모두 합하면 바로 대입 형변환이 되는 거죠.


- 그래그래 어디 계속 해봐.

대입이 일어나는 곳이 바로 컴파일러가 대입 형변환을 시전하는 곳입니다.

dynamic d = GetSomeDynamic();
Worksheet ws = d; //대입 형변환

이거 말고도, 대입 비스무리한 것들은 모두 대입 형변환을 사용합니다. return과 yield 그리고 프로퍼티와 인덱서, 배열 초기화구문, 그리고 foreach나 using같은 구문말이죠.

return d; //return할 타입으로 대입 형변환
yield return d; //반복자의 타입으로 대입 형변환
foo.Prop; //Prop의 타입으로 대입 형변환
foo[1] = d; //인덱서의 타입으로 대입 형변환
bool[] ba = new bool[] { true, d }; //bool로 대입 형변환
foreach(var x in d) {} //IEnumerable로 대입 형변환
using (d) {} //IDisposable로 대입 형변환
리스트3. 대입 형변환이 어디어디서 끼어드는지!

하지만, 이런 대입 형변환을 사용하지 않는 곳 중에 하나가 오버로드 판별입니다. [코드1]에서 보셨듯이 만약에 메서드를 호출하는데 대입 형변환을 적용하게 되면, 매번 메서드를 호출할때마다 모호함때문에 캐고생을 하게 될겁니다. 하지만, 대입 형변환을 적용하지 않는다고 해도, [코드1]은 제대로 컴파일 되지 않을거 같습니다. 왜냐면, d의 타입인 dynamic에서 C의 두 오버로드가 받는 파라미터 타입인 int와 string으로 형변환이 불가능 하기 때문입니다. 하지만, 이런 코드가 컴파일 되고 잘 돌아가야만 하니깐, 바로 지난 포스트에서 언급했던 유령 메서드가 끼어들게 되는거죠.


- 마치면서

어찌 퍼즐 조각이 좀 맞아 들어가시나요? 저도 글을 쓰면서 다시한번 자세히 읽다보니 퍼즐조각이 조금씩 맞아들어가는 느낌이 드는데요. 꼭 무슨 그것이 알고싶다에서 사건 조사하는 거 같은 기분이네요-_-. 그럼 다음 포스트에서 뵙져!!!


- 참고자료

1. http://blogs.msdn.com/cburrows/archive/2008/11/06/c-dynamic-part-iv.aspx
2. http://blogs.msdn.com/cburrows/archive/2008/11/11/c-dynamic-part-v.aspx

Welcome to Dynamic C#(11) - The Phantom of The Dynamic

C# 2010. 1. 14. 09:00 Posted by 알 수 없는 사용자
  이 포스트에서 소개해드리는 유령메서드는 정식버전에서는 없어진 개념입니다. 너무나도 복잡한 과정을 거쳐야 하기 때문에, 과정을 단순화 시키느라고 기존에 논의하던 개념들을 채택하지 않았다고 하는데요. 이 포스트는 예전에 이런 내용이 논의되었었다하는 정도로 봐주시구요. 실제 dynamic과 관계된 메서드 오버로딩 판별에 대해서는 이 글을 참조해주시기 바랍니다.

- 빙산의 일각!


사실 처음에는 그냥 단순히.. dynamic이란게 추가됐으니, '아 이거 쵸낸 신기하구나', '아 난 귀찮은 거 싫어하는데 이거 때매 이런거 편해지겠구나'하는 생각으로 접근을 했었는데요. dynamic이라는 키워드가 하나 추가되면서 생긴 많은 양의 추가사항들과 이야기들이 빙산 아래쪽으로 숨어 있었네요. 근데, 개인적인 게으름으로 아직 제대로 이야기 드리지 못하는거에 대해서 죄송하게 생각하구요-_- 일단, 원문을 한번 걸러서 약간의 창작을 보태는 수준에 불과하지만 계속해서 최선을 다해서 전달해드리고자 합니다.(저도 궁금하긴 하거든요-_-) 따쓰한 피드백!! 크하하하-_-


- 유령...뭐...?

유령 메서드(the phantom method)는 이 포스트시리즈의 바탕이 되는 Sam Ng의 포스트에서 언급이 되는 용어인데요. 컴파일러가 초기 바인딩단계에서 정적으로 해결할 수 없는 동적 바인딩을 해야하는 경우 사용하는 메서드를 말합니다. 즉, 실체는 없지만 컴파일러 내부에서 문제해결을 위해서 사용한다고 볼 수 있겠죠.

public class C
{
    public void Foo(int x)
    {
    }
   
    static void Main(string[] args)
    {
        dynamic d = 10;
        C c = new C();
        c.Foo(d);
    }
}

위와 같은 코드가 있다고 할때요, Foo의 호출을 바인드하기 위해서 컴파일러는 오버로드 판별 알고리즘을 통해서 C.Foo(int x)같이 딱들어맞는 후보메서드가 포함되어 있을 후보군을 만듭니다. 그리고 매개변수가 변환가능한지를 판단합니다. 그런데, 아직 dynamic의 변환가능성에 대해서는 이야기를 해본적이 없으므로~ 일단, 그거에 대해서 먼저 이야기 해보도록 하겠습니다.

우선, 빠르고 간단하게 정의를 내려보자면, "모든 타입은 dynamic으로 변환이 가능하고, dynamic은 어떤 타입으로도 형변환 할 수 없습니다." 말이 안된다고 생각하실 수 있습니다. 그럼 그 의문을 풀기 위해서 형변환을 고려할 수 있는 상황들을 생각해보기로 하죠.
(주석) 원문에서 "everything is convertible to dynamic, and dynamic is not convertible to anything"이라고 하고 있는데요, 조금 알아보니 Sam Ng가 이야기한 건 아마 암시적 형변환을 두고 이야기 한 것 같습니다. dynamic에서 다른 타입으로 명시적 형변환은 가능합니다. 그리고 이에 대해서 다음 포스트에서 좀 더 자세하게 말씀 드리겠습니다. 

우선, c를 정적인 타입이라고 하고, d를 동적인 타입의 표현식이라고 했을때요, 형변환에 대해서 고려해볼 수 있는 상황은 아래와 같습니다.

1. 오버로드 판별 - c.Foo(d)
2. 대입 형변환 - C c = d
3. 조건문 - if(d)
4. using절 - using(dynamic d = ..)
5. foreach - foreach(var c in d)

일단, 이번 포스트에서는 1번에 대해서 살펴보도록 하구요, 나머지는 다음기회로 미루겠습니다.


- 오버로드 판별의 경우

일단 매개변수 변환가능성으로 돌아가서 생각해보겠습니다. dynamic은 어떤 타입으로도 형변환 할 수 없기 때문에, d는 int로 형변환이 불가능합니다. 하지만, 모처럼 dynamic타입인 매개변수를 썼으니 오버로드 판별도 동적으로 일어났으면 합니다. 그래서 여기서 유령 메서드가 등장합니다.

유령메서드는 오버로드 판별의 후보군에 슬쩍 추가되는데요, 파라미터개수가 똑같고 모든 파라미터가 dynamic타입인 메서드입니다.

즉, 후보군에 Foo(int x, int y), Foo(string x, string y)가 있다면, 유령메서드는 Foo(dynamic x, dynamic y)처럼 생겼겠죠.

유령 메서드가 후보군에 끼어들게되면, 당연하겠지만, 다른 후보군 메서드와 똑같이 취급됩니다. '모든 타입은 dynamic으로 형변환이 되지만, dynamic은 어떤 타입으로도 형변환이 안된다.'는 명제를 다시 한번 떠올려 볼 때입니다. 아주 적절한 타이밍이죠. 이 말은 dynamic타입의 매개변수가 주어진 상황에서 다른 모든 오버로드 후보군은 판별을 통과하지 못하지만, 유령 메서드는 유유히 판별을 통과할 수 있다는 말입니다.

서두에서 제시했던 예제를 다시 보시면, 한개의 dynamic타입을 매개변수로 받습니다. 이 상황에서 오버로드는 두개인거죠. Foo(int)와 유령 메서드인 Foo(dynamic). 전자는 판별을 통과하지 못합니다. dynamic은 int로 형변환이 안되기 때문이죠. 하지만 후자는 성공합니다. 그래서 그 메서드에 호출을 바인드하게 되는거죠.

그리고 호출이 유령 메서드에 바인드 되면, 컴파일러는 DLR을 통해서 런타임에 호출이 제대로 이루어 지도록 조치를 취하는 거죠.

그럼, 한가지 의문이 남는데요. 유령 메서드는 언제 끼어들게 되는 걸까요?


- 유령이 끼어드는 그 순간

컴파일러가 오버로드 판별을 할때, 초기 후보군을 놓고 고심을 합니다. 메서드 호출에 dynamic 매개변수가 있다면, 컴파일러는 후보군을 찬찬히 살펴보면서 유령 메서드를 소환해야 하는지 고심합니다. 유령 메서드가 끼어드는 상황은 아래와 같습니다.

1. dynamic이 아닌 모든 매개변수는 후보군에 있는 파라미터로 형변환이 가능하다.
2. 최소한 한개 이상의 dynamic매개변수가 후보군에 있는 파라미터로 형변환이 불가능하다.

일전에, dynamic타입인 매개변수가 포함된 메서드 호출이라도 정적으로 바인드될 수 있다고 말씀을 드렸었는데요. 이 경우가 2번으로 설명이 됩니다. dynamic매개변수와 일치하는 dynamic파라미터를 갖는 후보메서드가 있다면, 호출은 그 메서드로 정적 바인딩이 됩니다.

public class C
{
    public void Foo(int x, dynamic y)
    {
    }
   
    static void Main(string[] args)
    {
        C c = new C();
        dynamic d = 10;           
        c.Foo(10, d);
    }
}

위와 같은 경우, 오버로드 후보군에 정확하게 Foo호출의 매개변수인 int와 dynamic타입에 일치하는 메서드가 있기 때문에, 정적으로 바인딩되고, dynamic lookup이 발생하지 않습니다. 즉, 오버로드 판별시에 후보군을 한번 쭉 훑었는데도 유령 메서드가 끼어들지 않았다면, 비록 dynamic타입의 매개변수가 있다고 하더라도 원래 하던대로 오버로드 판별을 진행하는 거죠.


- 유령메서드를 통한 할당과 dynamic receiver를 통한 할당은 뭐가 틀린거냐

dynamic receiver의 경우는 런타임 바인더가 실제 런타임시의 receiver의 타입을 기준으로 오버로드 판별을 하려고 합니다. 하지만 유령메서드가 끼어든 할당의 경우는 컴파일타임의 receiver의 타입을 기준으로 진행되는 거죠.

그냥 직관적으로 생각해봤을때, receiver를 컴파일타임에 알 수 있다면, 매개변수에 dynamic타입이 있다고 하더라도 오버로드 판별의 후보군은 컴파일 타임에 밝혀져야 하는 거겠죠.


- 마치면서

사실, 이 부분에 대해서 좀 더 언급할 사항들이 있습니다. 나머지 형변환 케이스에 대해서 더 나아가기 전에, dynamic의 형변환에 대한 부분과 오버로드에 대한 이야기를 좀 더 하려고 하는데요. 여러분과 제가 가진 의문이 조금씩 더 해결됐으면 하는 생각입니다.


- 참고자료

1. http://blogs.msdn.com/samng/archive/2008/11/09/dynamic-in-c-iv-the-phantom-method.aspx


Welcome to Dynamic C#(10) - Dynamic Returns Again.(2)

C# 2010. 1. 11. 09:00 Posted by 알 수 없는 사용자

- 살림살이 좀 나아시졌습니까.

날씨가 계속 춥네요. 난데없이 목이 부어서 지난 금요일에는 조퇴를 했습니다-_-. 다들 건강 조심하시구요. 날씨가 추워서 그런가 여기저기서 어려움을 호소하는 목소리가 들리는 거 같습니다. 다들 하시는 일 잘되고 살림살이 좀 나아지셨으면 좋겠네여!


- 이어서 이어서!

지난포스트에서 보셨던 예제를 다시한번 보시죠.

dynamic d = 10;
C c = new C();

d.foo();
d.SomeProp = 10;
d[10] = 10;

c.Foo(d);
C.StaticMethod(d);
c.SomeProp = d;

지난 포스트에서는 위쪽 그룹에 대해서 다뤘었는데요, 이번 포스트에서는 위 두 그룹중에서 아래쪽 그룹에 대해서 설명드려 보겠습니다. 우선, 가장 간단한 부분부터 다뤄보려고 하는데요, 아래와 같은 코드가 있다고 가정해보죠.

public class C
{
    public void Foo(decimal x) { ... }
    public void Foo(string x) { ... }
    static void Main(string[] args)
    {
        C c = new C();
        dynamic d = 10;
        c.Foo(d);
    }
}

이 코드가 실행되면 어떤일이 벌어질까요? 직관적으로 생각해봤을때... 지역변수c의 타입이 C라는 걸 알 수 있으니깐, C의 오버로드 2개중에 하나가 실행될거라고 생각해볼 수 있습니다. 근데, d의 타입이 dynamic이라는 것도 위 소스코드에서 알 수 있는데요, 그렇다는 이야기는 d의 실제 타입은 런타임에서 알 수 있으므로 컴파일러는 어떤 오버로드를 호출해야 하는지 판단할 수가 없습니다.

그래서 이렇게 생각해볼 수 있습니다. 컴파일러는 호출가능한 후보군을 추출해서 집합을 만들고, 런타임에 실제로 오버로드 판별을 통해서 적합한 메서드를 호출한다고 말이죠. 위의 경우에는 d의 값이 10이므로, 아마도 호환이 안되는 string보다는 decimal을 인자로 받는 오버로드가 호출될거라고 예측해볼 수 있습니다. 그러면, 좀 더 구체적으로 이야기 해보면서 뭐가 예측이랑 다르게 돌아가는지에 대해서 이야기 해보죠.

public class C
{
    public void Foo(decimal x) { ... }
    public void Foo(string x) { ... }
    static void Main(string[] args)
    {
        C c = new D();
        dynamic d = 10;
        c.Foo(d);
    }
}

public class D : C
{
    public void Foo(int x) {...}
}


클래스 D를 C로 부터 상속했고, c를 D의 생성자로 생성한 게 차이점인데요. 그리고 런타임에 c의 타입은 D일텐고, D에는 C가 가진 모든 오버로드보다 더 이 경우에 적합한 int형 파라미터를 가진 Foo가 있습니다. 10은 int형으로 간주될테니, D의 Foo가 가장 적합한 선택이 되겠죠.

그런데, 결과는 어떨까요? 이 부분에 대해서 잘 아시는 분이나 코드를 작성하다가 이상한 점을 발견하신 분이라면 대답하실 수 있으실텐데요. 아래의 그림에서 확인을..


네. 분명히 오버로드가 총 3개여야 할텐데, 두개 밖에 안 나옵니다. 그래서 분명히 D의 Foo가 가장 좋은 선택임에도 불구하고 계속해서 C의 Foo(decimal x)가 호출이 됩니다. 하지만, c를 생성할때 "D c = new D();"나 "dynamic c = new D()"처럼 생성하면, D의 Foo(int x)가 호출이 됩니다. 왜 그럴까요?


- 내가 알면 이글 보고 있겠냐.

dynamic과 관련된 동적 바인딩을 설계할때, 최대한 동적바인딩이 기존의 컴파일러가 정적바인딩에서 하던 짓을 비슷하게 하게끔 유지했다고 합니다. 그래서 그 결과로 dynamic이라고 명시된 매개변수나 receiver가 아니라면, 컴파일타임에 확인할 수 있는 타입을 그 변수의 타입으로 간주한다고 합니다. 즉, 위의 예제에서 c.Foo의 오버로드로 D.Foo(int x)가 포함이 안된 이유는, "C c = new D();"의 결과로 c가 런타임에 가질 타입은 D겠지만, 컴파일타임에서는 C라고 간주한다는 겁니다. 그래서 아무리 인텔리센스를 뒤져봐도 D의 Foo를 발견할 수 없으며 호출도 할 수 없는 거죠.

하지만, "D c = new D();"나 "dynamic c = new D()"처럼 생성하면, 전자의 경우는 컴파일 타임의 c의 타입을 D로 간주하므로 오버로드 3개모두를 인텔리센스에서 확인하실 수 있구요, 후자의 경우는 동적 바인딩을 통해서 c의 런타임 타입이 D임을 알기때문에, 오버로드 후보군에서 Foo(int x)를 골라낼 수 있습니다. 예제를 하나 더 보면요.

public class C
{
    public void Foo(int x, object o)
    {
        Console.WriteLine("Foo(int x, object o)");
    }

    static void Main(string[] args)
    {
        C c = new D();
        dynamic d = 10;
        c.Foo(d, c);
    }
}

public class D : C
{
    public void Foo(int x, D d)
    {
        Console.WriteLine("int x, D d");
    }
}

위의 경우와 마찬가지로, C.Foo(int x, object o)가 호출됩니다. 같은 이유로 말이죠.


- 마치면서

사실 이번 포스트는 좀 애로사항이 있었는데요. 제 실력부족으로 참고했던 원문이 잘 이해가 안가서 말이죠. 그래서 제 나름대로 이런저런 실험을 해보다가 결론을 내렸습니다. 바로 이럴때가 고수님들의 나눔이 필요한 시점입니다. 혹시 더 자세하게 아시는 분이 있다면, 따쓰한 피드백으로 풍성하게 해주시기 바랍니다. "따쓰한" 잊지마세염^^;;;


-참고자료

1. http://blogs.msdn.com/samng/archive/2008/11/06/dynamic-in-c-iii-a-slight-twist.aspx


[JumpToDX11-9] Multi-threaded Rendering 을 위한 API.

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



이번 시간에는 Multi-threaded Rendering 을 위한 API 들에 대해서 살펴보겠습니다.
기능 위주의 설명을 위해서 인자들에 대한 명시는 생략했습니다.
이점 주의해주시기 바랍니다.

ID3D11Device::CreateDeferredContext()

가장 먼저 살펴볼 것은 DeferredContext 의 생성입니다.
DeferredContext 는 스레드당 하나씩 생성되어질 수 있음을 앞선 시간을 통해서 언급했습니다.
또한 이 DeferredContext 는 Command List 들을 생성해서 가지고 있습니다.
즉, 렌더링이 가능한 상태라는 것입니다.
그런 기능을 우리는 Device 인터페이스를 통해서 생성합니다.
이것은 역시 Free thread 한 작업이기 때문에 Device 인터페이스를 이용합니다.

하나의 DeferredContext 는 thread-safe 합니다.
즉, 스레드 상에서 DeferredContext 가 관련 Command 들을 기록하는 것은 안전한 작업입니다.

간단한 사용 방법은 아래와 같습니다.
ID3D11DeviceContext* pDeferredContext = NULL;
hr = g_pd3dDevice->CreateDeferredContext(0, &pDeferredContext);


ID3D11DeviceContext::FinishCommandList()

신기하게도 우리는 이 API 호출 한번으로 CommandList 들을 기록하고 생성할 수 있습니다.
API 이름이 Finish 여서 Start나 Begin 계열의 API 를 검색해 보았지만, 없었습니다.^^
각각의 DeferredContext 별로 호출되기 때문에 DeviceContext 의 멤버함수로 되어 있습니다.
앞선 시간을 통해서 DeviceContext 는 ImmeidateContext 와 DeferredContext 로
분리될 수 있다고 언급했었습니다.
두 Context 모두 ID3D11DeviceContext 인터페이스를 사용하기 때문에 오해의 소지가 약간 있습니다.
FinishCommandList 는 DeferredContext 를 위한 API 임을 유념하시기 바랍니다.

간단한 사용 방법은 다음과 같습니다.
ID3D11CommandList* pd3dCommandList = NULL;
hr = pDeferredContext->FinishCommandList( FALSE, &pd3dCommandList );


ID3D11DeviceContext::ExecuteCommandList()

이 API는 DeferredContext 에 의해서 생성된 CommandList 들을 실행합니다.
역시나 ID3D11DeviceContext 의 멤버함수이기 때문에 혼란스러울 수 있습니다.
과연 ImmediateContext 가 이 함수를 호출할까요? 아니면, DeferredContext 일까요?

지난 시간들을 통해서 우리는 실제로 Multi-threaded Rendering 이라는 것은
CommandList 생성을 Multi-thread 기반으로 하는 것이라고 언급했었습니다.
그 이후에 실제 그래픽 카드로의 전송은 하나의 스레드만 할 수 있다고 했었습니다.
바로 그 사실입니다.
이 함수는 ImmediateContext 에 의해서 호출됩니다.
즉, 이 API 는 그래픽 카드로 해당 CommandList 들을 전송하는 것입니다.

간단한 사용 방법은 아래와 같습니다.
g_pImmediateContext->ExecuteCommandList( g_pd3dCommandList, TRUE );


이상 3가지 API 에 대해서 살펴보았습니다.
믿기지 않으시겠지만(?)
Multi-threaded Rendering 작업은 이 세가지 API로 할 수 있습니다.
나머지는 스레드 생성과 제어를 위한 작업이 결합되어야 할 것입니다.
일반적인 스레드 프로그래밍과 관련된 내용이라 이곳에서는 배제를 했습니다.
현재 DirectX SDK Sample 에는 'MultithreadedRendering11' 라는 것이 있습니다.( 2009 August 버전 기준 )
이것과 관련된 소스가 있으니 참고해서 보시면 좋을 것 같습니다.

이상으로 Multi-threaded Rendering 의 기본 개념 설명을 마치고자 합니다.
이 부분과 관련된 내용은 앞으로 정리가 되는대로 추가하거나 수정이 되어질 수 있을 것입니다.
다음 시간부터는 DirectX11 의 다른 주제를 가지고 돌아오겠습니다.^^
 

Welcome to Dynamic C#(9) - Dynamic Returns Again.

C# 2010. 1. 7. 09:00 Posted by 알 수 없는 사용자

- 정말 오랜만에 다시 Dynamic이군요.

안녕하세요~! 눈 때문에, 어떤 사람들은 로맨틱한 겨울이고, 어떤 사람들은 악마의 똥가루의 냄새에 신음하고, 어떤 사람들은 방에 콕처박혀 있고 뭐 아주 버라이어티한 겨울입니다. 겨울이 버라이어티 정신이 충만하네요. 연예대상같은거라도 하나 받고 싶은 가봐요. ㅋㅋ 아무튼! 정말 오랜만에 다시 dynamic시리즈를 쓰게 되네요. 워낙 한 내용도 없이 중간에 끊어서 좀 그랬습니다;;; 물론, 기다리신 분이 얼마나 있을지는 미지수지만요-_- 그럼. 한번 이야기를 시작해볼까요? dynamic에 대해서 조금씩 자세하게 들어가 보겠습니다.


- 어서내놔 dynamic

우선 예제를 하나 보시죠.

dynamic d = 10;
C c = new C();

//위쪽 그룹 
d.foo();
d.SomeProp = 10;
d[10] = 10;

//아래쪽 그룹 
c.Foo(d);
C.StaticMethod(d);
c.SomeProp = d;




위 그룹과 아래 그룹의 차이점은 뭘까요? 네~! Give that man a cigar!(누가 정답을 말했을때 하는 말이라네요) 위 그룹은 액션을 받는 객체가 동적인 객체, 즉 dynamic receiver이구요. 아래 그룹은 static receiver와 static method가 바로 차이점입니다.

위 그룹은 동적인 표현식(expression)속에서 직접적으로 동적인 행위가 일어나고, 아래그룹은 직접적으로  동적표현식은 아닙니다. 각각의 연산의 매개변수로 동적인 타입이 들어가면서, 전체적인 표현식을 간접적으로 동적으로 만들고 있는거죠. 이런 경우에는 컴파일러가 동적인 바인딩과 정적인 바인딩을 섞어서 수행하는데요. 예를 들어서 동적타입을 매개변수로 받는 오버로드가 있을 경우에, 어떤 멤버집합(member set)을 오버로드해야 할지 결정할때는 정적인 타입을 사용해서 판단할테구요, 실제로 오버로드를 판별(resolution)할때는 매개변수의 런타임 타입을 사용할 것이기 때문이죠.

컴파일러가 dynamic타입인 표현식을 보게되면, 그 안에 포함된 연산들을 동적 연산처럼 처리하게 됩니다. 즉, 표현식이 인덱스를 통한 접근이든 메서드호출이든 상관없이 그 표현식의 결과로 나오는 타입은 런타임에 결정될거라는 거죠. 그 결과로 컴파일 타임에 동적인 표현식의 결과로 나오는 타입은 dynamic이겠죠.

컴파일러는 이런 모든 동적인 연산들을 DLR을 통해서 dynamic call site라는 걸로 변환을 합니다. 지지지난 포스트에서 설명을 드렸던거 같은데요, 제네릭한 델리게이트를 가지고 있는 정적 필드입니다. 어떤 연산에 대한 호출을 가지고 있다가, 추후에 같은 타입의 연산이 호출되면 다시 call site를 생성할 필요없이 정적필드에 저장된 델리게이트를 호출해서 실행에 필요한 부하를 최대한 줄이는데 도움을 주는 친구죠. call site가 만들어지면, 컴파일러는 그 call site에 저장된 델리게이트를 호출할 코드를 생성하구요, 거기에다가 매개변수를 넘겨줍니다.

만약에, 호출한 객체가 IDynamicObject를 구현해서 스스로 동적 연산을 어떻게 처리할지 아는 객체가 아니거나, 미리 저장된 델리게이트와 타입이 안맞아서 캐시가 불발이 나면, call site와 같이 생성된 CallSiteBinder가 호출됩니다. CallSiteBinder는 call site에 필요한 바인딩을 어떻게 처리해야 하는지 알고 있는 객체인데요, C#은 이 CallSiteBinder에서 상속한 바인더를 갖고 있습니다. 이 C# CallSiteBinder가 적절한 바인딩을 통해서 DLR의 call site가 갖고 있는 델리게이트에 저장될 내용을 expression tree형태로 만들어서 리턴합니다. 이 내용역시 전전, 전포스트에서 다뤘었쬬? 못봤다고 하시면!!!! 제가 절대 가만있을수는 없는 문제고! 링크를 드..드리겠습니다. 전포스트, 전전포스트. 친절하죠?-_-


- 캐시되는 과정은 어떠냥

공개된 문서를 통해 볼 수 있는 현재의 캐시 방식은 그냥 단순히 매개변수들의 타입이 일치하는지 검사하는겁니다.  만약에.. 이런 호출이 있다고 할때...

args0.M(arg1, arg2, ...);

그리고, 이전에 args0이 C라는 타입이며, 매개변수 arg1과 arg2가 모두 int인 호출이 있었다고 해보면요, 캐시를 체크하는 코드는 대략아래와 같습니다.

if (args0.GetType() == typeof(C) &&
    arg1.GetType() == typeof(int) &&
    arg2.GetType() == typeof(int) &&
    ...
    )
{
    //CallSiteBinder의 바인드 결과는 여기에 계속 통합되구요
}
    ......//캐시 검사는 좀 더 많을 수도 있구요
else
{
    //여기서 CallSiteBinder의 bind메서드를 호출하고, 캐시를 업데이트 합니다.
}

지금까지 간단하게 알아본 내용을 그래도 마무리 하려면, C# CallSiteBinder가 뭘 어떻게 하는지를 알면 되겠네요. 서두에 두그룹의 연산중에 위 그룹의 연산을 보면요, 메서드 호출, 속성 접근, 인덱서 호출등 3가지 연산이 있었죠. 일단 모든 연산은요 표준 C# runtime binder를 통해서 생성되고, C# runtime binder가 걔네들을 데이터 객체로 사용합니다. 그 데이터객체는 바운드되야할 액션을 설명하는데요, 그런 객체를 C# payload라고 부른다고 합니다. 

C# runtime binder는 쉽게 작은 컴파일러라고 생각하면 되는데요, 얘가 일반적인 컴파일러가 갖고 있는 심볼테이블이나 타입시스템, 오버로드 판별 및 타입 교체같은 기능을 갖고 있기 때문입니다. 간단하게 d.Foo(1)을 예로 생각해보죠.

runtime binder가 호출되면, 현재 call site에 대한 payload과 call site에 대한 런타임 매개변수를 갖습니다. 그리고 dynamic receiver를 포함해서 그 모든 런타임 매개변수와 타입을 모아서는 그 타입에 대한 심볼테이블을 만듭니다.(심볼테이블에 대한 간략한 설명은 여기를 참조하세영!) 그리곤 payload꾸러미를 풀어헤쳐서 수행하려고 하는 연산의 이름을 꺼냅니다.(Foo) 그리고 d의 타입에서 리플렉션을 사용해서 Foo라는 이름을 갖는 모든 멤버를 뽑아냅니다. 그리고 걔네들도 심볼테이블에 적어넣죠. 말로 설명하니깐 깝깝하시죠? 설명하는 저도 깝깝하네여-_-;;; 제가 상상력을 동원해서 부연설명을 드리면요,

d.Foo(1)에서 먼저 매개변수의 타입과 d의 타입을 갖고와서 심볼테이블에 적어두고요.

주소     타입            이름
서울시   int             익명(= 1)

수원시   dynamic      d

그리고 리플렉션으로 d의 타입에서 Foo를 모두 찾아냈는데 대략 아래와 같다고 해보죠.
Foo(int a)
Foo(double b)
Foo(string c)

그리고 얘네들도 따로 심볼테이블에 집어넣으면?

-call site에 대한 심볼테이블
주소     타입            이름
서울시   int            익명(= 1)
수원시   dynamic      d

-d의 멤버중에 Foo라는 동명이인들
주소      타입        이름
부산시   void     Foo(int a)
창원시   void     Foo(double b)
안양시   void     Foo(string c)

그러면, 타입을 찬찬히 들여다보면, 어떤 Foo가 호출되야 할지 명확하게 보입니다. d.Foo(1)호출에서 매개변수의 런타임타입이 int이므로 Foo(int a)가 호출이 되겠죠. 이건 그냥 제가 설명을 위해서 상상력을 동원해본거니깐요 믿지는 마시기 바랍니다. 예비군 동원 무쟈게 귀찮으시져? 상상력도 무쟈게 귀찮아 하네요-_-. 어서 집에 보내고 다시 설명을 이어 가겠습니다.

위에서 설명드린 runtime binder를 설계할때 세웠던 한가지 원칙은 "runtime binder는 정적 컴파일러가 하는 짓을 똑같은 의미로 할 수 있어야 한다."였다고 하는데요. 그래서 에러메세지 역시 동일한 에러메세지를 뱉어낸다고 합니다.

위의 바인딩의 결과로 바인딩이 성공적일 경우에 수행할 동작을 표현한 expression tree가 만들어집니다. 그렇게 안되는 경우에는 runtime binder exception을 던진다고 하네요. 결과로 만들어진 expression tree는 DLR의 캐시에 포함되고 호출되면서 원래의 호출을 성공적으로 완료합니다.


- 약간의 제약사항?

그런데, 위에서 정적 컴파일러와 똑같은 짓을 하게 만들려고 했지만, 아마도 예산과 시간때문에 선택과 집중을 해야 하니깐 몇가지 못집어 넣은게 있다고 합니다. 람다식과 확장 메서드, 메서드 그룹(델리게이트)에 대한 이야기 인데요. 현재로서는 바인딩 안된 람다식을 런타임에서 표현할 방법이 없다고 합니다. 개발하다가 디버깅을 할때 브레이크 포인트를 잡고 그 상태에서 현재 상태의 객체에 값을 가져온다거나 메서드를 호출하고 값을 확인할 수 있잖아요? 근데, 람다식은 그런식으로 디버깅이 안됐던거 같은데, 아마 그문제가 계속 이어지는 거 같습니다.

그리고 메서드 그룹역시 런타임에 표현할 수 있는 방법이 없다고 합니다. 예를 들면,

delegate void D();
public class C
{
     static void Main(string[] agrs)
     {
        dynamic d = 10;
        D del = d.Foo; //뭐 이렇게는 안된다고 하네요. 그래서 런타임 익셉션이 난다고 합니다.
     }
}

그리고 확장 메서드 역시 using절과 범위를 바인딩없이 넘겨줄 방법이 없기때문에, 확장메서드역시 안된다고 하구요.


- 마물!

무척 오랜만의 포스팅인데요, 갈증이 조금이라도 해소가 되셨으면 좋겠네요. 제 실력이 바닥을 기다보니 원문의 내용을 한번 걸러서 드리는 정도밖에 못드리는 면이 많은데요. 뭐-_- 내공이 부족하니 한계가 명확하네요. 그럼 다음 포스트에서 뵙져~!!!!!!!


- 참고자료

1. http://blogs.msdn.com/samng/archive/2008/11/02/dynamic-in-c-ii-basics.aspx