Lyn
조회 수 39066 추천 수 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
조회 수 40854 추천 수 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 등 비교 예정

?

2009.08.19 03:08

C++ new 연산자의 진실

Lyn
조회 수 57940 추천 수 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
조회 수 42740 추천 수 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
조회 수 42114 추천 수 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
조회 수 41662 추천 수 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
조회 수 41401 추천 수 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
조회 수 36748 추천 수 0 댓글 0
?

단축키

Prev이전 문서

Next다음 문서

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

단축키

Prev이전 문서

Next다음 문서

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

multi_array는 다차원 정적배열을 지원하는 클래스이다.
이를 이용하는 다른 방법은 std::tr1::array 를 중첩해서 사용 하는 방법이 있는데, 이것을 좀 더 편하게 확장했다고 보면 되겠다.

std::tr::array에 관한 내용은 아래 링크를 참조해라
http://lunapiece.net/Article/476

 

아래는 multi_array와 중첩된 array에 관한 예제코드이다. 이것도 별 내용이 없다보니 그냥 코드 보여주는걸로 끝낸다.

 

#include "stdafx.h"

#include <array>

#include <multi_array.hpp>

#include <cstdio>

 

using namespace boost;

 

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

{

        //TR1Array를이용한Static Array of Array 구현

        printf("std::tr1::array<std::tr1::array<int,20>, 10>\n");

        std::tr1::array<std::tr1::array<int,20>, 10> Stdarr; //int[10][20] 선언

        //10*20인데선언시에는숫자가반대로씌여지므로헤깔린다!

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

        {

               for(int j = 0;j < 20; ++j)

               {

                       Stdarr[i][j] = 1;

               }

        }

 

        //boost::multi_array

        printf("multi_array 사용\n");

        multi_array<int, 2> MultiArray(extents[10][20]);

        //int2차원배열[10][20]을선언. 템플릿인자가Type 과차원을나타냄.

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

        {

               for(int j = 0;j < 20; ++j)

               {

                       MultiArray[i][j] = 2;

               }

        }

       

        printf("multi_array range 조정\n");

        //Pascal 처럼배열의인덱스를변형할수있는기능을제공한다.

        typedef multi_array<int, 2>::extent_range range; //이름이너무길어서ㅡ.

        multi_array<int, 2> MultiArray2(extents[range(1,4)][range(20,40)]);

        //Pascal 과비교하면Array [1..4-1] of Array[20..40-1] of Integer 라고할수있겠다.

        for (int i = 1; i < 4 ; ++i)

        {

               for(int j = 20;j < 40; ++j)

               {

                       MultiArray2[i][j] = 2;

               }

        }

        return 0;

}

 

 

 

?

Lyn
조회 수 44218 추천 수 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;

}

 

?

2009.05.11 22:44

[Boost 살펴보기] 2. any

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

단축키

Prev이전 문서

Next다음 문서

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

단축키

Prev이전 문서

Next다음 문서

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

any 는 마치 스크립트 언어처럼 임의의 type을 가지는 변수를 만들 수 있다.
COM의 Varient 와도 비슷하다 하겠다.

하지만 이것을 언어수준에서 구현 해 놓았고, 다른 컨테이너와 매우 잘 맞물린다는것이 장점이라 하겠다.
vector 에 이런저런 잡데이터 쑤셔넣을때 참 좋더라
(당연히 오버헤드는 있다. 꼭 필요한 곳에만 사용하자!)

#include "stdafx.h"
#include <cstring>
#include <cstdio>
#include <boost/any.hpp>
#include <string>
#include <vector> 

using namespace boost;

using namespace std;

 

struct MyData

{

        int i;

        double d;

        MyData()

        {

        }

        MyData(const MyData& t) //Any의요구사항1 Copy Constructor

        {

               this->i = t.i;

               this->d = t.d;

        }

        MyData& operator =(const MyData &t) //Any의요구사항2 = operator overloading

        {

               this->i = t.i;

               this->d = t.d;

        }

};

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

