728x90

Deferred Context를 이용하여 텍스처를 수정하고 렌더링하는 과정에서 


D3D11 WARNING: ID3D11Device::RemoveContext: Deferred Context removal has been triggered for the following reason (어쩌구 저쩌구...)


라는 엄청 긴 메세지와 함께 프로그램은 실행되고 있지만, DebugView에서 엄청나게 많은 메세지를 찍어대면서 몇개의 영상이 멈추었다.




그래서 인터넷에 찾아보니 ID3D11CommandList는 SAFE_RELEASE()를 매크로를 사용하지말고 그냥 Release() 함수를 사용해야만 한다고 한다.


// Deferred Execute

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

{

    if (m_pDX11DisplayProperty->m_pRenderingCommandList[i] != nullptr)

    {

        DeviceContext->ExecuteCommandList(m_pDX11DisplayProperty->m_pRenderingCommandList[i], TRUE);

        //SAFE_RELEASE(m_pDX11DisplayProperty->m_pRenderingCommandList[i]);     // <-- 이렇게 사용하면 안됨.

        m_pDX11DisplayProperty->m_pRenderingCommandList[i]->Release();

    }


    //m_pUpdateClass[i].m_pKeyedMutex->ReleaseSync(uRelKey);

}


출처 : https://community.amd.com/thread/128535



위에 이야기는 테스트 결과로 봤을 때 잘못된 정보였다 !!!!


결과만 말하면 FinishCommandList()가 ExecuteCommandList()보다 많이 호출되어 CommandList의 데이터가 소모가 되지 않고 쌓이기 때문에 Memory Leak 처럼 보인 것이다.


항상 ExecuteCommandList()가 FinishCommandList() 보다 더 많이 호출될 수 있게 하도록 하자.

728x90
728x90

Windows에서 할 일 


CreateWindow()를 한 이후에 SetLayeredWindowAttributes()를 호출 할 것.


WS_EX_LAYERED를 꼭 넣어야 한다.


DWORD dwWindowStyle = WS_POPUPWINDOW | WS_CLIPCHILDREN | WS_CLIPSIBLINGS;

hWnd = CreateWindow(this->m_lpszTitle, this->m_lpszTitle, dwWindowStyle,

  nStartX, nStartY, nWidth, nHeight, NULL, NULL, hInstance, NULL);


if (!hWnd)

{

    return FALSE;

}

 

::SetWindowLong(hWnd, GWL_EXSTYLE, ::GetWindowLong(hWnd, GWL_EXSTYLE) | WS_EX_LAYERED | WS_EX_TOPMOST);

::SetWindowPos(hWnd, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE);

::SetLayeredWindowAttributes(hWnd, 0x0000ff00, 255, LWA_COLORKEY);





DirectX9에서 할 일


CreateOffscreenPlainSurface()로 Surface를 생성.


Clear()를 할 때에는 SetLayeredWindowAttributes()에서 지정한 색상으로 할 것.


Present() 사용하지 말 것.


BitBlt()을 이용하여 윈도우 DC에 RenderTarget DC를 그릴 것.


소스 코드 : 

Banner_dx9_test.zip


728x90
728x90

우리가 일상적으로 사용하는 각의 단위는 디그리(degree)입니다. 즉, 원 한바퀴를 360도로 표현하는 방법입니다. 반원은 180도, 직각은 90도 등 degree는 우리에게 매우 익숙한 각의 단위입니다.

 

그런데, 각을 표현하는 다른 방법으로 라디안(radian)이 있습니다. 보통 라디안은 부채꼴의 중심각을 가지고 설명되는데, 아래 그림과 같이 호의 길이가 반지름과 같게 되는 만큼의 각을 1 라디안(radian)이라고 정의합니다.

 


 

정의에 따르면 왠지 라디안은 반지름에 대한 상대적인 각도의 단위처럼 생각됩니다. 하지만 radian은 degree처럼 절대적인 각도의 단위입니다. 실제로 1 radian은 약 57.3도에 해당하는 각입니다. 그러면 2 radian은 약 114.6도가 되겠지요. 여기서 우리는 degree보다는 radian이 훨씬 큰 각의 단위라는 걸 알 수 있습니다.

 

