Lyn
조회 수 208745 추천 수 0 댓글 0
?

단축키

Prev이전 문서

Next다음 문서

크게 작게 위로 아래로 댓글로 가기 인쇄
?

단축키

Prev이전 문서

Next다음 문서

크게 작게 위로 아래로 댓글로 가기 인쇄

주의 : 본 글은 거의 2년전 시점에 씌여진 글을 깜빡하고 안올리고 (...) 있던걸 꺼낸라 현재의 boost 최신버전하고는 상황이 다를 수 있습니다.


boost::interprocess::managed_shared_memory(이하 msm)에 관하여.

 

msm는 내부적으로 Red-Black Tree 를 이용하여 구현되어 있다

서로 다른 프로세스에서 공유 가능한 std::map<string, void*> 라고 보면 간단하게 이해된다.

공유메모리를 map의 형태로 기능확장한 것.

 

msm 는 생성시 mutex를 가지게 됨. Msm rb-tree를 구성하는 boost::rbtree_best_fit boost::interprocess::interprocess_mutex 를 상속받은 header_t 구조체를 소유하고 있음.

header_t 구조체는 추가적으로 msm의 헤더를 관리하기위한 멤버를 소유하고 있고, 이것들은 메모리 세그먼트를 관리할때 자동으로 scoped lock 이 걸림

 

msm 생성시 POXIS에서는 shm_open 를 이용하고윈도우에서는 CreateFile를 이용한다

 msm 생성 과정에서는 OS에 의해서 보호된다여기서 의문인 것은 왜 CreateFileMapping 을 이용한 Windows OS의 공유메모리 메커니즘을 사용 하지 않았는가 이다.

물론 FILE_ATTRIBUTE_TEMPORARY 플래그를 주어 파일을 메모리에 생성 하긴 했지만 그다지 효율적으로 보이지는 않는다궂이 예측 하자면 CreateFileMapping이 생성시의 이름을 어떻게 주느냐에 따라  세션관련 제어 기능을 가지게 되는데 그것을 의도적으로 피하고자 함이 아닌가 싶다

boost의 기본 설계 방침은 “플랫폼 특화” 된 기능을 막아서라도 같은 인터페이스를 제공 하는 것이라고 하니까아니면 그냥 단순히 POSIX랑 똑같이 하고 싶어서 일수도 있고.

 

msm는 윈도우에서는 추가적인 과정이 있는데만약 공유위반으로 인해 생성이 실패 할 경우 250ms 간격으로 최대 3번까지 재시도 하게 된다

이는 MSDN 에 명시된 동작으로 실제 사용 하지 않더라도 OS의 백그라운드 조각모음 때문에 일시적으로 파일이 잠길 수 있다고 하고 있다또한 FILE_SHARE_READ, FILE_SHARE_WRITE, FILE_SHARE_DELETE 권한이 있다 하더라도 파일 생성 중에는 lock이 걸려 공유위반이 발생 할 수 있다단 이것은 임시파일이 아닐 경우인데실제 테스트 결과 임시파일에서는 아무리 빠르게 반복해도 관련 현상을 재현할 수 없었다.

 

어쨋든 Windows, POSIX 양쪽 다 OS 차원에서 “파일 생성” 이라는 부분은 완벽히 동기화 시켜주기 때문에 절대로 “같은 이름을 가진 msm 객체가 생성 될 수 없다” 또한 open_or_create를 사용 하여 생성 할 경우 반드시 성공한다는 것을 보장 하게 된다.

 

msm 의 읽기,쓰기 동작(find, construct )도 일부러 nolock 버전의 함수를 호출하지 않는 이상 thread safe 하다 find → add 의 동작을 반복 할 필요가 없다.

 

construct 는 이미 존재하는 Key 대해 요청 할 경우 exception 을 발생 시킨다.

이것의 예외 처리는 매우 중요한데만약 한쪽에서 construct 가 중복 실행되어 exception 이 발생 될 경우 예외처리를 제대로 하지 않으면 msm 내부의 lock이 풀리지 않아 무한히 대기 하게 된다. Windows , POSIX 양쪽 다 프로세스가 Crash 되어 종료된 상황에서도 lock 이 해제되지 않는것이 확인되었다이런 경우를 방지하기 위해 find_or_construct 를 제공 하므로 이쪽을 사용 하는 것이 바람직하다.

 

위의 내용에 덧붙이면 사실 윈도우에서 제공하는 모든 동기화 객체는 lock의 재진입을 지원하고 프로세스 레벨의 레퍼런스 카운팅을 제공하여 프로세스가 죽으면 그 프로세스가 획득한 모든 Lock이 해제되도록 되어있다문제는 boost의 구현방법에 있는데 posix 에서는 pthread mutex를 사용하는것이 기본인 반면 Windows 시스템에서는boost가 자체 구현한 spin_mutex를 사용하는것이 default로 되어 있다그런데 이녀석은 커널 레벨에서 시스템 전체를 관리하는게 아니라 프로세스 레벨에서 atomic operation을 이용해 임의로 구현한 기능이므로프로세스가 Exception에 의해 Crash 되었을 경우의 뒷처리를 전혀 할 수 없다 Windows 시스템의 안정적인 관리 기능을 사용하려면 boost::interprocess::windows_mutex를 명시적으로 선언해야 한다이는 공유메모리도 마찬가지로기본적으로는 위에 적은것 처럼 CreateFile 을 사용 하고 있지만 boost::interprocess::windows_shared_memory 를 명시적으로 사용하면 CreateFileMapping를 이용하는 공유메모리가 생성되며 Windows에서 제공하는 기능 전체를 사용할 수 있다.

 

TAG •
?

Atachment
첨부 '3'
?

단축키

Prev이전 문서

Next다음 문서

크게 작게 위로 아래로 댓글로 가기 인쇄
?

단축키

Prev이전 문서

Next다음 문서

크게 작게 위로 아래로 댓글로 가기 인쇄

오늘 같은 MS VC++ MVP이신 유영천 님께서 개인 프로젝트로 출시하신 Project D Online(Steam에서 즐기실 수 있습니다)에 재밋는 이슈가 발생하였습니다.

바로 vmovd eax,xmm0 명령어에서 illegal instruction 오류를 내면서 크래시가 난다는건데요, 원인분석의 결과를 한번 공유해 보겠습니다.


인텔에서는 2011년 새로운 SIMD 명령어인 AVX를 지원하는 CPU를 출시 하였고, 당연히 그 이후 각 컴파일러들은 해당 명령어에 대한 지원을 시작하엿고 당연히 현재 최신의 MS C++ 컴파일러인 VC++2013버전에서는 2011년에 출시된 AVX 명령어를 지원 하고 있습니다.


AVX를 지원하는 CPU는 아래와 같습니다. 출처는 영문판 위키입니다

원본 : http://en.wikipedia.org/wiki/Advanced_Vector_Extensions

 


인텔은 샌디브릿지 이상, AMD는 불도저 이상이군요. 저도 하스웰-E 가지고싶습니다...


VC++2013의 intrin.h에는 각 어셈명령어와 매칭되는 함수들과 타입이 제공 되고, dvec.h 에는 2개의 클래스 F32vec8과 F64vec4가 제공됩니다.
각각 단정밀도 8개, 배정밀도 4개를 동시에 계산할 수 있는 클래스인데요


극히 간단한 샘플 코드를 돌려 보겠습니다


#include <cstdio>
#include <dvec.h>

