728x90

병렬 프로그래밍을 할 때 std::mutex를 이용하여 여러개의 Thread에서 사용하는 공유 자원을 서로 침범 없이 사용할 수 있다. 하지만 lock을 사용하여 mutx에 대한 소유권을 얻은 후 unlock을 하지 않으면 Dead Lock에 빠지게 된다.


예를 들면


std::mutex.lock();


-----


if (true)

return true;


-----


std::mutex.unlock();



위와 같은 코드에서는 lock을 걸고 중간에 return을 했기 때문에 unlock을 하지 않게 된다.


이것은 코딩을 해보면 굉장히 실수하기 쉽다.


이런 실수를 방지하기 위해 C++ 11에서는 lock_guard라는 것이 나왔다.


사용 예 :


#include <iostream>

#include <thread>

#include <mutex>


typedef lock_guard<std::mutex> LockMutex;


int g_nCount = 0;

std::mutex g_mtxUpdate;


[ Thread 1 ]

{

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

{

LockMutex AutoLock(g_mtxUpdate);

g_nCount++;

}

}


[ Thread 2 ]

{

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

{

LockMutex AutoLock(g_mtxUpdate);

g_nCount++;

}

}


이렇게 사용하면 된다.


이것은 continue, return, 혹은 괄호의 끝을 만나게 되면 자동으로 unlock을 시켜준다.


-------------------------------------------------------------------------------------------


다음은 try_lock()이다.


원래 lock()은 다른 스레드에서 lock()이 걸려있다면 unlock()이 될 때까지 기다리게 된다.


하지만 try_lock()은 다른 스레드에서 lock()이 걸려있다면, false를 리턴하며 빠져나온다. 즉 다른 스레드에서 lock이 걸려 


자원을 사용할 수 없을 때에는 다른 일을 하고 싶을 때 사용하면 된다.


-------------------------------------------------------------------------------------------


지정한 시간 동안만 락을 시도하게 할 수도 있다. 이것을 timed_mutex 라고 부른다.


timed_mutex의 종류 중 하나인 try_lock_for()를 이용하여 지정된 시간동안 lock()을 시도하게 할 수 있다.


이 함수는 특정 시간 동안만 시도하고 성공하지 못하면 try_lock 호출을 멈추고 빠져나오길 바랄 때 사용하면 된다.


사용 예 :


if (mutex.try_lock_for(std::chrono::seconds(10)))

{

----


mutex.unlock();

}


-------------------------------------------------------------------------------------------


Mutex의 lock 멤버 함수의 설명을 보면 lock을 호출한 Thread에서 unlock을 호출하지 않고 다시 lock을 호출하면 알 수 없는 동작을 한다고 되어 있다.


실제로 동작시켜보면 프로그램이 죽어버리는 현상을 겪을 수 있을 것이다.


그럴 때에는 recursive_mutex를 사용하면 된다. 이 Mutex는 같은 Thread에서 lock을 건 후 바로 lock을 걸어도 괜찮다. 


단, lock을 호출한 횟수만큼 unlock을 호출해야 한다.


사용 예 :


#include <iostream>

#include <thread>

#include <mutex>


typedef lock_guard<std::recursive_mutex> RecursiveLockMutex;


std::recursive_mutex g_mtx;


[ CheckFunction ]

{

RecursiveLockMutex autoLock(g_mtx);


------

}


[ AddFunction ]

{

RecursiveLockMutex autoLock(g_mtx);


CheckFunction();


----------

}


위의 예제 코드를 보면 알 수 있듯이 사용방법은 간단하다. std::mutex -> std::recursive_mutex로 바꾸기만 하면된다.


-------------------------------------------------------------------------------------------





ps. Windows에서 std::mutex는 내부적으로 Critical Section API를 이용한다.

728x90

'Basic Programming > C++ 11' 카테고리의 다른 글

C++11 - std::thread  (0) 2016.12.02
C++11 - "default" and "delete" keyword  (0) 2016.11.29
C++11 - nullptr  (0) 2016.02.26
C++11 - enum  (0) 2016.02.26
C++11 - range based for  (0) 2016.02.25
728x90
구조체나 클래스나 C++에서는 같이 취급됩니다. 다만 default 영역이 구조체는 private 이고 클래스는 public이라는 점만 다를 뿐입니다. 
그래서 클래스라고 하면 그것은 union까지도 포함하는 구조체를 포괄하는 개념입니다. 