반지름이 3이고 중심각이 2 radian인 부채꼴이 있습니다. 그러면 이 부채꼴의 호의 길이는 얼마일까요? radian의 정의에 의해 답은 6입니다. 반지름과 중심각의 크기만 알면 호의 길이를 이렇게 쉽게 구할 수 있다는게 신기하지 않나요? 반대로, 호의 길이가 6이고 반지름이 3인 부채꼴이 있다면 이 부채꼴의 중심각은 얼마일까요? 바로 2 radian입니다. 즉, 약 114.6도입니다. 부채꼴 도형에서 반지름, 중심각, 호의 길이 3가지 중에 2가지만 알면 다른 한가지는 곧바로 구할 수 있게 됩니다.


자동차가 달리고 있는데 오른쪽으로 10도 틀으라고 하면 대략 얼마나 돌려야 할지 감이 올 것입니다. 그런데, 만일 오른쪽으로 0.5 radian 돌려라고 한다면 도대체 이게 어느 정도의 각인지 도무지 감이 오지 않을 것입니다. 이와 같이 우리에게 친숙한 각의 단위는 degree입니다.

 

그러면, 우리에게 친숙한 degree로만 각을 표현하면 좋을텐데 왜 이렇게 복잡하게 radian이라는 것을 도입해서 문제를 어렵게 하는 걸까요? 사실 저도 이유는 잘 모릅니다. 하지만 분명한 것은 우리 사람이 선호하는 각 체계는 degree인 반면 컴퓨터나 수학에서 주로 사용되는 각 체계는 radian이라는 것입니다. 그래서 표준을 정하거나 공동작업을 할 때 종종 각을 어떻게 표현할 것인가를 두고 사람들끼리 논쟁이 일어나기도 합니다. 사람을 중심에 놓고 생각하는 사람들은 degree를 사용하자고 주장하고, 다른 한편에서는 어차피 컴퓨터에서는 radian으로 고쳐서 사용해야 하기 때문에 계산의 효율성을 위해서 radian을 사용하자고 주장합니다.

 

컴퓨터에서 프로그램을 개발할 때에도 degree와 radian을 변환하는 일이 수시로 발생합니다. 사용자에게 각을 입력받을 때는 보통 degree로 입력받은 후 이를 내부적으로 radian으로 고쳐서 필요한 계산을 수행합니다. 그리고 계산된 결과를 보여줄 때는 다시 사람에게 친숙한 degree로 변환하여 보여줍니다. 이러한 변환작업은 사실 번거롭긴 하지만 어쩔 수 없는 일이기도 합니다.

 

그렇다면 degree와 radian은 어떤 변환 관계를 가지고 있을까요? 180도는 π radian입니다. 여기서 π(파이)는 3.1415926535... 의 값을 갖는 무한소수로 원주율(원의 지름에 대한 원주의 비)을 말합니다. 이 관계식을 이용하면 다음과 같이 자유롭게 degree와 radian의 단위를 변경할 수 있습니다.

 

180 degree = π radian

1 degree = π / 180 radian

x degree = x * π / 180 radian

 

π radian = 180 degree

1 radian = 180 / π degree

x radian = x * 180 / π degree

 

좀전에 자동차 예에서 나온 0.5 radian을 위 수식을 이용하여 degree로 고치면 0.5 * 180 / 3.1415926535 = 28.64788... degree 정도가 됩니다. 그러나, 수식을 이용하지 않고도 대략적인 radian의 값을 파악하는 방법이 있습니다. 그건 먼저 머리속에서 부채꼴을 하나 상상합니다. 그 부채꼴의 호의 길이는 반지름과 같은 길이를 갖도록 합니다. 그러면 그 부채꼴의 중심각이 1 radian입니다. 이제 0.5 radian을 구하고 싶으면 상상속의 그 부채꼴의 중심각을 2등분하면 됩니다. 그러면 대략적으로 0.5 radian이 어느정도의 각인지 알 수 있을 것입니다.

 

 

변환 방법.

1. 라디안->디그리으로 변환공식

 - 180.0f/NI_PI * (라디안) = 디그리

 

2. 디그리->라디안으로 변환공식

 - NI_PI/180.0f * (디그리) = 라디안

 

#define RadToDeg(x) (57.29577951 * x)

#define DegToRad(x) (0.017453293 * x)

 

360도는 라디안 값으로-> 360 * DegToRad = 6.28318548(2π)

다시 디그리 값으로->6.28318548 * RadToDeg = 360

 

출처 : http://darkpgmr.tistory.com/26

         http://blog.naver.com/yooglory?Redirect=Log&logNo=110067469483

728x90

'Math' 카테고리의 다른 글

