С++ - boost:: любая сериализация
Насколько я понимаю, нет сериализации (boost::serialization
, фактически) поддержки boost::any
placeholder.
Кто-нибудь знает, есть ли способ сериализации пользовательского объекта boost::any
?
Проблема здесь очевидна: boost::any
использует шаблонные заполнители для хранения объектов и typeid
, чтобы проверить, подходит ли boost::any_cast
.
Итак, существует специальный абстрактный суперкласс placeholder
и настраиваемые производные классы на основе шаблонов, которые создаются следующим образом:
template <T> custom_placeholder : public placeholder {
virtual std::type_info type() const { return typeid(T); }
virtual ...
};
Очевидно, это приносит некоторые проблемы, даже когда мы думаем о сериализации этого материала. Может быть, кто-то знает какую-то трюк, чтобы сделать такую сериализацию (и, конечно, правильную десериализацию)?
Спасибо
Ответы
Ответ 1
Это невозможно вообще, по крайней мере для произвольных типов. Обратите внимание, что, возможно, вы можете сериализовать использование какого-нибудь сложного кода (например, найти размер элементов, содержащихся в любом), но любой код использует компилятор, статически ставящий любой тип_кода и соответствующие типы внутри заполнителя. Вы, конечно же, не можете сделать это при десериализации на С++, поскольку тип, который вы получите от десериализации, неизвестен во время компиляции (как требуется новообразованным boost::any
).
Лучшее решение - создать какой-то специализированный любой тип для точных типов элементов, которые вы собираетесь сериализовать. Затем вы можете иметь специальные случаи для десериализации определенного типа элемента, но обратите внимание, что каждая сериализация/десериализация сериализации элемента должна быть физически записана как статический код на С++.
PD. Некоторые другие предложили использовать boost::variant
как представление этого специализированного типа, содержащего точные типы, которые вы собираетесь сериализовать. Вам нужен способ распознавания точного типа при десериализации, хотя (возможно, назначение идентификаторов типам в варианте).
Ответ 2
Если вы хотите придерживаться boost:: any, я не уверен, но вы можете написать свой собственный "boost:: any". Я использую этот код для прокси-методов для передачи параметров.
#include <iostream>
#include <boost\smart_ptr\scoped_ptr.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/serialization/access.hpp>
#include <boost/serialization/shared_ptr.hpp>
#include <boost/archive/text_oarchive.hpp>
#include <boost/archive/text_iarchive.hpp>
#include <boost/serialization/export.hpp>
#include <sstream>
class my_placeholder
{
public:
virtual ~my_placeholder(){}
my_placeholder(){}
private:
friend class boost::serialization::access;
template<class Archive>
void serialize(Archive & ar, const unsigned int version)
{
// serialize base class information
//ar & boost::serialization::base_object<bus_stop>(*this);
//ar & m_placeholder;
}
};
template<typename T>
class my_derivedplaceholder:
public my_placeholder
{
public:
my_derivedplaceholder()
{
}
my_derivedplaceholder(T &value)
{
m_value=value;
}
T m_value;
private:
friend class boost::serialization::access;
template<class Archive>
void serialize(Archive & ar, const unsigned int version)
{
// serialize base class information
ar & boost::serialization::base_object<my_placeholder>(*this);
ar & m_value;
}
};
BOOST_CLASS_EXPORT_GUID(my_derivedplaceholder<int>, "p<int>");
class my_any
{
public:
my_any()
{
}
template<typename T>
my_any(const T &value)
{
m_placeholder.reset(new my_derivedplaceholder<T>(const_cast<T&>(value)));
}
template<typename T>
void operator=(const T &value)
{
m_placeholder.reset(new my_derivedplaceholder<T>(const_cast<T&>(value)));
}
protected:
friend class boost::serialization::access;
template<class Archive>
void serialize(Archive & ar, const unsigned int version)
{
// serialize base class information
//ar & boost::serialization::base_object<bus_stop>(*this);
ar & m_placeholder;
}
template<typename T>
friend T my_anycast(my_any &val);
boost::shared_ptr<my_placeholder> m_placeholder;
};
template<typename T>
T my_anycast(my_any &val)
{
boost::shared_ptr<my_derivedplaceholder<T>> concrete=boost::dynamic_pointer_cast<my_derivedplaceholder<T>>(val.m_placeholder);
if (concrete.get()==NULL)
throw std::invalid_argument("Not convertible");
return concrete->m_value;
}
void main()
{
my_any m=10;
int a=my_anycast<int>(m);
std::cout << a << std::endl;
std::stringstream ss,ss2;
boost::archive::text_oarchive oa(ss);
oa << m;
boost::archive::text_iarchive ia(ss);
my_any m2;
ia >> m2;
std::cout << my_anycast<int>(m2) << std::endl;
}
Ответ 3
Предполагая, что вы должны использовать boost::any
, и вы не можете переключиться на variant
, решение на основе map<type_info const*, string(*)(any)>
могло бы вам помочь.
Во время выполнения вы должны инициализировать такой map
со всеми типами, которые вы планируете использовать. Конечно, вы можете использовать что-то вдоль линий
template <typename T>
struct any_serializer
{
static string perform(any a)
{
T const& x = any_cast<T const&>(a);
stringstream out;
out << x;
return out.str();
}
};
и введите карту с адресами any_serializer<T>::perform
под ключ &typeid(T)
. Вы можете специализировать класс any_serializer
и использовать некоторые (уродливые) макросы для заполнения карты.
Более трудным является, конечно, десериализация. Я не смотрел на boost::lexical_cast
какое-то время, возможно, это может помочь. Я боюсь, что это полностью зависит от проблем. Однако вам нужна только одна функция, которая принимает string
и возвращает один any
. Вы также можете добавить строку вывода с идентификатором пользовательского типа.
Ответ 4
Нет необходимости создавать новый класс. Попробуйте использовать xany https://sourceforge.net/projects/extendableany/?source=directory
Класс xany позволяет добавлять новые методы к любой существующей функциональности. Кстати, в документации есть пример, который делает именно то, что вы хотите.