{

        any Data;

        //값이비어있는지를확인하는방법

        if(Data.empty())

        {

               puts("Data 는비어있음!");

        }

        else

        {

               puts("Data 는비어있지않음!");

        }

        Data = 3.14; //실수리터럴의기본타잎은double

        Data = 3; //정수리터럴의기본타잎은Int

 

        //Data = "Hello!"; //에러.

        //any 에넣기위해선CopyConstructor = Operator Overloading 이필요하다.

        string str = "Hello";

        Data = str; //정상적

 

        //Any 에어떤타잎이들어있는지확인

        //type매소드로type 을확인후any_cast 연산자를사용하여캐스팅

        if(Data.type() == typeid(int))

        {

               printf("DataType : Int, Value : %d\n", any_cast<int>(Data));

        }

        else if(Data.type() == typeid(double))

        {

               printf("DataType : Double, Value : %lf\n", any_cast<double>(Data));

        }

        else if(Data.type() == typeid(string))

        {

               printf("DataType : string, Value : %s\n", any_cast<string>(Data).c_str());

        }

 

        //Vector Any 의복합사용예.

        printf("임의의타잎을담는Vector 선언하기\n");

        vector<any> AnyVector;

        MyData myData;

        myData.d = 5.12;

        myData.i = 5;

 

        AnyVector.push_back(3);

        AnyVector.push_back(3.14);

        AnyVector.push_back(myData);

 

        vector<any>::iterator it;

        for (it = AnyVector.begin(); it != AnyVector.end(); ++it)

        {

               if(it->type() == typeid(int))

               {

                       printf("Any Type : Int, Value : %d\n", any_cast<int>(*it));

               }

               else if(it->type() == typeid(double))

               {

                       printf("Any Type : Double, : %lf\n", any_cast<double>(*it));

               }

               else if(it->type() == typeid(MyData))

               {

                       printf("Any Type : MyData, Value : %d, %lf\n", any_cast<MyData>(*it).i, any_cast<MyData>(*it).d);

               }

        }

        return 0;

}

 

 

?

2009.05.11 22:04

[Boost 살펴보기] 1. pool

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

단축키

Prev이전 문서

Next다음 문서

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

단축키

Prev이전 문서

Next다음 문서

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

TR1에 포함되지 않은 Boost 의 다른 요소들 중 자주 쓸 만한 것들을 한번 눈여겨 볼까 한다

첫번째 예제는 Pool이다. 메모리를 미리 생성 해 놨다가 해제/생성이 반복 될 경우 미리 생성해 놓은 메모리를 재활용 하는데 유용하게 사용 할 수 있다. 개인적으로도 많이 구현해서 써 보았고 탁월한 메모리 성능을 보여 주는 방법이다.

아래에는 예제코드를 담았다. 예제코드에 있는 주석만으로도 충분히 사용 방법과 기능을 알 수 있을것이라 본다.
그림은 아래 코드를 실행한 속도 테스트 스크린샷이다(VS2008 Sp1 + Realese Mode)
일반적으로 메모리를 할당하는것 보다 훨신 빠름을 알 수 있다. 게다가 단편화의 문제도 적다.

#include "stdafx.h"

#include <iostream>

#include <cstring>

#include <boost/pool/pool.hpp>

#include <boost/pool/object_pool.hpp>

#include <windows.h>

 

using namespace std;

using namespace boost;

 

struct MyData

{

        int i;

 

        MyData()

        {

               puts("Constructor!");

        };

        ~MyData()

        {

               puts("Destructor!");

        }

};

struct MyData2

{

        int i;

};

 

MyData2** Array; //임시저장용

 

void UseNormalPool()

{

        pool<> pool(sizeof(MyData));

 

        MyData *p = (MyData*)pool.malloc();      

        //pool.free(p); //Free를하지않을경우pool이파괴될때pool에서생성한모든메모리가파괴됨.

}

 

void UseObjectPool()

{

        object_pool<MyData> pool;

 

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

        {

               MyData *p = pool.malloc();

               //기본적으로같지만파괴시Destructor 가호출됨(주의: Constructor 는호출되지않음)

        }

}

void Test1NewAndDelete()

{

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

        {

               MyData2* p = new MyData2;

               delete p;

        }

}

void Test1MallocAndFree()

{

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

        {

               MyData2* p = (MyData2*)malloc(sizeof(MyData2));

               free(p);

        }

}

void Test1BoostPool()

{

        pool<> pool(sizeof(MyData));

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

        {

               MyData *p = (MyData*)pool.malloc();  

               pool.free(p);

        }

}

