함수 포인터 (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와 가상 함수처럼 루틴을 구현할 수 있다.
댓글
댓글 쓰기