2016-11-06 7 views
3

Я оказался в странной ситуации, пытаясь добавить некоторую синтаксическую симпатию к некоторому C-коду. У меня триплеты чисел, которые являются просто структурами._Generic параметр за минимальное количество аргументов

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

Теперь у меня есть некоторые функции, которые принимают две такие структуры, как аргументы. Самый простой подсчет количества координат внутри 3D пространстве определяется двумя структурами:

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); 

Nevermind это например, быть глупым, он имеет больше смысла для более сложных функций.

Я думал, что буду использовать _Generic, чтобы пройти либо триплеты ints, либо structs.

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, который, вероятно, близок к тому, что делает библиотека препроцессора boost. Но я больше не смущаюсь в ограничениях Generic в первый раз, используя его, и задаюсь вопросом, не упустил ли я что-то глупое ... – Cimbali

+0

Я думаю, что этот подход не может работать; все ветви общего не должны иметь ограничений ограничений, даже если они не выбраны. –

ответ

1

Ну, вот что мы обсудили в комментариях, хотя это не очень удовлетворительно, так как это не очень элегантное решение.

  • Во-первых, определить boxes_inside_X для каждого X быть приемлемым количество аргументов, с помощью _Generic при необходимости.
  • Затем перегрузите макрос, вставив после него количество аргументов (или используя Boost.PP, как предложено Quentin).
/* 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’ если типы неправильно.

 Смежные вопросы

  • Нет связанных вопросов^_^