CWKSC's blog | 博客

判斷參數數目

Featured image

目錄: C/C++ Macro 宏系列 | CWKSC’s blog | 博客

來個簡單應用:判斷參數數目

▌選取第 n 項參數的宏

首先需要一個 選取第 n 項參數的宏

#define get1th(a1, ...) a1
#define get2th(a1, a2, ...) a2
#define get3th(a1, a2, a3, ...) a3
#define get4th(a1, a2, a3, a4, ...) a4
#define get5th(a1, a2, a3, a4, a5, ...) a5
// ... getnth(a1, a2, a3, ... , an, ...) an

很容易理解

get1th(a, b, c, d, e)
// a
get2th(a, b, c, d, e)
// b
get3th(a, b, c, d, e)
// c

Visual Studio 在處理 __VA_ARGS__ 時會視為一個整體的 Token 來處理

我們需要製作一個新的版本專門給 VS

後記:Visual Studio 更新了

重掃描可以解決這個問題

我們先定義中間層,也就是 重掃描 那篇的 expend(...),不過換個名字

#define midLayer(...) __VA_ARGS__  // 中間層

然後是針對 VS 的新版本:

#define get1thVS(...) midLayer( get1th(__VA_ARGS__) )
#define get2thVS(...) midLayer( get2th(__VA_ARGS__) )
#define get3thVS(...) midLayer( get3th(__VA_ARGS__) )
#define get4thVS(...) midLayer( get4th(__VA_ARGS__) )
#define get5thVS(...) midLayer( get5th(__VA_ARGS__) )
// ... getnthVS(a1, a2, a3, ... , an, ...) midLayer( getnth(__VA_ARGS__) )

效果相同:

get3thVS(a, b, c, d, e)

// #define get3thVS(...) midLayer( get3th(__VA_ARGS__) )
midLayer( get3th(__VA_ARGS__) )
midLayer( get3th(a, b, c, d, e) )

// #define midLayer(...) __VA_ARGS__
__VA_ARGS__
get3th(a, b, c, d, e)

// #define get3th(a1, a2, a3, ...) a3
a3
c

▌判斷參數數目

#define parameterNum(...) get5thVS(__VA_ARGS__, 4, 3, 2, 1)

這裏利用到 __VA_ARGS__ 展開時導致 參數項數目改變 的特性

當初學到這個的時候實在是很驚喜、意想不到

來直接看展開流程

parameterNum(a)

// #define parameterNum(...) get5thVS(__VA_ARGS__, 4, 3, 2, 1)
get5thVS(__VA_ARGS__, 4, 3, 2, 1)
get5thVS(a, 4, 3, 2, 1)

// #define get5thVS(...) midLayer( get5th(__VA_ARGS__) )
midLayer( get5th(a) )

// #define midLayer(...) __VA_ARGS__
__VA_ARGS__
get5th(a, 4, 3, 2, 1)

// #define get5th(a1, a2, a3, a4, a5, ...) a5
a5
1

再來其他例子

parameterNum(a, b, c, d)

// #define parameterNum(...) get5thVS(__VA_ARGS__, 4, 3, 2, 1)
get5thVS(__VA_ARGS__, 4, 3, 2, 1)
get5thVS(a, b, c, d, 4, 3, 2, 1)

// #define get5thVS(...) midLayer( get5th(__VA_ARGS__) )
midLayer( get5th(a, b, c, d) )

// #define midLayer(...) __VA_ARGS__
__VA_ARGS__
get5th(a, b, c, d, 4, 3, 2, 1)

// #define get5th(a1, a2, a3, a4, a5, ...) a5
a5
4

還有

parameterNum(?)          // 1
parameterNum(?, ?)       // 2
parameterNum(?, ?, ?)    // 3
parameterNum(?, ?, ?, ?) // 4

__VA_ARGS__ 展開時改變了參數項數目

把原本在前面的參數項擠到後面

再配合 選取第 n 項參數的宏

就可以判斷參數數目,是不是很神奇

▌分辨 0 和 1 個參數數目

上面的例子可以看到, 1 個參數數目時

parameterNum(a) 成功展開了 1

但是,目前的 parameterNum 無法分辨 0 個參數數目的情況

就是說:

parameterNum()  // 1
parameterNum(?) // 1

我們來試一次

parameterNum()

// #define parameterNum(...) get5thVS(__VA_ARGS__, 4, 3, 2, 1)
get5thVS(__VA_ARGS__, 4, 3, 2, 1)
get5thVS(, 4, 3, 2, 1)

// #define get5thVS(...) midLayer( get5th(__VA_ARGS__) )
midLayer( get5th() )

// #define midLayer(...) __VA_ARGS__
__VA_ARGS__
get5th(, 4, 3, 2, 1)

// #define get5th(a1, a2, a3, a4, a5, ...) a5
a5
1

parameterNum()parameterNum(a) 都是展開了 1

我們預期 parameterNum() 會展開 0

爲了解決這個問題,會利用 Visual Studio 2019 和 C++20 的宏 提到的 __VA_OPT__

這是 C++ 20 的新功能,會判斷 ... 有沒有輸入參數

當可變參數丟失(不僅僅是空),不會展開括弧裏的 Token

反之而言,沒有丟失則展開

#define parameterNum(...) get5th( __VA_ARGS__  __VA_OPT__(,) 4, 3, 2, 1, 0)

試一次

parameterNum()

// #define parameterNum(...) get5thVS( __VA_ARGS__  __VA_OPT__(,) 4, 3, 2, 1, 0)
get5th( __VA_ARGS__  __VA_OPT__(,) 4, 3, 2, 1, 0)
get5th(4, 3, 2, 1, 0)

// #define get5th(a1, a2, a3, a4, a5, ...) a5
a5
0

parameterNum(a)

parameterNum(a)

// #define parameterNum(...) get5thVS( __VA_ARGS__  __VA_OPT__(,) 4, 3, 2, 1, 0)
get5th( __VA_ARGS__  __VA_OPT__(,) 4, 3, 2, 1, 0)
get5th(a, 4, 3, 2, 1, 0)

// #define get5th(a1, a2, a3, a4, a5, ...) a5
a5
1

現在的話就沒有問題了:

parameterNum()           // 0
parameterNum(?)          // 1
parameterNum(?, ?)       // 2
parameterNum(?, ?, ?)    // 3
parameterNum(?, ?, ?, ?) // 4

▌判斷參數數目的上限

parameterNum() 是依賴 get5th() 來做的

get5th() 確定了 parameterNum() 可判斷參數數目的上限

get5th() 最多可判斷 4 個,get7thVS() 可判斷 6 個 . . . . . .

getnth() 可判斷 n-1

我個人會寫好十個左右的 getnth()get1th() ~ get10th()

正常夠用的了,如果 9 個可判斷參數數目都不夠用的話 …

該反省一下這個函數的設計,會不會過於復雜

▌結語

爲了以免一直說理論

就來了個簡單的應用:判斷參數數目

如果知道可變參數函數的話

你會發現這個宏配合可變參數函數會很有用

CWKSC

Author 作者

CWKSC

喜歡編程,會一點點鋼琴,會一點點畫畫,喜歡使用顏文字 About me 關於我

For any comments or discussions on my blog post, you can open an issue here

對於我博客文章的任何評論或討論,可以在這裏開一個 issue

Feel free to give your comments. OW<