2009-11-22 44 views
13

Câu hỏi nhanh. Có ai biết làm thế nào để có được con trỏ hàm của một phương pháp c mục tiêu? Tôi có thể khai báo một phương thức C++ như một con trỏ hàm, nhưng đây là một phương thức gọi lại để phương thức C++ cần phải là một phần của lớp Vì vậy, nó có thể truy cập các kênh INSTIT. Tôi không biết làm thế nào để làm cho một phần C + + phương pháp của một lớp c mục tiêu. Bất kỳ đề xuất?Con trỏ hàm trong mục tiêu C

Trả lời

14

Thông thường, bạn cần hai mẩu thông tin để gọi lại vào Mục tiêu-C; phương thức được gọi và đối tượng để gọi nó. Không chỉ là bộ chọn hay chỉ là IMP - kết quả instanceMethodForSelector: - sẽ là đủ thông tin.

Hầu hết các API gọi lại cung cấp một con trỏ ngữ cảnh được coi là giá trị mờ đục được chuyển đến hàm gọi lại. Đây là chìa khóa cho câu hỏi hóc búa của bạn.

I.e. nếu bạn có một hàm callback mà được khai báo là:

typedef void (*CallBackFuncType)(int something, char *else, void *context); 

Và một số API mà tiêu thụ một con trỏ của nói kiểu hàm callback:

void APIThatWillCallBack(int f1, int f2, CallBackFuncType callback, void *context); 

Sau đó, bạn sẽ thực hiện một cái gì đó gọi lại của bạn như thế này:

void MyCallbackDude(int a, char *b, void *context) { 
    [((MyCallbackObjectClass*)context) myMethodThatTakesSomething: a else: b]; 
} 

Và sau đó bạn sẽ gọi API giống như sau:

MyCallbackObjectClass *callbackContext = [MyCallbackObjectClass new]; 
APIThatWillCallBack(17, 42, MyCallbackDude, (void*)callbackContext); 

Nếu bạn cần chuyển đổi giữa các bộ chọn khác nhau, tôi khuyên bạn nên tạo một lớp keo nhỏ nằm giữa hàm gọi lại và API mục tiêu-C. Ví dụ của lớp keo có thể chứa cấu hình cần thiết hoặc logic cần thiết để chuyển đổi giữa các bộ chọn dựa trên dữ liệu gọi lại đến.

+0

Hmmm ... mẹo hay. Tôi đoán đây là sorta cùng một điều như sử dụng mục tiêu/hành động. Về cơ bản, bạn phải nói rằng chức năng gọi lại ngớ ngẩn chỉ là người để nói chuyện. Nếu tôi đang làm việc với một API mà tôi không có nguồn thì nó sẽ hữu ích. Trên thực tế, tôi chỉ đang triển khai toàn bộ quá trình thiết lập với mã của riêng tôi. Tuy nhiên, cảm ơn một tấn. –

+0

Nó giống như mục tiêu/hành động, nhưng không hoàn toàn giống nhau. Nếu viết tinh khiết C, bối cảnh sẽ là đoạn malloc'd của bộ nhớ có chứa bất kỳ trạng thái nào bạn cần trong cuộc gọi lại. Đây là gần như chính xác những gì đang xảy ra ở đây, nhưng bạn chỉ chuyển tiếp gọi lại vào mục tiêu-C ... – bbum

+0

Khi phát triển với Cocoa, NSInvocation có thể là lớp keo: http://developer.apple.com/mac/library/ tài liệu/ca cao/tham khảo/nền tảng/Lớp/NSInvocation_Class/Tham khảo/Tham khảo.html – outis

1

Bạn có thể tạo lớp C++ làm trình bao bọc. Và gọi tin nhắn C mục tiêu từ lớp đó. Chỉ cần nhớ tạo tệp của bạn yoursource.mm thay vì yoursource.cpp.

13

Bạn có thể làm điều đó với @selectorSEL loại:

SEL sel = @selector(myMethod:); 

/* Equivalent to [someObject myMethod:Paramater] */ 
[someObject performSelector:sel withObject:Parameter] 

Ngoài ra còn có các loại IMP ...

IMP imp = [someObject methodForSelector:sel]; 

