2008.10.05 23:37

[TR1 살펴보기] 1. Random

Lyn
조회 수 38376 추천 수 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
조회 수 37901 추천 수 0 댓글 0
?

단축키

Prev이전 문서

Next다음 문서

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

단축키

Prev이전 문서

Next다음 문서

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

지인이 물어온 김에 정리 해 둡니다....



Windows Thread API 는 고성능이지만 치명적인 단점이 하나 있(었)는데... 바로 대기하는 Thread를 몽땅 한번에 다 깨우는 방법이 없다는 겁니다.
Event 객체는 유용하지만 하나의 쓰래드만 깨우거나(오토리셋 이벤트) 아니면 락이 다 풀리거나(메뉴얼리셋 이벤트) 둘중 하나의 동작만을 하게됩니다.


Java에 익숙하신 분이라면 notifyAll을 유용하게 사용하셨을 거고 pthread 라이브러리를 쓰시는 분도 역시 자주 사용 하실 겁니다.


그래서 거기에 대한 몇가지 꼼수를 프로그래밍 하다 보면 만나볼 수 있는데


1. 무한 루프 돌면서 상태를 체크한다 

- CPU 낭비도 이만한게 없습니다.

2. 메뉴얼 리셋 이벤트를 이용해서 잠깐 락을 풀고 다시 건다 

- 락이 걸리지 않고 그냥 지나칠 가능성이 있어 위험합니다.

3. pthread나 ACE에 포팅된 상태변수를 가져다 쓴다 해결책이 아니잖아


실제로 ACE는 상태변수를 자체 구현하고 있는데 그 대략적인 방법은 아래와 같습니다.

1. 오토 리셋 이벤트와 크리티컬 섹션을 생성한다

2. join 할 때 마다 카운트를 증가시킨다

3. 이벤트 발생시 증가된 카운트만큼 이벤트를 Set 한다.



뭐 그리 복잡한 방법도 아니고 구현 자체는 어렵지 않은데...
어쨋든 OS의 부족한 기능을 외부에서 가져오자니 좀 그렇습니다.


어쨋든 Windows Vista/2008 부터는 여기에 대응하는 API가 추가되었습니다.
WakeAllConditionVariable라는 놈인데요. 말 그대로 모든놈 다 깨우라는 뜻입니다.


pthread의 상태변수가 그런것 처럼 상태변수 자체만으론 Lock 의 기능을 하지 않고 다른 락의 힘을 빌리게 되는데 기본적으론 Critical Section 을 사용하게 됩니다.
이 다음은 귀찮으니 소스 보면서 하겠습니다.


#include <Windows.h>
#include <cstdio>

CRITICAL_SECTION cs;
CONDITION_VARIABLE cv;

DWORD PASCAL TestThread(LPVOID pParam)
{
	EnterCriticalSection(&cs);
	
	SleepConditionVariableCS(&cv, &cs, INFINITE);
	printf("%d\n", GetCurrentThreadId());
	LeaveCriticalSection(&cs);

	return 0;
}

int _tmain(int argc, _TCHAR* argv[])
{
	InitializeCriticalSection(&cs);
	InitializeConditionVariable(&cv);

	HANDLE Threads[10];
	for(int i = 0; i < 10; ++i)
	{
		Threads[i] = CreateThread(nullptr, 0, &TestThread, nullptr, 0, nullptr);
	}
	Sleep(1000);
	WakeAllConditionVariable(&cv);
	WaitForMultipleObjects(10, Threads, TRUE, INFINITE);
	DeleteCriticalSection(&cs);
	return 0;
}


CriticalSection 에 추가적으로 ConditionVariable 을 초기화 시켜 준 후, WaitForSingleObject 대신 SleepConditionVariableCS을 사용합니다(만약 Read/Write Lock 을 사용중이라면 SleepConditionVariableSRW을 대신 이용합니다)


그리고 Thread가 대기하고 있을때 WakeAllConditionVariable를 이용해서 쓰래드를 몽땅 깨워 주면 됩니다.
당연히 대기중인 Thread가 하나도 없을땐 아무 일도 하지 않습니다.


만약 하나만 깨운다면 WakeConditionVariable를 사용할 수 있습니다.
하나만 깨운다는면에서는 동일하지만 Event 와는 동작이 미묘하게 다른것이 이벤트는 대기중인 Thread가 없어도 Set 상태가 되면 언제라도 통과할 수 있지만 WakeConditionVariable은 대기중인 Thread가 없으면 아무 일도 하지 않습니다.


덤으로 Condition Variable 은 해제 함수가 없습니다. 그냥 냅두시면 됩니다.


TAG •
?

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

단축키

Prev이전 문서

Next다음 문서

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

단축키

Prev이전 문서

Next다음 문서

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

boost  shared_ptr 은 편한 놈이지만..

문제가 생기는 상황이 몇 가지 있다 있다그 중 하나 바로 객체 내부에서 전역 함수(혹은 다른 class 의 함수)를 호출 할 때 this 를 넘기는 문제인데

 