클래스에는 흔히 가상함수가 존재합니다. 
하나의 클래스 내에서 가상함수는 상속관계에 의해 그 함수주소가 변해야 하므로 가상함수 테이블 일명 VTable을 유지하고, 클래스 내에서는 이 VTable(Virtual Table)을 가리키는 4바이트 포인트를 가집니다. 
C++빌더의 경우는 인스턴스를 생성하면 그 인스턴스의 최초 4바이트가 바로 VTable을 가르키는 포인트입니다. 
VC++의 경우도 마찬가지로 인스턴스의 최초 4바이트가 VTable을 가리키는 포인트입니다. 

우선 가상함수가 없는 클래스를 보겠습니다. 

// 가상함수가 없는 구조체 또는 클래스 

class TT1 

public: 
    int        t1; 
    int        t2; 
    String    str;    // AnsiString클래스는 문자열을 가르키는 4바이트포인트만을 가지는 클래스로, 처음선언시 0(NULL)으로 초기화된다. 그러므로 문자열 대입전에는 0으로 재설정해도 상관없다. 어차피 0 이므로. 

    TT1() 
    { 
        // 가상함수가 없는 순수 클래스/구조체는 이렇게 한줄로 모두 0으로 간단히 초기화 할수 있다. 
        ZeroMemory(this, sizeof(*this)); 
    } 
}; 

생성자에서  ZeroMemory(this, sizeof(*this)); 로 
한방에 멤버변수를 0으로 초기화하는데 아무런 문제가 없습니다. 
가상함수가 없는 간단한 클래스는 이런식으로 초기화하는 것은 실무에서도 많이 쓰이는 기법입니다. 
MS 플머들도 많이 애용하죠. 

하지만 멤버함수중에 가상함수가 존재할때 이렇게 초기화하면 
인스턴스 첫 4바이트인 VTable을 가리키는 포인트값이 0으로 되어 가상함수는 기능을 하지 못하게 됩니다. 
엑세스바이얼레이션 예외를 일으키게 됩니다. 
그러므로 가상함수를 포함하고 있을때는 VTable을 가리키는 포인트를 피해서 초기화를 하면 되겠죠. 

다음은 가상함수를 포함하는 클래스를 보겠습니다. 

// 가상함수를 포함하는 클래스/구조체 

class TT 

public: 
    int        temp; 
    virtual ~TT()    {    }        // VTable 0번 
    virtual void func1()       { add("run func1");    } // VTable 1번 
    virtual void func2()       { add("run func2");    } // VTable 2번 
    virtual void func3(int a)      { add(String("run func3 ") + a); } // VTable 3번 

    TT() 
    { 
        //add(String().sprintf("&t:%08X &t.temp:%08X", this, &temp)); 
        ZeroMemory(&temp, sizeof(*this) - 4);    // VTable의 포인트를 피해서. 
        add(temp); 
    } 
}; 

생성자에 VTable을 피해서 초기화하는 기법이 보입니다. 
앞 4바이트를 피하겠다고, ZeroMemory(this + 4, sizeof(*this) - 4); 식으로 했다가는 
엉뚱한 곳의 메모리를 0으로 채우게 됩니다. this 는 TT 형이므로 this + 4는 곧 this 메모리번저 + 4 * sizoef(TT); 
와 같기 때문입니다. 위처럼 첫 멤버변수의 메모리 번지를 취하는 것이 안전합니다. 

사이즈에 있어서도 sizeof(*this) - 4 표현보다 
sizeof(*this) - ((int)&temp - (int)this) 가 더 낫습니다만, win32 어플인 경우는 바뀌지 않는 사항이기 때문에, 
그냥 위처럼 그냥 간편하게 사용해도 됩니다. 

위에서는 4개의 가상함수가 쓰였는데, 4개의 함수포인트 배열인 VTable은 클래스에 가상함수가 쓰여진 순서대로 
존재하게 됩니다. 그러므로 위에서 0번부터 순서를 붙여놓은 순서대로 VTable이 구성됩니다. 

여기서 주의해야할 것은 VTable의 구성방법에 대해서는 C++표준이 정해진바가 없어 컴파일러 제작사마다 
구현방법이 틀릴수 있다는 사실입니다. C++빌더와 VC++의 구성방법이 틀리며, 지금 설명하고 있는 내용은 
전부  C++빌더 중심적인 설명입니다. 