void Test2NewAndDelete()

{

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

        {

               Array[i] = new MyData2;

        }

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

        {

               delete Array[i];

        }

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

        {

               Array[i] = new MyData2;

        }

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

        {

               delete Array[i];

        }

}

void Test2MallocAndFree()

{

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

        {

               Array[i] = (MyData2*)malloc(sizeof(MyData2));

        }

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

        {

               free(Array[i]);

        }

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

        {

               Array[i] = (MyData2*)malloc(sizeof(MyData2));

        }

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

        {

               free(Array[i]);

        }

}

void Test2BoostPool()

{

        pool<> pool(sizeof(MyData2));

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

        {

               Array[i] = (MyData2*)pool.malloc();  

        }

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

        { 

               pool.free(Array[i]);

        }

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

        {

               Array[i] = (MyData2*)pool.malloc();  

               //Free후재할당을할경우이미생성된메모리가재사용됨

               //Free를하더라도실제메모리는파괴되지않음

        }

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

        { 

               pool.free(Array[i]);

        }

}

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

{

        UseObjectPool();

        UseNormalPool();

       

        Array = new MyData2*[1000000];

        int TickCount;

 

        puts("Alloc and Free 1000000 count");

        TickCount = GetTickCount();

        Test1NewAndDelete();

        printf("new And delete : %dms\n", GetTickCount() - TickCount);

 

        TickCount = GetTickCount();

        Test1MallocAndFree();

        printf("malloc And Free : %dms\n", GetTickCount() - TickCount);

 

        TickCount = GetTickCount();

        Test1BoostPool();

        printf("boost pool : %dms\n", GetTickCount() - TickCount);

 

        puts("Alloc 1000000 count and Free 1000000 count");

 

        TickCount = GetTickCount();

        Test2NewAndDelete();

        printf("new And delete : %dms\n", GetTickCount() - TickCount);

 

        TickCount = GetTickCount();

        Test2MallocAndFree();

        printf("malloc And Free : %dms\n", GetTickCount() - TickCount);

 

        TickCount = GetTickCount();

        Test2BoostPool();

        printf("boost pool : %dms\n", GetTickCount() - TickCount);

        return 0;

}

 

?

Lyn
조회 수 15485 추천 수 0 댓글 2

"비밀글입니다."


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

단축키

Prev이전 문서

Next다음 문서

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

단축키

Prev이전 문서

Next다음 문서

크게 작게 위로 아래로 댓글로 가기 인쇄
아래코드는 C++ Builder 2009에서 테스트되었습니다.

//---------------------------------------------------------------------------

#include <vcl.h>
#include <vector>
#include <memory>
#include <iostream>
#include <cstdlib>
#pragma hdrstop

#include <tchar.h>

using namespace std;
using namespace std::tr1;

class Test
{
public:
    int i;
    Test()
    {
        cout << "Test is Create!" << endl;
    }
    Test(const Test& )
    {
        cout << "Test's Copy Constructor!" << endl;
    }
    ~Test()
    {
        cout << "Test is Destroy!" << endl;
    }
};
using namespace std;

//---------------------------------------------------------------------------

