Các tham số tùy chọn với Macro C ++


105

Có một số cách để nhận các tham số tùy chọn với Macro C ++? Một số loại quá tải cũng sẽ tốt.


1
Tương tự đối với C: stackoverflow.com/questions/11761703/… Sẽ giống nhau vì các bộ tiền xử lý về cơ bản giống nhau: stackoverflow.com/questions/5085533/…
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功

Có lẽ quá tải chức năng, các thông số mặc định, các mẫu variadic hoặc có thể là tên tham số thành ngữ là những gì bạn đang tìm kiếm
smoothware

Vui lòng cập nhật câu trả lời của bạn được lựa chọn để những người cao upvoted với các giải pháp thực tế, không phải là người tầm thường-upvoted nóiNo you can't
Albert Renshaw

Câu trả lời:


156

Đây là một cách để làm điều đó. Nó sử dụng danh sách các đối số hai lần, trước tiên để tạo thành tên của macro trợ giúp, sau đó để chuyển các đối số đến macro trợ giúp đó. Nó sử dụng một thủ thuật tiêu chuẩn để đếm số lượng đối số cho một macro.

enum
{
    plain = 0,
    bold = 1,
    italic = 2
};

void PrintString(const char* message, int size, int style)
{
}

#define PRINT_STRING_1_ARGS(message)              PrintString(message, 0, 0)
#define PRINT_STRING_2_ARGS(message, size)        PrintString(message, size, 0)
#define PRINT_STRING_3_ARGS(message, size, style) PrintString(message, size, style)

#define GET_4TH_ARG(arg1, arg2, arg3, arg4, ...) arg4
#define PRINT_STRING_MACRO_CHOOSER(...) \
    GET_4TH_ARG(__VA_ARGS__, PRINT_STRING_3_ARGS, \
                PRINT_STRING_2_ARGS, PRINT_STRING_1_ARGS, )

#define PRINT_STRING(...) PRINT_STRING_MACRO_CHOOSER(__VA_ARGS__)(__VA_ARGS__)

int main(int argc, char * const argv[])
{
    PRINT_STRING("Hello, World!");
    PRINT_STRING("Hello, World!", 18);
    PRINT_STRING("Hello, World!", 18, bold);

    return 0;
}

Điều này giúp người gọi macro dễ dàng hơn chứ không phải người viết.


1
Điều này khá tuyệt, nhưng tôi không nghĩ rằng nó sẽ hoạt động nếu tôi chỉ làm PRINT_STRING. Trong trường hợp đó sẽ không có bản in mặc định (và đó thực sự là trường hợp tôi muốn sử dụng). Vẫn +1 vì thực sự thú vị.
Cenoc

