如何在C++中实现矢量调配?

X S*_*ish 12 c++ mingw g++ glsl visual-studio

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) {}
};
Run Code Online (Sandbox Code Playgroud)

上面的代码在Visual Studio中编译并按预期工作,因此我可以使用它

vec3 v1(1.f, 2.f, 3.f);
vec2 v2 = v1.yz; // (2, 3)
Run Code Online (Sandbox Code Playgroud)

不是用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'
Run Code Online (Sandbox Code Playgroud)

我想我不应该首先这样做.有任何想法吗?

编辑:在阅读了大量文章和探索开源项目之后,我开始了解矢量调整应该如何并在下面发布解决方案,但仍在等待更好的答案.

编辑2:所有vec*成员只能从父访问GLM库.

X S*_*ish 6

好吧,我自己仅使用 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;
}
Run Code Online (Sandbox Code Playgroud)

当然,您可以添加/创建更多的混合。现在进行一个小测试。

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;
}
Run Code Online (Sandbox Code Playgroud)

出去:

(30, 20, 10)
(-5, -5, 30)
(-5, 30)
(60, 70)
4200
Run Code Online (Sandbox Code Playgroud)

std::cout如果您没有像我在 gcc 中那样使用IDE,则可以打印向量进行调试。

  • 您违反了[严格别名规则](/sf/ask/6905531/)。 (3认同)
  • 我的意思是这个表达式:`*(vec_type*)this`。事实上,联合[在C++中也很危险](/sf/ask/796124241/),这与C中的不同。 (3认同)

xsk*_*xzr 5

首先,匿名结构是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
};
Run Code Online (Sandbox Code Playgroud)

现在代码在GCC下编译,但这还不够.在Clang下-pedantic-errors,你会得到几个错误:

error: anonymous types declared in an anonymous union are an extension [-Werror,-Wnested-anon-types]
Run Code Online (Sandbox Code Playgroud)

这是因为您无法在匿名联合中声明嵌套类型,因此您还应将这些结构定义移到联合之外:

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} {}
};
Run Code Online (Sandbox Code Playgroud)

虽然此解决方案有效,但您只能通过,例如v.xy.x,而不是简单来访问成员v.x.此外,vec2使用两个floats进行别名会导致未定义的行为.我认为没有标准的解决方案可以完美地实现矢量调配.

对于非标准解决方案,可以使用不带构造函数的代理类,而不是vec2使编译器工作.该GLM库也使用了这个想法.OP已经发布了一个答案作为这个想法的完整实现.


归档时间:

查看次数:

683 次

最近记录:

6 年,8 月 前