#pragma argsused
int _tmain(int argc, _TCHAR* argv[])
{
    cout << "스택에 1개 생성" <<endl;
    Test t1; // 생성 되고, 함수가 끝날때 파괴 될 것

    cout << "힙에 1개 생성" <<endl;
    Test* t2 = new Test; //생성
    cout << "힙에서 1개 파괴" <<endl;
    delete t2; //파괴

    cout << "스택에 10개 생성" <<endl;
    Test t3[10];
    vector<Test> v1;

    cout << "복사생성자가 작동한다" <<endl;
    for(int i=0;i<10;++i)
    {
        v1.push_back(t3[i]);
        //이렇게 하면 Copy Contructor 가 작동하므로 오버헤드가 큼
        //게다가 반드시 동적으로 할당해야 하는 VCL 객체에는 사용이 불가능
    }

    cout << "힙에 10개 생성 후 Vector에 넣는다" << endl;
    vector<Test*> v2;
    for(int i=0;i<10;++i)
    {
        Test* tt = new Test;
        v2.push_back(tt);
        //이렇게 하면 동적으로 할당할 수 있음.
        //그러나 vector 를 파괴하기 전에 일일히 루프를 돌면서 delete 해줘야함
        //vetror가 파괴될때 내장하고 있는 모든 객체를 파괴할 수는 없을까?
        //VCL 에서는 TObjectList 클래스를 제공한다! 그러나 이는 C++ 표준이 아니다.
        //게다가 Templete Class 가 아니라 Casting 을 추가로 해줘야 한다는 부담도있다
    }
    cout << "Vector 에 있는 10개를 파괴" << endl;
    for(int i = 10 - 1;i >= 0;--i)
    {
        delete v2[i];
    }

    cout << "shared_ptr 을 이용하여 넣는다" << endl;
    vector<shared_ptr<Test> > v3;
    for(int i=0;i<10;++i)
    {
        shared_ptr<Test> spt(new Test);
        v3.push_back(spt);
    //vector 인 v3 역시 Main이 끝나야 파괴됨.
    //v3이  파괴되기 전 shared_ptr 의 효과에 의해 v3 이 가지고있던 Test가 모두 파괴
    }

    cout << "벡터 자체를 동적으로 생성해서 테스트 해 보자" << endl;
    vector<shared_ptr<Test> > *v4 = new vector<shared_ptr<Test> >;
    for(int i=0;i<10;++i)
    {
        shared_ptr<Test> spt(new Test);
        v4->push_back(spt);
    }
    cout << "벡터를 박살내면 가지고 있던 Test 10개도 다 박살난다" << endl;
    delete v4;

    system("pause"); //이게 끝나야 Main 이 끝나므로 t1은 이 뒤에 파괴
    //vector 인 v3 역시 Main이 끝나야 파괴됨.
    //v3이  파괴되기 전 shared_ptr 의 효과에 의해 v3 이 가지고있던 Test가 모두 파괴
}
//---------------------------------------------------------------------------

/*
Ps. CopyConstructor 가 10번이 아닌 그 이상 실행 되는데.. 그 이유는 Vector 가 공간이 부족해서 재할당을 하면서 다시 복사생성자가 호출되기 때문입니다. 생각보다 오버헤드가 많이큽니다.
*/
?

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

단축키

Prev이전 문서

Next다음 문서

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

단축키

Prev이전 문서

Next다음 문서

크게 작게 위로 아래로 댓글로 가기 인쇄
STL 에서는 B-Tree 기반의 Collection 이 준비되어 있는데, set ,multiset ,map ,multimap 의 4 종류이다.

그에 반헤 TR1에서는 위의 4종류의 Collection 에 대응되는 Hash 기반의 Collection 을 제공한다.
이름하여 unordered 시리즈(unordered_set, unordered_multiset, unordered_map, unordered_multimap) 이다. (이름너무 길다)

기본적인 사용법은 STL 의 콜렉션과 완전히 같으니 그냥 무시하도록 하겠다.

제일 기본적인 unordered_set 에 대해서만 예제를 보자

#include <iostream>
#include <string>
#include <unordered_set>

using namespace std;
using namespace std::tr1;

int main()
{
    unordered_set<string> UnOrderSet;

    UnOrderSet.rehash(10); //버켓의 갯수를 정의한다.
    //UnOrderSet.insert("김호광");
    UnOrderSet.insert("남병철");
    UnOrderSet.insert("류종택");
    UnOrderSet.insert("린");
    UnOrderSet.insert("박지훈");

    if(UnOrderSet.find("김호광") != UnOrderSet.end())
    {
        cout << "김호광 님은 볼랜드 포럼 회원입니다." << endl;
    }
    else
    {
        cout << "김호광 님은 볼랜드 포럼 회원이 아닙니다." << endl;
    }
}

위에말한대로 기본적인 사용법은 set 과 완전히 동일하다.
중요한 것은 rehash 매소드.  버켓의 갯수가 너무 많으면 메모리 낭비가 극심해지고, 너무 작으면 성능이 나빠집니다. 이래저래 귀찮은 Collection 이라 할 수 있겠군요





기본적으로 unordered_set 은 4개의 템플릿 인수를 받아드립니다.

Value(사용할 Type),  Hash(Value 를 Hash화 하는 함수객체), Pred(비교함수객체), Alloc(할당자)의 순서인데
Hash, Pred, Alloc 은 디폴트 파라미터가 있으므로 사용 하지 않아도 무방하다.