int main()
{	
	F32vec8 A(1.1f, 2.1f, 3.1f, 4.1f, 5.1f, 6.1f, 7.1f, 8.1f);
	F32vec8 B(1.1f, 2.1f, 3.1f, 4.1f, 5.1f, 6.1f, 7.1f, 8.1f);		
	
	A += B;

	printf("%f %f %f %f %f %f %f %f", A[0], A[1], A[2], A[3], A[4], A[5], A[6], A[7]);
		
	return 0;
}


아래와 같은 경고가 발생하는데, 뭐 일단 여기에선 무시해도 상관 없고, 빌드 옵션에 추가해도 상관 없습니다.
어차피 바이너리엔 영향 없으니까요.


01.png


그럼 위 코드는 어떤 바이너리를 만들어 낼까요?

02.png


대충 이와 같습니다.
v 로 시작하는 명령어들이 AVX명령어 들인데 8개의 float += 연산을 단 한줄로 끝내는것을 볼 수 있습니다.


하지만 세상엔 똥컴(.....) 을 쓰시는분들이 참 많고 당연히 AVX를 지원하지 않는 CPU에서도 프로그램을 돌려야할 경우가 있습니다. 하지만 그렇게 되면 최적화를 상당부분 포기하게 되죠. java나 .net 처럼 JIT 을 지원하는 환경이라면 처음 실행할때 가상머신이 CPU 를 확인해서 최적의 명령어셋으로 만들어 내겠지만(이게 닷넷, 자바의 속도가 간혹 C보다 빨라지는 이유중 하나이기도 합니다) 미리 다 바이너리로 만들어져 있는 Native Application 들은 이런 방법이 불가능 합니다. 

그래서 자주 쓰는 방법은, 미리 CPU 종류별로 다 코드를 컴파일 해 두고, 실행할때 CPU가 지원하는 최적의 명령어를 실행하도록 분기하는거죠... 실제로 극한의 속도를 요하는 라이브러리 (Intel IPP 등) 에서는 당연하다시피 쓰이고 있는 방식이고, 일반적인 라이브러리를 만들때도 쓰이는 경우가 있습니다. 혹시 리눅스에 관심있으신분이라면 최근버전의 리눅스 커널에서 아주 오래된 CPU의 지원이 조금씩 끊기고 있다는것을 아실 수 있는데, 이게 특정 이상의 명령어 셋을 기본적으로 사용하겟다는 의미이기도 합니다.


네 중요한건 바로 여기에 있습니다. VS2013은 내부적 라이브러리에 AVX코드를 생성해서 쓰는 경우가 있다는것이고, cpu의 명령어 셋을 체크 한 후에 쓰기때문에 실행엔 문제가 없어야 합니다만... 현재 명령어 셋을 체크하는것은 cpu만이 아닙니다. 각종 코드 보안 관련 기능에 사용하기 위해 OS도 명령어를 체크하지요.
아래 내용은 역시 영문위키에서 가져왔습니다.... 일일히 찾기귀찮아서