/* don't remember if this syntax is correct; it's been a while... 
* the idea is that it's like a function pointer. */ 
imp(someObject, sel, Parameter); 

Cập nhật dựa trên ý kiến ​​của bạn

Nếu bạn không muốn phải chỉ định đối tượng, trong trường hợp này bạn đang yêu cầu điều gì đó khủng khiếp không di động. Về cơ bản bạn muốn biểu thức lambda không phải là một tính năng của C, mặc dù nó đến trong C++ 0x.

Vì vậy, giải pháp của tôi sẽ là "ngoài kia", sơ sài, và không di động ...

NHƯNG ... có lẽ bạn có thể làm điều đó với thế hệ mã thời gian chạy ...

Bạn có thể bắt đầu bằng cách viết một hàm còn sơ khai trong lắp ráp (giả sử bạn muốn x86)

push dword 0 ; SEL will go here 
push dword 0 ; Object will go here 
push dword 0 ; IMP will go here 
pop eax  ; eax = imp 
call eax  ; call imp 
add esp, 8 ; cleanup stack 
ret   ; return 

này lắp ráp để:

0068 0000 6800 0000 0000 0068 0000 5800 d0ff c481 0004 0000 00c3 

Lưu ý hướng dẫn push dword 0 là các byte 68 00 00 00 00. Chúng ta sẽ điền các số 0 vào một con trỏ khi chạy.

Vì vậy, chúng tôi có thể sao chép bộ đệm đó vào bộ đệm malloc() 'd, vá lên và gọi mprotect() để làm cho tệp có thể thực thi được.

Mã dưới đây là dành cho mục đích minh họa và tôi không biết nó có hoạt động hay không. :-)

/* x86 code... */ 
char code[] = { 0x68, 0x00, 0x00, 0x00, 0x00, 0x68, 
       0x00, 0x00, 0x00, 0x00, 0x68, 0x00, 
       0x00, 0x00, 0x00, 0x58, 0xff, 0xd0, 
       0x81, 0xc4, 0x04, 0x00, 0x00, 0x00, 
       0xc3 }; 

char *buf = malloc(sizeof(code)); 

SEL Selector = @selector(Method); 
IMP Imp = [object methodForSelector:Selector]; 

/* Copy template */ 
memcpy(buf, code, sizeof(code)); 

/* Patch the "push dword 0" parts with your arguments 
* This assumes everything is 32-bit, including SEL, IMP, etc. */ 
memcpy(buf + 1, &Selector, sizeof(Selector)); 
memcpy(buf + 6, &object, sizeof(object)); 
memcpy(buf + 11, &Imp, sizeof(Imp)); 

/* Now here comes the sketchy part... 
* Make it executable and turn it into a function pointer. */ 
mprotect(buf, sizeof(code), PROT_EXEC); 
void (*Function)() = (void(*)())buf; 

/* Now, crazy as it sounds, you should be able to do: */ 
Function(); 

Bạn có thể muốn làm một [object retain] càng lâu càng chức năng này tồn tại, và [object release] khi nào và nếu bạn nên chọn để giải phóng nó. (Có lẽ tốt nhất để quấn sketcyness này bên trong một đối tượng dù sao, sau đó sử dụng refcounting objc bình thường để điều khiển bộ đệm và một tham chiếu đến object.) Có lẽ bạn cũng sẽ muốn sử dụng mmap() phân bổ thay vì malloc() ...

Nếu nghe có vẻ phức tạp không cần thiết vì đó là. :-)

+0

Đoạn mã thứ hai cung cấp con trỏ hàm kiểu C, theo yêu cầu. Các chi tiết có thể là một chút khôn lanh, mặc dù. –

+0

Đó là một lựa chọn tốt đẹp, nhưng tôi muốn chỉ cần vượt qua một con trỏ đến hàm. KHÔNG phải là con trỏ tới đối tượng. nếu không, tôi sẽ chỉ sử dụng bộ chọn. –

+1

Thông thường, không phải IMP cũng như Selector sẽ giúp bạn nhiều như bạn cũng cần tham chiếu đến cá thể bạn cần gọi lại .... – bbum

1

Bạn có thể thực hiện việc này với instanceMethodForSelector: Xem Apple NSObject documentation để biết thêm thông tin.

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