在物理逻辑中,我习惯于这样的代码: -
Position+=Velocity;
Velocity+=Acceleration;
//unit of Velocity = unit of Position / time-step
//unit of Acceleration = unit of Position / (time-step^2)
他们都是 Vector3D - 我的自定义类。 代码工作正常。
然后,我彻底改变了我的库,以匹配流行的物理引擎的标准,例如,子弹。
时间单位现在秒(或分钟等),而不再是时间步。
现在有效代码变为:
Position+=Velocity*time_per_timestep;
Velocity+=Acceleration*time_per_timestep;
当语句有点复杂时,很难意识到我刚刚添加的以下代码是错误的:
Vector3D velocity=rigidBody->getPosition()-calibrator->getLastPosition();
在一些忙碌/晕眩的日子里,我忘了这样添加÷ time_per_timestep
结果,我糟糕的一天开始了。
如何防止此人为错误? (有断言?)
我会创建课程Position
,Velocity
和Acceleration
来强制执行。
例如,Position minus Position
仍会返回Position
。我必须强制Position
不能隐含地投放到Velocity
缺点:过于繁琐,降低可维护性(?)和过度杀伤(?)。
答案 0 :(得分:1)
C ++ 11为User-defined literals为您提供了解决此问题的正确方法。 这有助于您通过在编译时检查它们来防止度量之间的错误操作。
答案 1 :(得分:1)
这是一个单位标签类型:
template<std::ptrdiff_t...Units>
struct unit_tags_t {
template<std::ptrdiff_t...Lhs, std::ptrdiff_t...Rhs>
friend unit_tags_t<(Lhs+Rhs)...>
operator*( unit_tags_t<Lhs...>, unit_tags_t<Rhs...> ) {
return {};
}
template<std::ptrdiff_t...Lhs, std::ptrdiff_t...Rhs>
friend unit_tags_t<(Lhs-Rhs)...>
operator/( unit_tags_t<Lhs...>, unit_tags_t<Rhs...> ) {
return {};
}
friend unit_tags_t operator+( unit_tags_t, unit_tags_t ) { return {}; }
friend unit_tags_t operator-( unit_tags_t, unit_tags_t ) { return {}; }
friend unit_tags_t& operator+=( unit_tags_t& lhs, unit_tags_t ) { return lhs; }
friend unit_tags_t& operator-=( unit_tags_t& lhs, unit_tags_t ) { return lhs; }
friend unit_tags_t operator*( unit_tags_t ) { return {}; }
friend unit_tags_t operator-( unit_tags_t ) { return {}; }
};
它是签名值的编译时列表。
我们挑选一些基本单位,比如时间和距离。
using base_time_t = unit_tags_t<1,0>;
using base_distance_t = unit_tags_t<0,1>;
现在,base_time_t{}+base_distance_t{}
是编译时错误,但base_time_t{}+base_time_t{}
不是。
同样地,base_time_t{}*base_time_t{}
不是编译时错误,而是时间平方的新单位&#34;。
接下来,我们使用扩充运算符定义元组。它有一个标量作为一个字段,一些单位标记作为它的第二个字段。所有算术运算依次对每个单元执行,就像数学产品类型一样。
所以,如果我们有
{2.0, base_distance_t}/{3.14, base_time_t}
我们得到了
{0.636..., unit_tags_t<-1,1>{}}
只需将每个操作员转发到每个组成部分。
将此增强元组称为&#34;单位值&#34;。
下一步是处理标量。定义
的全局常量using time = unit_value<double, base_time_t>;
using distance = unit_value<double, base_distance_t>;
const time second(1./time_per_timestep, {});
const distance meter(1., {});
或者其他什么是正确的。我们可以推导出新的类型:
const auto meter_per_second = meter/second;
const auto meter_per_second_squared = meter/second/second;
using speed = std::decay_t<decltype(meter_per_second)>;
using acceleration = std::decay_t<decltype(meter_per_second_squared)>;
基于这些。
现在,getPosition
会返回distance
值。
velocity
是speed
和
Velocity3D velocity=rigidBody->getPosition()-calibrator->getLastPosition();
是类型错误。
如果您使用的是压缩元组,double
或unit_value<double, blah>
所需的空间应该相同(内存中的布局也应如此)。因此,您可以将Velocity3D
设为unit_value<Vector3d, base_distance_t>
或Vector3d< base_distance_t >
。
像升压装置这样的图书馆会为你做这一切;我只想说明你是如何接近它的。