만약 기본적으로 C++에서 제공하는 놈(int, string, double 등등...) 들이 아닌 다른놈들을 사용하려면?
2가지 구현이 필요하다.

첫째로 == 연산자의 오버로딩, 두번째로 해시함수객체의 제공이다.  unordered_set의 2번째 파라메터인 Hash를 내가 사용하기 원하는 타잎을 Hash 할 수 있도록 제공 해 주는것이 필수적이다.

#include <iostream>
#include <string>
#include <unordered_set>

using namespace std;
using namespace std::tr1;

struct TTest
{
    int i;
    double d;

    bool operator == (const TTest &T) const
    {
      return ((i == T.i) && (d == T.d));
    }


};

struct TTestHash
{
    size_t operator () (const TTest &T) const
    {
        return T.i;  //그냥 간단하게 i값을 해시값 취급 해버렷다
    }
};

int main()
{
    TTest test[2];


    unordered_set<TTest, TTestHash> UnOrderSet;

    test[0].i = 5;
    test[0].d = 3.14;

    test[1].i = 9;
    test[1].d = 180.36;

    UnOrderSet.insert(test[0]);
    UnOrderSet.insert(test[1]);

    TTest FindValue;
    FindValue.i = 5;
    FindValue.d = 3.15;

    if(UnOrderSet.find(FindValue) != UnOrderSet.end())
    {
        cout << "찾는 객체가 있습니다" << endl;
    }
    else
    {
        cout << "찾는 객체가 없습니다" << endl;
    }
}

위처럼 사용자 정의타잎 TTest 를 정의하고, 그에대한 해시함수객체 TTestHash 를 정의한 후, unordered_set 을 생성할 시에, Hash함수 객체 타잎을 제공하엿다.



결론을 내자면...

1. 기본타잎을 쓰더라도 어느정도 데이터의 양을 예측 해야 만족스러운 성능이 나온다.
2. 사용자 정의타잎을 쓰고 싶으면 Hash 를 재정의 해야 하기때문에 얼마나 Hash 를 잘 시키느냐에 따라 성능이 크게 달라진다.
3. 위의 조건을 다 만족시킨다면 상황에따라 다르겠지만 대충 set 의 2배정도의 성능을 보여주는 것 같다(속도측면, 메모리는 아무래도 소비가 크다)
4. C++ Collection 너무 많다 =_=;;; 상황에 따라 최적의 Collection 을 찾는 것 만도 일이다.
5. STL 의 Collection 들은 별 존재가치가 없어졋단 생각도 든다... 메모리 아껴야되는 상황이 아니라면(오해의 소지 매우 많음!)

Ps. C++ 에서는 보통 Containers 라 하는 것 같은데... 익숙하지 않아 그냥 일반적인 Collection 이란 용어를 사용했다.
?

2008.10.05 23:38

[TR1 살펴보기] 2. Array

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

단축키

Prev이전 문서

Next다음 문서

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

단축키

Prev이전 문서

Next다음 문서

크게 작게 위로 아래로 댓글로 가기 인쇄
예제코드

#include <iostream>
#include <array>

using namespace std;
using namespace std::tr1;

void main()
{
    array<int, 100> intarr;

    //초기화
    intarr.assign(0);  //모든 요소를 0으로 초기화한다
    for (int i = 0; i < 100; ++i)
    {
        cout << intarr[i] << " ";
    }
    //값을 대입해본다
    for (int i = 0; i < 100; ++i)
    {
        intarr[i] = i;
    }

    cout << endl << "Size : " << intarr.size() << endl;

    cout << "이터레이터를 사용한 루프" << endl;
    array<int, 100>::iterator ia;
    for (ia = intarr.begin() ; ia != intarr.end(); ++ia)
    {
        //이터레이터를 사용한 루프
        cout << *ia << " ";
    }
    cout << endl << "일반 배열처럼 접근하는 루프" << endl;
    for (int i = 0; i < 100; ++i)
    {
        cout << intarr[i] << " ";
    }

    //만약 범위를 벗어난다면 std::range_error 예외를 생성한다
//    intarr[100] = 1;
    system("pause");
}

