[질문부탁] 콜백함수

naisr00t의 이미지

부탁드리겠습니다.
여기 많은 분들이 콜백함수에 대해 많은 질문을 올렸고, 답변을 달아 주셔서
거의 모든 글을 읽었습니다. 그런데도 불구하고 이해가 확 오지가 않네요.

1. 콜백함수의 정의 무엇인가요? 개념이 무척이나 혼란합니다.

2. 콜백함수의 간단한 예제가 없을까요?

3. 저는 개념을 생각하면서 코딩을 다음과 같이 해 보았습니다. 맞나요?

#include <stdio.h>

void test_func(void (*func)(int aa), int a)
{
    printf("I am Test Function\n");

    //callback function call
    func(a);
}

void call_back_func(int a)
{
    printf("I am CallBack Function -- #%d\n", a);
}

int main(void)
{
    test_func(call_back_func, 1);
}


맞다면 왜 저렇게 하지요? 아싸리, test_func에서 call_back_func 함수를 직접 호출해도 같은 것이 아닌가요? 왜 함수포인터로 넘기는지를 모르겠네요. -_-

4. C에서 콜백함수와 C++ 버추얼함수 비교 좀 해주시면 고맙겠습니다. 아울러 간략한 샘플 코드도 보여주시면 더 고맙겠습니다. 어떤 분이 비슷하다고 하는데, 이해가 잘 안가네요.

doldori의 이미지

naisr00t wrote:
1. 콜백함수의 정의 무엇인가요? 개념이 무척이나 혼란합니다.

콜백은 함수를 직접 호출하지 않고 다른 함수가 대신 호출하게 하는 것입니다.

naisr00t wrote:
2. 콜백함수의 간단한 예제가 없을까요?

흔히 드는 예가 표준 라이브러리의 qsort입니다. 이 함수는 정렬할 데이터의
형에 제한이 없도록 설계된 함수입니다. 내장형 뿐만 아니라 구조체 같은
사용자 정의형에도 사용이 될 수 있으려면 "어떤 기준으로 정렬할 것인가"가
문제가 됩니다. 이 문제를 콜백을 이용하여 구현합니다.

    typedef struct 
    {
        int number;
        const char* name;
    } S;

    int cmp_by_number(const void* p1, const void* p2)
    {
        const S* ps1 = p1;
        const S* ps2 = p2;
        return ps1->number - ps->number;
    }

    int cmp_by_name(const void* p1, const void* p2)
    {
        const S* ps1 = p1;
        const S* ps2 = p2;
        return strcmp(ps1->name, ps2->name);
    }

    S array[10];
    qsort(array, 10, sizeof(S), cmp_by_number); // number 순으로 정렬
    qsort(array, 10, sizeof(S), cmp_by_name);   // name 순으로 정렬

qsort 입장에서는 주어진 데이터를 어떻게 정렬할지 미리 알 수는 없으므로
qsort를 호출하는 쪽에서 "이런 방법으로 정렬하라"는 정보를 콜백을 통해
알려주는 것입니다.

naisr00t wrote:
3. 저는 개념을 생각하면서 코딩을 다음과 같이 해 보았습니다. 맞나요?
#include <stdio.h>

void test_func(void (*func)(int aa), int a)
{
    printf("I am Test Function\n");

    //callback function call
    func(a);
}

void call_back_func(int a)
{
    printf("I am CallBack Function -- #%d\n", a);
}

int main(void)
{
    test_func(call_back_func, 1);
}


맞다면 왜 저렇게 하지요? 아싸리, test_func에서 call_back_func 함수를 직접 호출해도 같은 것이 아닌가요? 왜 함수포인터로 넘기는지를 모르겠네요. -_-

이것도 콜백의 일종이라고 볼 수 있겠지요. 만약 call_back_func_1,... 등이
여러 개 있고 test_func에서 호출할 함수를 미리 결정할 수 없다면 함수포인터를
이용한 콜백이 좋은 방법입니다. 예를 들어
void call_back_func_1(int a);
void call_back_func_2(int a);

int answer;
switch (answer)
{
case 1:
     test_func(call_back_func_1, answer);
     break;
case 2:
     test_func(call_back_func_2, answer);
     break;
}