Math - 동차 좌표계  (0) 2017.04.20
Math - 점과 점 사이의 각도 구하기  (0) 2017.04.19
Math - 점과 점 사이의 거리 구하기  (0) 2017.04.19
Math - 정반사 벡터  (0) 2016.03.07
Math - 피타고라스의 정리  (0) 2016.03.07
728x90

해외 튜토리얼http://www.rastertek.com/tutindex.html

위 사이트는 해외에서 DirectX, OpenGL 등 다양한 튜토리얼이 있다.



해외 튜토리얼http://www.braynzarsoft.net/old/

위 사이트는 해외에서 DirectX, OpenGL 등 다양한 튜토리얼이 있다.



해외 튜토리얼 : http://www.dhpoware.com/demos/index.html

위 사이트는 해외에서 DirectX, OpenGL 등 다양한 튜토리얼이 있다.



빠재의 노트http://ppparkje.tistory.com/10

위 사이트는 해외에서 튜토리얼을 만들어 놓은 것을 꼼꼼하게 번역하여 정리를 한 사이트. 



Vanica's Lifeloghttp://blog.naver.com/sorkelf

위 사이트는 C, DirectX9 ~ 11 등등 게임에 필요한 많은 정보를 담고 있다.




728x90

'좋은 사이트' 카테고리의 다른 글

사이트 - Hooking  (0) 2021.01.19
사이트 - Vulkan 강좌  (0) 2020.10.25
사이트 - wxWidget  (0) 2017.09.04
사이트 - MS에서 제공하는 무료 서적 사이트  (0) 2016.03.07
사이트 - 무료 수학 강의 사이트  (0) 2016.03.07
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
728x90

이번 시간에는 지금까지 배운 내용을 종합하여 STL의 ptr_fun(), mem_fun(), mem_fun_ref()를 직접 구현해 보도록 하겠습니다.

이 세 함수는 함수자를 요구하는 알고리즘에 일반적인 함수들을 실행가능하도록 랩핑하는 함수들입니다.

이 페이지는 '함수 포인터와 함수자1,2,3'를 공부하고 보시기 바랍니다.

 

앞쪽에서 함수 포인터는 호출형태에 따라 세 가지로 나뉜다고 했습니다.(앞쪽 참고)

  • 첫째, 정적함수 호출 (예  (*pfunc)( 10 );  )
  • 둘째, 주소로 멤버 함수 호출 (예  (data.*pf)( 10 ); )
  • 셋째, 객체의 주소로 멤버 함수 호출 (예 (data->*pf)( 10 ); )

이 세가지 경우처럼 어댑터 역할을 하는 랩핑함수 ptr_fun(), mem_fun(), mem_fun_ref()도 그래서 세 가지입니다.

 

첫 번째 경우인 ptr_fun()함수를 사용한 예제입니다.

for_each()로 콜백하는 코드를 예제로 사용합니다.

bool Printint n )
{
    cout << "int : " << n << endl;
    return true;
}
void main()
{
    vector<int> v(10);
    int i;

    for(i = 0 ; i < (int)v.size() ; i++ )
        v[i] = i+1;

    for_each(v.begin(), v.end(), Print );
    cout << "=================" << endl;
    for_each(v.begin(), v.end(), ptr_fun(Print) );
}
  1. int : 1
    int : 2
    int : 3
    int : 4
    int : 5
    int : 6
    int : 7
    int : 8
    int : 9
    int : 10
    =================
    int : 1
    int : 2
    int : 3
    int : 4
    int : 5
    int : 6
    int : 7
    int : 8
    int : 9
    int : 10

내용은 어렵지 않지만, for_each()에 ptr_fun()을 사용한 이유가 궁금할 수 있습니다. 위 코드에는 굳이 ptr_fun()이 없어도 되지만 다른 어댑터 함수와 같이 사용하는 경우에(not1()등) 꼭 필요합니다. 간단한 설명은 ptr_fun()에서 사용하는 함수자 클래스의 typedef인 argument_type과 result_type을 사용하기 위해서 입니다. 자세한 내용은 STL에서 공부하고 지금은 ptr_fun()구현 사항에 집중하도록 하겠습니다.

 

이제 위와 같은 ptr_fun()과 비슷한 함수 Ptr_fun()을 만들어 보도록 하겠습니다.

