본문 바로가기
SW/C,C++

[C/C++] 콜백함수의 인자로써 void 포인터

by FastBench 2024. 4. 21.

콜백함수의 인자로써 void 포인터

콜백함수에 대한 그림을 그려달라니, 이런걸 그려주네요 ㅋㅋ

Intro

지난 게시글에서, 이중포인터와 void 포인터에 대해 간략하게 다뤘다.

https://microelectronics.tistory.com/54

 

[C/C++] 이중포인터와 void 포인터

이중포인터와 void 포인터 문자열배열은 이중포인터이다 아래와 같이 문자열 배열이 있고, 해당 문자열배열의 주소를 void 포인터에 초기화시켰다고 가정해보자. char* arr[] = {"banana", "apple"}; void* vr

microelectronics.tistory.com

또한 콜백함수에 대해 왜/언제 쓰는지를 알아보았다.

https://microelectronics.tistory.com/46

 

[C/C++] 콜백함수 (Callback Function)

콜백 함수란? 콜백 함수는 다른 코드의 인자로 전달되어, 그 코드에 의해 어느 시점에 호출되는 함수인데 특히 비동기적 작업, 이벤트 리스닝, 또는 특정 조건 하에서 실행되어야 할 코드 조각을

microelectronics.tistory.com

이를 바탕으로 단일 sort 함수로 다양한 데이터타입을 처리하는 예제를 다뤄보고자 한다.

타입에 맞는 sort 함수

내가 라이브러리를 만드는 개발자로서, sort 와 관련된 라이브러리를 만든다고 가정해보자.

이 경우에는, 여러 데이터 타입에 대응하는 각각의 sort 함수를 생성하는 대신, 사용자가 제공하는 비교 함수를 사용하여 유연하게 작동하는 단일 sort 함수를 설계하는 것이 효율적일 것이다.

 

이 접근 방식에서 sort 함수는 비교 로직을 처리할 수 있는 '함수 포인터'를 인자로 받는다.

이 함수 포인터는 사용자가 정의한 비교 함수를 가리키며, 이 함수는 sort 함수 내에서 데이터 항목들을 비교하는 데 사용된다.

 

이렇게 하면, 라이브러리 사용자는 자신의 데이터 타입에 맞는 비교 로직을 작성하여 이를 sort 함수에 전달할 수 있다.

라이브러리 제작자는 이 비교 함수의 프로토타입(즉, 함수의 반환 타입과 매개변수 타입)만 정의해두면 되고,
사용자는 자신의 데이터 타입에 맞는 비교 함수를 정의하고, 이 함수를 sort 함수에 콜백으로 전달하면 된다.

 

결과적으로, 단 하나의 범용 sort 함수를 제공함으로써 다양한 데이터 타입과 상황에 적합하게 쓰일 수 있는 매우 유연한 정렬 라이브러리를 만들 수 있다.

sort 함수 예시

아래는 주어진 배열에 대해 선택정렬을 실행하는 함수의 예시이다.

void sort(void* arr, int len, int typesize, bool (*cmp)(void* a, void *b)){
    void* tmp = (void*)malloc(typesize);
    for (int i = 0; i < len - 1; i++){
        for ( int j = i; j < len; j++){
            if ( cmp ( (char*)arr + i*typesize, (char*)arr + j*typesize)){
                memcpy(tmp, (char*)arr + i*typesize, typesize);
                memcpy((char*)arr + i*typesize, (char*)arr+j*typesize, typesize);
                memcpy((char*)arr + j*typesize, tmp, typesize);
            }
        }
    }
    free(tmp);
}

비교를 하는 콜백함수로 cmp 를 마련했고, cmp 는 아래와 같이 정의 가능하다.

주목해야 할 부분은, 어떤 데이터 타입이 올지 모르기때문에 제네릭 포인터인 void 포인터를 비교함수의 인자로 주어줘야 한다는 것이다.

 

각 비교함수에서는 void 타입 포인터를 실제 데이터타입에 맞게 캐스팅을 해주어야 한다.

지난 게시글에서 문자열배열은 이중포인터라고 다뤘다. 그에 맞게 char** 타입으로 캐스팅을 해줘야 한다는 것을 꼭 이해하고 넘어가야 한다.

bool compare_string(void* a, void*b){
    char* str1 = *(char**)a;
    char* str2 = *(char**)b;
    return strcmp(str1,str2) > 0;
}

bool compare_int(void* a, void* b){
    if (*(int*)a > *(int*)b) return 1;
    else return 0;
}

 

 

C++ - Template

C++ 에서는 다양한 데이터타입에 대한 코드 중복을 줄이기 위해, template 이란 키워드를 제공한다.

template<typename T>
void sort(T* arr, size_t len) { 
    T tmp;
    for (size_t i = 0; i < len; i++) {
        for (size_t j = i + 1; j < len; j++) {
            if (arr[i] > arr[j]){
                tmp = arr[i];
                arr[i] = arr[j];
                arr[j] = tmp;
            }
        }
    }
}

코드도 훨씬 간결하고, 가독성 있게 작성할 수 있다.

 

이게 가능한 이유는 컴파일러가 컴파일타임에 여러 데이터 타입에 해당하는 sort 함수를 자동으로 만들어 주기 때문이다.

 

출력하는 함수도 마찬가지이다. 템플릿을 사용하면 C와는 다르게 단일 함수로 어떠한 배열이 오더라도 모두 처리할 수 있다.

댓글