Использование директив #define в качестве & quot; части & quot; кода, а не в начале файла

Глядя на исходный код C ++, я почти всегда вижу #define макросы в начале файла, это имеет смысл в большинстве случаев, и я понимаю, почему это лучший метод, но недавно я столкнулся с ситуацией, когда может быть лучше сохранить определения препроцессора в теле функции.

Я пишу кватернион класс, и мой код для рассматриваемой функции выглядит следующим образом:

Quaternion Quaternion::operator*(const Quaternion& q){
Quaternion resultQ;

// These are just macros to make the code easier to read,
// 1 denotes the quaternion on the LHS of *,
// 2 denotes the quaternion of the RHS 0f *, e.a. Q1 * Q2.
// the letter denotes the value of the real number constant part
// for each seperate part of the quaterion, e.a. (a+bi+cj+dk)

#define a1 this->get_real()
#define a2 q.get_real()
#define b1 this->get_i()
#define b2 q.get_i()
#define c1 this->get_j()
#define c2 q.get_j()
#define d1 this->get_k()
#define d2 q.get_k()

// This arithemtic is based off the Hamilton product
// (http://en.wikipedia.org/wiki/Quaternion#Hamilton_product)
resultQ.set_real(a1*a2 - b1*b2 - c1*c2 - d1*d2);
resultQ.set_i(a1*b2 + b1*a2 + c1*d2 - d1*c2);
resultQ.set_j(a1*c2 - b1*d2 + c1*a2 + d1*b2);
resultQ.set_k(a1*d2 + b1*c2 - c1*b2 + d1*a2);
return resultQ;
}

Я решил добавить в #define потому что, если я подставлю все макросы вручную, каждая строка будет слишком длинной и будет либо обрезана, либо перенесена на следующую строку при чтении. Я мог бы сделать то же самое с переменными, но я решил, что это будет ненужными накладными расходами, поэтому я использовал #define потому что у него нет времени выполнения. Это приемлемая практика? Есть ли лучший способ сделать то, что я здесь делаю, читабельным?

2

Решение

Вместо

#define a1 this->get_real()

записывать

auto const a1 = get_real();

И просто использовать разные имена для каждого значения количества, которое изменяется.

Да, есть случаи, когда местный #define имеет смысл. Нет, это не такой случай. В частности, так как вы забыли #undef макросы, которые они почти наверняка будут вызывать непреднамеренную замену текста в каком-либо другом коде, если это находится в заголовке (как указано).


Кстати, вместо

Quaternion Quaternion::operator*(const Quaternion& q){

Я бы написал

Quaternion Quaternion::operator*(const Quaternion& q) const {

так что также const кватернионы могут быть умножены.

3

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

Макросы не относятся к сфере действия. Эти макросы определены для остальной части файла после их появления. Если в следующей функции у вас есть переменная a1, она испортится. Вам следует #undef все макросы в конце функции.

Лучше как-нибудь создать некоторые служебные функции. В С ++ 11

auto a1 = [this](){ return this->get_real(); }
...

resultQ.set_real(a1()*a2() ...

Не совсем так, как вам нужно () но может быть достаточно хорош для тебя.

Если значения a1 и т. Д. Не меняются во время вычислений, вам следует воспользоваться предложением Альфа.

1

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

Но я чувствую, что ваш код все равно нуждается в переработке, после чего вы увидите, что не нужны ни макросы, ни их альтернативы.

Кватернион — это обобщение комплексного числа, и, как таковое, по существу, оно имеет количество элементов данных (четыре, а не два), количество конструкторов и количество операторов.

Вы могли бы взглянуть на дизайн std::complex чтобы получить идеи. Конструкция кватерниона не должна сильно отличаться. В частности, зачем вам нужны сеттеры / геттеры для доступа к данным из функции-члена? Эти методы именно так что делает выражения длинными! (наряду с ненужным использованием this).

Итак, если данные члены кватерниона a, b, c, dи если есть конструктор с этими четырьмя значениями аргументов, то ваш operator* должно действительно выглядеть так:

Quaternion Quaternion::operator*(const Quaternion& q) const
{
return Quaternion{
a*q.a - b*q.b - c*q.c - d*q.d,
a*q.b + b*q.a + c*q.d - d*q.c,
a*q.c - b*q.d + c*q.a + d*q.b,
a*q.d + b*q.c - c*q.b + d*q.a
};
}

Нет необходимости в макросах, вспомогательных функциях или промежуточных переменных.

1
По вопросам рекламы [email protected]