2012-02-06 12 views
6

Có cách nào để tôi có thể khám phá loại biến tự động trong C, hoặc thông qua một số cơ chế trong chính chương trình, hoặc - nhiều khả năng - thông qua quá trình biên dịch trước kịch bản sử dụng trình biên dịch đi đến điểm mà nó đã phân tích các biến và gán cho chúng các kiểu của chúng? Tôi đang tìm kiếm các đề xuất chung về điều này. Dưới đây là nền tảng về những gì tôi cần và lý do tại sao.Lấy loại biến trong mã C

Tôi muốn thay đổi ngữ nghĩa của điều khoản giảm OpenMP. Tại thời điểm này, có vẻ như đơn giản nhất là thay thế mệnh đề trong mã nguồn (thông qua một kịch bản lệnh) bằng một cuộc gọi đến một hàm, và sau đó tôi có thể định nghĩa hàm để thực hiện các ngữ nghĩa giảm mà tôi muốn. Ví dụ, kịch bản của tôi sẽ chuyển đổi này

#pragma omp parallel for reduction(+:x) 

vào đây:

my_reduction(PLUS, &x, sizeof(x)); 
#pragma omp parallel for 

ở đâu, trước đó, tôi có (nói)

enum reduction_op {PLUS, MINUS, TIMES, AND, 
    OR, BIT_AND, BIT_OR, BIT_XOR, /* ... */}; 

my_reduction có chữ ký

void my_reduction(enum reduction_op op, void * var, size_t size); 

Trong số các t khác hings, my_reduction sẽ phải áp dụng hoạt động bổ sung cho biến giảm như lập trình ban đầu đã dự định. Nhưng chức năng của tôi không thể biết làm thế nào để làm điều này một cách chính xác. Đặc biệt, mặc dù nó biết loại hoạt động (PLUS), vị trí của biến gốc (var) và kích thước của loại biến, nó không biết chính loại của biến đó. Đặc biệt, nó không biết liệu var có loại tích phân hay dấu phẩy động hay không. Từ POV cấp thấp, hoạt động bổ sung cho hai loại loại này hoàn toàn khác nhau.

Nếu chỉ nhà điều hành không chuẩn typeof, GCC hỗ trợ, sẽ hoạt động theo cách sizeof hoạt động - trả về một số loại biến - tôi có thể giải quyết vấn đề này dễ dàng. Nhưng typeof không thực sự thích sizeof: nó chỉ có thể được sử dụng, rõ ràng, trong các khai báo giá trị l.

Bây giờ, trình biên dịch rõ ràng không biết loại x trước khi nó kết thúc việc tạo mã thực thi. Điều này khiến tôi tự hỏi liệu bằng cách nào đó tôi có thể tận dụng bộ phân tích cú pháp của GCC, chỉ để lấy kiểu của x và chuyển nó vào kịch bản của tôi, và sau đó chạy GCC một lần nữa, tất cả các cách, để biên dịch mã nguồn thay đổi của tôi. Sau đó, nó sẽ đủ đơn giản để khai báo

enum var_type { INT8, UINT8, INT16, UINT16, /* ,..., */ FLOAT, DOUBLE}; 
void my_reduction(enum reduction_op op, void * var, enum var_type vtype); 

my_reduction có thể truyền một cách thích hợp trước khi dereferencing và áp dụng toán tử.

Như bạn có thể thấy, tôi đang cố gắng tạo ra một loại cơ chế "cử đi" trong C. Tại sao không chỉ sử dụng quá tải C++? Bởi vì dự án của tôi hạn chế tôi làm việc với mã nguồn kế thừa được viết bằng C. Tôi có thể thay đổi mã tự động bằng một tập lệnh, nhưng tôi không thể viết lại nó thành một ngôn ngữ khác.

Cảm ơn!

+2

Cách xử lý sau mã nguồn của bạn bằng một số công cụ/tập lệnh? Ví dụ. phân tích cú pháp bằng tiếng kêu, tìm các loại, chèn/điều chỉnh mã loại cụ thể, biên dịch? –

+0

Cảm ơn, Alex. Nghe có vẻ như nó đang đi đúng hướng. –

+1

Tôi đọc ở đâu đó mà người dùng xác định giảm sẽ là một phần của tiêu chuẩn 3.1 hoặc 4.0. Hm 3.1 nói: 'giảm ​​({toán tử | intrinsic_procedure_name}: danh sách)' ..không cố gắng intrinsic_procedure_name. mặc dù nó chỉ giải quyết một phần vấn đề phát hiện kiểu của bạn. – Bort

Trả lời

2

GCC cung cấp phần mở rộng typeof. Nó không phải là tiêu chuẩn, nhưng đủ phổ biến (một số trình biên dịch khác, ví dụ: clang/llvm, có nó).

Bạn có thể xem xét tùy chỉnh GCC bằng cách mở rộng nó bằng MELT (một ngôn ngữ cụ thể của miền để mở rộng GCC) để phù hợp với mục đích của bạn.

+1

Cảm ơn, Basile. Như bạn có thể thấy từ bài đăng của tôi, tôi nhận thức được typeof, nhưng typeof sẽ không cho tôi những gì tôi cần: nó không trả lại bất cứ điều gì; bạn không thể truyền nó hoặc thông tin của nó cho một hàm. Bạn có thể làm điều này: # define thêm (x, y, z) {\ typeof (x) _x = x; \ typeof (y) _y = y; \ z = _x + _y; \ } Toán tử typeof dường như với tôi có tính hữu dụng rất hạn chế. –