왜나면 아래와 같은 코드가 안되기 때문이다.

 

shared_ptr과 포인터형은 서로 대입이 되지 않기 때문에 위와 같은 코드는 컴파일 에러를 뱉는다그럼 아래와 같이 고치면 어떨까.

 

 

this 를 shared_ptr<SPtrTestthisptr(this);

로 변환 해서 넘겻다.

 

이렇게 하면.

SPtrTest::Operation이 끝날때와 Func 가 끝날 때 2 destructor 가 호출 되게 되어 에러가 난다.

이걸 방지하는 방법은 자기 자신의 shared_ptr 을 레퍼런스 카운트 문제가 생기지 않도록(this 를 넘길 때도 레퍼런스 카운터가 증가 하는하는 것이다.

 

즉 boost::enable_shared_from_this<T> 를 상속 받은 class 를 만드는 방법이다.

boost::enable_shared_from_this<T> 를 상속 받은 뒤 shared_from_this() 를 호출 하면 자기 자신의 shared_ptr 을 리턴 해 준다.

 

그럼 아래와 같이 된다

#include <boost/shared_ptr.hpp>

#include <cstdio>

 

using namespace boost;

 

class SPtrTest;

 

void Func(shared_ptr<SPtrTestsp);

 

class SPtrTest

{

public:

        int var1;

        SPtrTest()

        {

               printf("Constructor\n");

        }

        ~SPtrTest()

        {

               printf("Destructor\n");

        }

        void operation()

        {

              

               Func(this);

        }

};

 

void Func(shared_ptr<SPtrTestsp)

{

        sp->var1++;

        printf("%d\n"sp->var1);

};

 

int _tmain(int argc_TCHARargv[])

{

 

        shared_ptr<SPtrTesttestclass(new SPtrTest);

        testclass->var1 = 0;

        testclass->operation();

              

        return 0;

}

 

이렇게 하면 아무 문제 없이 작동이 가능하다.

 

 

 

Ps. 특히 조심해야 할 것은..

shared_ptr<SPtrTestthisptr(this); 를 여러 번 생성 한다고 레퍼런스 카운터가 증가하는 것은 아니다레퍼런스 카운터의 증가는 변수끼리의 대입에 의해서만 증가할 뿐저런 선언은 카운터1짜리 변수를 여러 개 늘려 에러를 내게 할 뿐이다.

#include <boost/shared_ptr.hpp>

#include <cstdio>

 

using namespace boost;

 

class SPtrTest;

 

void Func(shared_ptr<SPtrTestsp);

 

class SPtrTest

{

public:

        int var1;

        SPtrTest()

        {

               printf("Constructor\n");

        }

        ~SPtrTest()

        {

               printf("Destructor\n");

        }

        void operation()

        {

               shared_ptr<SPtrTestthisptr(this);

               Func(thisptr);

        }

};

 

void Func(shared_ptr<SPtrTestsp)

{

        sp->var1++;

        printf("%d\n"sp->var1);

};

 

int _tmain(int argc_TCHARargv[])

{

 

        shared_ptr<SPtrTesttestclass(new SPtrTest);

        testclass->var1 = 0;

        testclass->operation();

              

        return 0;

}




#include <boost/shared_ptr.hpp>

#include <boost/weak_ptr.hpp>

#include <boost/smart_ptr/enable_shared_from_this.hpp>

#include <cstdio>

 

using namespace boost;

 

class SPtrTest;

 

void Func(shared_ptr<SPtrTestsp);

 

class SPtrTest : public boost::enable_shared_from_this<SPtrTest>

{

public:

        int var1;

        SPtrTest()

        {

               printf("Constructor\n");

        }

        ~SPtrTest()

        {

               printf("Destructor\n");

        }

        void operation()

        {

               Func(shared_from_this());

        }

};

 

void Func(shared_ptr<SPtrTestsp)

{

        sp->var1++;

        printf("%d\n"sp->var1);

};

 

int _tmain(int argc_TCHARargv[])

{

 

        shared_ptr<SPtrTesttestclass(new SPtrTest);

        testclass->var1 = 0;

        testclass->operation();

              

        return 0;

}

?

2008.10.05 23:38

[TR1 살펴보기] 2. Array

Lyn
조회 수 37586 추천 수 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 등의 멤버함수를 제공하여, 초기화, 교체 등의 작업을 쉽게 할 수 있으며, 배열의 크기를 얻어 올 수도 있습니다.

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

2011.03.31 08:21

Mysql 툴

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

단축키

Prev이전 문서

Next다음 문서

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

단축키

Prev이전 문서

Next다음 문서

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

2015년 7월 현재 상황이 많이 바뀌었다.

HeidiSQL 은 발전이 없고, Mysql Workbench 는 엄청 좋아졋다. 특히 DB가 클 경우 HeidiSQL 은 테이블 목록 불러오는데만도 세월을 느낄 수 있을 정도이다.

HeidiSQL 을 쓸 경우는 그저 MariaDB 10.x 를 사용할 때 뿐이다(MariaDB와 같이 배포되며, Workbench 는 MariaDB 10.x를 지원하지 않는다)

 

HeidiSQL

 

MysqlFront 이제 구버전이다

 

TAG •
?

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

단축키

Prev이전 문서

Next다음 문서

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

단축키

Prev이전 문서

Next다음 문서

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

1. 자동업데이트(이거 이슈 거의 무조건 생기는거 같다..)


2. 서버인증


3. 패킷 암호화


4. 접속유지체크(이걸 프레임워크단에 넣어야하나 어플단에서 구현해야하나...)


5. 일부 알고리즘을 분리해서 인증없이 실행 안되도록.


6. 크래시 덤프



?

2009.05.11 22:04

[Boost 살펴보기] 1. pool

Lyn
조회 수 37112 추천 수 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
조회 수 36783 추천 수 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
조회 수 36765 추천 수 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;

}

 

 

 

