Russian Qt Forum

Программирование => Общий => Тема начата: Igors от Апрель 17, 2017, 14:17



Название: Объект умеет сам себя рисовать?
Отправлено: Igors от Апрель 17, 2017, 14:17
Добрый день

Получилось как-то совсем некрасиво (псевдокод)
Код
C++ (Qt)
void СWindow::Draw( ... )
{
for (int i = 0; i < mObjects.size(); ++i) {
  CObjectParam1 * param1 = dynamic_cast<CObjectParam1 *> (mObjects[i]);
  if (param1) {
   DrawObjectsParam1(param1);
   continue;
  }
 
// то же для  CObjectParam2  
  CObjectParam2 * param2 = dynamic_cast<CObjectParam2 *> (mObjects[i]);
  if (param2) {
   DrawObjectsParam2(param2);
   continue;
  }
 
// то же для  CObjectParam3
 ...
 и.т.д.
 
Типов CObjectParam-xxx не так уж много (< 10), можно просто перетерпеть, но как-то уж очень коряво. Напрашивается сделать как в классическом примере ООП с фигурами, объявив виртуальный Draw
Код
C++ (Qt)
void СWindow::Draw( ... )
{
for (int i = 0; i < mObjects.size(); ++i) {
  mObjects[i]->Draw(...);
}
 
Однако я быстро убедился что в данном случае это никуда не годится. Дело в том что классы CObjectParam-xxx очень маленькие, напр
Код
C++ (Qt)
// базовый
struct CObjectParam {
 СOpenGLObject * mObject;   // рисуемый объект
 QMatrix4x4 mMatrix;          
};
 
// один из наследников
struct CObjectParam1 : public  CObjectParam  {
 Color mColor;
};
 
Т.е. это просто не более чем 2-3 "опции". С др стороны класс СWindow обширный, имеет десятки методов и неск наследников. Поэтому получив упр-е в виртуальном Draw, классам CObjectParam-xxx нечего делать, весь ф-ционал рисования (работа с СOpenGLObject и.т.п.) сосредоточен в СWindow, да и большинство опций задаются там же (для окна), лишь несколько в CObjectParam

Да, ну и как же сделать правильно?

Спасибо


Название: Re: Объект умеет сам себя рисовать?
Отправлено: Racheengel от Апрель 17, 2017, 20:25
Ну если вся отрисовка так или иначе в CWindow - как вариант, можно добавить в CObjectParam что-то типа virtual int getId() const;
Тогда каждый наследник вернет свой id, а в СWindow::Draw() вместо кучи динамик-кастов появится один свитч по этим id.

Либо вместо id возвращать набор флагов, которые бы указывали, какой параметр можно использовать для отрисовки.


Название: Re: Объект умеет сам себя рисовать?
Отправлено: GreatSnake от Апрель 18, 2017, 10:46
Унифицируйте хранение параметров.
Храните их, например, в QVariant-e.
Код
C++ (Qt)
struct CObjectParam {
 СOpenGLObject * mObject;
 QVariantList params;          
};
 


Название: Re: Объект умеет сам себя рисовать?
Отправлено: Igors от Апрель 18, 2017, 11:54
Либо вместо id возвращать набор флагов, которые бы указывали, какой параметр можно использовать для отрисовки.
id или флагами не отделаться, наследники CObjectParam могут иметь хранимые данные (напр контейнер mColor). Примеры

- СObjectParam этот объект надо просто рисовать, только учитывая установки окна (напр с заливкой или без)

- СObjectParam1 - а этот надо рисовать игнорируя освещенность и используя хранимый mColor. И в одном из режимов (задаваемых для окна) нужно дорисовать wireframe поверх заливки

и.т.д.  штук 8 таких вариантов набегает

Унифицируйте хранение параметров.
Храните их, например, в QVariant-e.
Примерно так и было до того как я создал эту серию мелких классов CObjectParam_xxx (не уверен что это верное решение). Ну и был один CWindow::DrawObjectsParam на всех, капитально запутанный, все if'ы были свалены туда, вставить каждый новый случай превращалось в мучение


Название: Re: Объект умеет сам себя рисовать?
Отправлено: GreatSnake от Апрель 18, 2017, 13:38
Ну и был один CWindow::DrawObjectsParam на всех, капитально запутанный, все if'ы были свалены туда, вставить каждый новый случай превращалось в мучение
Регистрируйте обработчики (std::function) на каждый тип параметра и вызывайте уже их при отрисовке. Через те же лямбды всё можно упростить.


Название: Re: Объект умеет сам себя рисовать?
Отправлено: Racheengel от Апрель 18, 2017, 15:19
id или флагами не отделаться, наследники CObjectParam могут иметь хранимые данные (напр контейнер mColor).

Ну это почти аналог QStyleOption. Там тоже хранятся данные и эти структуры имеют также int type.
Думаю, можно смело пойти этим путем)


Название: Re: Объект умеет сам себя рисовать?
Отправлено: Igors от Апрель 18, 2017, 16:32
Регистрируйте обработчики (std::function) на каждый тип параметра и вызывайте уже их при отрисовке. Через те же лямбды всё можно упростить.
Я понял, ну это "синтаксический сахар", я имел ввиду может как-то принципиально, напр перепланировать классы (хотя не вижу как)

Ну это почти аналог QStyleOption. Там тоже хранятся данные и эти структуры имеют также int type.
Да, действительно, ситуация похожа, как-то не подумал об этом
Думаю, можно смело пойти этим путем)
Каким "этим"? Что я помню из исходников стилей - меня совсем не вдохновляет (суровый процедурный стиль и кастование)


Название: Re: Объект умеет сам себя рисовать?
Отправлено: GreatSnake от Апрель 18, 2017, 17:17
Я понял, ну это "синтаксический сахар", я имел ввиду может как-то принципиально, напр перепланировать классы (хотя не вижу как)
Похоже Вы меня не поняли.
Я имел в виду, что достаточно будет иметь только один CObjectParam, в котором будут заданы все параметры для отрисовки.
Плюс набор обработчиков для каждого типа параметров.
Код
C++ (Qt)
struct CObjectParam {
 COpenGLObject * mObject;
 QVariantList mParams;          
};
 
void CWindow::registerParamProcessor( int type, std::function< bool( CObjectParam* o, const QVariant& v ) > f )
{
  mParamProcessors[ type ] = f;
}
 
void CWindow::Draw( ... )
{
for (int i = 0; i < mObjects.size(); ++i) {
  auto& obj = mObjects[i];
  for( auto& v : obj->mParams ) {
     auto f = mParamProcessors[ v.userType() ];
     if( !f( obj, v ) )
         break;
  }
}
}


Название: Re: Объект умеет сам себя рисовать?
Отправлено: Igors от Апрель 20, 2017, 12:12
Я имел в виду, что достаточно будет иметь только один CObjectParam, в котором будут заданы все параметры для отрисовки.
За это придется заплатить "дешифрированием" mParams в runtime. Может сделать указатель на метод-обработчик СWindow членом базового класса CObjectParam? напр
Код
C++ (Qt)
typedef bool CWindow::(*TDrawObject)( CObjectParam * );
struct CObjectParam {
 СOpenGLObject * mObject;
 QMatrix4x4 mMatrix;          
 TDrawObject mDrawer;
};
Но тут небольшие неудобства. Во-первых если метод рисует конкретного наследника (напр  CObjectParam1), то хотелось бы его и подавать (а не приводить от базового). Во-вторых, есть ситуация (правда всего одна) когда метод рисования имеет еще один параметр. Может тогда как-то через "туплы"?

Какой-то острой необходимости в этом нет, корявенький теперешний код вполне устраивает, просто интересно как же "по уму"  :)


