这个C++ setter/getter模式打破了什么?

sam*_*var 5 c++ design-patterns glsl getter-setter

在C++中使用GLSL语法

我写了自定义矢量类,如vec2,vec3等模仿GLSL类型和大致如下:

struct vec3
{
    inline vec3(float x, float y, float z)
      : x(x), y(y), z(z) {}
    union { float x, r, s; };
    union { float y, g, t; };
    union { float z, b, p; };
};
Run Code Online (Sandbox Code Playgroud)

矢量的操作以这种方式实现:

inline vec3 operator +(vec3 a, vec3 b)
{
    return vec3(a.x + b.x, a.y + b.y, a.z + b.z);
}
Run Code Online (Sandbox Code Playgroud)

这允许我使用类似GLSL的语法创建向量并访问它们的组件,并对它们执行操作,就像它们是数字类型一样.工会允许我指的是第一个冷漠的坐标作为x或作为r,如在GLSL的情况.例如:

vec3 point = vec3(1.f, 2.f, 3.f);
vec3 other = point + point;
point.x = other.b;
Run Code Online (Sandbox Code Playgroud)

调酒问题

但GLSL也允许调整访问,即使组件之间存在漏洞.例如,p.yx表现为vec2with 和ps .当没有重复组件时,它也是左值.一些例子:xy

other = point.xyy; /* Note: xyy, not xyz */
other.xz = point.xz;
point.xy = other.xx + vec2(1.0f, 2.0f);
Run Code Online (Sandbox Code Playgroud)

现在,这可以利用标准的getter和setter方法,如做vec2 xy()void xy(vec2 val).这就是GLM库的功能.

透明吸气剂和二传手

但是,我设计了这种模式,让我在C++中完全相同.由于一切都是POD结构,我可以添加更多的联合:

template<int I, int J> struct MagicVec2
{
    friend struct vec2;
    inline vec2 operator =(vec2 that);

private:
    float ptr[1 + (I > J ? I : J)];
};

template<int I, int J>
inline vec2 MagicVec2<I, J>::operator =(vec2 that)
{
    ptr[I] = that.x; ptr[J] = that.y;
    return *this;
}
Run Code Online (Sandbox Code Playgroud)

如.vec3类成为(我简化事情有点,例如没有什么能够阻止xx被用作这里左值):

struct vec3
{
    inline vec3(float x, float y, float z)
      : x(x), y(y), z(z) {}

    template<int I, int J, int K>
    inline vec3(MagicVec3<I, J, K> const &v)
      : x(v.ptr[I]), y(v.ptr[J]), z(v.ptr[K]) {}

    union
    {
        struct { float x, y, z; };
        struct { float r, g, b; };
        struct { float s, t, p; };

        MagicVec2<0,0> xx, rr, ss;
        MagicVec2<0,1> xy, rg, st;
        MagicVec2<0,2> xz, rb, sp;
        MagicVec2<1,0> yx, gr, ts;
        MagicVec2<1,1> yy, gg, tt;
        MagicVec2<1,2> yz, gb, tp;
        MagicVec2<2,0> zx, br, ps;
        MagicVec2<2,1> zy, bg, pt;
        MagicVec2<2,2> zz, bb, pp;
        /* Also MagicVec3 and MagicVec4, of course */
    };
};
Run Code Online (Sandbox Code Playgroud)

基本上:我使用一个联合来将向量的浮点组件与一个魔法对象混合在一起,这个对象实际上不是一个vec2但可以隐式地转换为a vec2(因为它有一个vec2允许它的构造函数),并且可以被赋值为vec2(因为它的重载赋值)运营商).

我对结果非常满意.上面的GLSL代码有效,我相信我得到了不错的类型安全性.我可以#include在我的C++代码中使用GLSL着色器.

限制

当然有一些限制.我知道以下几点:

  • sizeof(point.xz)3*sizeof(float)取代预期2*sizeof(float).这是设计上的,我不知道这是否有问题.
  • &foo.xz不能用作vec2*.这应该没问题,因为我只是按值传递这些对象.