?

2009.05.11 22:44

[Boost 살펴보기] 2. any

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

}

 

 

?

Lyn
조회 수 35667 추천 수 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 이란 용어를 사용했다.
?

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

단축키

Prev이전 문서

Next다음 문서

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

단축키

Prev이전 문서

Next다음 문서

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

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

단축키

Prev이전 문서

Next다음 문서

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

단축키

Prev이전 문서

Next다음 문서

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

역시 이번에도 아무도 관심가져주지 않는 (...) 불쌍한 주제를 하나 가지고 나와봤습니다


C++의 표준 라이브러리인 stl 의 특징이라고 하면 반복자(iterator)의 존재하고 할 수 있는데요...

이 반복자들은 여러가지 종류가 있습니다. 

정방향 or 역방향, 임의접근 or 순차접근, 상수 or 비상수 등 여러가지 조합의 반복자를 생성 할 수 있는데요 ...


이중 방향은 애초에 별도의 함수로 제공되고 (begin vs rbegin), 임의접근이냐 순차접근이냐는 컨테이너의 특성에 달린 문제었습니다.
그러나 상수 반복자(const_iterator)는 별도의 함수가 제공되지 않았는데요, C++특성상 비상수 -> 상수 의 변환은 자유롭기 때문입니다.

그래서 비상수 반복자를 리턴하는 begin 을 호출 후 상수반복자 type 의 변수에 대입하여 사용하는 식으로 구현 되었었지요.



하지만 C++11에선 상수반복자를 리턴하는 cbegin 이 추가되었습니다.
왜냐구요? auto 키워드의 존재 때문입니다. 

사용해보신 분은 아시겠지만 auto 는 한번 써보고 나면 없으면 stl 쓰기 싫어질 정도로 반복자와 함께 사용할때 아주 편한 기능인데, auto는 인터프리팅 언어들처럼 동적타입 변수가 아니라 우변의 type을 유추해서 컴파일타임에 type이 정해지기 때문에 begin 으로 리턴받은 반복자는 반드시 비상수 반복자가 됩니다.


그렇기에 C++11에서는 auto와 함께 사용할 수 있는 상수 반복자를 리턴하는 별도의 함수가 추가되었습니다. 애초에 상수반복자를 리턴하기 때문에 auto로 선언해도 좀 더 안전한 코드를 짤 수 있게 됩니다.


TAG •
?

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

단축키

Prev이전 문서

Next다음 문서

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

단축키

Prev이전 문서

Next다음 문서

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

==여기서부턴 내 개인적인 생각이니... 할말있으면 태클 거는건 좋다.==

 

프로그래머에게 앱스토어의 유행이란 과연 무엇일까..

 

 

혹자는 이렇게 말할지도 모른다. 개인 개발자들에게 대박의 기회를 주었다고...

물론 난 대박이란 말을 좋아하지 않는다. 왠지 운으로 된 것이라는 느낌을 강하게 주기때문에. 하지만 여기선 딱히 쓸 말이 없는 거 같아 그냥 대박이라 표현하기로 하자. 내 짧은 어휘력에 저주를 한번 보내주는 것은 좋다.

 

이 얘기는 물론 대박을 친 사람이 있다는 얘기는 아니다.

난 아직까지 앱스토어에서 대박친 사람을 본 적이 없다. 기껏해야 중박정도랄까. 애시당초 큰 돈을 벌 수 있는 시스템이 아니다.

그리고 앱스토어는 이제 개인 한두명일 달라붙을 수 있는 시장이 아니라 규모를 갖춘 팀이 아니면 승부를 하기 힘든 시장이다.

 

 

내가 말하고싶은건 뭐냐...

프로그래머에게 앱스토어는 저주나 다름없다는 것이다. 적어도 한국에서는..

공짜 혹은 비싸봐야 천원내지 수천원 이건 무슨 의미냐. 프로그램은 "싸다" 라는 인식을 사람들에게 박아준 것 이외엔 아무것도 아니다.

프로그램은 엄청난 인건비를 소모 해야만 만들 수 있다. 그런것이 천원 이천원에 팔린다는것은 가치를 우리 스스로 무시한 것 외에 아무것도 아니다.

 

