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.
No you can't
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.
No you can't
Câu trả lời:
Đâ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.
PRINT_STRING_MACRO_CHOOSER
thậ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?
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)!
XXX_X(,##__VA_ARGS__,` ...
XXX_X (, XXX_4 (), XXX_3 (), XXX_2 (), XXX_1 (), XXX_0 ()); '
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.
__FILE__
và __LINE__
và như vậy ...
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
, ##__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=c99
cho trình biên dịch của bạn nếu bạn muốn =)Hoạt động hợp lý trên nhiều nền tảng , ít nhất đã được thử nghiệm
Đố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_1
một phần đến từ MACRO_CHOOSER(__VA_ARGS__)
và (10)
một phần đến từ thứ hai (__VA_ARGS__)
.
Thủ thuật MACRO_CHOOSER
sử 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 20
và 20 ()
đượ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_1
là 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 used
khi chuyển -pedantic
đến trình biên dịch của bạn. Đây FUNC_RECOMPOSER
là 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! =)
Đố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
unresolved external symbol _bool referenced in function _main
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ó.
#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.
:%s/MY_MACRO_/THINK_/g
:)
i686-apple-darwin10-g++-4.2.1 (GCC) 4.2.1 (Apple Inc. build 5664)
Đó 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ở)!
#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.
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;
}
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:
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;
}
Bạn có thể sử dụng BOOST_PP_OVERLOAD
từ boost
thư 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
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.