2008-09-27 13 views
118

मैंने जो पढ़ा है, उससे कोर ऑडियो कॉलबैक पर भारी निर्भर करता है (और सी ++, लेकिन यह एक और कहानी है)।सी में "कॉलबैक" क्या है और वे कैसे कार्यान्वित किए जाते हैं?

मैं एक कार्य को स्थापित करने के लिए बार-बार किसी अन्य फ़ंक्शन द्वारा बुलाए जाने वाले फ़ंक्शन को स्थापित करने की अवधारणा (प्रकार) को समझता हूं। मैं समझ में नहीं आता कि वे कैसे स्थापित होते हैं और वे वास्तव में कैसे काम करते हैं। किसी भी उदाहरण की सराहना की जाएगी।

उत्तर

166

सी में कोई "कॉलबैक" नहीं है - किसी भी अन्य सामान्य प्रोग्रामिंग अवधारणा से अधिक नहीं।

वे फ़ंक्शन पॉइंटर्स का उपयोग करके कार्यान्वित किए गए हैं। यहाँ एक उदाहरण है:

void populate_array(int *array, size_t arraySize, int (*getNextValue)(void)) 
{ 
    for (size_t i=0; i<arraySize; i++) 
     array[i] = getNextValue(); 
} 

int getNextRandomValue(void) 
{ 
    return rand(); 
} 

int main(void) 
{ 
    int myarray[10]; 
    populate_array(myarray, 10, getNextRandomValue); 
    ... 
} 

यहाँ, populate_array समारोह अपने तीसरे पैरामीटर के रूप में एक समारोह सूचक लेता है, और साथ सरणी को भरने के लिए मूल्यों को प्राप्त करने के लिए यह कहता है। हमने कॉलबैक getNextRandomValue लिखा है, जो एक यादृच्छिक-आईएसएच मान देता है, और इसे populate_array पर पॉइंटर पास कर देता है। populate_array हमारे कॉलबैक फ़ंक्शन को 10 बार कॉल करेगा और दिए गए सरणी में तत्वों को लौटाए गए मान असाइन करेगा।

+1

मैं गलत यहाँ हो सकता है लेकिन ऐसा नहीं है कि समारोह सूचक कॉल populate_array में लाइन किया जाना चाहिए: सरणी [i] = (* getNextValue)(); ? –

+31

ड्रेफरेंस ऑपरेटर फ़ंक्शन पॉइंटर्स के साथ वैकल्पिक है, जैसा ऑपरेटर पता है। myfunc (...) = (* myfunc) (...) और myfunc = myfunc – aib

+1

