함수 포인터 (function pointer) 사용 예제 코드

본 글은 함수 포인터(function pointer)사용 법과 어디에 함수 포인터가 사용될 수 있는지 그 예를 싣고 있다.

함수 포인터 (function pointer)


함수 포인터는 해당 함수를 호출할 수 있도록 함수의 주소를 변수에 저장하는 것이다. 아래와 같이 함수를 만들고, plus 변수에 adder 함수의 주소를 할당해 사용할 수 있다.

int adder(int x, int y)
{
return x + y;
}

int main()
{
int result = 0;
int(*plus)(int, int);
plus = adder;
result = plus(1, 2);

return 0;
}
 
또, typedef 문을 사용하여 변수 타입으로도 선언하여 사용할 수 있다. 함수의 파라메터와 리턴 타입이 동일 해야 한다.

typedef int(*add_func)(int x, int y);

add_func plus = adder;
result = plus(2, 3);


함수 포인터 사용 Callback


함수 포인터는 Callback 함수를 등록에 유용하게 사용할 수 있다. 예를 들면 아래와 같이 filter함수 포인터를 callback으로 받아 사용할 수 있다. 이경우에는 passfilter를 호출할 때 필요에 따라 다른 필터를 사용할 수 있어 구현하고자 하는 목적에 맞게 사용할 수 있다. 물론 passfilter를 여러 필터에 맞게 여러 함수로 구현할 수도 있지만, 코드 구현 시 callback을 사용하는 것이 장점이 되는 경우도 있다. 

int passfilter(double *data, int len, double (*filter)(double x))
{
int i = 0;
if (!data || filter) return -1;

while (i < len)
{
data[i] = filter(data[i]);
i++;
}

return 0;
}


double my_filter1(double x)
{
if (x > 10)
return x;

return 0;
}

double my_filter2(double x)
{
if (x < 10)
return x;

return 0;
}


double data[10] = { 1, 2, 3, 4, 5, 11, 12, 13, 14,15 };
passfilter(data, 10, my_filter1);


데이터 정렬에 사용하는 qsort함수도 함수 포인터로 callback을 사용하는 예라고 할 수 있다. 
void qsort (void* base, size_t num, size_t size, int (*compar)(const void*,const void*));


함수 포인터를 사용해 c++ 추상 class 처럼

 

함수 포인터는 c++ class처럼 상황에 따라 달리 구현된 function을 호출해야 할 경우에도 유용하다. 예를 들어 file이나 usb 등에서 데이터를 읽어오는 class를 구현할 때, 추상 class를 두고 각각 file/usb에서 읽어오는 기능을 구현하여 사용하면 편한 경우가 있다. 예와 같은 코드는 다음과 같은 구조로 만들 수 있다. 

class creader
{
public:
creader(){};
~creader(){};
virtual int open(char *path) = 0;
virtual int read(char *data, int len) = 0;
virtual int close() = 0;

};

class cusbreader : public creader
{
public:
cusbreader(){};
~cusbreader(){};

int open(char *path){ return 0; }
int read(char *data, int len){ return 0; }
int close(){ return 0; }

};

class cfilereader : public creader
{
public:
cfilereader(){};
~cfilereader(){};

int open(char *path){ return 0; }
int read(char *data, int len){ return 0; }
int close(){ return 0; }

};


char data[10] = { 0, };
creader *reader = NULL;

if (isfile)
reader = (creader*)new cfilereader;
else
reader = (creader*)new cusbreader;

reader->open(".\file.txt");
reader->read(data, 10);
reader->close();

delete reader;

이제 위와 같은 구조를 c의 함수 포인터를 사용하여 구현하면, 아래와 같이 할 수 있다.

 1. 사용할 함수 포인터를 선언한다.

typedef void* hreader;
typedef int(*reader_open_func)(hreader reader, char *path);
typedef int(*reader_read_func)(hreader reader, char *data, int len);
typedef int(*reader_close_func)(hreader reader);

 
typedef enum
{
type_file,
type_usb,
type_max,
}reader_type;

 2. 데이터를 저장할 구조체를 선언한다. 필요에 따라 구조체 내부에 별도의 변수를 추가해도 된다.

