Как добиться успеха в работе C++?
struct vec2
{
union
{
struct { float x, y; };
struct { float r, g; };
struct { float s, t; };
};
vec2() {}
vec2(float a, float b) : x(a), y(b) {}
};
struct vec3
{
union
{
struct { float x, y, z; };
struct { float r, g, b; };
struct { float s, t, p; };
// Here is the problem with g++.
struct { vec2 xy; float z; };
struct { float x; vec2 yz; };
};
vec3() {}
vec3(float a, float b, float c) : x(a), y(b), z(c) {}
};
Вышеприведенный код компилируется и работает как ожидается в Visual Studio, поэтому я могу использовать его как
vec3 v1(1.f, 2.f, 3.f);
vec2 v2 = v1.yz; // (2, 3)
Не в g++ (MinGW).
src/main.cpp:22:23: error: member 'vec2 vec3::<unnamed union>::<unnamed struct>::xy' with constructor not allowed in anonymous aggregate
src/main.cpp:22:33: error: redeclaration of 'float vec3::<unnamed union>::<unnamed struct>::z'
src/main.cpp:18:30: note: previous declaration 'float vec3::<unnamed union>::<unnamed struct>::z'
src/main.cpp:23:32: error: member 'vec2 vec3::<unnamed union>::<unnamed struct>::yz' with constructor not allowed in anonymous aggregate
src/main.cpp:23:24: error: redeclaration of 'float vec3::<unnamed union>::<unnamed struct>::x'
src/main.cpp:18:24: note: previous declaration 'float vec3::<unnamed union>::<unnamed struct>::x'
Думаю, я не должен делать это в первую очередь. Есть идеи?
Редактирование: прочитав много статей и изучая проекты с открытым исходным кодом, я начал понимать, как должно выглядеть векторное swizzling, и разместил решение ниже, все еще ожидая лучших ответов.
Изменить 2: Все члены vec*
должны быть доступны только от родителя, например библиотеки GLM.
Ответы
Ответ 1
Ну, я нашел решение самостоятельно, используя только C++ Стандарты.
Никакие командные строки не используют код, специфичный для компилятора.
Итак, это моя новая и простая реализация
template<unsigned int I>
struct scalar_swizzle
{
float v[1];
float &operator=(const float x)
{
v[I] = x;
return v[I];
}
operator float() const
{
return v[I];
}
float operator++(int)
{
return v[I]++;
}
float operator++()
{
return ++v[I];
}
float operator--(int)
{
return v[I]--;
}
float operator--()
{
return --v[I];
}
};
// We use a vec_type in a template instead of forward declartions to prevent erros in some compilers.
template<typename vec_type, unsigned int A, unsigned int B>
struct vec2_swizzle
{
float d[2];
vec_type operator=(const vec_type& vec)
{
return vec_type(d[A] = vec.x, d[B] = vec.y);
}
operator vec_type()
{
return vec_type(d[A], d[B]);
}
};
struct vec2
{
union
{
float d[2];
scalar_swizzle<0> x, r, s;
scalar_swizzle<1> y, g, t;
vec2_swizzle<vec2, 0, 0> xx;
vec2_swizzle<vec2, 1, 1> yy;
};
vec2() {}
vec2(float all)
{
x = y = all;
}
vec2(float a, float b)
{
x = a;
y = b;
}
};
/* Debugging */
inline std::ostream& operator<<(std::ostream &os, vec2 vec)
{
os << "(" << vec.x << ", " << vec.y << ")";
return os;
}
template<typename vec_type, unsigned int A, unsigned int B, unsigned int C>
struct vec3_swizzle
{
float d[3];
vec_type operator=(const vec_type& vec)
{
return vec_type(d[A] = vec.x, d[B] = vec.y, d[C] = vec.z);
}
operator vec_type()
{
return vec_type(d[A], d[B], d[C]);
}
};
struct vec3
{
union
{
float d[3];
scalar_swizzle<0> x, r, s;
scalar_swizzle<1> y, g, t;
scalar_swizzle<2> z, b, p;
vec2_swizzle<vec2, 0, 1> xy;
vec2_swizzle<vec2, 1, 2> yz;
vec3_swizzle<vec3, 0, 1, 2> xyz;
vec3_swizzle<vec3, 2, 1, 0> zyx;
};
vec3() {}
vec3(float all)
{
x = y = z = all;
}
vec3(float a, float b, float c)
{
x = a;
y = b;
z = c;
}
};
/* Debugging */
inline std::ostream& operator<<(std::ostream &os, vec3 vec)
{
os << "(" << vec.x << ", " << vec.y << ", " << vec.z << ")";
return os;
}
Конечно, вы можете добавить/создать больше swizzlings. Теперь с небольшим испытанием.
int main()
{
vec3 v0(10, 20, 30);
std::cout << v0.zyx << std::endl;
vec2 c(-5, -5);
v0.xy = c;
vec2 v1(v0.yz);
std::cout << v0 << std::endl;
std::cout << v1 << std::endl;
vec3 v(50, 60, 70);
vec2 d = v.yz;
std::cout << d << std::endl;
float f = d.x * d.y;
std::cout << f << std::endl;
return 0;
}
Из:
(30, 20, 10)
(-5, -5, 30)
(-5, 30)
(60, 70)
4200
Вы можете распечатать векторы для отладки с помощью std::cout
если вы не используете среду IDE, как в gcc.
Ответ 2
Во-первых, анонимная структура является функцией C11 и не разрешена C++, поэтому она не поддерживает членов класса с конструкторами (а не с C-структурой). Чтобы написать переносимый код C++, вы должны избегать анонимной структуры:
struct vec2 // use C++ style struct declaration
{
// struct is public by default
union
{
struct { float x, y; } xy; // add member name,
struct { float r, g; } rg; // now the declaration declares a member
struct { float s, t; } st; // instead of an anonymous struct
};
vec2() {}
vec2(float a, float b) : xy{a, b} {}
// ^^^^^^^^ also change the initialization
};
struct vec3
{
public:
union
{
struct { float x, y, z; } xyz; //
struct { float r, g, b; } rgb; //
struct { float s, t, p; } stp; // add member name
struct { vec2 xy; float z; } vecz; //
struct { float x; vec2 yz; } xvec; //
};
vec3() {}
vec3(float a, float b, float c) : xyz{a, b, c} {}
// ^^^^^^^^ also change the initialization
};
Теперь код компилируется под GCC, но этого недостаточно. В разделе Clang с -pedantic-errors
вы получите несколько ошибок:
error: anonymous types declared in an anonymous union are an extension [-Werror,-Wnested-anon-types]
Это связано с тем, что вы не можете объявить вложенный тип в анонимный союз, поэтому вы также должны переместить эти определения структуры вне объединения:
struct vec2
{
struct XY { float x, y; };
struct RG { float r, g; };
struct ST { float s, t; };
union
{
XY xy;
RG rg;
ST st;
};
vec2() {}
vec2(float a, float b) : xy{a, b} {}
};
struct vec3
{
struct XYZ { float x, y, z; };
struct RGB { float r, g, b; };
struct STP { float s, t, p; };
struct VECZ { vec2 xy; float z; };
struct XVEC { float x; vec2 yz; };
union
{
XYZ xyz;
RGB rgb;
STP stp;
VECZ vecz;
XVEC xvec;
};
vec3() {}
vec3(float a, float b, float c) : xyz{a, b, c} {}
};
Наконец, если вы можете использовать C++ 17, я бы рекомендовал использовать std::variant
вместо объединения.
Ответ 3
Что касается "члена с конструктором, который не разрешен в анонимном агрегате", это связано с тем, что компилятор работает в соответствии со старым стандартом, поскольку, как и в C++ 11, профсоюзы могут иметь членов с нетривиальными конструкторами (вы определили свой собственный конструктор, это нетривиально, подробности об этом можно найти здесь). Добавьте -std = C++ 11 в аргументы компилятора g++, и эта ошибка, скорее всего, исчезнет.
Следующий. Единственные флаги g++, которые могли бы, возможно, сделать его компиляции кода являются -fms-расширения и -fvisibility-MS-Compat. Анонимные структуры - это нестандартное расширение, добавленное Microsoft в свой компилятор. Извините, сейчас я не могу проверить это, но я думаю, что это сделало бы трюк.
И теперь некоторые дополнительные услуги.
- В отличие от C, вы не должны создавать
typedef
структуры в C++ - если вы назвали свои структуры, вы можете ссылаться на них, используя это имя как тип. - Структуры общедоступны по умолчанию, здесь нет необходимости
public
. Классы, однако, по умолчанию являются закрытыми. - Если вы намерены просто использовать математику GLSL в C++, GLM - это способ сделать это. Если вы хотите узнать, как это сделать самостоятельно, вы можете ссылаться на их исходный код (хотя это довольно тяжело с шаблонами).
- Другие опции g++ можно найти здесь.
Надеюсь, что это хоть как-то поможет вам.