Как сделать класс адаптера соответствующим образом поддерживающим как const, так и не const const?
Пример бетона
RigidBody
- класс, описывающий физическое свойство объекта.
Вот его очень упрощенная версия (1D): -
class RigidBody{
float position=1;
public: float getPosition()const{ return position;}
public: void setPosition(float ppos){ position=ppos;}
};
Adapter
инкапсулирует RigidBody
.
Он обеспечивает мало искаженную функциональность get/set position
: -
class Adapter{
public: RigidBody* rigid; int offset=2;
public: float getPosition(){
return rigid->getPosition()+offset; //distort
}
public: void setPosition(float ppos){
return rigid->setPosition(ppos-offset); //distort
}
};
Я могу установить положение RigidBody
косвенно, используя Adapter
: -
int main() {
RigidBody rigid;
Adapter adapter; //Edit: In real life, this type is a parameter of many function
adapter.rigid=&rigid;
adapter.setPosition(5);
std::cout<<adapter.getPosition();//print 5
return 0;
}
Все работает (демонстрация).
Цель
Я хочу создать новую функцию, которая получит const
RigidBody* rigid
.
Я должен уметь читать с него (например, getPosition()
) с помощью адаптера.
Однако я не знаю, как это сделать элегантно.
void test(const RigidBody* rigid){
Adapter adapter2;
//adapter2.rigid=rigid; //not work, how to make it work?
//adapter2.setPosition(5); //should not work
//adapter2.getPosition(); //should work
}
Мои плохие решения
Решение A1 (2 адаптера + 1 виджет)
Создайте виджет: -
class AdapterWidget{
public: static Adapter createAdapter(RigidBody* a);
public: static AdapterConst createAdapter(const RigidBody* a);
};
AdapterConst
может только getPosition()
, а AdapterConst
может быть и получен и установлен.
Я могу использовать его как: -
void test(const RigidBody* rigid){
auto adapter=AdapterWidget::createAdapter(rigid);
Легко использовать.
Недостаток: Код AdapterConst
и Adapter
будет очень продублирован.
Решение A2 (+ наследование)
Это улучшение предыдущего решения.
Пусть Adapter
(имеет setPosition()
) получается из AdapterConst
(имеет getPosition()
).
Недостаток: Это не краткий. Я использую 2 класса для одной задачи!
Это может показаться тривиальным, но в большей базе кода это совсем не забавно.
В частности, расположение getPosition()
будет далеким от setPosition()
, например, в разных файлах.
Это вызывает проблему ремонтопригодности.
Решение B (шаблон)
Создайте класс шаблона. Есть много способов, например.: -
-
Adapter<T =RigidBody OR const RigidBody >
-
Adapter<bool=true is const OR false is non-const >
Недостаток: Во всех отношениях это неэлегантно. Это перебор. (?)
Я буду страдать от недостатка шаблона, например. все в заголовке.
Решение C1 (const_cast)
Я стараюсь избегать этого. Это зло.
class Adapter{
public: RigidBody* rigid;
void setUnderlying(const RigidBody* r){
rigid=const_cast< RigidBody*>(r);
}
....
};
Решение C2 (+ manual assert)
Я могу добавить некоторое утверждение вручную.
Он просто подчеркивает, насколько это непрофессионально: -
bool isConst;
void setUnderlying(const RigidBody* r){
...
isConst=true;
}
void setUnderlying(RigidBody* r){
...
isConst=false;
}
void setPosition(float a){
if(isConst){ /*throw some exception*/ }
....
}
Решение D (убежать)
- Lazy: измените с
test(
const
RigidBody* rigid)
наtest(RigidBody* rigid)
. - Сумасшедший: измените
RigidBody::setPosition()
наconst
.
В любом случае моя программа больше не будет const
-correct,
но достаточно одного класса Adapter
.
Вопрос
Нужно ли мне делать одну из этих вещей везде, где я сталкиваюсь с шаблоном const/non-const?
Пожалуйста, предоставьте красивое решение. (полный код не требуется, но я не против)
Извините за длинный пост.
Изменить: В реальной жизни Adapter
является параметром для многих функций.
Он проходит как игрушка.
Большинство таких функций не имеют знаний о RigidBody
, поэтому не совсем удобно изменять из пакета, вызывающего someFunction(adapter)
в someFunction(offset,rigidbody)
.