F.R.I.D.A.Y.

함수 포인터를 배워야 하는 이유2 : callback 함수 본문

DEV/C C++

함수 포인터를 배워야 하는 이유2 : callback 함수

F.R.I.D.A.Y. 2019. 11. 17. 23:28
반응형

 우리나라 많은 사람들이 이용하는 Windows 운영체제는 주기적으로 업데이트를 거쳐 다양한 기능을 추가로 제공합니다. 운영체제를 다시 설치하지 않는데 어떻게 새로운 기능이 추가될 수 있을까요?

# 이전 <함수 포인터를 배워야 하는 이유>에 이어서 작성하는 포스트입니다.

 


Callback

 사전에서의 Callback 정의는 다음과 같습니다.

네이버 사전

 프로그램을 작성하는 과정에서도 콜백(Callback)이라는 기술을 사용합니다. Node.js가 프로그램을 실행하는 방식이 콜백 함수가 다른 콜백 함수를 호출하는 식으로 이루어진다고 합니다.

 프로그래밍에선 아래와 같은 기능을 콜백 함수라고 부릅니다.

 "유용한 함수를 컴파일해 배포했습니다. 사람마다 원하는 세부 기능이 다를 수 있으므로 본인의 함수 안에서 사용자가 원하는 함수를 직접 작성할 수 있도록 공간을 마련했습니다. 이제 사용자는 이 공간을 채워 넣기만 하면 됩니다."

 이러한 콜백 함수는 주요 프로그램, 대표적으로 OS에서 사용합니다. Windows 운영체제에선 프로그램이 돌아갈 수 있도록 플랫폼 역할로서 운영체제 본인이 제공하는 많은 API(Application Programming Interface)를 담고 있습니다. 이러한 API는 그 개수에 있어 타의 추종을 불허하지만 그 많은 개수로도 이용자(프로그래머)들의 요구를 충족할 수는 없는 노릇입니다. 이용자 개개인의 요구를 들어주면 그만큼 비슷하면서 약간씩만 다른 함수를 무한정 만들어내야 하니까요. 개발사 입장에서도 이는 굉장히 비효율적인 일이고 OS에 부담을 주는 일이 됩니다. 그래서 기능의 특정 부분을 비워둔 채 이를 이용자들이 채우도록 하는 경우가 많습니다.

 일반적으로 API는 이용자들이 호출하는 함수입니다. 그러나 콜백 함수는 이용자들이 만든 함수를 API가 호출하는 구조를 가지고 있습니다. 이때 API가 호출하는 이용자 함수를 callback 함수라고 부릅니다.

 다음 문단에선 코드로 설명을 진행합니다.


코드로 살펴보기

 많은 사람들에게 유용한 함수(API)가 있다고 가정합니다. 여기에선 편의를 위해 두 수를 더하는 함수로 예시 들겠습니다.

int add(int a, int b){
	return a+b;
}

 이 add()는 굉장히 유용해서 많은 사람들이 이용합니다. 그러나 일부 사용자들이 이 add()를 만든 개발자에게 요구합니다.

 "입력받은 값이 음수일 땐 양수로 바꿔서 계산하는 기능을 넣어주면 안 되나요?"

 하나 둘, 사용자로부터 요구사항이 나오기 시작합니다. 개발자는 모든 요구사항에 대처할 수 있는 함수를 만들 수 없자 이제 원하는 기능을 직접 만들라고 인자 두 개를 추가했습니다.

int add(int a, int b, int (*fp_a)(int), int (*fp_b)(int)){
    if(fp_a != NULL){
    	a = fp_a(a);
    }
    if(fp_b != NULL){
    	b = fp_b(b);
    }
    return a + b;
}
int (*fp_a)(int) int (*fp_b)(int)

 추가된 인자는 이 두 개로 자료형이 함수 포인터입니다.

 이전 함수에서는 add() 함수를 사용하려면 아래와 같이 이용해야 했습니다.

add(3, 5);
add(6, -3);
add(7, 1);

 이제 새로이 만들어진 인자 네 개를 가진 add() 함수를 이용할 땐 아래와 같이 이용해야 합니다.

int positive(int a){ // 양수화 함수
    if(a < 0) return -a;
    return a;
}

int square(int a){ // 제곱함수
    return a * a;
}

add(1, 3,  NULL, NULL);     // 1
add(3, 6,  positive, NULL); // 2
add(5, -5, NULL, NULL);     // 3
add(7, 5,  NULL, NULL);     // 4
add(4, -4, NULL, positive); // 5
add(3, 7,  NULL, NULL);     // 6
add(2, -4, NULL, square);   // 7

 예시 2번과 5번엔 각각 3, 4번 인자에 positive 함수가 들어가 있습니다. 7번엔 4번 인자에 square 함수가 들어가 있네요. 이렇게 들어간 함수들은 add 함수가 본인 코드 안에서, 적절한 위치에서 실행하도록 합니다.

 아래 코드를 테스트해보세요.

#include <stdio.h>


int positive(int a){ // 양수화 함수
    if(a < 0) return -a;
    return a;
}

int square(int a){ // 제곱함수
    return a * a;
}

int add(int a, int b, int (*fp_a)(int), int (*fp_b)(int)){
    if(fp_a != NULL){
    	a = fp_a(a);
    }
    if(fp_b != NULL){
    	b = fp_b(b);
    }
    return a + b;
}

int main(void){
	printf("%d\t",add(1, 3,  NULL, NULL));     // 1
	printf("%d\t",add(3, 6,  positive, NULL)); // 2
	printf("%d\t",add(5, -5, NULL, NULL));     // 3
	printf("%d\t",add(7, 5,  NULL, NULL));     // 4
	printf("%d\t",add(4, -4, NULL, positive)); // 5
	printf("%d\t",add(3, 7,  NULL, NULL));     // 6
	printf("%d\n",add(2, -4, NULL, square));   // 7
    
    return 0;
}

 기존의 인자 두 개를 가진 add 함수에선 위 값들을 입력하면 아래 표와 같이 출력이 나옵니다. 그러나 인자 네 개를 지닌 add 함수를 이용하면 표 아래 이미지와 같이 다른 값이 나오게 됩니다.

TestCase a b return
1 1 3 4
2 3 6 9
3 5 -5 0
4 7 5 12
5 4 -4 0
6 3 7 10
7 2 -4 -2

인자 네 개의 add 함수의 결과값. 왼쪽부터 1번 case

 이런 식으로 코드의 유지보수도 간결하게 하면서 필요한 기능을 이용자가 전가하도록 할 수 있는 기능이 callback입니다. 이 콜백 기능에서 가장 중요한 것이 함수 포인터입니다.


그래서 어디다 써?

 이러한 콜백 함수는 Windows 프로그래밍을 진행할 때 가장 기본이 되는 함수입니다. Windows 프로그래밍에선 WndProc()라 불리는 함수가 존재합니다. 이 함수 또한 대표적인 콜백 함수입니다.

 또한, OS에서 차후에 나올 기능들에 대해 공간을 남겨놓는 것으로 이용이 가능합니다. 함수 포인터는 대상이 실재하지 않더라도 비워두고 프로그래밍을 진행할 수 있습니다. 그래서 운영체제가 개발된 후에 나온 하드웨어(cpu, gpu)들을 이용할 수 있는 것입니다. 필요할 때 하드웨어 드라이버를 갈아끼워 운영체제가 이 하드웨어를 이용할 수 있도록 H/W 제작사가 함수를 만들면 되니까요.

 

# index

728x90
반응형
Comments