프로그래머들의 재미있는 장난감이 될 수는 있다 앱스토어가. 하지만 재미삼아 만들었으면 그것으로 끝내라. 그걸 싼값에 공개하는건 우리 스스로 가치를 깎아먹는 것 밖에는 안된다. 

 

중급개발자가 월급이 300만원이라고 하면 시간당 2만원을 버는셈인데... 그것을 1달러(대충 1000원으로 치자) 에 판다고 하자.

아이폰 앱스토어에서 100개 이상 팔리는 어플의 비율이 10% 정도라고 하는데... 상위 10%의 어플을 만들어 냇다고 치자. 그럼 10만원을 버는 셈이다. 당신이 그걸 5시간 이내에 완성한 것이 아니라면 완전한 손해라고 밖에 할 수 없다.

?

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

단축키

Prev이전 문서

Next다음 문서

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

단축키

Prev이전 문서

Next다음 문서

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

사후 디버깅을 위해 가장 많이 쓰이는 방법중 하나가 메모리 덤프를 남기는 방법입니다.

보통 아래와 같은 형태로 구현 하게 되는데요. 과연 이 프로그램은 덤프가 남을까요?


#include <Windows.h>
#include <DbgHelp.h>
#include <cstdio>
#include <string>


using namespace std;

#pragma comment(lib, "dbghelp.lib")

LONG __stdcall TopLvFilter(PEXCEPTION_POINTERS pExceptionPointer)
{
	MINIDUMP_EXCEPTION_INFORMATION MinidumpExceptionInformation;
	std::wstring DumpFileName = L"dmpfile.dmp";

	MinidumpExceptionInformation.ThreadId = ::GetCurrentThreadId();
	MinidumpExceptionInformation.ExceptionPointers = pExceptionPointer;
	MinidumpExceptionInformation.ClientPointers = FALSE;

	if (DumpFileName.empty() == true)
	{
		::TerminateProcess(::GetCurrentProcess(), 0);
	}

	HANDLE hDumpFile = ::CreateFile(DumpFileName.c_str(),
		GENERIC_WRITE,
		FILE_SHARE_WRITE,
		nullptr,
		CREATE_ALWAYS,
		FILE_ATTRIBUTE_NORMAL, nullptr);

	MiniDumpWriteDump(GetCurrentProcess(),
		GetCurrentProcessId(),
		hDumpFile,
		MiniDumpNormal,
		&MinidumpExceptionInformation,
		nullptr,
		nullptr);

	::TerminateProcess(::GetCurrentProcess(), 0);

	return 0;
}

void main()
{
	SetUnhandledExceptionFilter(TopLvFilter);	
	atoi(nullptr);
}


네. 결론부터 말하자면 위 코드에서는 덤프가 남지 않습니다.
왜냐면 CRT 에서 에러가 발생하면 UnhandledExceptionFilter 를 CRT가 자신의 예외 핸들러로 다시 세팅 하기 때문입니다.

그러나 우리가 바라는건 별로 친절하지도 않은 (...) CRT의 에러메세지가 아니라 메모리 덤프지요.

우리가 원하는대로 덤프를 남기는 방법은 여러 가지가 있지만 그 중 한가지로 해결 해 보겠습니다.

이번에 사용 할 방법은 코드를 패치해서 CRT가 감히 내가 만든 예외처리기를 바꾸지 못하도록 (...) 하는 방법입니다



#include <Windows.h>
#include <DbgHelp.h>
#include <cstdio>
#include <string>

using namespace std;

#pragma comment(lib, "dbghelp.lib")

BYTE HotPatchingBackup[5];

LPTOP_LEVEL_EXCEPTION_FILTER WINAPI NullSetUnhandledExceptionFilter(
  _In_  LPTOP_LEVEL_EXCEPTION_FILTER lpTopLevelExceptionFilter
)
{
    return nullptr;
}

void InstallAPIHook_SetUnhandledExceptionFilter()
{
    BYTE *pSetUnhandledExceptionFilter;

    DWORD ProtectOption = 0;
    DWORD Temp = 0;

    pSetUnhandledExceptionFilter = (BYTE*)&SetUnhandledExceptionFilter;

	HANDLE hSelfProcess = OpenProcess(PROCESS_VM_OPERATION | PROCESS_VM_READ | PROCESS_VM_WRITE , FALSE, ::GetCurrentProcessId());
	VirtualProtectEx(hSelfProcess, (void *)pSetUnhandledExceptionFilter, 5, PAGE_EXECUTE_WRITECOPY, &ProtectOption);     	

    memcpy(HotPatchingBackup, pSetUnhandledExceptionFilter, 5);

    *pSetUnhandledExceptionFilter = 0xE9;
	pSetUnhandledExceptionFilter++;
    *((DWORD *)(pSetUnhandledExceptionFilter)) = ((DWORD)NullSetUnhandledExceptionFilter) - ((DWORD)pSetUnhandledExceptionFilter + 4);	
	pSetUnhandledExceptionFilter--;

	VirtualProtectEx(hSelfProcess, (void *)pSetUnhandledExceptionFilter, 5, ProtectOption, &Temp);

	CloseHandle(hSelfProcess);
}

