Как реализовать сериализацию в C++

Вы спрашиваете: "Почему методы hasNext (), затем () и удаляют () не непосредственно кодированный к самой реализации структуры данных?".

платформа Наборов Java принимает решение определить интерфейс Iterator, как воплощено к самому набору. Обычно, так как каждый набор Java реализует эти Iterable интерфейс, программа Java будет звонить iterator для создания ее собственного итератора так, чтобы это могло использоваться в цикле. Как другие указали, Java 5 позволяет нам прямому использованию итератора с циклом foreach.

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

В классической книге GoF, контраст между внутренними и внешними итераторами разъяснен вполне ясно.

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

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

Для примера того, как внутренние итераторы работают, посмотрите Ruby Enumerable API, который имеет внутренние итеративные методы такой как each. В Ruby идея состоит в том, чтобы передать блок кода (т.е. закрытие) к внутреннему итератору так, чтобы набор мог заботиться о своем собственном повторении.

28
задан iain 27 November 2009 в 18:03
поделиться

5 ответов

Использование чего-то вроде Boost Serialization ни в коем случае не является стандартом, но (по большей части) очень хорошо написанная библиотека, которая делает всю работу за вас.

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

EDIT
Базовая реализация C ++ фабрики объектов, упомянутая в предыдущем абзаце.

/**
* A class for creating objects, with the type of object created based on a key
* 
* @param K the key
* @param T the super class that all created classes derive from
*/
template<typename K, typename T>
class Factory { 
private: 
    typedef T *(*CreateObjectFunc)();

    /**
    * A map keys (K) to functions (CreateObjectFunc)
    * When creating a new type, we simply call the function with the required key
    */
    std::map<K, CreateObjectFunc> mObjectCreator;

    /**
    * Pointers to this function are inserted into the map and called when creating objects
    *
    * @param S the type of class to create
    * @return a object with the type of S
    */
    template<typename S> 
    static T* createObject(){ 
        return new S(); 
    }
public:

    /**
    * Registers a class to that it can be created via createObject()
    *
    * @param S the class to register, this must ve a subclass of T
    * @param id the id to associate with the class. This ID must be unique
    */ 
    template<typename S> 
    void registerClass(K id){ 
        if (mObjectCreator.find(id) != mObjectCreator.end()){ 
            //your error handling here
        }
        mObjectCreator.insert( std::make_pair<K,CreateObjectFunc>(id, &createObject<S> ) ); 
    }

    /**
    * Returns true if a given key exists
    *
    * @param id the id to check exists
    * @return true if the id exists
    */
    bool hasClass(K id){
        return mObjectCreator.find(id) != mObjectCreator.end();
    } 

    /**
    * Creates an object based on an id. It will return null if the key doesn't exist
    *
    * @param id the id of the object to create
    * @return the new object or null if the object id doesn't exist
    */
    T* createObject(K id){
        //Don't use hasClass here as doing so would involve two lookups
        typename std::map<K, CreateObjectFunc>::iterator iter = mObjectCreator.find(id); 
        if (iter == mObjectCreator.end()){ 
            return NULL;
        }
        //calls the required createObject() function
        return ((*iter).second)();
    }
};
27
ответ дан 28 November 2019 в 02:49
поделиться

Сериализация - щекотливая тема в C ++. ..

Быстрый вопрос:

  • Сериализация: недолговечная структура, один кодировщик / декодер
  • Обмен сообщениями: более длительный срок службы, кодировщики / декодеры на нескольких языках

Эти 2 полезны и имеют свое применение.

Boost.Serialization - наиболее рекомендуемая библиотека для сериализации обычно, хотя странный выбор operator & , который сериализует или десериализует в зависимости от константы, на самом деле является для меня злоупотреблением перегрузкой оператора.

Для обмена сообщениями я бы предпочел Google Буфер протокола . Они предлагают чистый синтаксис для описания сообщения и генерируют кодировщики и декодеры для огромного количества языков. Когда производительность имеет значение, есть еще одно преимущество: она допускает ленивую десериализацию (то есть только часть большого двоичного объекта сразу) по дизайну.

Переходя к

Теперь, что касается деталей реализации, это действительно зависит от того, что вы хотите.

  • Вам потребуется управление версиями , даже для регулярной сериализации, вам, вероятно, все равно потребуется обратная совместимость с предыдущей версией.
  • Вам может понадобиться, а может и не потребоваться система тегов + factory . Это необходимо только для полиморфного класса. И вам понадобится одна фабрика на каждое дерево наследования ( kind ), тогда ... код, конечно, можно создать по шаблону!
  • Указатели / ссылки собираются укусить вас в задницу ... они ссылаются на позицию в памяти, которая изменяется после десериализации. Я обычно выбираю касательный подход: каждому объекту каждого типа дается id , уникальный для его вида , поэтому я сериализую id , а не указатель. Некоторые фреймворки обрабатывают это до тех пор, пока у вас нет циклической зависимости, и сериализуют объекты, на которые указывает / ссылаются первыми.

Лично я старался изо всех сил отделить код сериализации / десериализации от реального кода, который выполняется класс. В частности, я стараюсь изолировать его в исходных файлах, чтобы изменения в этой части кода не аннигилировали двоичную совместимость.

При управлении версиями

я обычно стараюсь держать сериализацию и десериализацию одной версии близко друг к другу. Легче проверить, действительно ли они симметричны. Я также пытаюсь абстрагироваться от управления версиями непосредственно в моей структуре сериализации + несколько других вещей, потому что DRY следует придерживаться:)

Об обработке ошибок

Чтобы облегчить обнаружение ошибок, я обычно использую пару ' маркеры ' (специальные байты) для отделения одного объекта от другого. Это позволяет мне сразу бросать во время десериализации, потому что я могу обнаружить проблему десинхронизации потока (т. Е. Съел слишком много байтов или недостаточно).

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

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

20
ответ дан 28 November 2019 в 02:49
поделиться

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

5
ответ дан 28 November 2019 в 02:49
поделиться

Сериализация, к сожалению, никогда не будет полностью безболезненной в C ++, по крайней мере, в обозримом будущем просто потому, что в C ++ отсутствует критическая языковая функция, которая делает легкую сериализацию возможной на других языках: отражение . То есть, если вы создаете класс Foo , C ++ не имеет механизма для программной проверки класса во время выполнения, чтобы определить, какие переменные-члены он содержит.

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

6
ответ дан 28 November 2019 в 02:49
поделиться

Думаю, наиболее близким к стандартному способу будет Boost.Serialization . Я хотел бы услышать, и в каком контексте вы слышали это об идентификаторах классов. В случае сериализации я действительно не могу придумать другого способа (если, конечно, вы не знаете тип, который ожидаете при десериализации). А также Один размер не подходит всем .

2
ответ дан 28 November 2019 в 02:49
поделиться
Другие вопросы по тегам:

Похожие вопросы: