Модераторы: Daevaorn

Поиск:

Ответ в темуСоздание новой темы Создание опроса
> Сериализация, наверное, не знаю как тему назвать правильно 
:(
    Опции темы
georain
  Дата 15.2.2008, 15:18 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Бывалый
*


Профиль
Группа: Участник
Сообщений: 193
Регистрация: 28.11.2006
Где: Санкт-Петербург

Репутация: нет
Всего: нет



Вот приходят из сети данные блоками, в начале блока есть число - идентификационный номер класса который он содержит. Нужно преобразовать блок в класс с помощью статической функции этого класса.
Пользоваться case не хочу, классов много, хочу чтобы работало быстро smile

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

Код


//CDR - потоковый класс
CDR cdr(input);

//каждый класс содержит функцию create()
class ClassA: public Base{
    public:

    virtual ~ClassA(){};

    static Base * create(CDR * cdr){
        ClassA * bp = new ClassA;
        // тут инициализируем данными, например
        // member = cdr->getMember();
        return bp;
    };    
};



Делаю enum - перечисляю все мои классы, номера для каждого класса беру из протокола
Код

enum{
nClassA = 1,
nClassB,
nClassC
};


Далее делаю финт ушами

Код

//создаю класс - массив функций
class VTable{
    public:
    Base * (*VFunc[100])(CDR *);

    //конструктор
    VTable(){
    //делаю define для упрощения записи
    #define f(CN)   VFunc[n##CN] = CN::create;

    //записываю как в enum
    f(ClassA)
    f(ClassB)
    f(ClassC)

    };
} vt;


Для создания нужного класса пишу
Код

Base * bptr = (*vt.VFunc[cdr.getNumber()])(&cdr);

В итоге вместо множественного case получаю быстрый вызов нужной функции.

Реализация мне нравится, но может есть что получше? Потому что во-первых велосипед наверняка, а во вторых надо не забыть каждый класс в таблицу записать и в enum ещё.

Это сообщение отредактировал(а) georain - 15.2.2008, 15:23
PM MAIL   Вверх
marcusmae
Дата 15.2.2008, 15:53 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


stravaganza
**


Профиль
Группа: Участник
Сообщений: 874
Регистрация: 26.3.2006

Репутация: 5
Всего: 39



georain, действительно, велосипед солидный. Задача, как я понял, из потока распознать и десериализовать объекты. Если у вас дерево классов полиморфное, то почему нельзя эту иерархию как-то удобнее закодировать, чем прямой нумерацией? От этого в итоге будет зависеть, на сколько быстро их будет распознать.


--------------------
ἀπὸ μηχανῆς θεός
PM MAIL ICQ GTalk   Вверх
Alek86
Дата 15.2.2008, 15:53 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Завсегдатай
Сообщений: 1299
Регистрация: 30.1.2007
Где: Киев

Репутация: 21
Всего: 25



удалил

Это сообщение отредактировал(а) Alek86 - 15.2.2008, 15:56


--------------------
user posted image    user posted image
PM MAIL   Вверх
georain
Дата 15.2.2008, 16:36 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Бывалый
*


Профиль
Группа: Участник
Сообщений: 193
Регистрация: 28.11.2006
Где: Санкт-Петербург

Репутация: нет
Всего: нет



Цитата(marcusmae @ 15.2.2008,  15:53)
georain, действительно, велосипед солидный. Задача, как я понял, из потока распознать и десериализовать объекты. Если у вас дерево классов полиморфное, то почему нельзя эту иерархию как-то удобнее закодировать, чем прямой нумерацией? От этого в итоге будет зависеть, на сколько быстро их будет распознать.

Ну так я и прошу совета, подскажите какой есть способ лучше.

На счёт нумерации классов, а как можно удобнее закодировать, чем это будет лучше и чем плохо прямой нумерацией?
PM MAIL   Вверх
Alek86
Дата 15.2.2008, 16:46 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Завсегдатай
Сообщений: 1299
Регистрация: 30.1.2007
Где: Киев

Репутация: 21
Всего: 25



Цитата(georain @  15.2.2008,  15:18 Найти цитируемый пост)
надо не забыть каждый класс в таблицу записать и в enum ещё.

думаю, тут можно так извратиться
Код


enum {nClassA = 1, nClassB, nClassC, nClassE};

class ClassA {};
class ClassB {};
class ClassC {};
class ClassD {}; // нету в енаме, но есть в CREATE_NUMBER_FROM_CLASS
class ClassE {}; // есть в енаме, но нет в CREATE_NUMBER_FROM_CLASS

template<class T> struct Type2Int {/*тут enum'a нету*/};

#define CREATE_NUMBER_FROM_CLASS(class_) \
template<> struct Type2Int<class_> {enum {value = n##class_};};


CREATE_NUMBER_FROM_CLASS(ClassA)
CREATE_NUMBER_FROM_CLASS(ClassB)
CREATE_NUMBER_FROM_CLASS(ClassC)
//CREATE_NUMBER_FROM_CLASS(ClassD)




int main () {
  std::cout << Type2Int<ClassA>::value << std::endl;
  std::cout << Type2Int<ClassB>::value << std::endl;
  std::cout << Type2Int<ClassC>::value << std::endl;
//  std::cout << Type2Int<ClassD>::value << std::endl;
//  std::cout << Type2Int<ClassE>::value << std::endl;
};




раскоментируй все строки и, вникая, увидишь, что при отсутсвии или номера или связи между номером и типом будет бабах (причем, в общем, читабельный) smile

Добавлено @ 16:49
а способа лучше я не смог придумать (применимо к передаче кода класса вначале).

Это сообщение отредактировал(а) Alek86 - 15.2.2008, 16:50


--------------------
user posted image    user posted image
PM MAIL   Вверх
Lazin
Дата 15.2.2008, 17:03 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Завсегдатай
Сообщений: 3820
Регистрация: 11.12.2006
Где: paranoid oil empi re

Репутация: 41
Всего: 154



Думаю наиболее стандартным решением будет использование указателей на производящие ф-ии

Код

typedef Base* (create_fn*) (CDR * cdr);

std::map<int, create_fn> factory;

потом когда приходит пакет можно очень быстро найти нужную ф-ю, скорость конечно ниже чем у switch, зато эта реализация масштабируется
PM MAIL Skype GTalk   Вверх
Mayk
Дата 15.2.2008, 17:16 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


^аВаТаР^ сообщение>>
****


Профиль
Группа: Участник
Сообщений: 2616
Регистрация: 22.5.2005
Где: за границей разум а

Репутация: 45
Всего: 134



Цитата(Lazin @  15.2.2008,  21:03 Найти цитируемый пост)

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

В принципе вместо map'а можно использовать hash_map или даже vector.
Ну а фабрику можно инициализировать статически
Цитата

class A{
staticA* create(CDR* ){
}
};

class RegisterA
{
public:
RegisterA(){
factory[#number-for-A#] = A::create;
};  
};
static RegisterA regA; 





--------------------
 Здесь был кролик. Но его убили.
Человеки < кроликов, йа считаю.
PM MAIL WWW ICQ   Вверх
marcusmae
Дата 15.2.2008, 17:20 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


stravaganza
**


Профиль
Группа: Участник
Сообщений: 874
Регистрация: 26.3.2006

Репутация: 5
Всего: 39



georain, я бы посмотрел, например, следующее : пусть есть всем базовый класс А, его наследники A1, A2, у каждого их которых по паре своих - A11, A12 и A21, A22 соотвественно. Кодировать - цепочку наследования, скажем, для A22 : просто "A-A2-A22" или, нумируя наследников, "00-01-01" - битами, байтами, символами - как-угодно. Ещё пример для A12 : "00-00-01" и т.п. Что за цепочки получились? = Это - пути в дереве наследования. Таким образом, проблема поиска типа сведена к задаче поиска в дереве. Оно, конечно, имеет свои ньюансы, скажем, в отличие от case по прямой нумерации, по ходу поиска отсекается большое число вариантов.

Цитата(georain @  15.2.2008,  16:36 Найти цитируемый пост)
Ну так я и прошу совета, подскажите какой есть способ лучше.

georain, можно много-чего посоветовать, способов много, тк проблема весьма абстрактная.  А принятие ответственности за выбор способа решения для Вашего конкретного бизнеса - одна из составляющих работы smile


--------------------
ἀπὸ μηχανῆς θεός
PM MAIL ICQ GTalk   Вверх
Lazin
Дата 15.2.2008, 18:26 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Завсегдатай
Сообщений: 3820
Регистрация: 11.12.2006
Где: paranoid oil empi re

Репутация: 41
Всего: 154



а так более кошерно)) 
Код

class A{
staticA* create(CDR* ){
}
};
namespace {
 class RegisterA
 {
  public:
  RegisterA(){
   factory[#number-for-A#] = A::create;
  };  
 };
 RegisterA regA; 
};//namespace


Это сообщение отредактировал(а) Lazin - 16.2.2008, 13:28
PM MAIL Skype GTalk   Вверх
Alek86
Дата 16.2.2008, 13:13 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Завсегдатай
Сообщений: 1299
Регистрация: 30.1.2007
Где: Киев

Репутация: 21
Всего: 25



marcusmae, раз говоришь, что есть много способов создания классов по данным из потока, то не мог бы ты сказать, как это сделать лучше для N классов, не связанных родственными узами? ну, или ссылку, как обычно smile

потому как мне задачка понравилась, но ничего лучше представленного в пером посте (ну, + некоторые мета-уловки, которые, возможно, упростят жизнь при добавлении новых классов) не придумал


--------------------
user posted image    user posted image
PM MAIL   Вверх
marcusmae
Дата 16.2.2008, 14:13 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


stravaganza
**


Профиль
Группа: Участник
Сообщений: 874
Регистрация: 26.3.2006

Репутация: 5
Всего: 39



Цитата(Alek86 @  16.2.2008,  13:13 Найти цитируемый пост)
N классов, не связанных родственными узами

хорошо сказал  smile
вообще-то с самого начала речь шла о полиморфном дереве классов, ну а если нет, то его всегда можно искусственно создать, определив, к примеру, базовый абстрактный класс :

Код

template<class T> class ISerializable {
    virtual char* get_ID() { return "root"; }

    virtual char* Serialize() = 0;
    virtual T Deserialize(char* stream) = 0;
};


Уже после того, как вчера появились последние посты, я подумал, а как вообще нужно докатиться (в негативном смысле) до ситуации, когда в потоке непонятно-какие объекты, типы которых подлежат определению? Вижу две возможности :

1) на машине в сети работает программа с функцией main(), в которой наинициализирована и сериализована целая армия экземпляров разных типов. Но вообще-то так в кучу обычно не сваливают, а создают большой класс-контейнер Main или, как в .NET, Program, для которого уже можно определить функцию сериализации (которая в т.ч. будет вызывать сериализацию составных компонентов) и отправлять в поток уже как целостный объект.

2) экземпляр класса не может сериализовать сам себя. Если он сериализовался, то что-то извне этого потребовало : явный вызов или исключение. Это можно было бы обыграть, чтобы как-то дополнительно облегчить работу серверу.



--------------------
ἀπὸ μηχανῆς θεός
PM MAIL ICQ GTalk   Вверх
Alek86
Дата 16.2.2008, 15:08 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Завсегдатай
Сообщений: 1299
Регистрация: 30.1.2007
Где: Киев

Репутация: 21
Всего: 25



ну "вот так вот получилось"

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


--------------------
user posted image    user posted image
PM MAIL   Вверх
marcusmae
Дата 16.2.2008, 17:10 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


stravaganza
**


Профиль
Группа: Участник
Сообщений: 874
Регистрация: 26.3.2006

Репутация: 5
Всего: 39



Цитата(Alek86 @  16.2.2008,  15:08 Найти цитируемый пост)
"вот так вот получилось"


Alek86, получиться может много-чего, просто улучшаемость заведомо плохих решений весьма ограничена.

Ты сказал ключевое слово контейнер. Раз он есть, значит 1) его тип известен - можно сразу вызывать его десериализатор 2) "рассовывать" - забота контейнера, он должен уметь своим методом Deserialize прочитать, что в нём лежит. Этот вариант я описал в (1). А не нравится мне вариант, когда контейнера нет. То есть, просто ткнули заголовок и указатель на байты и длину - то, о чём шла речь вначале. Вот мне интересно : зачем тот, кто это сделал, так сделал?