void UnInstallAPIHook_SetUnhandledExceptionFilter()
{
    BYTE *pSetUnhandledExceptionFilter;

    DWORD ProtectOption;
    DWORD Temp;

    pSetUnhandledExceptionFilter = (BYTE*)&SetUnhandledExceptionFilter;
	
	HANDLE hSelfProcess = OpenProcess(PROCESS_VM_OPERATION | PROCESS_VM_READ | PROCESS_VM_WRITE , FALSE, ::GetCurrentProcessId());
	VirtualProtectEx(hSelfProcess, (void *)pSetUnhandledExceptionFilter, 5, PAGE_EXECUTE_READWRITE, &ProtectOption);    

    memcpy(pSetUnhandledExceptionFilter, HotPatchingBackup, 5);

	VirtualProtectEx(hSelfProcess, (void *)pSetUnhandledExceptionFilter, 5, ProtectOption, &Temp);     

	CloseHandle(hSelfProcess);
}

LONG __stdcall TopLvFilter(PEXCEPTION_POINTERS pExceptionPointer)
{
    MINIDUMP_EXCEPTION_INFORMATION MinidumpExceptionInformation;
    std::wstring DumpFileName = L"dmpfile.dmp";

    MinidumpExceptionInformation.ThreadId = ::GetCurrentThreadId();
    MinidumpExceptionInformation.ExceptionPointers = pExceptionPointer;
    MinidumpExceptionInformation.ClientPointers = FALSE;

    if (DumpFileName.empty() == true)
    {
        ::TerminateProcess(::GetCurrentProcess(), 0);
    }

    HANDLE hDumpFile = ::CreateFile(DumpFileName.c_str(),
        GENERIC_WRITE,
        FILE_SHARE_WRITE,
        nullptr,
        CREATE_ALWAYS,
        FILE_ATTRIBUTE_NORMAL, nullptr);

    MiniDumpWriteDump(GetCurrentProcess(),
        GetCurrentProcessId(),
        hDumpFile,
        MiniDumpNormal,
        &MinidumpExceptionInformation,
        nullptr,
        nullptr);

    ::TerminateProcess(::GetCurrentProcess(), 0);

    return 0;
}

void main()
{
    SetUnhandledExceptionFilter(TopLvFilter);
    InstallAPIHook_SetUnhandledExceptionFilter();
    atoi(nullptr);
    UnInstallAPIHook_SetUnhandledExceptionFilter();
}


API 훅을 해서 필터를 바꾸려 하면 아무 일도 하지 않는 NullSetUnhandledExceptionFilter 이 호출되도록 하여 막는 방법입니다...
멋있어 보이기는 하지만 이 코드는 64bit/32bit 를 따로 만들어야 하고, 심지어 윈도우 8 에서는 이 코드가 정상적으로 동작 하지 않습니다 (....) 아마도 강화된 보안때문에 그런게 아닌가 싶은데...

어쨋든 윈도우7 까지는 정상적으로 덤프가 남습니다.


그럼 이런 편법 말고 좀 정석적인 방법이 필요한데... 그건 다음글로...

TAG •
?

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

단축키

Prev이전 문서

Next다음 문서

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

단축키

Prev이전 문서

Next다음 문서

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

C++의 STL 에서는 여러가지 알고리즘을 제공한다.
그중 "임의의" 동작을 할 수 있는 알고리즘들은 그 "동작" 의 대상이 되는 인자를 받도록 되어 있는데, 
그 "동작"의 조건은 ()연산자로 호출이 가능할것! 이다.


그 동작을 만족시키는것은 C에서는 함수가 있다.
또 C++에서는 STL 과 함께 소개된 함수객체(Functor)가 있으며, STL 초기부터 제공된 bind1st, bind2nd가 있고, boost에서 제공되는 mem_fn 도 있으며, 이 모든것을 통합한 범용 bind(C++11 or boost에 포함)도 존재한다.

그리고 최신의 C++11 표준에 포함된 lambda도 있고, boost의 lambda도 있지만, 이쪽은 C++11 표준에 lambda가 포함 된 이상 더이상 쓸 일은 없을테니 이번 실험에선 제외한다.


실험순서는 전역함수, bind된 전역함수, 함수객체, 람다, bind된 람다, auto를 이용해 bind된 람다 의 순이다.

실험은 Visual C++ 2010 Sp1, x86 Debug Build에서 테스트 하엿다. Release Mode 도 특성은 동일하다.

궂이 Debug Build로 테스트 한 이유는 Release모드에서 자꾸 전역함수를 inline화 시키는 바람에 =_=;


코드 나간다.


#include <cstdlib>
#include <cstdio>
#include <vector>
#include <random>
#include <cstdint>
#include <algorithm>
#include <functional>
#include <windows.h>
 
using namespace std;
 
__int64 g_NumberSum;
 
