我编写了自定义 vector 类,例如 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; };
};
vector 上的操作是这样实现的:
inline vec3 operator +(vec3 a, vec3 b)
{
return vec3(a.x + b.x, a.y + b.y, a.z + b.z);
}
这让我可以创建 vector 并使用类似 GLSL 的语法访问它们的组件,并对它们执行操作,就好像它们是数字类型一样。 union 允许我将第一个坐标无差别地称为 x 或 r,就像 GLSL 中的情况一样。例如:
vec3 point = vec3(1.f, 2.f, 3.f);
vec3 other = point + point;
point.x = other.b;
但是 GLSL 也允许混合访问,即使组件之间存在漏洞。例如 p.yx 表现得像一个 vec2 有 p 的 x 和 y 交换。当没有组件重复时,它也是一个左值。一些例子:
other = point.xyy; /* Note: xyy, not xyz */
other.xz = point.xz;
point.xy = other.xx + vec2(1.0f, 2.0f);
现在这可以使用标准的 getter 和 setter 来完成,例如 vec2 xy() 和 void xy(vec2 val)。这就是GLM library
但是,我设计了这个模式,让我可以在 C++ 中做完全相同的事情。由于一切都是 POD 结构,我可以添加更多 union :
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;
}
例如 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 */
};
};
基本上:我使用 union 将 vector 的浮点分量与一个魔法对象混合,该对象实际上不是 vec2 但可以隐式转换为 vec2 (因为有一个 vec2 构造函数允许它),并且可以分配一个 vec2 (因为它重载了赋值运算符)。
我对结果很满意。上面的 GLSL 代码有效,我相信我得到了不错的类型安全。我可以在我的 C++ 代码中#include 一个 GLSL 着色器。
当然有限制。我知道以下几个:
sizeof(point.xz) 将是 3*sizeof(float) 而不是预期的 2*sizeof(float)。这是设计使然,我不知道这是否会造成问题。&foo.xz 不能用作 vec2*。这应该没问题,因为我只按值传递这些对象。所以我的问题是:我可能忽略了什么,这会让我在使用这种模式时遇到困难?另外,我还没有在其他任何地方找到这种模式,所以如果有人知道它的名字,我就是有兴趣。
注意:我希望坚持使用 C++98,但我确实依赖于允许通过 union 进行类型双关的编译器。我不想要 C++11 的原因是我的几个目标平台缺乏编译器支持;不过,我感兴趣的所有编译器都支持类型双关。
最佳答案
简而言之:我认为很难确保这种模式有效 - 这就是你问的原因。此外,这种模式可以用标准代理模式代替,这样更容易保证正确性。我不得不承认,当静态创建代理时,基于代理的解决方案的存储开销是一个问题。
这是没有明显错误的代码;但释义C. A. R. Hoare ,这不是明显没有错误的代码。而且,要说服自己没有BUG有多难?
我看不出该模式不起作用的任何原因 - 但要证明(即使是非正式地)它会起作用并不容易。事实上,尝试做一个证明可能会失败并指出一些问题。
为了安全起见,我会为 MagicVecN 类禁用所有隐式生成的构造函数/赋值运算符,只是为了避免考虑所有相关的并发症(请参阅下面的小节);但是这样做是被禁止的,因为对于 union 成员来说,不能覆盖隐式定义的复制赋值运算符,正如我所拥有的标准草案和 GCC 的错误消息所解释的那样:
member ‘MagicVec2<0, 0> vec3::<anonymous union>::xx’ with copy assignment operator not allowed in union
在所附的要点中,为了安全起见,我手动提供了一个实现。
请注意,MagicVec2 的赋值运算符应通过 const 引用接受其参数(参见下面的示例,其中有效);隐式转换仍然会发生(const 引用将指向创建的临时对象;如果没有 const 限定符,这将无法工作)。
我以为找到了一个错误(我没有),但考虑它仍然有些有趣 - 只是为了看看必须涵盖多少情况才能排除潜在的错误。 p.xz = p.zx 会产生正确的结果吗?我认为 MagicVec2 的隐式赋值运算符会被调用,导致不正确的结果;事实上,它不是(我相信)因为 I 和 J 是不同的并且是类型的一部分。当类型相同时怎么办?
p.xx = q.rr 是安全的,但是 p.xx = p.rr 是棘手的(尽管它可能很愚蠢,但它应该仍然不会破坏内存) : 是基于隐式生成的赋值运算符 memcpy 吗?答案似乎是否定的,但如果是,这将是重叠内存间隔之间的 memcpy,这是未定义的行为。
正如 OP 所注意到的,默认的复制赋值运算符也会为表达式 p.xz = q.xz 调用;在这种情况下,它实际上还会复制 .y 成员。如上所述,对于属于 union 的数据类型,不能禁用或修改复制赋值运算符。
此外,我相信有一个更简单的解决方案,即代理模式(您正在部分使用)。 MagicVecX 应该包含指向包含类的指针而不是 ptr;这样你就不需要使用 union 的技巧了。
template<int I, int J> struct MagicVec2
{
friend struct vec2;
inline MagicVec2(vec2* _this): ptr(_this) {}
inline vec2 operator=(const vec2& that);
private:
float *ptr;
};
我通过编译(但不链接)这段代码对此进行了测试,该代码概述了建议的解决方案:https://gist.github.com/1775054 .请注意,代码不完整也未经过测试 - 还应该重写 MagicVecX 的复制构造函数。
关于c++ - 这个 C++ setter/getter 模式破坏了什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/9012752/
类classAprivatedeffooputs:fooendpublicdefbarputs:barendprivatedefzimputs:zimendprotecteddefdibputs:dibendendA的实例a=A.new测试a.foorescueputs:faila.barrescueputs:faila.zimrescueputs:faila.dibrescueputs:faila.gazrescueputs:fail测试输出failbarfailfailfail.发送测试[:foo,:bar,:zim,:dib,:gaz].each{|m|a.send(m)resc
我有一个模型:classItem项目有一个属性“商店”基于存储的值,我希望Item对象对特定方法具有不同的行为。Rails中是否有针对此的通用设计模式?如果方法中没有大的if-else语句,这是如何干净利落地完成的? 最佳答案 通常通过Single-TableInheritance. 关于ruby-on-rails-Rails-子类化模型的设计模式是什么?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.co
我正在使用的第三方API的文档状态:"[O]urAPIonlyacceptspaddedBase64encodedstrings."什么是“填充的Base64编码字符串”以及如何在Ruby中生成它们。下面的代码是我第一次尝试创建转换为Base64的JSON格式数据。xa=Base64.encode64(a.to_json) 最佳答案 他们说的padding其实就是Base64本身的一部分。它是末尾的“=”和“==”。Base64将3个字节的数据包编码为4个编码字符。所以如果你的输入数据有长度n和n%3=1=>"=="末尾用于填充n%
我主要使用Ruby来执行此操作,但到目前为止我的攻击计划如下:使用gemsrdf、rdf-rdfa和rdf-microdata或mida来解析给定任何URI的数据。我认为最好映射到像schema.org这样的统一模式,例如使用这个yaml文件,它试图描述数据词汇表和opengraph到schema.org之间的转换:#SchemaXtoschema.orgconversion#data-vocabularyDV:name:namestreet-address:streetAddressregion:addressRegionlocality:addressLocalityphoto:i
为什么4.1%2返回0.0999999999999996?但是4.2%2==0.2。 最佳答案 参见此处:WhatEveryProgrammerShouldKnowAboutFloating-PointArithmetic实数是无限的。计算机使用的位数有限(今天是32位、64位)。因此计算机进行的浮点运算不能代表所有的实数。0.1是这些数字之一。请注意,这不是与Ruby相关的问题,而是与所有编程语言相关的问题,因为它来自计算机表示实数的方式。 关于ruby-为什么4.1%2使用Ruby返
我的瘦服务器配置了nginx,我的ROR应用程序正在它们上运行。在我发布代码更新时运行thinrestart会给我的应用程序带来一些停机时间。我试图弄清楚如何优雅地重启正在运行的Thin实例,但找不到好的解决方案。有没有人能做到这一点? 最佳答案 #Restartjustthethinserverdescribedbythatconfigsudothin-C/etc/thin/mysite.ymlrestartNginx将继续运行并代理请求。如果您将Nginx设置为使用多个上游服务器,例如server{listen80;server
鉴于我有以下迁移:Sequel.migrationdoupdoalter_table:usersdoadd_column:is_admin,:default=>falseend#SequelrunsaDESCRIBEtablestatement,whenthemodelisloaded.#Atthispoint,itdoesnotknowthatusershaveais_adminflag.#Soitfails.@user=User.find(:email=>"admin@fancy-startup.example")@user.is_admin=true@user.save!ende
它不等于主线程的binding,这个toplevel作用域是什么?此作用域与主线程中的binding有何不同?>ruby-e'putsTOPLEVEL_BINDING===binding'false 最佳答案 事实是,TOPLEVEL_BINDING始终引用Binding的预定义全局实例,而Kernel#binding创建的新实例>Binding每次封装当前执行上下文。在顶层,它们都包含相同的绑定(bind),但它们不是同一个对象,您无法使用==或===测试它们的绑定(bind)相等性。putsTOPLEVEL_BINDINGput
我可以得到Infinity和NaNn=9.0/0#=>Infinityn.class#=>Floatm=0/0.0#=>NaNm.class#=>Float但是当我想直接访问Infinity或NaN时:Infinity#=>uninitializedconstantInfinity(NameError)NaN#=>uninitializedconstantNaN(NameError)什么是Infinity和NaN?它们是对象、关键字还是其他东西? 最佳答案 您看到打印为Infinity和NaN的只是Float类的两个特殊实例的字符串
如果您尝试在Ruby中的nil对象上调用方法,则会出现NoMethodError异常并显示消息:"undefinedmethod‘...’fornil:NilClass"然而,有一个tryRails中的方法,如果它被发送到一个nil对象,它只返回nil:require'rubygems'require'active_support/all'nil.try(:nonexisting_method)#noNoMethodErrorexceptionanymore那么try如何在内部工作以防止该异常? 最佳答案 像Ruby中的所有其他对象