Это сообщение отредактировал(а) marcusmae - 16.2.2008, 17:18


--------------------
ἀπὸ μηχανῆς θεός
PM MAIL ICQ GTalk   Вверх
tol05
Дата 16.2.2008, 19:33 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Участник Клуба
Сообщений: 1632
Регистрация: 21.12.2006
Где: Харьков

Репутация: 1
Всего: 170



Привет всем. 
Я давно забыл плюсы и поэтому не буду писать здесь код.
Я не очень четко понял задачу в части
Цитата(georain @  15.2.2008,  14:18 Найти цитируемый пост)
В каждом классе делаю статическую функцию которая принимает указатель на данные, возвращает указатель на базовый класс указывающий на новый класс (производный с нужным номером) созданный из этих данных
Непонятно почему выбрана такая реализация: и в базовом, и в дочернем класса будут статические ф-ции, и какая связь между статическими ф-циями базового и дочернего классов? 

Но меня попросили посмотреть тему и высказаться, поэтому напишу свое видение вопроса концептуально  smile 

Десерилизация - это процесс создания и инициализации объекта, аналогичный созданию и инициализации объекта через конструктор. С тем только отличием, что вместо инициализирующих параметров в конструктор передается поток, содержащий полный набор данных объекта. Вместо конструктора marcusmae предлагает использовать вторую виртуальную ф-цию... Ну что ж, можно и так, просто это будет лишний вызов - в любом случае сначала нужно вызвать конструктор десерилизуемого объекта, а потом для него эту виртуальную ф-цию...
Но повторяю, пусть пока будет как у marcusmae  smile Сами недостаток этот увидите.

Соответственно, для того, чтобы определить, в каком порядке этот поток содержит даннные нашего класса, необходимо построить и зафиксировать граф серилизации. Граф создается при серилизации объекта, при десерилизации данные считываются в соответствии с ним. 
Для сложного (композитного) объекта подразумевается, что не только он, но и все его поля поддерживают серилизацию, т.е. каждое поле само должно восстановить себя из потока (ну и записать себя в поток при серилизации, конечно). При этом ... 

Например класс Base содержит в себе поля int, Class1, string. Класс Derived (наследник от Base добавляет bool, Class2, float). Все классы (Base, Derived, Class1 и Class2) наследуют базовый класс 
Код

class ISerializable 
{
    virtual void Serialize(char* stream) = 0;
    virtual ISerializable Deserialize(char* stream) = 0;
};

При серилизации вызывается метод Serialize класса Derived, с такой псевдо-реализацией:
Код

public override void Serialize(char* stream)
{
base.Serialize(stream);

//запись в stream значения поля bool

//вызов (для наглядности так напишу)      
ISerializable isClass2 = this.Class2;
isClass2.Serialize(stream);

//запись в stream значения поля float
}

где
метод Serialize класса Base, с такой псевдо-реализацией:
Код

public override void Serialize(char* stream)
{
//запись в stream значения поля int

//опять же, для наглядности
ISerializable isClass1 = this.Class1;
isClass1.Serialize(stream);

//запись в stream значения поля string
}

при десерилизации вызывается конструктор класса Derived, потом метод Deserialize с такой псевдо-реализацией:
Код

public override ISerializable Deserialize(char* stream)
{
base.Deserialize(stream);//десерилизация унаследованных членов

//чтение из stream значения поля bool

this.Class2 =  new Class2();
this.Class2.Deserialize(stream);

//чтение из stream значения поля float

return this;
}

