[英]Overloading Macro on Number of Arguments
我有兩個宏FOO2
和FOO3
:
#define FOO2(x,y) ...
#define FOO3(x,y,z) ...
我想定義一個新的宏FOO
如下:
#define FOO(x,y) FOO2(x,y)
#define FOO(x,y,z) FOO3(x,y,z)
但這不起作用,因為宏不會在參數數量上超載。
在不修改FOO2
和FOO3
的情況下,是否有某種方法可以定義宏FOO
(使用__VA_ARGS__
或其他方式)以獲得將FOO(x,y)
調度到FOO2
和FOO(x,y,z)
調度到FOO3
的相同效果?
簡單如:
#define GET_MACRO(_1,_2,_3,NAME,...) NAME
#define FOO(...) GET_MACRO(__VA_ARGS__, FOO3, FOO2)(__VA_ARGS__)
因此,如果您有這些宏,它們會按照描述進行擴展:
FOO(World, !) // expands to FOO2(World, !)
FOO(foo,bar,baz) // expands to FOO3(foo,bar,baz)
如果你想要第四個:
#define GET_MACRO(_1,_2,_3,_4,NAME,...) NAME
#define FOO(...) GET_MACRO(__VA_ARGS__, FOO4, FOO3, FOO2)(__VA_ARGS__)
FOO(a,b,c,d) // expands to FOO4(a,b,c,d)
自然,如果您定義FOO2
、 FOO3
和FOO4
,則輸出將被定義的宏替換。
要添加到netcoder 的答案,您實際上可以在 GCC ##__VA_ARGS__
擴展的幫助下使用 0 參數宏來執行此操作:
#define GET_MACRO(_0, _1, _2, NAME, ...) NAME
#define FOO(...) GET_MACRO(_0, ##__VA_ARGS__, FOO2, FOO1, FOO0)(__VA_ARGS__)
這是一個更通用的解決方案:
// get number of arguments with __NARG__
#define __NARG__(...) __NARG_I_(__VA_ARGS__,__RSEQ_N())
#define __NARG_I_(...) __ARG_N(__VA_ARGS__)
#define __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 __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
// general definition for any function name
#define _VFUNC_(name, n) name##n
#define _VFUNC(name, n) _VFUNC_(name, n)
#define VFUNC(func, ...) _VFUNC(func, __NARG__(__VA_ARGS__)) (__VA_ARGS__)
// definition for FOO
#define FOO(...) VFUNC(FOO, __VA_ARGS__)
定義你的功能:
#define FOO2(x, y) ((x) + (y))
#define FOO3(x, y, z) ((x) + (y) + (z))
// it also works with C functions:
int FOO4(int a, int b, int c, int d) { return a + b + c + d; }
現在您可以將FOO
與 2、3 和 4 個參數一起使用:
FOO(42, 42) // will use makro function FOO2
FOO(42, 42, 42) // will use makro function FOO3
FOO(42, 42, 42, 42) // will call FOO4 function
將其用於默認參數:
#define func(...) VFUNC(func, __VA_ARGS__)
#define func2(a, b) func4(a, b, NULL, NULL)
#define func3(a, b, c) func4(a, b, c, NULL)
// real function:
int func4(int a, int b, void* c, void* d) { /* ... */ }
將它用於可能具有無限個參數的函數:
#define SUM(...) VFUNC(SUM, __VA_ARGS__)
#define SUM2(a, b) ((a) + (b))
#define SUM3(a, b, c) ((a) + (b) + (c))
#define SUM4(a, b, c) ((a) + (b) + (c) + (d))
// ...
PS: __NARG__
是從 Laurent Deniau 和 Roland Illig 復制的: https ://groups.google.com/group/comp.std.c/browse_thread/thread/77ee8c8f92e4a3fb/346fc464319b1ee5?pli=1
我自己只是在研究這個,我在這里遇到了這個。 作者通過宏添加了對 C 函數的默認參數支持。
我將嘗試簡要總結這篇文章。 基本上,您需要定義一個可以計算參數的宏。 此宏將返回 2、1、0 或它可以支持的任何參數范圍。 例如:
#define _ARG2(_0, _1, _2, ...) _2
#define NARG2(...) _ARG2(__VA_ARGS__, 2, 1, 0)
有了這個,您需要創建另一個宏,它接受可變數量的參數、計算參數並調用適當的宏。 我采用了您的示例宏並將其與文章的示例相結合。 我有 FOO1 調用函數 a() 和 FOO2 調用函數 a 和參數 b (顯然,我在這里假設 C++,但你可以將宏更改為任何內容)。
#define FOO1(a) a();
#define FOO2(a,b) a(b);
#define _ARG2(_0, _1, _2, ...) _2
#define NARG2(...) _ARG2(__VA_ARGS__, 2, 1, 0)
#define _ONE_OR_TWO_ARGS_1(a) FOO1(a)
#define _ONE_OR_TWO_ARGS_2(a, b) FOO2(a,b)
#define __ONE_OR_TWO_ARGS(N, ...) _ONE_OR_TWO_ARGS_ ## N (__VA_ARGS__)
#define _ONE_OR_TWO_ARGS(N, ...) __ONE_OR_TWO_ARGS(N, __VA_ARGS__)
#define FOO(...) _ONE_OR_TWO_ARGS(NARG2(__VA_ARGS__), __VA_ARGS__)
所以如果你有
FOO(a)
FOO(a,b)
預處理器將其擴展為
a();
a(b);
我肯定會閱讀我鏈接的文章。 這是非常有用的,他提到 NARG2 不適用於空參數。 他在這里跟進。
這是上述答案的更緊湊版本。 舉個例子。
#include <iostream>
using namespace std;
#define OVERLOADED_MACRO(M, ...) _OVR(M, _COUNT_ARGS(__VA_ARGS__)) (__VA_ARGS__)
#define _OVR(macroName, number_of_args) _OVR_EXPAND(macroName, number_of_args)
#define _OVR_EXPAND(macroName, number_of_args) macroName##number_of_args
#define _COUNT_ARGS(...) _ARG_PATTERN_MATCH(__VA_ARGS__, 9,8,7,6,5,4,3,2,1)
#define _ARG_PATTERN_MATCH(_1,_2,_3,_4,_5,_6,_7,_8,_9, N, ...) N
//Example:
#define ff(...) OVERLOADED_MACRO(ff, __VA_ARGS__)
#define ii(...) OVERLOADED_MACRO(ii, __VA_ARGS__)
#define ff3(c, a, b) for (int c = int(a); c < int(b); ++c)
#define ff2(c, b) ff3(c, 0, b)
#define ii2(a, b) ff3(i, a, b)
#define ii1(n) ii2(0, n)
int main() {
ff (counter, 3, 5)
cout << "counter = " << counter << endl;
ff (abc, 4)
cout << "abc = " << abc << endl;
ii (3)
cout << "i = " << i << endl;
ii (100, 103)
cout << "i = " << i << endl;
return 0;
}
跑:
User@Table 13:06:16 /c/T
$ g++ test_overloaded_macros.cpp
User@Table 13:16:26 /c/T
$ ./a.exe
counter = 3
counter = 4
abc = 0
abc = 1
abc = 2
abc = 3
i = 0
i = 1
i = 2
i = 100
i = 101
i = 102
請注意,同時擁有_OVR
和_OVR_EXPAND
可能看起來多余,但預處理器有必要擴展_COUNT_ARGS(__VA_ARGS__)
部分,否則將被視為字符串。
這是從 Evgeni Sergeev 的回答中衍生出來的。 這個也支持零參數重載!
我用 GCC 和 MinGW 對此進行了測試。 它應該適用於新舊版本的 C++。 請注意,我不能保證它適用於 MSVC ......但是通過一些調整,我相信它也可以與它一起使用。
我還將其格式化為粘貼到頭文件(我稱之為macroutil.h)中。 如果你這樣做了,你可以在任何你需要的特性中包含這個標題,而不是查看實現中所涉及的骯臟。
#ifndef MACROUTIL_H
#define MACROUTIL_H
//-----------------------------------------------------------------------------
// OVERLOADED_MACRO
//
// used to create other macros with overloaded argument lists
//
// Example Use:
// #define myMacro(...) OVERLOADED_MACRO( myMacro, __VA_ARGS__ )
// #define myMacro0() someFunc()
// #define myMacro1( arg1 ) someFunc( arg1 )
// #define myMacro2( arg1, arg2 ) someFunc( arg1, arg2 )
//
// myMacro();
// myMacro(1);
// myMacro(1,2);
//
// Note the numerical suffix on the macro names,
// which indicates the number of arguments.
// That is the REQUIRED naming convention for your macros.
//
//-----------------------------------------------------------------------------
// OVERLOADED_MACRO
// derived from: https://stackoverflow.com/questions/11761703/overloading-macro-on-number-of-arguments
// replaced use of _COUNT_ARGS macro with VA_NUM_ARGS defined below
// to support of zero argument overloads
#define OVERLOADED_MACRO(M, ...) _OVR(M, VA_NUM_ARGS(__VA_ARGS__)) (__VA_ARGS__)
#define _OVR(macroName, number_of_args) _OVR_EXPAND(macroName, number_of_args)
#define _OVR_EXPAND(macroName, number_of_args) macroName##number_of_args
//#define _COUNT_ARGS(...) _ARG_PATTERN_MATCH(__VA_ARGS__, 15,14,13,12,11,10,9,8,7,6,5,4,3,2,1)
#define _ARG_PATTERN_MATCH(_1,_2,_3,_4,_5,_6,_7,_8,_9,_10,_11,_12,_13,_14,_15, N, ...) N
// VA_NUM_ARGS
// copied from comments section of:
// http://efesx.com/2010/07/17/variadic-macro-to-count-number-of-arguments/
// which itself was derived from:
// https://gustedt.wordpress.com/2010/06/08/detect-empty-macro-arguments/
#define _ARG16(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, ...) _15
#define HAS_COMMA(...) _ARG16(__VA_ARGS__, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0)
#define HAS_NO_COMMA(...) _ARG16(__VA_ARGS__, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1)
#define _TRIGGER_PARENTHESIS_(...) ,
#define HAS_ZERO_OR_ONE_ARGS(...) \
_HAS_ZERO_OR_ONE_ARGS( \
/* test if there is just one argument, eventually an empty one */ \
HAS_COMMA(__VA_ARGS__), \
/* test if _TRIGGER_PARENTHESIS_ together with the argument adds a comma */ \
HAS_COMMA(_TRIGGER_PARENTHESIS_ __VA_ARGS__), \
/* test if the argument together with a parenthesis adds a comma */ \
HAS_COMMA(__VA_ARGS__ (~)), \
/* test if placing it between _TRIGGER_PARENTHESIS_ and the parenthesis adds a comma */ \
HAS_COMMA(_TRIGGER_PARENTHESIS_ __VA_ARGS__ (~)) \
)
#define PASTE5(_0, _1, _2, _3, _4) _0 ## _1 ## _2 ## _3 ## _4
#define _HAS_ZERO_OR_ONE_ARGS(_0, _1, _2, _3) HAS_NO_COMMA(PASTE5(_IS_EMPTY_CASE_, _0, _1, _2, _3))
#define _IS_EMPTY_CASE_0001 ,
#define _VA0(...) HAS_ZERO_OR_ONE_ARGS(__VA_ARGS__)
#define _VA1(...) HAS_ZERO_OR_ONE_ARGS(__VA_ARGS__)
#define _VA2(...) 2
#define _VA3(...) 3
#define _VA4(...) 4
#define _VA5(...) 5
#define _VA6(...) 6
#define _VA7(...) 7
#define _VA8(...) 8
#define _VA9(...) 9
#define _VA10(...) 10
#define _VA11(...) 11
#define _VA12(...) 12
#define _VA13(...) 13
#define _VA14(...) 14
#define _VA15(...) 15
#define _VA16(...) 16
#define VA_NUM_ARGS(...) VA_NUM_ARGS_IMPL(__VA_ARGS__, PP_RSEQ_N(__VA_ARGS__) )
#define VA_NUM_ARGS_IMPL(...) VA_NUM_ARGS_N(__VA_ARGS__)
#define VA_NUM_ARGS_N( \
_1, _2, _3, _4, _5, _6, _7, _8, _9,_10, \
_11,_12,_13,_14,_15,_16,N,...) N
#define PP_RSEQ_N(...) \
_VA16(__VA_ARGS__),_VA15(__VA_ARGS__),_VA14(__VA_ARGS__),_VA13(__VA_ARGS__), \
_VA12(__VA_ARGS__),_VA11(__VA_ARGS__),_VA10(__VA_ARGS__), _VA9(__VA_ARGS__), \
_VA8(__VA_ARGS__),_VA7(__VA_ARGS__),_VA6(__VA_ARGS__),_VA5(__VA_ARGS__), \
_VA4(__VA_ARGS__),_VA3(__VA_ARGS__),_VA2(__VA_ARGS__),_VA1(__VA_ARGS__), \
_VA0(__VA_ARGS__)
//-----------------------------------------------------------------------------
#endif // MACROUTIL_H
也許你可以使用這個宏來計算參數的數量。
#define VA_NUM_ARGS(...) VA_NUM_ARGS_IMPL(__VA_ARGS__, 5,4,3,2,1)
#define VA_NUM_ARGS_IMPL(_1,_2,_3,_4,_5,N,...) N
這似乎在 GCC、Clang 和 MSVC 上運行良好。 這是這里一些答案的清理版本
#define _my_BUGFX(x) x
#define _my_NARG2(...) _my_BUGFX(_my_NARG1(__VA_ARGS__,_my_RSEQN()))
#define _my_NARG1(...) _my_BUGFX(_my_ARGSN(__VA_ARGS__))
#define _my_ARGSN(_1,_2,_3,_4,_5,_6,_7,_8,_9,_10,N,...) N
#define _my_RSEQN() 10,9,8,7,6,5,4,3,2,1,0
#define _my_FUNC2(name,n) name ## n
#define _my_FUNC1(name,n) _my_FUNC2(name,n)
#define GET_MACRO(func,...) _my_FUNC1(func,_my_BUGFX(_my_NARG2(__VA_ARGS__))) (__VA_ARGS__)
#define FOO(...) GET_MACRO(FOO,__VA_ARGS__)
根據@netcoder 的回答和@vexe關於Visual Studio 編譯器支持的建議,我發現這段代碼在各種平台上運行良好:
#define FOO1(a) func1(a)
#define FOO2(a, b) func2(a, b)
#define FOO3(a, b, c) func3(a, b, c)
#define EXPAND(x) x
#define GET_MACRO(_1, _2, _3, NAME, ...) NAME
#define FOO(...) EXPAND(GET_MACRO(__VA_ARGS__, FOO3, FOO2, FOO1)(__VA_ARGS__))
,其中func1()
、 func2()
、 func3()
只是接受不同數量參數的普通函數。
我有兩個宏FOO2
和FOO3
:
#define FOO2(x,y) ...
#define FOO3(x,y,z) ...
我想定義一個新的宏FOO
,如下所示:
#define FOO(x,y) FOO2(x,y)
#define FOO(x,y,z) FOO3(x,y,z)
但這是行不通的,因為宏不會在參數數量上重載。
在不修改FOO2
和FOO3
,是否有某種方法可以定義宏FOO
(使用__VA_ARGS__
或其他方式),以獲得與將FOO(x,y)
發給FOO2
以及將FOO(x,y,z)
發給FOO3
?
根據 R1tschY 的回復,這是我使用Deniau-Illig 構造實現變量參數和默認參數的方法。
// ----------------------------------------------------------------
// library
#define __nargs100__(a00,a01,a02,a03,a04,a05,a06,a07,a08,a09,a0a,a0b,a0c,a0d,a0e,a0f,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a1a,a1b,a1c,a1d,a1e,a1f,a20,a21,a22,a23,a24,a25,a26,a27,a28,a29,a2a,a2b,a2c,a2d,a2e,a2f,a30,a31,a32,a33,a34,a35,a36,a37,a38,a39,a3a,a3b,a3c,a3d,a3e,a3f,a40,a41,a42,a43,a44,a45,a46,a47,a48,a49,a4a,a4b,a4c,a4d,a4e,a4f,a50,a51,a52,a53,a54,a55,a56,a57,a58,a59,a5a,a5b,a5c,a5d,a5e,a5f,a60,a61,a62,a63,a64,a65,a66,a67,a68,a69,a6a,a6b,a6c,a6d,a6e,a6f,a70,a71,a72,a73,a74,a75,a76,a77,a78,a79,a7a,a7b,a7c,a7d,a7e,a7f,a80,a81,a82,a83,a84,a85,a86,a87,a88,a89,a8a,a8b,a8c,a8d,a8e,a8f,a90,a91,a92,a93,a94,a95,a96,a97,a98,a99,a9a,a9b,a9c,a9d,a9e,a9f,aa0,aa1,aa2,aa3,aa4,aa5,aa6,aa7,aa8,aa9,aaa,aab,aac,aad,aae,aaf,ab0,ab1,ab2,ab3,ab4,ab5,ab6,ab7,ab8,ab9,aba,abb,abc,abd,abe,abf,ac0,ac1,ac2,ac3,ac4,ac5,ac6,ac7,ac8,ac9,aca,acb,acc,acd,ace,acf,ad0,ad1,ad2,ad3,ad4,ad5,ad6,ad7,ad8,ad9,ada,adb,adc,add,ade,adf,ae0,ae1,ae2,ae3,ae4,ae5,ae6,ae7,ae8,ae9,aea,aeb,aec,aed,aee,aef,af0,af1,af2,af3,af4,af5,af6,af7,af8,af9,afa,afb,afc,afd,afe,aff,a100,...) a100
#define __nargs__(...) __nargs100__(,##__VA_ARGS__, ff,fe,fd,fc,fb,fa,f9,f8,f7,f6,f5,f4,f3,f2,f1,f0,ef,ee,ed,ec,eb,ea,e9,e8,e7,e6,e5,e4,e3,e2,e1,e0,df,de,dd,dc,db,da,d9,d8,d7,d6,d5,d4,d3,d2,d1,d0,cf,ce,cd,cc,cb,ca,c9,c8,c7,c6,c5,c4,c3,c2,c1,c0,bf,be,bd,bc,bb,ba,b9,b8,b7,b6,b5,b4,b3,b2,b1,b0,af,ae,ad,ac,ab,aa,a9,a8,a7,a6,a5,a4,a3,a2,a1,a0,9f,9e,9d,9c,9b,9a,99,98,97,96,95,94,93,92,91,90,8f,8e,8d,8c,8b,8a,89,88,87,86,85,84,83,82,81,80,7f,7e,7d,7c,7b,7a,79,78,77,76,75,74,73,72,71,70,6f,6e,6d,6c,6b,6a,69,68,67,66,65,64,63,62,61,60,5f,5e,5d,5c,5b,5a,59,58,57,56,55,54,53,52,51,50,4f,4e,4d,4c,4b,4a,49,48,47,46,45,44,43,42,41,40,3f,3e,3d,3c,3b,3a,39,38,37,36,35,34,33,32,31,30,2f,2e,2d,2c,2b,2a,29,28,27,26,25,24,23,22,21,20,1f,1e,1d,1c,1b,1a,19,18,17,16,15,14,13,12,11,10,f,e,d,c,b,a,9,8,7,6,5,4,3,2,1,0)
#define __vfn(name, n) name##n
#define _vfn( name, n) __vfn(name, n)
#define vfn( fn, ...) _vfn(fn, __nargs__(__VA_ARGS__))(__VA_ARGS__)
// ----------------------------------------------------------------
// example
// backend: actual implementation, 2 mandatory args, 3 optional args
#define _bshow(bdim,data, ncols,nbits,base)({ \
/* do stuff here */ \
})
// "frontend", default arguments get implemented here. the suffix is the number of arguments, in hexadecimal base
#define bshow2(...) _bshow(__VA_ARGS__, 16,32,16)
#define bshow3(...) _bshow(__VA_ARGS__, 32,16)
#define bshow4(...) _bshow(__VA_ARGS__, 16)
#define bshow5(...) _bshow(__VA_ARGS__)
#define bshow(...) vfn(bshow,__VA_ARGS__)
// test
bshow(0x100,data0);
bshow(0x100,data0, 14);
bshow(0x100,data0, 12,16);
bshow(0x100,data0, 10, 8,2);
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.