2
hoạt động với tôi trong gcc (và nó rất thông minh!) :-) nhưng không hoạt động với tôi trong Visual Studio :-(
Tim Gradwell

3
@TimGradwell - đó là do một lỗi trong trình biên dịch MSVC mà họ đã thừa nhận nhưng chưa sửa trong gần một thập kỷ. Tuy nhiên, các giải pháp thay thế có sẵn .
BeeOnRope

Thông minh, nhưng không hoạt động đối với các đối số macro biến thiên tùy chọn vì điều 'đẩy ra' mà bạn đang diễn ra trong `GET_4th_ARG '.
searchhengine 27

điều đó PRINT_STRING_MACRO_CHOOSERthậm chí còn cần thiết? Tôi có thể thay thế trực tiếp bằng phần thân bên trong của nó và gọi toàn bộ thứ này bằng (__VA_ARGS__)không?
Herrgott

85

Vô cùng tôn trọng Derek Ledbetter vì câu trả lời của anh ấy - và với lời xin lỗi vì đã làm sống lại một câu hỏi cũ.

Bắt một sự hiểu biết về những gì nó đã làm và nhặt ở đâu đó trên khả năng preceed sự __VA_ARGS__với ##tôi được phép đưa ra một sự thay đổi ...

// The multiple macros that you would need anyway [as per: Crazy Eddie]
#define XXX_0()                     <code for no arguments> 
#define XXX_1(A)                    <code for one argument> 
#define XXX_2(A,B)                  <code for two arguments> 
#define XXX_3(A,B,C)                <code for three arguments> 
#define XXX_4(A,B,C,D)              <code for four arguments>  

// The interim macro that simply strips the excess and ends up with the required macro
#define XXX_X(x,A,B,C,D,FUNC, ...)  FUNC  

// The macro that the programmer uses 
#define XXX(...)                    XXX_X(,##__VA_ARGS__,\
                                          XXX_4(__VA_ARGS__),\
                                          XXX_3(__VA_ARGS__),\
                                          XXX_2(__VA_ARGS__),\
                                          XXX_1(__VA_ARGS__),\
                                          XXX_0(__VA_ARGS__)\
                                         ) 

Đối với những người không phải là chuyên gia như tôi, những người tình cờ tìm ra câu trả lời, nhưng không thể hiểu nó hoạt động như thế nào, tôi sẽ thực hiện từng bước xử lý thực tế, bắt đầu với đoạn mã sau ...

XXX();
XXX(1); 
XXX(1,2); 
XXX(1,2,3); 
XXX(1,2,3,4); 
XXX(1,2,3,4,5);      // Not actually valid, but included to show the process 

Trở thành ...

XXX_X(, XXX_4(), XXX_3(),  XXX_2(),    XXX_1(),      XXX_0()         );
XXX_X(, 1,       XXX_4(1), XXX_3(1),   XXX_2(1),     XXX_1(1),       XXX_0(1)          );
XXX_X(, 1,       2,        XXX_4(1,2), XXX_3(1,2),   XXX_2(1,2),     XXX_1(1,2),       XXX_0(1,2)        );
XXX_X(, 1,       2,        3,          XXX_4(1,2,3), XXX_3(1,2,3),   XXX_2(1,2,3),     XXX_1(1,2,3),     XXX_0(1,2,3)      );
XXX_X(, 1,       2,        3,          4,            XXX_4(1,2,3,4), XXX_3(1,2,3,4),   XXX_2(1,2,3,4),   XXX_1(1,2,3,4),   XXX_0(1,2,3,4)    );
XXX_X(, 1,       2,        3,          4,            5,              XXX_4(1,2,3,4,5), XXX_3(1,2,3,4,5), XXX_2(1,2,3,4,5), XXX_1(1,2,3,4,5), XXX_0(1,2,3,4,5) );

Điều này chỉ trở thành đối số thứ sáu ...

XXX_0(); 
XXX_1(1); 
XXX_2(1,2); 
XXX_3(1,2,3); 
XXX_4(1,2,3,4); 
5; 

Tái bút: Xóa #define cho XXX_0 để gặp lỗi biên dịch [nghĩa là: nếu tùy chọn không đối số không được phép].

PPS: Sẽ rất tuyệt nếu các tình huống không hợp lệ (ví dụ: 5) là thứ gây ra lỗi biên dịch rõ ràng hơn cho lập trình viên!

PPPS: Tôi không phải là một chuyên gia, vì vậy tôi rất vui khi nghe những bình luận (tốt, xấu hay khác)!


3
Bạn có thể gặp lỗi biên dịch rõ ràng nếu bạn chuyển đổi đối số đã chọn được cho là tên MACRO thành chuỗi bằng cách sử dụng # (dấu thăng) và so sánh n ký tự đầu tiên của nó với tiền tố dự kiến ​​và nếu không khớp, hãy in một thông tin lỗi.
AturSams

1
Chà, tôi không biết nó có hiệu quả không, nhưng ít nhất nó rất sáng tạo!
Sự Chuộc Tội Có Giới Hạn vào

4
tại sao đối số đầu tiên luôn trống? tại sao chúng tôi không thể bỏ qua nó: XXX_X(,##__VA_ARGS__,` ... XXX_X (, XXX_4 (), XXX_3 (), XXX_2 (), XXX_1 (), XXX_0 ()); '
rahman

2
Đối số đầu tiên trống (dấu phẩy) là quan trọng. ## __ VA_ARGS__ nếu đứng trước dấu phẩy – nó sẽ xóa dấu phẩy nếu ## __ VA_ARGS__ mở rộng thành không. Bạn có thể nhìn thấy nó trong ví dụ "trở thành ..." như là người đầu tiên (không có đối số) dòng chỉ có 6 thông số nhưng phần còn lại được 7. Bí quyết này đảm bảo rằng không có công trình lý luận tình hình
David Sorkovsky

@Eric - đó là do lỗi trong trình biên dịch microsoft, nhưng bạn có thể xem câu hỏi này để biết cách giải quyết.
BeeOnRope

31

Macro C ++ không thay đổi so với C. Vì C không có đối số nạp chồng và mặc định cho các hàm nên chắc chắn nó không có chúng cho macro. Vì vậy, để trả lời câu hỏi của bạn: không, những tính năng đó không tồn tại cho macro. Tùy chọn duy nhất của bạn là xác định nhiều macro với các tên khác nhau (hoặc hoàn toàn không sử dụng macro).

Như một chú thích phụ: Trong C ++, thường được coi là phương pháp hay để tránh xa macro càng nhiều càng tốt. Nếu bạn cần các tính năng như thế này, rất có thể bạn đang lạm dụng macro.


4
Lưu ý rằng lý do tại sao không thể "quá tải" macro là vì chúng không có bất kỳ loại vốn có nào. Macro được mở rộng đơn giản.
mk12

2
Mặc dù tôi sử dụng các macro càng ít càng tốt, tôi thấy rằng gỡ lỗi thông qua dấu vết đầu ra được khá một chút dễ dàng hơn với những thứ như __FILE____LINE__và như vậy ...
Christian Severin

không phải là một câu trả lời hay. đây là một câu trả lời hay: stackoverflow.com/q/27049491/893406
v.oddou

Biên dịch có điều kiện và gỡ lỗi / ghi nhật ký là lĩnh vực mà macro thực sự tiện dụng và hợp pháp. Mọi lập trình viên nghiêm túc đều biết điều đó. Thực tiễn tốt là tránh sử dụng macro để xác định hằng số và thực hiện một số công cụ mã hóa cấp C điên rồ để tạo các mẫu vùng chứa. Tôi ước C ++ cũng sẽ thêm nhiều tính năng hơn vào macro. Chúng trực giao với các khuôn mẫu. Tất nhiên, tốt nhất sẽ là các bộ mã hóa cho phép tôi thêm trình tạo vào trình biên dịch cho ngôn ngữ miền cụ thể (các khía cạnh).
Lothar

1
Tôi cũng nghĩ rằng đây không phải là một câu trả lời hay, bởi vì macro là một thứ gì đó hoàn toàn khác với bất kỳ tùy chọn ngôn ngữ C ++ nào, bởi vì nó sẽ được xử lý TRƯỚC trình biên dịch. Vì vậy, bạn có thể làm những việc khác và không có trình biên dịch hoặc trình liên kết nào phải tối ưu hóa mã, vì có thể nó không tối ưu hóa.
alabamajack

26

Với sự tôn trọng lớn nhất đối với Derek Ledbetter , David Sorkovsky , Syphorlate vì câu trả lời của họ, cùng với phương pháp khéo léo để phát hiện các đối số macro trống của Jens Gustedt tại

https://gustedt.wordpress.com/2010/06/08/detect-empty-macro-arguments/

cuối cùng, tôi đưa ra một thứ kết hợp tất cả các thủ thuật, để giải pháp

  1. Chỉ sử dụng macro C99 tiêu chuẩn để đạt được quá tải hàm, không liên quan đến phần mở rộng GCC / CLANG / MSVC (nghĩa là nuốt dấu phẩy đối với biểu thức cụ thể , ##__VA_ARGS__cho GCC / CLANG và nuốt ngầm ##__VA_ARGS__đối với MSVC). Vì vậy, hãy chuyển phần còn thiếu --std=c99cho trình biên dịch của bạn nếu bạn muốn =)
  2. Hoạt động với đối số không , cũng như số lượng đối số không giới hạn , nếu bạn mở rộng thêm cho phù hợp với nhu cầu của mình
  3. Hoạt động hợp lý trên nhiều nền tảng , ít nhất đã được thử nghiệm

    • GNU / Linux + GCC (GCC 4.9.2 trên CentOS 7.0 x86_64)
    • GNU / Linux + CLANG / LLVM , (CLANG / LLVM 3.5.0 trên CentOS 7.0 x86_64)
    • OS X + Xcode , (XCode 6.1.1 trên OS X Yosemite 10.10.1)
    • Windows + Visual Studio , (Visual Studio 2013 Update 4 trên Windows 7 SP1 64 bit)

Đối với những người lười biếng, chỉ cần bỏ qua phần cuối cùng của bài đăng này để sao chép nguồn. Dưới đây là lời giải chi tiết, hy vọng sẽ giúp ích và truyền cảm hứng cho tất cả những người đang tìm kiếm các __VA_ARGS__giải pháp chung như tôi. =)

Đây là cách nó diễn ra. Đầu tiên xác định người sử dụng có thể nhìn thấy quá tải "chức năng", tôi đặt tên cho nó create, và định nghĩa hàm thực tế liên quan realCreate, và các định nghĩa vĩ mô với số lượng khác nhau của các đối số CREATE_2, CREATE_1, CREATE_0, như hình dưới đây:

#define create(...) MACRO_CHOOSER(__VA_ARGS__)(__VA_ARGS__)

void realCreate(int x, int y)
{
  printf("(%d, %d)\n", x, y);
}

#define CREATE_2(x, y) realCreate(x, y)
#define CREATE_1(x) CREATE_2(x, 0)
#define CREATE_0() CREATE_1(0)

Phần MACRO_CHOOSER(__VA_ARGS__)cuối cùng giải quyết các tên định nghĩa macro và phần thứ hai (__VA_ARGS__)bao gồm danh sách tham số của chúng. Vì vậy, lời gọi của người dùng để create(10)giải quyết CREATE_1(10), CREATE_1một phần đến từ MACRO_CHOOSER(__VA_ARGS__)(10)một phần đến từ thứ hai (__VA_ARGS__).

Thủ thuật MACRO_CHOOSERsử dụng thủ thuật rằng, nếu __VA_ARGS__trống, biểu thức sau được nối thành một lệnh gọi macro hợp lệ bởi bộ tiền xử lý:

NO_ARG_EXPANDER __VA_ARGS__ ()  // simply shrinks to NO_ARG_EXPANDER()

Thật khéo léo, chúng ta có thể xác định lệnh gọi macro kết quả này là

#define NO_ARG_EXPANDER() ,,CREATE_0

Lưu ý hai dấu phẩy, chúng sẽ sớm được giải thích. Macro hữu ích tiếp theo là

#define MACRO_CHOOSER(...) CHOOSE_FROM_ARG_COUNT(NO_ARG_EXPANDER __VA_ARGS__ ())

vì vậy các cuộc gọi của

create();
create(10);
create(20, 20);

thực sự được mở rộng thành

CHOOSE_FROM_ARG_COUNT(,,CREATE_0)();
CHOOSE_FROM_ARG_COUNT(NO_ARG_EXPANDER 10 ())(10);
CHOOSE_FROM_ARG_COUNT(NO_ARG_EXPANDER 20, 20 ())(20, 20);

Như tên macro cho thấy, chúng ta sẽ đếm số lượng đối số sau đó. Đây là một thủ thuật khác: bộ tiền xử lý chỉ thực hiện thay thế văn bản đơn giản. Nó suy ra số lượng đối số của một lệnh gọi macro đơn thuần từ số dấu phẩy mà nó nhìn thấy bên trong dấu ngoặc đơn. Các "đối số" thực tế được phân tách bằng dấu phẩy không cần phải có cú pháp hợp lệ. Chúng có thể là bất kỳ văn bản nào. Có nghĩa là, trong ví dụ trên, NO_ARG_EXPANDER 10 ()được tính là 1 đối số cho lệnh gọi giữa. NO_ARG_EXPANDER 2020 ()được tính là 2 đối số cho lệnh gọi dưới cùng tương ứng.

Nếu chúng tôi sử dụng các macro trợ giúp sau để mở rộng thêm

##define CHOOSE_FROM_ARG_COUNT(...) \
  FUNC_RECOMPOSER((__VA_ARGS__, CREATE_2, CREATE_1, ))
#define FUNC_RECOMPOSER(argsWithParentheses) \
  FUNC_CHOOSER argsWithParentheses

Dấu ,sau CREATE_1là phần xử lý xung quanh cho GCC / CLANG, loại bỏ lỗi (dương tính giả) nói rằng ISO C99 requires rest arguments to be usedkhi chuyển -pedanticđến trình biên dịch của bạn. Đây FUNC_RECOMPOSERlà một công việc xung quanh cho MSVC, hoặc nó không thể đếm số đối số (nghĩa là, dấu phẩy) bên trong dấu ngoặc đơn của lệnh gọi macro một cách chính xác. Kết quả tiếp tục được giải quyết để

FUNC_CHOOSER (,,CREATE_0, CREATE_2, CREATE_1, )();
FUNC_CHOOSER (NO_ARG_EXPANDER 10 (), CREATE_2, CREATE_1, )(10);
FUNC_CHOOSER (NO_ARG_EXPANDER 20, 20 (), CREATE_2, CREATE_1, )(20, 20);

Như con mắt đại bàng mà bạn có thể đã thấy, bước cuối cùng chúng ta cần là sử dụng thủ thuật đếm đối số tiêu chuẩn để cuối cùng chọn tên phiên bản macro mong muốn:

#define FUNC_CHOOSER(_f1, _f2, _f3, ...) _f3

giải quyết kết quả thành

CREATE_0();
CREATE_1(10);
CREATE_2(20, 20);

và chắc chắn cung cấp cho chúng ta các lệnh gọi hàm thực tế, mong muốn:

realCreate(0, 0);
realCreate(10, 10);
realCreate(20, 20);

Tổng hợp tất cả lại với nhau, với một số sắp xếp lại các câu lệnh để dễ đọc hơn, toàn bộ nguồn của ví dụ 2 đối số là ở đây:

#include <stdio.h>

void realCreate(int x, int y)
{
  printf("(%d, %d)\n", x, y);
}

#define CREATE_2(x, y) realCreate(x, y)
#define CREATE_1(x) CREATE_2(x, 0)
#define CREATE_0() CREATE_1(0)

#define FUNC_CHOOSER(_f1, _f2, _f3, ...) _f3
#define FUNC_RECOMPOSER(argsWithParentheses) FUNC_CHOOSER argsWithParentheses
#define CHOOSE_FROM_ARG_COUNT(...) FUNC_RECOMPOSER((__VA_ARGS__, CREATE_2, CREATE_1, ))
#define NO_ARG_EXPANDER() ,,CREATE_0
#define MACRO_CHOOSER(...) CHOOSE_FROM_ARG_COUNT(NO_ARG_EXPANDER __VA_ARGS__ ())
#define create(...) MACRO_CHOOSER(__VA_ARGS__)(__VA_ARGS__)

int main()
{
  create();
  create(10);
  create(20, 20);
  //create(30, 30, 30);  // Compilation error
  return 0;
}

Mặc dù phức tạp, xấu xí, tạo gánh nặng cho nhà phát triển API, nhưng có một giải pháp để xử lý quá tải và thiết lập các tham số tùy chọn của các hàm C / C ++ cho những kẻ điên rồ. Việc sử dụng các API quá tải sắp ra mắt trở nên rất thú vị và dễ chịu. =)

