Russian Qt Forum
Апрель 23, 2024, 13:40 *
Добро пожаловать, Гость. Пожалуйста, войдите или зарегистрируйтесь.
Вам не пришло письмо с кодом активации?

Войти
 
  Начало   Форум  WIKI (Вики)FAQ Помощь Поиск Войти Регистрация  

Страниц: 1 [2] 3 4 5   Вниз
  Печать  
Автор Тема: Переделать старые структуры  (Прочитано 32870 раз)
ViTech
Гипер активный житель
*****
Offline Offline

Сообщений: 858



Просмотр профиля
« Ответ #15 : Август 24, 2017, 13:15 »

Сейчас по существу реализован вариант с "расслоением", т.е. заводится контейнер на каждую компоненту вектора (x, y, z). Очевидно такая работа "покомпонентно" весьма затратна с любой точки зрения. Помнится Вы говорили что template схопывает код - ну вот Вам и карты в руки, схлопывайте.

Не думаю, что работа "покомпонентно" затратна с любой точки зрения. Нужно разбираться в конкретных вариантах использования. Например, для расхода по памяти что лучше: несколько больших непрерывных кусков или куча мелких осколков? Если будет необходимость передавать массивы координат в OpenGL, то лучше это делать из покомпонентных контейнеров или собирать из разбросанных кусков? Это может показаться преждевременной оптимизацией, но если с этим ошибиться в начале, то потом может ожидать значительная переработка. Заранее могу сказать, что есть способ обращаться к покомпонентным контейнерам как к структуре(аналогу) по "вертикальному" срезу.

ViTech, для краткости цитирование опускаю. Разумеется хотелось бы иметь нормальную структуру (а не кучу разобщенных массивов). Предположим есть 1 тип данных (напр вектор) и 1 тип сплайна. Тогда
Код
C++ (Qt)
struct Key {
   double time;
   Vector vec;
 
   int splineOption1;
   ...
   double splineOptionN;
};
Ну и кривая - просто контейнер Key, возможно еще с доп данными для всех ключей. Однако данные могут быть 3 типов, а сплайны 6 типов. Собсно это первое (и наверное главное) что хотят от template, задача очень банальна.

Как бы не хотелось, чтобы "эти чёртовы кривые просто были и работали", и не отвлекали от реальных дел, но придётся уделить им внимание и силы, чтобы работали они как "надо и удобно" Улыбающийся. Нужно проработать предметную область этих кривых. Например, определить составные части:
Код
C++ (Qt)
template <class Key, class Data, class Option>
struct CurvePoint
{
   Key key;
   Data data;
   Option option;
};
 
enum class SplineType
{
   Type_1,
   Type_2;
   ...
};
 
template <class Point, SplineType _SplineType>
struct Curve
{
   vector<Point> points;
};
 

Где Option - это структура из набора опций, но можно и по другому представить.

Далее придётся писать кучу специализаций Curve для заданных Point, SplineType (или чего-то ещё), выявлять в них общий код, выносить его отдельно и т.п. Легко не будет, но без шаблонов может получиться значительно сложнее Улыбающийся.
Записан

Пока сам не сделаешь...
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #16 : Август 25, 2017, 10:13 »

Если будет необходимость передавать массивы координат в OpenGL, то лучше это делать из покомпонентных контейнеров или собирать из разбросанных кусков?
Зачем приводить в пример OpenGL с которым Вы никогда не имели дела? "Разбросанные куски" для него не проблема (для этого есть stride), а вот склеивать 3D координаты из массивов x, y, z - такого еще не видел Улыбающийся Да и вообще если (когда-нибудь) нужно будет передавать туда контрольные точки - то будет задействовано VBO, и для данных на карте формат будет свой

Не думаю, что работа "покомпонентно" затратна с любой точки зрения.
Вместо одного контейнера 3 или 4. Одни и те же ключи (время) и флаги в каждом. Операции вставки, удаления, поиска и др дублируется для каждого контейнера. Единственное что может это оправдать - соображение "зато без всяких template!". Но уж очень коряво

Код
C++ (Qt)
template <class Key, class Data, class Option>
struct CurvePoint
{
   Key key;
   Data data;
   Option option;
};
 
Такое рисовать всякий может, ну и что с ним дальше делать? Curve должна быть членом всяких классов, делать их всех тоже шаблонными совершенно нереально.
Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #17 : Август 25, 2017, 10:19 »

Итого:
class UnionData{union {double d; Color c; Vector3d v}} с тремя конструкторами и тремя функциями get и математикой(для сплайна)
union не позволит иметь хоть что-то "выше травы" POD, напр есть хотя бы конструктор - все, с приветом, членом union этот класс быть не может
Тогда и шаблон в принципе не нужен.
Непонимающий Не понял

class Key { double time, ..... }
Spline { QMap<Key, UnionData> map; void set(Key, UnionData); UnionData get(Key); }
А можно как-то более внятно?  Улыбающийся
Записан
deMax
Хакер
*****
Offline Offline

Сообщений: 600



Просмотр профиля
« Ответ #18 : Август 25, 2017, 11:06 »

Ну например, что то типа этого. (не скорую руку, нет констант, не все области видимости сделал, но компилиться и смысл думаю понятен):
Код:
struct Options {
    int arg1, arg2;
};

struct Color{char r,g,b,a;};
struct Vector3{float x,y,z;};

class UnionData {
public:
    enum DataType { NO,DOUBLE, COLOR, VECTOR3 };
private:
    enum TYPE_SPLINE{T1, T2} typeSpline;
    Options options;

    DataType dataType = NO;
public:
    union {double d; Color c; Vector3 v3;};
    bool isDataType(DataType dataType) {return dataType == this->dataType;}

    UnionData() { dataType = NO; }
    UnionData(double d){ dataType = DOUBLE; this->d = d;}
    UnionData(Color c){ dataType = COLOR; this->c = c;}
    UnionData(Vector3 v3){ dataType = VECTOR3; this->v3 = v3;}
};

class Spline {
    QMap<double /*time*/, UnionData> map;

    void getData(double time, double *data){if(map[time].isDataType(UnionData::DOUBLE)) *data = map[time].d;}
    void getData(double time, Color *data){if(map[time].isDataType(UnionData::COLOR)) *data = map[time].c;}
    void getData(double time, Vector3 *data){if(map[time].isDataType(UnionData::VECTOR3)) *data = map[time].v3;}
public:
    template <typename T> void set(double time, T data) { map[time] = UnionData(data); }
    template<typename T> T get(double time) {T data; getData(time, &data); return data; }
};
Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #19 : Август 25, 2017, 12:08 »

и смысл думаю понятен
Понятен, это еще один вариант "без всяких template" (get/set просто удобство доступа), по существу все на "варианте" (кстати самопальный "вариант" здесь вполне оправдан).

- хранится "по максимуму", что вообще-то не есть хорошо

