2013-06-19 29 views
13

Tôi có một mô hình về cơ bản là một số mã soạn sẵn với một phần mà thay đổi ở giữaBạn có thể sử dụng khối mã làm đối số cho macro C không?

if(condition){ 
    struct Foo m = start_stuff(); 
    { m.foo = bar(1,2); m.baz = 17; } //this part varies 
    end_stuff(); 
} 

Is it OK để thực hiện một taht vĩ mô mất rằng khối mã trung gian như một cuộc tranh cãi? Các quy tắc để mở rộng macro trong C có vẻ phức tạp đến mức tôi không chắc chắn nếu không có bất kỳ trường hợp góc nào có thể đến và cắn tôi trong tương lai (đặc biệt, tôi không hiểu cách đối số macro được tách ra nếu mã của tôi có dấu phẩy trong đó).

#define MY_MACRO(typ, do_stuff) do { \ 
    if(condition){ \ 
     struct typ m = start_stuff(); \ 
     do_stuff; \ 
     end_stuff(); \ 
    } \ 
}while(0) 

//usage 
MY_MACRO(Foo, { 
    m.foo = bar(1,2); 
    m.baz = 17; 
}); 

Cho đến nay điều duy nhất mà tôi cố gắng nghĩ ra được breakcontinue bị bắt nếu tôi sử dụng báo cáo lặp trong vĩ mô của tôi và đó sẽ là một sự cân bằng có thể chấp nhận đối với trường hợp sử dụng cụ thể của tôi.

chỉnh sửa: Tất nhiên, tôi đã sử dụng một hàm nếu có thể. Ví dụ tôi sử dụng trong câu hỏi này được đơn giản hóa và không hiển thị các bit chỉ có thể hoạt động với ma thuật macro.

+5

Do-while-false là thành ngữ chuẩn cho phép bạn thoát khỏi macro nếu cần. Đặt phòng duy nhất của tôi về macro là nó làm cho gỡ lỗi khó khăn, đặc biệt là trong các tình huống tai nạn. Nếu có một khối macro dài khoảng 100 dòng, và sau khi chạy trong 5 giờ, mã bị treo ở đâu đó trong đó trên một trong hàng trăm cuộc gọi đến macro, bạn sẽ gặp khó khăn khi theo dõi nó xuống. – cup

+0

Để biên dịch lỗi, hãy sử dụng clang. Nó xác định các lỗi cú pháp bên trong các macro lớn. Đối với các lỗi thời gian chạy, tôi thường sử dụng 'gcc -E prog.c | grep -v^# | indent> prog-dbg.c' và sau đó biên dịch và liên kết' prog-dbg.c' sang mã khác. Đó là vụng về nhưng làm việc. – user172818

+1

Khái niệm của bạn có vẻ không sao vì nó ... nhưng tại sao bạn không gọi lại các cấu trúc hàm để làm cho cuộc sống của bạn dễ dàng hơn? Tốc độ nội tuyến có quan trọng không? như @cup chỉ ra đây là một nỗi đau để gỡ lỗi. –

Trả lời

12

Bạn có thể đặt một khối mã vào đối số macro miễn là nó không có dấu phẩy không được bảo vệ. Trong ví dụ của bạn, dấu phẩy duy nhất trong đối số được bảo vệ bởi vì nó được bao quanh bởi dấu ngoặc đơn.

Lưu ý rằng chỉ dấu ngoặc đơn bảo vệ dấu phẩy. Chân đế ([]) và niềng răng ({}) thì không.

+0

Nó thực sự giúp tôi tìm ra lý do tại sao tôi nhận được lỗi: 'lỗi: macro" some_macro_name "thông qua 8 đối số, nhưng chỉ mất 7' – xhg

+0

Dường như bất cứ điều gì bạn gọi là:' <> '(ngoặc vuông?) Thì không. Tìm thấy điều này đang cố gắng sử dụng mã với nhiều hơn một đối số mẫu – Sean

+0

@sean: Có, chỉ dấu ngoặc đơn có nghĩa là chỉ có dấu ngoặc đơn. – rici

1

Bạn có thể đặt khối mã vào macro nhưng bạn phải được cảnh báo rằng nó làm cho việc gỡ lỗi khó khăn hơn nhiều bằng cách sử dụng trình gỡ lỗi. IMHO là tốt hơn chỉ để viết một hàm hoặc cut'n'paste các dòng mã.

0

Làm thế nào về con trỏ hàm thay vì (và tùy chọn inline chức năng)?

void do_stuff_inner_alpha(struct Foo *m) 
{ 
    m->foo = bar(1,2); m->baz = 17; 
} 

void do_stuff_inner_beta(struct Foo *m) 
{ 
    m->foo = bar(9, 13); m->baz = 445; 
} 


typedef void(*specific_modifier_t)(struct Foo *); 

void do_stuff(specific_modifier_t func) 
{ 
    if (condition){ 
     struct Foo m = start_stuff(); 
     func(&m); //this part varies 
     end_stuff(); 
    } 
} 

int main(int argc, const char *argv[]) 
{ 
    do_stuff(do_stuff_inner_beta); 

    return EXIT_SUCCESS; 
} 
+0

Thật không may, trong mã rel của tôi, các loại cấu trúc khác nhau và là một đối số bổ sung cho macro.Trong khi điều này có thể được làm việc xung quanh với con trỏ void, cũng có những điều othre mà tôi chỉ có thể làm với các macro vì lý do kỹ thuật. – hugomg