метод Deserialize класса Base:
Код

public override ISerializable Deserialize(char* stream)
{
//чтение из stream значения поля int

this.Class1 =  new Class1();
this.Class1.Deserialize(stream);

//чтение из stream значения поля string

return this;
}

как видно, за позицию курсора потока беспокоиться не нужно - если все классы серилизуют и десерилизуют себя в одном и том же порядке (граф серилизации, определенный в Derived.Serialize используется в Derived.Deserialize), то каждый поток будет его смещать на величину именно своих данных и все будет хоккей.. Закон простой - в каком порядке записывались данные, в таком и читать. Классы сами себя "рассуют" куда нужно (потому что разработчик классов сам определяет что, как и когда писать и читать. Один раз и навсегда, для каждого класса)

Здесь явно виден недостаток: постоянные вызовы конструкторов и ф-ций Deserialize... А вот если через конструктор базового класса (с параметром потоком) делать... то его бы не было smile
и никаких индексов... они просто не имеют смысда в последовательном и единственном потоке.
Ну вот и все. Не знаю, понятна ли мысль... Но это ясная и четкая структура, полиморфная и весьма юзабельная.
Спасибо за внимание smile

Это сообщение отредактировал(а) tol05 - 16.2.2008, 19:36


--------------------
На хорошей работе и сны хорошие снятся.
PM MAIL   Вверх
marcusmae
Дата 16.2.2008, 20:58 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


stravaganza
**


Профиль
Группа: Участник
Сообщений: 874
Регистрация: 26.3.2006

Репутация: 5
Всего: 39



tol05, спасибо за ответ!

Цитата(tol05 @  16.2.2008,  19:33 Найти цитируемый пост)
Здесь явно виден недостаток: постоянные вызовы конструкторов и ф-ций Deserialize... А вот если через конструктор базового класса (с параметром потоком) делать... то его бы не было 


Согласен, что присутствие спецконструктора полезно в любом случае. А фишка наличия виртуальной функции в том, что с помощью шаблона она возвращает точный тип, а не интерфейс :

(плюсифицирую код tol05  smile)
Код

public virtual ISerializable Deserialize(char* stream)
{
Base::Deserialize(stream);//десерилизация унаследованных членов

//чтение из stream значения поля bool

this->Class2 =  new Class2();
this->Class2->Deserialize(stream);

//чтение из stream значения поля float

return this;

}


Здесь внутри был нужный нам конкретный Class1, но при возврате он разобщился до ISerializable, который потом придётся обратно приводить к Class1, правильно?


--------------------
ἀπὸ μηχανῆς θεός
PM MAIL ICQ GTalk   Вверх
Alek86
Дата 18.2.2008, 11:42 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Завсегдатай
Сообщений: 1299
Регистрация: 30.1.2007
Где: Киев

Репутация: 21
Всего: 25



а терь для совсем тупых
есть у меня вектор Base'ов. В нем находятся потомки в последовательности:
Derived1, Derived2, Derived1, Derived1, Derived2
засериализировать вектор легко

КАК его снова получить из потока? как узнать, что для 0-го элемента требуется вызвать Deserialize у Derived1б для 1-го - у Derived2 и т.д.?


--------------------
user posted image    user posted image
PM MAIL   Вверх
Lazin
Дата 18.2.2008, 12:23 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Завсегдатай
Сообщений: 3820
Регистрация: 11.12.2006
Где: paranoid oil empi re

Репутация: 41
Всего: 154



Сохраняй числовые идентификаторы, по ним определяй что создавать и читать...
PM MAIL Skype GTalk   Вверх
Alek86
Дата 18.2.2008, 12:35 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Завсегдатай
Сообщений: 1299
Регистрация: 30.1.2007
Где: Киев

Репутация: 21
Всего: 25



Цитата(Lazin @  18.2.2008,  12:23 Найти цитируемый пост)
по ним определяй что создавать и читать...

и как сюда приткнуть красивейшую полиморфическую схему, предложенную ранее (имхо, очевидную, ибо вся трудность в этой связке)


--------------------
user posted image    user posted image
PM MAIL   Вверх
xvr
Дата 18.2.2008, 12:44 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Комодератор
Сообщений: 7046
Регистрация: 28.8.2007
Где: Дублин, Ирландия

Репутация: 60
Всего: 223



Цитата(georain @ 15.2.2008,  15:18)
Вот приходят из сети данные блоками, в начале блока есть число - идентификационный номер класса который он содержит. Нужно преобразовать блок в класс с помощью статической функции этого класса.
Пользоваться case не хочу, классов много, хочу чтобы работало быстро smile

Насколько быстро надо? Если умеренно быстро, то можно в качестве ID взять не просто число а пару - строку, с именем типа класса и хэш от этой строки. Если хэш будет достаточно хороший (в идеале без колизий вообще), то определение типа класса сведется просто к индексации массива с хэшами.

Реализация:
1) Для получения имен типов используется RTTI
2) Все типы классов должны наследоваться от базового, содержащего примитивы для сериализации
3) Для регистрации классов используется специальный темплэйтный класс - фабрика.
Код

class BaseSerializable {
public:
 virtual void serialize(ostream&) =0;
 virtual void deserialize(istream&) =0;
};

class BaseFabric {
protected:
 void registrate(const char* id, BaseFabric* creator);
public:
 virtual BaseSerializable* create() =0;
};

template <class Item>
class Fabric : public BaseFabric {
public:
 Fabric() {registrate(typeid(Item).name(),this);}
 virtual BaseSerializable* create() {return new Item;}
};
#define CREATE_FABRIC(Type) static Fabric<Type> instance_of_class_fabric_for_##Type;


При записи сначала пишется ID класса (хэш(typeid(Item).name()) и typeid(Item).name()), затем сам класс (методом serialize). Опционально можно проверить, что данный ID был зарегистрированн какой либо фабрикой классов (Fabric<xxx>)
При чтении читается ID, ищется среди зарегистрированных фабрик (в идеале просто индексируется массив), создается класс, заполняется из потока



Это сообщение отредактировал(а) xvr - 18.2.2008, 12:45
PM MAIL   Вверх
marcusmae
Дата 18.2.2008, 13:05 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


stravaganza
**


Профиль
Группа: Участник
Сообщений: 874
Регистрация: 26.3.2006

Репутация: 5
Всего: 39



Цитата(Alek86 @  18.2.2008,  11:42 Найти цитируемый пост)
есть у меня вектор Base'ов. В нем находятся потомки в последовательности:Derived1, Derived2, Derived1, Derived1, Derived2засериализировать вектор легко


Цитата(Alek86 @  18.2.2008,  11:42 Найти цитируемый пост)
а терь для совсем тупых

Хорошо, перефразирую. Тип Вы восстановите, я не сомневаюсь (Леонардо да Винчи плакаль бы от приведённых здесь неистовых механизмов осуществления этого). Мне непонятно, что Вы будете делать в случае неоднозначности соответствия сериализованного экземпляра и "места", в которое его нужно десериализовать. Так что, в этой постановке,
Цитата

КАК его снова получить из потока?

ответ : НИКАК. Ещё вопросы? smile

Это сообщение отредактировал(а) marcusmae - 18.2.2008, 13:10


--------------------
ἀπὸ μηχανῆς θεός
PM MAIL ICQ GTalk   Вверх
georain
Дата 18.2.2008, 13:12 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Бывалый
*


Профиль
Группа: Участник
Сообщений: 193
Регистрация: 28.11.2006
Где: Санкт-Петербург

Репутация: нет
Всего: нет



В общем ничего лучше чем в первом после я не услышал, замечания tol05 относятся к методам сериализации/десериализации, т.е. как упаковать/распаковать класс, это совсем другая проблема. На счёт создания в конструкторе: новый класс можно создавать с параметрами конструктора взятые из потока в той-же функции create(). 
Код

    static Base * create(CDR * cdr){
        return new ClassA(cdr->getMember());
    };  

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

