Именованные структуры в объединениях C

#c #gcc #struct #forward-declaration #unions

#c #gcc #структура #прямое объявление #объединения

Вопрос:

В C я пытаюсь создать специализированный класс point в виде объединения, вот так:

 union point
{
  struct { float x, y, z; };
  float val[3];
  float operator[](unsigned i) { return val[i]; }
};
  

Чтобы я мог получить доступ к точке как к массиву или как к нескольким точкам, для удобства чтения.

Однако, давайте предположим, что я хочу немного обобщить это:

 template<unsigned n>
  union point
  {
    struct { float ???; };
    float val[n];
    float operator[](unsigned i) { return val[i]; }
  };
  

Что я могу указать для ??? ? Я мог бы иметь x , x, y x, y, z или x, y, z, w в зависимости от того, что n есть. Решение? Пересылать объявления!

 template<unsigned n>
  union point
  {
    struct coords;
    float val[n];
    float operator[](unsigned i) { return val[i]; }
  };

template<>
  struct point::coords<3>
  {
    float x, y, z;
  };

// ...
  

Но, похоже, это не работает. Однако в GCC 4.6 он компилируется всякий раз, когда я пытаюсь использовать элементы, например, так:

 point<3> val;
val.x;
  

Я получаю сообщение об ошибке:

 error: ‘union point<3>’ has no member named ‘x’
  

Даже если я изменю val.x на val.coords::x , я все равно получу ошибку:

 error: ‘union point<3>::coords’ is not a base of ‘union point<3>’
  

Добавление using coords; в определение объединения также не помогло.

Есть ли какой-либо способ выполнить это в GCC 4.6? Существует ли другой способ сделать это? Возможно ли это вообще?

Комментарии:

1. Если вам нужно специализировать каждый экземпляр шаблона, почему бы просто не объявить 4 разных объединения (без шаблонов) и покончить с этим?

2. Вас интересуют решения для «[доступа] к точке в виде массива или в виде нескольких точек, для удобства чтения» в более общем смысле тоже, или материал объединения необходим?

3. Luc — Основная причина в том, что в 99% случаев x, y, z и w будут использоваться в качестве средств доступа к элементам, но я все еще хочу разрешить возможность простого перебора значений точек без необходимости выполнять хакерское приведение типов указателей.

Ответ №1:

Я бы предложил использовать переменный макрос для определения ваших union<N> шаблонов.

 template<unsigned int N>
union point; // declared and undefined

#define DECLARE_POINT(NUM, ...) 
template<> 
union point<NUM> 
{ 
  struct { float __VA_ARGS__; }; 
  float val[NUM]; 
}

#undef DECLARE_POINT
  

Сделав это, вы можете просто объявить / определить свои различные комбинации для координат (в данном случае перед #undef ):

 DECLARE_POINT(1, x);
DECLARE_POINT(2, x, y);
DECLARE_POINT(3, x, y, z);
  

это эквивалентно,

 template<> union point<1> { struct { float x; }; float val[1]; };
template<> union point<2> { struct { float x, y; }; float val[2]; };
template<> union point<3> { struct { float x, y, z; }; float val[3]; };
  

Его можно использовать так же, как вы просили:

 point<3> p;
p.z = 0;
  

Кроме того, вы можете выполнить перекрестную проверку, используя некоторый шаблонный трюк ( static_assert ), чтобы проверить, что числовые аргументы (например, 1,2,3,... ) соответствуют общему переданному аргументу (например, x,y,z,... ).

Ответ №2:

Эта строка внутри вашего объединения:

 struct coords;
  

forward — объявляет тип coords , но в вашем шаблонном объединении нет struct coords поля.

Кроме того, только члены анонимных структур могут быть доступны в качестве полей верхнего уровня объединения. Например:

 union foo {
    struct { // anonymous struct
        short i;
        short j;
    };
    int k;
};

foo f;
// it's possible to access the fields of the anonymous struct as if they were
// direct members of the union
f.i = 4;
f.j = 8;
std::cout << f.k;
  

Я не уверен, что вы сможете это сделать, если будете специализировать только внутренний struct тип.

Однако это работает:

 template<unsigned n>
union point;

template<>
union point<2> {
    struct { float x, y; };
    float val[2];
};

template<>
union point<3> {
    struct { float x, y, z; };
    float val[3];
};
  

Однако есть ряд недостатков; главный из них заключается в том, что вам придется переопределять operator[] для каждой версии point .

Я знаю, как использовать шаблоны, но я не бог шаблонов, поэтому не исключено, что существует хитрый трюк.

Комментарии:

1. Следует упомянуть, что анонимные структуры не являются частью C , а всего лишь популярным расширением (и станут частью C1x, следующей версии C; однако, не C 0x).