Название: Re: Объект умеет сам себя рисовать?
Отправлено: Racheengel от Апрель 20, 2017, 13:25
Каким "этим"? Что я помню из исходников стилей - меня совсем не вдохновляет (суровый процедурный стиль и кастование)

Я имел в виду - проверять поле типа и в зависимости от него брать нужные параметры.
Ну, статик каст придется сделать, но эта операция ничего не стоит.
(Ну или, как вариант, можно параметры представить в виде QVariant - но это на любителя)


Название: Re: Объект умеет сам себя рисовать?
Отправлено: Old от Апрель 20, 2017, 14:49
Я имел в виду - проверять поле типа и в зависимости от него брать нужные параметры.
Ну, статик каст придется сделать, но эта операция ничего не стоит.
(Ну или, как вариант, можно параметры представить в виде QVariant - но это на любителя)
Для чего все эти сложности? :)

Если рисование обеспечивается средствами CWindow, можно просто передавать ссылку на него в параметре метода draw:
Код
C++ (Qt)
struct CObjectParam
{
 СOpenGLObject * mObject;   // рисуемый объект
 QMatrix4x4 mMatrix;          
 
 virtual void draw( CWindow &surface ) const = 0;
};
 
// один из наследников
struct CObjectParam1 : public  CObjectParam
{
 Color mColor;
 
