Ваша основная проблема в том, что вы пытаетесь решить что-то, используя ООП, где ОО абсолютно не требуется!Цель объектной ориентации:
- разделение интересов
- описание иерархий классов
Ни один из этих пунктов не подходит для вашего примера!(Не беспокойтесь, почти все, кто начинает с ОО, ошибаются с первых попыток ...) Это приводит к запутанному коду с ненужными зависимостями.Например:
- Почему ваш
RigidBody
хранит информацию о ротации?Это должно быть просто тело, а ротация должна преобразовывать лежащие в его основе данные. - Почему все ваши классы имеют функцию
SomethingFromQuaternion
?Если вы не можете установить его с помощью EulerAngles
, вам не следует добавлять его к имени.Кроме того, вращение связано с Quaternion
, оно не должно появляться как функция-член.
Правильный путь
Правильный путь крешить вашу проблему - реализовать библиотеку типов вращения и функций для работы с этими типами.
//We need a forward declaration of Quaternion in order to get the conversion
//operator working.
struct Quaternion;
//All of the classes (probably) can be struct as they do not save any private
//data members.
struct EulerAngles
{
//members ...
//constructors...
//we declare a user defined conversion function
explicit operator Quaternion() const;
};
struct Quaternion
{
//members ...
//constructors...
//we declare a user defined conversion function
explicit operator EulerAngles() const;
};
struct RigidBody
{
//members ...
//constructors...
//we need a friend declaration to make our life easier
friend auto inplace_rotate(RigidBody&,const Quaternion&) -> void;
};
//We actually need to define the conversion after the class declaration,
//or the compiler complains...
Quaternion::operator EulerAngles() const
{
//add actual conversion here...
return EulerAngles{};
}
EulerAngles::operator Quaternion() const
{
//add actual conversion here...
return Quaternion{};
}
//the actual rotation function
auto inplace_rotate(RigidBody& body, const Quaternion& rotation) -> void
{
//implement...
//notice the friend declaration in the RigidBody class, which gives us direct access.
//(which we need in the case of private variables)
}
auto rotate(RigidBody body, const Quaternion& rotation) -> RigidBody
{
//using the body without a reference, gives us the copy we need.
//we are left with our rotation.
inplace_rotate(body, rotation);
return body;
}
int main()
{
auto body = RigidBody{};
//You need to get this from the user input.
auto euler_rotation = EulerAngles{};
//Using the user defined conversion operator.
auto quaternion = static_cast<Quaternion>(euler_rotation);
//Do the rotation.
inplace_rotate(body, quaternion);
return 0;
}
Этот подход, по-видимому, не отличается от того, что вы написали, но он резко сократил количество взаимосвязанностимежду классами.Каждый класс теперь можно просматривать самостоятельно и без учета всех остальных.Кроме того, это позволяет нам определять преобразования, используя способ c ++, с определенными пользователем конвертерами.(https://en.cppreference.com/w/cpp/language/cast_operator) Вытаскивая функцию вращения и делая ее автономной, мы можем легко расширить ее в одном месте (вам не нужно обновлять классы тела, чтобы вносить в нее изменения).
InКроме того, вы могли бы даже определить простые вспомогательные перегрузки для вашей EulerAngles
:
auto inplace_rotate(RigidBody& body, const EulerAngles& rotation)
{
return inplace_rotate(body, static_cast<Quaternion>(rotation));
//technically returning here is unnecessary because the return type is void,
//but this allows you to consistenly change the inplace_function at any time
//without needing to update the convenience funtion
}
auto rotate(RigidBody body, const EulerAngles& rotation)
{
return inplace_rotate(body, static_cast<Quaternion>(rotation));
}
Ваша настоящая проблема?
Я думаю, что путаница возникает из-за вашей попытки получитьпользовательский ввод работает как для Quaternions
, так и для EulerAngles
. Существует несколько подходов к решению этой проблемы:
- Напишите пользовательскую функцию, которая возвращает уже повернутые тела.
- Использование
std::variant
(c ++ 17) или альтернатива boost. - Возврат к наследованию классов.
Я бы порекомендовал использовать 1 или 2, однако, если это невозможно,Вы можете сделать следующее (3):
#include <string>
#include <memory>
//An empty base class for rotation values.
struct Rotation {};
struct EulerAngles : Rotation
{
//...
};
struct Quaternion : Rotation
{
//...
};
auto parse_user_input(const std::string& input)
{
if (input == "euler")
return std::make_unique<Rotation>(EulerAngles{});
else if (input == "quaternion")
return std::make_unique<Rotation>(Quaternion{});
else
throw;
}