Làm thế nào để tạo một macro matrixdic (số lượng đối số thay đổi)


196

Tôi muốn viết một macro trong C chấp nhận bất kỳ số lượng tham số nào, không phải một số cụ thể

thí dụ:

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

nơi Xlà bất kỳ số lượng các thông số

Tôi cần điều này vì whateverquá 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 sẽ ghi đè lên cái đầu tiên!

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


8
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 các macro biến đổi C99 thích hợp, bạn nên biên dịch với 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 biến đổi chuẩn.
Chris Lutz

Chà, tôi cho rằng C ++ là một tập hợp siêu C về vấn đề này ..
hasen

tigcc.ticalc.org/doc/cpp.html#SEC13 có một lời giải thích chi tiết về các macro biến đổi.
Gnubie

Một lời giải thích và ví dụ hay có ở đây http://gcc.gnu.org/onlinesocs/cpp/Variadic-Macros.html
zafarulq

3
Đối với độc giả tương lai: C không phải là phần phụ của C ++. Họ chia sẻ nhiều điều, nhưng có những quy tắc ngăn họ trở thành tập hợp con và thay thế cho nhau.
Pharap

Câu trả lời:


295

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

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

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

97
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 đối số biến là trống, ví dụ: FOO ("a") mở rộng thành printf ("a"). Đây là một phần mở rộng của gcc (và vc ++, có thể), C99 yêu cầu ít nhất một đối số phải có mặt thay cho dấu chấm lửng.
jpalecek

108
##là không cần thiết và không phải là di động. #define FOO(...) printf(__VA_ARGS__)thực hiện công việc theo cách di động; các fmttham số có thể được bỏ qua từ định nghĩa.
alecov

4
IIRC, ## là đặc trưng của GCC và cho phép truyền các tham số bằng không
Mawg nói rằng khôi phục lại

10
Cú pháp ## - cũng hoạt động với llvm / clang và trình biên dịch Visual Studio. Vì vậy, nó có thể không di động, nhưng nó được hỗ trợ bởi các trình biên dịch chính.
K. Biermann

37

__VA_ARGS__là cách tiêu chuẩn để làm điều đó. Đừng sử dụng các bản hack dành riêng cho trình biên dịch nếu bạn không phải làm vậy.

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


8
"Thật là ngớ ngẩn khi biên dịch mã C của bạn với trình biên dịch C ++" => Không được mọi người (kể cả tôi) xem xét như vậy. Xem ví dụ hướng dẫn cốt lõi C ++: CPL.1: Thích C ++ hơn C , CPL.2: Nếu bạn phải sử dụng C, hãy sử dụng tập hợp con phổ biến của C và C ++ và biên dịch mã C thành C ++ . Tôi rất khó để nghĩ về những gì "chỉ C-isms" thực sự cần để làm cho nó không đáng để 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ỉ để tạo ra tập hợp con tương thích đó.
HostileFork nói không tin tưởng SE

4
@HostileFork Đủ công bằng, mặc dù tất nhiên mọi người 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 đã từ chối nhiều bản vá nhân Linux được đề xuất cố gắng thay thế định danh classbằ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ẽ khiến bạn gặp khó khăn; chẳng hạn, toán tử ternary không được đánh giá theo cùng một cách trong cả hai ngôn ngữ và inlinetừ khóa có nghĩa là một cái gì đó hoàn toàn khác nhau (như tôi đã học được từ một câu hỏi khác nhau).
Kyle Strand

3
Đối với các dự án hệ thống đa nền tảng thực sự như một hệ điều hành, bạn thực sự muốn tuân thủ nghiêm ngặt C, bởi vì trình biên dịch C rất phổ biến hơn 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ó những nền tảng chỉ có trình biên dịch C có thể vượt qua!) Trình biên dịch C ++ khiến tôi lo lắng, đặc biệt là đối với các hệ thống vật lý không gian mạng và tôi đoán tôi không phải là lập trình viên phần mềm / C nhúng duy nhất có cảm giác đó.
giảm

2
@downbeat Cho dù bạn có sử dụng C ++ để sản xuất hay không, nếu bạn quan tâm đến nó, thì việc có thể biên dịch với C ++ mang lại cho bạn sức mạnh kỳ diệu để phân tích tĩnh. Nếu bạn có một truy vấn mà bạn muốn tạo ra một cơ sở mã C ... tự hỏi liệu một số loại nhất định có được sử dụng theo một số cách nhất định hay không, học cách sử dụng type_traits có thể xây dựng các công cụ được nhắm mục tiêu cho nó. Những gì bạn phải trả nhiều tiền cho một công cụ phân tích tĩnh của C để làm có thể được thực hiện với một chút C ++ biết cách và trình biên dịch bạn đã có ...
HostileFork nói không tin tưởng SE

1
Tôi đang nói về câu hỏi của Linux. (Tôi chỉ nhận thấy rằng nó nói "Linux Torvalds" ha!)
downbeat

28

Tôi không nghĩ điều đó là có thể, bạn có thể giả mạo nó bằng các phép nhân đôi ... miễn là bạn không cần các đối số riêng lẻ.

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

21
Mặc dù có thể có một macro biến đổi, sử dụng dấu ngoặc kép là một lời khuyên tốt.
David Rodríguez - dribeas

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

10
#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 các macro matrixdic, bạn cũng có thể loại bỏ IN với một trong các cách sau:

#define PRINT //

hoặc là

#define PRINT if(0)print

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


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

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

3
Tiêu chuẩn "không làm gì, duyên dáng" là làm {} trong khi (0)
vonbrand

ifPhiên bản thích hợp của "không làm điều này" có tính đến cấu trúc mã là: if (0) { your_code } elsedấu chấm phẩy sau khi mở rộng macro của bạn chấm dứt else. Các whilehình phiên bản thích: while(0) { your_code } Vấn đề với các do..whilephiên bản là mã trong do { your_code } while (0)được thực hiện một lần, đảm bảo. Trong cả ba trường hợp, nếu your_codetrống, nó là một cách thích hợp do nothing gracefully.
Jesse Chisholm

4

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

http: //www.d Bachelorie.com/gnu/docs/gcc/gcc_44.html

ví dụ nhanh:

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

3
Các macro biến đổi của GCC không phải là các macro biến đổi C99. GCC có các macro biến đổi C99, nhưng G ++ không hỗ trợ chúng, vì C99 không phải là một phần của C ++.
Chris Lutz

1
Trên thực tế g ++ sẽ biên dịch các macro C99 trong các tệp C ++. Tuy nhiên, nó sẽ đưa ra cảnh báo nếu được biên dịch với '-pedantic'.
Alex B

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

1
C ++ 11 cũng hỗ trợ __VA_ARGS__, mặc dù chúng cũng được hỗ trợ bởi trình biên dịch trong các phiên bản trước đó, như là một phần mở rộng.
Ethouris

1
Điều này không hoạt động cho printf ("hi"); nơi không có var args. Bất kỳ cách chung chung để khắc phục điều này?
BTR N Nikol
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.