Callback qua function pointer trong C

C không có class hay virtual method để dispatch callback theo kiểu OOP, nhưng có function pointer — cùng cơ chế ở tầng thấp hơn. Một callback trong C là một con trỏ tới hàm được lưu trong struct hoặc truyền làm tham số; caller invoke qua con trỏ đó như gọi hàm thường. Pattern này là cách C đạt được polymorphism động: cùng một parser/event loop/sort routine có thể chạy với behavior khác nhau tuỳ con trỏ được đăng ký.

Cấu trúc cơ bản

Khai báo trong header: kiểu con trỏ hàm là một phần của struct. Mỗi field con trỏ định nghĩa một “slot” mà người dùng có thể plug vào hành vi tuỳ chỉnh.

struct parser_t {
    char token[4096];
    enum STATE state;

    void (*on_method)(char*);
    void (*on_url)(char*);
    void (*on_header)(char*, char*);
    void (*on_headers_completed)(void);
    void (*on_body)(char*);
};

Người dùng đăng ký callback bằng cách gán con trỏ:

void my_on_method(char *m) { printf("Method: %s\n", m); }

struct parser_t parser;
parser.on_method = &my_on_method;

Parser invoke callback như gọi hàm bình thường, không cần (*parser.on_method)(token) vì C tự dereference con trỏ hàm khi đứng trước ():

parser->on_method(parser->token);

Ngữ nghĩa: late binding ở tầng C

Tại compile time parser không biết hàm cụ thể nào sẽ chạy — nó chỉ biết signature. Lúc runtime con trỏ được lookup từ struct và invoke. Đây là tương đương low-level của virtual table trong C++: cả hai đều là indirect call qua một con trỏ được set ở runtime, nhưng C++ tự sinh table, còn C bắt người dùng tự khai báo từng slot.

Một lợi điểm của pattern này so với hardcode: parser được tách rời khỏi consumer. Cùng một pparser_parse có thể được dùng để in ra stdout (như main.c), để build scope cho ASGI server, hay để build object cho ORM — chỉ cần đổi callback đăng ký.

Ứng dụng tiêu biểu

Function pointer callback là một trong những pattern phổ biến nhất trong C library:

Đánh đổi cần biết

Pattern này gọn nhưng có vài điểm phải tự quản:

Trải nghiệm cá nhân

Pattern này được dùng cụ thể trong http-parser — bản C song hành với HTTP parser Python trong gist; cả hai cùng một state machine, khác nhau ở chỗ Python dùng object có method còn C dùng struct chứa function pointer. Mục đích: thực hành cùng pattern trên hai ngôn ngữ để thấy rõ “kiểu OOP” thực ra là một dạng dressed-up của function pointer indirect call.

Cập nhật: 2026-05-29