+0

Hãy xem xét thay đổi thiết kế thay thế đáng kể hơn để phù hợp với những gì có thể là một lớp trừu tượng cần thiết. –

+0

Unfortunatelly, tôi thực sự làm việc với [nesC] (https://en.wikipedia.org/wiki/NesC), một phương ngữ C và một số điều tôi cần để parametrize hơn là giao diện và loại tên tên mà don ' t cho mình tốt để trừu tượng chức năng bình thường. – hugomg

-2

Trước khi trả lời câu hỏi của bạn "OK để sử dụng macro" Tôi muốn biết lý do bạn muốn chuyển đổi khối mã thành macro. Bạn đang cố gắng đạt được cái gì và chi phí là bao nhiêu?

Nếu cùng một khối mã bạn đang sử dụng nhiều lần, tốt hơn nên chuyển đổi trong một hàm, có thể là hàm nội tuyến và để nó ở trình biên dịch để làm cho nội tuyến trở thành nội tuyến hay không.

Nếu bạn gặp sự cố crash \, việc gỡ lỗi macro là một công việc tẻ nhạt.

0

"OK?" có thể có nghĩa là hai điều:

  1. Nó có hoạt động không? Ở đây câu trả lời thường là có, nhưng có những cạm bẫy. Một, as rici mentioned, là một dấu phẩy không được bảo vệ. Về cơ bản, hãy nhớ rằng việc mở rộng macro là một thao tác dán bản sao & và bộ tiền xử lý không hiểu mã mà nó sao chép và dán.

  2. Đó có phải là một ý tưởng hay không? Tôi muốn nói câu trả lời thường là không. Nó làm cho mã của bạn không đọc được và khó duy trì. Trong một số trường hợp hiếm hoi, điều này có thể tốt hơn so với các lựa chọn thay thế, nếu được thực hiện tốt, nhưng đó là ngoại lệ.

3

Thay vào đó, bạn có thể xem xét sử dụng macro trước câu lệnh ghép, như minh họa bên dưới.Một trong những ưu điểm này là tất cả các trình gỡ rối vẫn có thể bước vào bên trong câu lệnh ghép của bạn, mà không phải là trường hợp với phương thức đối số-câu lệnh-như-macro-đối số.

//usage 
MY_MACRO(Foo, condition) { 
    m.foo = bar(1,2); 
    m.baz = 17; 
} 

Sử dụng một số phép thuật goto (vâng, 'goto' có thể ác trong một số trường hợp, nhưng chúng tôi có vài lựa chọn thay thế trong C), macro có thể được thực hiện như:

#define CAT(prefix, suffix)   prefix ## suffix 
#define _UNIQUE_LABEL(prefix, suffix) CAT(prefix, suffix) 
#define UNIQUE_LABEL(prefix)   _UNIQUE_LABEL(prefix, __LINE__) 

#define MY_MACRO(typ, condition) if (condition) { \ 
            struct typ m = start_stuff(); goto UNIQUE_LABEL(enter);} \ 
            if (condition) while(1) if (1) {end_stuff(); break;} \ 
                  else UNIQUE_LABEL(enter): 

Lưu ý rằng điều này có hiệu suất nhỏ và ảnh hưởng đến dấu chân khi tối ưu hóa trình biên dịch bị tắt. Ngoài ra, một trình gỡ rối có vẻ sẽ nhảy trở lại dòng MY_MACRO khi chạy gọi hàm end_stuff(), điều này không thực sự mong muốn.

Ngoài ra, bạn có thể muốn sử dụng các macro bên trong một phạm vi khối mới để tránh ô nhiễm phạm vi của bạn với 'm' biến:

{MY_MACRO(Foo, condition) { 
    m.foo = bar(1,2); 
    m.baz = 17; 
}} 

Tất nhiên, sử dụng 'break' không phải bên trong một vòng lặp lồng nhau trong câu lệnh ghép sẽ bỏ qua 'end_stuff()'. Để cho phép đối với những người để phá vỡ vòng xung quanh và vẫn gọi là 'end_stuff()', tôi nghĩ rằng bạn phải kèm theo câu lệnh ghép với một sự khởi đầu token và một thẻ cuối như trong:

#define MY_MACRO_START(typ, condition) if (condition) { \ 
              struct typ m = start_stuff(); do { 

#define MY_MACRO_EXIT     goto UNIQUE_LABEL(done);} while (0); \ 
             end_stuff(); break; \ 
             UNIQUE_LABEL(done): end_stuff();} 

MY_MACRO_START(foo, condition) { 
    m.foo = bar(1,2); 
    m.baz = 17; 
} MY_MACRO_END 

Lưu ý rằng vì của 'break' trong cách tiếp cận đó, macro MY_MACRO_EXIT sẽ chỉ có thể sử dụng trong vòng lặp hoặc chuyển đổi. Bạn có thể sử dụng một thực hiện đơn giản hơn khi không bên trong một vòng lặp:

#define MY_MACRO_EXIT_NOLOOP } while (0); end_stuff();} 

tôi đã sử dụng 'điều kiện' như một đối số vĩ mô, nhưng bạn cũng có thể nhúng nó trực tiếp trong vĩ mô nếu muốn.

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