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

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

Страниц: [1] 2   Вниз
  Печать  
Автор Тема: Наследование template  (Прочитано 15251 раз)
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« : Май 10, 2016, 13:51 »

Добрый день

Название темы корявое (не нашел лучшего), пример: вот сейчас я рисую некий template
Код
C++ (Qt)
template <class T>
struct MyTemplateClass {
 
 T * Get( size_t );
 void Set( T *, size_t );
 
 void Read( MyStream & );
 void Write( MyStream & ) const;
};
Первые 2 метода универсальны (не завязаны на др классы), а следующие 2 не очень - совсем необязательно что и в др проекте будет MyStream. Может в данном случае лучше сделать внешние операторы << и >>, но это частное решение. А как в общем случае сделать аккуратнее?

Спасибо
Записан
ssoft
Программист
*****
Offline Offline

Сообщений: 579


Просмотр профиля
« Ответ #1 : Май 10, 2016, 14:48 »

В любом случае необходимо операции сериализации отделять от данных.
Т.е. такие структуры должны содержать информацию только о себе и методы для ее изменения.
О способе использования (в т.ч. способе сериализации) данные ни чего знать не должны.

Существует известная проблема, связанная с правами доступа к внутренним полям, но если использовать только public доступ, то таких проблем не возникнет.

Т.о. методы Read и Write должны быть внешними по отношению к MyTemplateClass и могут быть реализованы, например, через специализацию шаблона

Код
C++ (Qt)
template< class T >
struct Serializer {};
 
template< class T >
struct Serializer< MyTemplateClass< T > >
{
   static bool read ( MyStream &, MyTemplateClass< T > & );
   static bool write ( MyStream &, const MyTemplateClass< T > & );
};
 
struct Serializer< int >
{
   static bool read ( MyStream &, int & );
   static bool write ( MyStream &, int );
};
 
 

и т.д.

Вместе с методами сериализации можно и операторы <<,>> реализовать.

Код
C++ (Qt)
template < class T >
MyStream & operator << ( MyStream & stream, const T & value )
{
   Serializer< T >::write( stream, value );
   return stream;
}
 
template < class T >
MyStream & operator >> ( MyStream & stream, T & value )
{
   Serializer< T >::read( stream, value );
   return stream;
}
 

или что-то еще, не принципиально.
Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #2 : Май 11, 2016, 08:15 »

В любом случае необходимо операции сериализации отделять от данных.
Т.е. такие структуры должны содержать информацию только о себе и методы для ее изменения.
О способе использования (в т.ч. способе сериализации) данные ни чего знать не должны.
Не раз слышал эту "официальную доктрину", но мне она представляется спорной. Да, это убирает "зависимость", но ценой полного развала инкапсуляции - кому как не самому объекту знать как себя сохранять? Давно хотел попробовать такой вариант
Код
C++ (Qt)
template <class T>
struct MyTemplateClass {
 
 T * Get( size_t );
 void Set( T *, size_t );
 
 template <class Stream>
 void Read( Stream & );
 
 template <class Stream>
 void Write( Stream & ) const;
};
Что здесь плохого?
Записан
Racheengel
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 2679


Я работал с дискетам 5.25 :(


Просмотр профиля
« Ответ #3 : Май 11, 2016, 08:59 »

Мне тоже непонятна идея с разделением. Вернее, ее практический смысл.
Каким образом объект будет менять закрытые данные? И в чем преимущества подобного деления?
Если непременно хочется писать в несколько разных форматов, то объект все равно должен уметь выгрузить свои данные и восстановить их. В общем случае он может сериализироваться в какой-либо intermediate формат (например, в QDaraStream), а уже внешний сериалайзер может делать с этими данными что угодно. Но непосредственно сериализация данных должна быть частью объекта.
Записан

What is the 11 in the C++11? It’s the number of feet they glued to C++ trying to obtain a better octopus.

COVID не волк, в лес не уйдёт
m_ax
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 2094



Просмотр профиля
« Ответ #4 : Май 11, 2016, 09:34 »

Цитировать
Мне тоже непонятна идея с разделением. Вернее, ее практический смысл.
Вот сравните:
Код
C++ (Qt)
ObjA objA;
ObjB objB;
ObjC objC;
ObjD objD;
 
stream out;
objA.write(out);
out << objB;
objC.Write(out);
objD.write2Stream(out);
 

И это:
Код
C++ (Qt)
ObjA objA;
ObjB objB;
ObjC objC;
ObjD objD;
 
stream out;
 
out << objA << objB << objC << objD;
 

Цитировать
Каким образом объект будет менять закрытые данные?
Объявляете операторы чтения, записи друзьями.


Записан

Над водой луна двурога. Сяду выпью за Ван Гога. Хорошо, что кот не пьет, Он и так меня поймет..

Arch Linux Plasma 5
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #5 : Май 11, 2016, 14:59 »

Вот сравните:
Код
C++ (Qt)
...
stream out;
objA.write(out);
out << objB;
objC.Write(out);
objD.write2Stream(out);
 
Ну это можно пережить. Хужее если так
Код
C++ (Qt)
std::vector<Object1> arr1;
std::vector<Object2> arr2;
 
И выясняется что если методы записи/чтения Object1 и Object2 не совпадают, то обобщить (де) сериализацию контейнера не удастся, придется возюкаться с каждым. При этом операторы << и >> ничем не лучше, просто они традиционны/каноничны, поэтому лучше их придерживаться.
Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #6 : Май 11, 2016, 15:09 »

Давно хотел попробовать такой вариант
Код
C++ (Qt)
 template <class Stream>
 void Read( Stream & );
 
Сегодня попробовал и остался недоволен. Все хорошо пока (де)сериализация сводится к << и >>, на что я собсно и рассчитывал. Но вот как только что-то другое.. Напр у меня данные часто пишутся: идентификатор + длина + сами данные. А значит нужно подсчитать длину (в памяти или по файлу). Помещая этот код в тело template-метода я фактически признаю что пишу в конкретный (бинарный) поток, прощай общность.
Записан
m_ax
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 2094



Просмотр профиля
« Ответ #7 : Май 11, 2016, 16:57 »

Цитировать
Но вот как только что-то другое.. Напр у меня данные часто пишутся: идентификатор + длина + сами данные. А значит нужно подсчитать длину (в памяти или по файлу). Помещая этот код в тело template-метода я фактически признаю что пишу в конкретный (бинарный) поток, прощай общность.
Поэтому специализируете serializer, как уже было предложено под конкретные форматы..
Код
C++ (Qt)
template <class>
struct serializer {};
 
template <>
struct serializer<XMLStream>
{
   template <class T>
   static bool read(XMLStream & in, T & data) {}
 
   template <class T>
   static bool write(XMLStream & out, const T & data) {}
}
 
 
template <class Stream>
 void Read( Stream & in)
{
     serializer<Stream>::read(in, *this);
}
 
 
Записан

Над водой луна двурога. Сяду выпью за Ван Гога. Хорошо, что кот не пьет, Он и так меня поймет..

Arch Linux Plasma 5
Racheengel
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 2679


Я работал с дискетам 5.25 :(


Просмотр профиля
« Ответ #8 : Май 11, 2016, 18:13 »

Объявляете операторы чтения, записи друзьями.

Но в любом случае придется расширять классы, чтобы сделать это. Почему бы сразу не встроить в них универсальную сериализацию в стримовый формат?
Записан

What is the 11 in the C++11? It’s the number of feet they glued to C++ trying to obtain a better octopus.

COVID не волк, в лес не уйдёт
m_ax
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 2094



Просмотр профиля
« Ответ #9 : Май 11, 2016, 18:43 »

Цитировать
Но в любом случае придется расширять классы, чтобы сделать это.
Вы что имеете в виду под расширением? Я имел ввиду как то  так:
Код
C++ (Qt)
template <class T>
class SomeClass
{
public:
   template <class Stream, class R>
   friend Stream & operator<<(Stream &, const SomeClass<R> &);
 
   template <class Stream, class R>
   friend Stream & operator>>(Stream &, SomeClass<R> &);
 
private:
   T data;
};
 
 
template <class Stream, class R>
Stream & operator<<(Stream & out, const SomeClass<R> & obj)
{
   out << obj.data;
   return out;
}
 
template <class Stream, class R>
Stream & operator>>(Stream & in, SomeClass<R> & obj)
{
   in >> obj.data;
   return in;
}
 
 
 
int main()
{
   SomeClass<int> obj;
 
   std::cin >> obj;
   std::cout << obj;
 
   return 0;
}
 
 

Цитировать
Почему бы сразу не встроить в них универсальную сериализацию в стримовый формат?
Потому что во-первых, в этом нет особого смысла, а во-вторых, например, как вы запишите в поток какой-нибудь контейнер?
Я вот так вот:
Код
C++ (Qt)
QVector<SomeClass> vector;
 
out << vector;
 
А вы? Будете цикл организовывать?
Записан

Над водой луна двурога. Сяду выпью за Ван Гога. Хорошо, что кот не пьет, Он и так меня поймет..

Arch Linux Plasma 5
__Heaven__
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 2130



Просмотр профиля
« Ответ #10 : Май 12, 2016, 09:46 »

А каким образом реализация и объявления должны быть разбросаны по файлам?
Со структурой понятно, что она должна быть в MyStruct.h и MyStruct.cpp. А как быть с методами записи в потоки? Как их распределять так, чтобы сохранялась переносимость между проектами?
« Последнее редактирование: Май 12, 2016, 09:53 от __Heaven__ » Записан
Racheengel
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 2679


Я работал с дискетам 5.25 :(


Просмотр профиля
« Ответ #11 : Май 12, 2016, 10:00 »

Потому что во-первых, в этом нет особого смысла, а во-вторых, например, как вы запишите в поток какой-нибудь контейнер?

Ну, по сути, Вы делаете то же самое, но с помощью операторов Улыбающийся
Я имел в виду такой вариант:

class MyClass
{
public:
   virtual void storeData(QDataStream& v) const;
   virtual void restoreData(QDataStream& v);
...
}

и далее так:

void MyClass::storeData(QDataStream& v) const
{
  v << myData1 << myData2;
}


void MyClass::restoreData(QDataStream& v)
{
  v >> myData1;
  v >> myData2;
}

а уже внешний контроллер может делать с v все, что захочет:

MyClass c;
...
QByteArray data;
QDataStream v(&data);
c.storeData(v);

QSettings set(...);
set.setValue("MyClassData", v);
...
Записан

What is the 11 in the C++11? It’s the number of feet they glued to C++ trying to obtain a better octopus.

COVID не волк, в лес не уйдёт
m_ax
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 2094



Просмотр профиля
« Ответ #12 : Май 12, 2016, 10:52 »

Цитировать
А каким образом реализация и объявления должны быть разбросаны по файлам?
Как решите, так и будет) Если друзья шаблонные, то обычно в том же хедере и реализация.

Цитировать
Ну, по сути, Вы делаете то же самое, но с помощью операторов
Конечно)
Только я всё равно не вижу чем лучше функции члены для чтения записи, чем специально отведённые для этого операторы?
Когда я имею дело с каким-либо классом я в первую очередь буду ожидать от него следующего стандартного поведения (когда речь идёт о чтении/записи)
Код
C++ (Qt)
out << obj;
in >> obj;
 

а не искать у него методов типа Write/storeData и т.д..  Улыбающийся
Записан

Над водой луна двурога. Сяду выпью за Ван Гога. Хорошо, что кот не пьет, Он и так меня поймет..

Arch Linux Plasma 5
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #13 : Май 12, 2016, 11:01 »

Поэтому специализируете serializer, как уже было предложено под конкретные форматы..
Код
C++ (Qt)
template <class>
struct serializer {};
 
template <>
struct serializer<XMLStream>
{
   template <class T>
   static bool read(XMLStream & in, T & data) {}
 
   template <class T>
   static bool write(XMLStream & out, const T & data) {}
}
 
 
template <class Stream>
 void Read( Stream & in)
{
     serializer<Stream>::read(in, *this);
}
 
 
Ну а смысл этой городушки? Напр в роли Stream тот же QDataStream, методов read/write у него нет.

Я имел в виду такой вариант:

class MyClass
{
public:
   virtual void storeData(QDataStream& v) const;
   virtual void restoreData(QDataStream& v);
...
}

и далее так:

void MyClass::storeData(QDataStream& v) const
{
  v << myData1 << myData2;
}
Это сериализация для одного конкретного потока QDataStream, и этот код пролетает в проекте без Qt

как вы запишите в поток какой-нибудь контейнер?
Я вот так вот:
Код
C++ (Qt)
QVector<SomeClass> vector;
 
out << vector;
 

А вы? Будете цикл организовывать?
А за Вас кто это будет делать? Вообще цепочка операторов << и >> это самый низкий уровень когда данные следуют в известном порядке. А хотя бы уровнем выше их уже надо идентифицировать, как Вы будете это делать?
Записан
m_ax
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 2094



Просмотр профиля
« Ответ #14 : Май 12, 2016, 11:49 »

Цитировать
А за Вас кто это будет делать?
Да это уже сделано. Посмотрите на QVector, например. Теперь чтоб его вместо одного движения записать в поток, придётся для каждого элемента вызывать не стандартные методы.
Зачем?
 
Цитировать
А хотя бы уровнем выше их уже надо идентифицировать, как Вы будете это делать?
А какое это имеет отношение к вопросу об использовании операторов<<>> vs мемберов?
Записан

Над водой луна двурога. Сяду выпью за Ван Гога. Хорошо, что кот не пьет, Он и так меня поймет..

Arch Linux Plasma 5
Страниц: [1] 2   Вверх
  Печать  
 
Перейти в:  


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