void Serialize(char* stream)

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

tol05 я очень благодарен что хочешь помочь, но мысль совсем не понятна.
Цитата(tol05 @  16.2.2008,  19:33 Найти цитируемый пост)
А вот если через конструктор базового класса (с параметром потоком) делать... то его бы не было smile
и никаких индексов... они просто не имеют смысла в последовательном и единственном потоке.

Ну как тогда без индексов классифицировать классы и вообще отделять один класс от другого в потоке?

А изначальная проблема для этой задачи кстати звучит очень просто: есть дерево под сотню классов, набор экземпляров этих классов надо максимально эффективно отправлять по сети, причем последовательность типов экземпляров и их количество заранее не известно.

В общем вопрос открытый.

P.S. Alek86, кстати строчка
Код

   #define f(CN)   VFunc[n##CN] = CN::create;

проверят существует ли переменная nNameClass, если ее в enume нет, то ошибку выведет. Поэтому проверка enum'а нужна только на отравляющей стороне (а там проверка находится в функции "сериализации" при вставке константы из enum'а в поток).

Добавлено через 13 минут и 29 секунд
marcusmae, ну это же может быть вектор указателей на Base)

Это сообщение отредактировал(а) georain - 18.2.2008, 13:23
PM MAIL   Вверх
tol05
Дата 18.2.2008, 13:37 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Участник Клуба
Сообщений: 1632
Регистрация: 21.12.2006
Где: Харьков

Репутация: 1
Всего: 170



Да действительно, если мы имеем сложный класс MyClass, серилизуем в один и тот же поток 10 его экземпляров, а десерилизовать нужно будет (к примеру) только 2-й и 8-й....

В таком случае, в ф-ции Serealize класса MyClass перед началом записи в поток информации необходимо добавлять метки. Или индексы, как будет угодно smile

Здесь нужно написать базовую функциональность серилизации, а как ее применять - уже зависит от конкретного случая использования...

Вот как, например, сказать глупой машине, чтобы среди много-много байтов она нашла только те, которые относятся только к тому классу, у которого 23-й член равен 17?  smile 

Я описывал только паттерн (композитор ИМХО), реализованный в .Net, в частности.





--------------------
На хорошей работе и сны хорошие снятся.
PM MAIL   Вверх
Alek86
Дата 18.2.2008, 13:46 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Завсегдатай
Сообщений: 1299
Регистрация: 30.1.2007
Где: Киев

Репутация: 21
Всего: 25



Цитата(marcusmae @  18.2.2008,  13:05 Найти цитируемый пост)
ответ : НИКАК. Ещё вопросы? 

ты не веришь, что десериализация возможна? )


Цитата(georain @  18.2.2008,  13:12 Найти цитируемый пост)
 если ее в enume нет, то ошибку выведет.

да, похоже... перемудрил я )
тогда порекомендую вместо массива указателей на функции пользовать map и вызов с помощью cdr.getNumber() в try-блок обернуть...

Добавлено через 2 минуты и 46 секунд
Цитата(tol05 @  18.2.2008,  13:37 Найти цитируемый пост)
Да действительно, если мы имеем сложный класс MyClass, серилизуем в один и тот же поток 10 его экземпляров, а десерилизовать нужно будет (к примеру) только 2-й и 8-й....

помойму, tol05, ты не совсем понимаешь, в чем вопрос. Вопрос, как отличать разных наследников в списке. Если на это есть "паттерн" - с удовольствием выслушаю (говорю без стеба) smile


--------------------
user posted image    user posted image
PM MAIL   Вверх
georain
Дата 18.2.2008, 14:03 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Бывалый
*


Профиль
Группа: Участник
Сообщений: 193
Регистрация: 28.11.2006
Где: Санкт-Петербург

Репутация: нет
Всего: нет



Цитата(Alek86 @  18.2.2008,  13:46 Найти цитируемый пост)
тогда порекомендую вместо массива указателей на функции пользовать map и вызов с помощью cdr.getNumber() в try-блок обернуть...

Вот я вот этого не понимаю,  зачем map то нужен?

Это сообщение отредактировал(а) georain - 18.2.2008, 14:07
PM MAIL   Вверх
Alek86
Дата 18.2.2008, 14:13 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Завсегдатай
Сообщений: 1299
Регистрация: 30.1.2007
Где: Киев

Репутация: 21
Всего: 25



чтобы исключения кидать, когда айди несуществующий функция cdr.getNumber() отдаст

в твоем же случае боюсь подумать, что получится. отдастся какое-то число, оно посчитается указателем на функцию и запустится...

Добавлено через 1 минуту и 30 секунд
а заместо указателей на функции использовать boost::function чтоб и от других ошибок отгородиться


--------------------
user posted image    user posted image
PM MAIL   Вверх
tol05
Дата 18.2.2008, 14:19 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Участник Клуба
Сообщений: 1632
Регистрация: 21.12.2006
Где: Харьков

Репутация: 1
Всего: 170



Теперь понял. Конечно есть паттерн, как не быть.  smile 

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

Делаем класс - фабрику, куда передаем имя класса и поток. Фабрика создает объект нужного класса. Она ищет в потоке фрагменты данных, соответствующие требуемому классу. Если таких объектов - несколько, перегружаем методы фабрики и добавляем в запросы к фабрике параметры, конкретизирующие поиск.

P.S. все, больше мешать не буду. ИМХО решите проблему концептуально сначала. smile

P.S.S указателей не передавайте. Где гарантия уникальности значений? При десерилизации из нескольких потоков? На различных клиентах?

Это сообщение отредактировал(а) tol05 - 18.2.2008, 14:27


--------------------
На хорошей работе и сны хорошие снятся.
PM MAIL   Вверх
Alek86
Дата 18.2.2008, 14:42 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Завсегдатай
Сообщений: 1299
Регистрация: 30.1.2007
Где: Киев

Репутация: 21
Всего: 25



Цитата(tol05 @  18.2.2008,  14:19 Найти цитируемый пост)
Делаем класс - фабрику, куда передаем имя класса и поток. Фабрика создает объект нужного класса. Она ищет в потоке фрагменты данных, соответствующие требуемому классу. Если таких объектов - несколько, перегружаем методы фабрики и добавляем в запросы к фабрике параметры, конкретизирующие поиск.

немного неясная формулировка. короче говоря, нужно делать ту же карту, только между строкой и соответствующей функцией фабрики? тогда это практически то же, что и в первом посте.


--------------------
user posted image    user posted image
PM MAIL   Вверх
Lazin
Дата 18.2.2008, 14:48 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Завсегдатай
Сообщений: 3820
Регистрация: 11.12.2006
Где: paranoid oil empi re

Репутация: 41
Всего: 154



Ну вы тут напридумывали сами себе сложностей  smile 
вот приблизительный код, никаких имен классов, boost::function, и прочего. Похожий код использовал в одном проекте, этот в качестве иллюстрации на коленке написал за пару минут))

Код

//базовый класс

class Base
{
    virtual void save_(std::ostream&) = 0;
    virtual void load_() = 0;
public:
    void save(std::ostream& s)
    {
        save(s);
    }
    
    void load(std::istream& s)
    {
        load_(s);
    }
    
    /***************/
};

typedef Base* (base_factory_fn*)(std::istream&);

//global
std::map<int, base_factory_fn> BaseFactory;//это не обязательно map, может быть к примеру простой массив

//наследник
class Derived1 : public Base
{
    int field;
    virtual void save_(std::ostream& s)
    {
        s << type_trait;
        s << field;
    }
    virtual void load_(std::istream& s)
    {
        s >> field;
    }
public:
    enum {type_trait = 1};
    static Base* Make (std::istream& s)
    {
        Base* p = new Derived1(/*детали конструирования Derived1*/);
        p->load(s);//можно написать к примеру конструктор который будет принимать ссылку на поток и читать все оттуда, но это имхо дурной стиль
        return p;
    }
    
