2009-03-25 40 views
158

Tôi muốn viết một macro trong C chấp nhận bất kỳ số lượng các thông số, không phải là một con số cụ thểLàm thế nào để thực hiện một macro variadic (số biến của tham số)

dụ:

#define macro(X) something_complicated(whatever(X)) 

nơi X là bất kỳ số tham số nào

Tôi cần điều này vì whatever bị quá tải và có thể được gọi với 2 hoặc 4 tham số.

Tôi đã thử xác định macro hai lần, nhưng định nghĩa thứ hai ghi đè macro đầu tiên!

Trình biên dịch Tôi đang làm việc với là g ++ (cụ thể hơn, mingw)

+6

Bạn có muốn C hoặc C++ không? Nếu bạn đang sử dụng C, tại sao bạn biên dịch bằng trình biên dịch C++? Để sử dụng macro C99 variadic thích hợp, bạn nên biên dịch bằng trình biên dịch C hỗ trợ C99 (như gcc), không phải trình biên dịch C++, vì C++ không có macro Vdd chuẩn. –

+0

Vâng, tôi cho rằng C++ là một tập hợp siêu C trong lĩnh vực này .. – hasen

+0

http://tigcc.ticalc.org/doc/cpp.html#SEC13 có giải thích chi tiết về các macro biến thể. – Gnubie

Trả lời

244

Cách C99, cũng được hỗ trợ bởi trình biên dịch VC++.

#define FOO(fmt, ...) printf(fmt, ##__VA_ARGS__) 
+6

Tôi không nghĩ C99 yêu cầu ## trước __VA_ARGS__. Đó có thể chỉ là VC++. –

+85

Lý do cho ## trước __VA_ARGS__ là vì nó nuốt dấu phẩy trước trong trường hợp danh sách biến đối số trống, ví dụ: FOO ("a") mở rộng thành printf ("a"). Đây là phần mở rộng của gcc (và vC++, có thể), C99 yêu cầu ít nhất một đối số để có mặt thay cho dấu ba chấm. – jpalecek

+83

'##' là không cần thiết và không di động. '#define FOO (...) printf (__ VA_ARGS __)' thực hiện công việc theo cách di động; tham số 'fmt' có thể được bỏ qua từ định nghĩa. – alecov

4

giải thích cho g ++ ở đây, mặc dù nó là một phần của C99 rất nên làm việc cho tất cả mọi người

http://www.delorie.com/gnu/docs/gcc/gcc_44.html

ví dụ nhanh :

#define debug(format, args...) fprintf (stderr, format, args) 
+2

Macro Vd của GCC không phải là macro C99 variadic. GCC _has_ C99 Biến thể macro, nhưng G ++ không hỗ trợ chúng, vì C99 không phải là một phần của C++. –

+0

Trên thực tế g ++ sẽ biên dịch các macro C99 trong các tệp C++. Nó sẽ đưa ra một cảnh báo, tuy nhiên, nếu được biên dịch với '-pantic'. –

+2

Nó không phải là C99. C99 sử dụng macro __VA_ARGS__). – qrdl

22

Tôi không nghĩ điều đó là có thể, bạn có thể giả mạo nó bằng cách gấp đôi ... chỉ cần bạn không cần đối số riêng.

#define macro(ARGS) some_complicated (whatever ARGS) 
// ... 
macro((a,b,c)) 
macro((d,e)) 
+0

C99 thêm vectơ variadic. –

+16

Trong khi nó có thể có một vĩ mô variadic, sử dụng dấu ngoặc kép là một lời khuyên tốt. –

+1

Trình biên dịch XC của Microchip không hỗ trợ các macro variadic, và vì vậy thủ thuật ngoặc kép này là tốt nhất bạn có thể làm. – gbmhunter

8
#define DEBUG 

#ifdef DEBUG 
    #define PRINT print 
#else 
    #define PRINT(...) ((void)0) //strip out PRINT instructions from code 