Nếu có bất kỳ đơn giản hóa nào khác có thể có về phương pháp này, vui lòng cho tôi biết tại

https://github.com/jason-deng/C99FunctionOverload

Một lần nữa, xin gửi lời cảm ơn đặc biệt đến tất cả những con người xuất sắc đã truyền cảm hứng và dẫn dắt tôi đạt được tác phẩm này! =)


3
Làm thế nào để mở rộng điều này thành 3 hoặc 4 chức năng?
Phylliida

@Phylliida ideone.com/jD0Hm5 - không đến năm đối số được hỗ trợ.
xx

9

Đối với bất kỳ ai đang tìm kiếm một số giải pháp VA_NARGS hoạt động với Visual C ++. Macro sau đã làm việc cho tôi một cách hoàn hảo (cũng không có tham số nào!) Trong Visual c ++ express 2010:

#define VA_NUM_ARGS_IMPL(_1,_2,_3,_4,_5,_6,_7,_8,_9,_10,_11,_12,_13,_14,_15,_16,_17,_18,_19,_20,_21,_22,_23,_24,N,...) N
#define VA_NUM_ARGS_IMPL_(tuple) VA_NUM_ARGS_IMPL tuple
#define VA_NARGS(...)  bool(#__VA_ARGS__) ? (VA_NUM_ARGS_IMPL_((__VA_ARGS__, 24,23,22,21,20,19,18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1))) : 0

