Interoperability
Важная цель дизайна Boost QVM заключается в том, что он бесшовно работает с кватернионом 3-й стороны, векторными и матричными типами и библиотеками. Даже когда такие библиотеки перегружают те же операторы C++, что и Boost QVM, можно безопасно ввести в объем все boost::qvm, указав:
using namespace boost::qvm;
Вышеприведенная директива не вводит двусмысленностей с функциями и перегрузками оператора, которые может определить библиотека 3-й стороны, поскольку:
- qvmФункциональные перегрузки и все операторские перегрузкиSFINAE/enable_if, что приводит к их исчезновению, если выражение не использует типы, которые имеют соответствующие QVM-специфическиеТиповые чертыопределено;
- Всякий раз, когда такие перегрузки совместимы с заданным выражением, их подпись является чрезвычайно общей, что означает, что любая другая (определяемая пользователем) совместимая перегрузка будет лучше соответствовать любому разрешению перегрузки.
Привлечение пространства имен boost::qvm позволяет смешивать векторные и матричные типы, которые поступают из разных API, в общую, безопасную для типа структуру. В этом случае, однако, следует рассмотреть, какие типы должны быть возвращены бинарными операциями, которые возвращают объект по стоимости. Например, если умножить матрицу 3x3 m1 типа user_matrix1 на матрицу 3x3 m2 типа user_matrix2, какой тип должна вернуть эта операция?
Ответ заключается в том, что по умолчанию Boost QVM возвращает какой-то совместимый тип матрицы, поэтому всегда безопасно писать:
auto & m = m1 * m2;
Тем не менее, тип, выведенный по умолчанию, неявно преобразуется в любой совместимый тип матрицы, поэтому при стоимости временного:
user_matrix1 m = m1 * m2;
Хотя временный объект может быть оптимизирован многими компиляторами, его можно полностью избежать, специализируясь на шаблоне deduce_mat2. Например, чтобы указать, что умножение user_matrix1 на user_matrix2 всегда должно производить объект user_matrix1, вы можете указать:
namespace
boost
{
namespace
qvm
{
template <>
struct deduce_mat2<user_matrix1,user_matrix2,3,3>
{ typedef user_matrix1 type; };
template <>
struct deduce_mat2<user_matrix2,user_matrix1,3,3>
{ typedef user_matrix1 type; };
}
}
Наконец, каждый раз, когда вам нужно создать матрицу определенного типа C++ из любого другого совместимого типа матрицы, вы можете использовать функцию convert_to:
user_matrix2 m=convert_to<user_matrix2>(m1 * m2);
Возможно, удивительно, что унарные операции, возвращающие объект по стоимости, имеют аналогичную, хотя и более простую проблему. Это потому, что аргумент, с которым они вызваны, не может быть копируемым, как в:
float m[3][3];
auto & inv = inverse(m);
Повысьте QVM «просто работает», возвращая объект подходящего типа матрицы, который можно копировать. Этот процесс дедукции также можно контролировать, специализируясь на шаблоне deduce_mat.
Примечание: Привлечение всего пространства имен boost::qvm может привести к двусмысленности при доступе к types (в отличие от функций), определенных в библиотеках 3-х сторон. В этом случае вы можете безопасно принести пространство имен boost::qvm::sfinae в объеме, который содержит только функции и перегрузки оператора, которые используют SFINAE/enable_if.
Предупреждение: Помните о потенциальном нарушении ODR при использовании deduce_quat2, deduce_vec2 и deduce_mat2 в 3-х партийных библиотеках. Например, это может произойти, если lib1 определяет deduce_vec2::type как lib1::vec и в той же программе lib2 определяет deduce_vec2::vec,lib2::vec>::type как lib2::vec. Лучше всего избегать таких специализаций из lib1 и lib2. Конечно, всегда безопасно для lib1 и lib2 использовать convert_to для преобразования между типами lib1::vec и lib2::vec по мере необходимости.