#endif 

void print(const char *fmt, ...) { 

    va_list args; 
    va_start(args, fmt); 
    vsprintf(str, fmt, args); 
     va_end(args); 

     printf("%s\n", str); 

} 

int main() { 
    PRINT("[%s %d, %d] Hello World", "March", 26, 2009); 
    return 0; 
} 

Nếu trình biên dịch không hiểu macro variadic, bạn cũng có thể loại bỏ PRINT với một trong hai điều sau đây:

#define PRINT // 

hoặc

#define PRINT if(0)print 

Các bình luận đầu tiên ra hướng dẫn IN, lệnh thứ hai ngăn lệnh PRINT vì điều kiện NULL. Nếu tối ưu hóa được thiết lập, trình biên dịch sẽ loại bỏ không bao giờ thực hiện các hướng dẫn như: if (0) print ("hello world"); hoặc ((void) 0);

+6

#define PRINT // sẽ không thay thế PRINT bằng // – bitc

+7

#define PRINT nếu (0) in không phải là một ý tưởng hay vì mã gọi có thể có khác- nếu gọi PRINT. Tốt hơn là: #define PRINT nếu (true); in khác – bitc

+2

Tiêu chuẩn "không làm gì, duyên dáng" sẽ thực hiện {} trong khi (0) – vonbrand

26

__VA_ARGS__ là cách tiêu chuẩn để thực hiện. Không sử dụng hacks trình biên dịch cụ thể nếu bạn không phải.

Tôi thực sự khó chịu vì tôi không thể nhận xét về bài đăng gốc. Trong mọi trường hợp, C + + không phải là một superset của C. Nó thực sự là ngớ ngẩn để biên dịch mã C của bạn với một trình biên dịch C++. Đừng làm những gì Donny không làm.

+3

* "Thật là ngớ ngẩn khi biên dịch mã C của bạn bằng trình biên dịch C++" * => Không được mọi người xem xét (kể cả tôi). Xem ví dụ C++ lõi hướng dẫn: ** CPL.1: Ưu tiên C + + để C **, [** CPL.2: Nếu bạn phải sử dụng C, sử dụng các tập con chung của C và C + +, và biên dịch mã C là C++ * *] (https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md#Rcpl-subset). Tôi rất khó để nghĩ về những gì "C-only-isms" thực sự cần phải làm cho nó không đáng được lập trình trong tập hợp con tương thích, và các ủy ban C và C++ đã làm việc chăm chỉ để làm cho tập hợp con tương thích có sẵn. – HostileFork

+1

@HostileFork Đủ công bằng, mặc dù * tất nhiên * những người dùng C++ muốn khuyến khích sử dụng C++. Những người khác không đồng ý, mặc dù; Ví dụ, Linux Torvalds dường như đã từ chối nhiều bản vá lỗi hạt nhân Linux được đề xuất, cố gắng thay thế mã định danh 'class' bằng' klass' để cho phép biên dịch bằng trình biên dịch C++. Cũng lưu ý rằng có một số khác biệt sẽ giúp bạn đi lên; ví dụ, toán tử ternary không được đánh giá theo cùng một cách trong cả hai ngôn ngữ và từ khoá 'inline' có nghĩa là một cái gì đó hoàn toàn khác (như tôi đã học được từ một câu hỏi khác). –

+0

Đối với các dự án hệ thống thực sự đa nền tảng như hệ điều hành, bạn thực sự muốn tuân thủ nghiêm ngặt C, vì trình biên dịch C phổ biến hơn rất nhiều.Trong các hệ thống nhúng, vẫn có các nền tảng không có trình biên dịch C++. Các trình biên dịch C++ khiến tôi lo lắng, đặc biệt là cho các hệ thống mạng vật lý, và tôi đoán tôi không phải là lập trình viên phần mềm nhúng/C duy nhất có cảm giác đó. – downbeat

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