2016-11-06 62 views
3

我發現自己處於奇怪的狀況,試圖爲某些C代碼添加一些語法漂亮。我有三個數字,只是結構。_超出最小參數數量的通用參數

typedef struct {int x, y, z} coord; 

現在我有一些函數需要2個這樣的結構作爲參數。最簡單的一個計算定義的三維空間內的座標數兩個結構:

static inline int boxes_inside(const coord min, const coord max) 
{ 
    return (1 + max.x - min.x) * (1 + max.y - min.y) * (1 + max.z - min.z); 
} 

,我發現自己很經常與固定的論點,我覺得這是非常醜陋的

coord foo; 
/* initialize foo with something */ 
int n = boxes_inside((coord){.x = 0, .y = 0, .z = 0}, foo); 

算了這樣叫它例如很愚蠢,對於更復雜的函數更有意義。

我想我會使用_Generic傳遞三元組或整數。

int boxes_inside_cc(const coord min, const coord max); 
int boxes_inside_ci(const coord min, const int maxx, const int maxy, const int maxz); 
int boxes_inside_ic(const int minx, const int miny, const int minz, const coord max); 
int boxes_inside_ii(const int minx, const int miny, const int minz, const int maxx, const int maxy, const int maxz); 

#define arg1(a, ...) (a) 
#define arg2(a, b ...) (b) 
#define arg4(a, b, c, d, ...) (d) 

#define boxes_inside(...) _Generic(arg1(__VA_ARGS__), \ 
     coord: _Generic(arg2(__VA_ARGS__), coord: boxes_inside_cc, int: boxes_inside_ci) \ 
     int: _Generic(arg4(__VA_ARGS__), coord: boxes_inside_ic, int: boxes_inside_ii) \ 
    )(__VA_ARGS__) 

我認爲這樣會很好,因爲「未選擇的選項的表達式永遠不會被評估」。 (ref)但事實證明,因爲這是done after preprocessing,所有的宏仍然擴展,即使在未選擇的選擇。

特別是,如果我現在在做以下電話:

coord min, max; 
/* stuff */ 
int n = boxes_inside(min, max); 

我得到的問題arg4(__VA_ARGS__)試圖擴展更多的參數比它實際上有,即使_Generic的這個分支將永遠不會被後評估上。

於是,我又試圖擴大結構來,總是有足夠的論據:

#define boxes_inside_(a, b, c, d, ...) _Generic((a), \ 
     coord: boxes_inside_ii(a, b, c, d.x, d.y, d.z), \ 
     int: boxes_inside_ii(a, b, c, d, __VA_ARGS__) \ 
    ) 

#define boxes_inside(a, ...) _Generic((a), \ 
     coord: boxes_inside_(a.x, a.y, a.z, __VA_ARGS__) \ 
     int: boxes_inside_(a, __VA_ARGS__) \ 
    ) 

然而,這勿庸置疑失敗,同樣的原因:兩個分支擴大等宏觀,尤其boxes_inside(min, max)還擴展到boxes_inside_(min max)上我們已經知道的分支將不會被使用。

那麼有沒有辦法解決這個問題?或者如果你想測試一個超出你可能使用的最小參數數量的參數,_Generic表達式基本上是無用的嗎?

+2

我幾乎不敢建議把[Boost.PP](http://www.boost.org/doc/libs/master/libs/preprocessor/doc/ref/overload.html)東西,但你是咖啡機,所以你可能知道你在做什麼。 – Quentin

+0

@Quentin我真的可以總是從[在參數數目上重載宏](https://stackoverflow.com/questions/11761703/overloading-macro-on-number-of-arguments)開始,然後才能分割類型與_Generic,這可能接近推進預處理器庫無論如何。但更多的是,我對_Generic的限制感到困惑,因爲我第一次使用它,並想知道我是否沒有忽略某些愚蠢的東西...... – Cimbali

+0

我認爲這種方法無法工作;通用的所有分支即使未被選中也不得有約束違例 –

回答

1

好吧,這裏是我們在評論中討論的內容,儘管它並不令人滿意,因爲它不是一個真正的優雅解決方案。

  • 首先,定義boxes_inside_XX是的參數的可接受的數量,使用_Generic必要時。
  • 然後通過粘貼後面的參數數量(或使用Quentin建議的Boost.PP)來重載宏。
/* macros that can be reused (possibly with more arguments) */ 
#define paste2(a, b) a ## b 
#define paste(a, b) paste2(a, b) 
#define get_seventh(_1, _2, _3, _4, _5, _6, this_one, ...) this_one 
#define get_suffix(...) get_seventh(__VA_ARGS__, _6, _5, _4, _3, _2, _1) 

/* define all variants with number of arguments suffix */ 
int boxes_inside_2(const coord min, const coord max); 
int boxes_inside_6(const int minx, const int miny, const int minz, const int maxx, const int maxy, const int maxz); 

/* make it a _Generic, if several functions have the same number of arguments */ 
int boxes_inside_ci(const coord min, const int maxx, const int maxy, const int maxz); 
int boxes_inside_ic(const int minx, const int miny, const int minz, const coord max); 
#define boxes_inside_4(a, ...) _Generic((a),\ 
     coord: boxes_inside_ci) \ 
     int: boxes_inside_ic) \ 
    )(__VA_ARGS__) 

/* make macro call itself with the number of arguments pasted after it */ 
#define boxes_inside(...) paste(boxes_inside, get_suffix(__VA_ARGS__))(__VA_ARGS__) 

這種方法的好處是,你得到合理可讀的錯誤消息,例如

  • warning: implicit declaration of function ‘boxes_inside_3’了錯誤的參數數目,或
  • expected ‘coord {aka const struct <anonymous>}’ but argument is of type ‘int’如果類型錯誤。