naisr00t wrote:
4. C에서 콜백함수와 C++ 버추얼함수 비교 좀 해주시면 고맙겠습니다. 아울러 간략한 샘플 코드도 보여주시면 더 고맙겠습니다. 어떤 분이 비슷하다고 하는데, 이해가 잘 안가네요.

글쎄요... 어떤 이유로 비슷한지는 말씀 안하시던가요? 제가 보기에는 별로 비슷한
점이 없거든요. 제가 직접 해본 것은 아니지만 C++의 다형성을 C에서 구현할 때
콜백을 이용할 수 있을지도 모르겠습니다. 만약 C++에 한정해서 얘기한다면
사용 목적이 전혀 다르다고 하겠습니다. C++에서는 함수 포인터를 확장한
함수 개체(function object)라는 것이 있어서 함수 포인터보다 훨씬 다양한
기능을 제공합니다.
whomilv의 이미지

영어로 callback.은 상대방에게 메시지를 전달, 일을 부탁하고나서 자기일을 계속하면서
상대방으로 부터 결과를 연락받는 다는 것입니다.

즉 자기가 일을 하다가, 그 해당일이 data dependency가 없을 경우, 그일을 다른 프로세스나 thread에 부탁(function을 던져 주는 거죠)
자기일을 계속 하는 거죠. (프로그램에서, OS에서 측면에서 보면 개념이 조금 차이가 있긴하지만).
보통 클래식 OS 측에서 보면, 프로그램이 실행시, 주도권을 프로그램이 갖지만,(자원을 Os 로부터 얻죠)
프로그램에서 busywaiting 할 경우, 그 프로그램은 시스템 자원을 낭비하게 됩니다.
하지만 callback을 통해서 OS에서 처리하고, 프로그램은, 자기을 계속 수행하게 됩니다. (data dependency가 없을시)

개인적으로 패러랠 컴푸팅에서 조금 다르게 생각됩니다.(shared memory system. distributed memory system) ==> many core system.
요새, 이쪽으로 연구를 하고 있는데, 좀 더 조사를 해봐야 할 것습니다.

영어가 되시면, 영문 wiki에서 잘 설명이 되었읍니다. 참조 하시길 바랍니다.

ed.netdiver의 이미지

상황을 만들어보죠.

1. A와 B module이 있다.
2. B는 A의 sub이다.
3. B는 A의 구조를 모른다.(즉 시키는대로만 할뿐이다.)

이때, A가 B의 어떤 function을 call했습니다.
그런데, 그 function의 결과값에 따라 A가 어떤 동작을 선택적으로
해야 합니다.

물론 return으로 처리가 가능할수도 있으나, A가 자기 내부 구조는
알리지 않은채 B가 알아서 결과값도 계산해내고, 또 그결과에 의해
A가 해야 할 처리까지도 맡게(책임을 지게) 하고 싶을 경우,
A는 각 동작방식(method)까지를 B에게 함께 알려줍니다.

그러면, B는 처음 A가 지시한 작업(혹은 function call)을 수행하고
그 결과에 따른 A의 동작까지를 책임지고 처리할수가 있습니다.
그렇게 A가 B한테 알려주는 A의 function을 callback이라고 합니다.
역으로 call한다고 해서 그렇게 붙여진 이름입니다.

뭐 이건 아주 단순화시킨 비유긴 하지만, 개념적으론 이렇게 됩니다.

--------------------------------------------------------------------------------
\(´∇`)ノ \(´∇`)ノ \(´∇`)ノ \(´∇`)ノ
def ed():neTdiVeR in range(thEeArTh)

philnet의 이미지

doldori wrote:
naisr00t wrote:
4. C에서 콜백함수와 C++ 버추얼함수 비교 좀 해주시면 고맙겠습니다. 아울러 간략한 샘플 코드도 보여주시면 더 고맙겠습니다. 어떤 분이 비슷하다고 하는데, 이해가 잘 안가네요.

글쎄요... 어떤 이유로 비슷한지는 말씀 안하시던가요? 제가 보기에는 별로 비슷한
점이 없거든요. 제가 직접 해본 것은 아니지만 C++의 다형성을 C에서 구현할 때
콜백을 이용할 수 있을지도 모르겠습니다. 만약 C++에 한정해서 얘기한다면
사용 목적이 전혀 다르다고 하겠습니다. C++에서는 함수 포인터를 확장한
함수 개체(function object)라는 것이 있어서 함수 포인터보다 훨씬 다양한
기능을 제공합니다.