    /***************/
};

namespace{//регистрация класса Derived1
    class Reg1
    {
    public:
        Reg1()
        {
            BaseFactory[Derived1::type_trait] = &Derived1::Make;
        }
    };
    Reg1 register_derived_1;
};

class Derived2 : public Base
{
    std::string field;
    virtual void save_(std::ostream& s)
    {
        s << type_trait;
        s << field;
    }
    virtual void load_(std::istream& s)
    {
        s >> field;
    }
public:
    enum {type_trait = 2};
    static Base* Make (std::istream& s)
    {
        Base* p = new Derived2(/*детали конструирования Derived2*/);
        p->load(s);
        return p;
    }
    /***************/
};

namespace{//регистрация класса Derived2
    class Reg2
    {
    public:
        Reg2()
        {
            BaseFactory[Derived2::type_trait] = &Derived2::Make;
        }
    };
    Reg2 register_derived_2;
};



std::vector<Base*> base_array;
.....
out_str - ostream - поток в который сохраняются объекты
....
сохранение:
for (std::vector<Base*>::iterator i = base_array.begin(); i != base_array.end(); ++i)
{
    i->save(out_str);
}
........
inp_str - istream - поток для чтения
.......
восстановление 1-го объекта:

int trait = 0;
inp_str >> trait;
Base* p = BaseFactory[trait](inp_str);


PM MAIL Skype GTalk   Вверх
tol05
Дата 18.2.2008, 15:00 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Участник Клуба
Сообщений: 1632
Регистрация: 21.12.2006
Где: Харьков

Репутация: 1
Всего: 170



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

Ты получаешь пакеты данных. Это во-первых. Вначале пакета идет его идентификатор. Замечательно. 
Цитата(georain @  15.2.2008,  14:18 Найти цитируемый пост)
Вот приходят из сети данные блоками, в начале блока есть число - идентификационный номер класса который он содержит. Нужно преобразовать блок в класс с помощью статической функции этого класса.

Получил ты число 12667845 например... Нужно создать какой-то класс. Какой? "с помощью статической функции какого класса"?

передаешь принятый пакет (поток) в фабрику. Она считывает из потока блок данных до определенной метки. Это имя класса. Дальше нужно создать экземпляр. Рефлексия в плюсах есть? Позднее связывание? Класс Activator, я имею в виду? marcusmae, здесь точно ответит. я - нет...

Но даже если и нет... Простой оператор switch по текстовому значению вызывает ф-цию ISerializable.Deserialize для каждого конкретного класса, а-ля
Код

ISerializable result = null;
switch(ClassName)
         {
                case "A": result = ((ISerializable)(new A())).Deserialize()); break;
                case "B": result = ((ISerializable)(new B())).Deserialize()); break;         
         }
return result;

и т.д.

сделайте абстракный класс, как говорилось выше. Если хотите полиморфности.


--------------------
На хорошей работе и сны хорошие снятся.
PM MAIL   Вверх
Alek86
Дата 18.2.2008, 15:13 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Завсегдатай
Сообщений: 1299
Регистрация: 30.1.2007
Где: Киев

Репутация: 21
Всего: 25



и tol05 и Lazin, фактически, предлагают одно и то же smile все равно решение аввтора покрасивше
в С++ нету свича для строк + мап полюбому получше от свича.
а у Lazin оно не так автоматизировано, как в пером посте

+ боюсь представить, что ЭТО такое. какой у этого класса размер?
Код

class Reg2
    {
    public:
        Reg2()
        {
            BaseFactory[Derived2::type_trait] = &Derived2::Make;
        }
    };
    Reg2 register_derived_2;
};



--------------------
user posted image    user posted image
PM MAIL   Вверх
Lazin
Дата 18.2.2008, 15:24 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Завсегдатай
Сообщений: 3820
Регистрация: 11.12.2006
Где: paranoid oil empi re

Репутация: 41
Всего: 154



Цитата(Alek86 @  18.2.2008,  15:13 Найти цитируемый пост)
+ боюсь представить, что ЭТО такое. какой у этого класса размер?

этот класс нужен только для того что-бы сработал его конструктор во время запуска программы

Добавлено через 3 минуты и 7 секунд
Цитата(georain @  15.2.2008,  15:18 Найти цитируемый пост)
Потому что во-первых велосипед наверняка, а во вторых надо не забыть каждый класс в таблицу записать и в enum ещё.

эту проблему я и решаю этим классом, можно не в enum записывать, а куда угодно, в простой массив, в объект класса автора  smile 
PM MAIL Skype GTalk   Вверх
Alek86
Дата 18.2.2008, 15:41 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Завсегдатай
Сообщений: 1299
Регистрация: 30.1.2007
Где: Киев

Репутация: 21
Всего: 25



Цитата(Lazin @  18.2.2008,  15:24 Найти цитируемый пост)
этот класс нужен только для того что-бы сработал его конструктор во время запуска программы

мой глюк - показалось, что он объект своего типа содержит )


--------------------
user posted image    user posted image
PM MAIL   Вверх
marcusmae
Дата 18.2.2008, 22:19 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


stravaganza
**


Профиль
Группа: Участник
Сообщений: 874
Регистрация: 26.3.2006

Репутация: 5
Всего: 39



Цитата(tol05 @  18.2.2008,  15:00 Найти цитируемый пост)
Рефлексия в плюсах есть?

Нет
Цитата(tol05 @  18.2.2008,  15:00 Найти цитируемый пост)
Позднее связывание?

Нет
Цитата(tol05 @  18.2.2008,  15:00 Найти цитируемый пост)
Класс Activator, я имею в виду?

Нет

Если позволите, немного резюмирую всё вышесказанное.

Цитата(Alek86 @  18.2.2008,  15:13 Найти цитируемый пост)
и tol05 и Lazin, фактически, предлагают одно и то же  все равно решение аввтора покрасивше

Код

//CDR - потоковый класс
CDR cdr(input);

//каждый класс содержит функцию create()
class ClassA: public Base{
    public:

    virtual ~ClassA(){};

    static Base * create(CDR * cdr){
        ClassA * bp = new ClassA;
        // тут инициализируем данными, например
        // member = cdr->getMember();
        return bp;
    };    
};

мне не нравится, потому что :
а) статическая функция десериализации
б) создаёт не ClassA *, а Base *. А массив указателей на функции - это всегда кажется, что "финт ушами", особенно если в первый раз делать...

Статичность функции ::create я отношу к недостатком, поскольку она не позволяет "каскадно" или по частям десериализовать объект, опираясь на десериализаторы базовых типов. Топикстартеру придётся в static Base * create(CDR * cdr){ ... } разводить помойку, которая при заявленных аппетитах в сотню классов примет формы опасные для здоровья smile Большинство участников придерживается другой схемы, виртуальной, а именно виртуальный сериализатор + виртуальный десериализатор или виртуальный сериализатор + конструктор-десериализатор.

И тот, и другой способ вполне имеют право на существование. Статический - скорее в случае наличия готовой большой иерархии, к которой сериализацию нужно лишь "временно" прикрутить, чтобы что-то там попробовать. Виртуальный - скорее в случае разработки кОмплексного решения, так, как это обычно делается на современных платформах. В любом случае, странно, что 
Цитата(georain @  18.2.2008,  13:12 Найти цитируемый пост)
ничего лучше чем в первом после я не услышал
, и вообще вдруг выяснилось, что это всё не нужно, и 
Цитата(georain @  18.2.2008,  13:12 Найти цитируемый пост)
как упаковать/распаковать класс, это совсем другая проблема
, а просто 
Цитата(georain @  18.2.2008,  13:12 Найти цитируемый пост)
набор экземпляров этих классов надо максимально эффективно отправлять по сети, причем последовательность типов экземпляров и их количество заранее не известно.
 Я не понимаю, почему georain так уверен в том, что эти две проблемы не связаны. По-моему, задача как раз в том, как упаковать-переправить-распаковать, так чтобы суммарные трудозатраты на это оказались минимальны. Или сокеты нужно применить что ли?!