다음은 가상함수를 포함하는 클래스를 합성한 경우입니다. 

// 가상합수가 있는 클래스를 포함하는 클래스. 

class TT2 

public: 
    int    temp2; 
    int temp3; 
    TT  t;   // 가상함수가 있는 클래스죠. 

    TT2() 
    { 
        add(String().sprintf("this:%08X temp2:%08X t:%08X t.temp:%08X", this, &temp2, &t, &t.temp)); 
        // 여기서는 ZeroMemory 전체를 초기화해서는 안된다. 
        // 가상테이블이 있는 클래스를 피해서, 
        // 원하는 부분만 한번에 0으로 초기화하는 기법은 아래와 같다. 
        ZeroMemory(&temp2, offsetof(TT2, t) - offsetof(TT2, temp2)); 
        add(String().sprintf("TT2 특정 블럭 크기:%d", offsetof(TT2, t) - offsetof(TT2, temp2))); 
    } 
    virtual ~TT2() { } 
}; 

예제에서 add(...)함수가 자꾸 나오는데 add(...)는 결과를 메모장에 찍어보기 위한 간단함수입니다. 아래와 같습니다. 

void    add(String msg) 

    Form1->Memo1->Lines->Add(msg); 


TT2 클래스의 경우를 보면 TT2의 인스턴스의 첫 4바이트는 TT2에 대한 VTable을 가르킵니다. 
안에 포함한 TT의 인스턴스 t 도 VTalbe을 가지고 있는데 마찬가지로 t 의 인스턴스 첫 4바이트가 VTable을 
가르킵니다. 
temp3 변수 다음번지에 t의 인스턴스가 위치하며, t의 인스턴스 첫 4바이트는 TT의 VTable을 가르킨다는 것입니다. 

그러면 TT2 클래스를 한방에 초기화 할수 있는 방법은 없을까요? 
당연히 t 가 VTable에 대한 포인트를 포함하고 있으므로 t 인스턴스를 피해서 초기화를 해야 합니다. 
즉 temp2 변수부터 t 바로 위인 temp3 까지만 0으로 초기화 하면 되죠. 

ZeroMemory(&temp2, offsetof(TT2, t) -    offsetof(TT2, temp2)); 

offsetof를 이용한 한번에 초기화하는 기법입니다. 
temp2부터 t 인스턴스 이전 까지의 크기를 구하는 기법이 보이는데, int 변수가 2개이므로 짐작한대로 8바이트의 
크기가 됩니다. 

지금까지 VTable과 한방에 멤버변수를 0으로 만드는 기법을 설명했는데, 
정확하게 알지 못한다면 그냥 초기화 리스트 즉 생성자에서 일일이 멤버변수에 값을 할당하는 기법으로 
초기화하시기 바랍니다. 
그것이 보다 좋은 프로그래밍 습관입니다. 
여기서의 설명은 C++의 구현된 내부구조를 살펴보는데 목적이 있습니다. 

VTable과 VTable에 대한 포인트의 성질을 이용해서 다음과 같은 실험을 해 볼수 있습니다. 


typedef void (*Tfunc)(void *pClass); 
typedef void (*Tfunc3)(void *pClass, int a); 

void __fastcall TForm1::Button4Click(TObject *Sender) 

    TT t; 

    int        *pClass = (int *)&t; 
    void    **pVTable = (void **)(*pClass); 
    Tfunc   *func = (Tfunc *)pVTable; 
    add(String().sprintf("VTable:%08X", &func)); 
    func[1](pClass); 
    func[2](pClass); 
    Tfunc3     func3 = (Tfunc3)func[3]; 
    func3(pClass, 4); 
    add(""); 

    TT2 t2; 


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

TT 클래스는 4개의 가상함수를 가지는 클래스입니다. 
그러므로 그 인스턴스의 번지를 받으면 곧 그 클래스에 대한 포인트가 되죠. 
그 포인트의 첫 4바이트는 VTable에 대한 포인트이므로 
그 값을 받아서 VTable를 함수포인트배열로 인식시킵니다. 
    void    **pVTable = (void **)(*pClass); 
이렇게 되면 
pVTable[0] => ~TT() 
pVTable[1] => func1() 
pVTable[2] => func2() 
pVTable[3] => func3(int a) 
를 가르키게 됩니다. 
하지만 pVTable은 함수포인트의 포인트가 아닌 void * 형으로 선언되어 있으므로, 
함수포인트로 캐스팅을 해 줍니다. 
    Tfunc   *func = (Tfunc *)pVTable; 
    Tfunc3     func3 = (Tfunc3)func[3]; 