Nếu bạn muốn một macro với các tham số tùy chọn, bạn có thể thực hiện:

//macro selection(vc++)
#define SELMACRO_IMPL(_1,_2,_3, N,...) N
#define SELMACRO_IMPL_(tuple) SELMACRO_IMPL tuple
#define mymacro1(var1) var1
#define mymacro2(var1,var2) var2*var1
#define mymacro3(var1,var2,var3) var1*var2*var3
#define mymacro(...) SELMACRO_IMPL_((__VA_ARGS__, mymacro3(__VA_ARGS__), mymacro2(__VA_ARGS__), mymacro1(__VA_ARGS__))) 

Điều đó làm việc cho tôi cũng như trong vc. Nhưng nó không hoạt động đối với các tham số bằng không.

int x=99;
x=mymacro(2);//2
x=mymacro(2,2);//4
x=mymacro(2,2,2);//8

Tôi đang nhậnunresolved external symbol _bool referenced in function _main
Avidan Borisov

vâng điều đó có thể xảy ra trong một số trường hợp. bạn cần biết rằng bool (#__ VA_ARGS__)? khác với các macro khác vì nó đang được đánh giá tại thời điểm chạy. tùy thuộc vào trường hợp của bạn, bạn có thể bỏ qua phần đó của mã.
Syphorlate

2
Tôi thực sự đã kết thúc với pastebin.com/H3T75dcn hoạt động hoàn hảo (0 đối số quá).
Avidan Borisov

Cảm ơn vì liên kết và vâng, bạn cũng có thể làm điều đó bằng cách sử dụng sizeof nhưng đối với tôi điều đó không hoạt động trong một số trường hợp nhưng nguyên tắc thì giống nhau (đánh giá boolean).
Syphorlate

Bạn có thể đưa ra một số ví dụ mà nó không thành công?
Avidan Borisov

7

gcc/ g++hỗ trợ macro varargs nhưng tôi không nghĩ đây là tiêu chuẩn, vì vậy hãy tự chịu rủi ro khi sử dụng nó.


4
Chúng là tiêu chuẩn trong C99 và chúng cũng được thêm vào C ++ 0x.
greyfade

5
#include <stdio.h>

#define PP_NARG(...) \
    PP_NARG_(__VA_ARGS__,PP_RSEQ_N())
#define PP_NARG_(...) \
    PP_ARG_N(__VA_ARGS__)
#define PP_ARG_N( \
    _1, _2, _3, _4, _5, _6, _7, _8, _9,_10, \ 
    _11,_12,_13,_14,_15,_16,_17,_18,_19,_20, \
    _21,_22,_23,_24,_25,_26,_27,_28,_29,_30, \
    _31,_32,_33,_34,_35,_36,_37,_38,_39,_40, \
    _41,_42,_43,_44,_45,_46,_47,_48,_49,_50, \
    _51,_52,_53,_54,_55,_56,_57,_58,_59,_60, \
    _61,_62,_63,N,...) N