제 개인적인 생각으로는, C++의 가상 함수란 것이, 특정 실행 시점에서 실행될 함수가 컴파일 시에 정적으로 결정되는 것이 아니라, 실행 시간에 동적으로 결정되게 하는 점에서, 함수 포인터를 이용하는 것과 비슷하다는 말씀인 것 같습니다.

실제로, C에서 C++의 다형성과 비슷한 방식으로 구현하려면,

- 하나 이상의 함수 포인터를 가지는 struct 를 선언해 둔 다음, (다형성이 구현된 class의 포인터를 선언하는 부분에 해당)
- 특정 기능을 수행하는 함수에서는 해당 struct 의 함수 포인터를 실행하게 해 두고, (class 포인터로 가상 함수를 호출하는 부분에 해당)
- 특정 조건에 따라 그에 맞는 해당 struct 의 함수 포인터 값만 바꿔지도록 (class 포인터가 특정 조건에 맞게 base 및 subclass 들의 개체들을 가리키도록 하는 부분에 해당)

구현하는 것이지요.

예를 들자면,

----------------------------------------
class CBase {
  ...
  virtual void vfnc();
  ...
};
----------------------------------------
class CDer1 : public CBase {
  ...
  virtual void vfnc() { cout << "CDer1::vfnc" << endl; };
  ...
};
----------------------------------------
class CDer2 : public CBase {
  ...
  virtual void vfnc() { cout << "CDer2::vfnc" << endl; };
  ...
};
----------------------------------------
class CUsage {
  ...
  void DoSomething() { if ( m_ptr ) m_ptr->vfnc(); };
  void OnCondition1() { 
    if ( m_ptr ) delete m_ptr;
    m_ptr = new CDer1; 
  };
  void OnCondition2() { 
    if ( m_ptr ) delete m_ptr;
    m_ptr = new CDer2; 
  };
  ...
  CBase* m_ptr;
  ...
}
----------------------------------------
void TestFnc() {
  CUsage usage;
  usage.OnCondition1(); usage.DoSomthing(); // CDer1::vfnc 출력
  usage.OnCondition2(); usage.DoSomthing(); // CDer2::vfnc 출력
}

======================
void fnc1() { printf("fnc1\n"); };
void fnc2() { printf("fnc2\n"); };
----------------------------------------
struct Usage {
  ...
  void (*funPtr)();
  ...
};
----------------------------------------
void DoSomething (Usage* a_usage) { 
  if ( a_usage) a_usage->fncPtr();
}
void OnCondition1 (Usage* a_usage) {
  if ( a_usage) a_usage->fncPtr = fnc1; 
}
void OnCondition2 (Usage* a_usage) {
  if ( a_usage) a_usage->fncPtr = fnc2; 
}
----------------------------------------
void TestFunc(Usage* a_usage) {
  OnCondition1(a_usage); DoSomething(a_usage); // fnc1 출력
  OnCondition2(a_usage); DoSomething(a_usage); // fnc2 출력
}

(예제가 너무 단순한 감이 없지는 않지만,) 뭐 이런 식으로 C에서 C++의 다형성과 비슷한 효과를 낼 수 있을 거라고 설명하는 용도라면야 그럭저럭... ^^;

ajoupark의 이미지

프레임웍 기반의 프로그램을 해보시면 이해가 쉬울듯 합니다.

MFC나 미들웨어 기반의 프로그램시에는 각 필요한 해당 function 만을 코딩을 합니다.

이미 프레임웍(MFC, 미들웨어)에서 어떤 function 을 call 할지는 결정이 되어 있고 그것대로 call 을 하면 우리가 코딩한 function 들이 call 이 되어서 하나의 프로그램이 완성되는 겁니다.

대게 보면 이런류의 프로그램은 프로그래머가 main() 혹은 WinMain() 을 코딩을 하지 않게 되지요..

이미 프레임웍에서 main 을 가지고 있는 형태가 있기 때문에요.

/** 주워 들은 것도 내 것이다. 많이 주워 들어야지*/

powersys의 이미지

