Доступ к элементам массива по имени

Я пишу код, который передает много многопараметрических переменных. Например, я мог бы передать «ориентацию», которая равна шести двойным (три декартовы координаты и три поворота вокруг осей). Было бы разумно определить структуру 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 всегда согласован, но загрязняет глобальное пространство имен. Я мог бы скрыть это в пространстве имен, но тогда это было бы более неудобно для использования.

Идея, упомянутая ранее Вот:

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 ++. я считать что это безопасно, пока Orientation это стручок Я нервничаю, полагаясь на это.

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);
}

Это больше не зависит от правил упаковки структуры и имеет приятный синтаксис. Тем не менее, он опирается на оптимизация компилятора чтобы убедиться, что у него нет накладных расходов.

На основе этот комментарий 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));
}
  1. Решение 3 кажется мне лучшим. Есть ли у него потенциальный недостаток, который
    Я скучаю, не полагаясь на оптимизацию компилятора?

  2. Есть ли другое решение, которое превосходит любое из этих трех?

3

Решение

Во-первых, я бы не стал сбрасывать со счетов просто копирование параметров из массива в простую структуру. Копирование 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 я сомневаюсь, что компилятор может оптимизировать размер вашего Orientation структура, она будет иметь размер, содержащий 6 ссылок. С Решением 3 его нельзя назначить из-за ссылок.

1

Другие решения

Не пытайтесь переосмыслить массив значений типа double как структуру. Конечно, это будет работать, но это не безопасно, и это сбивает с толку. Сигнатура функции совершенно четко определена, она принимает массив из шести двойников, из которых первые три — это x, y и z, а вторые три — углы Эйлера. Так что это ваш интерфейс.

/*
get distance of an orientation from an origin

Params: p[0] = x, p[1] = y, p[2] = z, p[4].p[4],p[6] Euler angles (unused)
Returns: Euclidean distance of x,y,z from origin.
*/
double distance_for_origin(const double *p);

Здесь нет проблем, звонить немного сложно, но это то, что нужно, чтобы жить
с.

Теперь, как это реализовать? У вас есть несколько вариантов, в зависимости от того, сколько
из этих структур «ориентации» у вас есть в коде. Если вы только
один или два

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);
}

Это хорошо, даже самый худший оптимизатор в мире оптимизирует назначения, если по какой-то причине у него заканчиваются регистры.

Вопрос, однако, в том, когда и где этот интерфейс будет
перерыв? Допустим, мы идем к описанию ориентации с кватернионами.
Теперь, конечно, у вас будет семь двойных, x, y, z и нормальный вектор, и
угол вокруг этого нормального вектора. Вполне вероятно, что кто-то захочет
сделать это.

Но в этом случае, кто будет принимать это решение, и каков процесс
для обновления кода? Если вы используете API, предоставленный Megacorp,
тогда только Мегакорп может принять решение перейти на кватернионы, и
вероятно, только в официальном выпуске новой версии API. Если
Вы сами написали код, предположительно, вы сами определились с Эйлером
углы скорее кватернионов, и вы могли бы даже изменить представление
в ответ на этот ответ.

Это реальная проблема. Потому что вы удалили информацию о типе,
компилятор не может вам помочь, поэтому вам нужно спланировать его нарушение.

1

По вопросам рекламы ammmcru@yandex.ru
Adblock
detector