그리고 원하는 가상함수를 호출할 수 있습니다. 
    func[1](pClass); 
    func[2](pClass); 
    func3(pClass, 4); 

인자로 꼭 pClass를 붙인 이유는 
모든 클래스의 멤버함수는 그 첫 인자로 자신의 인스턴스의 주소 값을 가져야 하기 때문입니다. 
그래야, 그 값을 가지고 인스턴스의 멤버변수를 엑세스 할수있을 테닌까요. 
물론, func1, func2 멤버 함수처럼 인자가 없고, 안에서 멤버변수를 엑세스하지 않는다면 
pClass 가 아닌 NULL을 넘기거나 아예 인자 없이 호출해도 됩니다. 
이 경우 스택의 인자를 호출 받은 함수에서 제거하는 파스칼식 호출 규약이 아니어야 겠죠. 

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

이상 간략한 설명을 했는데 
프로그램할때 거의 신경을 안쓰는 사항이기는 해도, 
아무래도 고급기법을 구사하려면 내부 구조를 아는 것이 좋습니다. 
어차피 COM을 제대로 이해하려면 VTable 에 대한 지식은 필수입니다. 
멤버 변수 초기화 기법은 그냥 참고적으로 이해해도 좋은데, 
제가 프로그램할 때는 사실 자주 이용하는 기법이기도 합니다. 

그림도 없이 설명해서 미안한데, 다음에 기회되면 보충하죠.

출처 : http://cbuilder.borlandforum.com/impboard/impboard.dll?action=read&db=bcb_tutorial&no=97


728x90
728x90



 이 소스코드는 MinGW 32/64bit 모두에서 동작을 하는 CPU 사용을 확인을 위한 C++ 소스 코드 입니다. cpuu.h 와 cpuu.cpp 파일 내에 CPUUages 라는 Class 가 정의 되어 있으며, 이 Class 에서 CPU 사용율을 얻어 올 수 있도록 만들어 져 있습니다.

 이미 많은 부분들에서 이 방법들을 사용하고 있긴 합니다만, MinGW 에서는 제대로 동작하거나 빌드 되지 않아서 이 부분을 다시 재정의 하여 만들었습니다.


 CPU 가 평균 특정 Percentage 이하로 내려 가야 동작하게 한다거나, 반대로 특정 사용율 이상 일때 뭔가 하도록 하는 방법을 찾으신다면 첨부된 소스를 참조 해 보시기 바랍니다.

 별거 아닌 소스 이기 때문에 별다른 정보를 기재 하지는 않겠습니다.


cpumeter.zip


출처 : http://rageworx.tistory.com/1378

728x90
728x90

이번 프로젝트 중에 LOGFONT를 쓸 일이 있어서 LOGFONTA로 작업을 했다가 LOGFONTW로 변경해야 하는 시점이 왔다.


그래서 SetTitle(, , , , LOGFONTA) -> SetTitle(, , , , LOGFONTW) 로 변경하는 작업을 진행하는 도중 이상한 현상을 발견하였다.



int main()

{

LOGFONTW lf; <- 정상적인 값 세팅.


SetTitle(, , , , lf );

}


void SetTitle(, , , , LOGFONTW in_lf)

{

in_lf; <- 이 시점에서 봤을 때 값은 쓰레기 값이었다.

}



그래서 Packing 크기와 구조체의 크기 등등을 비교해도 다 같은데 왜 값이 서로 다른지에 대해 찾아보던 중


메모리 값을 비교해보니 lf의 주소값이 in_lf의 첫번째 멤버변수에 들어가 있는 것을 확인했다.


그래서 이런저런 테스트를 더 해보려고했지만, 개발 일정이 늦은 상태이기 때문에 다음에 더 테스트를 더 해봐야 할듯하다. ㅎㅎ



결론은


void SetTitle(, , , , LOGFONTW in_lf) -> void SetTitle(, , , , const LOGFONTW& in_lf) 로 바꾸면 정상적인 값으로 들어간다.

728x90
728x90



이 메세지의 경우 개발하면서 되게 자주보게 된다.... (코딩을 못해서 그런가...)