원본 : http://en.wikipedia.org/wiki/Advanced_Vector_Extensions

  • Apple OS X: Support for AVX added in 10.6.8 (Snow Leopard) update[13] released on June 23, 2011.
  • Linux: supported since kernel version 2.6.30,[14] released on June 9, 2009.[15]
  • Windows: supported in Windows 7 SP1 and Windows Server 2008 R2 SP1,[16] Windows 8
  • Windows Server 2008 R2 SP1 with Hyper-V requires a hotfix to support AMD AVX (Opteron 6200 and 4200 series) processors, KB2568088
  • FreeBSD in a patch submitted on 21 January 2012,[17] which was included in the 9.1 stable release[18]
  • DragonFly BSD added support in early 2013.
  • Solaris 10 Update 10 and Solaris 11

  • 리눅스는 굉장히 삐른 편인데 대체 어디서 정보를 얻어서 구현했는진 모르겠습니다(....), 윈도우는 날자가 적혀있지 않아 확인해보니 2011년 3월 15일입니다. 맥은 좀 늦었군요 뭐 어차피 자기들이 하드웨어 내놓을때 같이 내 놓으면 상관없겠습니다. 어차피 OS가 아니라 펌웨어에 가까우니까요


    문제는 여기에서 발생합니다. 바로 CPU는 지원하는데 OS가 지원을 안할 경우입니다 (...)
    위에 써있는것을 보시면 알겠지만 Windows 7은 SP1 이상부터 AVX를 지원합니다. 즉 SP1이 설치되지 않은 구버전의 Windows 7 이라면 AVX명령어가 실행이 되지 않는다는 겁니다....


    그래서 MS는 VC++2013 재배포 패키지( http://www.microsoft.com/en-us/download/details.aspx?id=40784 )  에 다음과 같이 명시 해 두었습니다.

    Supported Operating System

    Windows 7 Service Pack 1, Windows 8, Windows 8.1, Windows Server 2003, Windows Server 2008 R2 SP1, Windows Server 2008 Service Pack 2, Windows Server 2012, Windows Server 2012 R2, Windows Vista Service Pack 2, Windows XP 


    네 분명히 Windows7 SP1 이라고 적혀 있지요.
    문제는... 저게 SP1이 아닌데도 설치가 된다는겁니다 (....)


    03.png



    그냥 재배포 패키지설치시 확인 해서 올리라고 했었어야 할거 같은데... 어쨋든 안해줍니다 그래서 만약을 대비해 버전 체크를 하시는게 좋습니다.
    빌드넘버로 확인할 수 있는 Windows 7은 6.1.7600, Windows7 SP1은 6.1.7601 로 다르니까요.

    안그러면 이런 크래시가 발생할 수 있습니다.


    사실 AVX 같은 확장명령어로 최적화 될 상황이 그리 많지는 않습니다... 저런걸 쓸 상황이 오는건 진짜 대량의 계산을 요하는 프로그램이나, 영상관련 프로그램, 아니면 게임정도니까요.
    그래도 만약을 대비해서 나쁠건 없겟죠.


    그리고 서비스팩이 설치되지 않은 7600버전의 Windows 7은 더이상 어떤 패치도 제공 되지 않는 상황입니다(Sp1을 올려야 나머지 패치가 적용 됩니다) 그러니까 웬만하면 패치좀 하고 씁시다 (...)

    PS. Project D Online 많이좀 해주세요.

    TAG •
    ?

    Lyn
    조회 수 175927 추천 수 0 댓글 4
    ?

    단축키

    Prev이전 문서

    Next다음 문서

    크게 작게 위로 아래로 댓글로 가기 인쇄
    ?

    단축키

    Prev이전 문서

    Next다음 문서

    크게 작게 위로 아래로 댓글로 가기 인쇄
    PS. 이 글은 어디까지나 "개발환경" 이야기지 "배포환경" 얘기가 아닙니다.
    배포시에는 대충 .net > java > python > php > ruby 정도로 순서가 뒤집어집니다...


    이번에 페이스북 SSAG 그룹의 조대협 님의 글을 보고 "빌드속도" 와 "리로딩속도" 에 대해서 고민해 보았습니다.
    사실 전 C++로 주로 작업을 하기 때문에 리로딩속도라는건 의미가 없었는데다가
    빌드속도는 빌드서버 몇개 더 둬서 장비빨로 해결하는게 당연시 되었고, 그 컴파일 느리다는 C++도 파일 몇개 바꾸는 정도면 Incremental link를 사용할 경우 링크 시간도 별로 안걸립니다.
    하지만 지금은 C#을 쓰고 있는데다가, "배포중" 이 아닌 "개발중" 이라면 was 를 다시 띄우는 속도가 생산성에 큰 지장을 줄 것이라고 생각되어 간단히 테스틀 해 보았습니다.
    제 주력플랫폼은 역시 Windows 인지라, 테스트 환경이 VM이 되어 버렷기에 성능이 중구난방이고(...) 애초에 각 프로젝트의 규모도 다르니 절대 적인 수치를 비교하는것은 의미가 없다고 생각되어 단순히 체감 순위만 
    나열해 보겠습니다.
    제가 모든 플랫폼을 잘 아는게 아니라서 그냥 인터넷에 흔하게 돌아다니는 개발환경을 기준으로 했습니다.
    테스트는 각 플랫폼에서 유명한 이슈트래커 솔루션(자바나 닷넷은 좀 미묘 ...) 으로 하였습니다(규모큰 프로젝트를 찾을수가 없어서...)
    node.js 기반 이슈트래커를 아는게 없어서 테스트를 못해본건 좀 아쉽네요
    perl 은 한국에선 쓰는 프로젝트가 아예 없다시피 한거같아서 빼둿습니다. 일본에선 php와 맞먹는 인기라는데
    1. apache + php + mantis
    2. python + trac (내장된 tracd 사용)
    3. ruby + redmine + webrick
    4. tomcat + java+ itracker
    5. iisexpress + C# + BugTracker.NET
    테스트 결과
    php > [넘사벽] > dotnet > python > [슬슬 짜증이 나기 시작함] > java > ruby
    아예 다시 띄울 필요가 없는 php가 이 상황에선 압도적이군요 (....)
    그 뒤로 닷넷 파이썬이 있고, java 정도만 가도 리로딩시간이 개발리듬에 영향을 줄 정도로 느렸습니다.
    작은 프로젝트라면 확실히 java에서 플랫폼을 갈아타는 것만으로 생산성 향상을 볼 수 있을지도 모르겠습니다.
    저는 조금만 프로젝트가 커져도 약타입 언어는 생산서에 한계가 온다는 입장이라 아마 쓸일은 없겠지만요...
    제가 공감하는 관련 글 링크 :http://youngrok.com/WeakTyping_%EC%96%B8%EC%96%B4%EC%9D%98%ED%95%9C%EA%B3%84 
    마침 이분도 오픈마루에서 이슈트래커를 만드셧던 분이라고 합니다 (...) 아무상관없지만
    하지만 ruby... 토나옵니다. 안떠요 안떠 ... 저보고 이거 가지고 개발하라 그러면 도망갈지도 모르겠어요(............)
    ?
    • ?
      미도반 2014.09.16 12:27 SECRET

      "비밀글입니다."

    • profile
      Lyn 2014.09.17 11:44
      ㅡ.ㅡ;;;;;
    • ?
      김선영 2014.12.18 06:07
      이런 문제때문인지 IT의 본고장 미국에서도 python쪽이 대세가 되는것 같더군요. 아무래도 개발, 배포 양쪽에서 크게 문제가 안된다고 생각하나 봅니다. 그런데 "슬슬 짜증이 나기 시작함"의 체감수치는 어느 정도일까요? 그게 더 궁금해지긴 합니다. ^^
    • profile
      Lyn 2015.01.05 10:30
      제가 체감하는 짜증이 나는 시점은 로딩되는거 안 지켜보고 웹툰 한편정돈 볼까.. 하는 마음이 생길 시간이었습니다 :)

    Lyn
    조회 수 64710 추천 수 0 댓글 0
    ?

    단축키

    Prev이전 문서

    Next다음 문서

    크게 작게 위로 아래로 댓글로 가기 인쇄
    ?

    단축키

    Prev이전 문서

    Next다음 문서

    크게 작게 위로 아래로 댓글로 가기 인쇄

    http://www.visualstudio.com/news/vs2015-preview-vs


    기존에 14라고 부르던 버전이군요. 정식출시가 얼마 남지 않은 것 같습니다.
    더 좋은 소식도 있지만

    ?

    2009.08.19 03:08

    C++ new 연산자의 진실

    Lyn
    조회 수 58062 추천 수 0 댓글 2
    ?

    단축키

    Prev이전 문서

    Next다음 문서

    크게 작게 위로 아래로 댓글로 가기 인쇄
    ?

    단축키

    Prev이전 문서

    Next다음 문서

    크게 작게 위로 아래로 댓글로 가기 인쇄

    new 연산자의 진실

     

    C++에서는 메모리를 할당할 때 new 연산자를 사용합니다.

    그리고 이 new 라는 연산자는 오버로딩이 가능합니다. 그럼 과연 이 new 의 정체는 뭘까요?

    new 를 호출하면 실제로는 malloc 이 내부에서 다시 호출 된 다는 것은 일단 다 안다고 가정하고 넘어가겠습니다.

     

    첫 코드 나갑니다. 연산자 new 를 오버로딩 한 경우입니다.

    #include <stdio.h>

    #include <stdlib.h>

     

    class Test

    {

    public:

               int a;

     

               void* operator new(size_t size)

               {

                         printf("한 개 할당중\n");

                         Test* temp = (Test*)malloc(size);

                         return temp;

               }

               Test()

               {

                         printf("Test Constructor\n");

               }

    };

    int main()

    {

               Test* t1;

     

               t1 = new Test;

     

               system("pause");

               return 0;

    }

     

    new 에는 size_t 형태의 파라메터가 하나 존재합니다. 이 파라메터로 할당 해야 할 크기를 알려줍니다. 단순히 그 크기로 메모리를 할당 하고 리턴 만 하엿습니다.

     

    실행결과는 아래와 같습니다.

     

    image1.png

     

    우리는 부른적도 없는 생성자가 호출되었습니다. new 가 단순히 메모리 할당만 하도록 오버로딩 하엿는데요. 여기서 new 는 실제로는 오버로딩이 되지 않았다고 할 수 있겠습니다. 뭔가 수상하군요. 디스어셈블 해 보았습니다.

     

    image2.png

     

    이런 우리는 new 를 호출했을 뿐인데 실제로는 operator new 라는 함수와 생성자를 따로따로 호출 하고 있었네요. 그럼 우리가 수정한 것은 operator new 라는 함수 일 뿐 new 연산자 자체가 아니라는것을 알 수 있습니다.

     

    심지어 윗줄에서는 할당 할 메모리 크기를 스택에 집어넣어주는 친절함(?) 까지 엿볼 수 있군요. Pop 이 호출부위 아래에 존재한다는 것은 operator new cdecl 방식으로 call 되고 있다고 짐작 할 수 있겠습니다.

     

    그럼 new 는 단지 operator new 와 생성자를 호출 하고 있을 뿐이라면 이 두 함수를 따로 따로 호출하는것도 가능하지 않을까요? 다음과 같은 코드를 짜 보았습니다.

    #include <stdio.h>

    #include <stdlib.h>

     

    class Test

    {

    public:

               int a;

     

               void* operator new(size_t size)

               {

                         printf("한 개 할당중\n");

                         Test* temp = (Test*)malloc(size);

                         return temp;

               }

               Test()

               {

                         printf("Test Constructor\n");

               }

    };

    int main()

    {

               Test* t1;

     

               t1 = (Test*)Test::operator new(sizeof(Test));

               t1:Test();

     

               system("pause");

               return 0;

    }

     

    그리고 실행 해 보았습니다.

     

    image3.png

     

    이런 완전히 똑같군요.

     

     

    그럼 이렇게 결론 내릴 수 있을까요? new 연산자는 operator new 함수를 호출 한 후, 생성자를 호출 해 주는 연산자이다.

     

    그런데 이게 또 아닌거 같습니다. 왜냐면 이 두 함수에는 VMT(Virtual Methor Table) 을 생성 해 주는 부분이 없거든요. 가상함수가 한 개 이상 존재하고 상속관계가 있는 클래스(상속 받았던 상속 했던) 에는 반드시 VMT의 포인터가 존재합니다. 그럼 이 VMT는 언제 등록되었을까요? 살짝 코드를 고쳐보았습니다.

     

    #include <stdio.h>

    #include <stdlib.h>

     

    class Test

    {

    public:

               int a;

     

               void* operator new(size_t size)

               {

                         printf("한 개 할당중\n");

                         Test* temp = (Test*)malloc(size);

                         return temp;

               }

               Test()

               {

                         printf("Test Constructor\n");

                         func();

               }

     

               virtual void func()

               {

                         printf("Test::func()\n");

               }

    };

     

    class Test2 : public Test

    {

               virtual void func()

               {

                         printf("Test2::func()\n");

               }

    };

    int main()

    {

               Test* t1;

     

               t1 = new Test2;

     

               system("pause");

               return 0;

    }

     

     

    생성자에서 func라는 가상함수를 호출 하고 있습니다.

    우리는 Test2를 생성하였으므로 Test2::func() 가 출력 될 거라고 기대 할 수 있겠습니다. 결과를볼까요?

     

    image4.png

     

    어라 뭔가 이상합니다. Test::func() 가 출력되었네요. 이게 어찌 된 일일까요?

    그 이유는 생성자를 호출 하는 시점에서는 VMT의 포인터가 제대로 등록 되지 않기 때문에, 가상함수 본래의 역할을 제대로 하지 못하는 겁니다. 디스어셈블 한 코드를 보겠습니다.

     

    image5.png

     

    생성자를 호출 한 다음에도 추가적인 작업을 하고 있습니다.

    그 중 빨간 네모로 표시 된 부분이 가상함수가 없을때는 존재하지 않던 부분입니다.

    즉 저 부분에서 VMT포인터를 추가하는 작업을 하고 있다고 생각 할 수 있겠습니다.

     

    이제 new 의 진실이 밝혀졋군요.

    new 연산자를 호출하면

     

    1.     메모리 할당을 위해 operator  new 함수를 호출한다

    2.     생성자를 호출한다

    3.     VMT를 등록해 준다

     

    3가지 과정으로 new 의 역할은 모두 종료됩니다.

    쉽고 편하게 쓰던 연산자가 참 하는 일도 많네요 : )

     

    그럼 또 다른 언어인 Delphi 의 경우는 어떨까요?

     

    program Project2;

     

    {$APPTYPE CONSOLE}

     

    uses

      SysUtils;

     

    type

      Test = class

      public

        a : Integer;

     

        procedure func;virtual;

        constructor Create;

      end;

     

      Test2 = class(Test)

      public

        procedure func;override;

      end;

     

    constructor Test.Create;

    begin

      func;

    end;

     

    procedure Test.func;

    begin

      WriteLn('Test.Func');

    end;

     

    procedure Test2.func;

    begin

      WriteLn('Test2.Func');

    end;

     

    var

      T : Test;

    begin

      T := Test2.Create;

      ReadLn;

    end.

     

    출력 결과는 아래와 같습니다.

    image6.png

     

    Delphi는 생성자 호출 시점이 VMT 생성 시점보다 뒤입니다.

    Delphi 에서는 생성자 내에서 가상함수를 호출하더라도 정상적으로 작동합니다.

    ?

    Lyn
    조회 수 52457 추천 수 0 댓글 0
    ?

    단축키

    Prev이전 문서

    Next다음 문서

    크게 작게 위로 아래로 댓글로 가기 인쇄
    ?

    단축키

    Prev이전 문서

    Next다음 문서

    크게 작게 위로 아래로 댓글로 가기 인쇄

    윈도우즈 정기 업데이트 날이죠 (...)


    이번엔 꽤 중요한 보안 이슈(원격 실행 취약점)가 해결되었다고 하니 꼭 패치하세요


    https://technet.microsoft.com/library/security/ms14-dec

    ?

    Lyn
    조회 수 51174 추천 수 0 댓글 3
    Atachment
    첨부 '4'
    ?

    단축키

    Prev이전 문서

    Next다음 문서

    크게 작게 위로 아래로 댓글로 가기 인쇄
    ?

    단축키

    Prev이전 문서

    Next다음 문서

    크게 작게 위로 아래로 댓글로 가기 인쇄

    네.. 제가 얼마전에 감격스럽게도 C++ MVP로 선정 되었는데요... 드디어 까 보았습니다.

    2.jpg


    페덱스로 배달이 왔습니다. 겉포장엔 MVP KIT 라고 씌여 있네요



    3.png


    열어보니 이런 박스가 나옵니다.

    가운데에 있는 박스는 뭐라그러나 상패? 그런게 있고위에는 일종의 인증서가 들어 있습니다. C++이라는 글자와 함께 제 이름이 박혀있네요.
    앞쪽에는 카드와 뱃지, 그리고 비밀유지서약 관련 팜플렛이 있습니다


    4.png


    카드에는 기간과 MVP ID, 이름, 분야가 씌여 있습니다. ID는 가렸습니다.그림판으로 지웠더니 다 보이네요 ;


    1.jpg



    상패에는 2013년 디스크를 끼울수 있게 되어 있습니다.  아마 나중에 또 된다면 그땐 디스크만 주지 않을까 싶네요.



    이것 외에 제공되는 것 중엔 역시 Visual Studio Ultimate MSDN 라이센스가 제공되는것이 가장 크네요. MS의 거의 전제품을 자유롭게 테스트해볼 수 있습니다 : )

    TAG •
    ?

    ?

    단축키

    Prev이전 문서

    Next다음 문서

    크게 작게 위로 아래로 댓글로 가기 인쇄
    ?

    단축키

    Prev이전 문서

    Next다음 문서

    크게 작게 위로 아래로 댓글로 가기 인쇄

    권한을 줄 아이디를 입력할 때 

    IIS AppPool\[풀 이름] 


    처럼 하면 권한을 줄 아이디가 나타난다

    ?

    Lyn
    조회 수 47090 추천 수 0 댓글 0
    Atachment
    첨부 '4'
    ?

    단축키

    Prev이전 문서

    Next다음 문서

    크게 작게 위로 아래로 댓글로 가기 인쇄
    ?

    단축키

    Prev이전 문서

    Next다음 문서

    크게 작게 위로 아래로 댓글로 가기 인쇄

    1. nuget 사이트에서 nuget command-line utility 를 다운로드 받는다.

    01.png


    2. nuget.exe를 적당한곳에 복사하고 nuget install redis-64 를 입력한다

    (redis-64 패키지의 주소는 http://www.nuget.org/packages/redis-64/   이지만 별로 중요하지 않다. 패키지 명만 알면 됨)

    02.png


    설치된 redis 는 C:\Redis-64.2.8.17 에 있다.(현재 최신버전인 2.8.17 기준)


    3. redis 를 서비스로 등록하기 위해 redis 설치 폴더로 이동해서 아래와 같은 명령을 친다(관리자권한이 필요하다)

    redis-server --service-install redis.windows.conf


    여기서의 redis.windows.conf는 기본적으로 존재하는 redis 설정 파일이다. 만약 다른설정파일이 있으면 원하는대로 수정 하면 OK

    만약 여러개 생성하는것이 목적이라면(사실 달랑 하나 쓸 경우보단 여러개 띄울 경우가 많을거다... redis 는 single thread밖에 사용하지 못하기 때문에)

    –service-name redisServiceName –port 10000 처럼 서비스명과 포트번호를 추가하여 여러개 띄울 수 있다


    03.png


    4. 서비스를 시작시키고 redis-cli 를 실행시켜 테스트 해본다

    04.png



    정상이다 : ) 이제 잘 갈궈서 (...) 써먹을일만 남았다



    TAG •
    ?

    Lyn
    조회 수 44332 추천 수 0 댓글 0
    ?

    단축키

    Prev이전 문서

    Next다음 문서

    크게 작게 위로 아래로 댓글로 가기 인쇄
    ?

    단축키

    Prev이전 문서

    Next다음 문서

    크게 작게 위로 아래로 댓글로 가기 인쇄

    이번에는 timer 클래스이다.
    단순히 시간을 재는데 이용 할 수 있는 클래스로 알고리즘 돌려놓고 시간측정하기 딱좋다.
    아래 pool 예제 등에서도 이 timer 를 사용햇으면 더 편햇을텐데... 어쩌다 보니 이게 뒤로나왔다.

    #include "boost/timer.hpp"

    #include <cstdio>

    #include <windows.h>

     

    using namespace boost;

     

    int main(int argc, char **argv)

    {

            timer t; //생성시0으로초기화

            Sleep(1234);

            printf("%lf 초지남\n", t.elapsed());

            Sleep(1766);

            printf("%lf 초지남\n", t.elapsed());

            t.restart();//Restart 를이용하여다시초기화

            Sleep(512);

            printf("%lf 초지남\n", t.elapsed());

            return 0;

    }

     

    ?

    Lyn
    조회 수 42884 추천 수 0 댓글 0
    Atachment
    첨부 '2'
    ?

    단축키

    Prev이전 문서

    Next다음 문서

    크게 작게 위로 아래로 댓글로 가기 인쇄
    ?

    단축키

    Prev이전 문서

    Next다음 문서

    크게 작게 위로 아래로 댓글로 가기 인쇄

    tokenizer 는 말 그대로 Token 단위로 문자열을 쪼개는 기능을 한다.

    토큰은 사전상의 의미로는 버스탈때 내는 동전 비슷한것 (....) "의미를 갖는 최소한의 문자열" 의 의미를 갖는다. 당연히 여기에서는 후자의 의미다.

     

    c 라이브러리에서는 strtok 이 제공 되지만 이는 thread 에서 사용 할 수 없다(전역변수를 사용 하기 때문에)

    VCL 에서는 TStringList 가 비슷 한 기능을 제공 하고 MFC에서도 기억은 안나지만 (...) 비슷한 기능을 제공 한다.

     

    뭐 항상 이야기 하는 거지만 boost 는 플랫폼을 가리지 않는 다는 것 만으로도 충분한 가치가 있다 : )

    그럼 간단하게 사용 예제만 보도록 하자.

     

    첫번째 예제 소스 나간다.

     

    #include<boost/tokenizer.hpp>

    #include<string>

    #include<iostream>

     

    using namespace std;

    using namespace boost;

     

    typedef tokenizer<char_separator<char> > TOKC;

    int main(int argc, _TCHAR* argv[])

    {

             string BorlandUser = "Lyn/Yull/TestCode/Imp//bkyang/4000king/RyuJT/gilgil/esniper/civilian";        

             char_separator<char> sep("/",""); 

            

             TOKC tok(BorlandUser, sep);       

     

             for (TOKC::iterator i = tok.begin(); i != tok.end(); ++i)

             {

                      cout << *i << endl;

             }

             return 0;

    }



     

    문자열을 '/' 단위로 쪼개기로 하자.

    그럼 결과는 아래와 같다.

     

    1.png

    잘 쪼개 졋다. 그런데 한가지 주의해서 볼 점이 있는데 빈 토큰은 나타나지 않는 다는 것이다.

    빈 토큰도 살리기 위해서는 separator 의 생성자에 keep_empty_tokens 옵션을 주면 된다.

     

    두번째 예제를 보자

    #include<boost/tokenizer.hpp>

    #include<string>

    #include<iostream>

     

    using namespace std;

    using namespace boost;

     

    typedef tokenizer<char_separator<char> > TOKC;

    int main(int argc, _TCHAR* argv[])

    {

             string BorlandUser = "Lyn/Yull/TestCode/Imp//bkyang/4000king/RyuJT/gilgil/esniper/civilian";        

             char_separator<char> sep("/","", keep_empty_tokens);

            

             TOKC tok(BorlandUser, sep);       

     

             for (TOKC::iterator i = tok.begin(); i != tok.end(); ++i)

             {

                      cout << *i << endl;

             }

             return 0;

    }

     

    2.png

     

    빈 토큰도 구해진 것을 볼 수 있다 : )

    참여해 주신 볼랜드(코드기어? 엠바카데로?) 유저분에게 심심한 감사를 표하면서 끝내겠다 (__)



    PS. 유니코드 일 경우 typedef tokenizer<char_separator<wchar_t>, wstring::const_iterator, wstring> TOKC; 와 같이 선언하는것이 편리하다

    ?

    Lyn
    조회 수 42484 추천 수 0 댓글 0
    ?

    단축키

    Prev이전 문서

    Next다음 문서

    크게 작게 위로 아래로 댓글로 가기 인쇄
    ?

    단축키

    Prev이전 문서

    Next다음 문서

    크게 작게 위로 아래로 댓글로 가기 인쇄

    XMarks : 북마크 동기화


    Fire Gesutrs : 제스쳐


    FasterFox : 속도 증가(캐시 이용)

    ?

    Lyn
    조회 수 42238 추천 수 0 댓글 0
    ?

    단축키

    Prev이전 문서

    Next다음 문서

    크게 작게 위로 아래로 댓글로 가기 인쇄
    ?

    단축키

    Prev이전 문서

    Next다음 문서

    크게 작게 위로 아래로 댓글로 가기 인쇄

    이번엔 공백 처리 함수들이다. 로그인할때 아이디 처리라던지에 써먹은 기억이 있다 : )


    #include <cstdio>

    #include <string>

    #include <boost/algorithm/string.hpp>

     

    using namespace boost;

    using namespace std;

     

    int _tmain(int argc, _TCHAR* argv[])

    {

            string str1 = "   I love Lyn!      ";

            string str2;

            trim(str1); //양옆의공백을다제거한다.

            printf("trim : %s\n", str1.c_str());

     

            str1 = "   I love Lyn       ";

            str2 = trim_copy(str1); //양옆의공백을다제거하되원본을변경하지않고새로운문자열을리턴

            printf("trim_copy : %s, %s\n", str1.c_str(), str2.c_str());

     

            str1 = "#######   I love Lyn       #######";

            trim_if(str1,is_any_of("#")); //제거할문자를직접지정해서제거한다.

            printf("trim_if-1 : %s\n", str1.c_str());

     

            str1 = "!@#!@#I love Lyn!@#!@#";

            trim_if(str1,is_any_of("!@#")); //제거할문자열의길이에는제한이없다!

            printf("trim_if-2 : %s\n", str1.c_str());

     

            str1 = "   I love Lyn       ";

            trim_left(str1); //왼쪽의공백을제거한다.

            printf("trim_left : %s\n", str1.c_str());

           

            //물론trim_left_copy, trim_left_if, trim_right, trim_right_copy, trim_right_copy_if 등등.. 있을함수는다있다.

            //여기서는몇가지만소가했지만네이밍규칙을보면다알수있을수준이다.

            return 0;

    }

     

    ?

    Lyn
    조회 수 41798 추천 수 0 댓글 0
    ?

    단축키

    Prev이전 문서

    Next다음 문서

    크게 작게 위로 아래로 댓글로 가기 인쇄
    ?

    단축키

    Prev이전 문서

    Next다음 문서

    크게 작게 위로 아래로 댓글로 가기 인쇄

    이번엔 문자열 다루는 알고리즘 들이다.
    C++ 기본 라이브러리에서 당연히 지원 해줄것 같은데 안해주는(...) 문자열 관련 함수들을 담고 있다.
    첫번째로 대소문자 변경 기능만 살펴본다.

    String 알고리즘은 원본을 변형하는 형태와, 원본을 보존하고 새로운 문자열을 리턴하는 두가지 형태로 지원되는점이 특징이다.

    #include <string>

    #include <boost/algorithm/string.hpp>

     

    using namespace std;

    using namespace boost;

     

    int _tmain(int argc, _TCHAR* argv[])

    {

            string str = "Hello Lyn!";

            string str2;

            printf("%s\n", str.c_str());

     

            to_upper(str); //모두대문자로바꾼다

            printf("%s\n", str.c_str());

            to_lower(str); //모두소문자로바꾼다

            printf("%s\n", str.c_str());

           

            str = "Hello Lyn!";

            str2 = to_upper_copy(str); //모두대문자로바꾸되원본을변형하지않고새로운문자열을리턴

            printf("%s %s\n", str.c_str(), str2.c_str());

            str2 = to_lower_copy(str); //모두소문자로바꾸되원본을변형하지않고새로운문자열을리턴

            printf("%s %s\n", str.c_str(), str2.c_str());

     

            return 0;

    }

    ?

    Lyn
    조회 수 41510 추천 수 0 댓글 0
    Atachment
    첨부 '1'
    ?

    단축키

    Prev이전 문서

    Next다음 문서

    크게 작게 위로 아래로 댓글로 가기 인쇄
    ?

    단축키

    Prev이전 문서

    Next다음 문서

    크게 작게 위로 아래로 댓글로 가기 인쇄

    이번엔 lexical_cast 에 대해서 알아보자.
    cast 란 이름에서 눈치챗겠지만 일종의 캐스팅 연산자(인 척 하는 함수) 이다.
    string -> Integer 같은 상식적으론 말이 되지만 C++문법상으론 말이 안되는 일들을 처리한다.

    일단 예제코드를 소개한다.
    timer 에 관해서는
    http://lunapiece.net/3795 를 참조해라

    #include <boost/lexical_cast.hpp>

    #include <cstdlib>

    #include <string>

    #include <boost/timer.hpp>

     

    using namespace std;

    using namespace boost;

     

    string Number = "1024768";

     

    void StrToIntAtoi()

    {

            int num;

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

            {

                   num = atoi(Number.c_str());

            }

            printf("atoi : %d\n", num);

    }

    void StrToIntStringStream()

    {

            stringstream sst;

            int Num;

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

            {

                   sst << Number.c_str();

                   sst >> Num;

            }

            printf("StringStream : %d\n", Num);

    }

    void StrToIntLexicalCast()

    {

            int num;

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

            {

                   num = lexical_cast<int>(Number);

            }

            printf("LexicalCast : %d\n", num);

    }

    int _tmain(int argc, _TCHAR* argv[])

    {

            timer t;

            printf("atoi 를이용한방법\n");

            t.restart();

            StrToIntAtoi();

            printf("%lf \n", t.elapsed());

     

            printf("StringStream 을이용한방법\n");

            t.restart();

            StrToIntStringStream();

            printf("%lf \n", t.elapsed());

     

            printf("lexical_cast 를이용한방법\n");

            t.restart();

            StrToIntLexicalCast();

            printf("%lf \n", t.elapsed());

     

            return 0;

    }

     

    .....

    예제코드 자체는 별 문제가 없었으리라 본다. 준비작업도 필요없고 간단하게 쓸 수 있어서 참 좋아보이기는 한다.
    하나의 함수로 이런저런 캐스팅을  다 할수 있으니 다형성도 뛰어나다.
    그러나 치명적인 문제가 딱 하나 있는데...

     

     

    바로 이거다 속도 =_=;

    stringstream 에 비해 약 10배, atoi 에 비해선 대략 50배 가량 느리다 =_=;; 편의성을 위해서 가끔 쓰는정도라면 몰라도 자주 반복되는 루틴에서는 쓰지 않아야 하겠다.

    ?

    Lyn
    조회 수 40970 추천 수 0 댓글 1
    ?

    단축키

    Prev이전 문서

    Next다음 문서

    크게 작게 위로 아래로 댓글로 가기 인쇄
    ?

    단축키

    Prev이전 문서

    Next다음 문서

    크게 작게 위로 아래로 댓글로 가기 인쇄

    - 2010.1.3 지금 이걸 계속 써야 하나 말아야하나 고민중이다. 단돈 몇만원이 아까운게 아니라면 Beyond Compare 사서 써라. 진리다. 비교할 의미자체를 모르겠다(단. 3Way Marge 는 내가 쓰지 않아 잘 모른다)

      2011.11.22 2년사이에 많이 바뀌어서 내용을 추가


    [파일비교]

    Araxis Merge :

      달라진 부분을 보여주는 가이드라인이 너무 작아서 눈에 띄지 않음.

      단 선으로 바뀐 부분을 보여주기에 매우 많은 길이의 코드가 연속적으로 달라졋을 경우 직관적.

     

    Beyond Compare :

      바뀐 부분을 영역색, 글씨색으로 나누어 보여주어서 보기 편함.

      달라진 부분을 보여주는 가이드라인의 길이가 화면크기와 별도로 나타나 직관적이지 않음.

      바뀐 부분이 한 줄의 일부, 아니면 몇줄 이하일 경우 직관적으로 보이나, 바뀐 부분의 길이가 길 경우, 특히 한페이지가 넘어 갈 경우는 보기가 상당히 불편하다.

     

    Ultra Compare : 

      돈내고 쓸 가치가 없다. 알고리즘이 개판이다.


    Kdiff

      한글 지원이 쓰레기 of 쓰레기라 배제


    WinMerge

      제작자는 이 UI로 정말 코드 비교를 할수 있는지 한번 묻고싶다


    [폴더비교]

    Araxis Merge :

      폴더 비교 창이 매우 난잡하여 보기힘들다.

      필터가 매우 단순

      최신버전에서 필터 기능이 강력해졋다

    Beyond Compare :

      알아보기 쉽게 색으로 잘 표시되어 있다.

      필터링 기능이 매우 막강하다. 특히 프로그래머가 jpg, bmp 파일을 Compare 툴로 보는것은 의미가 없는데 필터 옵션에서 아주 간단하게 걸러낼 수 있다.


    [기타기능]

    Araxis Merge:

      소스비교, 바이너리 비교. 그 이상의 잡기능은 없다

    Beyond Compare:

      Mp3비교, 이미지 비교 등등 굉장히 다양한 포맷을 비교하는 기능을 제공한다.


    [가격]

    Araxis Merge :

      비싼 값에 비해, 밥값을 못한다.

      이제 밥값정돈 한다. 그래도 비싸다

    Beyond Compare :

      유료이긴 하지만 비싸지 않고, 이 가격대에 이정도 툴이면 매우 훌륭하다.

     

    KDiff, WinMerge : 

      싼게 비지떡이다. 정말 돈이 없다면 그때나 고민해 보자


     

    PS. DiffMerge, AcroDiff 등 비교 예정

    ?

    Lyn
    조회 수 39768 추천 수 0 댓글 0
    ?

    단축키

    Prev이전 문서

    Next다음 문서

    크게 작게 위로 아래로 댓글로 가기 인쇄
    ?

    단축키

    Prev이전 문서

    Next다음 문서

    크게 작게 위로 아래로 댓글로 가기 인쇄

    볼포 모임에서도 이런저런 얘기 하다 나온거지만...

    델파이 / C++빌더 초보들은 웃기는 특징을 하나 가지고 있다.



    뭔가 하다가 안된다.

    그럼 이렇게 생각한다. '아 이게 델파이의 한계인가'


    .... 웃긴다.

    그런사람들이 VC를 하면서 똑같은 문제에 부딛히면 이렇게 생각한다 '아 내가 뭘 잘못한게 있나보다'

    ... 진짜 웃긴다




    뭔가 하려고 할때 개발툴 자체에 한계가 있는 경우는 드물다.

    (물론 VM 의 한계, Native의 한계 등이 있지만... 이 경우는 애초에 언어선택자체가 잘못된거니까 제쳐두자.)

    개발툴 탓하기 전에 자신의 실력을 한번쯤 돌아보길.





    근데... 난 델파이 프로그래머 아니잖아.... 안될거야 아마

    ?

    Lyn
    조회 수 39409 추천 수 0 댓글 0
    ?

    단축키

    Prev이전 문서

    Next다음 문서

    크게 작게 위로 아래로 댓글로 가기 인쇄
    ?

    단축키

    Prev이전 문서

    Next다음 문서

    크게 작게 위로 아래로 댓글로 가기 인쇄

    http://google-styleguide.googlecode.com/svn/trunk/cppguide.xml#Non-ASCII_Characters


    번역중


    헤더파일

      - 중복방지를 위한 헤더가드 : 모든 헤더는 중복 include 를 막기 위한 헤더가드를 가진다. 이는 <PROJECT>_<PATH>_<FILE>_H_ 의 포맷으로 만들어져있어야 한다

      - 헤더파일 의존성 : 번역중

      - 인라인 함수 : 10줄 혹은 그보다 작은 함수에만 사용한다.

      - -ini.h 파일 : 복잡한 인라인함수를 만들어야 할 때 ~ini.h 형태를 사용한다

      - 함수 파라메터 순서 : 함수 파라메터는 입력파라메터가 출력 파라메터보다 먼저 온다



    ?

    Lyn
    조회 수 39200 추천 수 0 댓글 0
    ?

    단축키

    Prev이전 문서

    Next다음 문서

    크게 작게 위로 아래로 댓글로 가기 인쇄
    ?

    단축키

    Prev이전 문서

    Next다음 문서

    크게 작게 위로 아래로 댓글로 가기 인쇄

    1. Visual Studio 2008  2010 + Intel C++ 11.1 12.1 // 개발툴.

        - Visual Assist // VS Add-on


    2. Delphi 2007, Delphi 2010 XE2 //개발툴

        -Castailla // Delphi Add-on


    3. VirtualBox 

    가상머신 가벼워서 자주 이용


    3. VMWare

    VirtualBox 가 불안해서 VMWare로 완전 이사


    4. TeraCopy

    쉘확장. 윈도우의 파일복사/이동 기능을 확장해줌.

    복사중 일부분 취소, 일시정지, Copy<->Move 전환기능 등


    걍 Total Commander 쓰기로 했다. 윈도7에선 TeraCopy가 너무 느리다.


    5. Microsoft Office 200 2010 // 문서


    6. Total Commander

    다기능 툴이지만 파일이름변경, FTP 등의 기능을 주로 이용


    7. 구글 크롬

        - IE Tab

        - Gesture

    초고속 웹 브라우저


    8. Beyond Compare 3

    파일비교 솔루션. 개발보조툴


    9. Tortoise SVN, TortoiseHG

    SVN 프론트엔드. 개발보조툴


    10. Warcraft III, Startcraft II

    게임


    11. NateOn, Google Talk, MSN

    메신저

    ?

    Lyn
    조회 수 38973 추천 수 0 댓글 1
    ?

    단축키

    Prev이전 문서

    Next다음 문서

    크게 작게 위로 아래로 댓글로 가기 인쇄
    ?

    단축키

    Prev이전 문서

    Next다음 문서

    크게 작게 위로 아래로 댓글로 가기 인쇄

    Delphi 2009(Win32) 에서 추가된 Generics 에 관해서 간략하게 써 보겠습니다.

     

    Generic 이란 임의의 타입에 대해 동작하는 매소드(클래스) 를 만드는 방법으로, C++에서는 Template이라 불리며 C#, Java 등에서는 Generics 라 불리는 기능이며, 일반화 프로그래밍을 가능하게 해주는 방법입니다.

     

    간단한 예제를 하나 만들어 보겠습니다

    변수의 크기를 출력하는 간단한 예제입니다.

     

     

    program Project1;

     

    {$APPTYPE CONSOLE}

     

    uses

      SysUtils, VarUtils;

     

    type

      TempClass = Class

      public

        procedure GetSize<T>(Item : T);

      end;

     

      TempRecord = packed Record

        A : Integer;

        B : Double;

        C : Double;

      End;

     

    procedure TempClass.GetSize<T>(Item : T);

    begin

      WriteLn('Size : ', Sizeof(Item));

    End;

     

    var

      A : TempClass;

      B : Integer;

      C : TempRecord;

    begin

      A.GetSize(3000);

      A.GetSize(4.0);

     

      B := 30;

      A.GetSize(B);

     

      A.GetSize(C);

      ReadLn;

    end.

     

     

    결과는 각각

    2, 10, 4, 20 이 나옵니다.

     

    여기서 즉 T 이라는 임의의 Type 에 관해서 크기를 알아 올 수 있는겁니다.

     

    여기서 어떻게 Type 이 지정 되냐를 알 수 있는데.

    3000을 넘었을 경우는 2byte 정수형 으로 인정한 것을 알 수 있습니다.

     

    정수형의 경우는 입력한 숫자의 크기에 따라 1~8 byte 정수형으로 함수가 생성 됩니다.

    4.0을 넣었을 경우는 크기가 10이 나왔는데, 모든 실수형은 Extended 형으로 생성합니다.

     

    Integer형의 변수 B를 넣었을 경우는 이미 타입이 Integer  라는 것을 명시적으로 선언 하였으므로 크기는 4Byte 가 됩니다.

     

    TempRecord 형의 변수는 당연히 20Byte가 나옵니다(Integer + Double + Double)

     

    이처럼 하나의 함수로 다양한 Type 에 대한 계산이 가능해 집니다.

     

    이것은 컴파일 시점에 GetSize 라는 함수를 프로그래머가 사용한 Type 에 따라 여러벌 만들기에 가능한 것입니다.

     

    디스어셈블한 코드를 구경 해보도록 하겠습니다

    Project1.dpr.22: WriteLn('Size : ', Sizeof(Item));

    0040E1BB A1980A4100       mov eax,[$00410a98]

    0040E1C0 BAF0E14000       mov edx,$0040e1f0

    0040E1C5 E86A73FFFF       call @Write0UString

    0040E1CA BA0A000000       mov edx,$0000000a

    0040E1CF E89856FFFF       call @Write0Long

    0040E1D4 E8BF56FFFF       call @WriteLn

    0040E1D9 E8724DFFFF       call @_IOTest
    Project1.dpr.22: WriteLn('Size : ', Sizeof(Item));

    0040E200 A1980A4100       mov eax,[$00410a98]

    0040E205 BA30E24000       mov edx,$0040e230

    0040E20A E82573FFFF       call @Write0UString

    0040E20F BA04000000       mov edx,$00000004

    0040E214 E85356FFFF       call @Write0Long

    0040E219 E87A56FFFF       call @WriteLn

    0040E21E E82D4DFFFF       call @_IOTest

    Project1.dpr.22: WriteLn('Size : ', Sizeof(Item));

    0040E240 A1980A4100       mov eax,[$00410a98]

    0040E245 BA70E24000       mov edx,$0040e270

    0040E24A E8E572FFFF       call @Write0UString

    0040E24F BA14000000       mov edx,$00000014

    0040E254 E81356FFFF       call @Write0Long

    0040E259 E83A56FFFF       call @WriteLn

    0040E25E E8ED4CFFFF       call @_IOTest

     

     

    이와 같이 같은 이름의 함수가 비슷비슷한 코드로 여러벌 생성 되어 메모리에 들어있는 것을 확인 할 수 잇습니다.

     

     

    이번엔 Type 을 명시적으로 알려주는 코드를 보도록 하겠습니다.

    A.GetSize(3); 의 호출 결과는 1이었습니다.

    숫자가 작기떄문에 최대한 작은 크기의 메모리를 소모 하기 위하여 Byte 형으로 선언 된 결과입니다.

     

    그런데 간혹 정해진 Type 이 필요 할 수도 있습니다.

    이럴경우 Type 을 명시적으로 지정해서 호출 해 보겠습니다.

     

    A.GetSize<Integer>(3);

    로 코드를 변경 한 후 호출하면 4가 출력 되는 것을 알 수 있습니다.

     

    무조건 Integer 형의 함수를 호출 하라는 일종의 협박(?) 비슷한 코드를 만들어 낸 셈이 되는겁니다.

     

     

     

    다음으로 알아 볼 것은 모든 알고리즘이 임의의 타입에 대해 동작 될 수 없는 경우에 관한 해결책입니다.

     

    위의 코드에서 Integer, Double, Record 형의 크기는 아무 문제 없이 구할 수 있었지만, String 형의 크기는 sizeof() 연산자로 구할 수 없습니다.

     

    이럴경우 String에 관해서만 특별한 처리를 해 주어야 하는데이는 익히 잘 아시는 오버로딩을 응용하면 됩니다.

     

    예제코드를 보겠습니다.

    program Project1;

     

    {$APPTYPE CONSOLE}

     

    uses

      SysUtils, VarUtils;

     

    type

      TempClass = Class

      public

        procedure GetSize(Item : String); overload;

        procedure GetSize<T>(Item : T); overload;

      end;

     

      TempRecord = packed Record

        A : Integer;

        B : Double;

        C : Double;

      End;

     

    procedure TempClass.GetSize<T>(Item : T);

    begin

      WriteLn('Size : ', Sizeof(Item));

    End;

     

    procedure TempClass.GetSize(Item : String);

    begin

      WriteLn('Size : ', Length(Item));

    End;

     

    var

      A : TempClass;

      B : Integer;

      C : TempRecord;

    begin

      A.GetSize<Integer>(3);

      A.GetSize(4.0);

     

      B := 30;

      A.GetSize(B);

     

      A.GetSize(C);

      A.GetSize('Hello Delphi!');

      ReadLn;

    end.

     

    GetSize 매소드를 T 형과 String 형에 관해 각각 오버로딩 하엿습니다.

    String 형으로 GetSize 를 호출하면 Length 를 호출해서 계산하는 함수가, 다른 형으로 GetSize를 호출하면 Sizeof 를 사용하는 함수가 호출 됩니다.

     

    이처럼 특정한 Type 에 관해 매소드를 다른 형태로 사용 할 수 있습니다.

     

     

    지금까지는 함수의 Generic 을 살펴 보았습니다. 이제 클래스에 관한 Generic 을 살펴 보도록 하겠습니다.

     

    program Project1;

     

    {$APPTYPE CONSOLE}

     

    uses

      SysUtils, VarUtils;

     

    type

      TempClass<T> = Class

      public

        Item : T;

      end;

     

      TempRecord = packed Record

        A : Integer;

        B : Double;

        C : Double;

      End;

     

    var

      A : TempClass<String>;

      B : TempClass<Integer>;

      C : TempClass<TempRecord>;

    begin

      A := TempClass<String>.Create;

      A.Item := 'Hello RAD Studio!';

     

      B := TempClass<Integer>.Create;

      B.Item := 512;

     

      C := TempClass<TempRecord>.Create;

      C.Item.A := 55805096;

      C.Item.B := 3.14159;

      C.Item.C := 0.0;

      ReadLn;

    end.

     

    위 코드에서는 T 라는 임의의 타입을 가지는 클래스 TempClass<T> 를 선언하엿습니다.

    그리고 각각 : String, Integer, TempRecord 형을 가지는 클래스 TempClass<String>. TempClass<Integer>, TempClass<TempRecord> 를 선언 하엿습니다 (TempClass<타입명> 까지가 클래스 이름이라고 생각하시면 편합니다)

     

    그리고 각각의 클래스를 생성하고 Item 변수에 접근하여 값을 넣었습니다.

    선언한 형태에 따라 TempClass 는 여러 타입의 Item 을 가지는 클래스로 탄생합니다.

     

    TIntegerList, TDoubleList 등등을 따로 제작 할 필요 없이 임의의 타입을 가지는 콜렉션을 만들어 쓸 수 있습니다. 사실 이게 Generic을 쓰는 가장 큰 이유라고도 할 수 있습니다. Generic 으로 콜렉션을 만들면 캐스팅이 필요 없고, 타입이 명확하기 때문에 편리하게 사용 할 수 있습니다.

    (.net Dictionary<> , STL Vactor<>, List<>, MFC CArray<,> 등이 그에 해당합니다)

    마지막으로 한가지만 더 보도록 하겠습니다.

    program Project1;

     

    {$APPTYPE CONSOLE}

     

    uses

      SysUtils, VarUtils;

     

    type

      TempClass<T1, T2> = Class

      public

        Item1 : T1;

        Item2 : T2

      end;

     

    var

      A : TempClass<String, Integer>;

    begin

      A := TempClass<String, Integer>.Create;

      A.Item1 := 'Hello RAD Studio!';

      A.Item2 := 3;

     

      ReadLn;

    end.

     

    임의의 Type 은 반드시 한 개여야 한다는 제약은 없습니다.

    여러 개의 임의의 Type 을 가지는 클래스도 얼마든지 선언 가능합니다.

     

    위 코드는 2개의 임의의 Type을 가지는 TempClass<,> 를 선언, 사용 한 모습입니다.

     

     

    PS. 간단한 함수도 매소드로 만들어서 예제를 구성했는데... 제가 잘 못해서 그런건진 모르겠는데, 델파이의 제네릭은 전역함수의 제네릭화를 허용 하지 않는 것 같습니다.

     

    ----

    ?
    • ?
      GomSun2 2010.06.17 19:54

      와우 좋은 강좌 잘봣음. :)


    Board Pagination Prev 1 2 3 4 5 6 7 ... 8 Next
    / 8