c - va_args - define## args




宏來計算參數的數量 (3)

使用## VA_ARGS擴展名可以在GCC中執行:

#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

/* Note 63 is removed */
#define PP_RSEQ_N()                                        \
         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_NARG_(...)    PP_ARG_N(__VA_ARGS__)    

/* Note dummy first argument _ and ##__VA_ARGS__ instead of __VA_ARGS__ */
#define PP_NARG(...)     PP_NARG_(_, ##__VA_ARGS__, PP_RSEQ_N())

#define my_func(...)     func(PP_NARG(__VA_ARGS__), __VA_ARGS__)

現在PP_NARG(a, b, c)給出3, PP_NARG()給出0。

不幸的是,我看不到一種使其工作的方法。

我有一個來自第三方C庫的可變參數函數:

int func(int argc, ...);

argc表示傳遞的可選參數的個數。 正如這裡所建議的那樣,我用一個包含參數個數的宏包裝它。 為了方便閱讀,下面是這個宏:

#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_NARG_(...)    PP_ARG_N(__VA_ARGS__)    

#define PP_NARG(...)     PP_NARG_(__VA_ARGS__, PP_RSEQ_N())

我就像這樣包裝它:

#define my_func(...)     func(PP_NARG(__VA_ARGS__), __VA_ARGS__)

PP_NARG宏適用於接受一個或多個參數的函數。 例如, PP_NARG("Hello", "World")計算結果為2

問題是沒有參數傳遞時, PP_NARG()計算為1而不是0
我理解這個宏是如何工作的 ,但是我不能想出一個修改它的方法,以使它在這種情況下也能正確運行。

有任何想法嗎?

編輯
我找到了PP_NARG的解決方法,並將其作為答案發布。
我仍然有包裝可變參數的問題。 當__VA_ARGS__為空時, my_func擴展為func(0, ) ,這會觸發編譯錯誤。


另一種不使用sizeof和GCC擴展的方法是在代碼中添加以下內容

#define PP_COMMASEQ_N()                                    \
          1,  1,  1,  1,                                   \
          1,  1,  1,  1,  1,  1,  1,  1,  1,  1,           \
          1,  1,  1,  1,  1,  1,  1,  1,  1,  1,           \
          1,  1,  1,  1,  1,  1,  1,  1,  1,  1,           \
          1,  1,  1,  1,  1,  1,  1,  1,  1,  1,           \
          1,  1,  1,  1,  1,  1,  1,  1,  1,  1,           \
          1,  1,  1,  1,  1,  1,  1,  1,  0,  0

#define PP_COMMA(...)    ,

#define PP_HASCOMMA(...)                                   \
          PP_NARG_(__VA_ARGS__, PP_COMMASEQ_N())

#define PP_NARG(...)                                       \
          PP_NARG_HELPER1(                                 \
              PP_HASCOMMA(__VA_ARGS__),                    \
              PP_HASCOMMA(PP_COMMA __VA_ARGS__ ()),        \
              PP_NARG_(__VA_ARGS__, PP_RSEQ_N()))

#define PP_NARG_HELPER1(a, b, N)    PP_NARG_HELPER2(a, b, N)
#define PP_NARG_HELPER2(a, b, N)    PP_NARG_HELPER3_ ## a ## b(N)
#define PP_NARG_HELPER3_01(N)    0
#define PP_NARG_HELPER3_00(N)    1
#define PP_NARG_HELPER3_11(N)    N

結果是

PP_NARG()       // expands to 0
PP_NARG(x)      // expands to 1
PP_NARG(x, 2)   // expands to 2

說明:

這些宏中的訣竅在於,當使用零個或一個參數調用PP_HASCOMMA(...)時, PP_HASCOMMA(...)將擴展為0,當使用至少兩個參數調用時, PP_HASCOMMA(...)將擴展為0。 為了區分這兩種情況,我使用了PP_COMMA __VA_ARGS__ () ,當__VA_ARGS__為空時返回一個逗號,當__VA_ARGS__非空時返回任何__VA_ARGS__

現在有三種可能的情況:

  1. __VA_ARGS__為空: PP_HASCOMMA(__VA_ARGS__)返回0, PP_HASCOMMA(PP_COMMA __VA_ARGS__ ())返回1。

  2. __VA_ARGS__包含一個參數: PP_HASCOMMA(__VA_ARGS__)返回0, PP_HASCOMMA(PP_COMMA __VA_ARGS__ ())返回0。

  3. __VA_ARGS__包含兩個或多個參數: PP_HASCOMMA(__VA_ARGS__)返回1, PP_HASCOMMA(PP_COMMA __VA_ARGS__ ())返回1。

只需要PP_NARG_HELPERx宏來解決這些情況。

編輯:

為了解決func(0, )問題,我們需要測試我們是否提供了零個或多個參數。 PP_ISZERO宏在這裡發揮作用。

#define PP_ISZERO(x)    PP_HASCOMMA(PP_ISZERO_HELPER_ ## x)
#define PP_ISZERO_HELPER_0    ,

現在讓我們定義另一個宏,它將參數個數前置到參數列表中:

#define PP_PREPEND_NARG(...)                               \
          PP_PREPEND_NARG_HELPER1(PP_NARG(__VA_ARGS__), __VA_ARGS__)
#define PP_PREPEND_NARG_HELPER1(N, ...)                    \
          PP_PREPEND_NARG_HELPER2(PP_ISZERO(N), N, __VA_ARGS__)
#define PP_PREPEND_NARG_HELPER2(z, N, ...)                 \
          PP_PREPEND_NARG_HELPER3(z, N, __VA_ARGS__)
#define PP_PREPEND_NARG_HELPER3(z, N, ...)                 \
          PP_PREPEND_NARG_HELPER4_ ## z (N, __VA_ARGS__)
#define PP_PREPEND_NARG_HELPER4_1(N, ...)  0
#define PP_PREPEND_NARG_HELPER4_0(N, ...)  N, __VA_ARGS__

再次需要許多助手來將宏展開為數字值。 最後測試一下:

#define my_func(...)  func(PP_PREPEND_NARG(__VA_ARGS__))

my_func()          // expands to func(0)
my_func(x)         // expands to func(1, x)
my_func(x, y)      // expands to func(2, x, y)
my_func(x, y, z)   // expands to func(3, x, y, z)

在線示例:

http://coliru.stacked-crooked.com/a/73b4b6d75d45a1c8

也可以看看:

請看看P99項目,它有更先進的預處理器解決方案。


關於Mehrwolf答案的完整例子不幸的是不能在VS2010上編譯,也不能在VS2015上編譯(我也試過這個在線VS編譯器)。 在這個問題上的另一種方法,可以生成在各種編譯器上編譯的跨平台代碼,就是使用MS編譯器中可用的非標準擴展,就像我在回答類似的問題時一樣。 同樣,引用的答案也不同PP_NARG((a, b, c))使用擴展(在MS和gcc / clang編譯器中)的PP_NARG((a, b, c))的結果是1而不是0。





variadic