DirectX11로 개발하면서 특정 하드웨어 스펙에서만 이 에러가 발생해서 인터넷을 찾아봤더니,


MS에서 좋은 자료를 찾았다.


https://support.microsoft.com/en-us/kb/2628738


https://msdn.microsoft.com/en-us/library/windows/hardware/ff569918(v=vs.85).aspx



쉽게 말해서, 레지스트리를 수정하여 디스플레이의 작업 시간이 2초가 넘어가도, 디스플레이의 재시작을 하지 않는 방법이다. (8초로 수정)



cmd -> regedit -> HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\GraphicsDrivers -> TdrDelay(8)

-

728x90
728x90

보통 해상도 구하는건 이걸 사용하죠.


 int iWidth = GetSystemMetrics(SM_CXSCREEN);
 int iHeight = GetSystemMetrics(SM_CYSCREEN);


그런데, 이건 주 모니터의 해상도 밖에 못 구합니다.


아래 걸 사용하면 다중 모니터를 고려한 최대 해상도를 구해 옵니다.


 int iWidth = GetSystemMetrics(SM_CXVIRTUALSCREEN);
 int iHeight = GetSystemMetrics(SM_CYVIRTUALSCREEN);


각 모니터 위치나 세세한 정보도 얻어 올 수 있습니다.


출처 : http://heagi2.blog.me/100093550249

728x90
728x90

프로그래밍을 최적화 하는데에는 크게 2개의 방법이 있다.


1. Memory Access를 줄여라. (Cache를 이용하여 해결 가능)

2. 분기(if)를 줄여라. (예측 분기를 이용하여 해결 가능)



Cache를 하는 기준은 다음과 같다.

1. 시간성. (가져온지 얼마 안된 데이터)

2. 공간성. (가져온 데이터의 근처에 있는 데이터)


for(x = 0; ; )

{

for(y = 0; ;)

{

// 다음과 같은 2중 for문일 때에는 공간성을 위해 가로로 돌게하는 것이 성능에 도움이 된다.

}

}


for()

{

// L1 레지스트리가 32k이고, 하이퍼 스레드의 경우 16k씩 2개가 돌게 된다.

// if 없이  16k이하의 크기만 돌게 작성해야 한다. Cache보다 데이터가 크다면 Cache가 아예 동작을 하지 않는다.

// if가 7개 이상 있으면 예측 분기가 불가능 하다.

}


출처 : 회사 세미나 중에서...


728x90
728x90

오늘은 술어 구문(predicate)와 어댑터(not1(), not2(), bind1st(), bind2st())를 공부하겠습니다.

이 페이지를 공부하려면 기초적인 STL을 알아야 합니다.

 

1, 술어 구문(predicate)

술어 구문(predicate)이란 bool형을 반환하는 함수류입니다. 즉, 비교 함수입니다.

함수 역할을 하는 함수자(functor)도 operator()가 bool형을 반환하면 술어 구문이겠죠?

대표적인 술어 구문 클래스가 less와 greater 클래스입니다.

less와 greater 클래스는 사용자 정의가 가능한 술어 구문 함수자 클래스입니다.

 

술어 구문 함수 예제입니다.

#include <iostream>
#include <functional>
using namespace std;
bool LessPredicate(int left, int right)
{
    return left < right;
}
void main( )
{
    cout << LessPredicate(10, 20) << endl;
    cout << LessPredicate(10, 10) << endl;
    cout << LessPredicate(20, 10) << endl;
}
  1. 1
    0
    0

LessPredicate()함수는 bool을 반환하므로 술어 구문(predicate)입니다. 쉽죠~~잉!

 

less와 greater를 공부해 볼까요?

#include <iostream>
#include <functional>
using namespace std;

void main( )
{
    less<int> lessPred; // 함수 < 와 같은 기능
    cout << lessPred( 10, 20 ) << endl;
    cout << lessPred( 10, 10 ) << endl;
    cout << lessPred( 20, 10 ) << endl<<endl;

    greater<int> greaterPred; // 함수 > 와 같은 기능
    cout << greaterPred( 10, 20 ) << endl;
    cout << greaterPred( 10, 10 ) << endl;
    cout << greaterPred( 20, 10 ) << endl<<endl;

    less_equal<int> less_ePred; // 함수 <= 와 같은 기능
    cout << less_ePred( 10, 20 ) << endl;
    cout << less_ePred( 10, 10 ) << endl;
    cout << less_ePred( 20, 10 ) << endl<<endl;

    greater_equal<int> greater_ePred; // 함수 >= 와 같은 기능
    cout << greater_ePred( 10, 20 ) << endl;
    cout << greater_ePred( 10, 10 ) << endl;
    cout << greater_ePred( 20, 10 ) << endl<<endl;
}
  1. 1
    0
    0
  2.  
  3. 0
    0
    1
  4.  
  5. 1
    1
    0
  6.  
  7. 0
    1
    1