void GetSumFunction(int Num)
{
	g_NumberSum = g_NumberSum + Num;
}
 
struct GetSumFunctor
{
	void operator()(int Num)
	{
		g_NumberSum = g_NumberSum + Num;
	}
};
 
void wmain()
{
	const int NUMBER_COUNT = 100000000;
 
	mt19937_64 RandomEngine;
	uniform_int <> RandomRange(0, INT32_MAX);
	RandomEngine.seed(GetTickCount());
 
	vector<int> RandumNumbers;
	RandumNumbers.reserve(NUMBER_COUNT);
	for (int i = 0; i < NUMBER_COUNT; ++i)
	{
		RandumNumbers.push_back(RandomRange(RandomEngine));
	}
 
	DWORD Tick;
//function
	g_NumberSum = 0;
	Tick = GetTickCount();
	for_each(RandumNumbers.begin(), RandumNumbers.end(), GetSumFunction);
	printf("Global Function :: Sum : %I64d, Time : %dms\n", g_NumberSum, GetTickCount() - Tick);
//bind function
	g_NumberSum = 0;
	function<void(int)> FunctionObject = bind(&GetSumFunction, tr1::placeholders::_1);
	Tick = GetTickCount();
	for_each(RandumNumbers.begin(), RandumNumbers.end(), FunctionObject);
	printf("Bind Function   :: Sum : %I64d, Time : %dms\n", g_NumberSum, GetTickCount() - Tick);
//functor
	g_NumberSum = 0;
	Tick = GetTickCount();	
	for_each(RandumNumbers.begin(), RandumNumbers.end(), GetSumFunctor());
	printf("Functor         :: Sum : %I64d, Time : %dms\n", g_NumberSum, GetTickCount() - Tick);
//C++ 11 Lambda
	g_NumberSum = 0;
	Tick = GetTickCount();
	for_each(RandumNumbers.begin(), RandumNumbers.end(), 
		[](int Num)
		{
			g_NumberSum = g_NumberSum + Num;
		});
	printf("C++11 Lambda    :: Sum : %I64d, Time : %dms\n", g_NumberSum, GetTickCount() - Tick);
//C++11 Bind Lambda
	g_NumberSum = 0;
	Tick = GetTickCount();
	function<void(int)> BindLambda = 
		[](int Num)
		{
			g_NumberSum = g_NumberSum + Num;
		};
	for_each(RandumNumbers.begin(), RandumNumbers.end(), BindLambda);
	printf("Bind Lambda     :: Sum : %I64d, Time : %dms\n", g_NumberSum, GetTickCount() - Tick);
//C++11 Auto bind Lambda
	g_NumberSum = 0;
	Tick = GetTickCount();
	auto AutoBindLambda = 
		[](int Num)
		{
			g_NumberSum = g_NumberSum + Num;
		};
	for_each(RandumNumbers.begin(), RandumNumbers.end(), AutoBindLambda);
	printf("Auto Bind Lambda:: Sum : %I64d, Time : %dms\n", g_NumberSum, GetTickCount() - Tick); 
} 



아래는 결과이다.

lambda.png


결과를 보면 lambda 가 가장 빠른 것을 볼 수 있다. 길게 나불대지 않고 성능 이슈와 원인을 아래 간략히 적어본다.
1. 전역함수의 경우 함수 Call 오버헤드에 의해 속도가 조금 감소하엿다.

2. lambda, functor 는 사실상 오차범위 내의 차이라고 보인다.

3. bind lambda 의 경우는 std::fuction 객체가 함수호출 정보를 RTTI에 의존하는 특징때문에 속도를 왕창 까먹게 된다.

4. bind 된 전역함수의 경우는 RTTI의 오버헤드 + 함수호출로 인해 가장 느려진다.

5. auto를 사용해 변수에 저장한 lambda 는 std::fuction객체에 저장 하는 것이 아니라 일종의 inline 함수처럼 구현해준다. 그래서 오버헤드가 전혀 없이 빠른 속도를 보여준다. 즉 같은 함수객체를 2번이상 이용 할 경우 코드를 Copy&Paste 하지 않아도 auto를 이용하면 성능과 중복방지 두마리 코드를 다 잡을 수 있다.

?
  • ?
    elrha 2012.08.16 18:27

    으아 이거 좋은정보네요


    테스트는 안해봤지만 auto 객체 마우스 오버해보면 std::function으로 나올듯 한데..


    결국 컴파일러 지능테스트인건가요 ㅋㅋㅋㅋ

  • profile
    Lyn 2012.08.17 07:23

    아뇨 함수포인터 취급해요 ㅎㅎ


    컴파일 타임에 완전한 호출 정보를 알고있는 상황에서 궂이 오버헤드가 있는 std::function으로 컴파일할 이유가 없죠

  • ?
    11ho 2014.03.25 05:22
    감사합니다. 속이 다 후련하네요 ㅠㅠ
  • ?
    해피 2015.07.17 18:45
    퍼가용

2012.01.14 00:40

Boost 빌드 방법

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

단축키

