close
前幾篇我介紹了一種不用 __VA_ARGS__ 實作可變參數 macro 的方式,例如可以這樣寫
PRINTF("%d %f %s", (1)(3.14)("hello"));
//展開後
printf("%d %f %s", 1, 3.14, "hello", '\0');
雖然看起來很接近我們的需求,不過最後插入的多餘字元非常礙眼,令人欲除之而後快。這個看似很簡單的工作卻超乎想像的困難,我耗掉了一些休閒時間仍然解決不了,至少得到的東西太過古怪以致於我都不太確定是否合法。
後來我忽然想到曾經在 Boost.Test 當中看過類似的寫法,但當時並沒有仔細思考他是怎麼實作的,這時想來卻不免好奇。於是我開始追蹤相關的程式碼,才發現 Boost 除了 MPL、Fusion、Lambda 這些新穎的 TMP 程式庫外,還有另一條長期被我忽視的蹊徑 -- Preprocessor Metaprogramming。
舉例來說
BOOST_PP_SEQ_ENUM( (B)(O)(O)(S)(T) )
// 展開成 B, O, O, S, T
其實他使用的概念和我這一兩天的想法類似,但我失敗在沒有想到可靠的程式碼串接功能,他則是針對不同的編譯器設計合適的串接功能。甘拜下風
又例如
BOOST_PP_SEQ_SIZE( (a)(b)(c) )
// 展開成 3
這會不會太 lisp 了...
#define LIST (1, (3, (5, (2, (4, BOOST_PP_NIL)))))
#define OP(d, state, x) BOOST_PP_MAX_D(d, state, x)
#define LIST_MAX(list) BOOST_PP_LIST_FOLD_LEFT(OP, 0, LIST)
LIST_MAX(LIST)
// 展開成 5
還有很多相當神奇的東西例如 foreach、iteration 等等常見的程式設計組件,但全都是用 Preprocessor 做出來的。
不過仔細看一下實作還真不是普通複雜,很多功能都是用相當暴力的方法完成的,並且受編譯器影響很大(我想也是),所以同一個功能會有一堆 #if 判斷該切換哪一個實作。雖然成果很神奇,但和 TMP 裡面的東西一樣,目前還很難想像這些東西的實用性。
全站熱搜
留言列表