bool operator( T , T ) 멤버 함수를 갖는 함수자 클래스입니다.

 

보통 임시객체를 만들어 사용합니다.

#include <iostream>
#include <functional>
using namespace std;

void main( )
{
    cout << less<int>()( 10, 20 ) << endl;
    cout << greater<int>()( 20, 10 ) << endl;
}
  1. 1
    1

 객체 단독으로 사용되는 경우가 없으므로 이 예제처럼 임시 객체를 만들어 바로 사용합니다.

 

 less와 greater의 실제 사용 예를 보겠습니다.

#include <iostream>
#include <vector>
#include <algorithm>
#include <functional>
using namespace std;

void Print(const vector<int>& v)
{
    vector<int>::const_iterator iter;
    for(iter = v.begin() ; iter != v.end() ; iter++)
        cout << *iter <<' ';
    cout << endl;
}
void main()
{
    vector<int> v(10);
    int i;

    for(i = 0 ; i < (int)v.size() ; i++ )
        v[i] = rand() % 1000;
    Print( v );
    sort(v.begin(), v.end(), less<int>()); // less<int>() 생략가능
    Print( v );
    sort(v.begin(), v.end(), greater<int>()); 
    Print( v );
}
  1. 41 467 334 500 169 724 478 358 962 464
    41 169 334 358 464 467 478 500 724 962
    962 724 500 478 467 464 358 334 169 41

 첫 번째 결과는 정렬 전입니다.

두 번째 결과는 less predicate를 사용한 정렬입니다.

세 번째 결과는 greater predicate를 사용한 정렬입니다.

 

연관 컨테이너 set, map은 디폴트로 less를 사용하여 내부 트리를 유지합니다.

간단한 예제를 보겠습니다.

#include <iostream>
#include <map>
#include <algorithm>
#include <functional>
using namespace std;

void main( )
{
    map<intdouble > lessMap; // less<int> 생략
    map<intdouble, greater<int> > greaterMap;

    lessMap.insert(pair<int,double>(10,1.5) );
    lessMap.insert(pair<int,double>(40,4.5) );
    lessMap.insert(pair<int,double>(50,5.5) );
    lessMap.insert(pair<int,double>(30,3.5) );
    lessMap.insert(pair<int,double>(20,2.5) );
   
    greaterMap.insert(pair<int,double>(10,1.5) );
    greaterMap.insert(pair<int,double>(40,4.5) );
    greaterMap.insert(pair<int,double>(50,5.5) );
    greaterMap.insert(pair<int,double>(30,3.5) );
    greaterMap.insert(pair<int,double>(20,2.5) );

    map<int , double, less<int> >::iterator iter;
    for(iter = lessMap.begin() ; iter != lessMap.end() ; iter++)       
        cout << iter->first << ',' << iter->second << endl;
    cout << "==============" << endl;
    map<int , double, greater<int> >::iterator giter;
    for(giter = greaterMap.begin() ; giter != greaterMap.end() ; giter++)      
        cout << giter->first << ',' << giter->second << endl;
}
  1. 10,1.5
    20,2.5
    30,3.5
    40,4.5
    50,5.5
    ==============
    50,5.5
    40,4.5
    30,3.5
    20,2.5
    10,1.5

 map에 less와 greater를 사용했을 때 내부적으로 정렬된 출력결과를 보여줍니다. inorder 출력 결과를 볼 수 있습니다.

less는 오름차순으로 왼쪽에 작은 값을 오른쪽에 큰 값을 위치시키고 greater는 반대입니다.

 

이제 less와 greater를 직접 만들어 볼까요?