//////////////////// server ///////////////////
// 클래스 : ptr_fun() 함수의 함수자 클래스
// 여러가지 기능을 추가로 제공하기 위한 클래스. typedef argument_type, result_type 등....
class Ptr_fun_class
{
    bool (*pf)(int );
public :
    Ptr_fun_class(bool (*_pf)(int ) ):pf(_pf) { }
    bool operator()(int n )
    {
        return pf(n);
    }
};
// 함수 : 함수자를 반환하는 ptr_fun() 함수
Ptr_fun_class Ptr_funbool (*pf)(int ) )
{
    return Ptr_fun_class(pf);  
}


/////////// client ///////////////////
 bool Printint n )
{
    cout << "int : " << n << endl;
    return true;
}
void main()
{
    vector<int> v(10);
    int i;

    for(i = 0 ; i < (int)v.size() ; i++ )
        v[i] = i+1;

    for_each(v.begin(), v.end(), Ptr_fun(Print) );
}

  1. int : 1
    int : 2
    int : 3
    int : 4
    int : 5
    int : 6
    int : 7
    int : 8
    int : 9
    int : 10

Ptr_fun_class 클래스는 정적함수의 주소를 저장하는 함수자 클래스입니다.

Print함수는 어댑터 기능을 하는 Ptr_fun()함수에 래핑되어 함수가 반환하는 Ptr_func_class의 함수자 객체를 for_each()에 전달합니다.

for_each()는 그 함수자를 호출합니다.(for_each()는 '함수 포인터와 함수자3'를 참고하세요)

 

다음은 모든 자료형이 가능하도록 템플릿 클래스로 수정한 예제입니다.

 //////////////////// server ///////////////////
// 클래스 : ptr_fun()의 함수자 클래스
// 여러가지 기능을 제공할 수 있습니다. typedef argument_type, result_type 등....
template<typename RType, typename AType>
class Ptr_fun_class
{
    RType (*pf)( AType );
public :
    Ptr_fun_class( RType (*_pf)( AType ) ):pf(_pf) { }
    RType operator()( AType n )
    {
        return pf(n);
    }
};
// 함수 : 함수자를 반환하는 ptr_fun() 함수
template <typename RType, typename AType>  
Ptr_fun_class<RType, AType> Ptr_fun( RType (*pf)(AType) )
{
    return Ptr_fun_class<RType, AType>(pf);
}

 

/////////// client ///////////////////
bool Printint n )
{
    cout << "int : " << n << endl;
    return true;
}
void main()
{
    vector<int> v(10);
    int i;

    for(i = 0 ; i < (int)v.size() ; i++ )
        v[i] = i+1;

    for_each(v.begin(), v.end(), Ptr_fun(Print) );
}

  1. int : 1
    int : 2
    int : 3
    int : 4
    int : 5
    int : 6
    int : 7
    int : 8
    int : 9
    int : 10

내용은 같습니다.

 

 

두 번째 경우인 mem_fun()를 사용한 예제입니다.

for_each()로 Point의 맴버함수 Print()를 콜백하는 예제입니다.

#include <iostream>
#include <vector>
#include <algorithm>
#include <functional>
using namespace std;
class Point
{
    int x, y;
public:
    Point(int _x = 0, int _y = 0) : x(_x), y(_y) { }
    void Print( )
    {
        cout << "(" << x << "," << y << ")" << endl;
    }
};

void main()
{
    vector<Point*> v(10);
    int i;

    for(i = 0 ; i < (int)v.size() ; i++ )
        v[i] = new Point(i+1, i+1);

    //for_each(v.begin(), v.end(), &Point::Print ); 에러~~~
    for_each(v.begin(), v.end(), mem_fun(&Point::Print) );
    // delete 생략
}
  1. (1,1)
    (2,2)
    (3,3)
    (4,4)
    (5,5)
    (6,6)
    (7,7)
    (8,8)
    (9,9)
    (10,10)

컨테이너 v는 Point의 주소들을 저장하고 있습니다.

for_each()는 Point의 멤버 함수 Print()를 호출할 수 없습니다. 이유는?? 우리가 앞쪽에서 공부했듯이 정적함수와 멤버 함수 호출방식(인터페이스)이 다르기 때문입니다. 그래서 호출방식을 통일 시키기위한 함수자(어댑터 객체)가 필요하고 mem_fun()이 멤버 함수를 호출가능하도록 함수자 객체를 생성하여 반환하는 것입니다.

 

이제 우리도 mem_fun()과 비슷한 함수 Mem_fun()을 만들어 보도록 하겠습니다.

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