Цитата(Alek86 @  18.2.2008,  13:46 Найти цитируемый пост)
ты не веришь, что десериализация возможна? )
 Могу привести пример, когда она, как мне кажется, невозможна. Сериализую массив структур "поштучно" и бросаю в перегруженную сеть, которая поперепутала пакеты (или есть сомнения в том, что сеть может менять очерёдность пакетов..?). Предлагаю их десериализовать и расставить ровно в той последовательности, что были изначально.

Это сообщение отредактировал(а) marcusmae - 18.2.2008, 22:26


--------------------
ἀπὸ μηχανῆς θεός
PM MAIL ICQ GTalk   Вверх
georain
Дата 19.2.2008, 01:42 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Бывалый
*


Профиль
Группа: Участник
Сообщений: 193
Регистрация: 28.11.2006
Где: Санкт-Петербург

Репутация: нет
Всего: нет



Lazin, скажи пожалуйста, зачем делать в базовом классе невиртуальные методы-обёртки для виртуальных методов? Чем это лучше, почему не следует сразу виртуальные методы  save_() load_() вызывать?

marcusmae, полность с тобой согласен, единственное только хочу сказать, что естественно внутри static create ,будет вызываться функция - член десериализатор с "каскадной" функциональностью либо поток будет передаваться конструктору, и вот тут
Цитата(marcusmae @  18.2.2008,  22:19 Найти цитируемый пост)
 Могу привести пример, когда она, как мне кажется, невозможна. Сериализую массив структур "поштучно" и бросаю в перегруженную сеть, которая поперепутала пакеты (или есть сомнения в том, что сеть может менять очерёдность пакетов..?). Предлагаю их десериализовать и расставить ровно в той последовательности, что были изначально.

пример очень не удачный, потому как есть протокол TCP гарантирующий доставку в нужном порядке.

PM MAIL   Вверх
georain
Дата 19.2.2008, 02:13 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Бывалый
*


Профиль
Группа: Участник
Сообщений: 193
Регистрация: 28.11.2006
Где: Санкт-Петербург

Репутация: нет
Всего: нет



Цитата(marcusmae @  18.2.2008,  22:19 Найти цитируемый пост)
 Я не понимаю, почему georain так уверен в том, что эти две проблемы не связаны. По-моему, задача как раз в том, как упаковать-переправить-распаковать, так чтобы суммарные трудозатраты на это оказались минимальны. Или сокеты нужно применить что ли?!

ИМХО в каждом классе есть метод для превращения класса в кусок потока, и наоборот и есть функция которая по этому куску потока вызывает этот метод для необходимого класса - её функция определить нужный класс. По идее должна быть симметричная  ей функция которая пишет информацию в поток в зависимости от того какой класс записывается, но только потому что класс сам знает свой тип удобнее не делать отдельную функцию, а делать чтобы класс перед тем как "линеаризироваться" записывал информацию о себе сам.
Это две разные задачи и никак не связаны. За вторую задачу может быть ответственен базовый класс, например ISerializable у tol05, первая же задача "каскадно" размазана по методам производных классов. У меня вторая задача тоже размазана по производным классам в статик методах плюс один внешний класс с таблицей которая может быть и внутри базового класса как статический член. Хотя это всё в общем-то довольно удобно, мне кажется что это не очень хорошо. Именно про это и был задан мой вопрос, как лучше (и эффективнее) решить вторую задачу (именно вторую) - нахождение класса из потока, а решение первой задачи довольно тривиальное и тут мало что можно придумать.
Ну вы меня сильно не бейте если чё smile 
PM MAIL   Вверх
marcusmae
Дата 19.2.2008, 02:48 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


stravaganza
**


Профиль
Группа: Участник
Сообщений: 874
Регистрация: 26.3.2006

Репутация: 5
Всего: 39



Цитата(georain @  19.2.2008,  02:13 Найти цитируемый пост)
Именно про это и был задан мой вопрос, как лучше (и эффективнее) решить вторую задачу (именно вторую) - нахождение класса из потока, а решение первой задачи довольно тривиальное и тут мало что можно придумать.

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

Цитата(georain @  19.2.2008,  01:42 Найти цитируемый пост)
протокол TCP гарантирующий доставку в нужном порядке.

Ахтычорт, TCP, и правда smile Кстати, есть какой-то специфический протокол, построенный для этой задачи над TCP? = Если да, то что он делает?



--------------------
ἀπὸ μηχανῆς θεός
PM MAIL ICQ GTalk   Вверх
xvr
Дата 19.2.2008, 08:00 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Комодератор
Сообщений: 7046
Регистрация: 28.8.2007
Где: Дублин, Ирландия

Репутация: 60
Всего: 223



Цитата(georain @ 19.2.2008,  02:13)
Именно про это и был задан мой вопрос, как лучше (и эффективнее) решить вторую задачу (именно вторую) - нахождение класса из потока, а решение первой задачи довольно тривиальное и тут мало что можно придумать.
Ну вы меня сильно не бейте если чё smile

Чем тебя мое решение не устроило (на 2й странице 3е сверху)?


PM MAIL   Вверх
Lazin
Дата 19.2.2008, 09:16 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Завсегдатай
Сообщений: 3820
Регистрация: 11.12.2006
Где: paranoid oil empi re

Репутация: 41
Всего: 154



Цитата(georain @  19.2.2008,  01:42 Найти цитируемый пост)
скажи пожалуйста, зачем делать в базовом классе невиртуальные методы-обёртки для виртуальных методов? Чем это лучше, почему не следует сразу виртуальные методы  save_() load_() вызывать?

в моем примере оверхеда на вызов дополнительной ф-ии не будет, компилятор ее просто выкинет. 
А преимуществ масса, почитай про паттерн NVI ^_^
PM MAIL Skype GTalk   Вверх
tol05
Дата 19.2.2008, 12:17 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Участник Клуба
Сообщений: 1632
Регистрация: 21.12.2006
Где: Харьков

Репутация: 1
Всего: 170



Цитата(georain @  19.2.2008,  01:13 Найти цитируемый пост)
За вторую задачу может быть ответственен базовый класс, например ISerializable у tol05, первая же задача "каскадно" размазана по методам производных классов

не ну я так не играю!..  smile  smile  smile 

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

А по поводу статических ф-ций - это ИМХО плохо. Какое преимущество их использования? Из статической ф-ции производного класса нельзя вызвать статическую ф-цию базового класса, как же тогда инициализировать закрытые члены базового класса? В производном классе к ним доступа нет, а в потоке данные о них есть smile Передавать указатель на текущий объект производного класса в метод его же базового класса? ... Извините ...
Код

class A
{
    public static void F()
    {
    }
}

class B: A
{
    public static void F()//уже здесь у меня warning-и выскакивают. Мы скрываем функциональность базового класса
    {
        //base.F(); нельзя
        A.F();//this в него передавать?! Гм.. прикольно...
    }
}

В статические ф-ции нужно передавать дополнительные параметры, идентифицирующие конкретный объект. Зачем?
Раздувается таблица методов класса. Дублируется значительная часть функциональности....

И как статические ф-ции обеспечивают потокозащищенность в плюсах? Я не помню просто smile

Это сообщение отредактировал(а) tol05 - 19.2.2008, 12:21


--------------------
На хорошей работе и сны хорошие снятся.
PM MAIL   Вверх
marcusmae
Дата 19.2.2008, 13:16 (ссылка) |    (голосов:1) Загрузка ... Загрузка ... Быстрая цитата Цитата


stravaganza
**


Профиль
Группа: Участник
Сообщений: 874
Регистрация: 26.3.2006

Репутация: 5
Всего: 39



