2009-03-10 44 views
11

Tôi đã bắt đầu xem lại các cuộc gọi lại. Tôi tìm thấy liên kết này trên SO: What is a "callback" in C and how are they implemented? Nó có một ví dụ tốt về gọi lại rất giống với những gì chúng tôi sử dụng tại nơi làm việc. Tuy nhiên, tôi đã cố gắng làm cho nó hoạt động, nhưng tôi có nhiều lỗi.Con trỏ hàm và gọi lại trong C

#include <stdio.h> 

/* Is the actual function pointer? */ 
typedef void (*event_cb_t)(const struct event *evt, void *user_data); 

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

int event_cb_register(event_ct_t cb, void *user_data); 

static void my_event_cb(const struct event *evt, void *data) 
{ 
    /* do some stuff */ 
} 

int main(void) 
{ 
    event_cb_register(my_event_cb, &my_custom_data); 

    struct event_cb *callback; 

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

    return 0; 
} 

Tôi biết rằng hàm callback sử dụng con trỏ hàm để lưu địa chỉ của hàm. Nhưng có một vài điều mà tôi thấy tôi không hiểu:

  • Điều gì có nghĩa là "đăng ký gọi lại" và "người điều phối sự kiện"?
+0

Tại sao không bao gồm lỗi của bạn? –

Trả lời

12

Mã này biên dịch và chạy dưới GCC với -Wall.

#include <stdio.h> 

struct event_cb; 

typedef void (*event_cb_t)(const struct event_cb *evt, void *user_data); 

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

static struct event_cb saved = { 0, 0 }; 

void event_cb_register(event_cb_t cb, void *user_data) 
{ 
    saved.cb = cb; 
    saved.data = user_data; 
} 

static void my_event_cb(const struct event_cb *evt, void *data) 
{ 
    printf("in %s\n", __func__); 
    printf("data1: %s\n", (const char *)data); 
    printf("data2: %s\n", (const char *)evt->data); 
} 

int main(void) 
{ 
    char my_custom_data[40] = "Hello!"; 
    event_cb_register(my_event_cb, my_custom_data); 

    saved.cb(&saved, saved.data); 

    return 0; 
} 

Có thể bạn cần xem lại chức năng gọi lại có toàn bộ cấu trúc event_cb hay không - thông thường, bạn chỉ chuyển dữ liệu bởi vì, như được minh họa, nếu không bạn có hai nguồn giống nhau (và một bản sao dự phòng của con trỏ đến hàm bạn đang ở). Có rất nhiều dọn dẹp có thể được thực hiện về điều này - nhưng nó hoạt động.


Câu hỏi trong nhận xét hỏi: Đây có phải là ví dụ hay về gọi lại không?

Ngắn gọn, không - nhưng một phần vì không có đủ cơ sở hạ tầng ở đây.

Trong ý nghĩa, bạn có thể nghĩ rằng hàm so sánh được chuyển đến các hàm qsort() hoặc bsearch() làm gọi lại. Nó là một con trỏ trỏ tới một hàm được chuyển vào hàm generic mà những gì mà hàm generic không thể làm cho chính nó.

Ví dụ khác về gọi lại là chức năng xử lý tín hiệu. Bạn yêu cầu hệ thống gọi hàm của bạn khi sự kiện - một tín hiệu - xảy ra. Bạn thiết lập các cơ chế trước thời hạn để khi hệ thống cần gọi một hàm, nó biết hàm nào cần gọi.

Mã mẫu đang cố gắng cung cấp cơ chế phức tạp hơn - gọi lại với ngữ cảnh. Trong C++, điều này có lẽ sẽ là một hàm functor.

Một số mã tôi làm việc có yêu cầu rất cầu kỳ về quản lý bộ nhớ - khi được sử dụng trong một ngữ cảnh cụ thể. Vì vậy, để thử nghiệm, tôi sử dụng malloc() et al, nhưng trong sản xuất, tôi phải thiết lập bộ cấp phát bộ nhớ cho các bộ phân bổ chuyên dụng. Sau đó, tôi cung cấp một cuộc gọi hàm trong gói để mã cầu thủ có thể ghi đè lên các bộ cấp phát bộ nhớ mặc định bằng các phiên bản thay thế của riêng nó - và cung cấp cho người thay thế hoạt động OK, mã sẽ hoạt động như trước. Đó là một dạng gọi lại - một lần nữa, một biểu mẫu không cần nhiều (hoặc bất kỳ thứ gì) trong cách dữ liệu ngữ cảnh của người dùng.