Prev이전 문서

Next다음 문서

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

단축키

Prev이전 문서

Next다음 문서

크게 작게 위로 아래로 댓글로 가기 인쇄
1. Gem 만들기
bootstrap 실행 -> b2 실행파일이 생성

1.48.0에선 bjam -> b2 로 바뀌었다. 근데 bjam 도 그대로 된다 (...) 같은걸 2개 만들어줌

2. b2 빌드옵션
toolset=<>            ▷ 컴파일러 설정
--buildtype=<>      ▷ 빌드방법 설정
--libdir=<>            ▷ output 폴더 설정
--architecture=<>   ▷ target platform 설정
address-model=<>  ▷ bit수 설정
install                    ▷ 빌드방법 설정
define=<>              ▷ define 상수 설정
-j<>                      ▷ 병렬컴파일할 코어갯수 설정

절대로 = 앞뒤에 공백 넣지 말것. 반드시 원하는 컴파일러/bit수의 Command Prompt  에서 실행할것

3. 예제
vc++ 2010으로 모든형태의 라이브러리(debug dll, debug static, release dll, release static)를 CRT 버전을 최신으로 하여 특정 Path에  쿼드코어로 빌드 할경우

AMD64(EM64T)는 x86 아키텍쳐에 address 를 64bit로 지정해야한다. x64, AMD64, EM64T 등의 키워드는 불가능

  1. 32bit build

    b2 -j4 toolset=msvc-10.0 --build-type=complete define=_BIND_TO_CURRENT_VCLIBS_VERSION  --libdir=c:\library\boost\lib\i386 install

  2. 64bit build
    b2 -j4 toolset=msvc-10.0 --build-type=complete  define=_BIND_TO_CURRENT_VCLIBS_VERSION --libdir=c:\library\boost\lib\amd64 architecture=x86 address-model=64 install
?
  • profile
    Lyn 2012.01.14 00:41

    실수로 글을 지워서 다시썻다 씁 ㅡㅡ;


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

단축키

Prev이전 문서

Next다음 문서

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

단축키

Prev이전 문서

Next다음 문서

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

아래 코드는 최신의 Visual C++ 2013 에서 작성 되었습니다.


요즘 하는거 없이 바빠서 (...) 신기능 정리하는 사이에 2013이 정식 출시되어 버렷네요.

C++11이 나오면서 추가된 기능 중 하나가 가변인자 템플릿인데, 이번엔 가변인자 템플릿 함수를 한번 구경해 보겠습니다.
tmp.jpg

혹시 C++ Template Metaprogramming 라는 희대의 마도서를 보신 분들이나,  순수 함수형 언어를 경험하신 분들이라면 이해가 쉬울텐데요, 가변인자 템플릿 함수는 기존 템플릿 메티프로그래밍(이하 TMP)과 매우 유사합니다.


결정적인 공통점은 루프가 아니라 재귀로서 반복한다는 점이겠네요.
즉 하나의 함수에서 여러개의 인자를 처리하는게 아니라 가변인자 인자 N개 함수가 N-1개의 함수를 호출하고, 또 거기서 N-2개의 함수를 호출하고... 반복하다가  최종적으로 가변인자가 0개인 함수를 호출 하면서 가변인자 함수 호출이 완료되게 됩니다.


여기 까지 읽으셧으면 눈치 채셧을텐데.. 네 맞습니다. 가변인자 함수와 가변인자가 아닌 함수 2개가 필요하게 됩니다.

잠깐 예지를 보면

#include <string>
#include <iostream>

using namespace std;

template<typename T>
void LogWrite(T value)
{
	cout << value << endl;
	return;
}
template<typename T, typename... Args>
void LogWrite(T value, Args... args) 
{	
	cout << value << ' ';
	LogWrite(args...);
}

void wmain()
{
	LogWrite<int, int, double>(123, 456, 789.012);
	LogWrite(123, 456, 789.012);
}

이런 형식이 됩니다. Args라는 식별자를 가변템플릿 인자 목록으로 쓰고 있는데 이는 가변인자함수와 비슷 합니다.
... 이 들어가는것도 그렇구요, 가변인자 함수와 마찬가지로 모든 인자가 가변일수는 없습니다. 반드시 1개 이상의 고정 인수를 가져야 합니다(단 템플릿이므로 type이 고정되진 않지만요)

여기서 저 ... 는 Parameter Pack 이라고 부른다고 합니다.



네 참 심플한데... C++ 문법 만드는 사람들이 여기서 끝낼정도로 단순할리가 없습니다(....) 
여기에 붙을 수 있는 각종 추가 기능들이 있는데 그중 하나가 가변인자의 갯수를 세는 sizeof... 연산자 입니다.

다시한번 말하지만 sizeof... 입니다 ... 까지 붙어야 정확한 이름입니다.


그리고 이 sizeof... 연산자는 가변인자템플릿 내부에서만 사용 가능합니다. 다른곳에서 사용하려고 하면 0이 나오는것이 아닌 컴파일 오류가 발생합니다.
이것을 사용하면 아래와 같은 코드가 가능합니다