georain
tol05

Я тут ночью думал-думал и вот что надумал. Что вообще хорошего в статических функциях (при том, что они ужасно плохие  smile)? = А то, что у них адреса фиксированные, и их адын штука на всю генерацию экземпляров класса. А раз так, то пользоваться адресом таких функций просто и удобно (чем georain и воспользовался). Если так уж прям нравится, то предлагаю пойти дальше. Вот эти ваши таблицы функций ... ничего не напоминают? = Дело в том, что таблица функций есть всегда. Ею является образ exe. То есть моя идея состоит в следующем. В начале времён серверу передаётся просто копия экзешника (или библиотеки), который работает на клиенте. Далее, когда понадобится десериализация, предлагается класть в пакет его размер, а за ним - декорированное имя функции, которая "сертифицирована" проводить десериализацию этого пакета :

Код

    *(unsigned int*)stream = size;
    memcpy_s(stream + SIZE_LENGTH, LP_DESERIALIZATION_PROC_LENGTH,
        deserializeMangledName, LP_DESERIALIZATION_PROC_LENGTH);


Сервер получил этот stream, считал размер пакета и имя функции. Имея последнее, сервер лезет в эталонную таблицу и загружает оттуда точку входа функции-десериализатора :

Код

    // Extract a deserialization function pointer from the stream.
    HMODULE hModule = LoadLibrary(L"Classes.dll");
    if (!hModule) return -1;
    LPDESERIALIZE lpDeserialize = (LPDESERIALIZE)GetProcAddress(hModule,
        stream + SIZE_LENGTH);
    if (!lpDeserialize) return -1;
    LPVOID a = lpDeserialize(stream);


Готово. Копия объекта загружена в LPVOID a. 

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

Архив с полным решением присоединён.

Присоединённый файл ( Кол-во скачиваний: 7 )
Присоединённый файл  Koder.rar 43,99 Kb


--------------------
ἀπὸ μηχανῆς θεός
PM MAIL ICQ GTalk   Вверх
Lazin
Дата 19.2.2008, 14:44 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Завсегдатай
Сообщений: 3820
Регистрация: 11.12.2006
Где: paranoid oil empi re

Репутация: 41
Всего: 154



Цитата(tol05 @  19.2.2008,  12:17 Найти цитируемый пост)
Из статической ф-ции производного класса нельзя вызвать статическую ф-цию базового класса

это на каком языке? на плюсах можно, если позволяет видимость...


Цитата(marcusmae @  19.2.2008,  13:16 Найти цитируемый пост)
Далее, когда понадобится десериализация, предлагается класть в пакет его размер, а за ним - декорированное имя функции, которая "сертифицирована" проводить десериализацию этого пакета

а если завтра тебе понадобится добавить десяток новых классов, как быть?
PM MAIL Skype GTalk   Вверх
marcusmae
Дата 19.2.2008, 14:49 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


stravaganza
**


Профиль
Группа: Участник
Сообщений: 874
Регистрация: 26.3.2006

Репутация: 5
Всего: 39



Цитата(Lazin @  19.2.2008,  14:44 Найти цитируемый пост)
а если завтра тебе понадобится добавить десяток новых классов, как быть?


ну, понадобится иметь для них десяток функций сериализации - по своей собственной для каждого класса. Ну и декорированные имена через dumpbin посмотреть и забить в константы. Что-то мне подсказывает, что если всё аккуратно сделать, то можно обойтись без имён - одними указателями. По идее, если dll, то у неё фиксированная виртуальная адресация, и адрес функции у клиента и сервера должен быть одинаковый.

Цитата(Lazin @  19.2.2008,  14:44 Найти цитируемый пост)
это на каком языке? на плюсах можно, если позволяет видимость...

Можно-можно. Просто, для статических функций само понятие наследования отсутствует. Но их всегда можно вызвать явно.

А вот
Цитата(tol05 @  19.2.2008,  12:17 Найти цитируемый пост)
как же тогда инициализировать закрытые члены базового класса?

- хороший довод. По-моему, никак.


Это сообщение отредактировал(а) marcusmae - 19.2.2008, 14:55


--------------------
ἀπὸ μηχανῆς θεός
PM MAIL ICQ GTalk   Вверх
tol05
Дата 19.2.2008, 15:44 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Участник Клуба
Сообщений: 1632
Регистрация: 21.12.2006
Где: Харьков

Репутация: 1
Всего: 170



Цитата(marcusmae @  19.2.2008,  13:49 Найти цитируемый пост)
Можно-можно. Просто, для статических функций само понятие наследования отсутствует. Но их всегда можно вызвать явно.

Это называется "нельзя"  smile  smile 

Какое же это наследование, если нужно this в параметры передать??? Я так могу любую ф-цию вызвать и передать указатель на мой класс. Так что, эти классы предками/потомками станут? Я имел в виду использование Base... когда и базовый, и производный класс оперируют с одним и тем же указателем на объект.

Цитата(marcusmae @  19.2.2008,  13:49 Найти цитируемый пост)
хороший довод

мда, по-моему тоже...  smile 

ну да ладно... этот пост можно считать оффтопом.  smile 



--------------------
На хорошей работе и сны хорошие снятся.
PM MAIL   Вверх
georain
Дата 20.2.2008, 01:38 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Бывалый
*


Профиль
Группа: Участник
Сообщений: 193
Регистрация: 28.11.2006
Где: Санкт-Петербург

Репутация: нет
Всего: нет



xvr
Цитата(xvr @  19.2.2008,  08:00 Найти цитируемый пост)
Чем тебя мое решение не устроило (на 2й странице 3е сверху)?

Потому что решение более сложное, а строка
Код

#define CREATE_FABRIC(Type) static Fabric<Type> instance_of_class_fabric_for_##Type;

обязывающая для каждого класса писать
Код

CREATE_FABRIC(ClassName)

остаётся, в чём выгода?


tol05
Цитата(tol05 @  19.2.2008,  12:17 Найти цитируемый пост)
по поводу статических ф-ций - это ИМХО плохо. Какое преимущество их использования? Из статической ф-ции производного класса нельзя вызвать статическую ф-цию базового класса, как же тогда инициализировать закрытые члены базового класса? В производном классе к ним доступа нет, а в потоке данные о них есть smile Передавать указатель на текущий объект производного класса в метод его же базового класса? ... Извините ...

потому что
Цитата(georain @  19.2.2008,  01:42 Найти цитируемый пост)
внутри static create ,будет вызываться функция - член десериализатор с "каскадной" функциональностью либо поток будет передаваться конструктору

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


marcusmae
Цитата(marcusmae @  19.2.2008,  13:16 Найти цитируемый пост)

Я тут ночью думал-думал и вот что надумал.

развеселил, хохот в час ночи. Возможно это даже будет работать, но мне кажется это не очень кроссплатформенно, этож на win-lin-mac-bsd-и.т.д. должно работать, а разный порядок байтов на разных машинах, сомнительное предложение, но прикольно!  smile 
PM MAIL   Вверх
xvr
Дата 20.2.2008, 08:48 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Комодератор
Сообщений: 7046
Регистрация: 28.8.2007
Где: Дублин, Ирландия

Репутация: 60
Всего: 223



Цитата(georain @ 20.2.2008,  01:38)
xvr
Цитата(xvr @  19.2.2008,  08:00 Найти цитируемый пост)
Чем тебя мое решение не устроило (на 2й странице 3е сверху)?

Потому что решение более сложное, а строка
Код

#define CREATE_FABRIC(Type) static Fabric<Type> instance_of_class_fabric_for_##Type;

обязывающая для каждого класса писать
Код

CREATE_FABRIC(ClassName)

остаётся, в чём выгода?