@NathanFellman मैंने अभी पढ़ा [विशेषज्ञ सी प्रोग्रामिंग] (http://www.amazon.co.uk/Expert- प्रोग्रामिंग- पीटर- वैन-लिंडेन/डीपी/0131774298) और यह फ़ंक्शन पॉइंटर को अच्छी तरह से कॉल करता है। –

4

सी में कॉलबैक आमतौर पर फ़ंक्शन पॉइंटर्स और संबंधित डेटा पॉइंटर का उपयोग करके कार्यान्वित किए जाते हैं। आप अपने फ़ंक्शन on_event() और डेटा पॉइंटर्स को फ्रेमवर्क फ़ंक्शन watch_events() (उदाहरण के लिए) पास करते हैं। जब कोई ईवेंट होता है, तो आपके फ़ंक्शन को आपके डेटा और कुछ ईवेंट-विशिष्ट डेटा के साथ बुलाया जाता है।

कॉलबैक का भी जीयूआई प्रोग्रामिंग में उपयोग किया जाता है। GTK+ tutorial पर theory of signals and callbacks पर एक अच्छा अनुभाग है।

0

आमतौर पर यह फ़ंक्शन पॉइंटर का उपयोग करके किया जा सकता है, यह एक विशेष चर है जो फ़ंक्शन के स्मृति स्थान को इंगित करता है। फिर आप विशिष्ट तर्कों के साथ फ़ंक्शन को कॉल करने के लिए इसका उपयोग कर सकते हैं। तो शायद एक ऐसा फ़ंक्शन होगा जो कॉलबैक फ़ंक्शन सेट करता है। यह एक फ़ंक्शन पॉइंटर स्वीकार करेगा और उसके बाद उस पते को कहीं भी स्टोर करेगा जहां इसका उपयोग किया जा सकता है। उसके बाद जब निर्दिष्ट ईवेंट ट्रिगर होता है, तो वह उस फ़ंक्शन को कॉल करेगा।

3

यह wikipedia article सी

में एक उदाहरण है एक अच्छा उदाहरण है कि नई उन्हें समारोह संकेत पारित करके मुख्य अपाचे प्रक्रिया के साथ अपाचे वेब सर्वर रजिस्टर बढ़ाने के लिए लिखा मॉड्यूल तो उन कार्यों वापस प्रक्रिया वेब के लिए कहा जाता है पेज अनुरोध

106

यहाँ सी

में

मान लीजिए कि आप कुछ कोड कॉलबैक दर्ज की अनुमति देता है कि जब कुछ घटना होती है के नाम से जाना लिखना चाहते हैं कॉलबैक का एक उदाहरण है।

पहले कॉलबैक के लिए इस्तेमाल किया समारोह के प्रकार को परिभाषित:

typedef void (*event_cb_t)(const struct event *evt, void *userdata); 

अब, एक समारोह है कि एक कॉलबैक रजिस्टर करने के लिए प्रयोग किया जाता है परिभाषित:

int event_cb_register(event_cb_t cb, void *userdata); 

इस कोड है कि कैसा दिखेगा क्या है एक कॉलबैक पंजीकृत करता है:

static void my_event_cb(const struct event *evt, void *data) 
{ 
    /* do stuff and things with the event */ 
} 

... 
    event_cb_register(my_event_cb, &my_custom_data); 
... 

ईवेंट प्रेषक के आंतरिक भाग में, कॉलबैक k को ऐसी संरचना में संग्रहीत किया जा सकता है जो इस तरह कुछ दिखता है:

struct event_cb { 
    event_cb_t cb; 
    void *data; 
}; 

यह कोड कॉलबैक निष्पादित करने जैसा दिखता है।

struct event_cb *callback; 

... 

/* Get the event_cb that you want to execute */ 

callback->cb(event, callback->data); 
+2

+ 1 बस मुझे जो चाहिए था। यदि उपयोगकर्ता उपयोगकर्ता कॉलबैक फ़ंक्शन में आवश्यक कस्टम डेटा (जैसे डिवाइस हैंडल) पास करना चाहते हैं तो उपयोगकर्ता डेटा भाग बहुत उपयोगी होता है। –

+0

के लिए – uceumern

+0

सत्यापन प्रश्न: कॉलबैक टाइपपेफ एक तारांकन के साथ है क्योंकि यह फ़ंक्शन पते के लिए सूचक है? अगर तारांकन गायब है, तो क्या यह गलत होगा? अगर वह फिर वहाँ GitHub पर सिस्को की libsrtp पुस्तकालय में दो लापता सितारों रहे हैं वह सही नहीं है: https://github.com/cisco/libsrtp/blob/93aba3e767502343c255fad8962dbf0ff29c4ca6/include/srtp.h#L1640 https://github.com/cisco/libsrtp/blob/93aba3e767502343c255fad8962dbf0ff29c4ca6/include/srtp.h#L1709 – twildeman

16

एक साधारण कॉल बैक प्रोग्राम। उम्मीद है कि यह आपके प्रश्न का उत्तर देगा।

#include <stdio.h> 
#include <stdlib.h> 
#include <unistd.h> 
#include <fcntl.h> 
#include <string.h> 
#include "../../common_typedef.h" 

typedef void (*call_back) (S32, S32); 

void test_call_back(S32 a, S32 b) 
{ 
    printf("In call back function, a:%d \t b:%d \n", a, b); 
} 

void call_callback_func(call_back back) 
{ 
    S32 a = 5; 
    S32 b = 7; 

    back(a, b); 
} 

S32 main(S32 argc, S8 *argv[]) 
{ 
    S32 ret = SUCCESS; 

    call_back back; 

    back = test_call_back; 

    call_callback_func(back); 

    return ret; 
} 
5

सी में एक कॉलबैक फ़ंक्शन एक समारोह पैरामीटर/एक और समारोह के भीतर किया जाना करने के लिए आवंटित चर के बराबर है। Wiki Example

नीचे कोड में,

#include <stdio.h> 
#include <stdlib.h> 

/* The calling function takes a single callback as a parameter. */ 
void PrintTwoNumbers(int (*numberSource)(void)) { 
    printf("%d and %d\n", numberSource(), numberSource()); 
} 

/* A possible callback */ 
int overNineThousand(void) { 
    return (rand() % 1000) + 9001; 
} 

/* Another possible callback. */ 
int meaningOfLife(void) { 
    return 42; 
} 

/* Here we call PrintTwoNumbers() with three different callbacks. */ 
int main(void) { 
    PrintTwoNumbers(&rand); 
    PrintTwoNumbers(&overNineThousand); 
    PrintTwoNumbers(&meaningOfLife); 
    return 0; 
} 

समारोह (* numberSource) समारोह कॉल PrintTwoNumbers अंदर एक समारोह "वापस बुलाने के" करने के लिए अंदर PrintTwoNumbers से/पर अमल के रूप में के रूप में यह चलाता कोड से निर्धारित है।

तो यदि आपके पास कोई पर्थ्रेड फ़ंक्शन की तरह कुछ था तो आप लूप के अंदर अपने तत्कालता से चलाने के लिए एक और फ़ंक्शन असाइन कर सकते हैं।

0

सी में एक कॉलबैक एक ऐसा फ़ंक्शन है जो किसी अन्य फ़ंक्शन को "वापस कॉल" करने के लिए प्रदान किया जाता है जब कोई अन्य कार्य अपना कार्य कर रहा होता है।

two ways that a callback is used हैं: सिंक्रोनस कॉलबैक और एसिंक्रोनस कॉलबैक। एक सिंक्रोनस कॉलबैक किसी अन्य फ़ंक्शन पर प्रदान किया जाता है जो कुछ कार्य करने जा रहा है और फिर पूरा किए गए कार्य के साथ कॉलर पर वापस आ जाता है। एक एसिंक्रोनस कॉलबैक किसी अन्य फ़ंक्शन को प्रदान किया जाता है जो एक कार्य शुरू करने जा रहा है और फिर संभवतः पूरा नहीं होने वाले कार्य के साथ कॉलर पर वापस आ जाता है।

एक सिंक्रोनस कॉलबैक आमतौर पर किसी अन्य फ़ंक्शन को एक प्रतिनिधि प्रदान करने के लिए उपयोग किया जाता है जिस पर अन्य फ़ंक्शन कार्य के कुछ चरण को प्रतिनिधि करता है। इस प्रतिनिधिमंडल के क्लासिक उदाहरण मानक सी लाइब्रेरी से bsearch() और qsort() फ़ंक्शंस हैं। इन दोनों कार्यों में कॉलबैक होता है जिसका उपयोग कार्य के दौरान किया जाता है ताकि bsearch() के मामले में डेटा का प्रकार खोजा जा सके, या सॉर्ट किया गया हो, 'qsort() के मामले में, होने की आवश्यकता नहीं है इस्तेमाल किए जा रहे समारोह द्वारा जाना जाता है।

उदाहरण के लिए यहां विभिन्न तुलनात्मक कार्यों, सिंक्रोनस कॉलबैक का उपयोग करके bsearch() के साथ एक छोटा नमूना कार्यक्रम है। कॉलबैक फ़ंक्शन में डेटा तुलना करने के लिए हमें अनुमति दें, bsearch() फ़ंक्शन हमें रन टाइम पर निर्णय लेने की अनुमति देता है कि हम किस प्रकार की तुलना का उपयोग करना चाहते हैं। यह तुल्यकालिक है क्योंकि जब bsearch() फ़ंक्शन देता है तो कार्य पूरा हो जाता है।

#include <stdio.h> 
#include <stdlib.h> 
#include <string.h> 

typedef struct { 
    int iValue; 
    int kValue; 
    char label[6]; 
} MyData; 

int cmpMyData_iValue (MyData *item1, MyData *item2) 
{ 
    if (item1->iValue < item2->iValue) return -1; 
    if (item1->iValue > item2->iValue) return 1; 
    return 0; 
} 

int cmpMyData_kValue (MyData *item1, MyData *item2) 
{ 
    if (item1->kValue < item2->kValue) return -1; 
    if (item1->kValue > item2->kValue) return 1; 
    return 0; 
} 

int cmpMyData_label (MyData *item1, MyData *item2) 
{ 
    return strcmp (item1->label, item2->label); 
} 

void bsearch_results (MyData *srch, MyData *found) 
{ 
     if (found) { 
      printf ("found - iValue = %d, kValue = %d, label = %s\n", found->iValue, found->kValue, found->label); 
     } else { 
      printf ("item not found, iValue = %d, kValue = %d, label = %s\n", srch->iValue, srch->kValue, srch->label); 
     } 
} 

int main() 
{ 
    MyData dataList[256] = {0}; 

    { 
     int i; 
     for (i = 0; i < 20; i++) { 
      dataList[i].iValue = i + 100; 
      dataList[i].kValue = i + 1000; 
      sprintf (dataList[i].label, "%2.2d", i + 10); 
     } 
    } 

// ... some code then we do a search 
    { 
     MyData srchItem = { 105, 1018, "13"}; 
     MyData *foundItem = bsearch (&srchItem, dataList, 20, sizeof(MyData), cmpMyData_iValue); 

     bsearch_results (&srchItem, foundItem); 

     foundItem = bsearch (&srchItem, dataList, 20, sizeof(MyData), cmpMyData_kValue); 
     bsearch_results (&srchItem, foundItem); 

     foundItem = bsearch (&srchItem, dataList, 20, sizeof(MyData), cmpMyData_label); 
     bsearch_results (&srchItem, foundItem); 
    } 
} 

एक अतुल्यकालिक कॉलबैक कि में अलग है जब कहा जाता है कार्य करने के लिए जो हम एक कॉलबैक रिटर्न प्रदान करते हैं कार्य पूरा नहीं हो सकता है। इस प्रकार का कॉलबैक अक्सर एसिंक्रोनस I/O के साथ प्रयोग किया जाता है जिसमें एक I/O ऑपरेशन प्रारंभ होता है और फिर जब यह पूरा हो जाता है, तो कॉलबैक लागू होता है।

निम्न प्रोग्राम में हम टीसीपी कनेक्शन अनुरोधों को सुनने के लिए एक सॉकेट बनाते हैं और जब कोई अनुरोध प्राप्त होता है, तो सुनवाई करने वाला फ़ंक्शन प्रदान किए गए कॉलबैक फ़ंक्शन को आमंत्रित करता है। telnet उपयोगिता या किसी अन्य विंडो में कनेक्ट करने का प्रयास करने के लिए एक वेब ब्राउज़र का उपयोग करते समय इसे एक विंडो में चलाकर इस सरल एप्लिकेशन का उपयोग किया जा सकता है।

मैं https://msdn.microsoft.com/en-us/library/windows/desktop/ms737526(v=vs.85).aspx

पर उदाहरण माइक्रोसॉफ्ट accept() समारोह के साथ प्रदान करता है से WinSock कोड के सबसे उठा लिया यह आवेदन स्थानीय होस्ट, 127.0.0.1 पर एक listen() शुरू होता है, पोर्ट 8282 का उपयोग कर तो आप इस्तेमाल कर सकते हैं या तो telnet 127.0.0.1 8282 या http://127.0.0.1:8282/

यह नमूना एप्लिकेशन विजुअल स्टूडियो 2017 सामुदायिक संस्करण के साथ एक कंसोल एप्लिकेशन के रूप में बनाया गया था और यह सॉकेट के माइक्रोसॉफ्ट विनसॉक संस्करण का उपयोग कर रहा है। लिनक्स एप्लिकेशन के लिए WinSock फ़ंक्शंस को लिनक्स विकल्पों के साथ प्रतिस्थापित करने की आवश्यकता होगी और विंडोज थ्रेड लाइब्रेरी इसके बजाय pthreads का उपयोग करेगी।

#include <stdio.h> 
#include <winsock2.h> 
#include <stdlib.h> 
#include <string.h> 

#include <Windows.h> 

// Need to link with Ws2_32.lib 
#pragma comment(lib, "Ws2_32.lib") 

// function for the thread we are going to start up with _beginthreadex(). 
// this function/thread will create a listen server waiting for a TCP 
// connection request to come into the designated port. 
// _stdcall modifier required by _beginthreadex(). 
int _stdcall ioThread(void (*pOutput)()) 
{ 
    //---------------------- 
    // Initialize Winsock. 
    WSADATA wsaData; 
    int iResult = WSAStartup(MAKEWORD(2, 2), &wsaData); 
    if (iResult != NO_ERROR) { 
     printf("WSAStartup failed with error: %ld\n", iResult); 
     return 1; 
    } 
    //---------------------- 
    // Create a SOCKET for listening for 
    // incoming connection requests. 
    SOCKET ListenSocket; 
    ListenSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); 
    if (ListenSocket == INVALID_SOCKET) { 
     wprintf(L"socket failed with error: %ld\n", WSAGetLastError()); 
     WSACleanup(); 
     return 1; 
    } 
    //---------------------- 
    // The sockaddr_in structure specifies the address family, 
    // IP address, and port for the socket that is being bound. 
    struct sockaddr_in service; 
    service.sin_family = AF_INET; 
    service.sin_addr.s_addr = inet_addr("127.0.0.1"); 
    service.sin_port = htons(8282); 

    if (bind(ListenSocket, (SOCKADDR *)& service, sizeof(service)) == SOCKET_ERROR) { 
     printf("bind failed with error: %ld\n", WSAGetLastError()); 
     closesocket(ListenSocket); 
     WSACleanup(); 
     return 1; 
    } 
    //---------------------- 
    // Listen for incoming connection requests. 
    // on the created socket 
    if (listen(ListenSocket, 1) == SOCKET_ERROR) { 
     printf("listen failed with error: %ld\n", WSAGetLastError()); 
     closesocket(ListenSocket); 
     WSACleanup(); 
     return 1; 
    } 
    //---------------------- 
    // Create a SOCKET for accepting incoming requests. 
    SOCKET AcceptSocket; 
    printf("Waiting for client to connect...\n"); 

    //---------------------- 
    // Accept the connection. 
    AcceptSocket = accept(ListenSocket, NULL, NULL); 
    if (AcceptSocket == INVALID_SOCKET) { 
     printf("accept failed with error: %ld\n", WSAGetLastError()); 
     closesocket(ListenSocket); 
     WSACleanup(); 
     return 1; 
    } 
    else 
     pOutput(); // we have a connection request so do the callback 

    // No longer need server socket 
    closesocket(ListenSocket); 

    WSACleanup(); 
    return 0; 
} 

// our callback which is invoked whenever a connection is made. 
void printOut(void) 
{ 
    printf("connection received.\n"); 
} 

#include <process.h> 

int main() 
{ 
    // start up our listen server and provide a callback 
    _beginthreadex(NULL, 0, ioThread, printOut, 0, NULL); 
    // do other things while waiting for a connection. In this case 
    // just sleep for a while. 
    Sleep(30000); 
} 
संबंधित मुद्दे