#define PP_RSEQ_N() \
    63,62,61,60,                   \
    59,58,57,56,55,54,53,52,51,50, \
    49,48,47,46,45,44,43,42,41,40, \
    39,38,37,36,35,34,33,32,31,30, \
    29,28,27,26,25,24,23,22,21,20, \
    19,18,17,16,15,14,13,12,11,10, \
    9,8,7,6,5,4,3,2,1,0

#define PP_CONCAT(a,b) PP_CONCAT_(a,b)
#define PP_CONCAT_(a,b) a ## b

#define THINK(...) PP_CONCAT(THINK_, PP_NARG(__VA_ARGS__))(__VA_ARGS__)
#define THINK_0() THINK_1("sector zz9 plural z alpha")
#define THINK_1(location) THINK_2(location, 42)
#define THINK_2(location,answer) THINK_3(location, answer, "deep thought")
#define THINK_3(location,answer,computer) \
  printf ("The answer is %d. This was calculated by %s, and a computer to figure out what this"
          " actually means will be build in %s\n", (answer), (computer), (location))

int
main (int argc, char *argv[])
{
  THINK (); /* On compilers other than GCC you have to call with least one non-default argument */
}

KHUYẾN CÁO: Hầu hết là vô hại.


có một lỗi trong mã của bạn. làm ơn làm :%s/MY_MACRO_/THINK_/g:)
João Portela