所以我的问题是:我可能忽略了哪些因为这种模式会让我的生活变得困难?此外,我还没有在其他任何地方找到这种模式,所以如果有人知道它的名字,我感兴趣.

注意:我希望坚持使用C++ 98,但我依靠编译器允许通过联合进行类型惩罚.我不想要C++ 11的原因是我的几个目标平台缺乏编译器支持; 但是,我感兴趣的所有编译器都支持打字.

Bla*_*ade 3

简而言之:我认为很难确保这种模式有效 - 这就是你问的原因。此外,该模式可以替换为标准代理模式,其正确性更容易保证。我不得不承认,当静态创建代理时,基于代理的解决方案的存储开销是一个问题。

\n\n

上述代码的正确性

\n\n

这是没有明显错误的代码;但解释一下CAR Hoare 的话,这显然不是没有 bug 的代码。此外,说服自己没有错误有多难? \n我看不出该模式不起作用的任何原因 - 但要证明(即使是非正式的)它会起作用并不容易。事实上,尝试进行证明可能会失败并指出一些问题。\n为了安全起见,我会禁用MagicVecN类的所有隐式生成的构造函数/赋值运算符,只是为了避免考虑所有相关的复杂性(请参阅下面的小节);然而这样做是被禁止的,因为对于联合成员来说,不能覆盖隐式定义的复制赋值运算符,正如我拥有的标准草案和 GCC 的错误消息所解释的那样:

\n\n
member \xe2\x80\x98MagicVec2<0, 0> vec3::<anonymous union>::xx\xe2\x80\x99 with copy assignment operator not allowed in union\n
Run Code Online (Sandbox Code Playgroud)\n\n

在所附要点中,为了安全起见,我改为手动提供实现。

\n\n

请注意,MagicVec2\ 的赋值运算符应通过const引用接受其参数(请参见下面的示例,其中有效);隐式转换仍然会发生(const 引用将指向创建的临时对象;如果没有 const 限定符,这将不起作用)。

\n\n

几乎有问题,但不完全是问题

\n\n

我以为发现了一个错误(我没有),但考虑一下仍然有点有趣 - 只是看看必须覆盖多少个案例才能排除潜在的错误。会p.xz = p.zx产生正确的结果吗?我认为MagicVec2\ 的隐式赋值运算符会被调用,导致错误的结果;事实上,它不是(我相信)因为IJ是不同的并且是类型的一部分。当类型相同时怎么办?\np.xx = q.rr是安全的,但p.xx = p.rr很棘手(即使它可能很愚蠢,但它仍然不会损坏内存):是基于隐式生成的赋值运算符吗memcpy?答案似乎是否定的,但如果是的话,这将是memcpy重叠的内存间隔,这是未定义的行为。

\n\n

更新:一个实际问题

\n\n

正如OP所注意到的,默认的复制赋值运算符也会被表达式调用p.xz = q.xz;在这种情况下,它实际上也会复制该.y成员。如上所述,对于属于联合一部分的数据类型,不能禁用或修改复制赋值运算符。

\n\n

代理模式

\n\n

此外,我相信有一个更简单的解决方案,即代理模式(您正在部分使用)。MagicVecX应该包含指向包含类的指针而不是ptr; 这样你就不需要使用工会的技巧了。

\n\n
template<int I, int J> struct MagicVec2\n{\n    friend struct vec2;\n    inline MagicVec2(vec2* _this): ptr(_this) {}\n    inline vec2 operator=(const vec2& that);\nprivate:\n    float *ptr;\n};\n
Run Code Online (Sandbox Code Playgroud)\n\n

我通过编译(但不链接)此代码进行了测试,该代码概述了建议的解决方案: https: //gist.github.com/1775054。请注意,代码不完整,也未经过测试 - 还应该重写MagicVecX.

\n