2010-03-16 40 views
11

Tôi có một hàm có khối dữ liệu và kích thước của khối và con trỏ hàm làm đối số. Sau đó, nó lặp lại dữ liệu và thực hiện một phép tính trên mỗi phần tử của khối dữ liệu. Sau đây là phác thảo thiết yếu của những gì tôi đang làm:Con trỏ chức năng chung trong C

int myfunction(int* data, int size, int (*functionAsPointer)(int)){ 
    //walking through the data and calculating something 
    for (int n = 0; n < size; n++){ 
     data[n] = (*function)(data[n]); 
    } 
} 

Các chức năng tôi đi qua như các đối số giống như thế này:

int mycalculation(int input){ 
    //doing some math with input 
    //... 
    return input; 
} 

này hoạt động tốt, nhưng bây giờ tôi cần phải vượt qua một biến bổ sung cho hàm con trỏ của tôi. Một cái gì đó dọc theo các dòng

int mynewcalculation(int input, int someVariable){ 
    //e.g. 
    input = input * someVariable; 
    //... 
    return input; 
} 

Có cách nào thanh lịch để đạt được điều này và đồng thời giữ ý tưởng thiết kế tổng thể của tôi không?

+2

Có lý do nào mà bạn không thể thay đổi khai báo hàm để bao gồm tham số nguyên bổ sung không? –

+0

@dbingham: Nếu tôi không lầm, tôi sẽ cần một "chức năng" thứ hai sau đó, sẽ lấy một con trỏ hàm với hai "int". Vấn đề là, rằng sẽ có nhiều chức năng hơn với các loại khác nhau và số lượng đối số. Điều đó sẽ làm rối loạn thiết kế của tôi. – Lucas

+0

Tôi không chắc chắn, tôi hiểu câu hỏi một cách chính xác ngay từ đầu. Tôi nghĩ rằng Jefromi có căn cứ khá tốt được bảo hiểm tại thời điểm này anyway. –

Trả lời

13

Cách thông thường để làm cho nó hoàn toàn chung là với một void *, trong đó tất nhiên gây ra tất cả các loại vấn đề typesafety:

int map_function(int* data, int size, int (*f_ptr)(int, void*), void *userdata){ 
    //walking through the data and calculating something 
    for (int n = 0; n < size; n++){ 
     data[n] = (*f_ptr)(data[n], userdata); 
    } 
} 

int simple_calculation(int input, void *userdata) { 
    // ignore userdata and do some math with input 
    return input; 
} 

int calculation_with_single_extra_arg(int input, void *userdata) { 
    int input2 = * (int*) userdata; 
    // do some math with input and input2 
    return input; 
} 

int calculation_with_many_extra_args(int input, void *userdata) { 
    my_data_t data = (my_data_t *) userdata; 
    return input * (input * data->a + data->b) + data->c; 
} 

int main(int argc, char **argv) { 
    int array[100]; 
    my_data_t userdata = { 1, 2, 3 }; 

    populate(array, 100); 

    map_function(data, calculation_with_many_extra_args, &userdata); 
} 

Bất kỳ chức năng nhất định được thông qua trong phải có nguyên mẫu đó, nhưng bạn có thể đẩy bất kỳ dữ liệu nào bạn muốn vào qua userdata. Có hoàn toàn không có typechecking.

Bạn cũng có thể sử dụng va_args như dbingham gợi ý; điều này không thực sự khác nhiều lắm, vì nguyên mẫu cho chức năng của bạn để lập bản đồ sẽ phải là int(int, va_list).

Chỉnh sửa: Tôi ưu tiên phương pháp void*. Tuy nhiên, va_list không thêm bất kỳ loại an toàn nào và thêm nhiều tiềm năng hơn cho lỗi người dùng (đặc biệt gọi va_end hai lần hoặc không thực hiện vòng lặp va_arg ngay). Khoảng trống * cũng không thêm bất kỳ dòng mã bổ sung nào; nó được thông qua một cách rõ ràng, và người dùng chỉ dereferences nó vào (hy vọng) đúng loại (mà có lẽ là một struct, trong trường hợp chung).

+0

Tôi không thể nghĩ ra một cách rõ ràng để làm điều này, nhưng phương pháp 'void *' được mô tả ở đây không quá tệ. Đây là cách các hàm hệ thống như 'ioctl()' làm điều đó. Bạn có thể chuyển bất kỳ dữ liệu nào bạn cần (số nguyên, cấu trúc, NULL) thông qua con trỏ, nhưng cả hàm và người gọi đều phải cẩn thận vì bạn sẽ không có bất kỳ kiểu kiểm tra nào để bảo vệ bạn. – bta

+0

Vâng, thật đáng sợ, nhưng đó là C cho bạn! Đây cũng là cách nó được thực hiện với một loạt các chức năng GLib (ví dụ: 'g_hash_table_foreach'). – Cascabel

+0

Tôi đã thử một cái gì đó tương tự trước đó, nhưng điều đó đã cho tôi hàng trăm cảnh báo về loại: "đối số đi qua từ loại con trỏ không tương thích". Tôi có thể bỏ qua chúng không? – Lucas

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