cũng có, nó không làm việc với zero lập luận sử dụng g ++i686-apple-darwin10-g++-4.2.1 (GCC) 4.2.1 (Apple Inc. build 5664)
João Portela

1
Không có đối số không tồn tại cho macro thay đổi, vì mã thông báo trống là trình giữ chỗ hợp lệ.
Paul Fultz II

3

Đó thực sự không phải là những gì bộ tiền xử lý được thiết kế.

Điều đó nói rằng, nếu bạn muốn tham gia vào lĩnh vực lập trình macro đầy thử thách nghiêm túc với khả năng dễ đọc, bạn nên xem qua thư viện bộ tiền xử lý Boost . Rốt cuộc, nó sẽ không phải là C ++ nếu không có ba cấp độ lập trình hoàn toàn tương thích với Turing (bộ xử lý tiền xử lý, lập trình siêu mẫu và C ++ cấp cơ sở)!


3
#define MY_MACRO_3(X,Y,Z) ...
#define MY_MACRO_2(X,Y) MY_MACRO(X,Y,5)
#define MY_MACRO_1(X) MY_MACRO(X,42,5)

Bạn biết tại thời điểm gọi có bao nhiêu args bạn sẽ chuyển vào nên thực sự không cần quá tải.


2
Tôi thực sự đang hỏi về sự tồn tại của tính năng này.
Cenoc