궂이 특별한기능으로 생각하실필요 없습니다..
함수콜후에..그냥한번더..처리할함수를 알려주는 것입니다.
그냥순차적으로 두번 호출하는 구조로 하셔도됩니다..
(특수한경우 모듈내 처리중 사용자처리함수를 제공할수있게 하는기능으로 저런구조로 할수도 있습니다.)

다만 프레임웍 기반의 프로그램에서 저런류의 인터페이스를 제공하기때문에 하는거죠.

neogeo의 이미지

질문하신 내용중에 함수 포인터를 인자로 받는 이유는 아주 간단합니다.

사용자가 만든 임의의 함수라도 넘겨 받아서 수행 할 수 있어야 하는 케이스가 있기 때문입니다. ( 물론 임의의 함수라고 해도 인자와 리턴 값은 맞아야 겠지요 ) 이미 작성되지 않은 함수를 어떻게 호출 할 수 있을까요? 사용자가 미래에 작성할 임의의 함수를 호출 하려면 함수 포인터가 가장 깔끔한 해결법입니다.

Neogeo - Future is Now.

Neogeo - Future is Now.

Scarecrow의 이미지

C 표준라이브러리에 있는 qsort함수를 한번 사용해 보세요.

mach의 이미지

이 주제는 종종 이슈 되는군요.
가장 최근 답변했던게 어느덧 6년이 되었네요.

call vs. callback

1) call은 "지금! 당장 이것을 해줘! 이에 대한 결과 여부를 알려 줄 때까지...쭉~기다릴테니, 다른일은 절대로 안하고 말야" 라는 의미입니다.

내가 제어의 흐름을 관장하는 것이지요.
보통 제어의 흐름이 예측 가능한 경우에 널리 사용됩니다.

2) callback은 "나중에, 혹시 이런 경우가 발생되면, 나를 불러줘! 나는 지금은 다른 일을 해야 해서 말야...., 참~ 그리고, 날 부르는 방법은,.... 이 함수를 호출하면 돼! 호출해 주면 그에 대한 처리는, 내가 알아서 처리할게~"

(이때, 함수포인터를 주는 이유는, 나중에 상대가 나의 어느 부분(어떤 함수)을 불러줘야 할지를 그 포지션(번지)을 알려주기 위함입니다. 상대는 나의 어느 부분을/어떻게 불러야 할지를 알지 못하기 떄문입니다. 그래서, 함수를 그 둘 사이의 인터페이스로 사용하는 경우에는 함수 포인터를 사용합니다. 그래서, 나중에 상대는 내가 넘겨준 해당 함수를 호출해 주게됩니다. )
제어의 흐름은 보통 상대에 의해 좌우됩니다.
보통 제어의 흐름이 나보다는 상대에 의해 이뤄져야 하는 경우에 널리 사용됩니다.

이렇게 이해해 보는 것도 방법이라고 봅니다.
* 함수 포인터는 callback을 구현하는 방법론 중 하나라고 생각하는게 좋겠습니다.
------------------ P.S. --------------
지식은 오픈해서 검증받아야 산지식이된다고 동네 아저씨가 그러더라.

------------------ P.S. --------------
지식은 오픈해서 검증받아야 산지식이된다고 동네 아저씨가 그러더라.

댓글 달기

Filtered HTML

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

BBCode

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param>
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

Textile

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • You can use Textile markup to format text.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Markdown

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • Quick Tips:
    • Two or more spaces at a line's end = Line break
    • Double returns = Paragraph
    • *Single asterisks* or _single underscores_ = Emphasis
    • **Double** or __double__ = Strong
    • This is [a link](http://the.link.example.com "The optional title text")
    For complete details on the Markdown syntax, see the Markdown documentation and Markdown Extra documentation for tables, footnotes, and more.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Plain text

  • HTML 태그를 사용할 수 없습니다.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 줄과 단락은 자동으로 분리됩니다.
댓글 첨부 파일
이 댓글에 이미지나 파일을 업로드 합니다.
파일 크기는 8 MB보다 작아야 합니다.
허용할 파일 형식: txt pdf doc xls gif jpg jpeg mp3 png rar zip.
CAPTCHA
이것은 자동으로 스팸을 올리는 것을 막기 위해서 제공됩니다.