Я пишу код, который передает много многопараметрических переменных. Например, я мог бы передать «ориентацию», которая составляет шесть двухместных (три декартовых координаты и три поворота вокруг осей). Было бы разумно определить структуру 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]);
}
Очевидно, что это чревато ошибками. У меня есть три потенциальных решения, с одним из любимых.
Решение 1
можно использовать #define или константные глобалам, в заголовке где-то, чтобы имена псевдонимов для индексов.
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
всегда последовательны, но загрязняют глобальное пространство имен. Я мог бы скрыть это в пространстве имен, но тогда это более неудобно использовать.
Решение 2
Идея упоминалось ранее 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 упаковки. I думаю, что это безопасно, пока Orientation
является POD. Я нервничаю из-за этого.
Раствор 3
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);
}
Это больше не опирается на правила-структуры упаковки, и имеет приятный синтаксис. Однако он полагается на compiler optimizations, чтобы гарантировать, что он имеет нулевые служебные данные.
Solution 4
на основе this comment по GManNickG.
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));
}
Вопросы
Solution 3 кажется, что лучше для меня. Имеет ли потенциальный недостаток то, что Мне не хватает, за исключением оптимизации компилятора?
Есть ли другое решение, превосходящее любой из этих трех?
Является ли использование индексов действительно ошибкой? – GManNickG
В большинстве случаев, нетрудно получить правильные показатели. Но фактические параметры немного сложнее, чем мои примеры. Кроме того, математика более сложна. Устранение неполадок намного проще, когда вы просматриваете матричные умножения, которые используют 'orient.x' вместо' p [0] '. – swarn
Что-то рассмотреть - это просто свободная функция: 'double x (const double * const p) {return p [0]; } 'и' x (p) '. – GManNickG