#include <iostream>
#include <map>
#include <algorithm>
#include <functional>
using namespace std;
template <typename T>
struct Less
{
    bool operator ( ) (const T& left, const T& right)
    {
        return left < right;
    }
};
template <typename T>
struct Greater
{
    bool operator ( ) (const T& left, const T& right)
    {
        return left > right;
    }
};
void main( )
{
    Less<int> lessPred; // 함수 < 와 같은 기능
    cout << lessPred( 10, 20 ) << endl;
    cout << lessPred( 10, 10 ) << endl;
    cout << lessPred( 20, 10 ) << endl<<endl;
    Greater<int> greaterPred; // 함수 > 와 같은 기능
    cout << greaterPred( 10, 20 ) << endl;
    cout << greaterPred( 10, 10 ) << endl;
    cout << greaterPred( 20, 10 ) << endl<<endl;
}
  1. 1
    0
    0
  2.  
  3. 0
    0
    1

 직접 만들어 사용한 예제입니다. 결과는 less, greater와 동일합니다.

 

2, 어댑터

 대표적인 어댑터가 not1(), not2(), bind1st(), bind2nd()입니다.

bind1st(), bind2nd()등은 바인더라고도 합니다.

어댑터는 함수자 predicate를 다른 형태의 predicate로 변환하는 일을 합니다.

Boost 쪽에는 STL처럼 복잡하지 않고 어댑터 기능을 간단하고 강력하게 지원합니다.

 

각 어댑터의 사용 예제부터 보겠습니다.

 

bind1st()의 예제입니다.

이 어댑터는 두 인자를 받는 predicate를 하나의 인자를 받는 predicate로 변환합니다.

또, 왼쪽 인자를 고정시키고 오른쪽 인자를 매개 변수로 취하는 predicate를 반환합니다.

#include <iostream>
#include <functional>
using namespace std;
void main()
{
    binder1st< less<int> > binder = bind1st(less<int>(), 10 );

    // binder는 어댑터(bind1st() )에 의해 변환된 predicate입니다. 
    cout << binder(5) <<':'<< less<int>()(10, 5) << endl; // 두 문장이 동일합니다.
     cout << binder(10) <<':'<< less<int>()(10, 10) << endl;// 두 문장이 동일합니다.
     cout << binder(20) <<':'<< less<int>()(10, 20) << endl;// 두 문장이 동일합니다.
     cout << "=====위와 같음=====" << endl;
    cout << bind1st(less<int>(), 10 )(5) <<':'<< less<int>()(10, 5) << endl;// 같다.
     cout << bind1st(less<int>(), 10 )(10) <<':'<< less<int>()(10, 10) << endl;// 같다.
     cout << bind1st(less<int>(), 10 )(20) <<':'<< less<int>()(10, 20) << endl;// 같다.   
 }

  1. 0:0
    0:0
    1:1
    =====위와 같음=====
    0:0
    0:0
    1:1

 bind1st()의 왼쪽 인자는 변환하기 위한 predicate이며 오른쪽 인자는 변환후 고정시킬 왼쪽에 값입니다.

 

 

이제 bind2nd() 예제를 보도록 하겠습니다.

bind1st()와 동일한 역할을 합니다.

다른 점은 오른쪽 인자를 고정하고 왼쪽 인자를 매개 변수로 취하는 predicate를 반환합니다.

#include <iostream>
#include <functional>
using namespace std;

void main()
{
    binder2nd< less<int> > binder = bind2nd(less<int>(), 10 );
    cout << binder(5) <<':'<< less<int>()( 5, 10) << endl; // 두 문장이 동일합니다.
    cout << binder(10) <<':'<< less<int>()(10, 10) << endl;// 두 문장이 동일합니다.
    cout << binder(20) <<':'<< less<int>()(20, 10) << endl;// 두 문장이 동일합니다.
    cout << "==========" << endl;
    cout << bind2nd(less<int>(), 10 )(5) <<':'<< less<int>()(5, 10) << endl;// 같다.
    cout << bind2nd(less<int>(), 10 )(10) <<':'<< less<int>()(10, 10) << endl;// 같다.
    cout << bind2nd(less<int>(), 10 )(20) <<':'<< less<int>()(20, 10) << endl;// 같다.   
}
  1. 1:1
    0:0
    0:0
    ==========
    1:1
    0:0
    0:0

 쉽죠~~잉! ㅡㅡ;

 

not1()과 not2()는 bind1st()나 bind2nd로 변환한 predicate를 NOT하는 역할을 하는 어댑터입니다.

예제를 보도록 하겠습니다.

#include <iostream>
#include <functional>
using namespace std;