 virtual void draw( CWindow &surface ) const { surface.DrawObjectsColor( mColor ); }
};
 
struct CObjectParam2 : public  CObjectParam
{
 Texture mTex;
 
 virtual void draw( CWindow &surface ) const { surface.DrawObjectsTexture( mTex ); }
};
 

Код
C++ (Qt)
void СWindow::Draw( ... )
{
 for (int i = 0; i < mObjects.size(); ++i)
 {
   mObjects[i]->draw( *this );
 }
}
 


Название: Re: Объект умеет сам себя рисовать?
Отправлено: Racheengel от Апрель 20, 2017, 15:09
Я имел в виду - проверять поле типа и в зависимости от него брать нужные параметры.
Ну, статик каст придется сделать, но эта операция ничего не стоит.
(Ну или, как вариант, можно параметры представить в виде QVariant - но это на любителя)
Для чего все эти сложности? :)

Так я насколько понимаю проблему - Igors как раз хочет избежать множества виртуальных Draw.


Название: Re: Объект умеет сам себя рисовать?
Отправлено: Old от Апрель 20, 2017, 15:23
Так я насколько понимаю проблему - Igors как раз хочет избежать множества виртуальных Draw.
Тогда проще не использовать C++. :)


Название: Re: Объект умеет сам себя рисовать?
Отправлено: Racheengel от Апрель 20, 2017, 20:38
Так я насколько понимаю проблему - Igors как раз хочет избежать множества виртуальных Draw.
Тогда проще не использовать C++. :)

Преимущества плюсов, конечно, напрашиваются)
Но все-таки иногда, в очень редких случаях, но все же, полиморфных вызовов хочется избежать (если ну очень критична производительность) - все-таки тупой свитч и статик-каст отработают +несколько+ быстрее... Возможно, тут именно одно из подобных исключений :)


Название: Re: Объект умеет сам себя рисовать?
Отправлено: Old от Апрель 20, 2017, 21:38
все-таки тупой свитч и статик-каст отработают +несколько+ быстрее...
Чем вызов виртуального метода? Сильно сомневаюсь.


Название: Re: Объект умеет сам себя рисовать?
Отправлено: Igors от Апрель 21, 2017, 09:26
Преимущества плюсов, конечно, напрашиваются)
Но все-таки иногда, в очень редких случаях, но все же, полиморфных вызовов хочется избежать (если ну очень критична производительность) - все-таки тупой свитч и статик-каст отработают +несколько+ быстрее... Возможно, тут именно одно из подобных исключений :)
Учитывая грядущую отрисовку тонн полигонов, вопрос о какой-то "заточке по скорости" здесь не стоит. А достоинства свитча - не столько его быстрота как компактность (не путать с "краткость"). По крайней мере все руление собрано в одном месте, нередко удается запастись общим кодом для неск веток. А расхристанные виртуалы как минимум трудно обозреть.   

