你將如何編寫一個可以帶1或0參數的可變參數宏。即像這樣:需要0或1個參數的變量宏?
GREET() // returns @"Hello World"
GREET(@"John") // returns @"Hello John"
你將如何編寫一個可以帶1或0參數的可變參數宏。即像這樣:需要0或1個參數的變量宏?
GREET() // returns @"Hello World"
GREET(@"John") // returns @"Hello John"
這是很簡單的,你有這樣的事情:
#define __NARGS(unused, _1, _2, _3, _4, _5, VAL, ...) VAL
#define NARGS(...) __NARGS(unused, ## __VA_ARGS__, 5, 4, 3, 2, 1, 0)
#define __GREET(ARGC, ARGS...) GREET_ ## ARGC (ARGS)
#define _GREET(ARGC, ARGS...) __GREET(ARGC, ARGS)
#define GREET(...) _GREET(NARGS(__VA_ARGS__), __VA_ARGS__)
#define GREET_0(...) @"Hello World!"
#define GREET_1(ARG, ...) @"Hello, " ARG // strings are auto-concatenated in objc
int main()
{
NSLog(@"%@", GREET());
NSLog(@"%@", GREET(@"John"));
}
輸出:
2012-09-30 11:56:48.478 TestProj[51823:303] Hello World! 2012-09-30 11:56:48.480 TestProj[51823:303] Hello, John
現在,這是相當複雜的,但假設你在一個基本水平明白是怎麼預處理器作品,你應該很好地理解發生的事情。
太棒了。 「很簡單」,但? ;) –
@JimBuck當你將它與我所做的其他一些東西(一個'switch'語句支持objc對象,對象範圍的局部變量,在C中編寫一個完整的應用程序,以上帝的名義)比較時,這實際上非常簡單。 –
我對目標C不太熟悉,但在C中,您的宏將使用gcc擴展名「eat-the-comma」。這不能移植到標準C.(還有另一種符合標準的方法來檢查空的宏參數,它涉及更多一點。) –
一個宏有可變參數,或者它有固定數量的參數。要獲得所需的結果,請聲明2個宏,一個帶有0個參數,另一個帶有1個參數。
一個好辦法做到這一點是建立一個數據結構重複元素,如:
union greet_arg {
char *string;
};
struct greet_args {
union greet_arg *arg[2];
};
void greet_function(struct greet_args *x);
您的宏然後可以像這樣實現的:
#define GREET(x...) greet_function(&(struct greet_args){0, x})
現在的原因這項工作是,如果你打電話GREET("foo")
那麼你會得到:
greet_function(&(struct greet_args){0, "foo"});
而如果你c所有GREET()
你:
greet_function(&(struct greet_args){0, });
這是仍然有效; 「0」只是空值 - 填充數組的其餘部分。
您的greet_function()
然後只需檢查x->arg[1]
。
有趣,但請注意,由於您創建了C99複合字面值,這確實會導致創建本地範圍的iVar的(最小)開銷。 –
@ RichardJ.RossIII - 在'gcc -O2'下,它實際上產生與您的示例完全相同的程序集,並且具有實際使用非文字參數的優點。 – geocar
你必須記住,大多數objc用戶不會使用GCC,而是使用clang。我強烈要求你在那裏重新測試。 –
我不知道這是否會爲Objective C的工作,但對於C99和C11可以使用P99具有宏觀元P99_IF_EMPTY
#define GREET(...) P99_IF_EMPTY(__VA_ARGS__)("Hello World")("Hello " __VA_ARGS__)
考慮到objc使用與C完全相同的預處理器的事實,我不明白爲什麼這不起作用。 +1。 –
這是Objective-C的,那麼,由於您使用「@」字符串「成語」,對吧? –
@ RichardJ.RossIII yeah – lms