TR1에서 추가된 array는 크기가 고정된 배열입니다.
즉 위에서의 array<int, 100> intarr 은 int intarr[100] 과 본질적으론 차이가 없습니다.

단지 관리를 편리하게 하기 위해 객체로 제공된다는것(C#, Java 와 같다고 보심 편합니다) 그리고 범위를 넘어설 시 예외를 발생한다는점이 특징입니다. (C의 배열은 범위를 넘어서도 치명적인 부분에 접근하기전까지는 오류발생을 모르는 경우도 있고.. 이경우 오류의 추적이 매우 어렵습니다)

C++ 표준 라이브러리의 일부가 될 TR1이므로 STL에서 사용하는 이터레이터 문법을 그대로 사용 할 수 있으며,
일반 배열과 본질적으로 같으므로 [] 연산자로 값을 읽고 쓸 수도 있습니다.

또한 swap, assign, size 등의 멤버함수를 제공하여, 초기화, 교체 등의 작업을 쉽게 할 수 있으며, 배열의 크기를 얻어 올 수도 있습니다.

배열에 비해 사용시의 오버헤드가 좀 있긴 하지만... 안정적인 프로그램의 메모리 관리를 위해서는 사용 해 볼만 합니다.
?

2008.10.05 23:37

[TR1 살펴보기] 1. Random

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

단축키

Prev이전 문서

Next다음 문서

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

단축키

Prev이전 문서

Next다음 문서

크게 작게 위로 아래로 댓글로 가기 인쇄
참고사이트
http://www.boost.org/doc/libs/1_36_0/libs/random/index.html

예제소스
#include <iostream>
#include <random>
#include <windows.h>

using namespace std;
using namespace std::tr1;

void main()
{
    mt19937 Generator; //난수엔진
    uniform_int<> dst(100, 500); //범위지정자
    Generator.seed(GetTickCount());  //seed 초기화
    variate_generator<mt19937, uniform_int<> > rand(Generator, dst); //난수생성기

    for( int i = 0;i < 1000; ++i )
    {
       cout << rand() << endl;   //난수생성기는 () 연산자를 난수생성 연산자로 오버로딩 해 놓았습니다.
    }
}

TR1의 난수생성기는 3개의 요소로 이루어집니다.
1. 난수엔진
2. 범위지정자
3. 난수생성기

난수 엔진은 여러가지가 있으며,  대충 아래와같습니다.

정수난수엔진

minstd_rand
rand48
lrand48 (C 기본 라이브러리와 같음)
ecuyer1988
kreutzer1986
hellekalek1995
mt11213b
mt19937(C++0X 기본  난수엔진)

실수난수엔진 (전부 lagged_fibonacci 시리즈군요)
lagged_fibonacci607
lagged_fibonacci1279
lagged_fibonacci2281
lagged_fibonacci3217
lagged_fibonacci4423
lagged_fibonacci9689
lagged_fibonacci19937
lagged_fibonacci23209
lagged_fibonacci44497

아래로 갈 수록 랜덤의 성능이 좋아지지만, 메모리소모가 커지고 속도가 느려집니다.

만약 _HAS_CPP0X_ 매크로가 정의되어있다면,
mt19937엔진은 default_random_engine 라는 이름으로 typedef 됩니다.

Ps1. C++0X 표준에서는 variate_generator<mt19937, uniform_int<>> 처럼 템플릿의 >>를 붙여쓰는것을 허용합니다.
하지만 C++0X이전의 컴파일러에서는 >>를 시프트 연산자로 인식하여 오류를 내니 주의.
VS2008 Sp1은 >>를 정상적으로 템플릿으로 인식하며 CB2009는 오류를 냅니다.

Ps2. 위의 코드는 C++Builder 2009에서 제대로 실행되지 않습니다.
코드엔 이상이 없어 보이는데... 이상하게 Devide by Zero 익셉션을 뱉네요 ㅡ.ㅡ; 구현상의 버그인듯

Ps3. 위의 코드는 VS2008 SP1에서는 치명적인 오류를 가집니다. mt19937 엔진이 범위 밖의 숫자를 뱉어냅니다. 고쳐지기전까지 쓰지마세요(보이드소프트의 김호광님 감사합니다)
?

Lyn
조회 수 38845 추천 수 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 ... 3 4 5 6 7 8 Next
/ 8