void main( )
{
    binder2nd< less<int> > binder = bind2nd(less<int>(), 10 );
    unary_negate< binder2nd<less<int> > > unegate = not1(binder);

    cout << unegate(5) <<':'<< not1(binder)(5) <<':'<< not1( bind2nd(less<int>(), 10) )(5) << endl; //모두 같다.
    cout << unegate(10) <<':'<< not1(binder)(10) <<':'<< not1( bind2nd(less<int>(), 10) )(10) << endl;//모두 같다.
    cout << unegate(20) <<':'<< not1(binder)(20) <<':'<< not1( bind2nd(less<int>(), 10) )(20) << endl;//모두 같다.
}
  1. 0:0:0
    1:1:1
    1:1:1

단지, 한 인자를 매개 변수로 받는 predicate를 NOT 하는 predicate를 반환합니다.

 

아래 예제는 MSDN의 예제코드입니다. 참고하세요.

#include <vector>
#include <functional>
#include <algorithm>
#include <iostream>
using namespace std;
int main()
{
    vector<int> v1;
    vector<int>::iterator Iter;

    int i;
    for (i = 0; i <= 7; i++)     {
        v1.push_back(5 * i);
    }
    cout << "The vector v1 = ( ";
    for (Iter = v1.begin(); Iter != v1.end(); Iter++)
        cout << *Iter << " ";
    cout << ")" << endl;

    vector<int>::iterator::difference_type result1;
    // Count the elements greater than 10
    result1 = count_if(v1.begin(), v1.end(), bind2nd(greater<int>(), 10));
    cout << "The number of elements in v1 greater than 10 is: "
        << result1 << "." << endl;

    vector<int>::iterator::difference_type result2;
    // Use the negator to count the elements less than or equal to 10
    result2 = count_if(v1.begin(), v1.end(),
        not1(bind2nd(greater<int>(), 10)));

    cout << "The number of elements in v1 not greater than 10 is: "
        << result2 << "." << endl;
}
  1. The vector v1 = ( 0 5 10 15 20 25 30 35 )
    The number of elements in v1 greater than 10 is: 5.
    The number of elements in v1 not greater than 10 is: 3.

 아~! 성의 없죠? ㅡㅡ;;

 

네 어댑터가 모두 비슷비슷하므로 하나만 구현해 보도록 하겠습니다.

이제 마지막으로 bind1st()함수와 비슷한 함수를 만들어 보도록 하겠습니다.

실제 bind1st()는 몇 가지 멤버를 더 가지고 있지만, 주요 코드는 아래 예제와 같습니다.

#include <iostream>
#include <functional>
using namespace std;
template<typename PredType,typename ArgType>
class Binder1st
{
    //... 다른 정보를 표현하기 위한 멤버....
    PredType pred;
    ArgType left;
public:
    Binder1st(const PredType& _pred,const ArgType& _left):pred(_pred),left(_left) { }
    bool operator() ( const ArgType& right )
    {
        return left < right;
    }
};
template<typename PredType,typename ArgType>
Binder1st< PredType,ArgType > Bind1st(const PredType& pred, const ArgType& left)
{
    return Binder1st<PredType,ArgType>( pred, left );
}
void main( )
{
    Binder1st< less<int>, int > binder = Bind1st(less<int>(), 10 );
    cout << binder(5) <<':'<< less<int>()(10, 5) << endl; // 두 문장이 동일합니다.
    cout << binder(10) <<':'<< less<int>()(10, 10) << endl;// 두 문장이 동일합니다.
    cout << binder(20) <<':'<< less<int>()(10, 20) << endl;// 두 문장이 동일합니다.
    cout << "==========" << endl;
    cout << Bind1st(less<int>(), 10 )(5) <<':'<< less<int>()(10, 5) << endl;// 같다.
    cout << Bind1st(less<int>(), 10 )(10) <<':'<< less<int>()(10, 10) << endl;// 같다.
    cout << Bind1st(less<int>(), 10 )(20) <<':'<< less<int>()(10, 20) << endl;// 같다.   
}
  1. 0:0
    0:0
    1:1
    ==========
    0:0
    0:0
    1:1

 Bind1st()라는 함수는 왼쪽 인자를 고정시키고 오른쪽 인자를 매개변수로 취하는 함수자 클래스(Binder1st) 객체를 반환합니다. 이 Binder1st 객체가 변환된 predicate입니다.




출처 : http://blog.daum.net/coolprogramming/76

728x90

+ Recent posts