Hệ thống cửa sổ có trình xử lý sự kiện (gọi lại) được đăng ký và vòng lặp sự kiện chính GUI sẽ gọi khi sự kiện xảy ra. Những người này thường cần bối cảnh người dùng cũng như thông tin sự kiện cụ thể được cung cấp bởi hệ thống GUI.

+0

Và mã bị treo khủng khiếp nếu bạn không đăng ký hàm. –

+0

Xin chào, Cảm ơn sự nỗ lực của bạn. Tôi chỉ đi vào con trỏ hàm, vì tôi sẽ phải sử dụng chúng ở nơi làm việc lần đầu tiên. Đây có phải là một ví dụ hay về một cuộc gọi lại. Ví dụ tôi đưa cho bạn dường như gần với những gì chúng tôi sử dụng. Nếu bạn biết bất cứ điều gì tốt hơn? – ant2009

+0

Không rõ ràng. Ai được "cứu"? nó được định nghĩa ở đâu trong mã của bạn? – Curnelious

2

Nếu không có đầu ra trình biên dịch thì khó, nhưng tôi có thể thấy một số vấn đề;

int event_cb_register(event_ct_t cb, void *user_data); 

Nên

int event_cb_register(event_cb_t cb, void *user_data); 

Biến my_custom_data không tồn tại khi nó được sử dụng ở đây;

event_cb_register(my_event_cb, &my_custom_data); 

Con trỏ này chưa bao giờ được khởi tạo;

struct event_cb *callback; 

Và in;

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

Bạn không thể chuyển tên của loại ('sự kiện') vào hàm, bạn phải chuyển thể hiện loại đó.

+0

Tham số đầu tiên là event_ct_t nhưng phải là event_cb_t –

1

Đăng ký gọi lại có nghĩa là bạn đang chỉ định chức năng nào sẽ được gọi khi sự kiện quan tâm xảy ra. Về cơ bản bạn đang thiết lập con trỏ hàm khi đăng ký một cuộc gọi lại.

2
int event_cb_register(event_ct_t cb, void *user_data); 

Loại đó là event_ct_t? Bạn có nghĩa là event_cb_t?

struct event_cb *callback; 

Tạo con trỏ chưa được khởi tạo đến cấu trúc event_cb. Lưu ý chủ yếu là điểm này để rác.

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

Bạn đang cố gắng gọi rác. Bạn cần khởi tạo:

struct event_cb callback; 
callback.cb = my_event_cb; 
callback.data = 42; 

hoặc một số nội dung như vậy.

6

Điều gì đáp ứng bằng cách "đăng ký gọi lại" và "người điều phối sự kiện"?

"đăng ký gọi lại" là hành động báo cho hệ thống không có chức năng gọi chính xác, và (tùy chọn) với tham số nào và có thể được gọi ra lớp cụ thể nào.

"Bộ điều phối sự kiện" nhận sự kiện từ O/S (hoặc GUI, v.v.), và thực sự gọi các cuộc gọi lại, bằng cách tìm trong danh sách gọi lại để xem những gì quan tâm đến sự kiện đó.

+1

hello, saved.cb (& saved, saved.data); Liệu đây có phải là người phụ trách sự kiện gọi ra calback không? Vì vậy, việc đăng ký gọi lại sẽ được gán địa chỉ của hàm gọi lại cho con trỏ hàm, mà sẽ gọi hàm gọi lại khi con trỏ hàm được gọi? – ant2009

+0

vâng, đó là điều phối viên, mặc dù điều tầm thường nhất có thể. – Alnitak

1

Bạn tạo ra một con trỏ của struct bạn khai báo, nhưng nó không trỏ đến bất cứ điều gì:

struct event_cb *callback; 

Bạn chỉ nên tạo một loại struct của bạn:

struct event_cb callback; 

và sau đó vượt qua nó địa chỉ cho các chức năng.

Các vấn đề liên quan