코가손의 블로그

C++ 콜백 함수 본문

C++/문법

C++ 콜백 함수

Cogason 2021. 11. 11. 14:46

콜백 함수란, 상황이 일어나길 기다리다가 조건이 맞춰지면 특정한 기능을 호출하도록 하는 함수를 말한다.

콜백 함수를 이해하기위해 먼저 함수 포인터함수 객체에 대해 먼저 알아보겠다.

 

함수 포인터

말 그대로 함수를 가리키는 포인터 이다.

함수의 로직 자체를 매개변수로 넘겨주고 싶을 때 사용한다.

 

// int(int a, int b) -> Add함수의 시그니처 라고 말함
int Add(int a, int b)
{
    return a + b;
}

// 정의1
// typedef int(FUNC_TYPE)(int a, int b);   C++ 11 이전
// using FUNC_TYPE = int(int a, int b);  Modern C++
// FUNC_TYPE* fn;

// 정의2
int (*fn)(int, int);

// 함수 포인터
fn = &Add;  // &생략 가능(C 언어 호환성 때문)

int result1 = fn(1, 2);
int result2 = (*fn)(1, 2);    // 함수포인터는 *(접근 연산자)붙여도 함수 주소를 뜻함

함수 포인터 응용

 

class Item
{
public:
    int _itemId;    // 아이템을 구분하기 위함
    int _rarity;    // 희귀도
    int _ownerId;   // 소지자ID
}

Item* FindItem(Item items[], int itemCount, ITEM_SELECTOR* selector)
{
    for(int i = 0; i < itemCount; i++)
    {
        Item* item = &items[i];
        
        // TODO : 동작-> ID별 탐색, 희귀도별 탐색, 소지자 별 탐색, ...etc
        // 함수 포인터 이용하지 않으면 각 로직마다 함수를 따로 만들어야 함
        if (selector(item))
            return item;
    }
    
    return nullptr;
}

bool IsRareItem(Item* item)
{
    return item->_rarity >= 2;
}

// 함수 포인터
using ITEM_SELECTOR = bool(Item* item);
ITEM_SELECTOR *selector;

selector = &IsRareItem;

...

FindItem(items[], count, selector);

하지만, 이 방법으로는 전역함수/정적함수 와 같이 호출규약이 동일한 함수만 포인터에 담을 수 있다.

클래스의 멤버함수를 호출하려면 아래와 같은 방법을 사용해야 한다.

 

typedef int(*PFUNC)(int, int);
typedef int(Knight::*PMEMFUNC)(int, int);

Knight k1;
k1.GetHp(1, 1);

PMEMFUNC mfn;
mfn = &Knight::GetHp;

(k1.*mfn)(1, 1);

Knight* k2 = new Knight();
((*k2).*mfn)(1, 1);
(k2->*mfn)(1, 1);

 

함수 객체

함수처럼 동작하는 객체를 말한다.

 

// 함수 포인터 이용
void HelloWorld()
{
    cout << "Hello World" << endl;
}

int main()
{
    void (*pfunc)(void);
    pfunc = &HelloWorld;
    
    // 함수 호출
    (*pfunc)();
    
}

함수 포인터는 함수 포인터 객체의 상태를 표현할 수 없고 시그니처를 맞춰 주어야 사용가능하다는 단점이 있다.

이러한 단점을 보완한 함수 객체는 어떤지 살펴보겠다.

 

// 함수 객체 이용
// HelloWorld() 의 ()연산자 오버로딩 필요하다

class Functor
{
public:
    void operator()()
    {
        cout<<"Functor Test"<<endl;
        cout<< _value <<endl;
    }
    
    bool operator()(int num)
    {
        return true;
    }
private:
    int _value = 0;
}

int main()
{
    Functor functor;
    functor();
    bool ret = functor(1);
    
}

함수 객체를 이용하면

함수 객체의 상태를 미리 정의해두고(이동 좌표, 플레이어 상태 등)

정의해둔 상태를 실행하는 함수를 발동하여 (상태에 따른 좌표이동)

여유가 될 때, 또는 실행해야 될 때를 나중에 미루어서 동작시킬 수 있다.

이것을 Command Pattern이라고 한다.

 

콜백 함수

특정한 상황이 일어났을 때, 기능을 호출하는 콜백 함수를

함수 객체, 템플릿을 이용하여 구현해 보았다.

 

class Item
{
public:
    int _itemId = 0;
    int _rarity = 0;
    int _ownerId = 0;
};

// 함수 객체
class FindByOwnerId
{
public:
    bool operator()(const Item* item)
    {
        return (item->_ownerId == _ownerId);
    }
public:
    int _ownerId; // 함수 객체 상태
};

// 함수 객체2
class FindByRarity
{
public:
    bool operator()(const Item* item)
    {
        return (item->_rarity == _rarity);
    }
public:
    int _rarity;
};

template<typename T>
Item* FindItem(Item items[], int itemCount, T selector)
{
    for (int i = 0; i < itemCount; i++)
    {
        Item* item = &items[i];
        if (selector(item))
            return item;
    }
    return nullptr;
}

int main()
{
    Item items[10];
    items[3]._ownerId = 100;
    items[8]._rarity = 2;
    
    FindByOwnerId functor1;
    functor1._ownerId = 100;
    
    FindByRarity functor2;
    functor2._rarity = 2;
    
    Item* item1 = FindItem(items, 10, functor1);
    Item* item2 = FindItem(items, 10, functor2);
    
    return 0;
}

'C++ > 문법' 카테고리의 다른 글

[C++/기초] using vs typedef  (0) 2021.11.11
C++ 중괄호 초기화 {}  (0) 2021.11.11
C++ 캐스팅(static_cast, dynamic_cast, const_cast, reinterpret_cast)  (0) 2021.11.11
C++ 문자와 문자열  (0) 2021.11.11
C++ 동적 할당  (0) 2021.11.11
Comments