살군의 보조기억 장치

Another memory device…

typedef 으로 함수포인터 정의하기

leave a comment »

opencv 코드를 보면서 평소에 잘 사용하지 않는 다양한 문법들을 보게된다. 지금 소개하게될 내용도 그런것 가운데 하나다. typedef 를 사용해서 함수포인터function pointer를 정의하는 것이다. 첨 보면 잘 이해가 되지 않는 부분이다. 우선 이 문제를 보게된 것은 아래 코드를 살펴보면서이다.

void TS::init( const string& modulename )
{
    char* datapath_dir = getenv("OPENCV_TEST_DATA_PATH");

    if( datapath_dir )
    {
        char buf[1024];
        size_t l = strlen(datapath_dir);
        bool haveSlash = l > 0 && (datapath_dir[l-1] == '/' || datapath_dir[l-1] == '\\');
        sprintf( buf, "%s%s%s/", datapath_dir, haveSlash ? "" : "/", modulename.c_str() );
        data_path = string(buf);
    }

    cv::redirectError((cv::ErrorCallback)tsErrorCallback, this);

    if( ::testing::GTEST_FLAG(catch_exceptions) )
    {
        // ...

뭔가 오묘한 느낌이 드는 코드이다. callback 함수를 cv::redirectError() 에 넘기는데 cv::ErrorCallback 타입으로 변환해서 넘긴다. 그럼 cv::ErrorCallback 타입이 뭘까를 봐야된다. 당연히 찾아봤다. core.hpp 에 정의가 되어있다. 코드를 살펴보면,

typedef int (CV_CDECL *ErrorCallback)( int status, const char* func_name,
                                       const char* err_msg, const char* file_name,
                                       int line, void* userdata );

위와 같이 정의되어 있다. 자 이제부터 이번 글의 내용인 typedef 에 대한 문제가 나온다. typedef 는 뭔지 다 알테고 int 를 뭔가로 대체하는데 이 대체하기로 한 놈이 무지막지하게 이상한 형태로 보인다. 뒷쪽은 분명 함수의 형태인데 앞에는 typedef 이다. 결론부터 이야기하면 이런 형태가 typedef 로 만든 함수포인터이다. int 값을 리턴하는 ErrorCallback 함수포인터를 정의한 것이다.

우선 함수포인터가 무엇인지를 살펴보자. 함수포인터는 말 그대로 함수에 대한 포인터이다. 사용법은,

void (*func_pointer)(int, char);        // 선언

                         // foo 라는 함수가 있을 때,
func_pointer = foo;      // 초기화
func_pointer = &foo;     // 초기화 (둘다 가능하다)

func_pointer(arg1, arg2);               // 함수포인터 호출
(*func_pointer)(arg1, arg2);            // 함수포인터 호출 (둘다 가능)

위의 코드와 같다. 함수포인터라는 놈이 함수를 지정하는 포인터이고 이를 선언, 초기화, 호출하는 방법은 위와 같이 한다. 초기화는 함수이름을 그대로 사용하거나 혹은 함수이름 앞에 & 를 넣어서 할당할 수도 있다. 호출의 경우 일반 함수처럼 이름다음에 바로 인자를 넘길 수도 있고 포인터처럼 사용할 수도 있다. 둘다 정상적인 사용법이다.

이제 typedef 에서 함수포인터를 어떻게 사용하는지 살펴볼 차례다. stackoverflow 에서 잘 설명해주신 분이 있어서 그대로 발췌해왔다.

Is a function pointer created to store the memory address of a function? Yes, a function pointer stores the address of a function. This has nothing to do with the typedef construct which only ease the writing/reading of a program ; the compiler just expands the typedef definition before compiling the actual code.

typedef int (*t_somefunc)(int,int);
int square(int u, int v) { return u*v; }

t_somefunc afunc = □
// ...
int x2 = (*afunc)(123, 456); // call square() to calculate 123*456

위의 line:1 에서 의미하는 것은 두가지이다. 하나는 typedef 로 함수포인터를 선언하는 것이고, 다른 하나는 함수포인터가 typedef 에 의해 새로운 type 형이된다는 것이다. 즉, int 리턴값을 가지는 함수포인터 자체를 변수의 한 타입으로 만들어버린다. 자! 그럼 이제 다시 처음에 봤던 코드로 돌아가보자. 여기서 필요한 부분을 줄여서 쓰면,

typedef int (*ErrorCallback)(types args);

// ...

CV_EXPORTS ErrorCallback redirectError( ErrorCallback errCallback,
                                        void* userdata=0, void** prevUserdata=0);

// ts.cpp 에 정의된 tsErrorCallback() 함수
static int tsErrorCallback( int status, const char* func_name, const char* err_msg, const char* file_name, int line, TS* ts )
{
    ts->printf(TS::LOG, "OpenCV Error: %s (%s) in %s, file %s, line %d\n", cvErrorStr(status), err_msg, func_name[0] != 0 ? func_name : "unknown function", file_name, line);
    return 0;
}

가 된다. CV_CDECL 은 windows 에서 사용하는 prefix 로 관련 내용은 구글링을 하면 쉽게 무슨내용인지 알 수 있을 것이다. 코드에서 수행되는 내역과 상관없이 컴파일러단에서 뭔가를 처리해달라는 것으로 지금은 무시해도 된다. 자… 위에서 요약한 내요을 보니 typedef 로 선언한 함수포인터라는 것이 딱 드러났다. line:198 은 cv::ErrorCallback을 사용하는 모습을 보여준다. 그리고 실제 opencv 소스상에는 cv::ErrorCallback 에 대한 정의가 없다. 대신 위의 코드에서 보듯이 필요로하는 callback 함수를 만들어서 cv::ErrorCallback 타입으로 형변환 한 다음 사용하는 것을 볼 수 있다.

이제 다시한번 제일 위에 코드를 보면 쉽게 읽을 수 있을 것이다.

참고
Advertisements

Written by gomiski

2014/02/24 at 2:17 am

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: