Russian Qt Forum

Программирование => С/C++ => Тема начата: Андрей от Март 24, 2012, 11:41



Название: Шаблоны.
Отправлено: Андрей от Март 24, 2012, 11:41
Могу ли я наследовать класс от шаблона следующим образом?
Код
C++ (Qt)
template <class Ti, class Tp>
class VVectorIndex: public QObject
{  
public:
   VVectorIndex(QObject* parent = 0);
 
   virtual ~VVectorIndex();
   Ti getIndex(Tp*);
   Tp* getPointer(Ti);
   Ti createIndex(Tp* word);
   bool eraseIndex(Ti);
   Ti count(){return Indexes.count();};
   virtual unsigned char getWordVersion(Ti)=0;
   virtual bool incrWordVersion(Ti)=0;
   virtual char getGroupVersion()=0;
   virtual bool incrGroupVersion()=0;
 protected:
   QVector <Tp*> Indexes;
 private:
   virtual bool setWordVersion(Tp*word, unsigned char vers){return false;};
 
   char groupVersion;
   Ti badIndexes;
};
 
class VWord;
 
class VWordIndex: public VVectorIndex<unsigned short, VWord>
{
public:
   VWordIndex(QObject* parent = 0):VVectorIndex<unsigned short, VWord>(parent){};
   ~VWordIndex();
   unsigned char getWordVersion(unsigned short);
   bool incrWordVersion(unsigned short);
   char getGroupVersion();
   bool incrGroupVersion();
 
 private:
   bool setWordVersion(VWord *word, unsigned char vers);
};
 


Название: Re: Шаблоны.
Отправлено: mutineer от Март 24, 2012, 12:11
Ну вроде все в порядке, можешь наследоваться. Только не уверен нормально ли будет форвард-декларэйшн VWord. А из-за чего вопрос? компил ругается?


Название: Re: Шаблоны.
Отправлено: Igors от Март 24, 2012, 12:27
"Пользуйтесь тегом для оформления кода" (а то читать неудобно)
Indexes = Indices (а лучше просто везде Index)

По существу: унаследоваться можете, и getWordVersion, incrWordVersion будут работать, но "задействовать их в виртуальных целях" не получится


Название: Re: Шаблоны.
Отправлено: Андрей от Март 24, 2012, 14:01
Ругается...

Решил шаблоны освоить. Написал шаблон. Шаблон типонезависимый и не знает полей классов Ti и Tp. Описал частично его методы, понял что кое-где гораздо удобней, когда класс знает типы классов Ti и Tp.
 
Решил что нужно от него наследовать класс и в классе переопределять некоторые методы в зависимости от типов.

Вынес шаблон в отдельные cpp/h. Планирую описать там два шаблона VVectorIndex и VListIndex.

При компиляции выдаёт
Код:
ошибка: undefined reference to `VVectorIndex<unsigned short, VWord>::createIndex(VWord*)'
И так на каждую задействованную в коде функцию-метод шаблона, в том числе на
Код:
VWordIndex(QObject* parent = 0):VVectorIndex<unsigned short, VWord>(parent){};
выдаёт
Код:
ошибка: undefined reference to `VVectorIndex<unsigned short, VWord>::VVectorIndex(QObject*)'


Название: Re: Шаблоны.
Отправлено: Андрей от Март 24, 2012, 14:08
Т.е. вообще нельзя "задействовать методы в виртуальных целях" наследуя от шаблона. А деструктор?


Название: Re: Шаблоны.
Отправлено: m_ax от Март 24, 2012, 14:15
Т.е. вообще нельзя "задействовать методы в виртуальных целях" наследуя от шаблона. А деструктор?

Да можно, нормально там всё) 
А ошибки у вас выскакивают из-за того, что объявление и реализация шаблонных классов/функций должны нах. в одной единице трансляции. Перенесите из cpp всё обратно в h и всё заработает)


Название: Re: Шаблоны.
Отправлено: Igors от Март 24, 2012, 14:21
Да можно, нормально там всё) 
Интересно как. Прошу показать как задействовать напр метод getWordVersion (принимающий template аргумент) в виртуальном механизме


Название: Re: Шаблоны.
Отправлено: m_ax от Март 24, 2012, 14:33
Да можно, нормально там всё) 
Интересно как. Прошу показать как задействовать напр метод getWordVersion (принимающий template аргумент) в виртуальном механизме

Да легко:
Код
C++ (Qt)
template <class T>
class base
{
public:
   base() {}
   virtual ~base() {}
 
   virtual void print(const T& x) const = 0;
 
};
 
class derived : public base<double>
{
public:
   virtual void print(const double &x) const {
       std::cout << "derived.print, x = " << x << std::endl;
   }
};
 
 
int main()
{
   base<double> *base = new derived;
 
   base->print(4.5);
 
   return 0;
}
 


Название: Re: Шаблоны.
Отправлено: Igors от Март 24, 2012, 14:45
Да легко:
Что легко-то? :) В Вашем примере virtual хоть есть хоть нету - все работает одинаково, виртуальный механизм никак не используется


Название: Re: Шаблоны.
Отправлено: m_ax от Март 24, 2012, 14:51
Да легко:
Что легко-то? :) В Вашем примере virtual хоть есть хоть нету - все работает одинаково, виртуальный механизм никак не используется

Как это не используется?
А как же вот эта строчка:
Код
C++ (Qt)
base<double> *base = new derived;
 
И далее:
Код
C++ (Qt)
base->print(4.5);
 

Полиморфизм в чистом виде)


Да, кстатии, архитектура libssc на этом построена)

ЗЫ. Обычно, то, что хочет реализовать автор ветки, делают немного по другому.. Возможно следует покурить в сторону type_traits (классов характеристик)


Название: Re: Шаблоны.
Отправлено: Igors от Март 24, 2012, 15:24
Как это не используется?
А как же вот эта строчка:
Код
C++ (Qt)
base<double> *base = new derived;
 
И далее:
Код
C++ (Qt)
base->print(4.5);
 

Полиморфизм в чистом виде)
Так он заканчивается на <double>, напр
Код
C++ (Qt)
base<double> *baseD = new derivedD;
base<float> *baseF = new derivedF;
 
Хотя оба могут делать print - это разные методы (полиморфные но никак не связанные между собой) и использовать их в общем виртуальном механизме не удается. Эффект virtual равен нулю  

ЗЫ. Обычно, то, что хочет реализовать автор ветки, делают немного по другому.. Возможно следует покурить в сторону type_traits (классов характеристик)
Расскажите, мне тоже интересно


Название: Re: Шаблоны.
Отправлено: m_ax от Март 24, 2012, 16:16
Цитировать
Так он заканчивается на <double>, напр
Код
C++ (Qt)
base<double> *baseD = new derivedD;
base<float> *baseF = new derivedF;
 

Хотя оба могут делать print - это разные методы (полиморфные но никак не связанные между собой) и использовать их в общем виртуальном механизме не удается. Эффект virtual равен нулю  

Так сами классы base<double> и base<float> - это два совершенно разных класса с точки зрения компилятора.

Код
C++ (Qt)
Расскажите, мне тоже интересно
 
Об этом уже много написано.. Типичный пример это std::numeric_limits<T>

Если в двух словах:
Код
C++ (Qt)
template <class T>
struct my_type_traits
{
typedef T type;
typedef T* pointer_type;
typedef const T& const_reference;
typedef T& reference;
 
// Может содержать свой методы, но обычно это статические функции..
 
static void some_method(const_reference x) { std::cout << "my_type_traits::some_method, x =" << x << std::endl;  }
//...
};
 
// Можно сделать специализациию на my_type_traits или написать свой
template<>
struct my_type_traits<double>
{
//...
static void some_method(const_reference x) { std::cout << " my_type_traits<double>, x = " << x << std::endl; }
};
 
template <class T, class Traits = my_traits<T> >
class my_class
{
public:
typedef typename Traits::type type;
typedef typename Traits::pointer_type pointer_type;
typedef typename Traits::const_reference const_reference;
typedef typename Traits::reference reference;
//...
type my_method(const_reference x) {
   Traits::some_method(x);
  //...  
   return x;
}
// Ну и т.д.
};
 
//Используется это так
 
my_class<int> mi;
mi.some_method(2);
 
my_class<double> md;
md.some_method(3.14);
 
 
Это делает код более гибким, поскольку можно отдельно (независимо) писать свои типы характеристик, определяя их индивидуальное поведение, причём интерфейс класса, который их использует остаётся неизменным.

В книжке "Философия С++" (2 часть) есть хороший пример, как это используется..


Название: Re: Шаблоны.
Отправлено: Андрей от Март 24, 2012, 18:38
Спасибо. Скомпилил. На удивление больше ошибок не было. Хотя правил код, который писал в последний раз неделю назад, правил особо не вдумываясь:-)))

Только немного неудобно, когда всё в одном h-нике. Хорошо, хоть VWordIndex описал в другом h-нике отдельно от его срр кода.

Созрею, почитаю - сделаю через traits.


Название: Re: Шаблоны.
Отправлено: m_ax от Март 24, 2012, 21:04
Спасибо. Скомпилил. На удивление больше ошибок не было. Хотя правил код, который писал в последний раз неделю назад, правил особо не вдумываясь:-)))

Только немного неудобно, когда всё в одном h-нике. Хорошо, хоть VWordIndex описал в другом h-нике отдельно от его срр кода.

Созрею, почитаю - сделаю через traits.

Да может в вашей задаче нужно делать не обязательно через traits.. Всё зависит от постановки конкретной задачи.
Но почитать об этом патерне всё равно стоит)) 


Название: Re: Шаблоны.
Отправлено: mutineer от Март 25, 2012, 13:07
Решил шаблоны освоить. Написал шаблон. Шаблон типонезависимый и не знает полей классов Ti и Tp. Описал частично его методы, понял что кое-где гораздо удобней, когда класс знает типы классов Ti и Tp.
 
Решил что нужно от него наследовать класс и в классе переопределять некоторые методы в зависимости от типов.

Мне кажется, или тут правда можно обойтись специализацией шаблона?


Название: Re: Шаблоны.
Отправлено: m_ax от Март 25, 2012, 20:15
Решил шаблоны освоить. Написал шаблон. Шаблон типонезависимый и не знает полей классов Ti и Tp. Описал частично его методы, понял что кое-где гораздо удобней, когда класс знает типы классов Ti и Tp.
 
Решил что нужно от него наследовать класс и в классе переопределять некоторые методы в зависимости от типов.

Мне кажется, или тут правда можно обойтись специализацией шаблона?
Можно, конечно сделать всё и через специализацию, но в таком случае ему придётся реализовывать весь функционал заново..
С другой стороны можно написать базовый класс, функционал которого не зависит от типа шаблона и наследоваться от него с последующей реализацией специализации..
Но опять-таки здесь всё зависит от конкретной задачи..


Название: Re: Шаблоны.
Отправлено: Igors от Март 25, 2012, 20:35
Можно, конечно сделать всё и через специализацию, но в таком случае ему придётся реализовывать весь функционал заново..
С другой стороны можно написать базовый класс, функционал которого не зависит от типа шаблона и наследоваться от него с последующей реализацией специализации..
Но опять-таки здесь всё зависит от конкретной задачи..
Давайте попробуем конкретизировать. Есть такие простые структурки которые хотим обощить
Код
C++ (Qt)
struct Point3D  { double x, y, z; };
struct Point3F  { float x, y, z; };
struct Point3W { float x, y, z, w; };
 
Конечно мы можем писать напр
Код
C++ (Qt)
std::vector <Point3D> vec1;
std::vector <Point3F> vec2;
и.т.д.
 
Но так мы очень быстро окажемся заваленными ф-циями/методами которые отличаются только типом вектора. И тогда придется делать еще template. Вместо этого мы хотим сделать "нормальный" базовый класс, его потомки уже будут template, а мы будем работать с базовым.

Как это сделать?


Название: Re: Шаблоны.
Отправлено: m_ax от Март 25, 2012, 20:57
Можно, конечно сделать всё и через специализацию, но в таком случае ему придётся реализовывать весь функционал заново..
С другой стороны можно написать базовый класс, функционал которого не зависит от типа шаблона и наследоваться от него с последующей реализацией специализации..
Но опять-таки здесь всё зависит от конкретной задачи..
Давайте попробуем конкретизировать. Есть такие простые структурки которые хотим обощить
Код
C++ (Qt)
struct Point3D  { double x, y, z; };
struct Point3F  { float x, y, z; };
struct Point3W { float x, y, z, w; };
 
Конечно мы можем писать напр
Код
C++ (Qt)
std::vector <Point3D> vec1;
std::vector <Point3F> vec2;
и.т.д.
 
Но так мы очень быстро окажемся заваленными ф-циями/методами которые отличаются только типом вектора. И тогда придется делать еще template. Вместо этого мы хотим сделать "нормальный" базовый класс, его потомки уже будут template, а мы будем работать с базовым.

Как это сделать?

Не совсем понял, что мешает сделать класс Point шаблонным и все функции, связанные с математикой над этим классом так же шаблонными?
В данном случае нормальный базовый клас если и можно будет сделать, то только пустым..
Код
C++ (Qt)
struct base_point {
virtual ~base_point() {}
};
 
template <class T>
struct point : public base_point
{
   T x, y, z;
};
 
list<base_point*> list_of_points;
 
   
Но смысл в этом?


Название: Re: Шаблоны.
Отправлено: Igors от Март 25, 2012, 21:16
Я говорю о базовом классе контейнере, разные наследники которого будут хранить разные типы элементов.

Обобщать сами структуры - для простоты положим что это не нужно, в конце-концов результатом обобщения будут разные классы, и задача с контейнером никак не упрощается

Edit: m_ax, просьба: цитированием не злоупотребляйте, а то длинная портянка получается  :)


Название: Re: Шаблоны.
Отправлено: m_ax от Март 25, 2012, 21:22
Я говорю о базовом классе контейнере, разные наследники которого будут хранить разные типы элементов.

Обобщать сами структуры - для простоты положим что это не нужно, в конце-концов результатом обобщения будут разные классы, и задача с контейнером никак не упрощается

Edit: m_ax, просьба: цитированием не злоупотребляйте, а то длинная портянка получается  :)
Всё равно не понял, чем мотивировано такое поведение? Чем это отличается, скажем от простого typedef?
Код
C++ (Qt)
typedef vector<PointF> vector_of_pointsf;
typedef vector<PointD> vector_of_pointsd;
...
 
В чём Вы видите выйгрыш от вашей задумки (я не совсем понимаю какого поведения Вы хотите добиться)?


Название: Re: Шаблоны.
Отправлено: Igors от Март 25, 2012, 21:34
В чём Вы видите выйгрыш от вашей задумки (я не совсем понимаю какого поведения Вы хотите добиться)?
С точки зрения использования
Код
C++ (Qt)
class CModel {
...
CoordVec * mCoord; // или CoordVec & mCoord;
};
 
CoordVec - базовый класс (контейнер), возможно абстрактный. Конкретный потомок может хранить Point в разных форматах, но делать template для CModel мне утомительно и нежелательно


Название: Re: Шаблоны.
Отправлено: m_ax от Март 25, 2012, 21:53
Ну если CoordVec может быть абстрактным, то можно сделать так (хотя это сомнительное решение, имхо)
Код
C++ (Qt)
enum typeOfCoordVector { VectorPointF, VectorPointD};
 
struct base_coord_vec
{
   virtual ~base_coord_vec() {}
   virtual type_coord_vec() const = 0;
};
 
struct coord_vec_d : public base_coord_vec
{
   typedef std::vector<PointD> vector_type;
   vector_type vector;
   virtual type_coord_vec() const { return VectorPointD; }
};
 
struct coord_vec_f : public base_coord_vec
{
   typedef std::vector<PointF> vector_type;
   vector_type vector;
   virtual type_coord_vec() const { return VectorPointF; }
};
 
class CModel {
   base_coord_vec *mCoord;
};
 

Если я правильно уловил суть. Ещё можно написать свой класс variant и создавать вектор
Код
C++ (Qt)
vector<variant>
 
А variant может быть чем угодно.. Ну типа аналог QVariant..
 ???


Название: Re: Шаблоны.
Отправлено: Igors от Март 25, 2012, 22:12
Да, template вещь полезная, но "выносит моск" капитально  :) Давайте я еще раз попробую объяснить откуда "берется необходимость". Простейшее решение
Код
C++ (Qt)
template <class T>
class CModel {
...
vector <T> mCoord;
};
 
Да, так все будет работать, НО template "проникает наверх". То есть везде где я использую CModel (а таких мест масса) я обязан буду "специфицироваться", объявить template во всех ф-циях и методах которые принимают/возвращают CModel. Это очень неудобно, т.к. у CModel есть множество др дел кроме mCoord, Если же я хочу сделать CModel членом др класса - еще хуже.

Поэтому естественно желание "замкнуть" template в классе контейнера чтобы оно не лезло во все щели  
 


Название: Re: Шаблоны.
Отправлено: m_ax от Март 25, 2012, 22:26
Тогда логично написать свой класс variant (есть уже готовые реализации), объявит
Код
C++ (Qt)
typedef std::vector<variant> super_vector;
 

А в variant  уже записывать хоть PointF, хоть PointD, да и всё что душа пожелает)


Название: Re: Шаблоны.
Отправлено: Igors от Март 25, 2012, 22:48
А в variant  уже записывать хоть PointF, хоть PointD, да и всё что душа пожелает)
Это в данном случае не годится: Point xx - низкоуровневые POD структуры, никакие "обертки" недопустимы с точки зрения доп расходов по памяти и конвертации

А чем Вам не нравится template класс с non-template базовым ?


Название: Re: Шаблоны.
Отправлено: m_ax от Март 25, 2012, 23:02
Цитировать
А чем Вам не нравится template класс с non-template базовым ?
А как вы себе его представляете?


Название: Re: Шаблоны.
Отправлено: Igors от Март 25, 2012, 23:15
А как вы себе его представляете?
Код
C++ (Qt)
struct CoordVec {
virtual size_t size( void ) const = 0;
virtual Point3D & Get3D( size_t index ) = 0;
virtual Point3F & Get3F( size_t index ) = 0;
virtual Point3W & Get3W( size_t index ) = 0;
 // то же для Set и.т.д
};
 
template <class T>
struct CoordVecT : public CoordVec {
virtual size_t size( void ) const { return mData.size(); }
..
vector <T> mData;
};
 


Название: Re: Шаблоны.
Отправлено: m_ax от Март 25, 2012, 23:25
Ну можно и так. Я бы ещё в базовый класс добавил виртуальную функцию
Код
C++ (Qt)
virtual typeOfVector type() const = 0;
 
Чтоб можно было сразу определить с чем имеем дело.


Название: Re: Шаблоны.
Отправлено: Igors от Март 25, 2012, 23:31
А как теперь реализовать такую ф-цию (ну или метод) не городя switch(ей)
Код
C++ (Qt)
void Append( CoordVec & dst, const CoordVec & src );
 
?


Название: Re: Шаблоны.
Отправлено: m_ax от Март 25, 2012, 23:47
Поскольку в базовом классе CoordVec нет никакого вектора, то во-первых придётся использовать либо dynamic_cast, либо static_cast если известно к чему приводить..
И так вы не избавитесь от switch или ему подобных проверок и т.д. В этом плане этот вариант, конечно проигрывает..
Да и вызов виртуальных функций - тож дороговато( (а если учесть, что это чтение/запись)
   


Название: Re: Шаблоны.
Отправлено: Igors от Март 25, 2012, 23:54
Поскольку в базовом классе CoordVec нет никакого вектора, то во-первых придётся использовать либо dynamic_cast, либо static_cast если известно к чему приводить..
И так вы не избавитесь от switch или ему подобных проверок и т.д. В этом плане этот вариант, конечно проигрывает..
Да и вызов виртуальных функций - тож дороговато( (а если учесть, что это чтение/запись)
Ну эти соображения очевидны. А подумать, толкнуть идейку? Или умеем только утешать PinkPink ?  :)


Название: Re: Шаблоны.
Отправлено: m_ax от Март 26, 2012, 09:15
Цитировать
Ну эти соображения очевидны. А подумать, толкнуть идейку? Или умеем только утешать PinkPink ? 
Ну на мой взгляд, самое правильное решение это сделать класс CModel шаблонным.
Код
C++ (Qt)
template <class T>
class CModel {
   typedef T pointType;
   typedef std::vector<T> vectorType;
 
   vectorType *mCoord;
};
 
Это лучше и быстрее будет работать, нежели наследование, виртуальные функции и т.д.


Название: Re: Шаблоны.
Отправлено: m_ax от Март 26, 2012, 11:46
Да, есть ещё такая фишка, как определить принадлежность какого-либо объекта к конкретному классу/типу. Может будет где полезно:
Код
C++ (Qt)
template<class T>
struct tester
{
   static bool is_same(const T&) { return true; }
   template <class U>
   static bool is_same(const U&) { return false; }
};
 
Используется это так:
Код
C++ (Qt)
int i;
std::cout << tester<int>::is_same(i) << std::endl;
std::cout << tester<long>::is_same(i) << std::endl;
 
A *a = new A;
std::cout << tester<A>::is_same(*a) << std::endl;
std::cout << tester<B>::is_same(*a) << std::endl;
 
 
Вывод будет таким:
Код
Bash
true
false
 
true
false
 


Название: Re: Шаблоны.
Отправлено: Igors от Март 26, 2012, 12:14
Ну на мой взгляд, самое правильное решение это сделать класс CModel шаблонным.
Минусы такого решения подробно описаны в посте #22
http://www.prog.org.ru/index.php?topic=21385.msg148020#msg148020 (http://www.prog.org.ru/index.php?topic=21385.msg148020#msg148020)

Да, есть ещё такая фишка, как определить принадлежность какого-либо объекта к конкретному классу/типу. Может будет где полезно:
С 3-мя базовыми классами уже 9 вариантов, с большим...

Рассмотрим такое
Код
C++ (Qt)
template <class T1, class T2>
void Append( T1 & dst. const T2 & src );
 
Здесь очевидно никаких проблем нет. Можем ли мы как-то выдернуть (как бы "привести") базовый класс к template когда нам нужно? Это звучит не очень грамотно, но Вы поняли идею.

Ну или хотя бы число приведений было = числу базовых классов. Потому что таких ф-ций как Append достаточно и "обильно свитчеваться" не хочется


Название: Re: Шаблоны.
Отправлено: m_ax от Март 26, 2012, 12:30
Цитировать
Минусы такого решения подробно описаны в посте #22
А создавать базовые классы и определять туеву хучу виртуальных методов, это, конечно, достойная альтернатива))

Цитировать
Рассмотрим такое
Код
C++ (Qt)
template <class T1, class T2>
void Append( T1 & dst. const T2 & src );
 

Здесь очевидно никаких проблем нет. Можем ли мы как-то выдернуть (как бы "привести") базовый класс к template когда нам нужно? Это звучит не очень грамотно, но Вы поняли идею.
 
Не совсем понял..
Я бы написал специализацию этой функции..
Код
C++ (Qt)
template<>
void Append<vestor<PointD>, PointD>(vestor<PointD>& dst, const PointD &src);
 

Ну или что там под T1, T2 подразумевается..


Название: Re: Шаблоны.
Отправлено: m_ax от Март 26, 2012, 12:42
Идея написать свой базовый контейнер, имхо, пройгрышна.
Во-первых, по производительности.
Во-вторых, добавляется ещё много всякого кода (базовый класс, наследники, виртуальные функции)
В-третьих, страдает расширяемость и гибкость. Что если в будущем понадобится добавить ещё какой нить класс Точки? Всюду придётся переписывать всё.. Не гут(


 


Название: Re: Шаблоны.
Отправлено: Igors от Март 26, 2012, 13:05
А создавать базовые классы и определять туеву хучу виртуальных методов, это, конечно, достойная альтернатива))
Более чем. Даже если не существует хорошего решения для Append - я имею головную боль всего с одним классом, а в Вашем варианте - со всем приложением.

Не совсем понял..
Я бы написал специализацию этой функции..
Код
C++ (Qt)
template<>
void Append<vestor<PointD>, PointD>(vestor<PointD>& dst, const PointD &src);
 

А откуда я возьму Point3D (или др) если известен только базовый класс (который не template)?

Идея написать свой базовый контейнер, имхо, пройгрышна.
...
В-третьих, страдает расширяемость и гибкость. Что если в будущем понадобится добавить ещё какой нить класс Точки? Всюду придётся переписывать всё.. Не гут(
Зачем же мне всюду переписывать если классы верхнего уровня даже не знают о существовании разных форматов точек?  :)
Код
C++ (Qt)
class CModel {
...
CoordVec * mCoord;
};
 

Во-первых, по производительности.
Во-вторых, добавляется ещё много всякого кода (базовый класс, наследники, виртуальные функции)
Ага, а вот и результат общения с PinkPink  :)

[offtop]Конечно чтение книг развивает и.т.п. Но в то же время читающий "оказывается под влиянием". Ход мыслей сводится к подстановке/перебору стандартных решений (часто пошленьких), активность/инициатива подавляется. Если в книжке такого не находится, то это немедленно объявляется (позорным) велосипедом. Увы, это работает даже для тех людей у которых с креативом все нормально  :)


Название: Re: Шаблоны.
Отправлено: m_ax от Март 26, 2012, 13:13
По поводу функции Append:
Её можно сделать так:
Код
C++ (Qt)
template <class Point>
void Append( vector<Point> & dst, const vector<Point> & src ) {...}
 
Если для конкретного типа Point (пусть для определённости PointF) реализация будет отлична от общей, то логично просто написать специализацию:
Код
C++ (Qt)
template <>
void Append<PointF>( vector<PointF> & dst, const vector<PointF> & src ) {...}
 


Название: Re: Шаблоны.
Отправлено: m_ax от Март 26, 2012, 13:29
У меня как-то была похожая задачка. Нужно было реализовать некий класс, который использовал контейнер, но важно было дать возможность пользователю самому выбирать тип контейнера (вектора). Это, например мог быть и std::vector, и valarray и std::array, и даже std::complex (как двумерный вектор). Через шаблоны получилось очень прикольно)

Могу привести код) 


Название: Re: Шаблоны.
Отправлено: Igors от Март 26, 2012, 13:38
Если для конкретного типа Point (пусть для определённости PointF) реализация будет отлична от общей, то логично просто написать специализацию:
С этим ясно, также для простоты предположим все базовые структуры могут присваиваться друг другу, это легко обеспечить. Но где мне взять тип? Давайте набросаем первый (пусть уродливый) вариант

Код
C++ (Qt)
void Append( CoordVec & dst, const CoordVec & src )
{
switch (dst.Format()) {
 case format_3doub:
   switch (src.Format()) {
    case format_3doub:
    {
      CoordVecT <Point3D> & srcT = (CoordVecT <Point3D> &) src;
      CoordVecT <Point3D> & dstT = (CoordVecT <Point3D> &) dst;
      dst.mData.insert(...);
      break;
    }
    ...
 .... // и.т.д
   }
 .... // и.т.д
}
 
}
 
Конечно это ужасно :) Но при полном отсутствии лучшего на это можно пойти. Хорошо, как это можно сделать лучше - или хотя бы менее болезненно?

У меня как-то была похожая задачка. Нужно было реализовать некий класс, который использовал контейнер, но важно было дать возможность пользователю самому выбирать тип контейнера (вектора). Это, например мог быть и std::vector, и valarray и std::array, и даже std::complex (как двумерный вектор). Через шаблоны получилось очень прикольно)

Могу привести код) 
Идею поясните, а с кодом я разберусь


Название: Re: Шаблоны.
Отправлено: m_ax от Март 26, 2012, 14:01
Цитировать
С этим ясно, также для простоты предположим все базовые структуры могут присваиваться друг другу, это легко обеспечить. Но где мне взять тип? Давайте набросаем первый (пусть уродливый) вариант
Тип шаблона легко определить. Например так:
Код
C++ (Qt)
enum PointType {TypePointD, TypePointF, TypePointX, TypeUnknown};
 
template <class T>
struct traits {
static PointType type() { return TypeUnknown; }
};
 
template<>
struct traits<PointF>
{
static PointType type() { return TypePointF; }
};
 
template<>
struct traits<PointD>
{
static PointType type() { return TypePointD; }
};
 
template<>
struct traits<PointX>
{
static PointType type() { return TypePointX; }
};
 

Теперь можно Ваш авариант функции Append написать через шаблоны:
Код
C++ (Qt)
template <class Point>
void Append(vector<Point>& dst, const vector<Point> & src)
{
   PointType pt = traits<Point>::type();
   switch (pt) {
   case TypePointF:  {...} break;
   case TypePointD:  {...} break;
   case TypePointX:  {...} break;
   default: {...}  
   }
}
 

Ну как то так это видится..


Название: Re: Шаблоны.
Отправлено: Igors от Март 26, 2012, 14:14
Теперь можно Ваш авариант функции Append написать через шаблоны:
Код
C++ (Qt)
template <class Point>
void Append(vector<Point>& dst, const vector<Point> & src)
{
   PointType pt = traits<Point>::type();
   switch (pt) {
   case PointF:  {...} break;
   case PointD:  {...} break;
   case PointX:  {...} break;
   default: {...}  
   }
}
 

Ну как то так это видится..
Если бы я мог записать напр vector<Point>& dst - то тело ф-ции выписывается прекрасно без всяких switch. Но беда в том что аргументом(и) должен быть базовый класс который не template


Название: Re: Шаблоны.
Отправлено: m_ax от Март 26, 2012, 14:17
Цитировать
Если бы я мог записать напр vector<Point>& dst - то тело ф-ции выписывается прекрасно без всяких switch. Но беда в том что аргументом(и) должен быть базовый класс который не template
Ну так может и не стоит порождать базовый класс и его наследников, а сразу сделать через шаблоны?
Я просто не могу понять (несколько раз читал пост 22) чем же всё-таки вариант с шаблоном не устраивает? 
Я пока не вижу проблемы..


Название: Re: Шаблоны.
Отправлено: Igors от Март 26, 2012, 14:40
Ну так может и не стоит порождать базовый класс и его наследников, а сразу сделать через шаблоны?
Я просто не могу понять (несколько раз читал пост 22) чем же всё-таки вариант с шаблоном не устраивает? 
Я пока не вижу проблемы..
Меня совершенно не устраивает что все (абсолютно все) что хоть как-то связано с таким контейнером сразу же само становится template и требует спецификации. Добавили такой контейнер к чему-то (напр как член класса) - извольте и тот класс сделать template - и это не имеет конца. Пережить несколько неприятных switch несравненно проще

Да хоть бы и чисто теоретически
Код
C++ (Qt)
template <class T>
class CModel {
..
vector <T> mCoord;
};
 
Почему каждый (даже тот кто mCoord никак не использует) должен заниматься спецификацией CModel и копаться в подробностях формата точек? Где же инкапсуляция?


Название: Re: Шаблоны.
Отправлено: m_ax от Март 26, 2012, 14:47
Цитировать
Почему каждый (даже тот кто mCoord никак не использует) должен заниматься спецификацией CModel и копаться в подробностях формата точек? Где же инкапсуляция?
Ну если не использует, то и копаться не придётся..
Код
C++ (Qt)
template <class T>
void someMethod(CModel<T> &x) {
  x.someNonTemplateParametr = value; // ну и т.д.
}
 
Неужели так уж неприемлемо над каждой функцией template<class T> написать?


Название: Re: Шаблоны.
Отправлено: Igors от Март 26, 2012, 14:56
Неужели так уж неприемлемо над каждой функцией template<class T> написать?
Даже в среднем проекте - совершенно неприемлемо, ведь это коснется очень многих ф-ций, методов и классов

m_ax, думать будем или так, жопка к жопке из книжки тулить? :)  У меня дела, появлюсь завтра, надеюсь увидеть что-то содержательное, пусть спорное, обсудим


Название: Re: Шаблоны.
Отправлено: whitecemetery от Март 26, 2012, 16:24
А я вот не понял следующее:
- кто, на каком этапе должен определять, какой тип точки используется внутри CModel?
- разные объекты CModel могут использовать разные типы точек или нет?
А то получается парадокс - я использую объект CModel, но ни я при его создании, ни кто-либо еще в другом месте не сказал о том, какой тип точки лежит внутри него.



Название: Re: Шаблоны.
Отправлено: m_ax от Март 26, 2012, 16:34
А я вот не понял следующее:
- кто, на каком этапе должен определять, какой тип точки используется внутри CModel?
- разные объекты CModel могут использовать разные типы точек или нет?
А то получается парадокс - я использую объект CModel, но ни я при его создании, ни кто-либо еще в другом месте не сказал о том, какой тип точки лежит внутри него.

Я подозреваю, что CModel'и вообще фиолетово какой она контейнер содержит, не то что сами типы точек))
Контейнер то предполагается абстрактный.. Согласитесь, бредовая идея)


Название: Re: Шаблоны.
Отправлено: m_ax от Март 26, 2012, 17:10
Короче вот вам окончательный костыль.
Пусть есть базовый класс base и от него нужно породить такие классы, как derived<PointF>, derived<PointX>..
Тогда делаем так:
Код
C++ (Qt)
enum PointType {TPointF, TPointX, TUnknown};
 
// Предварительное объявление
template <class T>
struct derived;
 
// класс base
struct base
{
   virtual ~base() {}
   virtual PointType type() const = 0;
 
   template <class T>
   T get(std::size_t index) const {
          switch(type()) {
          case TPointF: {
                derived<PointF> *d = static_cast<derived<PointF>*>(this);
                return d->vector[index];
          } break;
          case TPointX: {
                derived<PointX> *d = static_cast<derived<PointX>*>(this);
                return d->vector[index];
          } break;
       }
       return T();
   }
 
   template <class T>
   void set(std::size_t index, const T& val) {
       switch(type()) {
          case TPointF: {
                derived<PointF> *d = static_cast<derived<PointF>*>(this);
                d->vector[index] = val;
          } break;
          case TPointX: {
                derived<PointX> *d = static_cast<derived<PointX>*>(this);
                d->vector[index] = val;
          } break;
       }
   }
};
 
// Реализация derived
template <class T>
struct derived : public base
{
   virtual PointType type() const { return traits<T>::type(); }
   std::vector<T> vector;
};
 
// объявляем некую функцию, которая принимает базовый класс base
void func(base *x) {
  x->set(0, PointF(1,2,3))
  std::cout << x->get(0) << std::endl;
}
 
Примерно так. Как реализовать traits я уже писал.

Но это плохое решение.



Название: Re: Шаблоны.
Отправлено: BRE от Март 26, 2012, 17:39
Это не первая попытка Igors сделать такой "чудо-контейнер": http://www.prog.org.ru/topic_19547_0.html :)

Но это плохое решение.
Точно. :)
А хорошее решение, скорее всего, потребует большого рефакторинга многих классов, их связей и, мне кажется, никому не нужна (судя по вышеуказанной теме). Сойдет и так. :)



Название: Re: Шаблоны.
Отправлено: Igors от Март 26, 2012, 20:19
Приехал раньше от заказчика, подключаюсь.

Вопросы whitecemetery приятно порадовали
А я вот не понял следующее:
- кто, на каком этапе должен определять, какой тип точки используется внутри CModel?
Исключительно "создатель модели" который может быть всяким-разным (читатель файла, модельный плагин и.т.п)

- разные объекты CModel могут использовать разные типы точек или нет?
Да. Более того, существует много данных "точки" и они в разном формате для разных экземпляров CModel
Код
C++ (Qt)
class CModel {
 ...
 CoordVec * mPosition;   // собственно координаты точек
 CoordVec * mNormal;    // нормали (перпендикуляры) к точкам
 CoordVec * mSрaceUV;  // позиции точек в UV(W) пространстве
 CoordVec * mBlur;        //  позиции motion blur
 CoordVec * mNURBS;
 
 // и.т.д  
};
 
Каждый генератор модели имеет право создавать перечисленные данные (все или какие считает нужным) и выбрать нужный формат. Причесать все "под одну гребенку" никак не хорошо. Напр частенько mSpaceUV - всего 2 float, но может и 3, mNURBS - в основном 4, но бывает и 3, сами mPosition - как правило 3 double но могут быть и 3 float. короче - число вариантов быстро растет.

Банкротство "простейшего template" здесь очевидно, напр

Код
C++ (Qt)
template <class T1. class T2. class T3... >
class CModel {
// вставить эту лабуду в проекте везде-везде
// и если у CModel появился новый член..
 
Бред собачий

А то получается парадокс - я использую объект CModel, но ни я при его создании, ни кто-либо еще в другом месте не сказал о том, какой тип точки лежит внутри него.
При создании - создатель отвечает за все. А при использовании - и не нужно (неправильно) знать. Для пользователя модели это выглядит так
Код
C++ (Qt)
// CModelUser
Point3F pt = model.mCoordinate->GetPoin3F(index);
if (model.mSpaceUV) {   // UV имеются?
// крутим UV
}
 
Типа "дай мне N-ю точку в формате что мне надо"


Название: Re: Шаблоны.
Отправлено: Igors от Март 26, 2012, 22:15
Это не первая попытка Igors сделать такой "чудо-контейнер": http://www.prog.org.ru/topic_19547_0.html :)
Верно что не первый раз я пытаюсь это здесь обсуждать, но вот тема что Вы упомянули не имеет к этому никакого отношения  :)

Но это плохое решение.
Да это вообще хз что.

Во-первых, чего Вы начинаете с того что это "костыль"? Тогда где же нормальное, правильное решение? Ах, Скуперфильд не пишет о нем в своей "философии"! Ну что ж поделать, друг мой, иногда приходится искать решения самому (а не только импозантно выглядеть перед глупенькими девушками).

Во-вторых, откуда взялись паника и пораженческие настроения? Ну да, Append конечно совсем плох/коряв, но все же работает, и таких ф-ций (взаимодействие контейнер-контейнер) относительно немного - все остальное прекрасно ложится в виртуальный механизм. А про "замедление на виртуалах" лучше не заикайтесь.

Так что, пацаны, слабО кинуть пару-тройку идеек как улучшить тот Append? Вся силенка ушла на нотации для нубов? Или как?   :)


Название: Re: Шаблоны.
Отправлено: m_ax от Март 26, 2012, 22:28
Ну как же Вы не понимаете, что в Ваших "реальных" задачах, вместо правильных решений приходится придумывать такие ходули, из-за того, что изначальна архитектура проекта была кривой и не продуманной.
Почему Вы такой вариант не принимаете?

 


Название: Re: Шаблоны.
Отправлено: BRE от Март 26, 2012, 22:39
Igors, скажу тебе по секрету, что язык программирования C++ является языком с сильной типизацией, поэтому такой подход (который ты пытаешься в очередной раз "решить") противоречит его главным идиомам. Соответственно никаких конструкций языка для обеспечения требуемого тебе поведения не может быть в принципе.
Ты, как разработчик на этом языке, должен всеми силами уходить от таких неопределенностей. Ты не должен хранить все числа в double, только потому что туда они помещаются (и char, и int, и ...), для проверки/приведения типов напридумывали всякие xxx_cast<>, для этого же придумали и шаблоны.

Вся силенка ушла на нотации для нубов?
А что делать, если у тебя столько вопросов.


Название: Re: Шаблоны.
Отправлено: Igors от Март 26, 2012, 22:41
Ну как же Вы не понимаете, что в Ваших "реальных" задачах, вместо правильных решений приходится придумывать такие ходули, из-за того, что изначальна архитектура проекта была кривой и не продуманной.
Почему Вы такой вариант не принимаете?
Да очень даже принимаю. Я накосячил - я и отвечу. Только вот в упор не вижу где же "правильно" (пусть субъективно/спорно). Влепить template на всех - ну Вы же видите что никак. Задать все "по максимуму" - решение начинающего. Что нереального/выдуманного (или надуманного) в моей задаче? Погуглите 5 мин - и Вы увидите что любая 3D модель имеет данные что я привел - я их не брал с потолка. Моя изначальная архитектура кривая - хорошо, а как правильно?  :)


Название: Re: Шаблоны.
Отправлено: Igors от Март 26, 2012, 22:53
Igors, скажу тебе по секрету, что язык программирования C++ является языком с сильной типизацией, поэтому такой подход (который ты пытаешься в очередной раз "решить") противоречит его главным идиомам. Соответственно никаких конструкций языка для обеспечения требуемого тебе поведения не может быть в принципе.
А что такого криминального в моем подходе? Чем он противоречит базовым принципам? Хочу сделать (один) класс который умеет хранить данные в разных форматах? Это что, "попытка интерпретирования в стиле Lisp"?  :)

(который ты пытаешься в очередной раз "решить")
Почему жизнь ничему не учит? Вы же прекрасно понимаете: все что мне нужно я уже давно решил - просто хочу обсудить с умными людьми  :)


Название: Re: Шаблоны.
Отправлено: BRE от Март 26, 2012, 22:58
А что такого криминального в моем подходе? Чем он противоречит базовым принципам? Хочу сделать (один) класс который умеет хранить данные в разных форматах?
Компилятор не может проверить типы (реальные типы). А он это очень не любит.

я уже давно решил
Могу себе представить.  ::)


Название: Re: Шаблоны.
Отправлено: m_ax от Март 26, 2012, 23:00
Ну если вы не знаете, я то от куда знаю? Я даже представления не имею что это за проект и как там всё устроено)
Есть догадки)

Но мне лично, идея наследоваться от какого то базового класса, переопределять вирт. функции для каждого типа точек и т.д. правильным путём не кажется(

Если с шаблонами совсем никак, то я бы сделал, как уже говорил, через патерн variant:
Код
C++ (Qt)
struct super_vector
{
   TypeOfPoint type;
   std::vector<variant> vector;
};
 
И никакого наследования..
А во всех функциях, я бы имел:
Код
C++ (Qt)
void Append(super_vector& dst, const super_vector &src)
{
   if (dst.type == TPointF) {
       PointF p1 = dst.vector[index].get<PointF>();
       ...
       dst.vector[index] = PointF(1,2,3);
   }
 
}
 
 
А сам класс variant занимает не намного больше места, чем то, что он в себя включает.

Возможный пример его реализации:
Код
C++ (Qt)
struct base_data
{
   virtual ~base_data() {}
};
 
template <class T>
struct data : public base_data
{
   data() {}
   data(const T& val) : value(val) {}
   T value;
};
 
class variant
{
public:
   variant() {}
 
   template <class T>
   variant(const T& val) {
       _data = ssc::counted_ptr<base_data>(new data<T>(val));
   }
 
   template <class T>
   void set(const T& val) {
       _data = ssc::counted_ptr<base_data>(new data<T>(val));
   }
   template <class T>
   T get(bool *is_valid = 0) const {
       data<T> *d = dynamic_cast<data<T>*>(_data.get());
       if (d) {
           if (is_valid)
               *is_valid = true;
           return d->value;
       }
       if (is_valid)
           *is_valid = false;
       return T();
   }
 
   bool is_empty() const { return _data.is_null(); }
 
private:
   ssc::counted_ptr<base_data> _data;
};
 
 
std::vector<variant> v;
   v.push_back(2.3);
   v.push_back(std::string("hello!"));
   v.push_back(1);
 
 
   std::cout << v[0].get<double>() << std::endl;
   std::cout << v[1].get<std::string>() << std::endl;
   std::cout << v[2].get<int>() << std::endl;
 


Да, counted_ptr можно заменить на std::shared_ptr


Название: Re: Шаблоны.
Отправлено: BRE от Март 26, 2012, 23:18
Если с шаблонами совсем никак, то я бы сделал, как уже говорил, через патерн variant:
variant не очень хорош как раз с точки зрения контроля типов во время компиляции. Мое мнение, чем реже ты скатываешься к варианту, тем лучше. ;)

Мы напрасно ушли от обсуждения задачи к реализации чуто-контейнера, а если вернутся, то можно предложить такой вариант.
Вот код Igors:
Код:
if (model.mSpaceUV) {   // UV имеются?
 // крутим UV
}

А что значит крутим? Давайте тогда сделаем метод spin, который будет этим заниматься и возвращать результат. Тогда для кручения, клиентам этого объекта, будет не нужно знать как, кто, в чем храниться.


Название: Re: Шаблоны.
Отправлено: Igors от Март 27, 2012, 00:55
variant не очень хорош как раз с точки зрения контроля типов во время компиляции. Мое мнение, чем реже ты скатываешься к варианту, тем лучше. ;)
Тот редкий случай когда Вы и я согласны :) Да, "вариант" - это уже на самый пожарный случай.

А что значит крутим? Давайте тогда сделаем метод spin, который будет этим заниматься и возвращать результат. Тогда для кручения, клиентам этого объекта, будет не нужно знать как, кто, в чем храниться.
Клиент может прекрасно жить не зная ненужных (ему) подробностей, напр (упрощенно)
Код
C++ (Qt)
if (model.mSpaceUV) {
mSpaceUV->SetPoinr3F(0, (mSpaceUV->GetPoint3F(0) + mSpaceUV->GetPoint3F(1)) * 0.5);
}
 
Клиенту в принципе-то нужно немного: Get/Set, и др стандартные операции с контейнером. Да, будут какие-то расходы на возврат по значению, на возможный перевод float <-> double, но они вполне приемлемы

Ну если вы не знаете, я то от куда знаю?
Виноват, немного подгрузил спецификой. Но в конце-концов (с точки зрения чисто программирования) почему мы должны рассматривать/планировать класс только с одним "таким членом"? А если "таких" будет 2 и больше? (как оно и есть)

Но мне лично, идея наследоваться от какого то базового класса, переопределять вирт. функции для каждого типа точек и т.д. правильным путём не кажется(
Всем нам что-то нравится или нет. Но как лучше? Вариант с "вариантом" уже обсуждался, он здесь просто неуместен, т.к. (возможно) миллионы POD точек не должны превратиться в что-то "умное" - они должны остаться столь же "тупыми" как они были созданы

Вообще объясните мне пожалуйста - ну откуда такой "полный минор"? Да, мы видим что ни один стандартный паттерн здесь не годится (насколько я знаю). Так что, обосраться и не жить, что ли? Есть довольно очевидное решение с базовым виртуальным классом (которое я и реализовал). Почему это "костыль", "велосипед", "вилы/грабли", тем более "против логики языка"? Только потому что так не писал Александреску (или кто-то еще)? Что же такого плохого мы делаем? Используем виртуальный механизм? Наследуемся template классом от non-template? И что с того, чем оно плохо?

Есть лучшие задумки (с точки зрения связки/взаимодействия классов) - с удовольствием приму участие в обсуждении. По поводу Append - ну я его маленько раскрутил, там не все так мрачно. Но я никогда не любил ни template ни теорию вообще, поэтому, честно говоря, я (немного) надеялся увидеть лучшие решения от Вас


Название: Re: Шаблоны.
Отправлено: BRE от Март 27, 2012, 08:44
Клиент может прекрасно жить не зная ненужных (ему) подробностей, напр (упрощенно)
Код
C++ (Qt)
if (model.mSpaceUV) {
mSpaceUV->SetPoinr3F(0, (mSpaceUV->GetPoint3F(0) + mSpaceUV->GetPoint3F(1)) * 0.5);
}
 
А почему ты здесь используешь Set/GetPoint3F, а не Set/GetPoint3D или Set/GetPoint2F. Ты как здесь определил, что в чудо-контейнере точки как раз 3F? Надеешься?

Клиенту в принципе-то нужно немного: Get/Set, и др стандартные операции с контейнером. Да, будут какие-то расходы на возврат по значению, на возможный перевод float <-> double, но они вполне приемлемы
Хорошо так "упрощать". То, сё и др. А на выходе получаем дублирование чуть ли не каждого метода для каждого возможного типа и сложная расширяемость такого контейнера. Для нового типа точки придется добавить свои методы в контейнер, и самое страшное добавить поддержку в клиенты этого класса.
А то что твои решения для тебя всегда приемлемы это я знаю. :)

Вообще объясните мне пожалуйста - ну откуда такой "полный минор"? Да, мы видим что ни один стандартный паттерн здесь не годится (насколько я знаю). Так что, обосраться и не жить, что ли? Есть довольно очевидное решение с базовым виртуальным классом (которое я и реализовал). Почему это "костыль", "велосипед", "вилы/грабли", тем более "против логики языка"? Только потому что так не писал Александреску (или кто-то еще)? Что же такого плохого мы делаем? Используем виртуальный механизм? Наследуемся template классом от non-template? И что с того, чем оно плохо?
Недостатки такого решения не в том, что есть виртуальный базовый класс, а в том что для каждого типа точек нужна отдельная поддержка в коде, причем как на стороне самого контейнера, так и на стороне его клиента. Чем же это решение отличается от variant? Результат тот-же, только более ограниченный.

Но я никогда не любил ни template ни теорию вообще, поэтому, честно говоря, я (немного) надеялся увидеть лучшие решения от Вас
Вот из-за этого все проблемы. Сначала ты (ни любящий ничего) создаешь свое "решение", а потом надеешься на чудесное решение от кого-то в одну строчку, что бы особо ничего не пришлось переделывать и сразу стало зашибись.


Название: Re: Шаблоны.
Отправлено: Igors от Март 27, 2012, 09:59
А почему ты здесь используешь Set/GetPoint3F, а не Set/GetPoint3D или Set/GetPoint2F. Ты как здесь определил, что в чудо-контейнере точки как раз 3F? Надеешься?
Никак не определял. Клиент не обязан подстраиваться под формат хранения (хотя и такая возможность есть). Он может попросить данные в нужном ему формате, сработает соотв конструктор или оператор =. 

Хорошо так "упрощать". То, сё и др. А на выходе получаем дублирование чуть ли не каждого метода для каждого возможного типа и сложная расширяемость такого контейнера. Для нового типа точки придется добавить свои методы в контейнер, и самое страшное добавить поддержку в клиенты этого класса.
А то что твои решения для тебя всегда приемлемы это я знаю. :)
Как и Ваши решения приемлемы для Вас :) Появился новый тип базовых - придется добавить еще Get/Set и операторы для переливания - это все. Но базовых типов немного, это не проблема. Много получается комбинаций

Недостатки такого решения не в том, что есть виртуальный базовый класс, а в том что для каждого типа точек нужна отдельная поддержка в коде, причем как на стороне самого контейнера, так и на стороне его клиента. Чем же это решение отличается от variant? Результат тот-же, только более ограниченный.
Ну вот не надо прикидываться непонимающим. В случае варианта мы меняем формат самого элемента, в нашем случае мы оперируем с доступом к элементам хранящимся в фиксированном формате. Это разница принципиальная. Никакого "чуда" (типа контейнер хранящий все на свете) не планировалось. Речь шла об узком круге базовых структур, никто не собирался хранить напр строки. Беда теоретиков в том что они пропускают специфику мимо ушей, а потом начинают доказывать что, мол, в общем виде это недостижимо. Так в общем никто и не просил.

Вот из-за этого все проблемы. Сначала ты (ни любящий ничего) создаешь свое "решение", а потом надеешься на чудесное решение от кого-то в одну строчку, что бы особо ничего не пришлось переделывать и сразу стало зашибись.
Почему Вы думаете что каждый кто поднимает тему - жадный нуб который хочет от Вас что-то получить? :) Я решал эту задачу так, интересно как бы делали другие, нормально. По поводу теории - я так вижу что не люблю я ее совершенно правильно. Если бы m_ax думал сам - он бы придумал в 100 раз лучше чем я. Но он оказался под влиянием глупых книг, зажался, скатился на шаблонные/банальные решения. Ото меньше надо читать всяких Скуперфильдов


Название: Re: Шаблоны.
Отправлено: BRE от Март 27, 2012, 10:26
Никак не определял. Клиент не обязан подстраиваться под формат хранения (хотя и такая возможность есть). Он может попросить данные в нужном ему формате, сработает соотв конструктор или оператор =. 
В контейнере лежит точка 2F, клиент запрашивает 3D - он что получит. Кто это должен отслеживать? Компилятор уже не может, ты ему ручки отбил.
Это решение ничем не отличается от варианта и дело вовсе не в том меняем или не меняем мы тип элемента, а в том, что клиент понятия не имеет до реального вызова, что храниться в контейнере и может ли он работать с этим типом точек.

Как и Ваши решения приемлемы для Вас :) Появился новый тип базовых - придется добавить еще Get/Set и операторы для переливания - это все. Но базовых типов немного, это не проблема. Много получается комбинаций
Могут быть клиенты, которые определяют тип точки и пытаются подстроиться под него, Тогда появиться необходимость менять еще их. А если использовать такой контейнер повсеместно, то может оказаться, что править придется очень много.

Если бы m_ax думал сам - он бы придумал в 100 раз лучше чем я. Но он оказался под влиянием глупых книг, зажался, скатился на шаблонные/банальные решения. Ото меньше надо читать всяких Скуперфильдов
Это случиться не раньше, чем m_ax отбросит глупые условия постановки задачи, которые только ты считаешь приемлемыми.
А книги зло, да.


Название: Re: Шаблоны.
Отправлено: Igors от Март 27, 2012, 11:10
В контейнере лежит точка 2F, клиент запрашивает 3D - он что получит. Кто это должен отслеживать? Компилятор уже не может, ты ему ручки отбил.
Пример: хранимая точка 2F (0.25f. 1.0f). Возвращаемая точка 3D (0.25, 1.0, 0.0)
Это решение ничем не отличается от варианта и дело вовсе не в том меняем или не меняем мы тип элемента, а в том, что клиент понятия не имеет до реального вызова, что храниться в контейнере и может ли он работать с этим типом точек.
Снова Вы рассматриваете "слишком общий" случай. Как правило клиенту пофиг есть ли 3-я UV координата. Типичная задача - интерполировать UV. Ну клиент будет работать с 3-мя (возможно в double), данные - считываться и сохраняться напр как 2 float. Все нормально, др случаи см ниже

Могут быть клиенты, которые определяют тип точки и пытаются подстроиться под него, Тогда появиться необходимость менять еще их. А если использовать такой контейнер повсеместно, то может оказаться, что править придется очень много.
Да, такой вариант возможен. Напр клиент хочет развернуться с SSL и его все-таки не устраивает конвертация (пусть относительно дешевая). В конце-концов Append - это тоже пример когда нужен "конкретный тип" (а не общий механизм). В этом случае я (пока) не вижу др решений кроме (уродливого) switch. Предлагайте - обсудим. А просто так охаять чужую корову - ума не надо  :)

Это случиться не раньше, чем m_ax отбросит глупые условия постановки задачи, которые только ты считаешь приемлемыми.
А книги зло, да.
Ну понеслась.. "Это неправильные пчелы! Они дают неправильный мед!". Но где ж я возьму "правильных" (чтобы было точно по книжке)?  :) Конечно я не имею права грузить Вас спецификой, но я никак не выдрючивался, не усложнял задачу, наоборот - максимально упростил как смог. Вы легко можете это проверить слегка погуглив.

Книги - не зло, а иногда даже необходимы. Но мне кажется лучше прочитать меньше да осмыслить больше чем наоборот  :)     


Название: Re: Шаблоны.
Отправлено: m_ax от Март 27, 2012, 11:12
Согласен с BRE)

Вариант (variant) здесь будет более гибким как раз из-за того, что не нужно будет переопределять функции set/get для каждого типа точек. И вообще (с вариантом) само наследование отпадает.
Но не подходит, ладно..

Остаётся вариант с наследованием и последующим переопределением гетеров, сетеров.
Я же предлагал такой вариант (где в базовом класе будут только две шаблонные функции не виртуальные)
Код
C++ (Qt)
struct base
{
   viryual ~base() {}
   template <class Derived, class Point>
   void set(size_t index, const Point &p) {
       Derived *d = dynamic_cast<Derived*>(this);
       if (d)
           d->vector[index] = p;
   }
   template <class Derived, class Point>
   Point get(size_t index) const {
       Derived *d = dynamic_cast<Derived*>(this);
       if (d)
          return d->vector[index];
       return Point();
   }
};
 

Чем этот вариант хуже того, где в базовый класс вводятся виртуальные функции для каждого типа точки?
 


Название: Re: Шаблоны.
Отправлено: Igors от Март 27, 2012, 11:28
Согласен с BRE)
:'(
Чем этот вариант хуже того, где в базовый класс вводятся виртуальные функции для каждого типа точки?
А как Вы позовете такие get/set? Откуда клиент возьмет Derived:?


Название: Re: Шаблоны.
Отправлено: m_ax от Март 27, 2012, 11:39
Цитировать
А как Вы позовете такие get/set? Откуда клиент возьмет Derived:?
Да, это верно) Он же (клиент) его не знает..

Ну тогда, следственно, все Ваши условия диктуют принять вариант с наследованием. А как ещё? 


Название: Re: Шаблоны.
Отправлено: BRE от Март 27, 2012, 11:46
Ну понеслась.. "Это неправильные пчелы! Они дают неправильный мед!". Но где ж я возьму "правильных" (чтобы было точно по книжке)?  :) Конечно я не имею права грузить Вас спецификой, но я никак не выдрючивался, не усложнял задачу, наоборот - максимально упростил как смог. Вы легко можете это проверить слегка погуглив.
Вот именно понеслась... Ты формулируешь конкретную задачу (хочу такой контейнер) - нате, думайте... Но задача уродлива с рождения. Стоит изменить условия и возможно все будет получаться лучше. Может стоит разделить контейнеты для точек и UW, может стоит объединить точки и нормали, может ...
Ну ты же не можешь нас грузить подробностями, поэтому нам остается тыкать палочкой твоего уродца.


Название: Re: Шаблоны.
Отправлено: m_ax от Март 27, 2012, 11:55
Цитировать
А как Вы позовете такие get/set? Откуда клиент возьмет Derived:?
Тогда вместо того, чтобы писать кучу функций на каждый тип точки, разумнее (имхо) написать один класс super точки, которая бы умела себя конвертировать и в PointF и в PointD и во что ей положено по условию..
Тогда была бы всего одна функция get и одна функция set:

Код
C++ (Qt)
void set(const super_point &sp);
 
Но, если у super_point будет сответствующие конструкторы, то в метод можно будет передовать непосредственно объекты и PointF и PointD и...
Код
C++ (Qt)
set(PointF(1,2,3));
set(PointD(3,2,1));
...
 
 

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


Название: Re: Шаблоны.
Отправлено: BRE от Март 27, 2012, 13:23
В контейнере лежит точка 2F, клиент запрашивает 3D - он что получит. Кто это должен отслеживать? Компилятор уже не может, ты ему ручки отбил.
Пример: хранимая точка 2F (0.25f. 1.0f). Возвращаемая точка 3D (0.25, 1.0, 0.0)
Хорошо же, а.
Т.е. какой-то нерадивый программист передает в функцию контейнер с UW, хотя функция ждет нормали и она начинает их обрабатывать.
Это не пресекается не то что во время компиляции, а даже во время выполнения.


Название: Re: Шаблоны.
Отправлено: Igors от Март 27, 2012, 16:02
Но задача уродлива с рождения. Стоит изменить условия и возможно все будет получаться лучше. Может стоит разделить контейнеты для точек и UW, может стоит объединить точки и нормали, может ...
Ну ты же не можешь нас грузить подробностями, поэтому нам остается тыкать палочкой твоего уродца.
Почему уродца-то? :) Здесь простая и естественная организация данных. Модель может иметь нормали и не иметь UV, а может и наоборот (хотя редко). Ну стало быть нормали и UV - 2 разных контейнера.

Тогда вместо того, чтобы писать кучу функций на каждый тип точки, разумнее (имхо) написать один класс super точки, которая бы умела себя конвертировать и в PointF и в PointD и во что ей положено по условию..
Тогда была бы всего одна функция get и одна функция set:
Хммм.. ну оно и сейчас примерно так и получается - близко к этому. Многочисленные конвертации можно вынести, а можно и прямо в базовом класса

Хорошо, а что же делать в случаях если понадобился "конкретный" контейнер, не устраивает общий? Напр ф-ции OpenGL требуют указателей на float или double. Здесь особых проблем нет

Код
C++ (Qt)
void DrawOpenGL( CoordVec * coord )
{
CoordVecT <Point3D> * coordD = dynamic_cast <CoordVecT <Point3D> *> (coord);
if (coord)
 glDrawFunction_d(&coordT[0].x, coordT->size());
else
 CoordVecF <Point3F> * coordF = dynamic_cast <CoordVecT <Point3F> *> (coord);
 ...
}
 
Ну и так по всем базовым типам. Не блеск конечно, но поскольку число базовых типов очень ограничено - нормально. А вот когда требуются 2 операнда "конкретных контейнера" - тогда хуже, получается громоздко
Код
C++ (Qt)
void Append( CoordVec & dst, const CoordVec & src );
CoordVec & operator = ( const CoordVec & src );
 
Не то чтобы таких очень много, но есть, и это проблема


Название: Re: Шаблоны.
Отправлено: m_ax от Март 27, 2012, 16:21
Цитировать
Не то чтобы таких очень много, но есть, и это проблема
Адаптер очевидно писать нужно..


Название: Re: Шаблоны.
Отправлено: Igors от Март 27, 2012, 18:12
Ага, а если сделать super_point, то Append получается в неск строк - и без всяких switch. Уже хорошо! Может как-то еще лучше (без расходов на перегонку в super и обратно)?


Название: Re: Шаблоны.
Отправлено: m_ax от Март 27, 2012, 18:34
Ага, а если сделать super_point, то Append получается в неск строк - и без всяких switch. Уже хорошо! Может как-то еще лучше (без расходов на перегонку в super и обратно)?
Цель с которой водится адаптер (или хрен знает как его назвать) заключается в том, чтобы полностью исключить в клиентском коде (в функциях внешних) все явные приведения типов и т.п. Это всё делает адаптер, а на выходе выдаёт super_point, которая уже умеет конвертить себя к другим частным PointX.
А уж вся ответственность за то, что было изначально в контэйнере и для чего предназначалось и что в результате получится в клиентском коде ложится на Вас))
Пугает какой то элемент неопределённости) Это как в рулетку играть) 


Название: Re: Шаблоны.
Отправлено: Igors от Март 27, 2012, 18:50
Пугает какой то элемент неопределённости) Это как в рулетку играть)  
Есть такое, напр клиент рассчитывал на 3-ю координату, ее "физически" нет, но адаптер ее искусственно создаcт. Ну такому клиенту придется проверить Format(). Вообще на случаи 2/4 можно и забить (они встречаются в году раз) и просто считать что координат всегда 3. А вот отделаться от float/double никак не получается.

А варианта с вмещающим super_point я не видел :) Конечно сейчас это кажется совсем простым. Спасибо (можете - когда хотите)


Название: Re: Шаблоны.
Отправлено: m_ax от Март 27, 2012, 19:23
Супер поинт - это обычная точка, но с доп возможностями:
Код
C++ (Qt)
class SuperPoint
{
public:
   SuperPoint(const double &x1, const double &x2, const double &x3, const double &x4)
       : _x1(x1), _x2(x2), _x3(x3), _x4(x4) {}
    SuperPoint(const PointF &p) : _x1(p.x), _x2(p.y), _x3(p.z), _x4(0) {}
    SuperPoint(const PointD &p) : _x1(p.x), _x2(p.y), _x3(p.z), _x4(0) {}
    //...
    PointF toPointF() const { return PointF(x1, x2, x3); }
    PointD toPointD() const { return PointD(x1, x2, x3); }
    //....
private:
   double x1, x2, x3, x4;    
};
 
В базовом классе тогда нужно будет определить два метода set и get:
Код
C++ (Qt)
struct base
{
   virtual ~base() {}
   virtual Format format() const = 0;
   virtual SuperPoint get(size_t index) const  = 0;
   virtual void set(size_t index, const SuperPoint &sp) = 0;
};
 
struct derivedF : public base
{
   virtual Format format() const { return TPointF; }
   virtual SuperPoint get(size_t index) const {
       return SuperPoint(vector[index]);
   }
   virtual void set(size_t index, const SuperPoint &sp) {
       vector[index] = sp.toPointF();
   }
   std::vector<PointF> vector;
};
 
// ...
 

Останется только адаптер написать.
В c++11, за счёт того, что там появилась такая фича, как constexpr, его можно было бы особенно изящно реализовать... (без всяких switch)


Название: Re: Шаблоны.
Отправлено: Igors от Март 27, 2012, 20:05
А зачем мне еще какой-то адаптер? Просто так
Код
C++ (Qt)
template <class T>
class CoordVecT : public CoordVec {
virtual TBigPoint Get( size_t index ) const { return mData[index]; }
virtual void Set( size_t index, const TBigPoint & pt ) { mData[index] = pt; }
..
vector <T> mData;
};
 
Ну операторы для всех базовых определить (для переливания в TBigPoint и обратно). Вот и все


Название: Re: Шаблоны.
Отправлено: m_ax от Март 27, 2012, 20:18
Цитировать
А зачем мне еще какой-то адаптер? Просто так
Может и незачем.. Хотя кто Вас знает) Сегодня не надо, а завтра будет надо)

Цитировать
Ну операторы для всех базовых определить (для переливания в TBigPoint и обратно). Вот и все
Не стоит делать так, чтоб к интерфейсу базовых точек PointF, PointD примешивался ещё и интерфейс SuperPoint. Они не должны о нём ничего знать.
Только SuperPoint (он же BigPoint) может знать о них..

Иначе идейная чистота нарушается)   


Название: Re: Шаблоны.
Отправлено: Igors от Март 27, 2012, 20:37
Не стоит делать так, чтоб к интерфейсу базовых точек PointF, PointD примешивался ещё и интерфейс SuperPoint. Они не должны о нём ничего знать.
Только SuperPoint (он же BigPoint) может знать о них..
Не вижу чем это мне грозит

Иначе идейная чистота нарушается)   
Ничего, я это переживу  :)

А вот такая деталька: ведь SuperPoint может знать из чего он получен. Это никак не связано с темой, просто может (в будущем, при случае) удастся задействовать в корыстных целях


Название: Re: Шаблоны.
Отправлено: m_ax от Март 27, 2012, 20:53
Цитировать
Не вижу чем это мне грозит
Это лишние связи, которые ничего, кроме синтаксического сахара не дают.. Но за этот сомнительный сахар придётся платить лишим кодом в каждой базовой точке.

Цитировать
А вот такая деталька: ведь SuperPoint может знать из чего он получен. Это никак не связано с темой, просто может (в будущем, при случае) удастся задействовать в корыстных целях
 
Тогда это уже будет жирная точка)