Как добиться успеха в работе 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 в свой компилятор. Извините, сейчас я не могу проверить это, но я думаю, что это сделало бы трюк.

И теперь некоторые дополнительные услуги.

  1. В отличие от C, вы не должны создавать typedef структуры в C++ - если вы назвали свои структуры, вы можете ссылаться на них, используя это имя как тип.
  2. Структуры общедоступны по умолчанию, здесь нет необходимости public. Классы, однако, по умолчанию являются закрытыми.
  3. Если вы намерены просто использовать математику GLSL в C++, GLM - это способ сделать это. Если вы хотите узнать, как это сделать самостоятельно, вы можете ссылаться на их исходный код (хотя это довольно тяжело с шаблонами).
  4. Другие опции g++ можно найти здесь.

Надеюсь, что это хоть как-то поможет вам.