- никакой проверки на этапе компиляции, напр если "вектору" попытались назначить "цвет" - придется ловить  это в runtime (понатыкать assert'ов)

Поэтому гордиться таким решением точно не получится, придется объяснять типа "ну это вот удобно, практично" и.т.п. Улыбающийся Но и это не так, забыли про тип сплайна - а он ведь тоже зависит от типа данных, напр
Код:
double	inRatioVector;	// Incoming Vector
double outRatioVector; // Outgoing Vector
Это ведь для типа данных double, для вектора эти параметры тоже будут вектора (3 double)
Записан
ViTech
Гипер активный житель
*****
Offline Offline

Сообщений: 858



Просмотр профиля
« Ответ #20 : Август 25, 2017, 13:37 »

Вы так заранее всё знаете, кто с чем имел дело и с чем нет, какова реализация того или иного решения и к чему оно приводит, что становится не интересно отвечать в Ваших темах Улыбающийся.

Зачем приводить в пример OpenGL с которым Вы никогда не имели дела? "Разбросанные куски" для него не проблема (для этого есть stride), а вот склеивать 3D координаты из массивов x, y, z - такого еще не видел Улыбающийся Да и вообще если (когда-нибудь) нужно будет передавать туда контрольные точки - то будет задействовано VBO, и для данных на карте формат будет свой
Если нет разницы передать данные в VBO одним(несколькими) большим куском (грубо говоря memcpy) или итерировать по мапе и копировать поэлементно/повекторно, то на этот аспект можно не обращать внимание. Ещё я никогда не имел дела с шейдерами тесселяции и геометрическим. Но подозреваю, что если их добавили в графический конвейер, то кому-то это нужно.

Вместо одного контейнера 3 или 4. Одни и те же ключи (время) и флаги в каждом. Операции вставки, удаления, поиска и др дублируется для каждого контейнера. Единственное что может это оправдать - соображение "зато без всяких template!". Но уж очень коряво
Если Вы других вариантов реализации здесь не видите, то и ладно. По-моему, тут есть пространство для размышлений. Часть из них я высказал ранее.

Такое рисовать всякий может, ну и что с ним дальше делать? Curve должна быть членом всяких классов, делать их всех тоже шаблонными совершенно нереально.

Дальше можно сделать объявления:
Код
C++ (Qt)
using QuadraticBezierPoint = CurvePoint<DateTimeKey, Vector3D, QuadraticBezierOption>;
using CubicBezierPoint = CurvePoint<DateTimeKey, Vector3D, CubicBezierOption>;
using OneMorePoint = CurvePoint<DateTimeKey, Vector3D, OneMoreOption>;
 
using QuadraticBezierCurve = Curve<QuadraticBezierPoint, SplineType::QuadraticBezier>
using CubicBezierCurve = Curve<CubicBezierPoint, SplineType::CubicBezier>
using OneMoreCurve = Curve<OneMorePoint, SplineType::OneMoreCurve>
 
и использовать их в членах классов, параметрах методов и т.п.
« Последнее редактирование: Август 25, 2017, 13:48 от ViTech » Записан

Пока сам не сделаешь...
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #21 : Август 25, 2017, 17:26 »

Дальше можно сделать объявления:
Код
C++ (Qt)
using QuadraticBezierPoint = CurvePoint<DateTimeKey, Vector3D, QuadraticBezierOption>;
using CubicBezierPoint = CurvePoint<DateTimeKey, Vector3D, CubicBezierOption>;
using OneMorePoint = CurvePoint<DateTimeKey, Vector3D, OneMoreOption>;
 
using QuadraticBezierCurve = Curve<QuadraticBezierPoint, SplineType::QuadraticBezier>
using CubicBezierCurve = Curve<CubicBezierPoint, SplineType::CubicBezier>
using OneMoreCurve = Curve<OneMorePoint, SplineType::OneMoreCurve>
 
и использовать их в членах классов, параметрах методов и т.п.
Так "с водой выплеснули ребенка", теперь у меня уже нет ОДНОГО класса Curve, а есть масса разных классов которые можно использовать только конкретно, "по месту". Разумеется это не устраивает, простейший резон уже приводился - поместить в контейнер. Обратите внимание что исходный класс, несмотря на всю корявость/убогость реализации, не имеет этого дефекта - он один. Также говорилось что юзер может менять тип сплайна, поэтому его параметризация неприемлема.
Записан
ViTech
Гипер активный житель
*****
Offline Offline

Сообщений: 858



Просмотр профиля
« Ответ #22 : Август 25, 2017, 19:40 »

Так "с водой выплеснули ребенка", теперь у меня уже нет ОДНОГО класса Curve, а есть масса разных классов которые можно использовать только конкретно, "по месту". Разумеется это не устраивает, простейший резон уже приводился - поместить в контейнер. Обратите внимание что исходный класс, несмотря на всю корявость/убогость реализации, не имеет этого дефекта - он один. Также говорилось что юзер может менять тип сплайна, поэтому его параметризация неприемлема.

Поместить в контейнер кривые, это примерно то же самое, что поместить в контейнер все виджеты формы. Можно это сделать, но контейнер тогда будет из элементов наиболее общего и близкого типа - QWidget *. Со всеми вытекающими последствиями: что можно с элементами этого контейнера делать и как отличать один от другого.

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

Пусть есть конкретные классы QuadraticBezierCurve и CubicBezierCurve, оба наследуются от интерфейса Curve, который не шаблонный. Какие методы у этого Curve? Что можно/нужно с ним делать? В общем-то об этом и вопрошали во первых сообщениях сей темы Улыбающийся.

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

Если юзер может изменять тип сплайна, то всегда ли можно это делать? Например, для построения сегмента QuadraticBezierCurve нужны 3 опорные точки, а для CubicBezierCurve уже 4. Где при преобразовании QuadraticBezierCurve -> CubicBezierCurve брать эту 4-ю точку? Или в сплайне был один набор параметров (с Ratio, Weight и пр.), а поменять надо в сплайн с другим набором параметров. Можно ли такое сделать? И как тогда должна выглядеть сигнатура метода в Curve, который такое преобразование выполняет?
Записан

Пока сам не сделаешь...
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #23 : Август 26, 2017, 08:39 »

Если юзер может изменять тип сплайна, то всегда ли можно это делать? Например, для построения сегмента QuadraticBezierCurve нужны 3 опорные точки, а для CubicBezierCurve уже 4. Где при преобразовании QuadraticBezierCurve -> CubicBezierCurve брать эту 4-ю точку? Или в сплайне был один набор параметров (с Ratio, Weight и пр.), а поменять надо в сплайн с другим набором параметров. Можно ли такое сделать? И как тогда должна выглядеть сигнатура метода в Curve, который такое преобразование выполняет?
Ну чего Вы мудрите? Если юзер меняет тип сплайна, то конечно все старые опции будут утеряны, замещены параметрами нового сплайна по умолчанию. Никаких "конверсий" ему не обещали, это др задача (причем достаточно сложная). Сигнатура - обычный  сеттер
Код
C++ (Qt)
void Curve::SetSplineType( SplineType type );

Поместить в контейнер кривые, это примерно то же самое, что поместить в контейнер все виджеты формы. Можно это сделать, но контейнер тогда будет из элементов наиболее общего и близкого типа - QWidget *. Со всеми вытекающими последствиями: что можно с элементами этого контейнера делать и как отличать один от другого.
Нормальная параллель/аналогия. И что плохого (или глупого) в контейнере <QWidget *>? Много действий существует для всех его наследников. Не все конечно. Напр не все виджеты имеют методы value/setValue и значения эти разных типов. Разумеется "отличать" - забота вызывающего, напр
Код
C++ (Qt)
int GetIntValue( const QWidget * widget );
 
Даже если действовать примитивно, перебирая dynamic_cast, в такой ф-ции есть смысл. Мы можем не хранить виджеты как члены классы, а слить их в контейнер и дергать напр по имени. Виджет не имеет int value? Ну assert и все дела, вызывающий понимает что делает. Да, ф-ция не будет работать автоматом для тех виджетов что когда-нибудь будут созданы и тоже иметь value - ничего, переживем, допишем код.

Здесь подобная ситуация, только вариантов намного меньше. Операции "изменить время ключа" или "удалить ключ" явно общие и от типа данных не зависят. Вот что делать со значениями? Очевидно что без вызовов напр таких
Код
C++ (Qt)
double Curve::GetDoubleValue( double time ) const;
 
не обойтись. Как их реализовать не влезая в switch-разборки?  
« Последнее редактирование: Август 26, 2017, 08:41 от Igors » Записан
ViTech
Гипер активный житель
*****
Offline Offline

Сообщений: 858



Просмотр профиля
« Ответ #24 : Август 26, 2017, 13:06 »

Если идти в русле геттеров/сеттеров и не разбираться, что из себя представляет кривая как объект какой-то предметной области, то можно попробовать делать так: ввести AbstractCurve и плодить наследников.

Код
C++ (Qt)
template <class Data, class Key>
struct Curve : AbstractCurve
{
   map<Key, Data> data;
}
 

В членах класса и контейнерах можно хранить AbstractCurve. Ну и в каждом методе, который будет получать такую курву в качестве параметра, будет лапша из dynamic_cast (или чуть облегчить её свитчем по dataType):
Код
C++ (Qt)
void doSomething( AbstractCurve * curve )
{
  if (dynamic_cast<Curve<double, DateTime>*>(curve))
  { ... }
  else if (dynamic_cast<Curve<Vector3D, DateTime>*>(curve))
  { ... }
  else if (dynamic_cast<Curve<Color, DateTime>*>(curve))
  { ... }
}
 

Можно ещё попробовать паттерн Visitor к AbstractCurve прикрутить. Возможно станет немного легче и такой лапши в каждом методе не будет (но она перенесётся в другое место Улыбающийся).

Фигня? Конечно фигня Улыбающийся. Но чтобы такой фигни не было, надо продумывать, что это за сущность такая эта кривая. Какие методы должны быть у AbstractCurve, общие для всех типов кривых, а не просто геттеры/сеттеры её данных.

Записан

Пока сам не сделаешь...
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #25 : Август 27, 2017, 10:05 »

Но чтобы такой фигни не было, надо продумывать, что это за сущность такая эта кривая. Какие методы должны быть у AbstractCurve, общие для всех типов кривых, а не просто геттеры/сеттеры её данных.
А что тут "продумывать"? Базовый ф-ционал очевиден
Код
C++ (Qt)
struct CurveDouble {
..
 void AddKey      ( double time, double value );  // добавить контрольную точку  
 void DelNthKey  ( int keyIndex );  // удалить контрольную точку  
 
 double GetNthKeyTime( int keyIndex ) const;   // получить время
 double SetNthKeyValue( int keyIndex, double value ) const;  // получить значение
 
 void SetNthKeyTime( int keyIndex, double time );   // установить время
 void SetNthKeyValue( int keyIndex, double value );  // установить значение
 
 double GetInterpolatedValue( double time ) const;   // получить интерполированное значение  
 
 const SplineOptions & GetNthSplineOptions( int keyIndex ) const;  // получить параметры сплайна
 void  SetNthSplineOptions( int keyIndex, const SplineOptions & opt ) const;  // установить параметры сплайна
...
};
 
Это просто вытекает из стартового поста, даже как-то неудобно это разжевывать. Ни о чем больше НЕ спрашивается, есть простой и ясный ф-ционал, давайте не будем менять задачу а подумаем как его обобщить для разных типов данных и сплайнов. Заметим что простейшее использование template, напр
Код
C++ (Qt)
Curve<double>
Curve<Vector>
Curve<Color>
 
Экономит реализацию, но "обобщением" не является, класс Curve должен быть ОДИН (а не куча всего)
Записан
ViTech
Гипер активный житель
*****
Offline Offline

Сообщений: 858



Просмотр профиля
« Ответ #26 : Август 27, 2017, 14:19 »

А что тут "продумывать"? Базовый ф-ционал очевиден
...
Все методы с именем xxxKey или параметром keyIndex - это функционал контейнера (map<Key, Data> data в моём примере выше). Если нужен полный доступ к этим данным, то дублировать функционал контейнера смысла мало. Тем более называть методы иначе, чем они есть в контейнере. Лучше соблюдать требования концептов SequenceContainer, AssociativeContainer и прочих, которые подходят к поставленной задаче. Это позволит применять к ним стандартные алгоритмы, "range-based for loop" и прочие плюшки современного С++.

В AbstractCurve должны быть методы, в которых кривая проявляет себя как объект (с точки зрения ООП, какой-то предметной области), а не как хранилище данных. Например: printTo(QTextStream &), drawOn(QImage &), applyTo(AbstractCurve &).

Ни о чем больше НЕ спрашивается, есть простой и ясный ф-ционал, давайте не будем менять задачу а подумаем как его обобщить для разных типов данных и сплайнов. Заметим что простейшее использование template, напр
Код
C++ (Qt)
Curve<double>
Curve<Vector>
Curve<Color>
 
Экономит реализацию, но "обобщением" не является, класс Curve должен быть ОДИН (а не куча всего)
С такой постановкой задачи путь решения ведёт к union, variant и тому подобному, от чего Вы хотите уйти. Тем более, что кривая с данными Vector3D и кривая с Color - два разных класса, хоть они внешне и очень похожи. Это как классы коровы и лошади, как QLineEdit и QTextEdit. Почему бы тогда всё многообразие виджетов Qt не засунуть в один класс QWidget?

Одна из причин, по которой не получится шаблонами всё запихнуть в один класс, это то, что виртуальный метод нельзя сделать шаблонным (надеюсь понятно почему). А с желанием хранить множество разных кривых в одном контейнере, вам всё равно придётся отделять коров от лошадей: будь то dynamic_cast, switch(dataType), variant.canConvert и т.п.
Записан

Пока сам не сделаешь...
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #27 : Август 28, 2017, 07:27 »

Почему так упорно выставляется требование "ОДИН класс"? Аргумент "поместить в контейнер" правда, но звучит не очень (ну подумаешь, контейнер). Вот гораздо более наглядный пример. Предположим QImage в Qt был бы реализован так
Код
C++ (Qt)
QImage<format_ARGB32>
QImage<format_GrayScale>
... // еще десяток таких
Какой кайф было бы с ним работать! Улыбающийся Однако почему-то то же самое предлагается для класса Curve...
Записан
deMax
Хакер
*****
Offline Offline

Сообщений: 600



Просмотр профиля
« Ответ #28 : Август 28, 2017, 07:55 »

- никакой проверки на этапе компиляции, напр если "вектору" попытались назначить "цвет" - придется ловить  это в runtime (понатыкать assert'ов)
Обращаясь к контейнеру разносолов на этапе компиляции ничего не проверить. А выдирая элемент он сам провериться.

Цитировать
- хранится "по максимуму", что вообще-то не есть хорошо
union, ничего лишнего не храниться. Но у меня ошибка, у вас наверное сплайн для каждой кривой одного типа, а я думал что для каждой точки свой тип сплайна.

Чем вам Curve как QList<QVariant> не нравиться? а типы как
Код:
template<typename T> Curve 
{ template <typename T>struct Value<T>{T data, in, out; float someOptions[];}
 QMap<double,Value>  }
Записан
deMax
Хакер
*****
Offline Offline

Сообщений: 600



Просмотр профиля
« Ответ #29 : Август 28, 2017, 08:03 »

Ну и работа с такими структурами мгновенно сваливается в бесконечное свитчевание по dataType. Масса кода дублируетcя т.к. его надо повторять для каждого контейнера x, y, z (a, r, g, b). Напр вместо одной вставки ключа - 3 или даже 4. Это уже просто неграмотно.
второй вариант: замените структуру классом и уберите внутрь логику, у вас уйдет дублирование кода.

p.s. вы бы показали пример кода который работает с данным классом, ну или гипотетический интерфейс (пусть его и сделать на плюсах нельзя, но мы постараемся натянуть на плюсы). А то не очень понятно зачем вам такой хитрый класс и как вы его используете.
Записан
Страниц: 1 [2] 3 4 5   Вверх
  Печать  
 
Перейти в:  


Страница сгенерирована за 0.284 секунд. Запросов: 23.