+1

Nó không phải là tiêu chuẩn và sẽ không hoạt động trên hầu hết các trình biên dịch C. Theo kinh nghiệm của tôi, việc sử dụng phần mở rộng GCC cho mã sản xuất là một ý tưởng tồi. – Lundin

2

Bạn cũng có thể xem xét tùy chỉnh GCC bằng plugin hoặc tiện ích mở rộng MELT cho nhu cầu của mình. Tuy nhiên, điều này đòi hỏi phải hiểu một số biểu diễn bên trong GCC (Gimple, Tree) phức tạp (vì vậy sẽ mất ít nhất một ngày làm việc).

Nhưng các loại là một điều chỉ biên dịch trong C. Chúng không được thống nhất.

+1

Đây là nhiều hay ít những gì tôi làm lên - không phải là một plug-in, nhưng một số mã hack trong GCC chính nó. Tôi đã tìm ra cách lấy thông tin kiểu từ các cây thông Gimple lúc biên dịch. Phải mất khá nhiều thời gian đào. Cảm ơn! –

4

C không thực sự có cách để thực hiện việc này ở thời gian biên dịch trước khi trừ khi bạn viết một loạt các macro. Tôi sẽ không khuyên lũ của phương pháp macro, nó sẽ cơ bản đi như thế này:

void int_reduction (enum reduction_op op, void * var, size_t size); 

#define reduction(type,op,var,size) type##_reduction(op, var, size) 

... 
reduction(int, PLUS, &x, sizeof(x)); // function call 

Lưu ý rằng đây là thực tế rất xấu và chỉ nên được sử dụng như phương sách cuối cùng khi duy trì mã di sản kém bằng văn bản, nếu ngay cả sau đó. Không có sự an toàn về loại hoặc các bảo đảm khác như vậy với phương pháp này.

Một cách tiếp cận an toàn hơn là để gọi một cách rõ ràng int_reduction() từ người gọi, hoặc để gọi một chức năng chung mà quyết định các loại trong thời gian chạy:

void reduction (enum type, enum reduction_op op, void * var, size_t size) 
{ 
    switch(type) 
    { 
    case INT_TYPE: 
     int_reduction(op, var, size); 
     break; 
    ... 
    } 
} 

Nếu int_reduction được inlined và khác nhau tối ưu hóa khác được thực hiện, đánh giá thời gian chạy này không nhất thiết phải chậm hơn nhiều so với các macro bị xáo trộn, nhưng nó an toàn hơn nhiều.

+1

Vâng, cảm ơn những suy nghĩ của bạn. Nhưng làm thế nào để bạn tìm ra loại? Đó là thách thức lớn của tôi. Phần còn lại chỉ là cú pháp - và tất nhiên tôi đồng ý với các nhận xét của bạn về cú pháp tốt nhất. Trong trường hợp trên, ví dụ, làm thế nào để tôi biết rằng một biến nhất định là INT_TYPE? Kịch bản tôi viết để di chuyển "giảm (+: x)" thành một cuộc gọi riêng biệt "giảm (INT_TYPE, PLUS, & var, sizeof (x));" sẽ phải biết rằng x có loại INT_TYPE, nhưng làm thế nào? - viết tắt của tôi toàn bộ một trình phân tích cú pháp chỉ để có được kiểu x? –

+1

@AmittaiAviram Làm thế nào để bạn biết khi khai báo một biến là 'int'? Tôi không có ý tưởng về bản chất của dữ liệu của bạn hoặc nó đến từ đâu, vì vậy tôi không thể trả lời câu hỏi đó. Nếu đó là một số loại dữ liệu thô bên ngoài, thì bạn sẽ tự nhiên phải xác định loại dữ liệu trước khi thực hiện bất kỳ phép tính nào. Và đó là một vấn đề thuật toán mà không có gì để làm với cú pháp lập trình C như vậy. – Lundin

+1

@ Lundin - Không, không. Hình ảnh giống như thế này. Giả sử bạn có hàm 'main' có khai báo' int x' ở gần đầu. Nói 100 dòng từ đó, bạn có '#pragma omp song song để giảm (+: x)'. 'X' có kiểu' int' vì đó là cách nó được khai báo trong phạm vi hiện tại. Các lập trình viên biết điều này và có thể chú thích một hàm gọi chính nó, nhưng tôi muốn có thể làm điều này tự động. –

7

C11 _Generic không phải là một giải pháp trực tiếp, nhưng nó cho phép bạn để đạt được kết quả mong muốn nếu bạn là bệnh nhân để mã hóa tất cả các loại như trong:

#define typename(x) _Generic((x), \ 
    int:  "int", \ 
    float: "float", \ 
    default: "other") 

int i; 
float f; 
void* v; 
assert(strcmp(typename(i), "int") == 0); 
assert(strcmp(typename(f), "float") == 0); 
assert(strcmp(typename(i), "other") == 0); 

Một điểm khởi đầu tốt với tấn các loại có thể được tìm thấy in this answer.

GCC chỉ thêm hỗ trợ trong 4.9.

4

Bạn có thể sử dụng hàm sizeof để xác định loại, để biến không xác định loại là var. sau đó

if(sizeof(var)==sizeof(char)) 
     printf("char"); 
    else if(sizeof(var)==sizeof(int)) 
     printf("int"); 
    else if(sizeof(var)==sizeof(double)) 
     printf("double"); 

Bạn sẽ gặp phải các biến chứng khi hai hoặc nhiều loại chính có cùng kích thước.