2011-06-29 19 views
9

Mã sau đây là từ một ứng dụng hiện có phải được biên dịch trong cả C và C++. Có một vĩ mô:Vui lòng giải thích vectơ hardcore này để đúc và đánh máy

/* Type-checking macro to provide arguments for CoCreateInstance() etc. 
* The pointer arithmetic is a compile-time pointer type check that 'obj' 
* really is a 'type **', but is intended to have no effect at runtime. */ 
#define COMPTR(type, obj) &IID_##type, \ 
(void **)(void *)((obj) + (sizeof((obj)-(type **)(obj))) \ 
       - (sizeof((obj)-(type **)(obj)))) 

được sử dụng như sau:

ISomeInterface *object; 
CoCreateInstance(&CLSID_SomeInterfaceImpl, NULL, 
    CLSCTX_INPROC_SERVER, COMPTR(ISomeInterface, &object)))); 

đây ý tưởng là hai tham số cuối cùng của CoCreateInstance()IID&void** và grabs vĩ mô ISomeInterface** và chuyển đổi nó để IID&void** đồng thời thực thi kiểm tra thời gian biên dịch địa chỉ được chuyển tại vị trí ISomeInterface** thực sự là địa chỉ của biến con trỏ ISomeInterface*.

Được rồi, nhưng nhu cầu

((obj) + (sizeof((obj)-(type **)(obj))) \ 
    - (sizeof((obj)-(type **)(obj))) 

biểu hiện phức tạp là những gì? Tôi thấy rằng việc kiểm tra loại được thực thi với (obj)-(type**)(obj) biểu hiện con. Cần thêm và sau đó trừ số sizeof() là gì? Và cần phải đúc gì để void* trước khi truyền tới void**?

Tôi cho rằng như vậy có thể được thực hiện như sau:

#define COMPTR(type, obj) &IID_##type, \ 
(void **)(sizeof((obj)-(type**)(obj)), obj) 

đây phần đầu của nhà điều hành dấu phẩy sẽ chứa một sizeof() đó sẽ thực thi các typecheck và đánh giá để một hằng số, phần thứ hai sẽ chỉ mang lại cùng một con trỏ và con trỏ sẽ được truyền tới void**.

Macro gốc có thể làm những gì tôi đề xuất không thể? Sự cần thiết cho những biến chứng đó là gì?

+0

Và đây là lý do tại sao đây là mã không hợp lệ. Hãy để tôi đoán, bạn đã đấu tranh với điều này cả ngày? – orlp

+0

@nightcracker: Không, tôi không duy trì mã đó, tôi chỉ tìm thấy nó và tò mò nó hoạt động như thế nào. – sharptooth

+2

+1 cho phần cứng trong tiêu đề, phải là một thẻ! –

Trả lời

5

Có thể tác giả gốc không biết về toán tử dấu phẩy? Đây không phải là chính xác chưa từng thấy trong số các lập trình viên C/C++.

+0

Đáng ngạc nhiên là một vài lập trình viên C tôi đã làm việc với biết về toán tử dấu phẩy. – Mike

+0

Một số dự án có các tiêu chuẩn lập trình cấm sử dụng toán tử dấu phẩy. Macro sử dụng cú pháp bị cấm! Lạy trời! Sử dụng cú pháp tối nghĩa hơn và viết một số không phụ (hoặc phép nhân) theo một cách khá sáng tạo hơn là sử dụng toán tử dấu phẩy hoặc toán tử.

4

Có thể tác giả gốc không biết về các mẫu chức năng. Macro này đang được thay thế bằng mẫu chức năng.

Dường như đối số thứ tư là CoCreateInstance là một con trỏ tới một đối tượng toàn cầu của loại IID liên quan đến đối số type (gõ đối số COMPTR) trong tầm tay. Đối số thứ năm và cuối cùng là CoCreateInstance được cho là một con trỏ type**.

Thay vào đó, hàm CoCreateInstance lấy một con trỏ void** (yech!) Làm đối số cuối cùng của nó, thu được bằng cách truyền con trỏ giả định type**. Các diễn viên đi qua void* như một trung gian bởi vì bất kỳ con trỏ có thể được đúc đến/từ một con trỏ void *.

Tránh bảo vệ trong macro COMPTR đó, người dùng có thể vượt qua con trỏ double* hoặc thậm chí là long long (không phải con trỏ!) Làm đối số thứ năm cho CoCreateInstance. Tất nhiên, toàn bộ mớ hỗn độn này có thể tránh được nếu tác giả gốc sử dụng C++, nó rất tốt, an toàn kiểu. Thay vào đó anh/cô ấy quyết định đi con trỏ void * và đặt bảo vệ trong macro.

Điều gì sẽ xảy ra: Đối số sizeof là biểu thức khác biệt con trỏ (obj)-(type**)(obj).Nếu obj là con trỏ type**, đây là 0 (dưới dạng ptrdiff_t). Nếu obj là một cái gì đó khác, biểu thức khác biệt con trỏ này là hình thành không đúng. Vì vậy, hai trường hợp, obj là con trỏ type** hoặc không.

Trường hợp 1, obj là một con trỏ type**: Khái niệm con trỏ khác biệt là có giá trị, vì vậy đối số cuối cùng để CoCreateInstance mở rộng để (void**)(void*)(obj+8-8), giả sử một máy 64 bit. (+ 8-8 trở thành + 4-4 trên máy 32 bit.) Bất kể kích thước máy, độ lệch được thêm vào và trừ đi, để lại con trỏ ban đầu.

Trường hợp 2, obj không phải là con trỏ type**: Biểu thức khác biệt con trỏ bị tạo hình sai, do đó mã không biên dịch.

+3

Xin lưu ý rằng không có mẫu nào trong C và mã được biên dịch trong cả C và C++ và cũng là 'CoCreateInstance()' là một hàm Win32 phục vụ như một nhà máy lớp toàn cầu không thể cụ thể cho bất kỳ giao diện nào và đó chính là lý do tại sao nó chấp nhận 'void **'. – sharptooth

+0

@sharptooth: Bạn có downvote không? Lưu ý rằng tôi đã giải thích chính xác macro này hoạt động như thế nào. –

+0

Không, tôi đã không downvote, tôi chỉ làm rõ hai điểm khá quan trọng - đó là mẫu không thể được sử dụng ở đây và rằng 'void **' là có cho một lý do. – sharptooth

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