Я имел в виду - проверять поле типа и в зависимости от него брать нужные параметры.
Так это по существу сейчас и сделано, конечно можно вместо dynamic_cast оформить ID + switch, но это не принципиально   

Так я насколько понимаю проблему - Igors как раз хочет избежать множества виртуальных Draw.
Просто образовался кусок кода с серией приведений (пусть относительно небольшой), вот думаю как этого можно избежать. В принципе я не против виртуальных Draw но пока из них ничего хорошего не выходит.


Название: Re: Объект умеет сам себя рисовать?
Отправлено: __Heaven__ от Апрель 21, 2017, 09:28
Есть ещё вариант использовать rtti и map
Код
C++ (Qt)
static const std::map<std::type_info, std::function> funMap = {
   {typeid(CObjectParam1), DrawObjectsParam1},
   ....
};


Название: Re: Объект умеет сам себя рисовать?
Отправлено: Igors от Апрель 22, 2017, 09:55
Есть ещё вариант использовать rtti и map
Код
C++ (Qt)
static const std::map<std::type_info, std::function> funMap = {
   {typeid(CObjectParam1), DrawObjectsParam1},
   ....
};
Так надо прилагать усилия чтобы связать каждый DrawObjectsParam_xxx с окном. Плюс заряжать и хранить мапу(ы) - возможно свою для каждого окна. Не вижу где "выйгрыш"


Название: Re: Объект умеет сам себя рисовать?
Отправлено: twp от Апрель 27, 2017, 22:01
Да, виртуальные вызовы не зря напрашиваются  :)
Этот случай настолько частый, что даже придумали для него паттерн Visitor (https://ru.wikipedia.org/wiki/Посетитель_(шаблон_проектирования)). Если открыть ссылку и затем ниже пример на C++, то код практически 1 в 1 тот, что предложил Old.


Название: Re: Объект умеет сам себя рисовать?
Отправлено: Igors от Апрель 28, 2017, 12:25
Да, виртуальные вызовы не зря напрашиваются  :)
Этот случай настолько частый, что даже придумали для него паттерн Visitor (https://ru.wikipedia.org/wiki/Посетитель_(шаблон_проектирования)). Если открыть ссылку и затем ниже пример на C++, то код практически 1 в 1 тот, что предложил Old.
Не уверен что visitor тут подходящий паттерн. Напр с какой стати каждый из крохотных классов QObjectPatamXX посвящен в подробности мощного CWindow? Чего это ведомые данные что-то решают? Что если напр один из наследников CWindow рисует тот же QObjectPatamXX иначе?


Название: Re: Объект умеет сам себя рисовать?
Отправлено: Old от Апрель 28, 2017, 12:45
Напр с какой стати каждый из крохотных классов QObjectPatamXX посвящен в подробности мощного CWindow?
Это получилось с легкой руки разработчика, который засунул все в класс MainWindow (CWindow).

Чего это ведомые данные что-то решают? Что если напр один из наследников CWindow рисует тот же QObjectPatamXX иначе?
А они ничего и не решают. Они для рисования используют публичные механизмы предоставленные CWindow и если наследник CWindow изменит "рисование", то QObjectPatamXX нарисует ровно тем, что предоставлено.


Название: Re: Объект умеет сам себя рисовать?
Отправлено: __Heaven__ от Апрель 28, 2017, 13:59
Напр с какой стати каждый из крохотных классов QObjectPatamXX посвящен в подробности мощного CWindow?
Тема: Объект умеет сам себя рисовать?