#include <string>
#include <iostream>

using namespace std;

template<typename T>
void LogWrite(T value)
{
	cout << "0 : " << value << endl;
	return;
}
template<typename T, typename... Args>
void LogWrite(T value, Args... args) 
{	
	cout << sizeof...(args) << " : " << value << ' ';	
	LogWrite(args...);
}

void wmain()
{
	LogWrite<int, int, double>(123, 456, 789.012);
	LogWrite(123, 456, 789.012);
}

위 코드의 출력 결과는 다음과 같습니다
01.png

인자 3개로 호출했지만, 1개는 고정인자 취급이기 때문에 2부터 시작합니다.

?

Atachment
첨부 '3'
?

단축키

Prev이전 문서

Next다음 문서

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

단축키

Prev이전 문서

Next다음 문서

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

C++ 템플릿 상속엔 골 때리는 문제가 있는데 ...

바로 베이스클래스의 멤버에 접근하려면 베이스 클래스의 이름을 명시해야 한다는 것이다... 안그러면 이름을 못찾아온다

사람 상당히 귀찮게 만드는놈이라 할 수 있겠다.

보통 일반적으론 볼일이 없고 Mixin 을 이용하거나 아니면 Effective C++ 을 읽으면서 알게된다(책에는 "못찾는다" 라고 되어 있다)


간단히 말하면 이런 코드다

#include <cstdio>

void foo()
{
	printf("Global Function\n");
}
template <typename T>
class Cls1
{
	T Var1;
public:
	void foo()
	{
		printf("Member Function\n");
	}
};

template <typename T>
class Cls2 : public Cls1<T>
{
	T Var2;
public:
	void test()
	{
		Cls1<T>::foo();
	}
};

int main()
{
	Cls2<int> cls2;
	cls2.test();

	return 0;
}


근데 만약 명시 하지 않으면 어떻게 될까?


일단 최신의 VC++ 2012에서 돌려보자

01.png


헐...? 정확히 멤버 함수를 찾아온다. 여기엔 올리지 않겠지만 VS2005~2010 도 모두 같은 동작을 한다.

다른컴파일러에서 해보자. gcc 4.4다.


02.png


헐?! 글로벌 함수를 찾는다 (...) 이거뭥미.


그럼 최신의 C++0x를 지원하는 gcc4.7에서 해보자.


03.png


역시 전역함수를 찾는다.


표준문서는 귀찮아서 (...) 확인하지 않았지만 관련 서적 등을 보면 gcc의 동작이 맞는 것으로 보인다.
하지만 저런 코드를 짜면서 gcc 쪽 동작을 원하는 사람이 과연 있을까... 하는 생각이 든다. 일반적인 클래스를 만드는 느낌으로 당연히 멤버함수가 호출 될 것이라고 생각 하지 않을까? 이 결과를 확인하면서 C++은 당최 생각대로 움직여 주지 않는 매저키스트 들을 위한 언어란 누군가의 말이 귓가에 맴돈다 (...)


결론 : EC++에 나온대로 그냥 제대로 포커스 지정 하자 (...) 야!


PS. gcc 에서는 저렇게 두가지 모두 있는 경우가 아니라 전역 foo 함수가 없는 (즉 이름을 찾을 수 없는) 경우에는 당연히 이름을 찾지 못해 에러를 내는데, 이 경우 컴파일 옵션에 -fpermissive 를 추가 하는 것으로 멤버함수로의 연결이 가능하다(워닝은 뜬다)
단 전역 foo 함수가 존재 할 경우 저 옵션을 그냥 무시하고 그냥 전역 함수로 연결해 버린다.


PS2. 혹시 LLVM-clang 을 사용할 수 있는 환경에 계신분은 어떤 결과가 나오는지 알려주시면 좋겠다.



?
  • ?
    123 2012.09.03 03:48
    VS 에서는 자동으로 찾아주네요.. 역시 상용이라 틀린가보네요..
    clang 으로 컴파일 해서 실행해보니 gcc 하고 같네요. .
    관련글에는 이렇게 나오네요
    While non-dependent names are resolved "normally" – when the template is defined,
    the resolution for dependent names happens at the point of the template’s instantiation.
    그러까 템플릿 인스턴스시 resolve 가 되게할려면 dependent name 으로 만들어 줘야 되네요
    Cls1<T>::foo() or this->foo() 이런식으로..

    http://eli.thegreenplace.net/2012/02/06/dependent-name-lookup-for-c-templates/

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

단축키

Prev이전 문서

Next다음 문서

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

단축키

Prev이전 문서

Next다음 문서

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

우분투 서버 설치시 LAMP 를 고르면 아파치가 자동으로 설치되는데...

이때 정말로 아파치만 설치되고 관련 툴들이 전혀 설치되지 않는다 .


sudo apt-get install apache2-utils 

를 실행해서 apache2-utils 패키지를 설치해 줘야, rotatelogs 를 포함한 각종 툴이 설치된다.


TAG •
?

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