В том, что:
1) Не надо добавлять никаких enum'ов, ID и пр.
2) Количество типов классов может быть любым (не ограничено размером числа, задающего тип)
3) Полная проверка на этапе сериализации, что CREATE_FABRIC не забыли указать (увы, может не сработать при раздельных sender'е и reviever'е)
4) Не нужен никакой switch и никакая другая централизованная обработка - потенциальное место где можно забыть что-то добавить.
5) CREATE_FABRIC - это ЕДИНСТВЕННОЕ что добавляется при сериализации нового класса

П1-4 следствия П5.

PM MAIL   Вверх
georain
Дата 20.2.2008, 09:11 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Бывалый
*


Профиль
Группа: Участник
Сообщений: 193
Регистрация: 28.11.2006
Где: Санкт-Петербург

Репутация: нет
Всего: нет



Цитата(xvr @  20.2.2008,  08:48 Найти цитируемый пост)
2) Количество типов классов может быть любым (не ограничено размером числа, задающего тип)

Ну если на то пошло, то хеш имени может оказаться одинаковым для двух каких-нибудь классов.  smile  Или ты предлагаешь вместе с каждым классом полностью его имя слать?

Цитата(xvr @  20.2.2008,  08:48 Найти цитируемый пост)
3) Полная проверка на этапе сериализации, что CREATE_FABRIC не забыли указать (увы, может не сработать при раздельных sender'е и reviever'е)

Проверять надо на этапе компиляции, а на этапе сериализации это накладно. К тому же классы отправителя и принимателя будут иметь разную реализацию, т.е. либо там либо там можно забыть написать CREATE_FABRIC.

Цитата(xvr @  20.2.2008,  08:48 Найти цитируемый пост)
4) Не нужен никакой switch и никакая другая централизованная обработка - потенциальное место где можно забыть что-то добавить.

Ну у меня вообще свича нет  smile , проверяй тока что в диапазон массива попадает и всё, а вот проверка и поиск хеша в map уже накладнее.

Цитата(xvr @  20.2.2008,  08:48 Найти цитируемый пост)
1) Не надо добавлять никаких enum'ов, ID и пр.

Цитата(xvr @  20.2.2008,  08:48 Найти цитируемый пост)
5) CREATE_FABRIC - это ЕДИНСТВЕННОЕ что добавляется при сериализации нового класса

Это факт, спору нет.

Тут есть и плюсы и минусы, в равной степени, но реализация толще и главная причина, будет тяжело обойти проблему, если имена классов на разных сторонах будут различаться.
PM MAIL   Вверх
marcusmae
Дата 20.2.2008, 11:15 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


stravaganza
**


Профиль
Группа: Участник
Сообщений: 874
Регистрация: 26.3.2006

Репутация: 5
Всего: 39



Цитата(georain @  20.2.2008,  01:38 Найти цитируемый пост)
развеселил, хохот в час ночи. Возможно это даже будет работать, но мне кажется это не очень кроссплатформенно, этож на win-lin-mac-bsd-и.т.д. должно работать, а разный порядок байтов на разных машинах, сомнительное предложение, но прикольно!


georain, ну что за чушь? smile Ну работает твой клиент на винде, а сервер - на маке, и что? = Ну и скомпилируй клиентскую программу не только для винды, но и для мака (раз она у Вас такая кроссплатформенная) и передай на сервер маковый вариант. Не вижу никакой проблемы, всё будет работать. Единственное, нужно будет подобрать аналог для LoadLibrary и GetProcAddress, но это штучная доделка. Кажется Вы просто чем-то себе сильно забили голову... smile

Это сообщение отредактировал(а) marcusmae - 20.2.2008, 11:22


--------------------
ἀπὸ μηχανῆς θεός
PM MAIL ICQ GTalk   Вверх
xvr
Дата 20.2.2008, 11:48 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Комодератор
Сообщений: 7046
Регистрация: 28.8.2007
Где: Дублин, Ирландия

Репутация: 60
Всего: 223



Цитата(georain @ 20.2.2008,  09:11)
Цитата(xvr @  20.2.2008,  08:48 Найти цитируемый пост)
2) Количество типов классов может быть любым (не ограничено размером числа, задающего тип)

Ну если на то пошло, то хеш имени может оказаться одинаковым для двух каких-нибудь классов.  smile  Или ты предлагаешь вместе с каждым классом полностью его имя слать?

Да. При приеме проверять, если для данного значения хэша есть более одного имени - проверять имя, если только одно - игнорировать. Хэш можно подобрать (в том числе и run-time), что бы минимизировать количество коллизий. Например, если в качестве хэша использовать CRC16, то можно выбрать один из нескольких порождающих полиномов.

Цитата

Цитата(xvr @  20.2.2008,  08:48 Найти цитируемый пост)
3) Полная проверка на этапе сериализации, что CREATE_FABRIC не забыли указать (увы, может не сработать при раздельных sender'е и reviever'е)

Проверять надо на этапе компиляции, а на этапе сериализации это накладно. 
Да, это имеет смысл  smile Можно поиграться с содержимым макроса CREATE_FABRIC, тут были подходящие идеи.

Цитата

К тому же классы отправителя и принимателя будут иметь разную реализацию, т.е. либо там либо там можно забыть написать CREATE_FABRIC.
Да, это проблема.

Цитата

Цитата(xvr @  20.2.2008,  08:48 Найти цитируемый пост)
4) Не нужен никакой switch и никакая другая централизованная обработка - потенциальное место где можно забыть что-то добавить.

Ну у меня вообще свича нет  smile , проверяй тока что в диапазон массива попадает и всё, а вот проверка и поиск хеша в map уже накладнее.
Хэш надо искать не в map'е а делать массив в 64К входов (для 16ти битного хэша) и прямо индексировать

Цитата

Тут есть и плюсы и минусы, в равной степени, но реализация толще и главная причина, будет тяжело обойти проблему, если имена классов на разных сторонах будут различаться.
Реализация самого framework'а действительно толще, но применять ее удобнее. Что касается разных имен классов, то они НЕ ДОЛЖНЫ быть разными, и это можно проверить (run-time), например отсортировать массив всех зарегистрированных в сериалайзере типов, затем скормить их посимвольно в CRC32, отправить с пакетом и сравнить на приемном конце. Кстати, так же можно договорится о использованном в передатчике хэше.

PM MAIL   Вверх
Dimonius
Дата 21.2.2008, 18:31 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


Профиль
Группа: Участник
Сообщений: 51
Регистрация: 11.10.2006

Репутация: нет
Всего: 1



Если все классы, которые приходят по сети наследуются от одного родительского, то здесь следует применить шаблон проектирования Factory Method, который позволит внести в программу желаемую расширяемость и управляемость. Если нужен пример кода, то на досуге напишу.
PM MAIL   Вверх
Ответ в темуСоздание новой темы Создание опроса
Правила форума "С++:Общие вопросы"
Earnest Daevaorn

Добро пожаловать!

  • Черновик стандарта C++ (за октябрь 2005) можно скачать с этого сайта. Прямая ссылка на файл черновика(4.4мб).
  • Черновик стандарта C (за сентябрь 2005) можно скачать с этого сайта. Прямая ссылка на файл черновика (3.4мб).
  • Прежде чем задать вопрос, прочтите это и/или это!
  • Здесь хранится весь мировой запас ссылок на документы, связанные с C++ :)
  • Не брезгуйте пользоваться тегами [code=cpp][/code].
  • Пожалуйста, не просите написать за вас программы в этом разделе - для этого существует "Центр Помощи".
  • C++ FAQ

Если Вам понравилась атмосфера форума, заходите к нам чаще! С уважением, Earnest Daevaorn

 
1 Пользователей читают эту тему (1 Гостей и 0 Скрытых Пользователей)
0 Пользователей:
« Предыдущая тема | C/C++: Общие вопросы | Следующая тема »


 




[ Время генерации скрипта: 0.2034 ]   [ Использовано запросов: 21 ]   [ GZIP включён ]


Реклама на сайте     Информационное спонсорство

 
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности     Powered by Invision Power Board(R) 1.3 © 2003  IPS, Inc.