boost::сериализация одного экземпляра для каждого уникального идентификатора

Я пытаюсь повысить:: сериализовать структуры, которые указывают на объекты (скажем, класса символов), реализующие идею единственного экземпляра для уникального «чего-то». Это означает, что эти объекты создаются не напрямую, а с использованием статического метода symbol::get(). Этот метод извлекает существующий объект из некоторого глобального словаря или при необходимости создает новый объект.

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

Стандартные методы boost::serialize, а именно load(...) и save(...) здесь не работают. При десериализации структуры теряется общесистемная уникальность символов, а сериализация занимает много места (мои объекты-символы довольно большие). Я прочитал документы о повышении и обнаружил, что для нестандартных конструкторов я могу использовать функции save_construct_data и load_construct_data. Но в документах также говорится, что load_construct_data по умолчанию «просто использует конструктор по умолчанию для инициализации ранее выделенной памяти». Так что опять не то.

Вопрос в том, как я могу обойти эту функцию загрузки, чтобы я мог избежать любого распределения и вместо этого использовать мой symbol::get()? А может быть есть более элегантное решение?

РЕДАКТИРОВАТЬ: прикрепил простой код, демонстрирующий проблему.

struct structure_element {
};

class symbol : public structure_element {
  symbol(string x);
  map<string, symbol> known_symbols;
public:
  static symbol *get(string x) {
    if (known_symbols.find(x) != known.symbols.end()){
      known_symbols[x] = symbol(x);
    }
    return &known_symbols[x];
  }
}

class structure_node : public structure_element {
  set<symbol *> some_attributes;
  vector<structure_element *> children;
}

person bzaborow    schedule 09.03.2015    source источник


Ответы (1)


Как правило, в исключительных случаях можно реализовать load_construct_data (очевидно, это означает, что вы не полагаетесь на реализацию по умолчанию, как вы уже заметили в своем вопросе).

Более конкретно: используйте Boost Flyweight. Или посмотрите, как они реализовали сериализацию для вдохновения.

Без конкретного образца программы я не могу вам что-то продемонстрировать.

Заполняя некоторые пробелы, вот демонстрационная программа, которая должна дать представление о вещах:

#include <iostream>
#include <boost/archive/text_oarchive.hpp>
#include <boost/serialization/string.hpp>
#include <boost/serialization/vector.hpp>
#include <boost/serialization/set.hpp>
#include <boost/serialization/map.hpp>

#if 0
#   define DEMO_FLYWEIGHT
#   include <boost/flyweight/serialize.hpp>
#   include <boost/flyweight.hpp>
#endif

struct structure_element { 
    virtual ~structure_element() {}

  private:
    friend class boost::serialization::access;
    template <typename Ar> void serialize(Ar& /*ar*/, unsigned /*version*/) {
    }
};

namespace detail {
    struct symbol_impl {
        symbol_impl(std::string const& x) : _x(x) { }

#ifdef DEMO_FLYWEIGHT
        size_t hash() const { return boost::hash_value(_x); }
        //bool operator< (symbol_impl const& other) const { return _x <  other._x; }
        bool operator==(symbol_impl const& other) const { return _x == other._x; }
#endif

      private:
        std::string _x;

        friend class boost::serialization::access;
        template <typename Ar> void serialize(Ar& ar, unsigned /*version*/) {
            ar & _x;
        }
    };
}

#ifdef DEMO_FLYWEIGHT
namespace boost {
    template <> struct hash<::detail::symbol_impl> {
        size_t operator()(::detail::symbol_impl const& s) const { return s.hash(); }
    };
}
#endif

struct symbol : public structure_element {
    symbol(std::string const& x) : _impl(x) {}

  private:
#ifdef DEMO_FLYWEIGHT
    boost::flyweight<detail::symbol_impl> _impl;
#else
    detail::symbol_impl _impl;
#endif

    friend class boost::serialization::access;
    template <typename Ar> void serialize(Ar& ar, unsigned /*version*/) {
        ar & boost::serialization::base_object<structure_element>(*this);
        ar & _impl;
    }
};

struct structure_node : public structure_element {
    structure_node(std::set<symbol*> a, std::vector<structure_element*> c) 
        : some_attributes(std::move(a)), children(std::move(c))
    {
    }

    // TODO value semantics/ownership
  private:
    std::set<symbol *> some_attributes;
    std::vector<structure_element *> children;

    friend class boost::serialization::access;
    template <typename Ar> void serialize(Ar& ar, unsigned /*version*/) {
        ar & boost::serialization::base_object<structure_element>(*this);
        ar & some_attributes;
        ar & children;
    }
};

#include <boost/make_shared.hpp>

int main() {
    // everything is leaked, by design
    symbol* bar = new symbol("bar");

    structure_node data { 
        {
            new symbol("foo"),
            bar,
            new symbol("foo"),
            new symbol("foo"),
            bar,
        },
        { 
            bar,
        }
    };

    boost::archive::text_oarchive oa(std::cout);
    oa << data;
}

Примечания:

  • Жить на Coliru >без легковеса

    22 serialization::archive 11 0 0 1 0
    0 0 0 4 0 3 1 0
    1
    2 0 0 3 bar 3
    3
    4 3 foo 3
    5
    6 3 foo 3
    7
    8 3 foo 0 0 1 0 3 1
    
  • Жить на Coliru >с включенным облегченным весом

    22 serialization::archive 11 0 0 1 0
    0 0 0 4 0 3 1 0
    1
    2 0 0 0 0 0 3 bar 3
    3
    4 1 3 foo 3
    5
    6 1 3
    7
    8 1 0 0 1 0 3 1
    

Обратите внимание, как объекты уже отслеживаются при сериализации через указатель. Это означает, что никакие дубликаты не сериализуются, даже если не используется облегченный вес, см., например. объект bar используется 3 раза.

Для объекта foo вы можете видеть, что его реализация «дедуплицирована», если хотите, при использовании легковеса.

Boost Flyweight легко настраивается, и его можно заставить работать значительно лучше, чем по умолчанию. Я ссылаюсь на документацию библиотеки, если вы хотите узнать больше

person sehe    schedule 09.03.2015
comment
Я приложил образец. Спасибо, что указали на «легковес», я не знал названия этого шаблона. Беглый взгляд на boost::flyweight показывает, что это более-менее то, что мне нужно, но мне нужно как-то поместить flyweight‹symbol› в мою иерархию классов (ранее я не упоминал об этом аспекте, извините). Хотелось бы услышать ваш подробный комментарий. - person bzaborow; 09.03.2015
comment
@bzaborow и у тебя есть - person sehe; 09.03.2015