//////////////////// server ///////////////////
// 클래스 : mem_fun()의 함수자 클래스
// 여러가지 기능을 제공할 수 있습니다. typedef argument_type, result_type 등....
class Point;
class Mem_fun_class
{
    bool (Point::*pf)();
public :
    Mem_fun_class( bool (Point::*_pf)() ):pf(_pf) {}
    bool operator()( Point* p )
    {      
        return (p->*pf)();
    }
};
// 함수 : 함수자를 반환하는 mem_fun() 함수
Mem_fun_class Mem_funbool (Point::*pf) () )
{
    return Mem_fun_class( pf );
}

/////////// client ///////////////////
class Point
{
    int x, y;
public:
    Point(int _x = 0, int _y = 0) : x(_x), y(_y) { }
    bool Print( )
    {
        cout << "(" << x << "," << y << ")" << endl;
        return true;
    }
};

void main()
{
    vector<Point*> v(10);
    int i;

    for(i = 0 ; i < (int)v.size() ; i++ )
        v[i] = new Point(i+1, i+1);

    for_each(v.begin(), v.end(), Mem_fun(&Point::Print) );
    cout << "=============" << endl;
    Mem_fun_class functor(&Point::Print);
    for_each(v.begin(), v.end(), functor );
    // delete 생략
}
  1. (1,1)
    (2,2)
    (3,3)
    (4,4)
    (5,5)
    (6,6)
    (7,7)
    (8,8)
    (9,9)
    (10,10)
    =============
    (1,1)
    (2,2)
    (3,3)
    (4,4)
    (5,5)
    (6,6)
    (7,7)
    (8,8)
    (9,9)

Mem_fun()함수는 Mem_fun_class의 객체(함수자)를 반환합니다. for_each()는 이 함수자를 이용하여 정적함수(일반함수)와 동일한 인터페이스로 Point::Print 멤버 함수를 호출합니다.

 

아래는 모든 자료형이 가능하도록 템플릿으로 변환한 예제입니다.

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

//////////////////// server ///////////////////
// 클래스 : mem_fun()의 함수자 클래스
// 여러가지 기능을 제공할 수 있습니다. typedef argument_type, result_type 등....
class Point;
template <typename RType, typename CType>
class Mem_fun_class
{
    RType (CType::*pf)();
public :
    Mem_fun_class( RType (CType::*_pf)() ):pf(_pf) {}
    RType operator()( CType* p )
    {      
        return (p->*pf)();
    }
};
// 함수 : 함수자를 반환하는 mem_fun() 함수
template <typename RType, typename CType>
Mem_fun_class<RType, CType> Mem_fun( RType (CType::*pf) () )
{
    return Mem_fun_class<RType,CType>( pf );
}

/////////// client ///////////////////
class Point
{
    int x, y;
public:
    Point(int _x = 0, int _y = 0) : x(_x), y(_y) { }
    bool Print( )
    {
        cout << "(" << x << "," << y << ")" << endl;
        return true;
    }
};

void main()
{
    vector<Point*> v(10);
    int i;

    for(i = 0 ; i < (int)v.size() ; i++ )
        v[i] = new Point(i+1, i+1);

    for_each(v.begin(), v.end(), Mem_fun(&Point::Print) );
    cout << "=============" << endl;
    Mem_fun_class<bool, Point> functor(&Point::Print);
    for_each(v.begin(), v.end(), functor );
    // delete 생략
}
  1. (1,1)
    (2,2)
    (3,3)
    (4,4)
    (5,5)
    (6,6)
    (7,7)
    (8,8)
    (9,9)
    (10,10)
    =============
    (1,1)
    (2,2)
    (3,3)
    (4,4)
    (5,5)
    (6,6)
    (7,7)
    (8,8)
    (9,9)
    (10,10)

 내용은 위 예제와 같습니다.

 

 

마지막으로 세 번째 경우인 mem_fun_ref()를 보도록 하겠습니다.

mem_fun()과 비슷합니다. 차이점은 컨테이너 v가 Point 주소가 아닌 Point 객체를 저장한다는 것입니다. for_each()로 Point의 맴버함수 Print()를 콜백하는 예제입니다.

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