3

Phiên bản ngắn gọn hơn của mã Derek Ledbetter:

enum
{
    plain = 0,
    bold = 1,
    italic = 2
};


void PrintString(const char* message = NULL, int size = 0, int style = 0)
{
}


#define PRINT_STRING(...) PrintString(__VA_ARGS__)


int main(int argc, char * const argv[])
{ 
    PRINT_STRING("Hello, World!");
    PRINT_STRING("Hello, World!", 18);
    PRINT_STRING("Hello, World!", 18, bold);

    return 0;
}

3

Là một fan cuồng của những con quái vật vĩ mô khủng khiếp, tôi muốn mở rộng câu trả lời của Jason Deng và biến nó thực sự có thể sử dụng được. (Tốt hơn hoặc tệ hơn.) Bản gốc không được tốt để sử dụng vì bạn cần phải sửa đổi súp bảng chữ cái lớn mỗi khi bạn muốn tạo một macro mới và thậm chí còn tệ hơn nếu bạn cần số lượng đối số khác nhau.

Vì vậy, tôi đã tạo một phiên bản với các tính năng sau:

  • 0 trường hợp đối số hoạt động
  • 1 đến 16 đối số mà không có bất kỳ sửa đổi nào đối với phần lộn xộn
  • Dễ dàng viết thêm các hàm macro
  • Đã thử nghiệm trong gcc 10, clang 9, Visual Studio 2017

Hiện tại, tôi chỉ tạo tối đa 16 đối số, nhưng nếu bạn cần nhiều hơn (thực sự bây giờ? Bạn chỉ thấy ngớ ngẩn ...), bạn có thể chỉnh sửa FUNC_CHOOSER và CHOOSE_FROM_ARG_COUNT, sau đó thêm một số dấu phẩy vào NO_ARG_EXPANDER.

Vui lòng xem câu trả lời xuất sắc của Jason Deng để biết thêm chi tiết về cách triển khai, nhưng tôi sẽ chỉ đặt mã ở đây:

