2016-11-06 63 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’如果类型错误。