class Point
{
    int x, y;
public:
    Point(int _x = 0, int _y = 0) : x(_x), y(_y) { }
    void Print( )
    {
        cout << "(" << x << "," << y << ")" << endl;
    }
};
void main()
{
    vector<Point> v(10);
    int i;

    for(i = 0 ; i < (int)v.size() ; i++ )
        v[i] = Point(i+1, i+1);

    //for_each(v.begin(), v.end(), &Point::Print ); 에러~~~~
    for_each(v.begin(), v.end(), mem_fun_ref(&Point::Print) );
    // delete 생략
}
  1. (1,1)
    (2,2)
    (3,3)
    (4,4)
    (5,5)
    (6,6)
    (7,7)
    (8,8)
    (9,9)
    (10,10)

 컨테이너 v가 Point객체를 저장합니다. 내용은 모두 동일합니다.

 

다음은 mem_fun_ref()과 비슷한 함수 Mem_fun_ref()을 만들어 본 예제입니다.

#include <iostream>
#include <vector>
#include <algorithm>
#include <functional>
using namespace std;
//////////////////// server ///////////////////
// 클래스 : mem_fun()의 함수자 클래스
// 여러가지 기능을 제공할 수 있습니다. typedef argument_type, result_type 등....
class Point;
template <typename RType, typename CType>
class Mem_fun_ref_class
{
    RType (CType::*pf)();
public :
    Mem_fun_ref_class( RType (CType::*_pf)() ):pf(_pf) {}
    RType operator()( CType& d )
    {
        return (d.*pf)();
    }
};
// 함수 : 함수자를 반환하는 mem_fun() 함수
template <typename RType, typename CType>
Mem_fun_ref_class<RType, CType> Mem_fun_ref( RType (CType::*pf) () )
{
    return Mem_fun_ref_class<RType, CType>( pf );
}

/////////// client ///////////////////
class Point
{
    int x, y;
public:
    Point(int _x = 0, int _y = 0) : x(_x), y(_y) { }
    bool Print( )
    {
        cout << "(" << x << "," << y << ")" << endl;
        return true;
    }
};

void main()
{
    vector<Point> v(10);
    int i;

    for(i = 0 ; i < (int)v.size() ; i++ )
        v[i] = Point(i+1, i+1);

    for_each(v.begin(), v.end(), Mem_fun_ref(&Point::Print) );
    cout << "=============" << endl;
    Mem_fun_ref_class<bool, Point> functor(&Point::Print);
    for_each(v.begin(), v.end(), functor );
    // delete 생략
}
  1. (1,1)
    (2,2)
    (3,3)
    (4,4)
    (5,5)
    (6,6)
    (7,7)
    (8,8)
    (9,9)
    (10,10)
    =============
    (1,1)
    (2,2)
    (3,3)
    (4,4)
    (5,5)
    (6,6)
    (7,7)
    (8,8)
    (9,9)
    (10,10)



728x90
728x90

'함수 포인터와 함수자' 세 번째 시간으로 이번에는 '콜백'에 대한 이야기를 해보도록 하겠습니다.

이 페이지는 함수 포인터, STL 기초를 알고 있어야 합니다.

 

콜백(callback)은 서버가 클라이언트를 호출하는 것을 말합니다.

보통 함수 호출은 클라이언트에서 서버를 호출하는 형태입니다. 하지만 때때로 서버가 클라이언트를 호출해야 하는 경우가 있습니다.

콜백메커니즘을 이용하면 알고리즘 정책을 클라이언트에서 유연하게 바꿀 수 있습니다. 또 GUI의 강력한 이벤트 기능도 콜백메커니즘으로 구현됩니다.

STL의 많은 알고리즘도 콜백을 이용하여 클라이언트 정책을 반영합니다.

윈도우즈(Windows)의 모든 프로시저(procedure)는 시스템이 호출하는 '콜백 함수'입니다. 중요하게 느껴지죠?

 

예를 들어 보겠습니다.

어떤 기능이나 서비스를 제공하는 코드 측을 서버라고 합니다.

그 기능을 제공 받는 코드 측을 클라이어트라 합니다.

void func( )
{
    printf("Hello!\n");
 }
void main( )
{
    func();
    func();
    func();
}
  1. Hello!
  2. Hello!
  3. Hello!

 main()함수 쪽이 클라이언트이고 func()쪽이 서버라면 main()쪽이 호출자(caller)가 되고 func()쪽이 호출받는자(callee)가 됩니다.

그림으로 보면 아래와 같습니다.

 

 

 

 

 

이때, 아래와 같이 클라이언트 코드와 서버 코드가 있을 때 서버 측에서 클라이언트 측 코드인 client()함수를 호출해야 한다면 서버 측에서 client()함수는 콜백되는 것입니다.

