我正在编写传递大量多参数变量的代码。例如,我可能会通过"方向,"这是六个双打(三个笛卡尔坐标和三个绕轴的旋转)。定义Orientation结构并将其用作参数类型是合理的。但是,由于API的限制,这些参数必须存储为,并且通常作为参数数组的指针传递给函数:
// The sane version, using a struct
double distance_from_origin(const Orientation & o) {
return sqrt(o.x * o.x + o.y * o.y + o.z * o.z);
}
// The version I must write due to API constraints
double distance_from_origin(const double * const p) {
return sqrt(p[0] * p[0] + p[1] * p[1] + p[2] * p[2]);
}
显然,这很容易出错。我有三个可能的解决方案,只有一个。
我可以在某个标题中使用#define或const globals将名称别名转换为索引。
const size_t x = 0;
const size_t y = 1;
const size_t z = 2;
double distance_from_origin(const double * const p) {
return sqrt(p[x] * p[x] + p[y] * p[y] + p[z] * p[z]);
}
这可确保x
始终保持一致,但会污染全局命名空间。我可以将其隐藏在命名空间中,但随后使用它会更加尴尬。
前面提到的一个想法here:
struct Orientation {double x, y, z, rot_x, rot_y, rot_z};
Orientation& asOrientation(double * p) {
return *reinterpret_cast<Orientation*>(p);
}
double distance_from_origin(const double * const p) {
Orientation& o = asOrientation(p)
return sqrt(o.x * o.x + o.y * o.y + o.z * o.z);
}
这有更好的语法,但依赖于C / C ++ struct packing的规则。只要Orientation
是POD,我认为它是安全的。我很担心依赖它。
struct Orientation {
Orientation(double * p): x{p[0]}, y{p[1]}, z{p[2]}, rot_x{p[3]},
rot_y{p[4]}, rot_z{p[5]} {}
double &x, &y, &z, &rot_x, &rot_y, &rot_z
};
double distance_from_origin(const double * const p) {
Orientation o{p};
return sqrt(o.x * o.x + o.y * o.y + o.z * o.z);
}
这不再依赖于struct-packing规则,并且语法很好。但是,它依赖compiler optimizations来确保它的开销为零。
基于GManNickG的this comment。
constexpr double& x(double * p) {return p[0];}
constexpr double& y(double * p) {return p[1];}
constexpr double& z(double * p) {return p[2];}
// ... etc.
double distance_from_origin(const double * const p) {
return sqrt(x(p) * x(p) + y(p) * y(p) + z(p) * z(p));
}
解决方案3对我来说似乎是最好的。它有潜在的缺点吗? 我失去了,除了依赖编译器优化?
是否有其他解决方案优于这三种解决方案中的任何一种?
答案 0 :(得分:1)
首先,我不打算只将数组中的参数复制到一个简单的结构中。将6个双打复制到结构中将非常快。
否则,我建议将数组包装在一个类中,并将参数公开为成员函数:
class Orientation {
const double *p_;
public:
Orientation(const double *p) : p_(p) {}
double x() const { return p_[0]; }
double y() const { return p_[1]; }
double z() const { return p_[2]; }
double rot_x() const { return p_[3]; }
double rot_y() const { return p_[4]; }
double rot_z() const { return p_[5]; }
};
使用您的解决方案3我怀疑编译器可以优化使用解决方案3它将无法分配到期参考文献。Orientation
结构的大小,它的大小将包含6个引用。
答案 1 :(得分:1)
不要尝试将双精度数组作为结构重新插入。当然它会起作用,但它不安全,而且令人困惑。函数签名是完美定义的,它需要六个双精度数组,其中前三个是x,y和z,后三个是欧拉角。这就是你的界面。
double distance_from_origin(const double *p)
{
double x = p[0];
double y = p[1];
double z = p[2];
return sqrt(x*x + y*y + z*z);
}
这里没问题,打电话有点难,但这就是你必须要的 用。
现在该如何实施呢?你有几个选择,取决于多少 您在代码中使用的这些“方向”结构。如果你只是 一两个
emulator64 -avd test-22 -no-skin -no-audio -no-window
这很好,即使是最糟糕的优化器,如果由于某种原因它用完了寄存器,也会优化分配。
然而,问题是这个界面何时何地可能发生 打破?假设我们用四元数描述方向。 当然,你将有七个双打,x,y,z和法线向量,和 圆形的法线向量。很可能有人会想要 这样做。
但在那种情况下,谁会做出这个决定,以及这个过程是什么 更新代码?如果您使用的是Megacorp提供的API, 然后只有Megacorp可以决定去四元数,并且 可能只是在正式发布新版本的API时。如果 你自己写了代码,大概是你自己决定使用Euler 角度而不是四元数,你甚至可以改变表示 回应这个回复。
这才是真正的问题。因为你已经删除了类型信息, 编译器无法帮助你,所以你需要计划它的破坏。