ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 함수 포인터.
    프로그래밍/C and C++ 2005. 7. 3. 01:44

      우선, 글을 시작하기 전에, 내가 알고 있는 "지식"이라는 것은, 결과론적으로 얻어진 것으로,
    그 내부적 상황을 자세히 알 수 없는 것임을 밝힌다. 나의 목표 중 하나는, 그 내부적 상황을 자
    세히 아는 것이지만, 우선 그것을 모르더라도 프로그래밍이 가능하긴 하니, 글을 쓰려 한다. 결
    과론적이라는 말은, 직접 코딩을 해보았다는 말이니, 그 결과는 확실히 보장할 수 있다(에러까
    지 >.<"")

      함수 이름 자체가 포인터라는 것은 어렵지 않게(그러나 자세히는 아니게) 알 수 있었다. 그리
    고 예제도 보았다. 어려울 것은 없었다. 그런데, 문제는 과연 이 함수 포인터라는 것을 어디에
    다 써먹을 것인가 하는 것이었다. 내가 알고 있는, 함수 포인터를 사용하는 예는, qsort가 전부
    였다. 그 하나만으로 함수 포인터를 어떻게 유용하게 쓰는지 알아내긴 나에겐 벅찬 일이었다. 하
    지만, 역시 하나의 단어를 안다는 것 자체가, 사고의 폭이 넓어지는 것을 의미하는 것처럼, 함
    수 포인터라는 것을 알고 있으니, 그것을 쓰면 좋을 상황이 오니, 그 생각이 났다.

      우선 많은 예제로 나오는, 단순히 함수의 이름을 함수 포인터에 주고, 그 결과를 얻어가는
    것. 이것은,....글쎄... 단순히 예제를 위한 예제일 뿐(혹?? 내것도 그런 건 아닌지...) 실제적
    인 사용법을 알기엔 너무나 부족했다. 그냥 부르면 되지, 왜 함수 포인터에 넣고, 그 포인터를
    이용해 함수를 호출한다는 말인가??? 내가 찾아낸 함수 포인터의 방법은, 바로 다른 함수의 파라
    미터로 쓰는 것이었다.

      함수 포인터를 다른 함수의 파라미터로 쓸 경우, 이것은 어쩌면 다형성의 개념과 상관이 있는
    것처럼 보인다. 다형성이 무엇인지 정확히 알 수 없으니(명확히 정의되어 있지도 않고), 함수 포
    인터를 쓰는 것이 다형성이 되는 것인지를 모르겠다. 서론이 너무 길었다. >.<""

      우선 코드를 말하기 전에, 이 코드는 완벽한 것이 아님을 밝힌다. 아주 많은 예외 처리와, 특
    수 상황 처리 루틴이 하나도 들어가 있지 않다. 그저 내가 원하는 최소한의 결과를 얻어내기 위
    한 것이다. 나는 글자를 찾는 것을 코딩해 보았다. 글자를 찾는 조건은 참 다양할 수 있다. 아
    니, 사실 어떤 조건으로 사용자가 찾고 싶은 지, 그것은, 사용자의 선택에 달려 있는 것이 아니
    고, 프로그래머가 제공하는 방법에 달려 있는 것 같은 느낌도 든다. 그러므로 프로그래머는 다양
    한 찾기 옵션을 제공해 주면 참 좋을 것 같다. 내가 잠깐 간단하게 생각해 낸 찾기 방법은 4가
    지 이다. 코딩의 명확성을 제공하기 위해, 우선 그 4가지 상황부터 정확히 정의한다.

    1. 완전히 똑같은 한 단어(단 대소문자 무시) : multimedia 와 multi는 다른 단어이다.
    2. 비슷한 단어 : 스펠링 중 한 글자만 틀리다면 그것은 비슷한 단어라고 정의한다.
    3. 설명하기 좀 난해하고(제가 원래 설명을 무지 못하는 편이걸랑요 ^^;;;;), *가 있는 곳에 아
    무거나 올 수 있는 방법 : a*a 라면, aaa, aba, aca, ada...을 의미한다.
    4. 포함하는 단어 : 그 문자열을 포함하는 단어. 가령, multimedia는 multi를 포함하는 단어이
    다. 완전히 같은 단어는 포함하는 단어가 아니다.

    우선, 조건에 맞는 찾는 루틴을 구현할 때, 기본이 되는 함수(함수 포인터를 파라미터로 가질 함
    수)를 보면,

    int    CPolyMorphView::FindWord(char **SrcBuffer, int nBufferLength,
                  CString FindWord, BOOL (*func)(CString, CString))
    {
        if(SrcBuffer == NULL){
            AfxMessageBox(" 검사할 버퍼가 널입니다.");
            return -1;
        }

        if( nBufferLength < 1){
            AfxMessageBox(" 버퍼의 길이가 잘못되었습니다.");
            return -1;
        }

        if( FindWord == ""){
            AfxMessageBox(" 찾을 단어가 없습니다.");
            return -1;
        }


        int number = 0;
        int i = 0;

        int FindWordLength = FindWord.GetLength() + 2; // equal 함수 때문에

        char *word = new char[FindWordLength + 1];
       
        word[FindWordLength] = NULL;   

        for(i = 1; i<nBufferLength - FindWordLength ; i++){
            strncpy(word,&(*SrcBuffer)[i-1],FindWordLength );
            if(func(word,FindWord))
                number++;
        }

        delete word;

     

        return number;
    }

    가 된다. SrcBuffer에 열라 많은 글자가 들어가 있을 테고, nBufferLength는 버퍼 길이이고, 뭐
    그렇다. 여기서, 맨 마지막 인자가 함수 포인터인데, 이 함수 포인터에 지금 찾는 글자와,
    SrcBuffer에서, 지금 검사하고 있는 글자를 넘겨 주고, 함수 포인터에서, 그 글자가 조건에 맞
    는, 같은 글자인지를 판단하게 된다.  그래서, 같은 글자라는 판단이 되면, 같은 글자수를 하나
    올려 주는 루틴이다(아차, 이 프로그램의 결과는 같은 글자의 "수"이다.) 전에도 말했지만, 이
    함수는 그리 완벽하지 않다. 아주아주 간단명료, 단순무식하기 땜에, 간단한 경우만 동작한다.
    그럼 이 함수에서 파라미터로 받는 함수들을 보자.

    BOOL CPolyMorphView::Equal(CString SrcWord, CString FindWord)
    {
       
        if( SrcWord.Left(1) != " " || SrcWord.Right(1) != " ")
            return false;

        if(SrcWord.Mid(1,FindWord.GetLength()).CompareNoCase(FindWord) != 0)
            return false;

        return true;
    }
    BOOL CPolyMorphView::Similar(CString SrcWord, CString FindWord)
    {
        if( SrcWord.Left(1) != " " || SrcWord.Right(1) != " ")
            return false;

        int DifferentNumber = 0;
        int WordLength = FindWord.GetLength();
        int i = 0;

        for(i = 0; i<WordLength; i++){
            if(SrcWord.Mid(i+1,1).CompareNoCase(FindWord.Mid(i,1)) != 0)
                if( ++DifferentNumber > 2) return false;
        }

        return true;

    }
    BOOL CPolyMorphView::Asteric(CString SrcWord, CString FindWord)
    {
        if( SrcWord.Left(1) != " " || SrcWord.Right(1) != " ")
            return false;

        int WordLength = FindWord.GetLength();
        int i = 0;

        for( i = 0; i<WordLength; i++){
            if(FindWord.Mid(i,1) == "*")
                FindWord.SetAt(i,SrcWord[i+1]);
            
        }

        if(SrcWord.Mid(1,WordLength).CompareNoCase(FindWord) != 0)
            return false;

        return true;

    }
    BOOL CPolyMorphView::Include(CString SrcWord, CString FindWord)
    {
        if(SrcWord[0] == " " && SrcWord[SrcWord.GetLength() - 1 ] == " ")
            return false;

        if(SrcWord.Mid(1,FindWord.GetLength()).CompareNoCase(FindWord) != 0)
            return false;

        return true;
    }

    각각은, 아까 정의한 1,2,3,4 순으로 나와 있다. 이 함수들은, 단순히, 인자로 받은 2개의 문자
    가, 각각의 함수가 의미하는 바에 따라 같은/혹은 다른 것인지를 판단하는 루틴만 들어있다. 그
    러면 실제의 사용 예를 보자.

        DWORD len = 0;

        HANDLE hFile = CreateFile("sample.txt",GENERIC_READ, 0, NULL, OPEN_EXISTING,
    FILE_ATTRIBUTE_NORMAL, NULL);
        int nFileSize = (int)GetFileSize(hFile,NULL);
        char* lpBuffer = new char[nFileSize];
        ReadFile(hFile,lpBuffer,nFileSize,&len,NULL);
        CloseHandle(hFile);

        char str[4];

        str[3] = NULL;
        int num = 0;

        for(int i = 0; i<nFileSize - 3; i++){
            strncpy(str,&lpBuffer[i],3);
            if( strcmp(str,"the") == 0){
                num++;
                TRACE1("%s\n",str);
            }

        }
     
        int kdk = FindWord(&lpBuffer,nFileSize, "the", Equal); // 완전한 하나의 단어로
    같은 것. athe와 the는 다른 것으로 판단.
        int kkk = FindWord(&lpBuffer,nFileSize, "the", Similar); // spelling 중 하나만
    틀린 것
        int ask = FindWord(&lpBuffer,nFileSize, "t*e", Asteric); // * 있는 곳만 다른 것
        int kkd = FindWord(&lpBuffer,nFileSize, "the", Include); // 이 문자열을 포함하
    는 것.

    단순하다. 그냥 찾고 싶은 단어와, 그에 맞는 방법을 갖고 있는 함수의 포인터만 넘겨 주면(사
    실 함수의 이름만 파라미터로 넘겨 주면) 된다. 기실, 언뜻 보기엔 함수 포인터의 장점을 모를수
    도 있다. 그렇다. 다른 방법으로 이 코드를 짤 수도 있다. 그럴 경우를 가정해 보자. 저 맨 위
    의, FindWord라는 함수 안에 이 4개의 함수 내용이 들어갈 것이다. 그리고, 옵션을 알려 주기 위
    해, FindWord라는 함수는, 옵션에 관한 파라미터를 하나 더 받을 것이다. 그리고,

            if(func(word,FindWord))

    이 한줄은, 그 옵션에 맞는, 아래의 4개의 함수 내용으로 바뀔 것이다. 뭐, 별로 불편한 것을 모
    르겠다. 코드가 조금 지저분해지는 것이 있긴 하네... 함수의 길이나 모양새에 신경을 쓰지 않는
    다면, 굳이 함수 포인터를 사용하는 이점을, 이 코드에선 아직 볼 수 없다.

      그러나, 만약 이 함수를 어느 클래스의 멤버 함수로 정의해 놓았다고 하자. 이 클래스를 쓰는
    사람은,"?"문자가 들어간 곳에 숫자만 들어가는 방법으로 찾기를 하고 싶다고 하면... 아니면,
    스펠링의 길이가 5개인 단어만 찾고자 한다면?? 참 많은,다양한 찾기 방법이 나올 수 있다. 그
    모든 경우를 클래스를 만드는 사람이 생각한다는 것은, 아무리 기를 써도 할 수 없는 일이다. 어
    떤 엽기적인 방법을 사용할지는, 아무도 알 수 없다. 그렇다. 함수 포인터를 파라미터로 쓰지 않
    는 경우엔, 이렇게 많은 다양한 찾기 방법을 제공하긴 힘들 것이다(전 모르겠네요.... 어디 좋
    은 방법 알고 계신 분은 리플좀....). 하지만 함수 포인터를 사용하면, 어떤 방법이든, 그 방법
    에 맞는 함수 포인터만 작성해서, 그 포인터만 넘겨 주면, 모든 것이 끝이다.

      무릇, 함수 포인터를 간단/명료/별 쓸모 없는 예제와 함께 설명했던 책의 한 부분이 생각난
    다. 목적이 갖다면 함수 포인터를 써라... 그렇다. 목적은 갖은 데, 도대체 그 목적을 달성하기
    위해, 사용하는 방법이 틀릴 때가 문제이다. 찾는다는 목적은, 각각의 경우마다 같을 것인데, 찾
    는 방법이 틀린 것이다.

      쉽게 말해, 어떤 함수를 만들 때, if나 option을 둘 필요가 있는 곳에서, 한번쯤은, 이곳에 함
    수 포인터를 쓰면 어떨까?... 하는 생각을 해보면 좀 더 범용적인 함수를 만들 수 있게 된다.

    참고로, 만약 다른 클래스에서도 이 함수를 쓸 수 있게 하려면, 함수 파라미터를 써주는 곳에
    BOOL (*parm_func)(int, int)
    뭐 이런 식으로 써주고,
    parm_func(a,2); 이런 식으로 호출 할 수 있고,
    파라미터로 넘길 함수를 static으로 정의하면 된다. 만약 그렇지 않고, 같은  클래스에 있는 함
    수만 파라미터로 넘길 것이라면,
    BOOL (CSSSClassView::*parm_func)(int,int)
    이렇게 해주고,
    (this->*parm_func)(a,20); 이런 식으로 호출 할 수 있다.

    도움이 되셨는 지 모르겠네요....
     
     
     

    댓글