#include <iostream>
using namespace std;
void client( );
//////// server //////////////////////
void func( )
{
    printf("Hello!\n");
    client( ); //클라이언트 코드 호출
}

//////// client //////////////////////
void client( )
{
    printf("난 client!\n");
}
void main( )
{
    func(); //서버 코드 호출
}
  1. Hello!
    난 client!

 main()함수 쪽이 클라이언트이고 func()쪽이 서버라면 client함수 호출쪽이 caller가 되고 client()함수 쪽이 callee가 됩니다.

 아래 그림을 참고하세요.

 

 

 

콜백은 서버 측에서 클라이언트 측의 코드를 호출하므로 클라이언트 측 함수를 알아야 합니다.

위 예제는 예를 들기 위함으로 client()라는 함수를 server가 알고 있다는 가정하에 만든 것입니다.

하지만 실제 서버는 여러 클라이언트에 의해 호출되므로 미리 클라이언트 코드를 알지 못합니다.

그래서 콜백을 구현하기 위해서는 클라이언트에서 서버에게 콜백으로 사용될 함수의 정보를 알려주고 서버가 클라이언트를 호출하도록 합니다.

 

 이제 실예로 STL코드를 보도록 하겠습니다.

STL 알고리즘의 for_each()을 예로 들겠습니다. for_each() 많이 쓰시죠?

for_each()는 반복자를 이용하여 컨테이너에 들어 있는 모든 요소를 반복적으로 처리하는 함수입니다.

이때 Print1(),Print2()가 콜백 함수입니다.

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

void Print1int n )
{
    cout << "int : " << n << endl;
}

void Print2int n )
{
    cout << "int : " << n << endl;
}
void main()
{
    vector<int> v(10);
    int i;

    for(i = 0 ; i < (int)v.size() ; i++ )
        v[i] = i+1;

    for_each(v.begin(), v.end(), Print1);

       cout << "=================" << endl;

    for_each(v.begin(), v.end(), Print2);
}

  1. 1
    2
    3
    4
    5
    6
    7
    8
    9
    10
  2. ===========================
  3. int : 1
    int : 2
    int : 3
    int : 4
    int : 5
    int : 6
    int : 7
    int : 8
    int : 9
    int : 10

 for_each()는 클라이언트 Print1(),Print2()함수를 콜백하며 클라이언트 기능을 유연하게 바꿔가며 호출합니다.

아래 그림을 참고하세요.

 

 

 

함수뿐만 아니라 함수류는 모두 가능합니다.

Functor를 사용한 예제입니다.

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

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

class CallbackClass
{
public:
    void operator() (int n)
    {
        cout << "int : " << n << endl;
    }
};
void main()
{
    vector<int> v(10);
    int i;

    for(i = 0 ; i < (int)v.size() ; i++ )
        v[i] = i+1;

    for_each(v.begin(), v.end(), CallbackClass() );
    cout << "=================" << endl;
    CallbackClass callback;
    for_each(v.begin(), v.end(), callback);
}
  1. int : 1
    int : 2
    int : 3
    int : 4
    int : 5
    int : 6
    int : 7
    int : 8
    int : 9
    int : 10
    =================
    int : 1
    int : 2
    int : 3
    int : 4
    int : 5
    int : 6
    int : 7
    int : 8
    int : 9
    int : 10

 함수자 클래스(CallbackClass)를 만들어 사용한 예제일 뿐 나머지는 같습니다.

 아래는 그림입니다.

 

 

 

 

이제 for_each()과 비슷한 알고리즘을 만들어 보도록 하겠습니다.

for_each()는 반복자를 이용하여 모든 컨테이너 요소를 지정한 함수류로 반복(콜백)합니다.

 아래는 예제 코드입니다.

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
/////////////// server //////////////////////
template<typename IterT, typename FunT>
void For_each(IterT biter, IterT eiter, FunT fun)
{
    for( ; biter != eiter ; biter++ )
        fun( *biter );
}

/////////////// client /////////////////////
class CallbackClass
{
public:
    void operator() (int n)
    {
        cout << "클래스 : " << n << endl;
    }
};
void Printint n )
{
    cout << "함수 : " << n << endl;
}
void main()
{
    vector<int> v(10);
    int i;

    for(i = 0 ; i < v.size() ; i++ )
        v[i] = i+1;

    For_each(v.begin(), v.end(), Print);
    cout << "=================" << endl;
    For_each(v.begin(), v.end(), CallbackClass() );
}
  1. 함수 : 1
    함수 : 2
    함수 : 3
    함수 : 4
    함수 : 5
    함수 : 6
    함수 : 7
    함수 : 8
    함수 : 9
    함수 : 10
    =================
    클래스 : 1
    클래스 : 2
    클래스 : 3
    클래스 : 4
    클래스 : 5
    클래스 : 6
    클래스 : 7
    클래스 : 8
    클래스 : 9
    클래스 : 10

 For_each()는 모든 컨테이너와 다양한 함수에 적용해야 하므로 템플릿 함수로 만들어 집니다.

 아래는 그림입니다.

 

 

 

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

728x90
728x90

이 페이지는 함수자를 공부해 보도록 하겠습니다.

이 페이지를 공부하기 위해서는 연산자 중복, 템플릿을 알고 있어야 합니다.

 

함수자(functor)는 함수처럼 동작하는 객체입니다.

함수자는 STL의 어뎁터나 술어(predicate), 바인더, 콜백 등에 사용됩니다.

 

1, 함수자 만들기

함수자를 사용하면 함수를 호출하는 것인지? 객체로 함수를 호출하는 것인지를 추상화할 수 있습니다.

함수자는 operator() 연산자 중복된 클래스의 객체입니다.

그럼 간단한 예제코드를 만들어 보도록 하겠습니다.

 

전역 함수 호출 예

#include <iostream>
using namespace std;
void Print( )
{
    cout << "전역 함수!" << endl;
}
void main( )
{
    Print( );
}
  1. 전역 함수!

 간단하죠? ㅡㅡ;;

 

함수자를 이용한 호출 예

#include <iostream>
using namespace std;
class Functor
{
public :
    void operator( ) ( )
    {
        cout << "함수자!" << endl;
    }
};
void main( )
{
    Functor functor;

    functor( ); //함수를 호출한 것인지, 객체의 멤버 함수 operator()를 호출한 것인지 알 수 없습니다.
    functor.operator( ) ( ); // 실제 이렇게 멤버 함수를 호출합니다..
}

  1. 함수자!
  2. 함수자!

 functor객체를 만들어 함수를 호출합니다.

 

인자를 갖는 함수를 보도록 하겠습니다.

전역 함수 호출 예

#include <iostream>
using namespace std;
void Print(int n)
{
    cout << "정수 : " << n << endl;
}
void main( )
{
    Print( 10 );
}
  1. 정수 : 10

간단!

 

함수자를 이용한 호출 예

#include <iostream>
using namespace std;
class Functor
{
public :
    void operator( ) (int n)
    {
        cout << "정수 : " << n << endl;
    }
};
void main( )
{
    Functor functor;

    functor( 10 ); // 암시적 호출
    functor.operator( ) ( 10 ); //명시적 호출
}
  1. 정수 : 10
    정수 : 10

간단하죠?!

 

2, 함수, 함수자, 함수 포인터

이 세 가지는 모두 동일한 인터페이스를 갖습니다.

 

함수와 함수자, 함수 포인터를 이용한 함수 호출 예제입니다.

#include <iostream>
using namespace std;
class Functor
{
public :
    void operator( ) (int n)
    {
        cout << "정수 : " << n << endl;
    }
};

void Print(int n)
{
    cout << "정수 : " << n << endl;
}

void main( )
{
    Functor functor;
    void (*pfunc)(int ) = &Print;

    Print( 10 );  //함수 호출 인터페이스가 모두 동일합니다.
    functor( 10 );//함수 호출 인터페이스가 모두 동일합니다.
    pfunc( 10 );  //함수 호출 인터페이스가 모두 동일합니다.
}
  1. 정수 : 10
    정수 : 10
    정수 : 10

이 예제처럼 호출 인터페이스만 보면 일반 함수, 함수자, 함수 포인터가 모두 동일한 방식으로 사용된다는 것을 알 수 있습니다.

 

이제 템플릿을 이용하면 함수의 리턴 자료형과 매개 변수 자료형에 상관없이 함수자를 이용할 수 있습니다.

#include <iostream>
using namespace std;
template <typename RType, typename AType>
class Functor
{
public :
    RType operator( ) (AType n)
    {
        cout << "인자 : " << n << endl;
    }
};

void main( )
{
    Functor< voidint > functor;

    functor( 10 );
}
  1. 인자 : 10

이처럼 좀 더 일반화할 수 있습니다.


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

728x90

+ Recent posts