#include <stdio.h>

void realCreate(int x, int y)
{
    printf("(%d, %d)\n", x, y);
}

// This part you put in some library header:
#define FUNC_CHOOSER(_f0, _f1, _f2, _f3, _f4, _f5, _f6, _f7, _f8, _f9, _f10, _f11, _f12, _f13, _f14, _f15, _f16, ...) _f16
#define FUNC_RECOMPOSER(argsWithParentheses) FUNC_CHOOSER argsWithParentheses
#define CHOOSE_FROM_ARG_COUNT(F, ...) FUNC_RECOMPOSER((__VA_ARGS__, \
            F##_16, F##_15, F##_14, F##_13, F##_12, F##_11, F##_10, F##_9, F##_8,\
            F##_7, F##_6, F##_5, F##_4, F##_3, F##_2, F##_1, ))
#define NO_ARG_EXPANDER(FUNC) ,,,,,,,,,,,,,,,,FUNC ## _0
#define MACRO_CHOOSER(FUNC, ...) CHOOSE_FROM_ARG_COUNT(FUNC, NO_ARG_EXPANDER __VA_ARGS__ (FUNC))
#define MULTI_MACRO(FUNC, ...) MACRO_CHOOSER(FUNC, __VA_ARGS__)(__VA_ARGS__)

// When you need to make a macro with default arguments, use this:
#define create(...) MULTI_MACRO(CREATE, __VA_ARGS__)
#define CREATE_0() CREATE_1(0)
#define CREATE_1(x) CREATE_2(x, 0)
#define CREATE_2(x, y) \
    do { \
        /* put whatever code you want in the last macro */ \
        realCreate(x, y); \
    } while(0)


int main()
{
    create();
    create(10);
    create(20, 20);
    //create(30, 30, 30);  // Compilation error
    return 0;
}

2

Bạn có thể sử dụng BOOST_PP_OVERLOADtừ boostthư viện.

Ví dụ từ tài liệu tăng cường chính thức :

#include <boost/preprocessor/facilities/overload.hpp>
#include <boost/preprocessor/cat.hpp>
#include <boost/preprocessor/facilities/empty.hpp>
#include <boost/preprocessor/arithmetic/add.hpp>

#define MACRO_1(number) MACRO_2(number,10)
#define MACRO_2(number1,number2) BOOST_PP_ADD(number1,number2)

#if !BOOST_PP_VARIADICS_MSVC

#define MACRO_ADD_NUMBERS(...) BOOST_PP_OVERLOAD(MACRO_,__VA_ARGS__)(__VA_ARGS__)

#else

// or for Visual C++

#define MACRO_ADD_NUMBERS(...) \
  BOOST_PP_CAT(BOOST_PP_OVERLOAD(MACRO_,__VA_ARGS__)(__VA_ARGS__),BOOST_PP_EMPTY())

#endif

MACRO_ADD_NUMBERS(5) // output is 15
MACRO_ADD_NUMBERS(3,6) // output is 9

0

Tùy thuộc vào những gì bạn cần, bạn có thể làm điều đó với var args với macro. Bây giờ, các tham số tùy chọn hoặc quá tải macro, không có điều đó.


-1

Không có ví dụ nào ở trên (từ Derek Ledbetter, David Sorkovsky và Joe D) để đếm các đối số bằng macro phù hợp với tôi khi sử dụng Microsoft VCC 10. Đối __VA_ARGS__số luôn được coi là một đối số duy nhất (mã hóa nó có ##hoặc không), vì vậy đối số thay đổi trong đó những ví dụ dựa vào không hoạt động.

Vì vậy, câu trả lời ngắn gọn, như nhiều người khác đã nêu ở trên: không, bạn không thể quá tải macro hoặc sử dụng các đối số tùy chọn trên chúng.


1
Bạn có thể, nhưng chỉ trong C99 hoặc C ++ 11 (do có __VA_ARGS__). VC2010 là C89 / C ++ 03 (với một số bit của C ++ 11 bắt đầu xuất hiện, nhưng chưa phải là bit đó).
puetzk
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.