typedef struct _reader_ctx_t
{
reader_open_func open;
reader_read_func read;
reader_close_func close;
}reader_ctx_t;

 3. 각각의 file/usb에서 데이터를 읽어오는 함수를 구현한다. 이때 함수 형식은 위 선언한 함수 포인터와 동일해야 한다.

int usbreader_open(hreader reader, char *path)
{
reader_ctx_t *ctx = (reader_ctx_t *)reader;

// TODO: 코드 구현.

return 0;
}

int usbreader_read(hreader reader, char *data, int len)
{
reader_ctx_t *ctx = (reader_ctx_t *)reader;

// TODO: 코드 구현.

return 0;
}

int usbreader_close(hreader reader)
{
reader_ctx_t *ctx = (reader_ctx_t *)reader;

// TODO: 코드 구현.

return 0;
}

int filereader_open(hreader reader, char *path)
{
reader_ctx_t *ctx = (reader_ctx_t *)reader;

// TODO: 코드 구현.

return 0;
}

int filereader_read(hreader reader, char *data, int len)
{
reader_ctx_t *ctx = (reader_ctx_t *)reader;

// TODO: 코드 구현.

return 0;
}

int filereader_close(hreader reader)
{
reader_ctx_t *ctx = (reader_ctx_t *)reader;

// TODO: 코드 구현.

return 0;
}

 4. reader 함수 포인터들은 선택하는 함수를 구현한다. reader_init함수에서 reader_type을 파라메터로 받아 file reader를 사용할지 usb reader를 사용할지 결정한다. 

typedef enum
{
type_file,
type_usb,
type_max,
}reader_type;


hreader reader_init(reader_type type)
{
reader_ctx_t *ctx = NULL;

if (type >= type_max) return NULL;

ctx = (reader_ctx_t*)malloc(sizeof(reader_ctx_t));
if (ctx)
{
memset(ctx, 0, sizeof(reader_ctx_t));

switch (type)
{
case type_file:
ctx->open = filereader_open;
ctx->read = filereader_read;
ctx->close = filereader_close;
break;
case type_usb:
ctx->open = filereader_open;
ctx->read = filereader_read;
ctx->close = filereader_close;
break;
default:
break;
}
}

return (hreader)ctx;
}

void reader_deinit(hreader reader)
{
reader_ctx_t *ctx = (reader_ctx_t *)reader;
if (ctx)
{
free(ctx);
}
}


int reader_open(hreader reader, char *path)
{
reader_ctx_t *ctx = (reader_ctx_t *)reader;
if (ctx && ctx->open)
return ctx->open(reader, path);

return -1;
}

int reader_read(hreader reader, char *data, int len)
{
reader_ctx_t *ctx = (reader_ctx_t *)reader;
if (ctx && ctx->read)
return ctx->read(reader, data, len);

return -1;
}

int reader_close(hreader reader)
{
reader_ctx_t *ctx = (reader_ctx_t *)reader;
if (ctx && ctx->close)
return ctx->close(reader);

return -1;
}

사용예는 아래와 같다. 

char data[10] = { 0, };
hreader reader = reader_init(type_file);

reader_open(reader, ".\file.txt");
reader_read(reader, data, 10);
reader_close(reader);

reader_deinit(reader);

위와 같이 하면 c에서도 c++ 추상 class와 가상 함수처럼 루틴을 구현할 수 있다.



댓글

이 블로그의 인기 게시물

간단한 cfar 알고리즘에 대해

windows에서 간단하게 크롬캐스트(Chromecast)를 통해 윈도우 화면 미러링 방법

쉽게 설명한 파티클 필터(particle filter) 동작 원리와 예제

base64 인코딩 디코딩 예제 c 소스

간단한 칼만 필터(Kalman Filter) 소스 코드와 사용 예제