함수 포인터 (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)를 통해 윈도우 화면 미러링 방법

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

안드로이드(android) 전체 화면 시계 앱(clock app) 예제 코드

mkfs.fat Device or resource busy 에러 해결법