#c #serialization #boost
#c #сериализация #ускорьте #c
Вопрос:
Я использую boost::serialize для сериализации документа, в котором я использую, например, juce::String. Вот так:
template<class Archive>
void serialize( Archive amp; ar, const unsigned int version )
{
ar amp; boost::serialization::make_nvp("title", m_docTitle);
...
}
Для boost::serialize, чтобы принять juce::String в качестве примитивного типа, я сделал:
#include <boost/serialization/string.hpp>
template<class IStream>
inline IStreamamp; operator >> (IStreamamp; stream, juce::Stringamp; s)
{
std::wstring t;
stream >> t;
s = juce::String(t.c_str());
return stream;
}
BOOST_CLASS_IMPLEMENTATION(juce::String, boost::serialization::primitive_type)
который отлично компилируется. Сериализация работает нормально, я получаю запись в XML:
<title>DocumentTitle</title>
так и должно быть. Однако при десериализации я могу отследить в операторе >>, что возвращаемая строка является:
"DocumentTitle</title>"
т.е. часть XML была «разжевана», что позже, конечно, приводит к исключению «ошибка входного потока».
Однако самое странное, что у меня это работало еще неделю назад … : ( и я понятия не имею, почему это НЕ работает сейчас…
Редактировать: небольшой пример кода, который показывает, воспроизводит поведение, только зависимость boost:
#include <boost/serialization/serialization.hpp>
#include <boost/serialization/string.hpp>
#include <boost/archive/xml_woarchive.hpp>
#include <boost/archive/xml_wiarchive.hpp>
#include <sstream>
class JuceString
{
public:
JuceString(const std::wstringamp; str = L"") : m_str(str) {;}
JuceString(const JuceStringamp; other) : m_str(other.m_str) {;}
JuceStringamp; operator = (const JuceStringamp; other)
{
if (this != amp;other)
{
m_str = other.m_str;
}
return *this;
}
const wchar_t* toWideCharPointer() const {
return m_str.c_str();
}
private:
std::wstring m_str;
};
template <class OStream>
OStreamamp; operator<< (OStreamamp; stream, const JuceStringamp; stringToWrite)
{
return stream << stringToWrite.toWideCharPointer();
}
template <class IStream>
IStreamamp; operator>> (IStreamamp; stream, JuceStringamp; s)
{
std::wstring t;
stream >> t;
s = JuceString(t.c_str());
return stream;
}
BOOST_CLASS_IMPLEMENTATION(JuceString, boost::serialization::primitive_type)
class Doc
{
friend class boost::serialization::access;
template<class Archive>
void serialize( Archive amp; ar, const unsigned int version )
{
ar amp; boost::serialization::make_nvp("title", m_title);
}
public:
Doc() {;}
Doc(const std::wstringamp; s) : m_title(s) {;}
private:
JuceString m_title;
};
int main (int argc, char* argv[])
{
std::wstringstream stream;
{
// Serializing document
Doc doc(L"DocumentTitle");
boost::archive::xml_woarchive oa(stream);
oa << boost::serialization::make_nvp("Document", doc);
}
{
// Deserializing document
Doc doc;
try
{
boost::archive::xml_wiarchive ia(stream);
ia >> boost::serialization::make_nvp("Document", doc);
}
catch (std::exceptionamp; e)
{
std::cerr << e.what() << std::endl;
}
}
return 0;
}
Использование текстового архива вместо этого прекрасно работает в обоих направлениях…
Комментарии:
1.Можете ли вы свести это к примеру из одного файла, который мы все можем скомпилировать и запустить на наших рабочих станциях? Чтобы попробовать это прямо сейчас, каждому читателю, который хочет попробовать, потребуется немного повозиться. Это почти готово, но с
int main()
несколькими#includes
у вас был бы отличный sscce и, вероятно, еще немало людей, заинтересованных в том, чтобы попробовать это.2. Это немного сложно, поскольку потребуется библиотека juce… но я исправлю это с учетом этого в качестве предпосылки.
3. Хорошо, обновил вопрос кодом, который воспроизводит проблему.
Ответ №1:
Наконец-то я заставил это работать (для удобства XML-архива), используя boost basic_xml_grammar для разбора строки в operator>> , вот так:
typedef boost::archive::basic_xml_grammar<wchar_t> xml_wgrammar;
template <class IStream>
IStreamamp; operator>> (IStreamamp; stream, JuceStringamp; s)
{
std::wstring t;
xml_wgrammar grammar;
if (!grammar.parse_string(stream, t))
{
boost::serialization::throw_exception(
boost::archive::xml_archive_exception(boost::archive::xml_archive_exception::xml_archive_parsing_error)
);
}
s = JuceString(t.c_str());
return stream;
}
Это гарантирует, что строка проанализирована правильно.
Комментарии:
1. 1, я кое-что узнал из этого. Я думаю, вам следует принять этот ответ вместо моего, поскольку это кажется правильным способом сделать это.
Ответ №2:
Проблема заключается в вашем operator >>
Когда вы делаете:
std::wstring t;
stream >> t;
Вы читаете до следующего пробела. В XML это после закрывающего тега, поэтому вы читаете часть потока, которую boost ожидает, что сможет прочитать там.
Решение состоит в том, чтобы либо принудительно ввести дополнительный пробел в выходные данные, либо изменить operator >>
, чтобы отказаться от чтения чего-либо вроде ‘<‘ . Я предполагаю, что ранее у вас было дополнительное пространство в строке, которую вы сохраняли и восстанавливали.
Предполагая, что вы можете установить / присвоить из std::wstring
и получить какое-то исходное значение, которое можно использовать для создания std::wstring, это работает на моей машине:
#include <boost/serialization/split_free.hpp>
…
namespace boost {
namespace serialization {
template<class Archive>
void load(Archive amp; ar, JuceString amp; j, const unsigned int version)
{
std::wstring tmp;
ar amp; boost::serialization::make_nvp("value", tmp);
j = tmp;
}
template<class Archive>
void save(Archive amp; ar, const JuceString amp; j, const unsigned int version)
{
const std::wstring tmp(j.toWideCharPointer());
ar amp; boost::serialization::make_nvp("value", tmp);
}
} // namespace serialization
} // namespace boost
BOOST_SERIALIZATION_SPLIT_FREE(JuceString)
Это избавляет от необходимости записывать operator >>
и позволяет boost напрямую обрабатывать чтение / запись wstring в архив. Вам нужно записать его только один раз, чтобы избежать проблем с дублированием, а затем вы можете использовать JuceString
так часто, как вам нравится.
Комментарии:
1. Дело в том, что раньше это работало нормально. И, кроме того, оператор std::basic_istream для std::wstring работает практически таким же образом, поэтому std:: wstring должен показывать ту же проблему, чего не происходит.
2. Я думаю, что на самом деле есть более приятное исправление, подождите
3. К сожалению, нет, у меня тоже не было пробела раньше … : (
4. @Robert — обновлено, разделив загрузку / сохранение, вы можете позволить архиву boost позаботиться о том, как читать
std::wstring
за вас.5. @awoodland: Ценю, но это именно то, чего я не хотел. Поскольку я собираюсь использовать большое количество juce :: String на протяжении сериализации, этот подход вскоре станет утомительным и подверженным ошибкам… О, и, кроме того, в (реальном) juce::String нет std::wstring для использования (в моем примере используется std::wstring для простоты)
Ответ №3:
Я не знаю, есть ли у вас ответ для вас, но при сравнении кода в моей рабочей области для сериализации ATL CString я получаю следующее:
inline void serialize(Archive amp; ar, CString amp; s, const unsigned int file_version)
{
std::wstring ss( s );
ar amp; BOOST_SERIALIZATION_NVP(ss);
s = ss.c_str();
}
Мне интересно, могли бы вы сделать что-то подобное в своем operator <<
— не уверен, применимо ли это решение к вам или нет. Заранее приношу извинения, если нет.
Комментарии:
1. Спасибо, это действительно работает, но затем он добавляет (используя XML) дополнительный элемент «ss», чего я хотел бы избежать. Хотя чисто косметически … 🙂